Welcome to LispyVille!
lispyville.el
’s main purpose is to provide a lisp-editing environment suited towards evil users. It can serve as a minimal layer on top of lispy-mode
for better integration with evil, but it does not require use of lispy’s keybinding style. The provided commands allow for editing lisp in normal state and will work even without lispy-mode
enabled.
Here are the main features of lispyville:
- Provides “safe” versions of vim’s yank, delete, and change related operators that won’t unbalance parentheses
- Provides lisp-related motions/commands
- Integrates evil with lispy by providing commands to more easily switch between normal state and special and by providing options for integrating visual state with lispy’s special
Note that this package does not create any new evil states; it assumes that the user will be using lispy in insert or emacs state.
lispyville.el
has a similar intent to evil-cleverparens and related packages. It creates “safe” versions of standard evil editing commands. For example, it ensures that dd
will not unbalance parenthesis but instead only delete the “safe” portion of the line. This allows the evil’s line-oriented commands to become far more useful for lisps.
The primary difference between lispyville and other similar packages is that it uses lispy instead of smartparens or paredit. This matters because lispyville is primarily intended to be used in conjunction with lispy. Lispy already has a lot in common with evil. Unlike smartparens and paredit, lispy’s primary keybindings are just letters. It may help to think of lispy as just an additional evil state. The main difference is that you can automatically enter and exit it without having to use ESC
.
However, if you’d rather stick to mainly vim keybindings, lispyville will also eventually provide “key themes” to replicate all of evil-cleveparens’ keybindings as well as the keybindings of some other popular evil/lisp editing packages. I also plan to add key themes that are more similar to lispy’s keybindings.
While lispyville can be used without lispy-mode
, some additional setup may be required to make editing lisp comfortable. For example, (
would need to be explicitly bound to lispy-parens
for auto-pairing behavior (and the other functionality provided by lispy-pair
). If your gripe with lispy is its style of having “special” locations where letter keys act as commands, you can still use lispy-mode
for the “normal” keybindings it provides by not using special
in your lispy “key theme”:
(lispy-set-key-theme '(lispy c-digits))
You can always override these keybindings later.
Lispyville is a minor mode. To enable it wherever lispy is enabled, you can add the following to your configuration:
(add-hook 'lispy-mode-hook #'lispyville-mode)
The operators behave similarly to evil-cleverparens’ operators with a few exceptions. The delete operator will always act safely by ignoring unmatched delimiters, whereas cleverparens will sometimes splice. While cleverparens’ yank operators will attempt to add unmatched delimiters, lispyville’s yank operators will simply exclude the unmatched delimiters, which is consistent with how the delete operator works. The operators will also work in visual block mode, unlike with cleverparens. The user can also choose whether or not they want to act safely on delimiters in strings and comments (see Lispy Settings).
Y
acts like a safe y$
unlike in evil and cleverparens. If anyone takes issue with this change, I can add a command for its regular functionality, but I think most people dislike the default inconsistency between Y
and D
in vim.
Additionally, I think that the function used for safe behavior is a lot more sanely implemented in lispyville than in other related packages (it intelligently analyzes a region once instead of repeatedly calling check-parens
).
I’ve added this functionality directly to lispy, and if you want lispy’s copy, delete, and/or paste commands to keep parentheses balanced, you can set the relevant options for lispy (see Lispy Settings).
By default, the only keys that lispyville remaps are the operators. To allow for the user to choose between various sets of keybindings without making them manually remap every command, lispyville provides “key themes” similarly to how lispy does.
The user can still define commands in lispyville-mode
using evil-define-key
or something like general, but lispyville-set-key-theme
can also be used to define keys. It takes one argument which is a list of symbols corresponding to the different themes. By default, most commands will be mapped in normal and visual state. The default states are listed below. To change them, a list of the symbol and the states to map the keys in can be used instead.
As an example, the following command will map the “operators” theme in the normal and visual states, the “escape” theme in just the insert state, and the “additional-movement” theme in the normal, visual, and motion states:
(with-eval-after-load 'lispyville
(lispyville-set-key-theme '(operators
(escape insert)
(additional-movement normal visual motion))))
lispyville-set-key-theme
will not reset lispyville’s keymap, so it will not remove user-defined keybindings (unless they are overwritten by a key in one of the themes). The keybindings will be added in the order of the list, so if there is overlap between the listed themes, the one listed last will take precedence.
The corresponding symbol is operators
. The default states are normal and visual. These are safe versions of the corresponding evil operators that won’t unbalance parentheses. Like with cleverparens, dd
will bring closing delimiters that are on a line by themselves to the previous line while cc
won’t. To disable this behavior, lispyville-dd-stay-with-closing
can be set to a non-nil value.
key | command |
---|---|
y | lispyville-yank |
d | lispyville-delete |
c | lispyville-change |
x | lispyville-delete-char-or-splice |
Y | lispyville-yank-line |
D | lispyville-delete-line |
C | lispyville-change-line |
X | lispyville-delete-char-or-splice-backwards |
The corresponding symbol is s-operators
. The default states are normal and visual. I’ve separated the s operators because I prefer to use cl
and cc
and bind the s keys to something else entirely.
key | command |
---|---|
s | lispyville-substitute |
S | lispyville-change-whole-line |
The corresponding symbol is additional-movement
. The default states are normal and visual. This key theme is the equivalent of cleverparen’s additional movement keys. [
and ]
are like the reverse of lispy-flow
. {
and }
are like lispy-flow
. (
and )
are like lispy-left
and lispy-right
. Also see here for some extra information on automatically enter special after executing these motions.
key | command |
---|---|
H | lispyville-backward-sexp |
L | lispyville-forward-sexp |
M-h | lispyville-beginning-of-defun |
M-l | lispyville-end-of-defun |
[ | lispyville-previous-opening |
] | lispyville-next-closing |
{ | lispyville-next-opening |
} | lispyville-previous-closing |
( | lispyville-backward-up-list |
) | lispyville-up-list |
lispyville-left
is an alias for lispyville-backward-up-list
, and lispyville-right
is an alias for lispyville-up-list
.
Two key themes are provided for slurping and barfing keybindings. The default state for both is normal. Note that the commands in both key themes work with digit arguments. A positive argument will barf or slurp that many times like in cleverparens. Additionally, for the slurp commands, an argument of -1
will slurp to the end of the line where the sexp after the closing paren ends, and an argument of 0
will slurp as far as possible. See the documentation for lispy-slurp for more information. Also see here for some extra information on automatically entering special after executing these commands.
Note that the commands for both key themes will act on the paren after the point, meaning that the point should be before a closing paren to be considered “on” it.
The slurp/barf-cp
key theme provides commands that act the same as cleverparens’ slurp and barf keys or lispy’s lispy-slurp-or-barf-right
and lispy-slurp-or-barf-left
. >
and <
can be thought of arrows that will move the paren at point in the corresponding direction. If there is no paren at the point, the keys will take the action they would on a right paren but will not move the point.
key | command |
---|---|
> | lispyville-> |
< | lispyville-< |
The slurp/barf-lispy
key theme provides commands that act the same as the default lispy-slurp
and lispy-barf
. In this case, >
and <
can be thought to correspond to “grow” and “shrink” respectively. >
will always slurp, and <
will always barf. If there is no paren at the point, the keys will take the action they would on a right paren but will not move the point.
key | command |
---|---|
> | lispyville-slurp |
< | lispyville-barf |
For both <
bindings, if lispyville-barf-stay-with-closing
is non-nil and barfing would move the closing delimiter behind the point, the point will instead be put on the closing delimiter.
The corresponding symbol is additional
. The default states are normal and visual. This key theme is the equivalent of cleverparen’s “additional bindings” keys. It is currently incomplete. M-j
is comparable to evil-cp-drag-forward
and lispy-move-down
. M-k
is comparable to evil-cp-drag-backward
and lispy-move-up
.
key | command |
---|---|
M-j | lispyville-drag-forward |
M-k | lispyville-drag-backward |
lispyville-move-down
is an alias for lispyville-drag-forward
, and lispyville-move-up
is an alias for lispyville-drag-backward
.
The corresponding symbol is escape
. The default states are insert and emacs. See here for more information.
key | command |
---|---|
ESC | lispyville-normal-state |
The corresponding symbol is mark
. See here for more information.
key | command |
---|---|
v | lispy-mark-symbol |
V | lispy-mark |
C-v | lispy-mark |
Getting to special when in insert or emacs state is already pretty easy. You can use )
or [
and ]
(if you like those keybindings) to jump to a special location at any time. If you want to get there from normal state, it’s a bit more tedious, since you need to first navigate to a special location and then enter insert or emacs state.
Lispyville provides an option that will automatically enter insert or emacs state for lispyville navigation commands that would put you at a paren. To enable this behavior, lispyville-motions-put-into-special
can be set to a non-nil value. If you prefer to edit in emacs-state, you can set it to the symbol emacs
.
Note that this behavior will not affect the use of motions with an operator or in visual state (which wouldn’t make sense).
There is also an option for commands called lispyville-commands-put-into-special
that can be customized in the same way. The currently applicable commands are the slurp and barf commands.
Lispyville tries to be unobtrusive by default, only rebinding the major operator keys. Since there are many potential ways to better integrate evil’s visual state with lispy’s special (with the region active), lispyville doesn’t make a default choice for the user.
This is probably the simplest method of improving things. By default, pressing escape after using something like lispy-mark
from special will enter visual state, requiring another press of escape to get to normal state. Lispyville provides lispyville-normal-state
to do this in one step. You can map it manually or use the escape
key theme (e.g. (lispyville-set-key-theme '(... (escape insert emacs)))
).
On the other hand, if you want to map a key in normal state to mark something with a lispy command like lispy-mark
, normally evil’s visual state will be entered. If you would rather use lispy’s keys after calling specific (but not all) mark commands, you can use lispyville-wrap-command
to create a function that will call a command and then enter a state.
(evil-define-key 'normal lispyville-mode-map
"v" (lispyville-wrap-command lispy-mark-symbol insert))
You may also want to use this for binding m
and related keys for lispy.
See the next section if you would prefer that lispy’s mark commands always enter insert or emacs state.
Lispy’s special mark state won’t work correctly when entered with an active region it wouldn’t normally mark (e.g. half of a symbol is marked). Because of this, you’ll probably want to rebind v
, V
, and C-v
. Lispyville provides a key theme to remap v
to lispy-mark-symbol
and V
and C-v
to lispy-mark
(e.g. (lispyville-set-key-theme '(... (mark normal)))
).
To always use lispy’s keys, one of the following add-hook
expressions can be added to the user’s configuration. They just cause insert or emacs state to be entered instead of visual state for commands that would enter visual state. Using this, there is no need to do anything special for gv
, as no evil commands will be used for altering the selected region.
(add-hook 'lispyville-mode-hook #'lispyville-enter-insert-when-marking)
;; Or if you prefer emacs state
(add-hook 'lispyville-mode-hook #'lispyville-enter-emacs-when-marking)
If after trying this out, you find you don’t like it, you can use the following to remove the behavior:
(add-hook 'lispyville-mode-hook #'lispyville-remove-marking-hooks)
(remove-hook 'lispyville-mode-hook #'lispyville-enter-insert-when-marking)
;; or
(remove-hook 'lispyville-mode-hook #'lispyville-enter-emacs-when-marking)
Similarly, one can have all lispy mark commands enter evil’s visual state instead:
(add-hook 'lispyville-mode-hook #'lispyville-enter-visual-when-marking)
The behavior can be removed in the same way:
(add-hook 'lispyville-mode-hook #'lispyville-remove-marking-hooks)
(remove-hook 'lispyville-mode-hook #'lispyville-enter-visual-when-marking)
If you would rather use a combination of the two, it’s probably best to pick the one you find the most useful and bind some keys from the other in the relevant keymap.
I may add a key theme for this, but I personally prefer to mainly using lispy’s keys, as they are generally more useful than the default evil motions and will keep the region balanced. Evil’s commands can be more useful for editing comments, so I’m personally using the first solution (Using Both Separately) to choose which to use.
I’ve added the main functions behind safe deletion and copying directly to lispy. To have lispy’s commands always act safely on a region, lispy-safe-delete
and lispy-safe-copy
can be set to non-nil values. Lispyville’s functions keep delimiters balanced regardless of these settings.
The options that will affect lispyville’s behavior are lispy-safe-threshold
, lispy-safe-actions-ignore-strings
, and lispy-safe-actions-ignore-comments
. lispy-safe-threshold
is the maximum size a region can be before operators will not try to keep delimiters balanced. The other two options will determine whether operators will attempt to keep delimiters balanced in strings and comments.
There is also a lispy-safe-paste
option that will add missing delimiters to the start and end of pasted text. I’ve yet to make an equivalent paste operator.