I recently found paredit. It is a mode which allows you to edit s-expressions (sexps) in a structured way.
Essentially it forces you to maintain balanced parentheses by always inserting/deleting matched pairs. It is very easy to modify the current sexp to: enclose (wrap) the next element, to merge it up one level (aka splice, i.e. remove a set of parentheses), to split the current sexp, etc.
It works with round parens ()
, square brackets []
, curly braces {}
, and angle braces <>
. It also handles string quotes "
the same way.
Deleting and killing of text also works nicely. For example, C-k
is bound to 'paredit-kill
which has the following behavior (note: |
represents the cursor position):
(foo bar)| ; Useless comment!It's taking a little getting used to, but I'm finding myself thinking less about where the current block of code ends than I used to. The parentheses just take care of themselves...
->
(foo bar)|
(|foo bar) ; Useful comment!
->
(|) ; Useful comment!
|(foo bar) ; Useless line!
->
|
(foo "|bar baz"
quux)
->
(foo "|"
quux)
This is how I enabled it:
(defun turn-on-paredit-mode ()
(paredit-mode +1))
;; I didn't like C-j being overridden, I want it to still work the old way in lisp-interaction
(eval-after-load "paredit"
'(progn
(define-key paredit-mode-map (read-kbd-macro "C-j") nil)))
(add-hook 'emacs-lisp-mode-hook 'turn-on-paredit-mode)
The cheat sheet is probably the best way to get an idea of what is possible. Check it out here. Also, the command documentation is very complete (see the above which was grabbed right from the
C-k
documentation, so it's easy to remind yourself what is possible.About the only problem I've had is when I've yanked code that has unbalanced parens. So I wrote a little advice which at least warns you and allows you to abort if the yanked text is unbalanced. It's not perfect, but it is a reasonable first implementation:
(defun paredit-check-region-for-yank ()
"run after yank and yank-pop to verify balanced parens"
(when paredit-mode
(save-excursion
(save-restriction
(narrow-to-region (point) (mark))
(condition-case nil
(check-parens)
(error
(if (not (y-or-n-p "The text inserted has unbalanced parentheses, continue? "))
(delete-region (point-min) (point-max)))))))))
(defadvice yank (after yank-check-parens activate)
(paredit-check-region-for-yank))
(defadvice yank-pop (after yank-pop-check-parens activate)
(paredit-check-region-for-yank))