Skip to content

Commit

Permalink
Vim: usher in a new era of autocompletion productivity
Browse files Browse the repository at this point in the history
So, I'm using YouCompleteMe everywhere now, not just on my Mac, so I am
expecting to be doing a lot more autocompletion from here on. Time to
fix some long-held grievances I have with the way autocompletion is
working with UltiSnips, YouCompleteMe and Supertab.

Here's an example scenario.

I have a snippet `log` that expands to `console.log();`, with a
placeholder inside the parens. Now, say I have a file with:

   var fooBar = 1;

and I type:

    log<tab>

UltiSnips puts me here:

    console.log(|);
                ^
                (cursor here)

If I then type:

    console.log(fooB<tab>);

I wind up here:

    console.log(fooB);|
                      ^
                      (cursor here)

When what I really want is:

    console.log(fooBar|);
                      ^
                      (cursor here)

ie. if Supertab _can_ complete something, I want it to, otherwise I want
UltiSnips to get the tab.

With YouCompleteMe in the picture, the experience is even worse, because
you'll see the pop-up as soon as you start typing "fooB". Reflexively,
you'd think you could push `<Tab>` to expand the suggestion, but you
can't; you have to remember to push `<C-j>` or `<C-n>` or `<Down>` (ugh)
instead.

Historically, the way I've gotten YouCompleteMe and UltiSnips to play
nicely is as described here:

    http://0x3f.org/blog/make-youcompleteme-ultisnips-compatible/

Effectively:

- Override YouCompleteMe's default bindings, freeing up `<Tab>` and
  `<S-Tab>`; set a new completion binding like `<C-j>` or something
- Install Supertab and tell it to use the completion binding that you
  just set up (in other words, pressing `<Tab>` will get "forwarded" to
  the `<C-j>` or other binding that you set up for YouCompleteMe)
- Tell UltiSnips to use `<Tab>` and `<S-Tab>` to do its thing

So, this works pretty darn well, except for the awkwardness described
above. In this commit, we make things better with a trick similar to the
one described here:

  ycm-core/YouCompleteMe#36 (comment)
  [via http://stackoverflow.com/questions/14896327/ultisnips-and-youcompleteme]

Specifically:

- Get rid of Supertab
- Make a custom function that does the following when `<Tab>` is pressed
  (note, the precedence here is different than in the linked examples):
  - Try to expand a snippet
  - If that doesn't work, try to cycle through completion options (if
    the YouCompleteMe popup is visible) and otherwise try to jump to the
    next UltiSnips placeholder
  - If none of this works, just do a normal tab
- Set up mappings for `<Tab>` and `<S-Tab>` that call this custom
  function; note that these have to happen inside an autocommand in
  order to overwrite the late-mapped default shortcuts set by
  YouCompleteMe (although, as I write this, I think that may not be true
  any more because I can select non-clashing mappings)
- Get rid of `g:UltiSnipsJumpBackwardTrigger` so that it doesn't
  overwrite our `<S-Tab>` mapping
- Set up `<CR>` to accept completion; this gives us an easy way to break
  out of an endless cycle when choosing YouCompleteMe completions inside
  an  UltiSnips placeholder

Some TODOs noted inline, but this basically works. It basically means:

