diff options
authorSean Whitton <>2020-11-08 14:16:05 -0700
committerSean Whitton <>2020-11-08 14:16:05 -0700
commitf7989f2161f546ccf4913265d10d1c1cf8f19315 (patch)
parent6f11e097b0dea12c3665178141c3e9ba9cf58349 (diff)
new blog entry
1 files changed, 54 insertions, 0 deletions
diff --git a/blog/entry/emacs-combined-repeat.mdwn b/blog/entry/emacs-combined-repeat.mdwn
new file mode 100644
index 0000000..94dd078
--- /dev/null
+++ b/blog/entry/emacs-combined-repeat.mdwn
@@ -0,0 +1,54 @@
+[[!meta title="Combining repeat and repeat-complex-command"]]
+[[!tag tech emacs]]
+In Emacs, you can use =C-x z= to repeat the last command you input, and
+subsequently you can keep tapping the 'z' key to execute that command again
+and again. If the command took minibuffer input, however, you'll be asked for
+that input again. For example, suppose you type =M-z := to delete through the
+next colon character. If you want to keep going and delete through the next
+few colons, you would need to use =C-x z : z : z := etc. which is pretty
+inconvenient. So there's also =C-x ESC ESC RET= or =C-x M-: RET=, which will
+repeat the last command which took minibuffer input, as if you'd given it the
+same minibuffer input. So you could use =M-z : C-x M-: RET C-x M-: RET= etc.,
+but then you might as well just keep typing =M-z := over and over. It's also
+quite inconvenient to have to remember whether you need to use =C-x z= or =C-x
+M-: RET=.
+I wanted to come up with a single command which would choose the correct
+repetition method. It turns out it's a bit involved, but here's what I came
+up with. You can use this under the GPL-3 or any later version published by
+the FSF. Assumes lexical binding is turned on for the file you have this in.
+[[!format el """
+(defun spw/repeat-complex-command-immediately (arg)
+ "Like `repeat-complex-command' followed immediately by RET."
+ (interactive "p")
+ (if-let ((newcmd (nth (1- arg) command-history)))
+ (progn
+ (add-to-history 'command-history newcmd)
+ (repeat-message "Repeating %S" newcmd)
+ (apply #'funcall-interactively
+ (car newcmd)
+ (mapcar (lambda (e) (eval e t)) (cdr newcmd))))
+ (if command-history
+ (error "Argument %d is beyond length of command history" arg)
+ (error "There are no previous complex commands to repeat"))))
+(let (real-last-repeatable-command)
+ (defun spw/repeat-or-repeat-complex-command-immediately ()
+ "Call `repeat' or `spw/repeat-complex-command-immediately' as appropriate.
+Note that no prefix argument is accepted because this has
+different meanings for `repeat' and for
+`spw/repeat-complex-command-immediately', so that might cause surprises."
+ (interactive)
+ (if (eq last-repeatable-command this-command)
+ (setq last-repeatable-command real-last-repeatable-command)
+ (setq real-last-repeatable-command last-repeatable-command))
+ (if (eq last-repeatable-command (caar command-history))
+ (spw/repeat-complex-command-immediately 1)
+ (repeat nil))))
+;; `suspend-frame' is bound to both C-x C-z and C-z
+(global-set-key "\C-z" #'spw/repeat-or-repeat-complex-command-immediately)