lifepillar / vim-mucomplete

Chained completion that works the way you want!
MIT License
912 stars 18 forks source link

Omnifunc is skipped if `c-n` or `c-p` or `tags` are set #179

Open cideM opened 4 years ago

cideM commented 4 years ago

I have gopls (Golang LSP) installed and I'm using nvim-lsp and the built-in LSP to use gopls for omnifunc completion. When invoked manually via c-x c-o I get gopls completion candidates. It also works if I remove c-n and c-p from my completion chains, so that only omni is left. But as soon as I re-add c-n or c-p omnifunc completion via mucomplete doesn't work. If I manually cycle through the chains it never shows omni.

lifepillar commented 4 years ago

Can you show the relevant configuration from your vimrc? Even better, can you reproduce your issue with the provided troubleshooting_vimrc.vim (you need to add your settings)?

lifepillar commented 4 years ago

Also, can you please provide links to the relevant plugins?

Are you using Vim or Neovim?

cideM commented 4 years ago

I'll try to come up with a reproducible example

cideM commented 4 years ago

@lifepillar let me know if this repository with a Dockerfile works. At least I can reproduce the issue this way.

lifepillar commented 4 years ago

Ok, I see. Does it help to add noselect to completeopt?

lifepillar commented 4 years ago

You should also unset g:mucomplete#reopen_immediately and possibly set g:mucomplete#completion_delay (see :help mucomplete-compatibility).

cideM commented 4 years ago

Unfortunately neither of the two options had any effect on the issue

lifepillar commented 4 years ago

Ok, I'll debug further.

lifepillar commented 4 years ago

I am wondering whether this is just an issue with MUcompleteNotify. I am using your Docker image for testing and I have set completeopt=menuone,noselect in init.vim. Now, using your main.go, when I type fu followed by Tab I am offered the same completion as when I type fu followed by CTRL-X CTRL-O. Can you provide an example in which the set of completions is different?

mg979 commented 4 years ago

I think the problem is that nvim-lsp omnifunc is actually asynch. I had similar problems with mucomplete and nvim-lsp because mucomplete doesn't handle well asynch sources. When he puts 'omni' alone it works because it's the only one that is called, but if he puts something after it, omni yields no results as far as mucomplete can see (because it's asynch), so mucomplete calls c-n that overwrites omni.

lifepillar commented 4 years ago

mucomplete doesn't handle well asynch sources

Well, MUcomplete doesn't handle asynch at all. If that is the culprit, I guess that MUcomplete cannot be used with nvim-lsp.

How is asynchronous completion implemented? With complete()?

mg979 commented 4 years ago

Probably. Anyway, if mucomplete could handle asynch sources it would be a big improvement, considering that lsp-based completions are generally asynch.

lifepillar commented 4 years ago

That would probably require a rework of the plugin. Currently, MUcomplete is based on strictly sequential fallback: try first source and wait for result; if no result, try second source and wait, etc. Asynchronous completion requires a totally different approach, Maybe, timers might be used; or maybe something else.

mg979 commented 4 years ago

I don't think you'd need to rework everything, for example you could try something like this (I didn't test it...).

Start by keeping a dict of recognized asynch methods/sources, suppose you fill this list somehow (yourself, or by letting an asynch source to be registered as such):

" key is the method, value is the callback status
let s:asynch_methods = {'nvim-lsp': 0}

Then when you verify the completion, if the current method is asynch you start a timer, and delay the verification:

fun! s:verify_completion(...)
  let method = s:compl_methods[s:i]
  if !a:0 && has_key(s:asynch_methods, method)
    if s:asynch_methods[method] == 0
      " completion hasn't been tried yet
      let s:asynch_methods[method] = 1
      call timer_start(100, function('s:verify_completion'))
      return ''
    else
      " keys have been fed by the callback, reset callback status
      " no return value, proceed to check pumvisible()
      let s:asynch_methods[method] = 0
    endif
  elseif a:0
    " this is the timer callback
    call feedkeys("\<Plug>(MUcompleteVerify)")
    return ''
  endif
  return pumvisible()
            \ ? s:act_on_pumvisible()
            \ : (method ==# 'cmd' ? s:ctrlx_out : '')
            \ . s:next_method()
endf
mg979 commented 4 years ago

Btw the problem with this approach is that you have to guess how long to wait (and overestimate it to be safer), it's not like the asynch source calls the next method on its own if it cannot provide completions (it would be ideal but not really possible unless a source supports this behaviour).

Edit: I tried the approach above but I couldn't make it work.

alexkornitzer commented 3 years ago

Okay, so I am having this issue (or similar) as well and its most prominent when using vim-lsp with pyls. I have no idea what the cause is but I don't believe its due to async as from what I understand vim-lsp is sync by default. https://github.com/prabirshrestha/vim-lsp/blob/master/doc/vim-lsp.txt#L1709

What I have found that fixes the issue but I have no idea on the fallout or why it works (@lifepillar maybe you could elaborate?) is to skip the pumvisible check on omni.

  257 fun! s:verify_completion()
+ 258   if s:compl_methods[s:i] == "omni"
+ 259     return s:act_on_pumvisible()
+ 260   endif
  261   return pumvisible()
  262            \ ? s:act_on_pumvisible()
  263            \ : (s:compl_methods[s:i] ==# 'cmd' ? s:ctrlx_out : '')
  264            \ . s:next_method()
  265 endf
lifepillar commented 3 years ago

@alexkornitzer Could you provide a minimal vimrc to reproduce your issue?

alexkornitzer commented 3 years ago

Why of course, sorry for not doing that to start so, here is my minimal conf:

if empty(glob("~/.vim/autoload/plug.vim"))
    execute '!curl -fLo ~/.vim/autoload/plug.vim'
    \ 'https://raw.github.com/junegunn/vim-plug/master/plug.vim'
endif
call plug#begin('~/.vim/plugged')
Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/vim-lsp'
Plug 'lifepillar/vim-mucomplete'
call plug#end()

set completeopt-=longest,preview
set completeopt+=menu,menuone,noinsert,noselect
set shortmess+=c

if executable('pyls')
  au User lsp_setup call lsp#register_server({
    \ 'name': 'pyls',
    \ 'cmd': {server_info->['pyls']},
    \ 'whitelist': ['python'],
    \ })
  autocmd FileType python setlocal omnifunc=lsp#complete
endif

let g:mucomplete#enable_auto_at_startup = 1
let g:mucomplete#chains = {}
let g:mucomplete#chains.default  = ['path', 'omni', 'c-p', 'dict', 'uspl', 'ulti']

The following to install pyls:

pip install --user python-language-server

And here is the test file I have been using:

aaa
aaaa
aaaaa
aaaaaa

import sys
sys.a

Hitting tab with the cursor after sys.a fails without the change in my comment above.

inventionate commented 3 years ago

I tried to use the Neovim built-in LSP client (Intelephense language server) in a chain ['omni', 'path', 'c-n'] and encountered exactly this problem. It quickly became apparent that the problem was the asynchrony of the built-in LSP client. One solution is to use the omnifunc function synchronously, but unfortunately this is currently only possible with a separate function. I have posted this in the linked discussion (neovim/neovim#12390). Maybe it will help someone and I am happy to receive suggestions for improvement. Anyway, thanks for the hints.