Emacs: Build a keychain with repeatable keys

A keychain includes a prefix key (or prefix keybind) and then more keys to launch a command. This is different from a keybind to launch a command. In a keychain, the keys are pressed in sequence, prefix first and then something else to complete the keychain, whereas in a keybind they are pressed at once.

A repeatable key in a keychain allows comfortable repetition of the keychain command. For example, undo in Emacs can be launched with keychain C-x u where u is repeatable. Also zooming, C-x + or C-x -, is repeatable with + or -. However, far from all commands with C-x prefix are repeatable like this.

When devising one’s own collection of miscellaneous key commands, it makes sense to put them under a keychain prefix. For the purposes of this post, the keychain prefix serves two functions:

  1. A reminder “Now I am launching a command that I use a lot or I have devised.”
  2. Ability to make the final key in the keychain repeatable.

My keychain prefix is the F8 key. In the following sample code, the keychain prefix is defined on the last line.

(progn
  (define-prefix-command 'my-f8)
  (define-key my-f8 "w" 'winner-undo)
  (define-key my-f8 "d" 'winner-redo)
  ;; Next key: Pressing F8 twice makes the buffer read-only.
  ;; Make it editable again by pressing e.
  (define-key my-f8 [f8] 'view-mode)
  ;; Next two keys (require 'marker-visit) https://github.com/emacsattic/marker-visit
  (define-key my-f8 [prior] 'marker-visit-prev)
  (define-key my-f8 [next] 'marker-visit-next)
  (global-set-key [f8] my-f8))

In the above keychain definition, F8 starts the keychain, and various commands are completed by other keys. For example, pressing F8 twice enters view-mode i.e. the buffer becomes read-only. It does not make sense to make this command repeatable.

Pressing F8 w does winner-undo i.e. enters into the previous window configuration in Emacs. This command is very much ripe for repetition.

Repeatable keys in Emacs version 28

For example undo and zooming in Emacs have been repeatable keychains a very long time, but in Emacs version 28 (probably) a repeat-mode was implemented that, when activated, puts keys on so-called repeat keymaps. In this new manner, for example the other-window command, that is C-x o, was made repeatable in core Emacs.

When repeat-mode is activated, users can follow up on those repeat keymaps (see desribe-repeat-maps) and make their own personal keychain commands repeatable.

In core Emacs, the relevant code to make o in C-x o repeatable looks as follows.

(defvar other-window-repeat-map
  (let ((map (make-sparse-keymap)))
    (define-key map "o" 'other-window)
    (define-key map "O" (lambda ()
                          (interactive)
                          (setq repeat-map 'other-window-repeat-map)
                          (other-window -1)))
    map)
  "Keymap to repeat `other-window' key sequences.  Used in `repeat-mode'.")
(put 'other-window 'repeat-map 'other-window-repeat-map)

Based on the above example, I have made my winner-mode and marker-visit keys repeatable as follows. (In Emacs configuration files, the following code needs to be loaded after the personal keychain definition.)

(defvar my-f8-winner-repeat-map
   (let ((map (make-sparse-keymap)))
      (define-key map "w" 'winner-undo)
      (define-key map "d" 'winner-redo)
      map)
  "Keymap to repeat `winner-mode' key sequences.")
(put 'winner-undo 'repeat-map 'my-f8-winner-repeat-map)
(put 'winner-redo 'repeat-map 'my-f8-winner-repeat-map)
(defvar my-f8-marker-repeat-map
  (let ((map (make-sparse-keymap)))
    (define-key map [prior] 'marker-visit-prev)
    (define-key map [next] 'marker-visit-next)
    map)
  "Keymap to repeat `marker-visit' key sequences.")
(put 'marker-visit-prev 'repeat-map 'my-f8-marker-repeat-map)
(put 'marker-visit-next 'repeat-map 'my-f8-marker-repeat-map)

Repeatable keys in Emacs version 29

Emacs’ repeat-mode has undergone an evolution in version 29 and the relevant code to make o in C-x o repeatable now looks as follows.

(defvar-keymap other-window-repeat-map
   :doc "Keymap to repeat `other-window'.  Used in
   `repeat-mode'."
   :repeat t
   "o" #'other-window
   "O" (lambda ()
     (interactive)
     (setq repeat-map 'other-window-repeat-map)
     (other-window -1)))

Based on the above example, the winner-mode and marker-visit keys in my F8 keychain become repeatable in Emacs version 29 as follows.

(defvar-keymap my-f8-winner-repeat-map
   :doc "Keymap to repeat `winner-mode' key sequences."
   :repeat t
   "w" 'winner-undo
   "d" 'winner-redo)
(defvar-keymap my-f8-marker-repeat-map
   :doc "Keymap to repeat `marker-visit' key sequences."
   :repeat t
   "<prior>" 'marker-visit-prev
   "<next>" 'marker-visit-next)

Universal repeatable solution: the Repeat command

However, the entire coding exercise above can be avoided by using the repeat command, which has been inbuilt since forever, so it works with all Emacs versions. The keybind for repeat is C-x z where z is repeatable.

The workflow to repeat any command looks as follows:

  1. Trigger an interactive command (does not matter if by a keybind, keychain, M-x, M-: or C-x M-:)
  2. To repeat the latest command, press C-x z
  3. Repeat it again and again ad nauseum by z z z z z…

And the repeat keybind works without repeat-mode. It is worth emphasising that the code further above requires repeat-mode to be activated.

Leave a Reply

Your email address will not be published. Required fields are marked *