|Number of watchers on Github||436|
|Number of open issues||45|
|Average time to close an issue||about 20 hours|
|Main language||Emacs Lisp|
|Average time to merge a PR||about 7 hours|
|Open pull requests||6+|
|Closed pull requests||53+|
|Last commit||over 1 year ago|
|Repo Created||over 5 years ago|
|Repo Last Updated||over 1 year ago|
|Organization / Author||abo-abo|
|Do you use lispy? Leave a review!|
|View open issues (45)|
|View lispy activity|
|View on github|
|Fresh, new opensource launches 🚀🚀🚀|
Trendy new open source projects in your inbox! View examples
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:
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
The price for these short bindings is that they are only active when:
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,
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
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 anicharacter 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:
|vi in insert mode||j||impossible|
|vi in normal mode||impossible||j|
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.
Basic navigation by-list and by-region:
Paredit transformations, callable by plain letters:
IDE-like features for Elisp, Clojure, Scheme, Common Lisp, Hy, Python and Julia:
edebug, while storing current function's arguments
Some pictures here.
condexpression to equivalent
ifexpressions to an equivalent
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
(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
automatically, use something like this:
(add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1)))
Enable lispy for
Although I prefer to eval things in
eval-expression is handy. Here's how to use lispy
in the minibuffer during
(defun conditionally-enable-lispy () (when (eq this-command 'eval-expression) (lispy-mode 1))) (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:
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
show-paren-mode or similar.
Here's an illustration to this effect, with
represents the point):
You can use plain Emacs navigation commands to get into special, or you can use some of the dedicated commands:
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
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:
A lot of Lispy commands come in pairs - one reverses the other:
||C-d or DEL|
These commands handle whitespace in addition to inserting the expected thing.
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
Not so useful, but fun is /: start it from
|( position and hold
until all your Lisp code is turned into Python :).
lispy-ace-paren jumps to a
( character within current
top-level form (e.g.
defun). It's much faster than typing in the
avy binding + selecting
(, and there's less candidates,
since they're limited to the current top-level form.
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:
Type c, -, b and C-d to get:
(buffer-substring-no-properties (region-beginning) (region-|))
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:
While not in special:
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 left
lispy-right- mark the parent list with the point on the right
To do the reverse of the previous operation, i.e. to mark the first
child of marked list, use i -
lispy-mark-list- deactivate region
lispy-clone- clone region and keep it active
lispy-move-down- move region one sexp down
lispy-move-up- move region one sexp up
lispy-undo- deactivate region and undo
lispy-teleport- move region inside the sexp you select with
lispy-convolute- exchange the order of application of two sexps that contain region
lispy-new-copy- copy region as kill without deactivating the mark
lispy-paste- replace region with current kill
These features are specific to the Lisp dialect used. Currently Elisp
and Clojure (via
cider) are supported. There's also basic
evaluation support for:
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:
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:
Bound to g.
Use completion 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.
There's a feature similar to
ipython-notebook. Evaluating an Emacs
outline will evaluate all of the outline's code and echo the result of
the last expression. When an outline ends with a colon (
result will instead be inserted into the buffer. If the evaluation
result changes for whatever reason, it will be replaced after each
Python, Clojure, and Julia currently have a slightly better notebook
support, pressing e on the parent outline will evaluate all
the children outlines sequentially. This allows to arrange scripts
hierarchically, with relatively few top-level outlines and relatively
many total outlines. Each outline's output can be examined by adding a
: to the title of the outline.
The following example shows a buffer before and after pressing e.
There is one top-level outline, with one level-2 child, which in turn
has a four level-3 children. Three of these children end in
their output will be updated after the eval.
You can have only Paredit + special key bindings by using this composition of key themes:
(lispy-set-key-theme '(special paredit))
The default setting is:
(lispy-set-key-theme '(special lispy c-digits))
M is now bound to
lispy-alt-multiline instead of
lispy-multiline. It has a much better and more customizable algorithm.
See these variables for customization:
They are set to reasonable defaults. But you can customize them if you feel that a particular form should be multi-lined in a different way.
lispy-multiline-threshold is a bit of ad-hoc to make things nice. Set this to nil if you want a completely rigorous multi-line. With the default setting of 32, expressions shorter than this won't be multi-lined. This makes 95% of the code look really good.
The algorithm has a safety check implemented for Elisp: if
read on the transformed expression returns something different than
read on the original expression, an error will be signaled and no change will be made. For expressions that can't be
read, like buffers/markers/windows/cyclic lists/overlays, only a warning will be issued (
lispy can read them, unlike
For the expression
(a) will be considered the sexp at point, instead of
(b). This is consistent with
show-paren-mode. If a space is present, all ambiguities are resolved anyway.
I've switched the point and mark history to markers instead of points. When the buffer is changed, the markers are updated, so b will work fine.
In order for i (prettify code) to work for Clojure, it must be able to read the current expression. I've been extending the Elisp reader to understand Clojure. In the past commits, support was added for:
It should be possible to read any
#<...> form, as well as
This is a powerful feature that the
helm back end has had for a long time. When you press g, C-n and C-p will change the current selection. But C-M-n and C-M-p will change the current selection and move there, without exiting the completion.
This also means that you can call
ivy-resume to resume either g (
As you might know, the regular C-x C-e or
eval-buffer will not reset the values of
defcustom and such (you need C-M-x instead). But e does it, now also for
I normally use a light background, so I didn't notice before that the faces looked horrible with a dark background.
If you have a region selected, pressing ` will result in:
lispy-visit) allows to open a file in current project. Previously, it used
Now it uses
find-file-in-project by default, with the option to customize to
looking-back isn't very efficient, so it's preferable to avoid it or at least add a search bound to improve efficiency. Also the bound became mandatory in 25, while it was optional before.
See the relevant test:
(should (string= (lispy-with "\"See `plu|mage'.\"" (kbd "M-m")) "\"See ~`plumage'|.\""))
Thanks to this, to e.g. get the value of a quoted var in a docstring or a comment, or jump to its definition, you can M-m. Then, you can step-in with i to select the symbol without quotes.
A much better algorithm with caching an examining of file modification time is used now. This means that the tags should be up-to-date 99% of the time, even immediately after a save, and no necessary re-parsing will be done. And it all works fine with the
1% of the time,
lispy-tag-arity stops working, I don't know why, since it's hard to reproduce. You can then pass a prefix arg to refresh tags bypassing the cache, e.g 2g or 2G.
Also a bug is fixed in Clojure tag navigation, where the tag start positions were off by one char.
The fetched tags retrieval is fast: less than
0.15s on Emacs'
lisp/ directory to retrieve 21256 tags from 252 files. Which means it's lightning fast on smaller code bases (
lispy has only 651 tags).
lispy-debug-step-in, bound to xj locally and C-x C-j globally can now step into macros, as well as into functions. This command is very useful for Edebug-less debugging. Stepping into macros with
&rest parameters should work fine as well.
When positioned at function or macro args, p will set them as if the function or macro was called with empty args, or the appropriate amount of nils. If the function is interned and interactive, use its interactive form to set the arguments appropriately.
Again, this is very useful for debugging.
As you might know, P (
lispy-paste) is a powerful command that:
Now, you can:
It's OK to pass a larger arg than the length of the current list. In that case, the paste will be made into the last element of the list.
When there's no next element within parent, jump to parent from appropriate side. When the region is active, don't deactivate it. When splicing region, remove random quotes at region bounds.
This change makes the splice a lot more manageable. For example, starting with this Clojure code, with
| marking the current point:
(defn read-resource "Read a resource into a string" [path] (read-string |(slurp (clojure.java.io/resource path))))
A double splice // will result in:
(defn read-resource "Read a resource into a string" [path] |(read-string slurp clojure.java.io/resource path))
After xR (reverse list), 2 SPC (same as C-f),
-> (plain insert), [M (back to parent and multi-line), the final result:
(defn read-resource "Read a resource into a string" [path] |(-> path clojure.java.io/resource slurp read-string))
This also shows off xR -
lispy-reverse, which reverses the current list. Finally, reverting from the last code to the initial one can be done simply with xf - it will flatten the
-> macro call.
lispy-elisp-modeslist. It means that you can eval there if you want.
lispy-visit) should turn on
projectile-global-modeif it's not on.
lispy-multiline) works better for Clojure: the regexes for vectors, maps and sets have been improved.
The movement commands, such as:
lispy-tab), only when called for an active region
will not store each movement in the point-and-mark history. You can press b to go back in history. This is especially useful for h, l, and f, since they are not trivially reversible.
b was previously bound to
lispy-store-region-and-buffer, so you could do Ediff with
b and B. Now it's bound to xB.
lispy-delete) when positioned at the start of a comment, and with only whitespace
before the start of the line, will delete the whole comment.
If you want to un-comment, just use C-u ; from any point in the comment.
lispy-flatten) now also works for Clojure, before it was only for Elisp.
|(->> [1 2 3 4 5] (map sqr) (filter odd?))
When you press xf you get this:
|(filter odd? (map sqr [1 2 3 4 5]))
|(map odd? [1 2 3 4 5])
(let [f odd? coll [1 2 3 4 5]] (lazy-seq (when-let [s (seq coll)] (if (chunked-seq? s) (let [c (chunk-first s) size (int (count c)) b (chunk-buffer size)] (dotimes [i size] (chunk-append b (f (.nth c i)))) (chunk-cons (chunk b) (map f (chunk-rest s)))) (cons (f (first s)) (map f (rest s)))))))
A bit of a gibberish, but at least we can confirm that
map is indeed lazy.
(defn sqr [x] (* x x))
This one requires the function to be properly loaded with C-c C-l (
otherwise Clojure will not know the location of the function.
(+ |(sqr 10) 20)
(+ |(let [x 10] (* x x)) 20)
This is similar to the lax eval for Elisp. If you mark an expression with a region:
asdf [1 2 3]
and press e, you will actually eval this:
(do (def asdf [1 2 3]) asdf)
You can do this for let bindings, it's super-useful for debugging. The rule is that if the first element of the region is a symbol, and there's more stuff in the region besides the symbol, a lax eval will be performed.
If CIDER isn't live, e will start it and properly eval the current statement.
Since Elisp is a LISP-2, there can be a function and a variable with the same name. F
lispy-follow) prefers functions, but now 2F will prefer variables.
|(filter odd? (map sqr [1 2 3 4 5]))
Pressing 2e gives:
(filter odd? (map sqr [1 2 3 4 5])) ;; => ;; (1 9 25)
This works for all dialects, so you can also have:
(symbol-function 'exit-minibuffer) ;; => ;; (closure (t) ;; nil "Terminate this minibuffer argument." (interactive) ;; (setq deactivate-mark nil) ;; (throw (quote exit) ;; nil))
*MODULES* ;; => ;; ("SWANK-ARGLISTS" "SWANK-FANCY-INSPECTOR" "SWANK-FUZZY" "SWANK-C-P-C" ;; "SWANK-UTIL" "SWANK-PRESENTATIONS" "SWANK-REPL" ;; "SWANK-TRACE-DIALOG" "SB-CLTL2" "SB-POSIX" "SB-INTROSPECT")
To do the last eval you need to be in special. So first mark the symbol
*MODULES* with a region.
A convenient function to mark the current symbol is M-m (
lispy-occur) now has an
lispy-occur launches an interactive search within the current top-level expression, usually a
defun. This is useful to see where a variable is used in a function, or to quickly navigate to a
You can customize
lispy-occur-backend to either
ivy (the default) or
helm (if you have it,
since it's no longer a dependency of
Now it's the default one for navigating to tags. You can select alternatively
ido if you
Instead the dependency on
ace-window will be re-used. This allows for a lot of code
simplifications and better tests.
New custom variables:
lispy-avy-style-char: choose where the overlay appears for Q (
lispy-avy-style-paren: choose where the overlay appears for q (
lispy-avy-style-symbol: choose where the overlay appears for a (
lispy-ace-symbol) and - (
lispy-ace-subword) and H (
lispy-avy-keys, which is
z by default.
This is a list of compatibility features with other packages, such as
They add overhead, so you might want to turn them off if you don't use the mentioned packages.
You can navigate to a symbol definition in Scheme with F. This feature was already in place for Elisp, Clojure and CL.
lispy--eval-elisp-formfirst arg is now named
form. It was impossible to evaluate an unrelated
formvariable with the previous behavior.
defn) from Clojure source.
delete-regionin some obscure branches.
lispy-paste) will add a newline when called from start of line. This way, nP becomes equivalent to c (
lispy-clone). Of course, it's more flexible: you can do e.g. nkP.
lispy-bind-variable) now works on regions as well. Use it to bind the current sexp or region as a let-bound variable: it will put you in
iedit. When you're done with
iedit, press M-m (
lispy-mark-symbol) to exit
iedit. If you need to move the let binding around, use a combination of C (
lispy-convolute) and h (
lispy-tag-arity: now g will recognize
lispy-outlinewas updated to match the standard
^;;;outline regex. Try pressing I in e.g. org.el, it's quite beautiful.
lispy-evalfunctions will preserve the match data.
undercover/Coveralls test coverage report.
lispy-ace-symbol-replace) is now a Hydra: type h to delete more, type u to undo.
lispy-ace-char) now uses
avyto jump. This change allows to cover this function with a test.
dolistvariables in Elisp.
(defun range (a b) (message "called range") (number-sequence a b)) (dolist |(i (range 1 3)) (message "i=%d" i))
Pressing p with point where
| is, will
This is another step toward
edebug-less debugging, adding to special behavior for
lispy-helm-columnsis now a list
'(60 80). The first number is the width of the tag name column, the second number is the width of both tag name and tag file.
lispy-shiftab) is now a two-way cycle, instead of three-way, like
org-mode. The contents can be obtained with C-u I or C-u C-TAB.