ncm2 / ncm2-ultisnips

44 stars 3 forks source link

UltiSnips LSP function snippets not working #6

Closed YaLTeR closed 6 years ago

YaLTeR commented 6 years ago

Hi!

I have a setup that uses the Tab key for both expanding snippets and jumping:

let g:UltiSnipsExpandTrigger       = "<Tab>"
let g:UltiSnipsJumpForwardTrigger  = "<Tab>"
let g:UltiSnipsJumpBackwardTrigger = "<S-Tab>"

function! MappingITab()
  let snippet = UltiSnips#ExpandSnippetOrJump()
  if g:ulti_expand_or_jump_res > 0
    return snippet
  elseif pumvisible()
    return "\<C-Y>"
  else
    return "\<Tab>"
  endif
endfunction

inoremap <silent> <expr> <Tab> MappingITab()

With ncm this works well; with ncm2 however it doesn't perform snippet expansion on LSP function completions for some reason. Expanding UltiSnips snippets (both short ones like if which don't trigger the completion yet, and long ones which do) and jumping around them works fine. Expanding LSP functions with

inoremap <silent> <expr> <CR> ncm2_ultisnips#expand_or("\<CR>", 'n')

works fine too, but if I use Tab here it doesn't work for both expanding and jumping, and also it doesn't expand short UltiSnips snippets (which don't trigger the completion menu), so it's not optimal.

roxma commented 6 years ago

ncm2_ultisnips#expand_or only expands dynamic completion snippet.

If you want it to have more features you need to define your own <Plug>(more_handling) then

inoremap <silent> <expr> <CR> ncm2_ultisnips#expand_or("\<Plug>(more_handling)", 'n')
YaLTeR commented 6 years ago

I think I got it working with:

" UltiSnips+NCM function parameter expansion

" We don't really want UltiSnips to map these two, but there's no option for
" that so just make it map them to a <Plug> key.
let g:UltiSnipsExpandTrigger       = "<Plug>(ultisnips_expand_or_jump)"
let g:UltiSnipsJumpForwardTrigger  = "<Plug>(ultisnips_expand_or_jump)"
" Let UltiSnips bind the jump backward trigger as there's nothing special
" about it.
let g:UltiSnipsJumpBackwardTrigger = "<S-Tab>"

" Try expanding snippet or jumping with UltiSnips and return <Tab> if nothing
" worked.
function! UltiSnipsExpandOrJumpOrTab()
  call UltiSnips#ExpandSnippetOrJump()
  if g:ulti_expand_or_jump_res > 0
    return ""
  else
    return "\<Tab>"
  endif
endfunction

" First try expanding with ncm2_ultisnips. This does both LSP snippets and
" normal snippets when there's a completion popup visible.
inoremap <silent> <expr> <Tab> ncm2_ultisnips#expand_or("\<Plug>(ultisnips_try_expand)")

" If that failed, try the UltiSnips expand or jump function. This handles
" short snippets when the completion popup isn't visible yet as well as
" jumping forward from the insert mode. Writes <Tab> if there is no special
" action taken.
inoremap <silent> <Plug>(ultisnips_try_expand) <C-R>=UltiSnipsExpandOrJumpOrTab()<CR>

" Select mode mapping for jumping forward with <Tab>.
snoremap <silent> <Tab> <Esc>:call UltiSnips#ExpandSnippetOrJump()<cr>
dvcrn commented 6 years ago

Thanks a lot for sharing @YaLTeR! You saved me a lot of headache in getting this play nicely together

kawing-chiu commented 4 years ago

I'm quite confused......What is completion snippet and what is LSP snippet, are they the same thing? Can someone please point me to some documentation? I thought that ncm2-ultisnips is about ultisnips's snippet, but ncm2_ultisnips#expand_or is used for another kind of snippets?

YaLTeR commented 4 years ago

So there's a number of components interacting together here.

First, there's UltiSnips, a snippet manager and expansion engine. This is the thing that expands snippets. By itself, it can expand snippets that are already in the buffer (insert if, hit <Tab>, get an expansion).

Then, there's NCM2. It is a completion engine. It gets completions from other plugins and displays it to the user to choose. Completions provided by the plugins can be custom snippets. NCM2 has a plugin, ncm2-ultisnips, that can take these custom snippets and tell UltiSnips about them so UltiSnips can expand them.

Finally, there's LanguageClient-neovim. It queries the language server for completions and forwards them to NCM2 to present to the user. The completions returned by the language server can be snippets in a special LSP snippet format (which is different from the UltiSnips format). LanguageClient-neovim tells NCM2 about the snippet in each of the completion items. Then ncm2-ultisnips converts that snippet from the LSP snippet format to the UltiSnips format, and invokes UltiSnips to actually expand it.

I believe this is how it works. The configuration is, then, to make sure all of this plays well together.

kawing-chiu commented 4 years ago

@YaLTeR Thanks for the explaining! I understand the ultisnips snippets. Is there any concrete examples of the latter two types of snippets? For example, if I'm using ncm2-ultisnips + LanguageClient-neovim + pyls to write Python code, which keywords can I used to get these snippets? I can hardly test my configuration since I haven't found any examples of these yet.

YaLTeR commented 4 years ago

Have you set let g:LanguageClient_hasSnippetSupport = 1? The snippets are generally the function arguments which come from function completions. I'm not sure if pyls provides them.