Mnemonic keymaps
First published .Where have all the good keys gone?
It was not long into my own personal Emacs saga when I ran into a
problem: finding available keys for my personal keybindings. Every good
and reasonable binding seemed to already be used up, at least in some
mode or other that I use. For a brief while I resorted to the function
keys and painful C-M-S-Super-Hyper-whatever
combinations. But
that quickly became unsustainable.
It was however another few years before I discovered a solution that I am happy with: mnemonic keymaps. That is, making your own nested keymaps with simple letters that are easy to remember. A variety of newer packages have suggested or encouraged this idea over the last ten years, and I don't claim to have invented it; but so far, I haven't seen anyone else give it a name and explicitly recommend it as a strategy for making your own keybindings.
Inspirations
The idea of mnemonic keymaps seems obvious in retrospect, but I came to it gradually, after learning a few more things in the intervening years.
From evil I learned
about the benefits of modal editing: composing sequences of keys,
especially normal letter keys, can be a powerful and intuitive way to
operate on text. Evil's normal-mode keys are often mnemonic: d w
deletes a word, d a "
deletes around a quotation, d i (
deletes inside parentheses, and so on.
There's a single, easy to remember key for "delete" (d
) and then a whole range of other ways to
describe what you want to delete, equally easy to remember; and
so on for other kinds of operations.
But of course, this only works if you have mentally escaped from the
normal Emacs paradigm of one-key-means-one-editing-command, and
especially the idea that an unmodified letter key should run self-insert-command
. Evil does this by borrowing
vim's concept of modes (called "states" in Evil so as not to be confused
with Emacs' major and minor modes). You need some kind of idea of
"modes" or "states", even if not the evil ones, to make use of this
idea.
Using magit reinforced the idea of
composition of memorable keys. Magit's system of transient menus, activated
in sequence when you press a single key, is one of the things that makes
magit feel
like magic. In magit, key sequences group related functions
together: everything under b
has to do
with branches, everything under P
has to
do with pushing, everything under r
with
reverting, etc.
Somewhere along the way, I also learned that the C-c [a-zA-Z]
keys are reserved
for users. I hadn't learned this early on and only discovered it
when someone complained on the Org mailing list that Org shouldn't use
those reserved keys. (RTFM, kids! It's long but it's good.)
Making your own mnemonic keymaps
The idea of mnemonic keymaps is simple: you group related functions into a single keymap, binding them to simple letters that help you remember them, and then bind the whole keymap to a single letter in the user-reserved key range.
For example, my 'mail' keymap is bound to C-c m
, and contains the following keys:
c
("compose"):compose-with-sender
, creates a buffer to compose an emailf
("fetch"):fetch-mail
, a custom function to download my emailm
("mail"):start-notmuch-at-inbox
, opens notmuch, my email interface
Thus I type C-c m c
to compose a new
email, C-c m f
to fetch email, and so
on.
The C-c
prefix transports us out of the
self-insert-mode
hegemony and into a tiny
"mode" for all things mail-related, freeing us up to use single,
easy-to-remember letter combinations to do what we want. Simple,
beautiful, and it works across all major modes (so long as they respect
the user-reserved keys). I have various other maps that use the same
idea:
C-c f
("files") for special files that I visit oftenC-c r
("reading") for reading notes and associated filesC-c z
("zettel") for my own Zettelkasten-like systemC-c b
("bullseye") for my targeted yanking functions
The great thing about this is that there's no limit: because keymaps
can be nested, you can apply this strategy recursively. This means
that the space of possible combinations is huge. Even though there are
only 52 keys reserved for you at the top level under C-c
, you'll never run out of easily-memorable
keys if you're careful about how you organize them.
So how do we do this concretely?
Create a keymap
The first step is to create a new keymap. You do this with the make-sparse-keymap
function. For example, here's
the definition of my mail keymap:
(defvar rwl-mail-map
(make-sparse-keymap)
"Keymap for mail-related functions")
Bind the keymap to a user-reserved key
Next, we want to make the new keymap accessible from a user-reserved
key using define-key
:
(define-key (current-global-map) (kbd "C-c m") rwl-mail-map)
I'm doing this here with the kbd
function to make it obvious what the top-level binding is, but if you
know that C-c
is actually bound to mode-specific-map
, you could also do it like
this:
(define-key mode-specific-map "m" rwl-mail-map)
Bind functions into the new keymap
Finally, we add bindings in our new keymap for all the functions we
want to group together, again using define-key
. Here for example are two of the
bindings in my mail keymap:
(define-key rwl-mail-map "m" 'start-notmuch-at-inbox)
(define-key rwl-mail-map "c" 'compose-with-sender)
Notice that we are not binding the functions directly here, but using their quoted names. That is, the binding is to a symbol. When Emacs finds a symbol at the end of a key sequence, it calls the function associated with that symbol.
In contrast, when we bound the new keymap into mode-specific-map
above, we did not
quote its name, but instead bound the key directly to the new keymap
value. When Emacs finds a keymap value at the end of a key sequence, it
will look up the next key in that keymap.
See the documentation for define-key
(C-h f define-key
) for more.
A nice freebie
Recent versions of Emacs have added a nice bit of polish: pressing
?
in a keymap where it is not bound will
automatically call describe-keymap
on that
keymap. So if you ever forget what you've put into your keymap, you can
use C-c /k/ ?
(for whatever key /k/
you've bound it to) to get an overview of
your keymap.
Update: using define-keymap
If you're using Emacs 29 or later, there's another option, which I
recently learned about thanks to this
newsletter: instead of the combination of defvar
, make-sparse-keymap
and define-key
I used above, you can use the new
macro define-keymap
to simplify the
declaration. The equivalent definition looks like this:
(global-set-key
(kbd "C-c m")
(define-keymap
:prefix 'rwl-mail-map
"c" 'compose-with-sender
"f" 'fetch-mail
"m" 'start-notmuch-at-inbox))
The (to me, nonobvious) :prefix
keyword
here must be "a symbol to be used as a prefix command", and if given,
the map will be stored as the function value of the symbol. Thus this
effectively says: bind the global key C-c m
to the command rwl-mail-map
, the value of which is the created
keymap.