Wednesday, September 16, 2020

Emacs: Paired Commands --- Efficient Keyboard Interaction Using Hydra

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 for undo-redo for many years. But now, a continuous sequence of undo operations requires too much chording.

1.2 A Hydra Inspired Solution

  1. Combine the two commands into a higher-level command.
  2. Bind that command to a single key, in my case C-/ (C-_ on a tty).
  3. When invoked, the Hydra-defined command does one of the following:
    1. Invokes undo-only on being called, then continues to read keys.
    2. Subsequent presses of / invoke command undo-only.
    3. Pressing \ invokes undo-redo.
    4. Any other key quits the Hydra-generated higher-level command.

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:

  1. C-y invokes the hydra-generated higher-level command.
  2. Subsequent presses of y is the same of yank-pop.
  3. Pressing Y invokes yank-pop with a negative argument.
  4. 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.
  5. You can also add smart affordances such as searching the kill-ring via interfaces such as ido.

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.