mirror of
https://github.com/neovim/neovim.git
synced 2026-04-01 13:22:08 +00:00
fix(lsp): vim.lsp.completion clean up triggers on client detach (#38009)
Problem: LspDetach didn't clean up stale client refs in triggers table. Solution: create LspDetach autocmd and call disable_completion.
This commit is contained in:
@@ -778,6 +778,28 @@ local function get_augroup(bufnr)
|
|||||||
return string.format('nvim.lsp.completion_%d', bufnr)
|
return string.format('nvim.lsp.completion_%d', bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param client_id integer
|
||||||
|
--- @param bufnr integer
|
||||||
|
local function disable_completions(client_id, bufnr)
|
||||||
|
local handle = buf_handles[bufnr]
|
||||||
|
if not handle then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
handle.clients[client_id] = nil
|
||||||
|
if not next(handle.clients) then
|
||||||
|
buf_handles[bufnr] = nil
|
||||||
|
api.nvim_del_augroup_by_name(get_augroup(bufnr))
|
||||||
|
else
|
||||||
|
for char, clients in pairs(handle.triggers) do
|
||||||
|
--- @param c vim.lsp.Client
|
||||||
|
handle.triggers[char] = vim.tbl_filter(function(c)
|
||||||
|
return c.id ~= client_id
|
||||||
|
end, clients)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- @inlinedoc
|
--- @inlinedoc
|
||||||
--- @class vim.lsp.completion.BufferOpts
|
--- @class vim.lsp.completion.BufferOpts
|
||||||
--- @field autotrigger? boolean (default: false) When true, completion triggers automatically based on the server's `triggerCharacters`.
|
--- @field autotrigger? boolean (default: false) When true, completion triggers automatically based on the server's `triggerCharacters`.
|
||||||
@@ -805,6 +827,14 @@ local function enable_completions(client_id, bufnr, opts)
|
|||||||
|
|
||||||
-- Set up autocommands.
|
-- Set up autocommands.
|
||||||
local group = api.nvim_create_augroup(get_augroup(bufnr), { clear = true })
|
local group = api.nvim_create_augroup(get_augroup(bufnr), { clear = true })
|
||||||
|
api.nvim_create_autocmd('LspDetach', {
|
||||||
|
group = group,
|
||||||
|
buffer = bufnr,
|
||||||
|
desc = 'vim.lsp.completion: clean up client on detach',
|
||||||
|
callback = function(args)
|
||||||
|
disable_completions(args.data.client_id, args.buf)
|
||||||
|
end,
|
||||||
|
})
|
||||||
api.nvim_create_autocmd('CompleteDone', {
|
api.nvim_create_autocmd('CompleteDone', {
|
||||||
group = group,
|
group = group,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
@@ -861,28 +891,6 @@ local function enable_completions(client_id, bufnr, opts)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param client_id integer
|
|
||||||
--- @param bufnr integer
|
|
||||||
local function disable_completions(client_id, bufnr)
|
|
||||||
local handle = buf_handles[bufnr]
|
|
||||||
if not handle then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
handle.clients[client_id] = nil
|
|
||||||
if not next(handle.clients) then
|
|
||||||
buf_handles[bufnr] = nil
|
|
||||||
api.nvim_del_augroup_by_name(get_augroup(bufnr))
|
|
||||||
else
|
|
||||||
for char, clients in pairs(handle.triggers) do
|
|
||||||
--- @param c vim.lsp.Client
|
|
||||||
handle.triggers[char] = vim.tbl_filter(function(c)
|
|
||||||
return c.id ~= client_id
|
|
||||||
end, clients)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Enables or disables completions from the given language client in the given
|
--- Enables or disables completions from the given language client in the given
|
||||||
--- buffer. Effects of enabling completions are:
|
--- buffer. Effects of enabling completions are:
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1567,6 +1567,45 @@ describe('vim.lsp.completion: integration', function()
|
|||||||
feed('<C-y>')
|
feed('<C-y>')
|
||||||
eq('w-1/2', n.api.nvim_get_current_line())
|
eq('w-1/2', n.api.nvim_get_current_line())
|
||||||
end)
|
end)
|
||||||
|
it('removes client from triggers and clients table on LspDetach', function()
|
||||||
|
local list1 = {
|
||||||
|
isIncomplete = false,
|
||||||
|
items = { { label = 'foo' } },
|
||||||
|
}
|
||||||
|
local list2 = {
|
||||||
|
isIncomplete = false,
|
||||||
|
items = { { label = 'bar' } },
|
||||||
|
}
|
||||||
|
local id1 = create_server('dummy1', list1, { trigger_chars = { '.' } })
|
||||||
|
local id2 = create_server('dummy2', list2, { trigger_chars = { '.' } })
|
||||||
|
n.command('set cot=menuone,menu')
|
||||||
|
local function assert_matches(expected)
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq(
|
||||||
|
1,
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.fn.pumvisible()
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
eq(expected, n.api.nvim_get_current_line())
|
||||||
|
end
|
||||||
|
feed('Sw.')
|
||||||
|
assert_matches('w.foo')
|
||||||
|
exec_lua('vim.lsp.buf_detach_client(0, ' .. id1 .. ')')
|
||||||
|
feed('<ESC>Sw.')
|
||||||
|
assert_matches('w.bar')
|
||||||
|
exec_lua('vim.lsp.buf_detach_client(0, ' .. id2 .. ')')
|
||||||
|
feed('<ESC>Sw.')
|
||||||
|
retry(nil, nil, function()
|
||||||
|
eq(
|
||||||
|
0,
|
||||||
|
exec_lua(function()
|
||||||
|
return vim.fn.pumvisible()
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("vim.lsp.completion: omnifunc + 'autocomplete'", function()
|
describe("vim.lsp.completion: omnifunc + 'autocomplete'", function()
|
||||||
|
|||||||
Reference in New Issue
Block a user