7

The 'write-plugin' section of the help recommends the following 3-level map approach:

map ,c  <Plug>TypecorrAdd 
noremap <unique> <script> <Plug>TypecorrAdd  <SID>Add 
noremap <SID>Add  :call <SID>Add(expand("<cword>"), 1)<CR> 

I understand how it works, but it wasn't immediately clear to me what was being gained by the middle level of indirection: specifically the map to Add. What is the advantage of this strategy over the following 2-level approach?

map ,c  <Plug>TypecorrAdd 
noremap <unique> <script> <Plug>TypecorrAdd :call <SID>Add(expand("<cword>"), 1)<CR> 

The help says... "If another script would also map Add, it would get another script ID and thus define another mapping."

While this is true, isn't this sort of script-uniqueness already guaranteed by the use of in the rhs of the 3rd mapping? I.e.,

:call Add(...)

Update: After much discussion with a patient and helpful @Peter Rincker, I now think the reason for the extra map level has become clear, but in the simple example I presented above, there's no need for it. The full example in the Vim help, however, had another map, which I neglected to copy into my question. The complete example looks like this:

map ,c  <Plug>TypecorrAdd 
noremap <SID>Add  :call <SID>Add(expand("<cword>"), 1)<CR> 
noremap <unique> <script> <Plug>TypecorrAdd  <SID>Add 
noremenu <script> Plugin.Add\ Correction      <SID>Add

Note that, in this full example, 2 distinct maps use <SID>Add in their rhs. The only purpose (AFAICT) of creating the map from <SID>Add is to avoid duplicating the code in its rhs in those 2 maps (normal map <Plug>TypecorrAdd and menu command Plugin.Add Correction). IOW, the extra (3rd) level of indirection seems to be more about code reuse than about hygiene or script insulation.

1 Answer 1

5

There are two things going on here: an <SID> mapping and the use of <script>

<SID>

<SID> is used to make "local" mappings or menus. It does via name mangling. These <SID> mappings are often created for the purpose of code reuse.

See :h <SID> for more information.

<script>

<script> only allows remaps of <SID> so it is more hygienic. The idea is to use <script> to make mappings more resistant to recursive mappings or being polluted by unintended mappings. See :h :map-script for more.

Your example

So this code is doing the following things:

  • Creating an <SID> mappings to some function (for reuse)
  • Creating a <Plug> mapping that will be available outside the current script. This uses <script> to be my hygienic.
  • Creates a map to a <Plug> mapping. This the kind of mapping that you would often find in your vimrc.

Contrived example

A contrived example below to show the issues with not using <script> and <SID>. In our case we are going to reuse the yiw mapping (<Plug>(foo)).

nnoremap <Plug>(foo) yiw
nmap <Plug>(bar) <Plug>(foo)<cr>p
nmap <down> <Plug>(bar)

This all works fine until <cr> or p is mapped. e.g. nnoremap <cr> *

To avoid this we can add another level of indirection via <script> and <SID>.

nnoremap <SID>(foo) yiw
nnoremap <Plug>(foo) <SID>(foo)
nmap <script> <Plug>(bar) <SID>(foo)<cr>p
nmap <down> <Plug>(bar)

This example both <cr> or * can be mapped and the <Plug>(bar) mapping is resistant to these side-effects.

Conclusions

If you need to reuse a mapping or menu then using <SID> and script is a great option.

However many plugin developers do not bother with this much indirection as they do not need to reuse a mapping and therefore using <script> as it is not needed. Alternatively many plugins also avoid this by refactoring the common mapping(s) into part of a function call.

You will also notice that most newer script use the <Plug>(plugin-name-function) pattern instead of <Plug>PlugNameFunction. The parens make it easier to extend the plugin without coming up with weird names due to a common prefix.

5
  • But I still don't see how the middle map provides hygiene beyond what is provided by the 2-level mapping. In both 2 and 3-level approaches, noremap protects us from unintended recursive mappings, and <SID> ensures that the function called belongs to the proper script. I do see that without the <script> modifier on the middle map, <SID>Add in the rhs wouldn't be remapped, but without the middle map, there would be no need for the <SID>Add mapping at all, as the <Plug> map would map directly to something like :call <SNR>123Add(...)<CR>, which seems just as hygienic...
    – BPS
    Dec 10, 2016 at 3:01
  • Perhaps it would help if you could provide an example scenario (even a contrived one) where the intermediate map (the one from <Plug>TypecorrAdd to <SID>Add) fixes a problem you'd have if you tried to map directly from <Plug>TypecorrAdd to the final rhs.
    – BPS
    Dec 10, 2016 at 3:05
  • Thanks. But wouldn't this (2-level approach) do the same thing? noremap <Plug>(bar) yiw<cr>p nmap <down> <Plug>(bar) Just looked again at the example in the 'using-<Plug>' section of the Vim help, and I'm thinking that perhaps the extra level of indirection was added because the <SID>Add functionality was needed in the rhs of multiple maps (both a normal map and a menu map). In other words, the purpose of the <SID>Add map was to avoid repeating the rhs code, not to make the mapping more hygienic. Does this make sense?
    – BPS
    Dec 11, 2016 at 3:08
  • Yes the idea be able to reuse<Plug>(foo) in multiple places. It is a way reuse certain mappings while reducing side effects (hygienic). In reality I doubt you need to worry so much about using this technique. I haven't seen a great deal plugins using it as it is often superfluous. Dec 11, 2016 at 7:15
  • I've always understood the purpose of <Plug> (and even <SID> used in the rhs). It's the use of <SID>(foo) in the lhs that was confusing me, but not because I didn't understand what it was doing; rather, my confusion stemmed from my mistaken assumption that the 3rd level of indirection was added to increase hygiene, when it was really more about common code reuse. IOW, without the noremenu <script> Plugin.Add\ Correction in the Vim doc example, there would have been no point in making a map from <SID>Add. I appreciate your patience and help. I'd never seen this use of parens in maps.
    – BPS
    Dec 11, 2016 at 13:28

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.