Emacs: paired Commands — Efficient Keyboard Interaction Using Hydra
1 Executive Summary
Emacs commands such as yank
and yank-pop
or undo-only
and
undo-redo
come in logical pairs. Such logical pairs (or command
tuples in general) are characterized by the fact that one often
invokes a sequence of these commands repeatedly until the desired
result is attained. Package Hydra defines an elegant means of
combining such command pairs into a single higher-level command that
can be invoked more efficiently from the keyboard.
To my knowledge, the only built-in Emacs command that exhibits a
similar behavior is Emacs' text-scale-adjust
.
1.1 Example: undo-oanly
And undo-redo
Command undo-only
allows one to move back through a sequence of
changes.Command undo-redo
in contrast undoes previous undo
commands.
In practice, one often moves back through a sequence of operations,
with a few undo-redo
invocations mixed in if one moves back too far.
Emacs convention would suggest that one bind these commands to separate keys, but this has a few disadvantages:
- Binding separate keys to the two commands feels wasteful, especially as unbound keys in Emacs become increasingly precious.
- It's not ergonomic to jump between the two commands in this pair
unless one chooses the key-bindings carefully — as an example,
I used C-x u for
undo-only and _C-x C-u
forundo-redo
for many years. But now, a continuous sequence of undo operations requires too much chording.
1.2 A Hydra Inspired Solution
- Combine the two commands into a higher-level command.
- Bind that command to a single key, in my case C-/ (C-_ on a tty).
- When invoked, the Hydra-defined command does one of the following:
- Invokes
undo-only
on being called, then continues to read keys. - Subsequent presses of / invoke command
undo-only
. - Pressing \ invokes
undo-redo
. - Any other key quits the Hydra-generated higher-level command.
- Invokes
This is defined as part of Emacspeak's collection of Muggles, as is evident, this is much nicer to your wrists.
(global-set-key (kbd "C-/") (defhydra emacspeak-muggles-undo-only/undo-redo (:body-pre (emacspeak-hydra-body-pre "Undo Smartly") :pre (progn (when hydra-is-helpful (emacspeak-hydra-toggle-talkative)) (emacspeak-hydra-pre)) :post emacspeak-hydra-post) "Undo" ("?" (emacspeak-hydra-self-help "emacspeak-muggles-undo-only/undo-redo")) ("/" undo-only nil) ("\\" undo-redo nil)))
Note that the above can be simplified by dropping the emacspeak-specific clauses when not using Emacspeak.
1.3 Repeatable Yank/Pop From The Hydra Wiki
I originally discovered the metaphor of repeatable paired commands a few years ago from the Hydra Wiki which demonstrates a repeatable yank/pop. To summarize its effects:
- C-y invokes the hydra-generated higher-level command.
- Subsequent presses of y is the same of
yank-pop
. - Pressing Y invokes
yank-pop
with a negative argument. - Consequence of above, you can grab the last entry on the
kill-ring
by pressing Y after invoking the command — would take a lot more keystrokes in vanilla Emacs. - You can also add smart affordances such as searching the
kill-ring
via interfaces such asido
.
1.4 Conclusion
As Emacs gains in functionality, grouping commands into higher-level abstractions, generating a single higher-level command that is bound to a key, and using that invocation context to implement the result of subsequent keypresses is an abstraction that generalizes well.