5

I've defined one keyboard macro, bound to keystroke 1, say, and I want to define a second keyboard macro that executes the first one indefinitely and then returns to the beginning of the buffer. I assume that the first one always terminates if iterated indefinitely. E.g., it could be a keyboard macro that operates on a line and then moves to the next line.

To define the new keyboard macro, I naively want to type:

C-x ( C-u 0 C-x C-k 1 C-u C-u C-x ( M-< C-x )

In other words, C-x ( to begin recording, C-u 0 to specify indefinite execution, C-x C-k 1 to specify that the indefinite execution refers to keyboard macro 1, C-u C-u C-x ( to resume recording the new keyboard macro (since recording automatically terminates after the last iteration of keyboard macro 1), M-< to return to the beginning of the buffer, and finally C-x ) to end recording.

If I bind all of that to a new key sequence and then invoke it, I find that this new keyboard macro goes as far as to execute keyboard macro 1 (as many times as it can) but fails to finish with the M-<. Am I doing something wrong? Is there another way to achieve what I want?

Drew
  • 75,699
  • 9
  • 109
  • 225

1 Answers1

5

What you are currently doing doesn't work because execution of keyboard macros terminates whenever there is an error. This is what makes it possible to execute keyboard macros "indefinitely" (but without entering an infinite loop) in the first place: If macro execution did not stop on error, you would enter an infinite loop even if you naturally reached the end of the current buffer after n executions. As a result, if you call a macro that executes another macro indefinitely, an error will be thrown once the end of the buffer is reached, and this error will also terminate the "parent" macro.

To get around this, you can define a custom command that ignores errors during macro execution and returns to the beginning of the current buffer after execution stops:

(defun kmacro-repeat-indefinitely-then-back-to-top ()
  "Repeat last keyboard macro indefinitely, ignoring errors, then
go back to beginning of buffer."
  (interactive)
  (ignore-errors (kmacro-call-macro 0))
  (goto-char (point-min)))
itsjeyd
  • 14,586
  • 3
  • 58
  • 87
  • I'm not sure I follow your custom command. When I try to run it, I get the error "Symbol's function definition is void: ignore-errors". And just to be clear, I should apply this function after attempting to define the second macro, right? I.e., the one that calls macro 1 indefinitely? Please forgive me for misunderstanding. – Paul Buckingham Feb 07 '15 at 14:25
  • @PaulBuckingham That's no problem at all. I could have given more detailed instructions in my answer. The command is designed to *replace* the need for a second macro: As I tried to explain, you can not achieve what you want with a second macro because the error that is thrown when the end of the current buffer is reached while executing the first macro will terminate not only the first but also the second macro. So the workflow would be: (1) Define first macro. (2) Execute command via `M-x` `kmacro-repeat-indefinitely-then-back-to-top` `RET`. – itsjeyd Feb 07 '15 at 15:58
  • @PaulBuckingham As for the error you are seeing: I tested the code using vanilla Emacs 24.4.1 (launched via `emacs -Q`), and it worked without issue. `ignore-errors` is a built-in; if you are using Emacs 23.1 or newer it shouldn't give you trouble. What steps did you take to run the command defined above? – itsjeyd Feb 07 '15 at 16:16
  • Thanks for your perseverance. You were right about the version; I've just run it on v24.3, and it works fine, as long as I simply type `M-x` `kmacro-repeat-indefinitely-then-back-to-top` `RET`. But how do I put that into a new kbd macro? When I try to surround it by `c-x (` and `c-x )`, I run into problems. Indeed, `c-x (` `M-x` `kmacro...top` appears not to run macro 1 at all, even if I type it immediately after defining macro 1. Something about `c-x (` changes the behaviour of what comes after it. – Paul Buckingham Feb 07 '15 at 20:15
  • @PaulBuckingham Maybe I'm missing something, but why do you want to put the command into a macro? If you are concerned about efficiency, you can just bind the command to a key via `(global-set-key (kbd "C-c m") 'kmacro-repeat-indefinitely-then-back-to-top)` (replace `C-c m` with a key sequence of your choice). – itsjeyd Feb 07 '15 at 20:54