An experimental set of keybindings to integrate ParEdit into Evil Mode. The general concept IMO is good (a separate 'Paredit State'), the keybindings however are personal preference and I am constantly tweaking them myself. No perfect solution, but see what works best for you and customise to your hearts content :-)
Last Updated: 8 October 2021
ParEdit1 is one of the oldest and most famous structural editing tools for the Lisp-family of languages, and one that I felt most comfortable with (whether it be its classic design or simply muscle memory). It is perfectly viable for use in Evil Mode, particularly with the assistance of Evil Paredit2 to avoid accidental breakage of parentheses parity.
However, key sequences without modifiers feel much more natural in VIM / Evil Mode, and my aim has been to find a set of keybindings that will allow for a more natural integration of ParEdit with Evil Mode. Indeed, whilst there are many other excellent packages which aim to do this (see Lispy3, Lispyville4, Smartparens5, Evil Cleverparens6, Symex7, and more8), it wouldn't be Emacs if we didn't reinvent the wheel and end up writing our own keybinds.
Thus, herein, is my current approach to structural editing within Evil Mode. You can download and load parevil.el
into your Emacs configuration and feel free to send through any comments & suggestions.
;; Add the below to your .emacs to use ParEvil and then evaluate it
;; Don't forget to save your .emacs!
(load "/path-to-parevil/parevil.el)
Modifying the standard bindings of VIM would be a foolish endeavour, and we will not do so as a starting principle. Rather, we create a new mode (Paredit State) as follows, which we can toggle from and to Normal Mode by pressing spacebar
. Note that we only bind spacebar
within Normal mode for lisp-related major modes, so you are free to bind spacebar
to something else for other major modes. As a starting point, our new mode inherits all of the keybindings from Normal mode. We then introduce the following bindings specific for common ParEdit commands. Any ParEdit command not listed below can be accessed by its default keybinding.
Key | Function in Paredit State | Key | Function in Paredit State |
---|---|---|---|
f |
paredit-forward |
sp |
paredit-backward-slurp-sexp |
b |
paredit-backward |
sb |
paredit-forward-slurp-sexp |
n |
paredit-forward-down |
{ |
paredit-backward-barf-sexp |
w |
paredit-backward-up |
} |
paredit-forward-barf-sexp |
q |
beginning-of-defun |
gr |
paredit-raise-sexp |
gn |
paredit-forward-up |
gl |
paredit-splice-sexp |
gp |
paredit-backward-down |
gw |
paredit-wrap-round |
gh |
paredit-recenter-on-sexp |
ge |
indent-region |
t |
transpose-sexps |
T |
transpose-sexps -1 |
p |
Custom Paste |
se |
mark-sexp (i.e. select sexp) |
sd |
Custom Sexp Cut |
sy |
Custom Sexp Copy |
gs |
Custom Multi Cut |
gy |
Custom Multi Copy |
Note that the some of the above commands accept numeric prefix arguments. For example, we can move 4 sexps forward with 4f
.
One command not found directly in ParEdit is transpose-sexps
9 which we add with the following. The trick to transposing is to have your cursor right after an sexp that you wish to transpose with the one before or after it.
;; Accepts numeric prefix argument
(define-key evil-paredit-state-map "t" 'transpose-sexps)
;; For convenience, a reverse transpose can be achieved with T:
(define-key evil-paredit-state-map "T" (lambda () (interactive) (transpose-sexps -1)))
We also added the following new functions to cut / copy all expressions within a group (from point onwards). These hopefully should work okay, but let me know any unintended behaviour.
;; Cut all remaining expressions in a group
(define-key evil-normal-state-map "gs"
(lambda ()
(interactive)
(let ((starting-point (point))
(ending-point nil))
(save-excursion
(paredit-backward-up)
(evil-jump-item)
(setq ending-point (point)))
(kill-region starting-point ending-point))))
;; Copy all remaining expressions in a group
(define-key evil-normal-state-map "gy"
(lambda ()
(interactive)
(let ((starting-point (point))
(ending-point nil))
(save-excursion
(paredit-backward-up)
(evil-jump-item)
(setq ending-point (point)))
(kill-ring-save starting-point ending-point))))
Footnotes
-
Main Website: http://mumble.net/~campbell/emacs/paredit.el. Quick Reference: http://mumble.net/~campbell/emacs/paredit.html ↩
-
GitHub Repo: https://github.com/roman/evil-paredit ↩
-
GitHub Repo: https://github.com/abo-abo/lispy ↩
-
GitHub Repo: https://github.com/noctuid/lispyville ↩
-
GitHub Repo: https://github.com/Fuco1/smartparens ↩
-
GitHub Repo: https://github.com/luxbock/evil-cleverparens ↩
-
GitHub Repo: https://github.com/countvajhula/symex.el ↩
-
Let me know if you have a package you want to list here ↩
-
Reference: https://www.gnu.org/software/emacs/manual/html_node/emacs/Expressions.html ↩