hrsh7th / vim-vsnip-integ

vim-vsnip integrations to other plugins.
MIT License
127 stars 15 forks source link

LSP snippet expansion with omnifunc #24

Closed jandamm closed 4 years ago

jandamm commented 4 years ago

I'm using vim-lsp with omnifunc and vsnip + integ for snippet expansion. This works fine if I have multiple possible completions but doesn't work when there is only one candidate. (I'm not using menuone, because it butchers C-p).

Is there anything to fix this or do I have to use menuone?

hrsh7th commented 4 years ago

it seems working fine on my side.

vsnip_integ

Could you explain your repro steps or your expected behavior?

jandamm commented 4 years ago

Yes that is exactly what I was expecting/hoping to get.

I've played around with some settings and now the snippet feature isn't working anymore 😅

Before I had used vim-lsp + asyncomplete and UltiSnips. But I had to hack it quite a bit to have asyncomplete behave like omnifunc.

I'll play around with it some more, can you tell me what config you've used (one from your dotfiles)?

My hope is to use vim-lsp (until nvim 0.5 is out) with set omnifunc=lsp#complete and vsnip for snippets/parameter expansion. I haven't changed the completeopt from its defaults (menu,preview).

hrsh7th commented 4 years ago

In my knowledge, You can enable the feature as the previous image by setting like following.

Plug 'prabirshrestha/vim-lsp'
Plug 'hrsh7th/vim-vsnip'
Plug 'hrsh7th/vim-vsnip-integ'

The steps in the previous image is below.

  1. Insert call getbufli
  2. Press <C-x><C-o><C-n> (current line be call getbufline at this time)
  3. Press ( to expand snippet.

Hm... If your couldn't get the correct result, please put the your vimrc, sorry!

jandamm commented 4 years ago

I've created a min-vimrc for neovim 0.4.3:

set omnifunc=lsp#complete
set completeopt=menu,noselect

and cloned (into pack/*/start):

prabirshrestha/vim-lsp
mattn/vim-lsp-settings
hrsh7th/vim-vsnip
hrsh7th/vim-vsnip-integ

And the result after icall getbufli<C-x><C-o><C-n>( is:

Screen Shot 2020-07-06 at 13 03 46

There aren't any placeholders.

hrsh7th commented 4 years ago

Hm... Your config seems correct, it's weird. I will investigate it!

jandamm commented 4 years ago

This isn't tied to a single element. It is the same when I call the completion on call getbu where three elements appear in the menu.

The latter case worked before but isn't working anymore. 🤷‍♂️

jandamm commented 4 years ago

My bad, I guess it had been working because I still had https://github.com/thomasfaingnaert/vim-lsp-ultisnips installed.

hrsh7th commented 4 years ago

Oh, thank you for investigating!

If you have no issue now, feel free to close this.

jandamm commented 4 years ago

It still doesn't work. I was wondering why it was for multiple results yesterday and now didn't work at all.

hrsh7th commented 4 years ago

I found the cause maybe.

vim -u ~/.vimrc.mini ~/.vimrc.mini does not work but e ~/.vimrc.mini after vim -u ~/.vimrc.mini work as expected.

So I guess it caused by the initializing timing problem. What do you think about my guessing?

jandamm commented 4 years ago

Yes, you are absolutely right. Loading a file after startup does work just fine.

Is there a way to expand a snippet without typing (? Is this what g:vsnip_integ_config.auto_expand is for?

hrsh7th commented 4 years ago

I will improve initialization, thanks!

You can press <C-y> to expand snippet.

FYI, I using below mapping to expand snippet.

inoremap <expr><CR> complete_info(['selected']).selected == -1 ? '<CR>' : '<C-y>'
jandamm commented 4 years ago

But this mapping would only work when there is more than one selection or with using menuone.

jandamm commented 4 years ago

Is there something possible like

au CompleteDone if vsnip_can_expand_lsp | vsnip_expand_lsp | endif
hrsh7th commented 4 years ago

No, vim/nvim does not fire CompleteDone at the time of pressed <C-x><C-o><C-n>.

the behavior that you want can enable by the below codes maybe.

autocmd TextChangedI * call timer_start(0, { -> s:on_text_changed_i() })
function s:on_text_changed_i()
  if complete_info(['selected']).selected != -1 && len(complete_info(['items']).items) == 1
    call feedkeys("\<C-y>", 't')
  endif
endfunction

But I can't support to use it.

jandamm commented 4 years ago

Thank you for your help so far 👍 It now works better than my previous setup!

With the mapping of CR it works in most cases. I'll try to find a solution for expanding when there is only one candidate. In case I find a reliable solution, I'll post it here.

I leave this issue open in case you want to use it to track the initialization issue. Otherwise feel free to close it.

Thanks again 😊

jandamm commented 4 years ago
set completeopt=menuone
autocmd CompleteChanged * call timer_start(0, { -> s:on_complete_changed() })
function s:on_complete_changed()
  if complete_info(['selected']).selected != -1 && len(complete_info(['items']).items) == 1
     call feedkeys("\<C-y>", 't')
  endif
endfunction

Adding this to my vimrc seems to work just fine. (Sometimes it flickers as the pum is displayed briefly)

This also has the benefit of being able to use noinsert and still having a single completion inserted.

Using TextChangedI would result in copying the char from the line above when there is no snippet in completion.

hrsh7th commented 4 years ago

Thank you for your report. Hm... When I was trying it with CompleteChanged, I couldn't get the correct behavior (I might have overlooked something)

hrsh7th commented 4 years ago

I might solve the initialization problem on https://github.com/hrsh7th/vim-vsnip-integ/pull/25.

Could you test it?

jandamm commented 4 years ago

Yes, it is fixed now. Thank you 👍

Ah, I forgot one line (and updated it). The key with CompleteChanged is using menuone instead of menu. This will open the pum and then checks if there is only one completion and confirms this with <C-y> which expands the snippet. This also does not work with noselect but works fine with noinsert which is the way I use it now. set completeopt=menuone,noinsert which results in set completeopt=menu when there is only one candidate.

hrsh7th commented 4 years ago

Ah, I see. Thank you for the explanation and confirmation.

I will merge the PR! thanks.

jandamm commented 4 years ago

A little follow up. The solution above did work for <C-x><C-o> but did fail for other completion methods (namely <C-x><C-p>.

I've tested a bit more and found a suitable solution:

augroup name
    au!
    autocmd CompleteDone * call timer_start(0, { -> s:on_complete_done() })
augroup END
function s:on_complete_done()
    if &completeopt =~# '\v<menuone>' | return | endif
    let info = complete_info(['selected', 'items', 'mode'])
    if info.mode ==? 'eval'
                \ && info.selected != -1
                \ && len(info.items) == 1
        call feedkeys(" \<BS>", 'n')
    endif
endfunction

I've found an even better solution, but it relies on undocumented behaviour and may break/not work for every version of Vim. It did work flawlessly with Neovim (0.4.3) though. -2 seems to be the index when an item has been inserted directly and has been the only match. So this should work fine as well:

function s:on_complete_done()
    if &completeopt =~# '\v<menuone>' | return | endif
    let info = complete_info(['selected', 'mode'])
    if info.mode ==? 'eval' && info.selected != -2
        call feedkeys(" \<BS>", 'n')
    endif
endfunction