14

Let's say I bind the key to a certain function as follows:

(global-set-key (kbd "C-c =") 'function-foo)

Now, I want the key binding to work as:
After I press C-c = for the first time, if I want to repeat the function-foo, I don't need to press C-c again, but simply repeat pressing =. Then, after I call the function-foo for enough times, I can just press keys other than = (or explicitly press C-g) to quit.

How to do this?

pcurry
  • 1,374
  • 11
  • 23
shelper
  • 10,053
  • 8
  • 41
  • 67
  • 1
    Are you familiar with the `repeat` command? It is bound to `C-x z` and you may use it to repeat the previous command. It repeats that command each time you press `z`. – mk1 Jun 19 '13 at 21:41
  • 1
    @mk1 i know C-x z, I just wonder if i can make my own key bindings that works in that way...anyway, thanks for your comments – shelper Jun 19 '13 at 23:36
  • `C-x e` for executing keyboard macros has the desired behavior. If the implementation of that binding is in the elisp somewhere, that could be a start on writing your own binding. – pcurry Jun 26 '13 at 03:25

5 Answers5

17

This may be the thing you are looking for:

(defun function-foo ()
  (interactive)
  (do-your-thing)
  (set-temporary-overlay-map
    (let ((map (make-sparse-keymap)))
      (define-key map (kbd "=") 'function-foo)
      map)))
juanleon
  • 9,220
  • 30
  • 41
8

There's a smartrep.el package that does exactly what you need. The documentation is a bit scarce but you can get a grip of how it's supposed to be used by looking into numerous emacs configs found on github. For example (taken from here):

(require 'smartrep)
(smartrep-define-key
    global-map "C-q" '(("n" . (scroll-other-window 1))
                       ("p" . (scroll-other-window -1))
                       ("N" . 'scroll-other-window)
                       ("P" . (scroll-other-window '-))
                       ("a" . (beginning-of-buffer-other-window 0))
                       ("e" . (end-of-buffer-other-window 0))))
immerrr
  • 1,251
  • 7
  • 13
7

This is what I use. I like it because you don't have to specify the repeating key.

(require 'repeat)
(defun make-repeatable-command (cmd)
  "Returns a new command that is a repeatable version of CMD.
The new command is named CMD-repeat.  CMD should be a quoted
command.

This allows you to bind the command to a compound keystroke and
repeat it with just the final key.  For example:

  (global-set-key (kbd \"C-c a\") (make-repeatable-command 'foo))

will create a new command called foo-repeat.  Typing C-c a will
just invoke foo.  Typing C-c a a a will invoke foo three times,
and so on."
  (fset (intern (concat (symbol-name cmd) "-repeat"))
        `(lambda ,(help-function-arglist cmd) ;; arg list
           ,(format "A repeatable version of `%s'." (symbol-name cmd)) ;; doc string
           ,(interactive-form cmd) ;; interactive form
           ;; see also repeat-message-function
           (setq last-repeatable-command ',cmd)
           (repeat nil)))
  (intern (concat (symbol-name cmd) "-repeat")))
jpkotta
  • 9,237
  • 3
  • 29
  • 34
  • 2
    I like this a lot, but note that CMD *must* already be loaded (autoloading is insufficient), otherwise the arglist and interactive-form interrogations will fail. (Actually the latter *would* trigger autoloading, but the arglist which preceeds it will be wrong). – phils Dec 30 '13 at 07:05
2

You want your function-foo to use set-temporary-overlay-map.

Stefan
  • 27,908
  • 4
  • 53
  • 82
2

In addition to what @juanleon suggested, which uses set-temporary-overlay-map, here is an alternative that I use quite a bit. It uses standard library repeat.el.

;; This function builds a repeatable version of its argument COMMAND.
(defun repeat-command (command)
  "Repeat COMMAND."
 (interactive)
 (let ((repeat-previous-repeated-command  command)
       (last-repeatable-command           'repeat))
   (repeat nil)))

Use that to define different repeatable commands. E.g.,

(defun backward-char-repeat ()
  "Like `backward-char', but repeatable even on a prefix key."
  (interactive)
  (repeat-command 'backward-char))

Then bind such a command to a key with a repeatable suffix, e.g., C-c = (for C-c = = = =...)

See this SO post for more information.

Community
  • 1
  • 1
Drew
  • 29,895
  • 7
  • 74
  • 104