Are you happy with your logging solution? Would you help us out by taking a 30-second survey? Click here

lispy

Short and sweet LISP editing

Subscribe to updates I use lispy


Statistics on lispy

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
Size 4.25 MB
Homepage http://oremacs.co...
Organization / Authorabo-abo
Latest Release0.26.0
Contributors10
Page Updated
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

Subscribe to our mailing list

Evaluating lispy for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

License GPL 3 Build Status Coverage Status MELPA MELPA Stable

lispy logo

short and sweet LISP editing

Table of Contents

Introduction

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:

  • the point is before an open paren: (, [ or {
  • the point is after a close paren: ), ] or }
  • the region is active

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.

Relation to vi

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:

  • Emacs can both insert and call commands without switching modes (since it has none), but the command bindings are long
  • vi has short command bindings, but you have to switch modes between inserting and calling commands
  • lispy has short command bindings and doesn't need to switch modes

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:

  • you are in normal mode when the point is before/after paren or the region is active
  • otherwise you are in insert mode

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.

Features

  • Basic navigation by-list and by-region:

    • h moves left
    • j moves down
    • k moves up
    • l moves right
    • f steps inside the list
    • b moves back in history for all above commands
  • Paredit transformations, callable by plain letters:

    • > slurps
    • < barfs
    • r raises
    • C convolutes
    • s moves down
    • w moves up
  • IDE-like features for Elisp, Clojure, Scheme, Common Lisp, Hy, Python and Julia:

    • e evals
    • E evals and inserts
    • g jumps to any tag in the current directory with semantic
    • G jumps to any tag in the current file
    • M-. jumps to symbol, M-, jumps back
    • F jumps to symbol, D jumps back
    • C-1 shows documentation in an overlay
    • C-2 shows arguments in an overlay
    • Z breaks out of edebug, while storing current function's arguments

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

Function reference

Most functions are cataloged and described at http://abo-abo.github.io/lispy/.

Getting Started

Installation instructions

via MELPA

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

via el-get

el-get also features a lispy recipe. Use M-x el-get-install RET lispy RET to install.

Configuration instructions

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)))
(add-hook 'minibuffer-setup-hook 'conditionally-enable-lispy)

Customization instructions

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

Operating on lists

