5

I have a function that is called via a mapping when I am in insert mode:

inoremap <buffer> <M-i> <ESC>:call TheFunc()<CR>

The function is supposed to "calculate" a string (l:text) and to insert it at the current cursor position:

fu! TheFunc()

   if g:FOO = 1
      let l:text = XYZ()
   elseif g:FOO = g:BAR
      let l:text = ABC()
   ... etc etc
   endif

   execute 'normal a' . l:text

   execute 'normal l'
   startinsert

endfu

I use execute 'normal l' so that after calling the function, further typed text is inserted after the text inserted by the function.

This works fine, except when the cursor is on the end of a line. In this case, the normal l cannot move the cursor one to the right, and the subsequent startinsert inserts one character to early.

I can circumenvent this problem by replacing the startinsert command with this construct

if col('.') + 1 == col('$')
   startinsert!
else
  execute 'normal l'
  startinsert
end

This works as I need it.

Yet, I feel there should be a more elegant way to achieve what I want. Is there?

2 Answers 2

9

There is a better way of using functions in mappings, and that is by using the <expr> flag. For example, say you wanted to insert the current line number at the cursor position by typing ln in insert mode. This is how you would accomplish that with an <expr> mapping:

inoremap <expr> ln line(".")

Instead of directly putting the keys you want to type in the mappping, you put a function whose return value is treated like normal keypresses. In this case, it inserts the output of the line() function. A normal vimscript expression can be used as well. For more on these kinds of mappings, see :help :map-<expr>.

7

The following is an example where the <expr> type mapping is used to obtain the same functionality. One of the main advantages is that you now stay in insert mode, which means the cursor position will be correct. It also means you don't have to worry about the startinsert issue.

inoremap <buffer><expr> <m-i> TheFunc()

function! TheFunc()
  if get(g:, 'foo', 1) == 1
    return "foo"
  else
    return "bar"
  endif
endfunction

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.