4

Specifically, the keyboard event C-x, following by N z characters, will repeat the previous command N times.

I do not understand how this can work. As far as I understand, after the input event C-x z, for the behavior to be as specified, Emacs would need to somehow remap the z character to the command repeat in order for the next z to actually again execute `repeat'. Is this correct? If so, I do not understand how Emacs can change the keymap immediately after executing a command. If not, what is going on?

5

The code is in repeat.el. See command repeat, which is what C-x z is bound to.

The code uses the fact that Emacs records the current command and the last command in variables. The repeat code resets the variable this-command to what it records as the command that was previous to command repeat.

Note that you can define your own repeatable command by using code such as this:

(defun my-repeat-command (command)
  "Repeat COMMAND."
  (require 'repeat)
  (let ((repeat-previous-repeated-command  command)
        (repeat-message-function           #'ignore)
        (last-repeatable-command           'repeat))
    (repeat nil)))

(defun some-command (...)
  (interactive...)
  ...)

(defun some-command-repeat ()
  "Invoke `some-command' in a repeatable way."
  (interactive)
  (my-repeat-command 'some-command))

repeat does kind of "remap the key" on the fly. It uses set-transient-map, defining the key in a transient map, and then it calls itself again.

Prior to Emacs 24 it didn't use a new (transient) keymap (such a thing didn't exist as such back then). It just checked whether the next key/command was the same as the last.

Essentially, repeat fools Emacs into thinking that the current command is whatever command was used last.

  • 1
    Looking at the repeat defun, it also uses set-transient-map to allow repeated taps of the last character in the binding to continue to repeat. – glucas Jan 7 at 1:46
  • @glucas: Yes. I edited the answer to be clearer about this. A transient map wasn't used originally. Using it is cleaner that what was done originally, but it's not a necessary part of the general approach behind repeat. – Drew Jan 7 at 5:17
3

As you guessed, for this to work, the repeat command needs to be able to "change the keymap" immediately after executing a command. And indeed, repeat ends with a call to set-transient-map which makes a new keymap active (rather than modify in-place one of the currently active keymaps, it works by changing the set of active keymaps) with a binding for z which shadows the normal binding of z. This keymap needs to be short-lived, so set-transient-map internally uses pre-command-hook to deactivate that special keymap as soon as you hit something else than z.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.