Search code examples
recursionvimkeymapping

Vim - mapping a key to a function which does something else plus the orginal function of that key


The target is to have the key j doing a possibly complex task and moving to the next line (the latter action performed just like the original function of the j key).

My initial attempt was to map j key this way:

nn j :<C-U>execute "call MyFun(" . v:count . ")"<CR>

(as you can see I intend to make j's behavior depend on the count which is prepended to it) and to define the function MyFun appropriately:

fu! MyFun(count)
  " do more stuff based on a:count
  normal j
endf

which is faulty, as hitting j now results in the error E169: Command too recursive, since the non-recursivity of nnoremap, as long as my deduction is correct, applies to the "literal" content of the {rhs} of the mapping, and not to whatever is "inside" it (in other words the function body makes use of the meaning of j at the moment it is called, thus causing the infinte recursion).

Therefore I tried the following

nn , j
nn j :<C-U>execute "call MyFun(" . v:count . ")"<CR>
fu! MyFun(count)
  " do more stuff based on a:count
  normal ,
endf

However this means that I waste the key ,. I know I can avoid the waste of that mapping doing

nn <Plug>Nobody j

but then I wouldn't know how to use <Plug>Nobody (my understanding is indeed that its use is only in the {rhs} of another, non-nore mapping).


Solution

  • My initial attempt was to map j key this way

    Using execute here is redundant. It's enough to do:

    nnoremap j :<C-U>call MyFun(v:count)<CR>
    

    now results in the error E169: Command too recursive

    That's because of normal. To suppress remapping you must use "bang"-form: normal! j. Please, refere to documentation for :normal, whose second paragraph describes exactly your use case:

    If the [!] is given, mappings will not be used. Without it, when this command is called from a non-remappable mapping (:noremap), the argument can be mapped anyway.

    Besides, note that j normally supports count, so 2j is expected to move two lines down. So you, probably, should do execute 'normal!' a:count . 'j' instead.