7

Let me define a keyboard-macro: I enter C-x (, then go to beginning with M-<, then type M-% to query-replace some occurrences of foo by bar. Out of 3 occurrences found, I replace only the last 2, then save with C-x C-s and finish the kbd-macro with C-x ). Well done!!

If I now run the kbd-macro on another buffer, it will not ask me whether I want to replace foo by bar, it will reproduce exactly my previous no-yes-yes answers and replace the 2nd and 3rd occurrences! It will not ask for the 4th or 5th occurrences (this 2nd buffer contains more foos)!! This is not what I expected when I defined the macro. I wanted it to call query-replace and let me answer its queries...

Aside question: Don't you find this wrong? [I can M-x edit-last-kbd-macro and see what is recorded and understand the logic of it all but I still find this wrong.]

Real question: What could I have used instead of query-replace to make my kbd-macro work as expected?

dgtized
  • 4,169
  • 20
  • 41
phs
  • 1,095
  • 6
  • 13
  • 1
    Personally, I don't find it wrong. The keyboard macro records *all* of your keystrokes—including the ones to specify the inputs for `M-%`—and plays them back exactly. I've never seen, and wouldn't expect, a macro that had interactive parts. You're probably better off writing a normal interactive elisp function at this point. – Tikhon Jelvis Oct 17 '14 at 23:01
  • Simple. End your keyboard macro before you answer any of the queries. EDIT: I just tried it, maybe it is not that simple. I guess somebody could write a new command `start-kbd-macro-dwim`. – nispio Oct 17 '14 at 23:03

2 Answers2

6

Emacs macros playback everything the user enters over the course of the macro, unless they escape the macro. The way to escape a macro in progress is with C-x q kbd-macro-query.

Specifically I believe you need to enter recursive edit, which can prompt for a command in minibuffer before continuing the macro.

I think you want to define your query-replace when calling the macro.

  • F3 start the macro
  • M-< jump to beginning
  • C-u C-x q to enter a recursive edit
  • C-M-c to exit recursive edit
  • ... rest of macro actions
  • F4 to save, and repeat

On the next kmacro-end-or-call-macro, it will jump to the beginning of the buffer, and enter a recursive edit, where you can call query-replace. Then when you exit recursive edit C-M-c it will continue the macro from that point.

If you wish to prompt for an alternate argument to query-replace, it might make sense to write a simple interactive wrapper function around query-replace instead of using a recursive edit.

dgtized
  • 4,169
  • 20
  • 41
  • 1
    Unless I am misunderstanding, this is recording everything *but* the call to `query-replace`. It doesn't even seem like this will save the `FROM-STRING` or `TO-STRING`. – nispio Oct 18 '14 at 00:57
  • 1
    Yes that is exactly what I was saying. If query-replace needs to be used interactively inside of the macro, without doing a replace all, I don't think a macro is appropriate – dgtized Oct 18 '14 at 06:18
3

This doesn't seem like a great fit for a keyboard macro, since you want to ask for user input. Presumably you are wanting to record a macro because this is a query-replace that you intend to do frequently. If you can't make the keyboard macro do what you are after, you could create a keybinding to your specific call to query-replace as follows:

(global-set-key (kbd "C-c q")   ;; Create a key binding for "Ctrl+c, q"
  (lambda () (interactive)      ;; Define an anonymous interactive funcion
  (query-replace "foo" "bar"    ;; Query-replace "foo" with "bar"
   nil                          ;; Don't require matching whole words
   1 (buffer-end 1))))          ;; Run from beginning to end of buffer

You can either add this to your init file and it will be available to you next time you start emacs, or you can take it for a test drive by pasting it into the *scratch* buffer and running M-x eval-buffer. It is bound to C-c q, but that can easily be modified to whatever you want.

nispio
  • 8,175
  • 2
  • 35
  • 73