Possible to detect if a completion-request has results other than using timer?
Closed this issue · 16 comments
Hi. Is it correct that mucomplete is using a timer to check if a completion-request has results to determine if it should invoke the next completion-method? Does vim really not have a native way to notify if a completion-request (like <c-n>
) opened a pum-window with results in it? Thanks.
No, it does not use timers for that. In fact, MUcomplete works without +timers: it should be compatible with Vim as old as v7.2. As you have guessed, it performs a completion request and then checks whether the pop-up menu has opened.
You can check whether the pumwindow opens but how do you know if it never opens because there are no results so you know when to trigger the next completion-method?
Vim is single-threaded, so it's as simple as sending the completion characters to Vim (say, by calling feedkeys("\<c-x>\<c-n>")
) then check if pumvisible()
returns true.
Info
NVIM v0.8.3
Minimal vimrc
function! Complete()
call feedkeys("\<c-x>\<c-n>", 'n')
if pumvisible()
echo 'pum'
else
echo '!pum'
endif
endfunction
inoremap <tab> <cmd>call Complete()<cr>
set noshowmode shortmess+=c
Example
- Open nvim with a new empty buffer
- Type
foo faa f*
where*
indicates the cursor-position - Press
<tab>
to triggerComplete()
based on the vimrc above
Expected
Pum-window should be opened with the two completion-suggestions foo
and faa
and the message pum
should be printed to indicate that the pum-window is open.
Actual Results
The message !pum
is printed and indicates that the pum-window is not visible although it is visible like can be seen in the following image.
:h feedkeys
feedkeys(string)
is non-blocking, try feedkeys(string, "nx")
.
feedkeys("\<c-x>\<c-n>", 'nx')
doesn't open the pum-window at all and moves the cursor one character to the left such that I end up with foo faa *f
without a pum-window and the message !pum
in the above example. Thank you for your time by the way. The only way I've gotten it to work is to run a timer to check sometime later if a pum-window is open (which is really not optimal).
This works:
function! Complete()
if pumvisible()
echo 'pum'
else
echo '!pum'
endif
endfunction
inoremap <buffer> <tab> <c-x><c-n><cmd>call Complete()<cr>
set noshowmode shortmess+=c
feedkeys(..., "x")
can't work because it feeds keys in normal mode, not insert mode. feedkeys(...)
can't work because it's non-blocking. So you can't use feedkeys
here.
Anyway if you tell what exactly you're trying to do it would help.
If you simply want to check pumvisible
after <tab>
, you can do
function! CheckPum()
if pumvisible()
echo 'pum'
else
echo '!pum'
endif
endfunction
inoremap <Plug>(CheckPum) <cmd>call CheckPum()<cr>
imap <tab> <plug>(MUcompleteFwd)<Plug>(CheckPum)
Thanks for chiming in! The example with feedkeys()
was misleading, sorry. @mg979 's last posted example nicely illustrates how MUcomplete actually does it (except for some technicalities).
My goal is to create a minimal example where I can trigger a completion and check if it produced any results. If the completion has no results I want to automatically trigger a 2nd completion-method. Basically I am trying to understand how chain-completion works under the hood.
I came up with the following minimal example to check if pum is open after triggering a completion:
function! Complete()
return "\<c-x>\<c-n>"
endfunction
function! CheckPum()
if pumvisible()
echo 'pumvisible'
else
echo '!pumvisible'
endif
endfunction
inoremap <expr> <plug>(Complete) Complete()
imap <plug>(CompleteAndCheck) <plug>(Complete)<cmd>call CheckPum()<cr>
imap <tab> <plug>(CompleteAndCheck)
set noshowmode shortmess+=c
This works but I wonder if this really is idiomatic vim. Next I will try to somehow reuse <plug>(CompleteAndCheck)
to write a function which triggeres a 2nd completion-method in case of !pumvisible
.
Thank you for your help. The final solution works although it feels a little bit hacky because now I have to misuse mappings as functions because feedkeys() does not block.
I wouldn't call MUcomplete's approach idiomatic, but I don't think that yours is a misuse of mappings either.
MUcomplete was started to satisfy exactly your need: I wanted to complete with omnifunc and fallback to keyword completion. This is what I had in my vimrc
before MUcomplete was developed.
How does mucomplete deal with the fact that v:lua.vim.lsp.omnifunc
async is? The code above doesn't work in this case.
It doesn't. But it's a problem of neovim implementation, more than this plugin's fault. In neovim you can implement a blocking lsp omnifunc, but the guy who wrote the default implementation probably thought that async is cooler. It's not the only example of this in neovim unfortunately.
Note that Neovim is not a drop-in replacement for Vim: at this point the two projects have diverged in significant ways. Neovim compatibility is not a goal of this project, nor has this project ever stated Neovim compatibility.
Regarding the async nature of some completion plugins: some people have seemingly succeeded in using MUcomplete with LanguageClient-neovim, and possibly other async plugins, but, afaik, only with +timers
(and a couple of MUcomplete settings: see :help mucomplete-compatibility
). Your mileage may vary.
I should also stress that the Readme clearly states the minimalistic nature of MUcomplete: that is, it's scope is limited to providing a more streamlined interface to Vim's built-in completion methods. It's also worth searching for “Does MUcomplete's implementation have any limitations?” in :help mucomplete.txt
.