18

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?

  • What's your use case for this? I'm really just curious :) – PhilippFrank Sep 4 '15 at 9:19
  • 2
    @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 '15 at 9:26
  • So it's like a macro with a parameter. Very nice idea, thank you! – PhilippFrank Sep 4 '15 at 9:36
  • @PhilippFrank kind of, yeah. I'm glad you like it. :) – iago-lito Sep 4 '15 at 9:37
11

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

  • 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 '15 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 '15 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 '15 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 '15 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 '15 at 19:11
17

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]
  • 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 '15 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 '15 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 '15 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 '15 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 '15 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.

  • 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 '15 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 '15 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 '15 at 20:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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