The Animated Guide to Paredit
Paredit is great, it brings structural editing to lisps, maintaining the syntactical correctness of your code. I’ve been a fan for a long time, but still was only using a small subset of the functionality available, even afer spending time reading the manual and paper printing out cheat sheets.
Lately I decided to work deliberately with Paredit and really understand it, and now I mostly do. Here is what I learned.
Note: I've included my key-presses in the animations so you can see what I'm doing. However, they are shown as Mac keys, M- is ⌥, C- is ⌃, and shift is ⇧.
The basics
Balanced pairs
Opening
Paredit has four opening functions, applying to the ()
, []
, {}
and <>
pairs. Paredit never lets your buffer become unbalanced, so each open function will produce a balanced pair.
(
->paredit-open-round
[
->paredit-open-square
{
->paredit-open-curly
Opening tags
By default, paredit-open-angle
is not bound to any key.
Closing and indenting
Each of these functions has a partner in the paredit-close-[round|square|curly|angle]
functions. These will move the cursor past the next closing delimiter and also indent.
Moving past the closing delimiter
And pulling the closing delimiter onto the current line
A nice feature of these functions is that they are delimiter agnostic, that means that you don't need to match the correct closing character of the pair with the opening character, any will do.
Quoting
Paredit treats double quoting similarly to the opening and closing pairs of characters above. However, because the same character is used for opening and closing, there are differences.
double-quote
is bound to the "
key and will:
- Insert an open and closing quote
""
when the cursor is not in a quoted space, - Move past the closing double quote character when the cursor is at the end of a string
- Insert an escaped quote when inside a string,
Double quoting
Wrapping an S-expression
Paredit has variants of all of the above that open a pair and automatically wrap the following S-expression into it. These functions are named paredit-wrap-[round|square|curly|angle]
and paredit-meta-doublequote
.
paredit-wrap-round
is bound toM-(
paredit-meta-doublequote
is bound toM-"
- The other functions are not bound to any keys by default
Wrapping an S-expression
Deleting
Like the open, close and double quote keys, Paredit also takes over the default key combinations that Emacs uses for deleting:
paredit-forward-delete
bound toC-d
paredit-forward-kill-word
bound toM-d
paredit-backward-delete
bound toDEL
(and probably backspace too)paredit-backward-kill-word
bound toM-DEL
paredit-kill
bound toC-k
These commands act just the same as the regular Emacs commands that they hijack the keys from, until you try to break the balance of your S-expressions and then they'll refuse to comply. By doing this Paredit can ensure that integrity of your code is maintained.
Deleting forward by character and word
Deleting backward by character and word
Killing to the end of the current S-expression
This refusal to unbalance code is also, unfortunately, a great cause of pain to people that are new to Paredit and still transitioning from string based to tree based editing. And is a cause of people quitting Paredit forever. Fortunately, the solution lies below.
Slurping and Barfing
Say what now?
Slurping is when the current S-expression or string is expanded by pulling in the next outer S-expression. Barfing is the opposite, contracting the S-expression by pushing out it's last-most form.
Slurping is provided by paredit-forward-slurp-sexp
, bound to C-)
, and barfing is provided by paredit-forward-barf-sexp
that is bound to C-}
.
Slurping and barfing
Notice that these function names contain the word "forward", Paredit also gives us the ability to slurp and barf backwards with paredit-backward-slurp-sexp
, bound to C-(
, and paredit-backward-barf-sexp
, bound to C-{
.
Slurping and barfing backwards
Structural Navigation
Paredit provides functions for graceful S-expression navigation, allowing you to move forward and backward amongst siblings, raise up to the enclosing S-expression and descend back down into the children.
Move forward and backward inside an S-expression using paredit-forward
and paredit-backward
, bound to C-M-f
and C-M-b
respectively. Upon reaching a delimiter, a further invocation will move the cursor outside of the current S-expression and into the enclosing S-expression.
Moving forward and backwards
Paredit offers two methods of descent and ascent, that can be used depending on the direction that you want to move.
To descend forwards use paredit-forward-down
, bound to C-M-d
. To reverse that and ascend backwards use paredit-backward-up
, bound to C-M-u
.
Descending forwards and ascending backwards
Then, when you want to descend backward you can use paredit-backward-down
, bound to C-M-p
. And to reverse that and ascend forwards use paredit-forward-up
, bound to C-M-n
.
Descending backwards and ascending forwards
Splicing
Splicing is the act of removing the current S-expression and joining (some of) the contents with the enclosing S-expression. There are two splices that will kill the content of the current S-expression either to the front of rear of the cursor.
Kill backwards with paredit-splice-sexp-killing-backward
, bound to M-<up>
.
Splice and kill backwards
And kill forwards with paredit-splice-sexp-killing-forward
that is bound to M-<down>
.
Splice and kill forwards
And there is a no kill variant paredit-splice-sexp
bound to M-s
, shown here splicing the quotes off a string.
Splice a string without killing anything
Splitting and Joining
An S-expression can be split into two, and two S-expressions can be joining back together into one.
Split is paredit-split-sexp
and bound to M-S
, join is paredit-join-sexps
and bound to M-J
. Note that the S and J are both uppercase.
Joining two printlns into one
Bonus
A final treat, here is paredit-convolute-sexp
and it is bound, quite appropriately, to M-?
.
The description from the function docs is:
Convolute S-expressions.
Save the S-expressions preceding point and delete them.
Splice the S-expressions following point.
Wrap the enclosing list in a new list prefixed by the saved text.
With a prefix argument N, move up N lists before wrapping.
Convoluting an expression
I'm still waiting for the day when I recognise a use for this one.
Discussion on Lobsters, Hacker News and Reddit