7

I want to set C-c j with such a function (called cmd), which can handle subsequent keystrokes.

For example, after typing C-c j Emacs will execute cmd, then every subsequent j will invoke cmd again.
Any non-j keystroke will interrupt this process. It won't invoke cmd; instead, it acts like a simple keystroke without the prefix C-c.

Another example is S-<arrow-key>:
11.6 Shift Selection:

If you set shift-select-mode to the value permanent, cursor motion keys that were not shift-translated will not deactivate the mark, so, for example, the region set by prior commands can be extended by shift-selection, and unshifted cursor motion keys will extend the region set by shift-selection.

6
  • Is the purpose of it to save one intermediate C-x z key sequence which would do the same by repeating z?
    – Claudio
    Apr 1, 2023 at 22:24
  • @Claudio: For the 1st example, yes, surely it saves an intermediate C-x z; for the 2nd one, in addition to omitting that key sequence, it distributes subsequent keystrokes to a set of relevant commands. Or, the functionality shown in the 2nd example can meet the need in the 1st example. I think I didn't make it clear enough in the question description, sorry for that ... But, anyway, answers providing either the first or second functionality is useful; they're helpful to the person who browses the related topic.
    – shynur
    Apr 2, 2023 at 9:13
  • Sorry, I don't understand what you try to say about the other example. In my eyes it would make sense to ask for a way you can choose after preliminary C-x j from a list of keys (for example: j, k, l, m, q) to have a set of commands you can choose from by using only one key press. This would require implementation of an explicit certain key which quits this behavior (preferably the q from the example list) instead of quitting by any key not in the list. Are you asking for a way making C-x added as preliminary to each typed key? But then you need a way to quit, "go out" of this behavior.
    – Claudio
    Apr 2, 2023 at 9:58
  • 2
    Is it maybe that you ask two questions in one? What about describing in detail the expected behavior you ask finally for along with code of your failed attempt of achieving it? This would make it much easier to give an answer making it also possible to unambiguously determine if an answer answers the question.
    – Claudio
    Apr 2, 2023 at 10:14
  • 1
    Yes, phils accepted answer addresses the first part of your question and the other one addresses the means you need to create self-designed key sequences of any length triggering execution of any self-written elisp code.
    – Claudio
    Apr 2, 2023 at 12:04

3 Answers 3

5

Starting from Emacs 28.1:

** New mode 'repeat-mode' to allow shorter key sequences.
Type 'M-x repeat-mode' to enable this mode.  You can then type
'C-x u u' instead of 'C-x u C-x u' to undo many changes, 'C-x o o'
instead of 'C-x o C-x o' to switch windows, 'C-x { { } } ^ ^ v v' to
resize the selected window interactively, 'M-g n n p p' to navigate
next-error matches.  Any other key exits this temporarily enabled
transient mode that supports shorter keys, and then after exiting from
this mode, the last typed key uses the default key binding.

The user option 'repeat-exit-key' defines an additional key usable to
exit the mode like 'isearch-exit' ('RET').

The user option 'repeat-exit-timeout' (default nil, which means
forever) specifies the number of seconds of idle time after which to
break the repetition chain automatically.

When user option 'repeat-keep-prefix' is non-nil, the prefix arg of
the previous command is kept.  This can be used to e.g. reverse the
window navigation direction with 'C-x o M-- o o' or to set a new step
with 'C-x { C-5 { { {', which will set the window resizing step to 5
columns.

Command 'describe-repeat-maps' will display a buffer showing
which commands are repeatable in 'repeat-mode'.

Enable repeat-mode and then use M-x describe-repeat-maps to see existing uses.

To create your own, add a repeat-map property to the symbol of the command in question. Here's an example from bindings.el which implements this for the forward/back page commands which are bound to C-x[ and C-x]:

(defvar page-navigation-repeat-map
  (let ((map (make-sparse-keymap)))
    (define-key map "]" #'forward-page)
    (define-key map "[" #'backward-page)
    map)
  "Keymap to repeat page navigation key sequences.  Used in `repeat-mode'.")

(put 'forward-page 'repeat-map 'page-navigation-repeat-map)
(put 'backward-page 'repeat-map 'page-navigation-repeat-map)
5

I think you're saying that you want C-c j to be bound to a repeatable command, repeatable in the sense that you can just hold down a key (thus repeating it) to repeat the command.

There are several ways to do this -- you'll likely get several answers. I like this simple way. Define a function that defines a repeatable version of any command, then use that to define any number of repeatable commands.

(defun repeat-command (command)
  "Repeat COMMAND."
  (require 'repeat)       ; Define its vars before we let-bind them.
  (let ((repeat-previous-repeated-command  command)
        (repeat-message-function           #'ignore)
        (last-repeatable-command           'repeat))
    (repeat nil)))

Then define a repeatable version of any existing command some-command, this way:

(defun some-command+ ()
  "Repeatable version of command `some-command`.
You can repeat this by hitting the last key again..."
  (interactive)
  (require 'repeat)
  (repeat-command 'some-command))

If the original (nonrepeatable) CMD accepts an optional prefix arg, then just provide for that in the definition of the repeatable command, using (interactive "P"), etc.

This approach, by the way, works with any Emacs version, starting with Emacs 22 (which is when repeat.el was added to Emacs).

3
  • Is the purpose of this above to save one intermediate C-x z key sequence which would do the same by repeating z?
    – Claudio
    Apr 1, 2023 at 22:19
  • @Claudio: The purpose is to be able to have a key, any key, including a key following a prefix key, any prefix key, repeat a command when you hold it pressed. E.g., C-x y y y y y y ..., yes, as opposed to, e.g., C-x y C-x y C-x y C-x y C-x y C-x y C-x y. (Becomes even more useful with a longer prefix key.)
    – Drew
    Apr 2, 2023 at 1:57
  • I think I didn't make my question very clear (or maybe it is an XY question?). But your answer is also useful, +1, thanks :-)
    – shynur
    Apr 2, 2023 at 9:21
4

If it's custom code, you can use set-transient-map to temporarily set an overriding keymap for the next keystroke.

set-transient-map is a byte-compiled Lisp function in `subr.el'.

(set-transient-map MAP &optional KEEP-PRED ON-EXIT)

Set MAP as a temporary keymap taking precedence over other keymaps.
Normally, MAP is used only once, to look up the very next key.
However, if the optional argument KEEP-PRED is t, MAP stays
active if a key from MAP is used.  KEEP-PRED can also be a
function of no arguments: it is called from `pre-command-hook' and
if it returns non-nil, then MAP stays active.

Optional arg ON-EXIT, if non-nil, specifies a function that is
called, with no arguments, after MAP is deactivated.

This uses `overriding-terminal-local-map', which takes precedence over all
other keymaps.  As usual, if no match for a key is found in MAP, the normal
key lookup sequence then continues.

This returns an "exit function", which can be called with no argument
to deactivate this transient map, regardless of KEEP-PRED.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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