nvim-cmp
nvim-cmp copied to clipboard
Manual whole-line completion with ^X^L inserts the wrong entry into the buffer.
FAQ
- [X] I have checked the FAQ and it didn't resolve my problem.
Announcement
- [X] I have checked Breaking change announcement.
Minimal reproducible full config
if has('vim_starting')
set encoding=utf-8
endif
scriptencoding utf-8
if &compatible
set nocompatible
endif
let s:plug_dir = expand('/tmp/plugged/vim-plug')
if !filereadable(s:plug_dir .. '/plug.vim')
execute printf('!curl -fLo %s/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim', s:plug_dir)
end
execute 'set runtimepath+=' . s:plug_dir
call plug#begin(s:plug_dir)
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-buffer'
call plug#end()
PlugInstall | quit
" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
cmp.setup {
mapping = {
['<CR>'] = cmp.mapping.confirm({ select = true })
},
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "buffer" },
}),
}
EOF
Description
Hi, Cool plugin! I've tried it this week for using it with LSP and really like it so far.
My only problem is that it breaks manual whole-line completion.
This seems to happen independently of other plugins, filetype, file content and doesn't depend on if the autocompletion menu was open or not.
So when I type (^X^L
) to start manual line completion, then the automatic completion menu is dismissed (if it was visible), and the manual line completion menu appears, and then I can use <C-p>
and <C-n>
to select an entry, but when I press <CR>
to accept the selected line, then a different line is inserted. This does not always happen, but in more than 50% of my attempts. I tried it with different settings for 'completeopt'
and even tried to override <CR>
to be used only when the menu is visible using cmp.core.view:visible()
, but it made no difference. Sometimes it seems to be an off-by-one selection, but sometimes the inserted text is farther away. It happens when the line is empty, and if there's something written to complete. I've spent about 2 hours trying to fix this, and have no idea what's happening here.
Steps to reproduce
nvim -u ~/cmp-repro.vim ~/cmp-repro.vim
Insert two empty lines at the beginning of the file, go to first line.
Press ^X^L
and use ^P
or ^N
to choose some entry.
Select the entry with <CR>
.
Expected behavior
The selected line should be inserted in the buffer.
Actual behavior
Another line that was not selected is inserted in the buffer.
Additional context
Output of :version
:
NVIM v0.8.0
Build type: Release
LuaJIT 2.1.0-beta3
Compiled by builduser
Running on Manjaro XFCE.
Hm... To be honest, I can't understand the root cause (but reproduced).
set completeopt=menuone,noselect,noinsert
I think the above option will improve the behavior. Could you try this?
I added that line at the beginning of the minimal reproducible config from above, but the bug is still happening.
Is it possible, as a workaround, to completely disable nvim-cmp
for only manual line completion?
Another workaround is to use https://github.com/amarakon/nvim-cmp-buffer-lines but that doesn't currently work for large files.
if has('vim_starting')
set encoding=utf-8
endif
scriptencoding utf-8
if &compatible
set nocompatible
endif
let s:plug_dir = expand('/tmp/plugged/vim-plug')
if !filereadable(s:plug_dir .. '/plug.vim')
execute printf('!curl -fLo %s/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim', s:plug_dir)
end
execute 'set runtimepath+=' . s:plug_dir
call plug#begin(s:plug_dir)
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-buffer'
call plug#end()
PlugInstall | quit
set completeopt=menu,menuone,noselect
" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
cmp.setup {
mapping = {
['<CR>'] = cmp.mapping.confirm({ select = true })
},
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "buffer" },
}),
}
EOF
In my environment, the above setup solve this problem.
I've just tried it with that file, i.e.
nvim --clean -u init.vim.reproduce_bug_in_nvim_cmp_2 init.vim.reproduce_bug_in_nvim_cmp_2
but the bug still happens.
On my first few attempts, the correct line was selected, but after about five attempts, the wrong line began to be selected.
Sometimes, another weird thing happens: I press <CR> to insert a line, but nothing happens, as if I hadn't pressed any key at all.
I just noticed that the same problem (the wrong entry gets selected) occurs when using repeated ^X^P selection, i.e. ^X^P^X^P^X^P to select words that appear in the file in a sequence. In my books, this is a much more serious problem than ^X^L not working.
On the plus, side, I think I found a workaround that actually works: when the entry is already selected, I can just keep writing, and the correct selection keeps being inserted. In other words, as long as I avoid the Enter key, nothing bad happens. Obviously, that makes using <Up>
and <Down>
inconvenient, which is a bit annoying. Maybe <CR>
could be made to work by a mapping that just does <C-n><C-p><Space><Backspace>
, but that does seem annoyingly hacky.
Another idea how to solve this: would it be possible to completely disable nvim-cmp for manual completion? I think, this would be an intuitive solution for everyone who is already used to manual completion.
Okay, so here's a hacky workaround that seems to work for me for now and actually lets me use <CR>
:
['<CR>'] = function(fallback) -- ugly workaround because manual completion (not only ^X^L) is broken!
if cmp.visible() and cmp.get_active_entry() then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Insert })
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Insert })
vim.cmd([[call feedkeys("\<Space>\<BS>")]])
else
fallback()
end
end,
Sorry to spam this issue with workarounds instead of fixing it, but I noticed that my last attempt at a workaround isn't suitable, because it destroys i.a. the snippet functionality. Here's a better way which so far seems to work perfectly:
['<CR>'] = function(fallback) -- ugly workaround for nvim-cmp bug that selects wrong entry for manual completion
if vim.fn.complete_info().mode ~= '' then
cmp.select_next_item({ behavior = cmp.SelectBehavior.Insert })
cmp.select_prev_item({ behavior = cmp.SelectBehavior.Insert })
vim.cmd([[call feedkeys("\<Space>\<BS>")]])
else
if not cmp.confirm({ select = false }) then fallback() end
end
end,
I didn't find a way to check if cmp is active, but the check on complete_info()
seems to be suitable to check if manual completion is active, because when I tested it with an open autocompletion menu of cmp, the return value of complete_info()
was always empty.
Could you please provide detailed steps to reproduce? Apparently I don't understand your actual problem
Ok, I'll try again:
Autocompletion works fine.
Manual completion invoked with ^X^L and ^X^P does not:
The problem is that using <CR>
to input a selected line into the buffer, sometimes a different line is inserted into the buffer instead.
It seems weirdly indeterministic, and does not happen every time, but it happens quite often, probably >50% of my attempts.
I find it very surprising that no one has reported this so far, since ^X^L
and ^X^P^X^P^X^P
are useful features even if autocompletion is available.
Here's an exact sequence of keystrokes which I can use to reproduce. I've tried this several times and the result is the same every time.
Save the minimal reproducible full config from my first post as init.vim.nvim-cmp-1326-reproduce
.
Save the following content as file nvim-cmp-1326-input-file:
111
222
333
where the last line is blank.
Now, in the shell, input:
nvim --clean -u init.vim.nvim-cmp-1326-reproduce nvim-cmp-1326-input-file
and then in neovim, the following keypresses:
Gi<C-x><C-l><CR>
After pressing <C-x><C-l>
the buffer contains 333
(as expected), but after pressing <CR>
, instead 111
will be inserted into the buffer.
Instead of <C-x><C-l>
if I try it with <C-x><C-p>
, the same thing happens.
@bagohart Yes. Your reproduction steps can be reproduced the problem.
I think it's depending on to the completeopt
value. Can you try to change the completeopt?
In my environment, the set completeopt=menu,menuone,noselect
solves the problem.
(Sorry. In my previous post, I wrote set completeopt=menu,menuone,noinsert
but it's just wrong)
The bug is happening with this setting for completeopt, too.
Minimal reproducible full config: (same as above, but with added completeopt
value)
if has('vim_starting')
set encoding=utf-8
endif
scriptencoding utf-8
if &compatible
set nocompatible
endif
let s:plug_dir = expand('/tmp/plugged/vim-plug')
if !filereadable(s:plug_dir .. '/plug.vim')
execute printf('!curl -fLo %s/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim', s:plug_dir)
end
execute 'set runtimepath+=' . s:plug_dir
call plug#begin(s:plug_dir)
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-buffer'
call plug#end()
PlugInstall | quit
set completeopt=menu,menuone,noselect
" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
cmp.setup {
mapping = {
['<CR>'] = cmp.mapping.confirm({ select = true })
},
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "buffer" },
}),
}
EOF
To reproduce:
Save that file as init.vim.nvim-cmp-1326-reproduce-menu_menuone_noselect
Execute nvim --clean -u init.vim.nvim-cmp-1326-reproduce-menu_menuone_noselect nvim-cmp-1326-input-file
.
Press Gi<C-x><C-l><C-p><CR>
.
Again, 333
should be inserted, but 111
is inserted. This works for both ^X^L
and ^X^P
.
Hi! I am facing the same issue. I find the bug is happening if you move backward in any native completion menu.
As an example, if you press <C-n>
in insert mode, then move around with <C-n>
and <C-p>
, completion works fine. However if you start with <C-p>
, or start any kind of completion by moving backwards in the completion menu (for example <C-x> <C-l> <C-p>
), completion breaks. It turns out completion still starts at the top. So if you press <C-p>
twice, it will paste the second suggestion from the top, not from the bottom.
I tried playing around with completeopt
, to no avail, it does not seem to have any effect on native completion as a matter of fact. Note that I am using this with lsp-zero.nvim
, where I can disablenvim-cmp
, which resolves the issue. Of course then you will not have completion for LSP, however it confirms that the issue is with the configuration of nvim-cmp
.
Running Neovim 0.8.2 on Fedora:
NVIM v0.8.2
Build type: RelWithDebInfo
LuaJIT 2.1.0-beta3
Compilation: /usr/bin/gcc -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -DNVIM_TS_HAS_SET_MATCH_LIMIT -DNVIM_TS_HAS_SET_ALLOCATOR -O2 -g -Og -g -Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion -Wdouble-promotion -Wmissing-noreturn -Wmissing-format-attribute -Wmissing-prototypes -Wimplicit-fallthrough -Wvla -fstack-protector-strong -fno-common -fdiagnostics-color=auto -DINCLUDE_GENERATED_DECLARATIONS -D_GNU_SOURCE -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -DMIN_LOG_LEVEL=3 -I/builddir/build/BUILD/neovim-0.8.2/redhat-linux-build/cmake.config -I/builddir/build/BUILD/neovim-0.8.2/src -I/usr/include -I/usr/include/luajit-2.1 -I/builddir/build/BUILD/neovim-0.8.2/redhat-linux-build/src/nvim/auto -I/builddir/build/BUILD/neovim-0.8.2/redhat-linux-build/include
Compiled by mockbuild@koji
Features: +acl +iconv +tui
See ":help feature-compile"
system vimrc file: "$VIM/sysinit.vim"
fall-back for $VIM: "/usr/share/nvim"
Run :checkhealth for more info
@bagohart, is this still reproducing for you using your minimal config from https://github.com/hrsh7th/nvim-cmp/issues/1326#issuecomment-1366593346 on latest main
branch (commit 777450f)? I had the same issue as you, still repro with your first config, but found that your second config with the completeopt
set did not repro the issue -- i.e. completeopt
with noselect
fixed my issue. Why noselect
fixes the behavior I'm observing and why it only affects a few native modes (<C-X><C-L>
,<C-X><C-P>
) is a deeper question.
In my observation, the base error also doesn't seem to be an off-by-1 error, but rather a sign error (it seems to reliably reverse top/bottom of menu). It can be hard to notice because the order of the menu changes and this can make it seem more like an off-by-n. I detail more about that in the duplicate issue I filed before finding this one. This is close to the behavior described by @hmrks but is different in that the behavior I observe does not occur with <C-N>
or <C-P>
completion, but rather only the two <C-X>
modes described above, and does not depend on initial <C-P>
.
@hmrks As another user, I do not see the same behavior with <C-N>
or <C-P>
completion. Simply disabling nvim-cmp
in lsp-zero.nvim
and seeing the issue disappear is useful information, but ultimately insufficient evidence that nvim-cmp
is the culprit. It only shows that lsp-zero
and nvim-cmp
together can cause the issue. It very well might still be nvim-cmp
, but without a minimal reproducible workflow there is nothing to help the maintainer or a contributor reproduce or trace the issue to begin troubleshooting.
NVIM v0.8.3
Build type: Release
LuaJIT 2.1.0-beta3
Compiled by builduser
Features: +acl +iconv +tui
See ":help feature-compile"
OS: WSL2 running Arch Linux on Win 11
@bagohart, is this still reproducing for you using your minimal config from #1326 (comment) on latest
main
branch (commit 777450f)? I had the same issue as you, still repro with your first config, but found that your second config with thecompleteopt
set did not repro the issue -- i.e.completeopt
withnoselect
fixed my issue. Whynoselect
fixes the behavior I'm observing and why it only affects a few native modes (<C-X><C-L>
,<C-X><C-P>
) is a deeper question.
Yes, it still reproduces with the same example and procedure described above, still using the same file
111
222
333
I think I am on the same nvim version:
NVIM v0.8.3
Build type: Release
LuaJIT 2.1.0-beta3
Compiled by builduser
Features: +acl +iconv +tui
See ":help feature-compile"
system vimrc file: "$VIM/sysinit.vim"
fall-back for $VIM: "/usr/share/nvim"
Run :checkhealth for more info
Snippets for debugging.
:imap \ <cmd>put =execute('lua print(vim.fn.complete_info({''selected''}).selected)')<CR>
imap [ <cmd>lua require('cmp.utils.feedkeys').call(require('cmp.utils.keymap').t(string.rep('<C-P>',1)),'in')<CR>
With the above mapping, you can hit \
in insert mode
and it will show you what nvim thinks is selected in the native pumenu
. You can also use [
navigate up, mimicking the feedkeys
behavior that nvim-cmp
is using to navigate native pumenu
.
In lua/cmp/init.lua
, nvim-cmp
differentiates between cmp
menus and native pumenu
s. cmp
menu is checked with cmp.core.view:visible()
and native menus checked with vim.fn.pumvisible()
. Behavior is different depending on which type of menu is visible.
- For
pumvisible()
native menu:- to navigate the menu up,
select_prev_item
usesfeedkeys...
. The mapping above for[
mimics this to make debugging clearer. -
cmp.confirm
callscomplete_info
. This normally gives the correct index and can be checked with the\
mapping above. Iffeedkeys.... <C-P>...
is called first, e.g. with[
mapping above, then theselected
index fromcomplete_info
is wrong.
- to navigate the menu up,
Testing just the \
mapping in nvim --clean
, I was unable to get wrong index, so my initial hunch is that this isn't an upstream issue but I can't rule it out completely. I am not a lua or nvim expert at all, so not sure if I'll be able to tackle this but hopefully tracking down this far will help.
Repro config
CLICK ON ME! Click to expand and save as `repro_nvim_cmp_vimrc`
if has('vim_starting')
set encoding=utf-8
endif
scriptencoding utf-8
if &compatible
set nocompatible
endif
let s:plug_dir = expand('/tmp/plugged/vim-plug')
if !filereadable(s:plug_dir .. '/plug.vim')
execute printf('!curl -fLo %s/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim', s:plug_dir)
end
execute 'set runtimepath+=' . s:plug_dir
call plug#begin(s:plug_dir)
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-buffer'
call plug#end()
PlugInstall | quit
set completeopt=menu,menuone,noselect
" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
cmp.setup {
mapping = cmp.mapping.preset.insert({
['<CR>'] = cmp.mapping.confirm({ select = true })
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "buffer" },
}),
}
EOF
imap \ <cmd>put =execute('lua print(vim.fn.complete_info({''selected''}).selected)')<CR>
imap [ <cmd>lua require('cmp.utils.feedkeys').call(require('cmp.utils.keymap').t(string.rep('<C-P>',1)),'in')<CR>
nmap \\ iaaa<CR>bbb<CR>ccc<CR>ddd<CR>eee<CR>
Note the 3 maps, \
to print the complete_info
index, [
to mimic the select_prev_item
behavior on pumenu
, and \\
to make it fast to get to an example file that shows the error.
Repro Steps
- download the config above to
repro_nvim_cmp_vimrc
-
nvim --clean -u repro_nvim_cmp_vimrc
and wait to get an empty buffer - in normal mode, hit
\\
which is mapped to create the example file:
and will put you on a newline under "eee" in insert modeaaa bbb ccc ddd eee
- in insert mode,
<C-X><C-L>
to pull up the native line completionpumenu
, which should have 5 lines in order from "aaa" at top to "eee" at bottom - in insert mode,
[[[[
to navigate backwards in the menu 4 times, apparently selecting the "bbb" option - in insert mode, with the
pumenu
still visible,\\\\
to see the selected item index be put in the buffer 4 times- (I decided to keep hitting
\
binding so that it goes down far enough and is not hidden by the completion menu)
- (I decided to keep hitting
- Notice that the index that it is showing is 3, when it should be 1
- in insert mode,
<CR>
to select what looks like "bbb", and see "ddd" get inserted instead. Wrong behavior, but corresponds to thecomplete_info
index.
Some more investigation. I think this is a subtle upstream bug affecting the scripting interface for complete_info
. Linked this issue in the upstream report, follow there to repro and see that complete_info
is not behaving as documented.
for now, here's a different, only slightly less hacky workaround than sending <Space><BS>
that relies on <C-X><C-Z>
stop completion.
mapping = cmp.mapping.preset.insert({
-- cmp.mapping.preset.insert provides <C-N>, <C-P>, etc
['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<CR>'] = function(fallback)
if vim.fn.pumvisible() == 1 then
-- native pumenu
-- workaround for neovim/neovim#22892
if vim.fn.complete_info({'selected'}).selected == -1 then
-- nothing selected, insert newline
feedkeys.call(keymap.t('<CR>'), 'in')
else
-- something selected, confirm selection by stopping Ctrl-X mode
-- :h i_CTRL-X_CTRL-Z*
feedkeys.call(keymap.t('<C-X><C-Z>'), 'in')
end
else
-- `nvim-cmp` default confirm action
-- Accept currently selected item.
-- Set `select` to `false` to only confirm explicitly selected items.
cmp.mapping.confirm({ select = false })(fallback)
end
end
}),
You'll need to put the following require
s somewhere sensible also:
local cmp = require'cmp'
local feedkeys = require('cmp.utils.feedkeys')
local keymap = require('cmp.utils.keymap')
Alright, now I'll stop spamming this thread. Hopefully upstream will fix the underlying issue and then we can get a better sense of if anything needs to change in nvim-cmp
.
@d-r-a-b is great. I checked the vim issue thread. thank you!
@d-r-a-b I'm now on neovim 0.9.4 and cannot reproduce the bug anymore, with neither ^x^l
nor ^x^p
. Same for you? If so, I guess this issue can finally be closed.
Edit: nevermind, it still doesn't work. I tried to remove the mapping, but didn't realize that it was then shadowed by another plugin, and that seemed to make the bug disappear. But it's still there.