5

Sometimes I type a long substitute command

/%s/foo/bar.../

and while I am typing it or only after hiting enter I notice that I have mistakenly used a the search key (/) instead of the ex-command key (:).

To fix it,

  1. If I have already pressed enter, I bring back the search string with /<C-P>.

  2. Use the mouse to copy from %s to the end of the search string to the primary selection.

  3. <ESC>:<C-R>* and the search string is now a proper substitute command.

Or I simply type it all again if it is short enough.

Is there some simpler way to do it? I know about the / register, but <C-R>/ only recovers %s.

6
  • 3
    You could use q/ to retrieve the whole thing.
    – B Layer
    Dec 4, 2020 at 19:03
  • 1
    @BLayer Indeed, we found that q/ out at the same time!
    – Quasímodo
    Dec 4, 2020 at 19:03
  • 1
    +1 for our idea. :D
    – B Layer
    Dec 4, 2020 at 19:07
  • Did this get featured in hot network questions or something? @Quasimodo did you think that what is essentially a question about user error would garner this much interest? :)
    – B Layer
    Dec 9, 2020 at 0:25
  • @BLayer As per the history, it did. I think the answer is twofold: (1) This question attracted many good answers which show different ways to do a same thing. (2) When I want to perform a substitution, my attention gets concentrated on coming up with the search regex, and a search regex is more likely to trigger / rather than : in my brain. Apparently I am not the only one.
    – Quasímodo
    Dec 9, 2020 at 13:19

3 Answers 3

7

I wasn't going to post this beyond my comment but then I see it turned into a competition. ;)

If you mistakenly enter /%s/foo/bar/ when you actually want to run :%s/foo/bar/ then you just need these eleven keystrokes:

q/kY:q<CR>:@0<CR>

Mapped:

nnoremap <F10> q/kY:q<CR>:@0<CR>

I guess I should explain. In the search history window (q/) we move to (k) and yank the line containing the last search with Y (short for yy). A yank without a register associated with it always goes to register 0. Leaving the search history window (:q<CR>) for the the regular command line (:) we use the "execute register contents" command (@) with said register like so: @0. Execute it (<CR>) and you're done.

A bit of background on why the (relatively) obvious solution won't work...

Those who are aware that the most recently entered search pattern gets saved in register / may wonder, "Why can't you just transfer what was entered at the / prompt to the normal command line by dumping the / register there (i.e. :<C-R>/<CR>)?"

The problem is the slash between %s and foo/bar/. This fools Vim into thinking you are entering the command that has form /{pattern}/{offset}<CR> (see :h search-commands). As a result it interprets the text after that slash, i.e. foo/bar/, as an invalid entry for the {offset} part. Valid or invalid {offset} is never saved in the / register. Only {pattern} is. (Side note: :h {offset} shows valid values for that parameter.)

Any workarounds?

Well, if OP Quasimodo was in the habit of entering substitution commands with a valid separator other than / (e.g. # or @) then they never would have gotten themselves in this mess because their original thought to use <C-R>/ would likely work then. ;)

0
7

As B Layer said, Vim interprets the rest of the pattern (namely, foo/bar.../) as an invalid offset, so it throws that bit away.

I have found, however, an alternative:

  • q/ opens the complete search history in a new buffer, just as q: opens the command history.
  • There I can yank the full last search term with ky$.
  • :q closes the buffer window.
  • :<C-R>" pastes the yanked pattern into the command line.

And here comes the obligatory mapping (here to F10) to make it easier.

nnoremap <F10> q/k"xy$:q<CR>:<C-R>x<CR>

It uses the register x so as not to mess with the default register.

1
  • one mapping option for yanking into a register (here I use @s as a mnemonic for "search" and <leader>ys as a mnemonic for "yank search") is nnoremap <leader>ys :normal! q/k"syg_<cr>
    – cmaher
    Dec 4, 2020 at 19:25
3

This is probably even shorter:

cnoremap <C-X> <C-\>ehistget('/')<CR>
6
  • Very nice one! Also possible: :<C-r>=histget('/')<CR>, it might be easier to remember <C-r>= to enter an expression, rather than <C-\>e which is similar but probably more obscure to most.
    – filbranden
    Dec 4, 2020 at 20:51
  • Aha! Well, I see your 15 (keystrokes) and raise you...er...lower you 4. (To 11. See my answer.) :)
    – B Layer
    Dec 4, 2020 at 21:45
  • 1
    What is this... VimGolf? :-)
    – filbranden
    Dec 4, 2020 at 21:46
  • 1
    Headed that way. :)
    – B Layer
    Dec 4, 2020 at 21:46
  • Agreed with Filbranden, I actually thought the command was ehistget :D Confused readers may will want to read help c_CTRL-\_e.
    – Quasímodo
    Dec 5, 2020 at 11:21

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.