Evil Collection
This is a collection of Evil bindings for the parts of Emacs that Evil does
not cover properly by default, such as help-mode
, M-x calendar
, Eshell and
more.
Warning: Expect some default bindings to change in the future.
Preliminaries
evil-overriding-maps
is assumed asnil
to reduce redundantw/W/l/f/t
etc evil bindings. See Fixup Info-mode for example.
Goals
- Reduce context switching: As soon as āmoving aroundā gets hardwired
to
<hjkl>
, it becomes frustratingly inefficient not to have it everywhere. - Community work: setting up bindings is tremendous work and joining force can only save hours for all of Evil users out there. While not everyone may agree on the chosen bindings, it helps to have something to start with rather than nothing at all. In the end, users are free to override a subset of the proposed bindings to best fit their needs.
- Consistency: Having all bindings defined in one place allows for enforcing consistency across special modes and coordinating the community work to define a reference implementation.
Installation
- Get the package, either from MELPA:
M-x package-install RET evil-collection RET
Or clone / download this repository and modify your
load-path
:(add-to-list 'load-path (expand-file-name "/path/to/evil-collection/" user-emacs-directory))
- Register the bindings, either all at once with
(evil-collection-init)
or mode-by-mode, for instance:
(with-eval-after-load 'calendar (evil-collection-calendar-setup))
or by providing an argument to
evil-collection-init
:(evil-collection-init 'calendar)
a list can also be provided to
evil-collection-init
:(evil-collection-init '(calendar dired calc ediff))
The list of supported modes is configured by
evil-collection-mode-list
.
evil-collection
assumes evil-want-keybinding
is set to nil
and
evil-want-integration
is set to t
before loading evil
and
evil-collection
. Note some other packages may load evil (e.g. evil-leader) so
bear that in mind when determining when to set the variables.
See #60 and emacs-evil/evil#1087 for more details.
For example:
(setq evil-want-integration t) ;; This is optional since it's already set to t by default.
(setq evil-want-keybinding nil)
(require 'evil)
(when (require 'evil-collection nil t)
(evil-collection-init))
Hereās another full TLDR use-package
example.
(use-package evil
:ensure t
:init
(setq evil-want-integration t) ;; This is optional since it's already set to t by default.
(setq evil-want-keybinding nil)
:config
(evil-mode 1))
(use-package evil-collection
:after evil
:ensure t
:config
(evil-collection-init))
NOTE: If you donāt like surprises but still want to use evil-collection-init
, setting evil-collection-mode-list
to nil
and adding each mode manually might be a better option.
Configuration
Modify evil-collection-mode-list
to disable or add any modes that should be
evilified by evil-collection
.
Variable | Default | Description |
---|---|---|
evil-collection-calendar-want-org-bindings | nil | Set up Org functions in calendar keymap. |
evil-collection-outline-bind-tab-p | nil | Enable <tab>-based bindings in Outline mode. |
evil-collection-term-sync-state-and-mode-p | t | Synchronize insert/normal state with char/line-mode in term-mode. |
evil-collection-setup-minibuffer | nil | Set up Vim style bindings in the minibuffer. |
evil-collection-setup-debugger-keys | t | Set up debugger keys for certain modes. |
evil-collection-want-unimpaired-p | t | Set up unimpaired bindings globally. |
evil-collection-want-find-usages-bindings | t | Bind -find references-, etc to various modes. |
evil-collection-config | * | List of mode specific configurations. |
evil-collection-key-whitelist | nil | List of keys Evil Collection is allowed to bind to. |
evil-collection-key-blacklist | nil | List of keys Evil Collection is not allowed to bind to. |
evil-collection-state-passlist | nil | List of Evil States Evil Collection is allowed to bind to. |
evil-collection-state-denylist | nil | List of Evil States Evil Collection is not allowed to bind to. |
For example, if you want to enable Evil in the minibuffer, youāll have to turn
it on explicitly by customizing evil-collection-setup-minibuffer
to t
. Some
minibuffer-related packages such as Helm rely on this option.
use-package
example:
(use-package evil-collection
:custom (evil-collection-setup-minibuffer t)
:init (evil-collection-init))
evil-collection-config
can also be modified to configure specific modes.
At the moment, it can be used to defer binding keys to those specific modes
in order to improve startup time.
Guidelines
The following rules serve as guiding principles to define the set of standard Evil bindings for various modes. Since special modes are by definition structurally incomparable, those rules cannot be expected to be applied universally.
The rules are more-or-less sorted by priority.
- Donāt bind anything to
:
nor<escape>
. - Keep the movement keys when possible and sensible.
h
,j
,k
,l
w
,W
,b
,B
,e
,E
,ge
,gE
f
,F
,t
,T
,;
, =,=gg
,G
|
(
,)
{
,}
%
+
,-
,0
,^
,$
C-i
,C-o
- Keep the yanking and register keys when possible and sensible.
y
,Y
- =ā=
- Keep the search keys when possible and sensible.
/
,?
#
,*
- Keep the mark keys when possible and sensible.
m
- =ā=,
~
- Keep the windowing keys when possible and sensible.
H
,L
,M
C-e
,C-y
C-f
,C-b
C-d
,C-u
C-w
-prefixed bindings.- Some
z
-prefixed bindings (see below).
- The following keys are free when insert state does not make sense in the
current mode:
a
,A
,i
,I
c
,C
,r
,R
,s
,S
d
,D
,x
,X
o
,O
p
,P
=
,<
,>
J
~
Any of those keys can be set to be a prefix key.
- Prefix keys:
g
andz
are the ubiquitous prefix keys.g
generally stands for āgoā and is best used for movements.z
is used for scrolling, folding, spell-checking and more.
- Macro and action keys
@
,q
.
- Ensure terminal compatibility without sacrificing GUI key bindings.
- Tab key
- Tab key is recognized as
<tab>
in GUI andTAB
in terminal.TAB
equalsC-i
. C-i
is bound to jumping forward for vim compatibility. If Shift+Tab is not relevant, just bindg TAB
to the function that Tab is bound to. If Shift+Tab is relevant, bindg]
andg TAB
to the function that Tab is bound to, and bindg[
to the function that Shift+Tab is bound to for terminal compatibility.
- Tab key is recognized as
- Enter key
- Enter key is recognized as
<return>
in GUI andRET
in terminal.RET
equalsCtrl+m
. - Bind only
RET
andM-RET
. Or, bindRET
andM-RET
to the same functions<return>
and<M-return>
are bound to. S-RET
is impossible on terminal. Bind<S-return>
and a vacant key to the same function for terminal compatibility.
- Enter key is recognized as
- Tab key
Rationale
Many special modes share the same set of similar actions. Those actions should share the same bindings across all modes whenever feasible.
[
, ]
, {
, }
, (
, )
, gj
, gk
, C-j
, C-k
)
Motion ([
and]
: Use[-
and]-
prefixed keys for navigation between sections.If the mode makes no difference between the end of a section and the beginning of the next, use
[
and]
.gj
andgk
: synonym for[
and]
. Thatās what evil-magit does.Question: Should
gj
/gk
rather be synonyms forC-j
/C-k
? They cannot emulate the behaviour of[]
or][
.C-j
,C-k
: If there is granularity, i.e. subsections, useC-j
andC-k
to browse them. This reflects evil-magit and evil-mu4e default bindings.{
,}
: If there is no paragraph structure,{
and}
can be used for sub-sectioning.(
,)
: If there is no sentence structure,(
and)
can be used for sub-sectioning.HJKL
:hjkl
can be used for atomic movements, butHJKL
can usually not be used becauseH
,K
andL
are all universal (J
isevil-join
and usually does not make sense in special modes).C-h
should not be remapped: Since we haveC-j
andC-k
for vertical motion, it would make sense to useC-h
andC-l
for horizontal motion. There are some shortcomings though:- In Vim,
C-h
works as backspace, but Evil does not follow that behaviour. - In Emacs, it is a prefix key for all help-related commands, and so is
<f1>
. - Most importantly,
C-h
is too widespread and ubiquitous to be replaced. So we donāt.
- In Vim,
C-l
: As a consequence of the former point,C-l
is available.M-<hjkl>
: Those keys are usually free in Evil but still bound to their Emacs default (e.g.M-l
isdowncase-word
). Besides, ifC-j
andC-k
are already used, havingM-j
andM-k
might add up to the confusion.
q
, ZQ
, ZZ
)
Quitting (In Vim, q
is for recording macros. Vim quits with ZZ
or ZQ
. In most
Emacs special modes, it stands for quitting while macros are recorded/played
with <f3>
and <f4>
.
A good rule of thumb would be:
- Always bind
q
,ZZ
andZQ
to the mode specific quitting functions. If there is none, - Bind
q
andZZ
toquit-window
- Bind
ZQ
toevil-quit
- If macros donāt make sense in current mode, then
@
is available.
gr
)
Refreshing / Reverting (gr
is used for refreshing in evil-magit, evil-mu4e, and some Spacemacs configurations (org-agenda and neotree among others).C-l
is traditionally used to refresh the terminal screen. Since there does not seem to be any existing use of it, we leave the binding free for other uses.
Marking
m
defaults to evil-set-marker
which might not be very useful in special
modes.
=ā= can still be used as it can jump to other buffers.
m
: Mark or toggle mark, depending on what the mode offers. In visual mode, always mark. With a numeric argument, toggle mark on that many following lines.u
: Unmark current selection.U
: Unmark all.~
: Toggle all marks. This mirrors the āinvert-charā Vim command bound to~
by default.M
: Mark all, if available. Otherwise useU~
.*
: Mark-prefix or mark all if current mode has no prefix.*
is traditionally a wildcard.%
: Mark regexp.x
: Execute action on marks. This mirrors Diredās binding ofx
.
If *
is used for marking, then #
is free.
Also note that Emacs inconsistently uses u
and U
to unmark.
Selecting / Filtering / Narrowing / Searching
s
andS
seem to be used in some places like mu4e.s
: [s]elect/[s]earch/filter candidates according to a pattern.S
: Remove filter and select all.
=
is usually free and its significance is obvious. Itās taken for zooming though.|
is not free but the pipe symbolic is very tantalizing.
Sorting
o
: Change the sort [o]rder.O
: Sort in reverse order.There is no real consensus around which key to bind to sorting. What others do by default:
gd
, gD
)
Go to definition (gd
: [g]o to [d]efinition. This is mostly for programming modes. If thereās a corresponding āpopā action, useC-t
.
gr
, gA
)
Go to references, etc (When evil-collection-want-find-usages-bindings
is set to t:
gr
: [g] to [r]eferences. This binding is also used for refresh/reverting modes in non programming modes but is usually empty for programming modes.gA
: [g]o to [A]ssignments.- Additional bindings: There may be additional binds under this category. Please file a Pull Request if so.
Go to current entity
.
: go to current entity (day for calendar, playing track for EMMS). Bind only if more relevant thanevil-repeat
.
RET
, S-RET
, M-RET
, go
, gO
)
Open thing at point (RET
,S-RET
,M-RET
: Open thing at point in current window, open in other window and display in other window respectively. The latter is like the former with the focus remaining on the current window.go
,gO
: When available, same asS-RET
andM-RET
respectively. This is useful in terminals whereS-RET
andM-RET
might not work.
J
)
Emacs-style jumping (J
: mu4e hasj
and evil-mu4e usesJ
, so we useJ
too.Some special modes like mu4e and ibuffer offer to ājumpā to a different buffer. This sometimes depends on the thing at point.
This is not related to Evil jumps like
C-i
andC-o
, nor to āgo to definitionā.
gx
)
Browse URL (gx
: go to URL. This is a default Vim binding.
?
)
Help (g?
: is the standard key for help related commands.?
in places where backward search is not very useful.
C-n
, C-p
)
History browsing (C-n
and C-p
are standard bindings to browse the history elements.
Bookmarking
?
gz
)
REPL (If the mode has a Go To REPL-type command, set it to gz
.
+
, -
, =
, 0
)
Zooming (+
and-
have obvious meanings.0
has a somewhat intuitive meaning, plus it is next to+
and-
on QWERTY.=
is useful as a synonym for+
because it is the unshifted key of+
on QWERTY.
Debugging
When debugging is on, debugger keys takes the most precedence.
These keys will be set when thereās an available command for them.
n
: Step Overi
: Step Intoo
: Step Outc
: Continue/Resume ExecutionL
: Localst
: Tracingq
: Quit DebuggingH
: Continue until Pointe
: Evaluate Expressionb
: Set Breakpointu
: Unset Breakpoint>
: Navigate to Next Frame<
: Navigate to Previous Frameg?
: HelpJ
: Jump to debugger locationR
: Restart
For debugging outside of debugger being on (e.g. setting initial breakpoints), we use similar keys to realgud.
f5
Start/Continue/Resume ExecutionS-f5
Continue ExecutionMouse-1
Toggle Breakpointf9
Toggle Breakpointf10
Step Overf11
Step IntoS-f11
Step Out
Editable Buffers
For buffers where insert-state doesnāt make sense but buffer can be edited,
(e.g. wdired or wgrep), pressing i
will change into editable state.
When this editable state is turned on,
ZQ
will abort and clear any changes.
ZZ
will finish and save any changes.
ESC
will exit editable state.
:q/:wq/etc
Modes with commands that can be bound to :q/:wq/etc will have those keys remapped.
Key Translation
evil-collection-translate-key
allows binding a key to the definition of
another key in the same keymap (comparable to how Vimās keybindings work). Its
arguments are the states
and keymaps
to bind/look up the key(s) in followed
optionally by keyword arguments (currently only :destructive
) and
key/replacement pairs. states
should be nil for non-evil keymaps, and both
states
and keymaps
can be a single symbol or a list of symbols.
This function can be useful for making key swaps/cycles en masse. For example,
someone who uses an alternate keyboard layout may want to retain the hjkl
positions for directional movement in dired, the calendar, etc.
Hereās an example for Colemak of making swaps in a single keymap:
(evil-collection-translate-key nil 'evil-motion-state-map
;; colemak hnei is qwerty hjkl
"n" "j"
"e" "k"
"i" "l"
;; add back nei
"j" "e"
"k" "n"
"l" "i")
Hereās an example of using evil-collection-setup-hook
to cycle the keys for
all modes in evil-collection-mode-list
:
(defun my-hjkl-rotation (_mode mode-keymaps &rest _rest)
(evil-collection-translate-key 'normal mode-keymaps
"n" "j"
"e" "k"
"i" "l"
"j" "e"
"k" "n"
"l" "i"))
;; called after evil-collection makes its keybindings
(add-hook 'evil-collection-setup-hook #'my-hjkl-rotation)
(evil-collection-init)
A more common use case of evil-collection-translate-key
would be for keeping
the functionality of some keys that users may bind globally. For example, SPC
,
[
, and ]
are bound in some modes. If you use these keys as global prefix
keys that you never want to be overridden, youāll want to give them higher
priority than other evil keybindings (e.g. those made by (evil-define-key
'normal some-map ...)
). To do this, you can create an āinterceptā map and bind
your prefix keys in it instead of in evil-normal-state-map
:
(defvar my-intercept-mode-map (make-sparse-keymap)
"High precedence keymap.")
(define-minor-mode my-intercept-mode
"Global minor mode for higher precedence evil keybindings."
:global t)
(my-intercept-mode)
(dolist (state '(normal visual insert))
(evil-make-intercept-map
;; NOTE: This requires an evil version from 2018-03-20 or later
(evil-get-auxiliary-keymap my-intercept-mode-map state t t)
state))
(evil-define-key 'normal my-intercept-mode-map
(kbd "SPC f") 'find-file)
;; ...
You can then define replacement keys:
(defun my-prefix-translations (_mode mode-keymaps &rest _rest)
(evil-collection-translate-key 'normal mode-keymaps
"C-SPC" "SPC"
;; these need to be unbound first; this needs to be in same statement
"[" nil
"]" nil
"[[" "["
"]]" "]"))
(add-hook 'evil-collection-setup-hook #'my-prefix-translations)
(evil-collection-init)
By default, the first invocation of evil-collection-translate-key
will make a
backup of the keymap. Each subsequent invocation will look up keys in the backup
instead of the original. This means that a call to
evil-collection-translate-key
will always have the same behavior even if
evaluated multiple times. When :destructive t
is specified, keys are looked up
in the keymap as it is currently. This means that a call to
evil-collection-translate-key
that swapped two keys would continue to
swap/unswap them with each call. Therefore when :destructive t
is used, all
cycles/swaps must be done within a single call to
evil-collection-translate-key
. To make a comparison to Vim keybindings,
:destructive t
is comparable to Vimās map
, and :destructive nil
is
comparable to Vimās noremap
(where the āoriginalā keybindings are those that
existed in the keymap when evil-collection-translate-key
was first called).
Youāll almost always want to use the default behavior (especially in your init
file). The limitation of :destructive nil
is that you canāt translate a key to
another key that was defined after the first evil-collection-translate-key
, so
:destructive t
may be useful for interactive experimentation.
evil-collection-swap-key
is also provided as a wrapper around
evil-collection-translate-key
that allows swapping keys:
(evil-collection-swap-key nil 'evil-motion-state-map
";" ":")
;; is equivalent to
(evil-collection-translate-key nil 'evil-motion-state-map
";" ":"
":" ";")
In some cases, keys are bound through `evil-define-minor-mode-key` and may
need to be translated using evil-collection-translate-minor-mode-key
and/or
evil-collection-swap-minor-mode-key
.
(evil-collection-swap-minor-mode-key '(normal motion)
'(evil-snipe-local-mode evil-snipe-override-local-mode)
"k" "s"
;; Set this to t to make this swap the keys everytime
;; this expression is evaluated.
:destructive nil)
(evil-collection-translate-minor-mode-key
'(normal motion)
'(evil-snipe-local-mode evil-snipe-override-local-mode)
"k" "s"
"s" "k"
;; Set this to t to make this swap the keys everytime
;; this expression is evaluated.
:destructive nil)
Third-party packages
Third-party packages are provided by several parties:
Major mode | Evil bindings |
---|---|
ledger | evil-ledger |
lispy | lispyville or evil-lispy |
org | org-evil or evil-org |
markdown | evil-markdown |
Also evil-collection
has minimal support (TAB
, S-TAB
and
sentence/paragraph forwarding) for markdown
and org
if you prefer less
packages installed.
Should you know any suitable package not mentioned in this list, let us know and file an issue.
Other references:
FAQ
spacemacs.
Making SPC work similarly toevil-collection
binds over SPC in many packages. To use SPC as a leader key
with the general library:
(use-package general
:ensure t
:init
(setq general-override-states '(insert
emacs
hybrid
normal
visual
motion
operator
replace))
:config
(general-define-key
:states '(normal visual motion)
:keymaps 'override
"SPC" 'hydra-space/body))
;; Replace 'hydra-space/body with your leader function.
See noctuidās evil guide for other approaches.
- Unintialized mode maps in
evil-collection-setup-hook
.evil-collection-setup-hook
is ran with a list of keymaps passed into it. Some misconfigured modes may not have yet initialized their keymap at this time so the value of the variable may be nil. In that case, an alternative is to use a mode-hook to do any custom settings.
(add-hook 'evil-collection-setup-hook
(lambda (_mode keymaps)
(add-hook 'ediff-mode-hook
(lambda ()
(... keymaps ...)))))
View 196 for more info.
Donāt allow Evil-Collection to bind some keys.
Look into evil-collection-key-whitelist
and evil-collection-key-blacklist
.
For example:
;; Don't allow Evil Collection to bind to gfu and gfp.
(setq evil-collection-key-blacklist '("gfu" "gfp"))
Modes left behind
Some modes might still remain unsupported by this package. Should you be missing
your <hjkl>
, please feel free to do a pull request.
Writing a new binding
This yasnippet template can be used to bootstrap a new binding.
For example, if we were to want to add evil-collection
support to eldoc
.
(e.g.) There is a package that contains:
(provide 'eldoc)
Create a directory named eldoc under modes/. Create a file
named evil-collection-eldoc.el under the newly created eldoc directory. Then use
the above template as an example or, using yasnippet,
yas-expand
the above template which will result in something like below:
;;; evil-collection-eldoc.el --- Bindings for `eldoc' -*- lexical-binding: t -*-
;; Copyright (C) 2022 James Nguyen
;; Author: James Nguyen <[email protected]>
;; Maintainer: James Nguyen <[email protected]>
;; URL: https://github.com/emacs-evil/evil-collection
;; Version: 0.0.2
;; Package-Requires: ((emacs "27.1"))
;; Keywords: evil, emacs, convenience, tools
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;; Bindings for eldoc.
;;; Code:
(require 'evil-collection)
(require 'eldoc nil t)
(defvar eldoc-mode-map)
(defconst evil-collection-eldoc-maps '(eldoc-mode-map))
(defun evil-collection-eldoc-setup ()
"Set up `evil' bindings for eldoc."
(evil-collection-define-key 'normal 'eldoc-mode-map
))
(provide 'evil-collection-eldoc)
;;; evil-collection-eldoc.el ends here
Finally, add eldoc
to evil-collection--supported-modes
.
(defvar evil-collection--supported-modes
`(
;; ...
eldoc
;; ...
)
"List of modes supported by evil-collection. Elements are
either target mode symbols or lists which `car' is the mode
symbol and `cdr' the packages to register.")
Submitting Issues
When reproducing issues, you can use this emacs -Q recipe.
(setq user-emacs-directory "~/.emacs.1.d")
(setq package-user-dir
(format "%s/elpa/%s/" user-emacs-directory emacs-major-version))
(setq package-enable-at-startup nil
package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "http://elpa.gnu.org/packages/")))
(require 'package)
(package-initialize)
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
(use-package evil
:ensure t
:init
(setq evil-want-keybinding nil)
:config
(evil-mode 1))
(use-package evil-collection
:after evil
:ensure t
:config
(evil-collection-init))
Contributing
We welcome any additional modes that are not already supported.
All bindings in evil-collection
are open to change so if thereās a better or
more consistent binding, please
open an issue or
submit a pull request.
Follow The Emacs Lisp Style Guide for coding conventions.
Erlang/OTP has a good read for helpful commit messages.