Wednesday, September 30, 2009

Emacs Tip #33: paredit

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!
->
(foo bar)|

(|foo bar) ; Useful comment!
->
(|) ; Useful comment!

|(foo bar) ; Useless line!
->
|

(foo "|bar baz"
quux)
->
(foo "|"
quux)
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...

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))

Somewhat meta: blogs on reddit

Recently, doing a vanity search, I saw a link to this blog from reddit. I'm not a reddit user, but found it interesting that some of my posts had made it on there (as you can see here: trey-jackson.blogspot.com on reddit.com).

What I find curious about reddit as it applies to blogs is that it ends up taking the conversation away from the blog. This works well for reddit users, but ends up fracturing the audience. I don't get many comments (I have very few readers) and would have appreciated the reading the comments made on reddit.

For what it's worth, I did update a post to fix a coding bug noticed by someone on reddit (ironically, their fix had syntax problems at the same spot as well).