- `<Tab>` to cycle completions, `<S-Tab>` in reverse
- `<Tab>` to expand snippets
- `<Tab>` to jump between placeholders (for now `<S-Tab>` doesn't work
  here, although I think I could make it do so; `<C-k> does work)
- `<Enter>` to get out of completion mode (as noted inline, may need to
  tap twice; not sure why, as the same happens with `<C-y>`)

This last behavior is a slight deviation from how Vim normally handles
autocomplete mappings (see `:h ins-completion-menu`). Basically, it
sometimes inserts newlines when you hit `<Enter>` during completion. I
find this annoying far more often than not, so I am happy to drop this
behavior in the name of progress.
  • Loading branch information
wincent committed Jun 11, 2015
1 parent e1e071e commit 0664b62
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 11 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Expand Up @@ -21,9 +21,6 @@
ignore = untracked
path = roles/dotfiles/files/.vim/bundle/nerdtree
url = https://github.com/scrooloose/nerdtree.git
[submodule "roles/dotfiles/files/.vim/bundle/supertab"]
path = roles/dotfiles/files/.vim/bundle/supertab
url = https://github.com/ervandew/supertab.git
[submodule "roles/dotfiles/files/.vim/bundle/tabular"]
path = roles/dotfiles/files/.vim/bundle/tabular
url = https://github.com/godlygeek/tabular.git
Expand Down
23 changes: 23 additions & 0 deletions roles/dotfiles/files/.vim/autoload/autocomplete.vim
@@ -0,0 +1,23 @@
function! autocomplete#expand_or_jump(direction)
call UltiSnips#ExpandSnippet()
if g:ulti_expand_res == 0
" No expansion occurred.
if pumvisible()
" Pop-up is visible, let's select the next (or previous) completion.
if a:direction == 'N'
return "\<C-N>"
else
return "\<C-P>"
endif
else
call UltiSnips#JumpForwards()
if g:ulti_jump_forwards_res == 0
" We did not jump forwards.
return "\<Tab>"
endif
endif
endif

" No popup is visible, a snippet was expanded, or we jumped, so nothing to do.
return ''
endfunction
1 change: 0 additions & 1 deletion roles/dotfiles/files/.vim/bundle/supertab
Submodule supertab deleted from c8bfec
35 changes: 28 additions & 7 deletions roles/dotfiles/files/.vim/plugin/autocomplete.vim
@@ -1,13 +1,34 @@
" YouCompleteMe and UltiSnips compatibility, with the help of Supertab
" (via http://stackoverflow.com/a/22253548/1626737)
let g:SuperTabCrMapping = 0
let g:SuperTabDefaultCompletionType = '<C-n>'
let g:UltiSnipsExpandTrigger = '<tab>'
let g:UltiSnipsJumpForwardTrigger = '<tab>'
let g:UltiSnipsJumpBackwardTrigger = '<s-tab>'
" YouCompleteMe and UltiSnips compatibility.
let g:UltiSnipsExpandTrigger = '<Tab>'
let g:UltiSnipsJumpForwardTrigger = '<Tab>'

" TODO: change UltiSnips to not try mapping if this is empty; setting this to
" <C-k> here rather than <S-Tab> to prevent it from getting clobbered in:
" ultisnips/pythonx/UltiSnips/snippet_manager.py: _map_inner_keys
let g:UltiSnipsJumpBackwardTrigger = '<C-k>'

let g:ycm_key_list_select_completion = ['<C-j>', '<C-n>', '<Down>']
let g:ycm_key_list_previous_completion = ['<C-k>', '<C-p>', '<Up>']

let g:ulti_jump_forwards_res = 0
let g:ulti_expand_res = 0

augroup WincentAutocomplete
autocmd!

" Override late bindings set up by YouCompleteMe in autoload file.
autocmd BufEnter * exec 'inoremap <silent> <Tab> <C-R>=autocomplete#expand_or_jump("N")<CR>'
autocmd BufEnter * exec 'inoremap <silent> <S-Tab> <C-R>=autocomplete#expand_or_jump("P")<CR>'

" TODO: ideally would only do this while snippet is active
" (see pythonx/UltiSnips/snippet_manager.py; might need to open a feature
" request or a PR to have _map_inner_keys, _unmap_inner_keys fire off
" autocommands that I can subscribe to)
" BUG: seems you have to do <CR> twice to actual finalize completion
" (this happens even with the standard <C-Y>
inoremap <expr> <CR> pumvisible() ? "\<C-Y>" : "<CR>"
augroup END

" Additional UltiSnips config
let g:UltiSnipsSnippetsDir = '~/.vim/ultisnips'
let g:UltiSnipsSnippetDirectories = ['ultisnips']
Expand Down

3 comments on commit 0664b62

@lencioni
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same grievances, and it looks like you solved them. This is great! I'll definitely be stealing this.

@wincent
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I consider it solved. Just make sure you look at the entire series of commits (or the final state) as there are a few subsequent tweaks/fixes.

@chip
Copy link

@chip chip commented on 0664b62 Jul 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are a Vim God.

I wasted a lot of time on various YCM/UltiSnips alternatives and this is the only one that worked for me. Thank you so much! 😄

Please sign in to comment.