Closed smacz42 closed 2 years ago
I can't reproduce it with gopls.
this might reproduce. press Tab to auto-complete.
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'
Plug 'hrsh7th/cmp-path'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/vim-vsnip'
Plug 'neovim/nvim-lspconfig'
Plug 'L3MON4D3/LuaSnip'
Plug 'saadparwaiz1/cmp_luasnip'
call plug#end()
PlugInstall | quit
" Setup global configuration. More on configuration below.
lua << EOF
local cmp = require "cmp"
local luasnip = require 'luasnip'
cmp.setup({
completion = {completeopt = 'menu,menuone,noinsert'},
snippet = {
expand = function(args)
expand = function(args) require('luasnip').lsp_expand(args.body) end
end,
},
mapping = {
['<c-p>'] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
['<c-n>'] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
['<Tab>'] = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = false }),
},
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{name= 'buffer' },
{name= 'path' },
},
experimental = {
ghost_text = true,
},
})
cmp.setup.cmdline(':', {
sources = cmp.config.sources({
{ name = 'path' },
-- { name = 'cmdline' },
})
})
EOF
lua << EOF
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())
require'lspconfig'.gopls.setup {
capabilities = capabilities,
}
EOF
Sorry, I don't know what gopls is, I'm using ansible. Using the minimal config I am unable to successfully get anything to even autocomplete, much less reproduce the issue.
I am including my relevant dotfiles in case you're able to craft a minimal config out of them. nvim.zip
This may be because I'm using williamboman/nvim-lsp-installer to :LspInstall ansiblels
to get the language server for ansible.
FWIW, <C-n>
is what triggers the removal. If I press the down arrow, it stays there.
I think I am able to get it to reproduce, but with a couple manual steps:
First, remove the cached plugins and language servers
WARNING THIS WILL DELETE EVERYTHING, MAKE SURE THIS IS WHAT YOU WANT TO DO FIRST
rm -r /tmp/plugged/
rm -r ~/.local/share/nvim/lsp_servers/
Then source the minimal 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'
Plug 'hrsh7th/cmp-path'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/vim-vsnip'
Plug 'neovim/nvim-lspconfig'
Plug 'L3MON4D3/LuaSnip'
Plug 'saadparwaiz1/cmp_luasnip'
" Companion plugin for nvim-lspconfig that allows you to seamlessly install LSP servers locally
Plug 'williamboman/nvim-lsp-installer'
call plug#end()
PlugInstall | quit
" Setup global configuration. More on configuration below.
lua << EOF
-- nvim-cmp setup
local cmp = require('cmp')
cmp.setup {
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body)
end,
},
mapping = {
['<c-p>'] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
['<c-n>'] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
['<Tab>'] = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = false }),
},
-- mapping = {
-- ['<C-p>'] = cmp.mapping.select_prev_item(),
-- ['<C-n>'] = cmp.mapping.select_next_item(),
-- ['<C-d>'] = cmp.mapping.scroll_docs(-4),
-- ['<C-f>'] = cmp.mapping.scroll_docs(4),
-- ['<C-Space>'] = cmp.mapping.complete(),
-- ['<C-e>'] = cmp.mapping.close(),
-- ['<CR>'] = cmp.mapping.confirm {
-- behavior = cmp.ConfirmBehavior.Replace,
-- select = true,
-- },
-- },
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'buffer',
option = {
get_bufnrs = function()
return vim.api.nvim_list_bufs()
end
},
},
},
experimental = {
ghost_text = true,
},
}
local capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())
local lspconfig = require('lspconfig')
lspconfig.cssls.setup {
capabilities = capabilities,
}
-- nvim-lspconfig
vim.lsp.set_log_level('debug')
-- nvim-lsp-installer
local lsp_installer = require("nvim-lsp-installer")
local function on_attach(client, bufnr)
-- Set up buffer-local keymaps (vim.api.nvim_buf_set_keymap()), etc.
end
-- Register a handler that will be called for all installed servers.
-- Alternatively, you may also register handlers on specific server instances instead (see example below).
lsp_installer.on_server_ready(function(server)
local default_opts = {
on_attach = on_attach,
}
-- Now we'll create a server_opts table where we'll specify our custom LSP server configuration
local server_opts = {
-- Provide settings that should only apply to the "eslintls" server
["pyright"] = function()
default_opts.settings = {
python = {
analysis = {
extraPath = {'.'},
},
},
}
end,
}
-- Use the server's custom settings, if they exist, otherwise default to the default options
local server_options = server_opts[server.name] and server_opts[server.name]() or default_opts
server:setup(server_options)
end)
-- cmp_luasnip
require("luasnip/loaders/from_vscode").lazy_load() -- You can pass { path = "./my-snippets/"} as well
EOF
Then run :LspInstall ansiblels
.
Clone down this repo: https://gitlab.com/compositionalenterprises/ansible-project-ourcompose_management.git
Edit with neovim the playbooks/migrate.yml
playbook. Insert what is shown here to create the new line 17:
15 delete_droplet: "{{ delete_droplet_prompt }}"
16
17 - name: Dummy name
18 blockin
19
After typing blockin
, the popup should show blockinfile Class
. Press <C-n>
to see the completion disappear.
Here's a clip demonstrating the issue. Note that the last time I do the selection, I use the arrow key, which works. The first two times were with <C-n>
, which blanks out the completion.
Here is some information I managed to extract from your repro. I've added this mapping for debugging:
['<C-d>'] = cmp.mapping(function()
local selected_entry = cmp.get_selected_entry()
print('before resolve', vim.inspect(selected_entry.completion_item))
print('after resolve', vim.inspect(selected_entry.resolved_completion_item))
end),
And, here is what it prints when I select the blockinfile
entry:
before resolve {
data = {
atEndOfLine = true,
documentUri = "file:///home/dmitmel/dotfiles/ansible-project-ourcompose_management/playbooks/migrate.yml",
inlineCollections = {},
moduleFqcn = "ansible.builtin.blockinfile"
},
detail = "ansible.builtin",
filterText = "blockinfile ansible.builtin.blockinfile",
kind = 7,
label = "blockinfile",
sortText = "2_blockinfile",
textEdit = {
newText = "",
range = {
end = {
character = 7,
line = 15
},
start = {
character = 6,
line = 15
}
}
}
}
after resolve {
data = {
atEndOfLine = true,
documentUri = "file:///home/dmitmel/dotfiles/ansible-project-ourcompose_management/playbooks/migrate.yml",
inlineCollections = { "ansible.builtin" },
moduleFqcn = "ansible.builtin.blockinfile"
},
detail = "ansible.builtin",
documentation = {
kind = "markdown",
value = "*Insert/update/remove a text block surrounded by marker lines*\n\n**Description**\n\n- This module will insert/update/remove a block of multi-line text surrounded by customizable marker lines.\n\n**Notes**\n\n- When using 'with_*' loops be aware that if you do not set a unique mark the block will be overwritten on each iteration.\n- As of Ansible 2.3, the _dest_ option has been changed to _path_ as default, but _dest_ still works as well.\n- Option _follow_ has been removed in Ansible 2.5, because this module modifies the contents of the file so _follow=no_ doesn't make sense.\n- When more then one block should be handled in one file you must change the _marker_ per task."
},
filterText = "blockinfile ansible.builtin.blockinfile",
kind = 7,
label = "blockinfile",
sortText = "2_blockinfile",
textEdit = {
newText = "blockinfile:",
range = {
end = {
character = 7,
line = 15
},
start = {
character = 6,
line = 15
}
}
}
}
Evidently, the issue is that the Language Server first, on the textDocument/completion
request, returns a completion item that has an empty text edit, and only on the completionItem/resolve
request, returns an actual text edit.
!!! @dmitmel Thank you!!!
Hm... OK. So I have to replace the all occurrence of self.completion_item.textEdit
to self:get_completion_item().textEdit
...
Thank you for fixing it. But I have one problem even after the fix. When selecting any item, inserted text gets erased. But only the first time. If I select a next item, then select back the previous item, text is inserted normally.
If I understand correctly, completionItem/resolve
is used for every item separately when selecting an item to get documentation and it can be quite expensive. So is it possible to use resolved text and will it hurt performance? Is ansible-ls is the only language server with such empty edits?
https://user-images.githubusercontent.com/1353637/147781937-419a3271-d0ee-4a5a-ac8b-9543bdb7b554.mp4
For me ansiblels
is the only server where this problem occurs.
I'm having the exact problem as @stasjok described, though for some completion items even getting back to them doesn't make them appear.
Same behavior as @FollieHiyuki on different lsp.
@hrsh7th should we reopen this issue? It doesn't seem to have been fixed completely. Or should I ask the ansible-language-server instead?
I don't know. I think, if your problem is not the same as odin-language-server's problem, we should re-open this. But if these provlem is the same, I don't think this should re-open because it just duplecated issue.
As pointed above, the issue is that ansiblels sends textEdit
with an empty newText
entry to the client until it gets resolved (compared to insertText
in odin case).
I think it is a different issue since we are talking about 2 different items here, but I could be wrong.
I think empty TextEdit.newText is valid (It's represented as removing text).
Thinking about it again, it kinda makes sense to me now, as ansiblels usually completes items to be the full-path version, e.g. type pod
-> choose podman_container
-> containers.podman.podman_container
I should go ask ansiblels devs whether removing the text and then replacing it is the intended behavior. If it is this issue should not be considered a bug.
Thanks for clarifying.
I set up nvim-cmp
in my environment and tried it, and I think #801 solved the problem.
DEMO (mp4):
https://user-images.githubusercontent.com/188642/154057504-a499773d-6b73-438e-a96f-eeb3ce4cdf6d.mp4
Thanks for the hard work! Confirmed working on my end as well.
FAQ
Issues
Neovim Version
NVIM v0.5.1
nvim-cmp config
Description
When auto-completing a module with the
ansiblels
LSP, when a Class is highlighted (CTRL-n) in the popup, the word disappears. Additionally, selecting an entry does nothing, and instead of having just the incomplete word which the auto-complete is based off of, there is nothing left of that word.Steps to reproduce
This is on any
yaml.ansible
filetype.Expected behavior
I would expect the word to be autocompleted, and the word fragment stay there as I look at the options, and have the selected option fill in when selected.
Actual behavior
I just don't know why when I highlight anything that's coming from the LSP, it completely wipes out the text - and it wipes out all of the text in that word, even if it's a FQCN (e.g. `ansible.builtin.set_fact) it gets rid of the whole thing.
Additional context
Logs from the incident: