4

I am very used to the shell autocompletion, which uses the <tab> character. Therefore I tried to build something similar into my .vimrc. I'm aware of the super popular YCM plugin but let's face this as an exercise in learning.

I have built the following piece of vimscript to: first, allow me to use different types of <c-x> completions by changing a variable; second, do not fire a completion if the cursor is not after a cword character.

let g:tab_completion_keys = "\<c-x>\<c-p>"
function! TabCompletion1()
  let l:char = matchstr(getline('.'), '\%' . col('.') . 'c.')
  if l:char =~# '\k'
    execute "normal! a" . g:tab_completion_keys . "\<esc>"
  else
    execute "normal! a\<tab>\<esc>"
  endif
endfunction
inoremap <tab> <esc>:call TabCompletion1()<cr>a

The problem is that I cannot leave normal! in insert-mode. i.e. I cannot enter insert-mode in normal! and finish the command without exiting the insert-mode. Thanks to that, the code above opens the completion list and always selects the first completion, because I leave the insert mode.

How can I make an insert-mode mapping that leaves me in insert-mode for some time (to select one word from the completion list) and then continues the mapping?

Or, maybe, there is a better way to deal with exe normal!?

4
  • 1
    Didn't you try supertab plugin?
    – SibiCoder
    Commented Jul 16, 2016 at 4:03
  • 4
    I second the recommendation for supertab. To answer your question: see :h :map-<expr>. Most imap maps are easier to write with <expr>. Commented Jul 16, 2016 at 4:16
  • 1
    Take a look at how VimCompletesMe does that.
    – romainl
    Commented Jul 16, 2016 at 7:43
  • @SatoKatsura - Yup :h map-<expr> clarified a lot of things for me. Thanks
    – grochmal
    Commented Jul 16, 2016 at 16:02

1 Answer 1

2

Just like Sato Katsura said, this is a job for the map modifier <expr>. With <expr> you can return the key combination from the function. The final code looks as this:

let g:tab_completion_keys = "\<c-x>\<c-p>"
function! TabCompletion()
  let l:col = max([col('.')-1, 1])
  let l:char = matchstr(getline('.'), '\%' . l:col . 'c.')
  if l:char =~# '\k'
    return g:tab_completion_keys
  else
    return "\<tab>"
  endif
endfunction
inoremap <expr> <tab> TabCompletion()

In practice it is much better to use a full plugin. I am now using supertab suggested by SibiCoder.

supertab has many more configuration options than the code above, including: defined patterns for which no completion shall be attempted, different completion contexts based on the text around the cursor and even an option to use a different key than <tab>.

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.