hrsh7th / nvim-cmp

A completion plugin for neovim coded in Lua.
MIT License
7.47k stars 370 forks source link

Snippet source shows up in preview before confirming #1780

Closed nguyenvukhang closed 3 months ago

nguyenvukhang commented 6 months ago

FAQ

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-nvim-lsp'
Plug 'hrsh7th/vim-vsnip'
Plug 'neovim/nvim-lspconfig'
call plug#end()
PlugInstall | quit

" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
cmp.setup {
  snippet = {
    expand = function(args)
      vim.fn["vsnip#anonymous"](args.body)
    end,
  },

  mapping = {
    ['<C-n>'] = cmp.mapping.select_next_item(),
    ['<CR>'] = cmp.mapping.confirm({ select = true })
  },

  preselect = cmp.PreselectMode.None,

  sources = cmp.config.sources({
    { name = "nvim_lsp" },
  }),
}
EOF

lua << EOF
local capabilities = require('cmp_nvim_lsp').default_capabilities()

require'lspconfig'.rust_analyzer.setup {
  capabilities = capabilities,
}
EOF

Description

In a Rust project, when auto-completing on a function that has parameters, the snippet source code gets pasted into the main buffer when using cmp.mapping.select_next_item()

Steps to reproduce

  1. Initialize a rust (cargo) project with cargo init test-proj
  2. Replace the contents of main.rs with this:

    fn f(a: u8, b: u8) {}
    
    fn main() {
    }
  3. Place cursor in main(), go into insert mode, and press f to trigger autocomplete on the function f (wait for rust_analyzer to load first).
  4. Press <C-n> to run cmp.mapping.select_next_item(), and then just move the cursor elsewhere. The buffer now reads:

    fn f(a: u8, b: u8) {}
    
    fn main() {
       f(${1:a}, ${2:b})$0
    }

Expected behavior

I'm not sure what's the expected behavior here, but if I had the choice I would like there to be either nothing printed or something neater printed.

Actual behavior

The source code of the snippet gets printed into the editing buffer, and stays there if the user moves the cursor away mid-selection.

Additional context

If the user confirms the selection with <CR> right after pressing <C-n>, then the snippet source will be replaced with the correct output, which is the snippet in action and the cursor highlighting the first argument, a.

nguyenvukhang commented 6 months ago

After a quick bisect I found the breaking commit to be 51260c0 and this particular patch fixes this particular issue, though I don't know if it introduces new ones (it does still pass all tests):

diff --git a/lua/cmp/entry.lua b/lua/cmp/entry.lua
index 09946f2..800e408 100644
--- a/lua/cmp/entry.lua
+++ b/lua/cmp/entry.lua
@@ -116,7 +116,7 @@ entry.get_word = function(self)
       word = str.trim(self:get_completion_item().textEdit.newText)
       local overwrite = self:get_overwrite()
       if 0 < overwrite[2] or self:get_completion_item().insertTextFormat == types.lsp.InsertTextFormat.Snippet then
-        word = str.get_word(word, string.byte(self.context.cursor_after_line, 1), overwrite[1] or 0)
+        word = str.get_word(vim.lsp.util.parse_snippet(word), string.byte(self.context.cursor_after_line, 1), overwrite[1] or 0)
       end
     elseif not misc.empty(self:get_completion_item().insertText) then
       word = str.trim(self:get_completion_item().insertText)
OliverChao commented 6 months ago

hey, I found that the default SelectBahavior is Insert. If you want the same behavior with Up/Down, you can set:

local types = require("cmp.types")

['<C-n>'] = cmp.select_next_item({ behavior = types.cmp.SelectBehavior.Select })
LinoWhy commented 3 months ago

Same issue for nevom nightly, but works as expected for version 0.9.6

hrsh7th commented 3 months ago

I'll fix it. sorry for late.

LinoWhy commented 3 months ago

Thanks for your reply!

But after updating the plugin, neovim version 0.9.6 may occur the following error message when completion is triggered.


...local/share/nvim/lazy/nvim-cmp/lua/cmp/utils/snippet.lua:27: attempt to call field 'list_cont
ains' (a nil value)

And with neovim nightly, the problem still exists...
LinoWhy commented 3 months ago

Thanks for your reply!

But after updating the plugin, neovim version 0.9.6 may occur the following error message when completion is triggered.

...local/share/nvim/lazy/nvim-cmp/lua/cmp/utils/snippet.lua:27: attempt to call field 'list_cont
ains' (a nil value)

And with neovim nightly, the problem still exists...

@hrsh7th Updates: The problem is not fixed for neovim nightly. And it works as expected for neovim 0.9.6.

hrsh7th commented 3 months ago

No. It's fixed.

LinoWhy commented 3 months ago

@hrsh7th Tested with my config, finding that the problem only occurs with an empty option of the select_next_item, such as:

["<C-k>"] = cmp.mapping.select_prev_item(),
["<C-j>"] = cmp.mapping.select_next_item(),

But if I fill the options with either Insert or Select, that works as expected.

["<C-k>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }), -- or Select
["<C-j>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }), -- or Select
hrsh7th commented 3 months ago

No, the implementation allows you to omit the argument. https://github.com/hrsh7th/nvim-cmp/blob/main/lua/cmp/init.lua#L132

LinoWhy commented 3 months ago

@hrsh7th Seems the problem only occurs if I set the cmp_nvim_lsp.default_capabilities() with clangd server capabilities. Comment the follwing line in my configuration works:

      -- Merge cmp & server configured capabilities
      server_opts.capabilities = vim.tbl_deep_extend(
        "force",
        {},
        vim.lsp.protocol.make_client_capabilities(),
        has_cmp and cmp_nvim_lsp.default_capabilities() or {},
        server_opts.server_capabilities or {}
      )

I will try to fix it. Thanks any way!