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.

StevieD
  • 1,492
  • 1
  • 16
  • 24

1 Answers1

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.

user9433424
  • 6,138
  • 2
  • 21
  • 30
  • 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 04: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 04: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 04: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: http://learnvimscriptthehardway.stevelosh.com/chapters/05.html – user9433424 Feb 12 '17 at 04: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 04:55