I have a shortcut in my vimrc to search for a string through my repository:

nnoremap <leader>gg :vimgrep // **/*.py \| clist

I want the cursor to be between the // characters ready for a search term when I invoke the shortcut. I can do this with 18 <Left>s but that is very long and ugly i.e.

... .py \| clist<Left><Left><Left><Left><Left><Left><Left><Left>.....

I see I can also use <C-Left> which is shorter:

... .py \| clist<C-Left><C-Left><C-Left><Left><Left>

Is there another way to repeat 18 actions without such a long list of <Left> in a mapping's definition?


You can use counts in a mapping just as you can use them in normal mode. Of course, when you want to use the count you're not in normal mode: you're in command-line mode.

A quick hack is to use the command-line window:

:nnoremap <leader>gg :vimgrep // **/*.py \| clist<C-F>17<Left><C-C>

You can also use repeat to create the series of <left> keystrokes as a string and feedkeys() to "type" this into the command-line. Unfortunately, this... uh... ends up quite a bit more ugly that what you had to start with:

:nnoremap <silent> <leader>gg :call feedkeys(':vimgrep // **/*.py \| clist' . repeat("\<lt>Left>", 17))<CR>

But an interesting technique nonetheless!

However, probably the cleanest technique — if you want to keep it all in a single mapping — is to set the position of the cursor directly using the setcmdpos() function:

:nnoremap <leader>gg :vimgrep // **/*.py \| clist<C-R>=setcmdpos(10)<CR><BS>

This uses the expression register (see :help c_CTRL-R_=) to execute the function: the <Backspace> at the end is required to remove the 0 that is entered as the successful result of the setcmdpos function.

  • 1
    Good one! But if you go to the command window, you can do even better with something like 2F/, which will keep working if you change the command line.
    – filbranden
    Aug 30 '19 at 16:36
  • 1
    @filbranden That did occur to me after as I was posting this answer, but I like to keep in the spirit of the question :).
    – Rich
    Aug 30 '19 at 16:40
  • @Rich Thank you. I've gone with the <C-F>17<Left><C-C> solution as I like seeing the command: keeping it fresh in the mind when I don't have my vimrc available
    – Noel Evans
    Sep 3 '19 at 14:10
  • @Rich A cheeky after-question: Would you know why I can't add an extra \| cc on the end? I was also hoping to have a prompt go to the relevant search result by index. This doesn't work as expected: nnoremap <leader>gg :vimgrep // **/*.py \| clist \| cc<C-R>=setcmdpos(10)<CR><BS>
    – Noel Evans
    Sep 3 '19 at 14:12
  • 1
    @NoelEvans That doesn't work because the bar notation | will attempt to immediately run the cc command (with no argument), whereas you want want to do is emulate typing :cc so that you can then add an argument. This mapping should have the desired effect: nnoremap <leader>gg :vimgrep // **/*.py \| clist \| call feedkeys(":cc ")<C-R>=setcmdpos(10)<CR><BS>
    – Rich
    Sep 4 '19 at 8:51

This is not a direct answer to "moving the cursor a large number of times in a nnoremap" or to "another way to repeat 18 actions without such a long list of keystrokes in a mapping's definition", but it's a different approach to the problem you presented.

Consider creating a new user-defined command for that operation.

That way, you can have the command-line argument typed by the user be at the end of the line itself.

In your particular example:

command! -nargs=1 Pygrep vimgrep /<args>/ **/*.py | clist
nnoremap <leader>gg :Pygrep<space>

You'll be left at the command-line, ready to type an argument to :Pygrep, which will then be used as search expression for vimgrep on your Python files.

Or, if you'd like to preserve the // look, to make it clear it's a regexp (and also that you need to escape /s):

command! -nargs=1 Pygrep vimgrep <args> **/*.py | clist
nnoremap <leader>gg :Pygrep //<Left>

You still use a <Left> in this case... But it's only a single one.

See :help user-commands for more on how to define them.

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.