fix(lsp): autotrigger should only trigger on client's triggerCharacters (#32266)

Problem: autotrigger option of vim.lsp.completion.enable() would trigger
all clients, as long as it matched at least one client's
triggerCharacters.

Solution: trigger only the clients with triggerCharacters matching the
character. overtriggering still happens if any client returns
isIncomplete=true (this case is more involved).


Co-authored-by: Mathias Fussenegger <f.mathias@zignar.net>
This commit is contained in:
Robert Muir
2025-02-13 05:08:11 -05:00
committed by GitHub
parent 0c3d2c9560
commit b42dc232c5
2 changed files with 42 additions and 4 deletions

View File

@ -518,11 +518,14 @@ local function on_insert_char_pre(handle)
end
local char = api.nvim_get_vvar('char')
if not completion_timer and handle.triggers[char] then
local matched_clients = handle.triggers[char]
if not completion_timer and matched_clients then
completion_timer = assert(vim.uv.new_timer())
completion_timer:start(25, 0, function()
reset_timer()
vim.schedule(M.trigger)
vim.schedule(function()
trigger(api.nvim_get_current_buf(), matched_clients)
end)
end)
end
end

View File

@ -770,13 +770,14 @@ end)
--- @param name string
--- @param completion_result lsp.CompletionList
--- @param trigger_chars? string[]
--- @return integer
local function create_server(name, completion_result)
local function create_server(name, completion_result, trigger_chars)
return exec_lua(function()
local server = _G._create_server({
capabilities = {
completionProvider = {
triggerCharacters = { '.' },
triggerCharacters = trigger_chars or { '.' },
},
},
handlers = {
@ -793,6 +794,7 @@ local function create_server(name, completion_result)
cmd = server.cmd,
on_attach = function(client, bufnr0)
vim.lsp.completion.enable(true, client.id, bufnr0, {
autotrigger = trigger_chars ~= nil,
convert = function(item)
return { abbr = item.label:gsub('%b()', '') }
end,
@ -957,6 +959,39 @@ describe('vim.lsp.completion: protocol', function()
end)
end)
it('insert char triggers clients matching trigger characters', function()
local results1 = {
isIncomplete = false,
items = {
{
label = 'hello',
},
},
}
create_server('dummy1', results1, { 'e' })
local results2 = {
isIncomplete = false,
items = {
{
label = 'hallo',
},
},
}
create_server('dummy2', results2, { 'h' })
feed('h')
exec_lua(function()
vim.v.char = 'h'
vim.cmd.startinsert()
vim.api.nvim_exec_autocmds('InsertCharPre', {})
end)
assert_matches(function(matches)
eq(1, #matches)
eq('hallo', matches[1].word)
end)
end)
it('executes commands', function()
local completion_list = {
isIncomplete = false,