diff --git a/README.org b/README.org index ab1343a..1f4fb11 100644 --- a/README.org +++ b/README.org @@ -164,15 +164,44 @@ The =slurp/barf-lispy= key theme provides commands that act the same as the defa For both =<= bindings, if =lispyville-barf-stay-with-closing= is non-nil and barfing would move the closing delimiter behind the point, the point will instead be put on the closing delimiter. ** Additional Key Theme -The corresponding symbol is =additional=. The default states are normal and visual. This key theme is the equivalent of cleverparen's "additional bindings" keys. It is currently incomplete. =M-j= is comparable to ~evil-cp-drag-forward~ and ~lispy-move-down~. =M-k= is comparable to ~evil-cp-drag-backward~ and ~lispy-move-up~. - -| key | command | -|-------+----------------------------| -| =M-j= | ~lispyville-drag-forward~ | -| =M-k= | ~lispyville-drag-backward~ | +The corresponding symbol is =additional=. The default state is normal state. This key theme is the equivalent of cleverparens' "additional bindings" keys. It is currently incomplete. =M-j= is comparable to ~evil-cp-drag-forward~ and ~lispy-move-down~. =M-k= is comparable to ~evil-cp-drag-backward~ and ~lispy-move-up~. + +| key | command | +|-------+------------------------------------------| +| =M-j= | ~lispyville-drag-forward~ | +| =M-k= | ~lispyville-drag-backward~ | +| =M-J= | ~lispy-join~ | +| =M-s= | ~lispy-splice~ | +| =M-S= | ~lispy-split~ | +| =M-r= | ~lispy-raise-sexp~ | +| =M-R= | ~lispyville-raise-list~ | +| =M-t= | ~transpose-sexps~ | +| =M-v= | ~lispy-convolute-sexp~ | ~lispyville-move-down~ is an alias for ~lispyville-drag-forward~, and ~lispyville-move-up~ is an alias for ~lispyville-drag-backward~. +*** Additional Insert Key Theme +The corresponding symbol is =additional-insert=. The default state is normal state. This key theme also corresponds to keybindings from cleverparens additional keybindings. + +| key | command | +|-------+------------------------------------------| +| =M-i= | ~lispyville-insert-at-beginning-of-list~ | +| =M-a= | ~lispyville-insert-at-end-of-list~ | +| =M-o= | ~lispyville-open-below-list~ | +| =M-O= | ~lispyville-open-above-list~ | + +Unlike cleverparens, these commands work only with lists. ~evil-cp-insert-at-beginning-of-form~, for example, will insert at the beginning of strings as well. To me, it is simpler and more consistent to only consider lists instead of specially handling string atoms. If you would prefer the original behavior, feel free to make an issue, and I can add alternative commands. + +** Arrows Key Theme +The corresponding symbol is =arrows=. The default state is normal state. This key theme provides similar keybindings to those from [[https://github.com/tpope/vim-sexp-mappings-for-regular-people][vim-sexp-mappings-for-regular-people]]. It is currently incomplete. + +| key | command | +|------+------------------------------------------| +| =i= | ~lispyville-insert-at-end-of-list~ | + +Note that the original plugin uses =>I= and == used with inner text objects. Since manual indentation is never necessary with lisp (e.g. use =aggressive-indent-mode= or ~lispyville-prettify~ / ~lispy-tab~ instead), this key theme does not attempt to leave the original keybindings intact. + ** Escape Key Theme The corresponding symbol is =escape=. The default states are insert and emacs. See [[#using-both-separately][here]] for more information. diff --git a/lispyville-test.el b/lispyville-test.el index 627ed26..a602715 100644 --- a/lispyville-test.el +++ b/lispyville-test.el @@ -7,6 +7,7 @@ additional-movement slurp/barf-cp additional + additional-insert mark-toggle)) (lispyville-set-key-theme) @@ -760,6 +761,104 @@ character after it is not considered as part of the region." ;; "((|b c d) a)")) ) +(ert-deftest lispyville-raise-list () + ;; should work in the basic case + (should (string= (lispyville-with "((|a))" + "M-R") + "(|a)")) + ;; should work in a string + (should (string= (lispyville-with "((\"|a\"))" + "M-R") + "(\"|a\")")) + ;; should work with a count + (should (string= (lispyville-with "(((|a)))" + "2 M-R") + "(|a)")) + ;; should work with a count larger than the max possible count + (should (string= (lispyville-with "(((|a)))" + "3 M-R") + "(|a)"))) + +(ert-deftest lispyville-insert-at-beginning-of-list () + ;; should work in the basic case + (should (string= (lispyville-with "(a |b c)" + "M-i") + "(|a b c)")) + ;; should work in a string + (should (string= (lispyville-with "(a \"|b\" c)" + "M-i") + "(|a \"b\" c)")) + ;; should work with a count + (should (string= (lispyville-with "((a |b c))" + "2 M-i") + "(|(a b c))")) + ;; should work with a count larger than the max possible count + (should (string= (lispyville-with "((a |b c))" + "3 M-i") + "(|(a b c))"))) + +(ert-deftest lispyville-insert-at-end-of-list () + ;; should work in the basic case + (should (string= (lispyville-with "(a |b c)" + "M-a") + "(a b c|)")) + ;; should work in a string + (should (string= (lispyville-with "(a \"|b\" c)" + "M-a") + "(a \"b\" c|)")) + ;; should work with a count + (should (string= (lispyville-with "((a |b c))" + "2 M-a") + "((a b c)|)")) + ;; should work with a count larger than the max possible count + (should (string= (lispyville-with "((a |b c))" + "3 M-a") + "((a b c)|)"))) + +(ert-deftest lispyville-open-below-list () + ;; should work in the basic case + (should (string= (lispyville-with "((|a)\n b)" + "M-o") + "((a)\n |\n b)")) + ;; should work with trailing sexps + (should (string= (lispyville-with "((|a) b)" + "M-o") + "((a)\n |b)")) + ;; should insert an extra newline at the top-level + (should (string= (lispyville-with "(|a)" + "M-o") + "(a)\n\n|")) + ;; should work with a count + (should (string= (lispyville-with "((|a))" + "2 M-o") + "((a))\n\n|")) + ;; should work with a count larger than the max possible count + (should (string= (lispyville-with "((|a))" + "3 M-o") + "((a))\n\n|"))) + +(ert-deftest lispyville-open-above-list () + ;; should work in the basic case + (should (string= (lispyville-with "(a\n (|b))" + "M-O") + "(a\n |\n (b))")) + ;; should work with leading sexps + (should (string= (lispyville-with "(a (|b))" + "M-O") + "(a |\n (b))")) + ;; should insert an extra newline at the top-level + (should (string= (lispyville-with "(|a)" + "M-O") + "|\n\n(a)")) + ;; should work with a count + (should (string= (lispyville-with "((|a))" + "2 M-O") + "|\n\n((a))")) + ;; should work with a count larger than the max possible count + (should (string= (lispyville-with "((|a))" + "3 M-O") + "|\n\n((a))"))) + ;;; * Visual and Special Mark Integration (ert-deftest lispyville-toggle-mark-type () (lispy-define-key lispy-mode-map "m" #'lispyville-toggle-mark-type) diff --git a/lispyville.el b/lispyville.el index d47fcda..5ce4db1 100644 --- a/lispyville.el +++ b/lispyville.el @@ -91,7 +91,7 @@ only has an effect if `lispyville-commands-put-into-special' is nil." :type 'boolean) (defcustom lispyville-preferred-lispy-state 'insert - "The preferred evil state for using lispy. + "The preferred evil state for insertion and using lispy. This is used by any command that should enter special to determine the correct state." :group 'lispyville @@ -99,6 +99,8 @@ state." (const :tag "Use insert state to get into special." insert) (const :tag "Use emacs state to get into special." emacs))) +(defvaralias 'lispyville-preferred-state 'lispyville-preferred-lispy-state) + (defcustom lispyville-motions-put-into-special nil "Applicable motions will enter insert or emacs state. This will only happen when they are not called with an operator or in visual @@ -895,6 +897,75 @@ This is the lispyville equivalent of `lispy-move-up' and (defalias 'lispyville-move-up 'lispyville-drag-backward) +(evil-define-command lispyville-raise-list (count) + "Raise the current list COUNT times. +This is the lispyville equivalent of `evil-cp-raise-form' except for lists +only." + (interactive "") + (save-excursion + ;; unlike `backward-up-list', works in string + (when (lispy--out-backward 1) + (lispy-raise (or count 1))))) + +;; ** Additional Insert Key Theme +(evil-define-command lispyville-insert-at-beggining-of-list (count) + "Enter `lispyville-preferred-state' at the beginning of the current list. +With COUNT, move backward/out COUNT lists first. This is the lispyville +equivalent of `evil-cp-insert-at-beginning-of-form' except for lists only." + (interactive "") + (when (lispy--out-backward (or count 1)) + (forward-char) + (evil-change-state lispyville-preferred-state))) + +(defun lispyville--out-forward (count) + "Like `lispyville--out-forward' but don't return nil if move at least once. +COUNT is passed to `lispy--out-forward'." + (let ((orig-pos (point))) + (lispy--out-forward count) + (not (= (point) orig-pos)))) + +(evil-define-command lispyville-insert-at-end-of-list (count) + "Enter `lispyville-preferred-state' at the end of the current list. +With COUNT, move forward/out COUNT lists first. This is the lispyville +equivalent of `evil-cp-insert-at-end-of-form' except for lists only." + (interactive "") + (when (lispyville--out-forward (or count 1)) + (backward-char) + (evil-change-state lispyville-preferred-state))) + +(defun lispyville--top-level-p () + "Return whether the point is at the top level." + (= (car (syntax-ppss)) 0)) + +(evil-define-command lispyville-open-below-list (count) + "Enter `lispyville-preferred-state' below the current list and indent. +With COUNT, move forward/out COUNT lists first. When exiting to the top-level, +insert in between two newlines. This is the lispyville equivalent of +`evil-cp-open-below-form' except for lists only. This is somewhat comparable to +`lispy-out-forward-newline' as well" + (interactive "") + (when (lispyville--out-forward (or count 1)) + (newline-and-indent) + (when (lispyville--top-level-p) + (insert "\n")) + (evil-change-state lispyville-preferred-state))) + +(evil-define-command lispyville-open-above-list (count) + "Enter `lispyville-preferred-state' above the current list and indent. +With COUNT, move backward/out COUNT lists first. When exiting to the top-level, +insert in between two newlines. This is the lispyville equivalent of +`evil-cp-open-above-form' except for lists only." + (interactive "") + (when (lispy--out-backward (or count 1)) + (save-excursion + (insert "\n") + (lispy--indent-for-tab)) + (lispy--indent-for-tab) + (when (lispyville--top-level-p) + (save-excursion + (insert "\n"))) + (evil-change-state lispyville-preferred-state))) + ;; * Integration Between Visual State and Lispy's Special Mark State ;; ** Using Both Separately (defun lispyville-normal-state () @@ -1125,10 +1196,29 @@ When THEME is not given, `lispville-key-theme' will be used instead." ">" #'lispyville-slurp "<" #'lispyville-barf)) (additional - (or states (setq states '(normal visual))) + (or states (setq states 'normal)) (lispyville--define-key states (kbd "M-j") #'lispyville-drag-forward - (kbd "M-k") #'lispyville-drag-backward)) + (kbd "M-k") #'lispyville-drag-backward + (kbd "M-J") #'lispy-join + (kbd "M-s") #'lispy-splice + (kbd "M-S") #'lispy-split + (kbd "M-r") #'lispy-raise-sexp + (kbd "M-R") #'lispyville-raise-list + (kbd "M-t") #'transpose-sexps + (kbd "M-v") #'lispy-convolute-sexp)) + (additional-insert + (or states (setq states 'normal)) + (lispyville--define-key states + (kbd "M-i") #'lispyville-insert-at-beggining-of-list + (kbd "M-a") #'lispyville-insert-at-end-of-list + (kbd "M-o") #'lispyville-open-below-list + (kbd "M-O") #'lispyville-open-above-list)) + (arrows + (or states (setq states 'normal)) + (lispyville--define-key states + "i" #'lispyville-insert-at-end-of-list)) (insert (lispyville-space-after-insert)) (escape