https://github.com/abo-abo/lispy.git
git clone 'git://github.com/abo-abo/lispy.git'
short and sweet LISP editing
Table of Contents
This package reimagines Paredit - a popular method to navigate and edit LISP code in Emacs.
The killer-feature are the short bindings:
command | binding | binding | command
——————————|——————|————–|——————-
paredit-forward
| C-M-f | j | lispy-down
paredit-backward
| C-M-b | k | lispy-up
paredit-backward-up
| C-M-u | h | lispy-left
paredit-forward-up
| C-M-n | l | lispy-right
paredit-raise-sexp
| M-r | r | lispy-raise
paredit-convolute-sexp
| M-? | C | lispy-convolute
paredit-forward-slurp-sexp
| C-) | > | lispy-slurp
paredit-forward-barf-sexp
| C-} | < | lispy-barf
paredit-backward-slurp-sexp
| C-( | > | lispy-slurp
paredit-backward-barf-sexp
| C-{ | < | lispy-barf
Most of more than 100 interactive commands that lispy
provides are
bound to a-z and A-Z in
lispy-mode
. You can see the full command reference with many
examples here.
The price for these short bindings is that they are only active when:
(
, [
or {
)
, ]
or }
The advantage of short bindings is that you are more likely to use them. As you use them more, you learn how to combine them, increasing your editing efficiency.
To further facilitate building complex commands from smaller commands,
lispy-mode
binds digit-argument
to 0-9. For
example, you can mark the third element of the list with
3m. You can then mark third through fifth element (three
total) with 2> or >>. You can then move the
selection to the last three elements of the list with 99j.
If you are currently using Paredit, note that lispy-mode
and
paredit-mode
can actually coexist with very few conflicts, although
there would be some redundancy.
The key binding method is influenced by vi, although this isn't modal editing per se.
Here's a quote from Wikipedia on how vi works, in case you don't know:
vi is a modal editor: it operates in either insert mode (where typed text becomes part of the document) or normal mode (where keystrokes are interpreted as commands that control the edit session). For example, typing i while in normal mode switches the editor to insert mode, but typing i again at this point places an “i” character in the document. From insert mode, pressing ESC switches the editor back to normal mode.
Here's an illustration of Emacs, vi and lispy bindings for inserting a char and calling a command:
| insert "j" | forward-list
——————|—————-|————– Emacs | j | C-M-n vi in insert mode | j | impossible vi in normal mode | impossible | j lispy | j | j
Advantages/disadvantages:
Of course it's not magic, lispy needs to have normal/insert mode to perform both functions with j. The difference from vi is that the mode is explicit instead of implicit - it's determined by the point position or the region state:
So people who generally like Emacs bindings (like me) can have the cake and eat it too (no dedicated insert mode + shorter key bindings). While people who like vi can still get an experience that's reasonably close to vi for LISP editing (since vi's line-based approach isn't very appropriate for LISP anyway).
But if you ask:
What if I want to insert when the point is before/after paren or the region is active?
The answer is that because of the LISP syntax you don't want to write this:
j(progn
(forward-char 1))k
Also, Emacs does nothing special by default when the region is active and you press a normal key, so new commands can be called in that situation.
edebug
, while storing current function's argumentsSome pictures here.
- Code manipulation:
- i prettifies code (remove extra space, hanging parens …)
- xi transforms cond
expression to equivalent if
expressions
- xc transforms if
expressions to an equivalent cond
expression
- xf flattens function or macro call (extract body and substitute arguments)
- xr evals and replaces
- xl turns current defun
into a lambda
- xd turns current lambda
into a defun
- O formats the code into one line
- M formats the code into multiple lines
- Misc. bindings:
- outlines navigation/folding (J, K, I, i)
- narrow/widen (N, W)
- ediff
(b, B)
- ert
(T)
- edebug
(xe)
Most functions are cataloged and described at http://abo-abo.github.io/lispy/.
It's easiest/recommended to install from MELPA.
Here's a minimal MELPA configuration for your ~/.emacs
:
(package-initialize)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
Afterwards, M-x package-install RET lispy RET (you might want to M-x package-refresh-contents RET beforehand if you haven't done so recently).
el-get also features a lispy recipe. Use M-x el-get-install RET lispy RET to install.
Enable lispy automatically for certain modes
After installing, you can call M-x lispy-mode for any
buffer with a LISP dialect source. To have lispy-mode
activated
automatically, use something like this:
(add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1)))
Enable lispy for eval-expression
Although I prefer to eval things in *scratch*
, sometimes
M-: - eval-expression
is handy. Here's how to use lispy
in the minibuffer during eval-expression
:
(defun conditionally-enable-lispy ()
(when (eq this-command 'eval-expression)
(lispy-mode 1)
(local-set-key "β" 'helm-lisp-completion-at-point)))
(add-hook 'minibuffer-setup-hook 'conditionally-enable-lispy)
If you want to replace some of the lispy-mode
's bindings you can do
it like this:
(eval-after-load "lispy"
`(progn
;; replace a global binding with own function
(define-key lispy-mode-map (kbd "C-e") 'my-custom-eol)
;; replace a global binding with major-mode's default
(define-key lispy-mode-map (kbd "C-j") nil)
;; replace a local binding
(lispy-define-key lispy-mode-map "s" 'lispy-down)))
The plain keys will call commands when: - the point is positioned before paren - the point is positioned after paren - the region is active
When one of the first two conditions is true, I say that the point is
special. When the point is special, it's very clear to which sexp the
list-manipulating command will be applied to, what the result be and
where the point should end up afterwards. You can enhance this effect
with show-paren-mode
or similar.
Here's an illustration to this effect, with lispy-clone
(here, |
represents the point):
before | key | after
——————–|————–|————————
(looking-at "(")|
| c | (looking-at "(")
| | (looking-at "(")|
before | key | after
——————–|————–|————————
|(looking-at "(")
| c | |(looking-at "(")
| | (looking-at "(")
You can use plain Emacs navigation commands to get into special, or you can use some of the dedicated commands:
Key Binding | Description
—————-|———————————————————–
] | lispy-forward
- move to the end of the closest list, analogous to C-M-n (forward-list
)
[| lispy-backward
- move to the start of the closest list, analogous to C-M-p (backward-list
)
C-3 | lispy-right
- exit current list forwards, analogous to up-list
) | lispy-right-nostring
exit current list forwards, but self-insert in strings and comments
These are the few lispy commands that don't care whether the point is special or not. Other such bindings are DEL, C-d, C-k.
Special is useful for manipulating/navigating lists. If you want to manipulate symbols, use region selection instead.
When special, the digit keys call digit-argument
which is very
useful since most lispy commands accept a numeric argument.
For instance, 3c is equivalent to ccc (clone sexp 3 times), and
4j is equivalent to jjjj (move point 4 sexps down).
Some useful applications are 9l and 9h - they exit list forwards
and backwards respectively at most 9 times which makes them
effectively equivalent to end-of-defun
and beginning-of-defun
. Or
you can move to the last sexp of the file with 999j.
To get out of the special position, you can use any of the good-old navigational commands such as C-f or C-n. Additionally SPC will break out of special to get around the situation when you have the point between the open parens like this
(|(
and want to start inserting; SPC will change the code to this:
(| (
Here's a list of commands for inserting pairs:
key | command
——————|——————————————————————-
( | lispy-parens
{ | lispy-braces
} | lispy-brackets
" | lispy-quotes
A lot of Lispy commands come in pairs - one reverses the other:
key | command | key | command
—————-|————————–|———————————-|———————-
j | lispy-down
| k | lispy-up
s | lispy-move-down
| w | lispy-move-up
> | lispy-slurp
| < | lispy-barf
c | lispy-clone
| C-d or DEL |
C | lispy-convolute
| C | reverses itself
d | lispy-different
| d | reverses itself
M-j | lispy-split
| + | lispy-join
O | lispy-oneline
| M | lispy-multiline
S | lispy-stringify
| C-u " | lispy-quotes
; | lispy-comment
| C-u ; | lispy-comment
xi | lispy-to-ifs
| xc | lispy-to-cond
These commands handle whitespace in addition to inserting the expected thing.
key | command
—————-|—————————
SPC | lispy-space
: | lispy-colon
^ | lispy-hat
C-m | lispy-newline-and-indent
Most special commands will leave the point special after they're
done. This allows to chain them as well as apply them
continuously by holding the key. Some useful hold-able keys are
jkf<>cws;.
Not so useful, but fun is /: start it from |(
position and hold
until all your Lisp code is turned into Python :).
ace-jump-mode
-related commandskey | command
—————-|————————–
q | lispy-ace-paren
Q | lispy-ace-char
a | lispy-ace-symbol
H | lispy-ace-symbol-replace
- | lispy-ace-subword
q - lispy-ace-paren
jumps to a “(” character within current
top-level form (e.g. defun
). It's much faster than typing in the
ace-jump-mode
binding + selecting “(”, and there's less candidates,
since they're limited to the current top-level form.
a - lispy-ace-symbol
will let you select which symbol to
mark within current form. This can be followed up with e.g. eval,
describe, follow, raise etc. Or you can simply m to
deactivate the mark and edit from there.
- - lispy-ace-subword
is a niche command for a neat combo. Start with:
(buffer-substring-no-properties
(region-beginning)|)
Type c, -, b and C-d to get:
(buffer-substring-no-properties
(region-beginning)
(region-|))
Fill end
to finish the statement.
Sometimes the expression that you want to operate on isn't bounded by parens. In that case you can mark it with a region and operate on that.
While in special:
- Mark a sexp with m - lispy-mark-list
- Mark a symbol within sexp a - lispy-ace-symbol
.
While not in special:
- C-SPC - set-mark-command
- mark a symbol at point with M-m - lispy-mark-symbol
- mark containing expression (list or string or comment) with C-M-, - lispy-mark
The arrow keys j/k will move the region up/down within the current list. The actual code will not be changed.
Use d - lispy-different
to switch between different sides
of the region. The side is important since the grow/shrink operations
apply to current side of the region.
Use a combination of:
- > - lispy-slurp
- extend by one sexp from the current side. Use digit
argument to extend by several sexps.
- < - lispy-barf
- shrink by one sexp from the current side. Use digit
argument to shrink by several sexps.
The other two arrow keys will mark the parent list of the current region:
lispy-left
- mark the parent list with the point on the leftlispy-right
- mark the parent list with the point on the rightTo do the reverse of the previous operation, i.e. to mark the first
child of marked list, use i - lispy-tab
.
lispy-mark-list
- deactivate regionlispy-clone
- clone region and keep it activelispy-move-down
- move region one sexp downlispy-move-up
- move region one sexp uplispy-undo
- deactivate region and undolispy-teleport
- move region inside the sexp you select with lispy-ace-paren
lispy-convolute
- exchange the order of application of two sexps that contain regionlispy-new-copy
- copy region as kill without deactivating the marklispy-paste
- replace region with current killThese features are specific to the Lisp dialect used. Currently Elisp
and Clojure (via cider
) are supported. There's also basic
evaluation support for Scheme (via geiser
) and Common lisp (via
slime
).
lispy-describe-inline
Bound to C-1. Show the doc for the current function inline.
C-h f is fine, but the extra buffer, and having to navigate to a symbol is tiresome. C-1 toggles on/off the inline doc for current function. No extra buffer necessary:
Here's how it looks for Clojure:
lispy-arglist-inline
Bound to C-2. Show arguments for current function inline.
eldoc-mode
is cool, but it shows you arguments over there and
you're writing over here!. No problem, C-2 fixes that:
As you see, normal, &optional and &rest arguments have each a different face. Here's how it looks for Clojure:
lispy-goto
Bound to g.
Use helm
to select a symbol to jump to from all top-level symbols in
the in current directory.
Works out of the box for Elisp, Scheme and Common Lisp. clojure-semantic is required for Clojure.