3

I'm trying to reverse engineer how a .vimrc file works. One of the lines is this:

autocmd BufEnter *.p[lm] nmap <buffer> ;t :call RunPerlTests()<CR>

The only part I'm unclear on is the nmap <buffer> ;t bit. So I'm not sure what will trigger a call to the function.

4

It seems to be an autocmd listening to the BufEnter event, and is only fired for a file whose extension is .pl or .pm (for more info about the syntax, see :h file-pattern).

Whenever this event occurs, the autocmd should install the following mapping:

nmap <buffer> ;t :call RunPerlTests()<CR>

This mapping is not global, but local to the current buffer (the one where the autocmd was fired), thanks to the argument <buffer> which was passed to the :nmap command.

The {lhs} of the mapping is ;t, and the {rhs} is :call RunPerlTests()<CR>.
It means that whenever you will hit ;t in normal mode, Vim should execute the Ex command :call RunPerlTests()<CR>. This last command should call the custom function RunPerlTests().


autocmd BufEnter *.p[lm]  nmap <buffer> ;t :call RunPerlTests()<CR>
"       |        |        |     |       |  |
"       |        |        |     |       |  +-- `{rhs}` of the mapping
"       |        |        |     |       +-- `{lhs}` of the mapping
"       |        |        |     +-- argument to pass to `:nmap`; limits the scope of the mapping to the current buffer
"       |        |        +-- mapping command to execute
"       |        +-- pattern to limit the scope of the autocmd to certain filetypes
"       +-- event of the autocmd

You probably want to replace the :nmap command, which is recursive, with the non-recursive version :nnoremap. You only want the recursiveness, when you need all or a part of the {rhs} to be replaced using another mapping, which doesn't seem to be the case here.

And you could prefix the Ex command :call with the keycode <C-u>, to delete a possible range which could be inserted by accident on the command-line if you hit a number before ;t.

It would give:

autocmd BufEnter *.p[lm]  nnoremap <buffer> ;t :<C-U>call RunPerlTests()<CR>

To understand why <C-U> could be useful, in normal mode hit 3:. On the command-line you should see :.,.+2. This is a range, which can be read as from the current line down to the 2nd line after the current one.

If you pass a range to the :call command, by default Vim should call the function as many times as there are addresses inside the range.

Example:

fu! MyFunc()
    echo 'hello'
endfu

nno cd :call MyFunc()<CR>

Now, hit 3cd. Because you hit 3 before the {lhs} of your custom mapping cd, Vim will automatically inserts the range .,.+2, which will give on the command-line:

:.,.+2call MyFunc()<CR>

This will call MyFunc() 3 times, because there are 3 addresses inside the range .,.+2, and you should see hello 3 times.

As said earlier, if you want to avoid this, you could prefix the :call command with the <C-U> keycode (see :h c^u). It will make sure that nothing is inserted before :call.

Note that if your custom function RunPerlTests() was defined with the range attribute, then it probably expects a range. In this case, don't add <C-U>.


If you haven't done it already, you also probably want to wrap your autocmd inside an augroup and clear the latter with the autocmd! command:

augroup YourAugroup
    autocmd!
    autocmd BufEnter *.p[lm]  nnoremap <buffer> ;t :<C-U>call RunPerlTests()<CR>
augroup END

The reason for this is to avoid that your autocmd is duplicated every time you source your vimrc file. See this question for more info.

| improve this answer | |
  • 1
    Thank you. I'm not sure I understand the :nmap vs. :nnoremap but I'll look that up. Regarding the insertion of the Ex command, are you saying if I don't do that and I accidentally type something like 20;t it will call the function 20 times? – StevieD Feb 12 '17 at 4:30
  • 2
    @StevieD Yes I think you're right, I'm going to edit my answer because what I said about the range is not correct. I'm also going to try to add an example and a link about :nmap vs :nnoremap. – user9433424 Feb 12 '17 at 4:36
  • OK, the function does not have a range attribute. I'm not sure what that is but I will learn when the time comes. I will add in the <C-U> bit as you suggest. I read up on recursive vs. non-recursive. I get that now, too, though I'm not quite clear on what you mean when you say "when you need all or a part of the right hand side to be replaced." – StevieD Feb 12 '17 at 4:40
  • 2
    @StevieD When you use a recursive mapping command, if any part of the {rhs} of your first mapping, is also the {lhs} of another 2nd mapping, then Vim will automatically replace this part with the {rhs} of the 2nd mapping. Sometimes, you may want this, in particular when you want to use a <Plug> mapping provided by a plugin which you installed. But most of the time, you don't want your mapping command to be recursive, because it can lead to unexpected side-effects. See here: learnvimscriptthehardway.stevelosh.com/chapters/05.html – user9433424 Feb 12 '17 at 4:53
  • 3
    @StevieD If you don't understand yet, just follow the rule given at the end of the page: never use recursive mapping, unless you really need to. :nmap, :vmap, :xmap, :omap, :smap, :cmap, :imap are all recursive mapping commands for various mode. Use :nnoremap, :vnoremap, :xnoremap, :onoremap, :snoremap, :cnoremap, :inoremap instead. – user9433424 Feb 12 '17 at 4:55

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.