How to get into list-editing mode (special)

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 ()\
before key after
`\ (looking-at ()` c
(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.

Digit keys in special

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.

How to get out of special

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:

(| (

List commands overview

Inserting pairs

Here's a list of commands for inserting pairs:

key command
( lispy-parens
{ lispy-braces
} lispy-brackets
" lispy-quotes

Reversible commands

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

Keys that modify whitespace

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

Command chaining

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

Navigating with avy-related commands

key 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 avy 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.

Operating on regions

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.

Ways to activate region

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

Move region around

The arrow keys j/k will move the region up/down within the current list. The actual code will not be changed.

Switch to the other side of the region

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.

Grow/shrink 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:

  • h - lispy-left - mark the parent list with the point on the left
  • l - 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-tab.

Commands that operate on region

  • m - lispy-mark-list - deactivate region
  • c - lispy-clone - clone region and keep it active
  • s - lispy-move-down - move region one sexp down
  • w - lispy-move-up - move region one sexp up
  • u - lispy-undo - deactivate region and undo
  • t - lispy-teleport - move region inside the sexp you select with lispy-ace-paren
  • C - lispy-convolute - exchange the order of application of two sexps that contain region
  • n - lispy-new-copy - copy region as kill without deactivating the mark
  • P - lispy-paste - replace region with current kill

IDE-like features

These 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)
  • Common lisp (via slime or sly).
  • Hy (via comint).
  • Python (via comint and jedi).
  • Julia (via julia-shell).

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:

screenshot

Here's how it looks for Clojure:

screenshot

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:

screenshot

As you see, normal, &optional and &rest arguments have each a different face. Here's how it looks for Clojure:

screenshot

lispy-goto

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.

lispy-eval

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 (:), the result will instead be inserted into the buffer. If the evaluation result changes for whatever reason, it will be replaced after each subsequent e.

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.

lispy-python-notebook.png

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 :, so their output will be updated after the eval.

Demos

Demo 1: Practice generating code

Demo 2: The substitution model for procedure application

Demo 3: Down the rabbit hole

Demo 4: Project Euler p100 and Clojure

Demo 5: ->>ification

Demo 6: cond->if->cond

Screencasts

  • The older stuff can be found on vimeo.
  • The newer stuff is on https://www.youtube.com/user/abo5abo/videos.
lispy open issues Ask a question     (View All Issues)
  • almost 3 years Unable to export index.org to info or texi
  • almost 3 years make lispy-avy-keys sensible to default avy-keys ?
  • almost 3 years Some strange behaviours of `lispy-comment`
  • almost 3 years Inserting pairs just before strings in clojure indents code unneccesarily
  • about 3 years Does lispy have anything like paredit-backward-down (C-M-p)?
  • about 3 years lispy-fill does not support filling comments
  • about 3 years Document lispytutor.el
  • about 3 years keep spacing after inserting
  • over 3 years lispy-open-curly does not add space
  • over 3 years Support for lispy-flatten with common lisp (at least for macros)
  • over 3 years Variables for Valid Syntax Preceding Delimiters
  • over 3 years Let special-lispy-other-mode decribe bindings
  • over 3 years Bugcatching for parinfer compat functions
  • over 3 years lispy-eval-display-style 'overlay breaks on repeat
  • over 3 years multiline on a clojure vector
  • over 3 years adding lispy support to other lisp modes
  • over 3 years Better support for lispy-goto in clojure-mode
  • almost 4 years lispy-ace-paren errors with Args out of range
  • almost 4 years lispy in spacemacs
  • almost 4 years debugging clojure code with cider-debug and or with lispy?
  • almost 4 years `(lispy-move-up)` unbalance parentheses when comments are involved
  • about 4 years lispy-goto support for ClojureScript
  • about 4 years lispy-goto shows less information than before
  • over 4 years paredit mode emulation: delete/backspace quirks
  • over 4 years Conflict with org-mode
lispy open pull requests (View All Pulls)
  • Add failing tests for lispy-paredit-delete fns
  • Add failing tests for lispy-paredit-delete fns
  • Improve lispy-comment behaviour at eol-like positions
  • Make evil-cp themed key bindings more practical
  • Always indent-sexp after lispy-tab
  • Add failing test case for lispy-delete-backward
lispy questions on Stackoverflow (View All Questions)
  • Lispy way of running a function multiple times
  • Lispy dialects with good multi-dimensional array programming support
  • How can I define a Lispy datatype in Haskell?
  • When a DSL gets 'lispy'
  • How to make this code simpler, clearer and "more lispy"?
  • Is there some lispy language that seamlessly integrates with Python?
  • Is there a more lispy way to write this code?
  • Problem Installing Lispy Package Manager
  • Lispy way to read user input from the keyboard in Clojure?
  • Is Clojure considered Lispy or Schemey?
lispy list of languages used
lispy latest release notes
0.26.0 lispy 0.26.0

Fixes

  • C-k should delete the whole multi-line string.
  • y should work for all parens, not just (.
  • p should actually eval in other window for dolist.
  • Prevent pairs inserting an extra space when at minibuffer start.
  • ol works properly for active region.

New Features

Misc

  • xf will pretty-print the macros for Elisp.
  • M-m works better when before ).
  • Fix ', after a ,.
  • Improve / (splice) for quoted regions.
  • Z works with &key arguments.
  • The new M is used in xf.
  • Allow to flatten Elisp defsubst.
  • c should insert an extra newline for top-level sexps.

Paredit key bindings

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

New algorithm for multi-lining

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:

  • lispy-multiline-threshold
  • lispy--multiline-take-3
  • lispy--multiline-take-3-arg
  • lispy--multiline-take-2
  • lispy--multiline-take-2-arg

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

d and > give priority to lispy-right

For the expression (a)|(b), (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.

b works fine even if the buffer changes

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.

Extend Clojure reader

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:

  • empty sets
  • commas
  • auto-symbols, like p1__7041#

Extend Elisp reader

It should be possible to read any #<...> form, as well as #1-type forms.

g and G get a persistent action for ivy

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 (lispy-goto) or G (lispy-goto-local).

e works with defvar-local

As you might know, the regular C-x C-e or eval-buffer will not reset the values of defvar, defcustom and such (you need C-M-x instead). But e does it, now also for defvar-local.

Improve faces for dark backgrounds

I normally use a light background, so I didn't notice before that the faces looked horrible with a dark background.

The ` will quote the region

If you have a region selected, pressing ` will result in:

`symbol'

Customize the file selection back end for V

V (lispy-visit) allows to open a file in current project. Previously, it used projectile. Now it uses find-file-in-project by default, with the option to customize to projectile.

Fixup calls to looking-back

Apparently, 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.

M-m will work better in strings and comments.

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.

Update the tags strategy

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 lispy-tag-arity modifications.

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

xj can also step into macros

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.

p can now lax-eval function and macro arguments

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.

Allow to paste anywhere in the list using a numeric arg

As you might know, P (lispy-paste) is a powerful command that:

  • Replaces selection with current kill when the region is active.
  • Yanks the current kill before or after the current list otherwise.

Now, you can:

  • Yank the current kill to become the second element of the list with 2P
  • Yank the current kill to become the third element of the list with 3P
  • ...

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.

Update the way / (lispy-splice) works

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.

0.25.0 lispy 0.25.0

Fixes

  • Add minibuffer-inactive-mode to the lispy-elisp-modes list. It means that you can eval there if you want.
  • V (lispy-visit) should turn on projectile-global-mode if it's not on.
  • M (lispy-multiline) works better for Clojure: the regexes for vectors, maps and sets have been improved.
  • C-k should not delete only the string when located at start of string.
  • M will not turn vectors into lists any more.
  • the backquote bug for i and M was fixed.
  • you can flatten Elisp closures as well, at least the plain ones.

New Features

b calls lispy-back

The movement commands, such as:

  • the arrows hjkl (lispy-left, lispy-down etc.)
  • f (lispy-flow)
  • q (lispy-ace-paren)
  • i (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.

Hungry comment delete

C-d (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.

Added flatten operation for Clojure

xf (lispy-flatten) now also works for Clojure, before it was only for Elisp.

Example 1 (flatten a macro):

|(->> [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]))

Example 2 (flatten a standard function):

Start with:

|(map odd? [1 2 3 4 5])

After xf:

(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.

Example 3 (flatten your own function):

Example function:

(defn sqr [x]
  (* x x))

This one requires the function to be properly loaded with C-c C-l (cider-load-file), otherwise Clojure will not know the location of the function.

Example statement:

(+ |(sqr 10) 20)

After xf:

(+ |(let [x 10]
     (* x x)) 20)

Added lax eval for Clojure

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.

e will auto-start CIDER

If CIDER isn't live, e will start it and properly eval the current statement.

2F will search for variables first

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.

2e will eval and insert the commented result

Starting with:

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

or

*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-mark-symbol).

y (lispy-occur) now has an ivy back end

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 statement.

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

Add ivy back end to lispy-completion-method

Now it's the default one for navigating to tags. You can select alternatively helm or ido if you wish.

Remove the dependency on ace-jump-mode

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-ace-char)
  • lispy-avy-style-paren: choose where the overlay appears for q (lispy-ace-paren)
  • lispy-avy-style-symbol: choose where the overlay appears for a (lispy-ace-symbol) and - (lispy-ace-subword) and H (ace-symbol-replace).

There's also lispy-avy-keys, which is a ... z by default.

Add lispy-compat

This is a list of compatibility features with other packages, such as edebug and god-mode. They add overhead, so you might want to turn them off if you don't use the mentioned packages.

F works for Scheme

You can navigate to a symbol definition in Scheme with F. This feature was already in place for Elisp, Clojure and CL.

0.24.0 lispy 0.24.0

Fixes

  • DEL behaves properly after a string and one space.
  • C-k works better for expressions preceded with #.
  • 3 should not add a space when there is one already.
  • # will not add a space after a comma.
  • C-j works better in comments after a quote.
  • lispy--eval-elisp-form first arg is now named lispy-form instead of form. It was impossible to evaluate an unrelated form variable with the previous behavior.
  • F again works properly when jumping to a jar (e.g. to defn) from Clojure source.
  • C-k won't call delete-region in some obscure branches.

Enhancements

  • P (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.
  • xb (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-left).
  • g will ignore loaddefs.el for Elisp.
  • M-m works better in unbalanced buffers, which should be an rare thing.
  • add defhydra to lispy-tag-arity: now g will recognize defhydra statements.
  • The tag logic was improved to do less parsing.
  • lispy-outline was updated to match the standard ^;;; outline regex. Try pressing I in e.g. org.el, it's quite beautiful.
  • All lispy-eval functions will preserve the match data.
  • > will delete the extra whitespace while slurping.
  • Added undercover/Coveralls test coverage report.
  • H (lispy-ace-symbol-replace) is now a Hydra: type h to delete more, type u to undo.
  • Q (lispy-ace-char) now uses avy to jump. This change allows to cover this function with a test.

New features

p can now iterate dolist variables 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

  • call range and set i to 1
  • set i to 2
  • set i to 3
  • set i to nil
  • call range and set i to 1

This is another step toward edebug-less debugging, adding to special behavior for let, cond and labels.

Incompatible changes

  • lispy-helm-columns is 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.
  • j and k should now move to outline when at beginning of comment. The previous behavior was to look for the first sexp in the direction. You can still do that with f.
  • I (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.
Other projects in Emacs Lisp