36

When I am recording a macro with q and I am done, I can type:

  • q to stop recording if I am in normal or visual mode.
  • <c-o>q to stop recording if I am in insert mode.
  • ..

    How do I stop recording if my macro ends in command mode?

4
  • What's your use case for this? I'm really just curious :) Sep 4, 2015 at 9:19
  • 3
    @PhilippFrank I will frequently search and replace the same pattern with different ones in various blocks of text. With such a macro, selecting the block, writing the actual search pattern and placing the cursor on the good position for typing the replacement chain can be made automatically. When I play it, it'll select the block, enter command mode, type :'<,'>s/toBeReplaced//g then put the cursor between the two // so that all I have to do is to type newString<cr> and I'm done. :)
    – iago-lito
    Sep 4, 2015 at 9:26
  • So it's like a macro with a parameter. Very nice idea, thank you! Sep 4, 2015 at 9:36
  • @PhilippFrank kind of, yeah. I'm glad you like it. :)
    – iago-lito
    Sep 4, 2015 at 9:37

3 Answers 3

25

Another solution is when in command line mode, use Ctrl-f, and press q to stop recording.

Ctrl-f allows you to open the command line window, in which you can edit the command in normal mode. To validate the command, simply use enter.


Therefore, after having added something like:

cnoremap <c-q> <c-f>i<c-o>q

in your .vimrc, you can use <c-q> to stop recording a macro while in command mode.


Florent

6
  • You still have to remove the <C-f> character (^F) from the end of the macro, right? At least that's what I see.
    – VanLaser
    Sep 6, 2015 at 16:08
  • Almost there! Really useful (I didn't know about the command line window but it's great), but.. aargh! It seems like entering this windows from insert mode looses your selected area. Is there a way to make the command you launch from the command line window work on a selected area?
    – iago-lito
    Sep 6, 2015 at 16:12
  • @VanLaser No, since you can go on with typing your command in the command-line window. Your macro will be like qqSTARTMACRO:STARTCOMMANDLINEPART<c-f>i<c-o>q and so the recording has actually stopped while your were typing the command part. To use the macro: @qFINISHYOURCOMMAND<cr> and you're done: no need to edit it. Unfortunately, this does not seem to work from visual mode (or it looses the selection).. or have I missed something?
    – iago-lito
    Sep 6, 2015 at 16:17
  • Ah, right :) BTW, I could select a visual range, then do @q and the range was added at command start '<,'> - as long as the macro started with : ....
    – VanLaser
    Sep 6, 2015 at 16:30
  • @lago-lito Actually, it looks like even if you don't see your selection, the specified range (i.e. '<,'>) still matches the selection when validating the command.
    – fflorent
    Sep 6, 2015 at 19:11
20

One way to do it:

  • start your macro as usual (e.g. qq ... other chars)
  • when you reach the cmdline-mode, write all the characters you want
  • you want to end the macro here. Hit ESC to get out of cmdline-mode, q to end your macro.
  • remove the ESC from your q register (*): you can paste it in a buffer, remove the ^[ character, select the whole stuff again (character-wise) and yank it back to register q.
  • that's it: run your macro: @q.

Basically, you "post-process" your macro a little, since it is stored in a register :)

(*) A quick way to remove the last character from a macro stored in register q would be:

:let @q = @q[:-2]
6
  • Haha! This has actually been my hack so far =D. Is there not an "canonical" way of doing so? (also faster)
    – iago-lito
    Sep 4, 2015 at 8:56
  • I don't know of one, but I don't use macros too much (I prefer writing small functions instead). Macros feel to cryptic for me, when I look at them afterwards.
    – VanLaser
    Sep 4, 2015 at 9:23
  • @VanLaser: could you give a little more details about the line :let @q = @q[:-2] please? Especially how does [:-2] work? :-)
    – statox
    Sep 4, 2015 at 13:57
  • It's "slicing" and works on lists, and strings (which is how I access/set @q above). Some examples here
    – VanLaser
    Sep 4, 2015 at 14:02
  • @VanLaser they truly look cryptic. There are useful however for repeating, sophisticated little things you need'a do in very specific cases.. you know this kind of problems that appear only once and only last for a few minuts. Being able to deal with them by creating macros on the fly and without having to write and debug a dedicated function is just.. woo so wonderful! ^ ^
    – iago-lito
    Sep 4, 2015 at 16:52
3

Having read your comment about the use case, it looks like you could use a key mapping instead of a macro.

I have this line in my .vimrc (don't remember where I found it):

nnoremap <Leader>s :%s/\<<C-r><C-w>\>//g<Left><Left>

It has a similar effect to the use case you described, putting you on the command line so the only thing you have to do is type the replacement and press enter, but in this case, replacing all occurrences of the word that was under your cursor.

To just replace within the current block, I think something like the following should work:

nnoremap <Leader>s m'va{<ESC>``:'<,'>s/\<<C-r><C-w>\>//g<Left><Left>

Of course, you can map it to whatever key you want.

What the above does: mark the current position using the unnamed marker, visually select the current block, leave visual mode and return the cursor to the saved position (the visual selection markers are still set), enter command mode and type out the substitute command, inserting the word that was under the cursor with <C-r><C-w>, and positioning your cursor to type out the replacement.

3
  • Yeah, I do have this one as well. In my specific use case, however, the toBeReplaced pattern is not as simple as "the word under cursor", which is why I need a small, specific macro that I could create on the go. And then again: too much specific for a map :\ Thank you anyway :)
    – iago-lito
    Sep 4, 2015 at 16:42
  • 2
    Perhaps, put the pattern in a register, then adapt the above mapping to automatically get the register content as the toBeReplaced part (instead of current word under cursor, which is more limiting).
    – VanLaser
    Sep 4, 2015 at 19:13
  • 1
    @VanLaser Right, that would allow for a general mapping, and would make it much simpler as well: va{:s/<C-r>0//g<Left><Left> With this you can yank whatever pattern you need before-hand, and then just press the mapped key to replace that pattern with something in the current block.
    – dhakimian
    Sep 4, 2015 at 20:13

Your Answer

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

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