mirror of
https://github.com/neovim/neovim.git
synced 2026-07-19 15:41:32 +00:00
fix(lsp): ensure augroup before querying autocmds #38254
This commit is contained in:
@@ -798,9 +798,120 @@ local function on_completechanged(group, bufnr)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function on_complete_done()
|
||||||
|
local completed_item = api.nvim_get_vvar('completed_item')
|
||||||
|
if not completed_item or not completed_item.user_data or not completed_item.user_data.nvim then
|
||||||
|
Context:reset()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local cursor_row, cursor_col = unpack(api.nvim_win_get_cursor(0)) --- @type integer, integer
|
||||||
|
cursor_row = cursor_row - 1
|
||||||
|
local completion_item = completed_item.user_data.nvim.lsp.completion_item --- @type lsp.CompletionItem
|
||||||
|
local client_id = completed_item.user_data.nvim.lsp.client_id --- @type integer
|
||||||
|
if not completion_item or not client_id then
|
||||||
|
Context:reset()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local bufnr = api.nvim_get_current_buf()
|
||||||
|
local expand_snippet = completion_item.insertTextFormat == protocol.InsertTextFormat.Snippet
|
||||||
|
and (completion_item.textEdit ~= nil or completion_item.insertText ~= nil)
|
||||||
|
|
||||||
|
Context:reset()
|
||||||
|
|
||||||
|
local client = lsp.get_client_by_id(client_id)
|
||||||
|
if not client then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local position_encoding = client.offset_encoding or 'utf-16'
|
||||||
|
local resolve_provider = (client.server_capabilities.completionProvider or {}).resolveProvider
|
||||||
|
|
||||||
|
local function clear_word()
|
||||||
|
if not expand_snippet then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove the already inserted word.
|
||||||
|
api.nvim_buf_set_text(
|
||||||
|
bufnr,
|
||||||
|
Context.cursor[1] - 1,
|
||||||
|
Context.cursor[2] - 1,
|
||||||
|
cursor_row,
|
||||||
|
cursor_col,
|
||||||
|
{ '' }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function apply_snippet_and_command()
|
||||||
|
if expand_snippet then
|
||||||
|
apply_snippet(completion_item)
|
||||||
|
end
|
||||||
|
|
||||||
|
local command = completion_item.command
|
||||||
|
if command then
|
||||||
|
client:exec_cmd(command, { bufnr = bufnr })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then
|
||||||
|
clear_word()
|
||||||
|
lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, position_encoding)
|
||||||
|
apply_snippet_and_command()
|
||||||
|
elseif resolve_provider and type(completion_item) == 'table' then
|
||||||
|
local changedtick = vim.b[bufnr].changedtick
|
||||||
|
|
||||||
|
--- @param result lsp.CompletionItem
|
||||||
|
client:request('completionItem/resolve', completion_item, function(err, result)
|
||||||
|
if changedtick ~= vim.b[bufnr].changedtick then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
clear_word()
|
||||||
|
if err then
|
||||||
|
vim.notify_once(err.message, vim.log.levels.WARN)
|
||||||
|
elseif result then
|
||||||
|
if result.additionalTextEdits then
|
||||||
|
lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, position_encoding)
|
||||||
|
end
|
||||||
|
if result.command then
|
||||||
|
completion_item.command = result.command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
apply_snippet_and_command()
|
||||||
|
end, bufnr)
|
||||||
|
else
|
||||||
|
clear_word()
|
||||||
|
apply_snippet_and_command()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param bufnr integer
|
||||||
|
---@return integer
|
||||||
|
local function register_completedone(bufnr)
|
||||||
|
local group = api.nvim_create_augroup(get_augroup(bufnr), { clear = false })
|
||||||
|
if #api.nvim_get_autocmds({ buffer = bufnr, event = 'CompleteDone', group = group }) > 0 then
|
||||||
|
return group
|
||||||
|
end
|
||||||
|
|
||||||
|
api.nvim_create_autocmd('CompleteDone', {
|
||||||
|
group = group,
|
||||||
|
buffer = bufnr,
|
||||||
|
callback = function()
|
||||||
|
local reason = api.nvim_get_vvar('event').reason ---@type string
|
||||||
|
if reason == 'accept' then
|
||||||
|
on_complete_done()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
return group
|
||||||
|
end
|
||||||
|
|
||||||
--- @param bufnr integer
|
--- @param bufnr integer
|
||||||
--- @param clients vim.lsp.Client[]
|
--- @param clients vim.lsp.Client[]
|
||||||
--- @param ctx? lsp.CompletionContext
|
--- @param ctx lsp.CompletionContext
|
||||||
local function trigger(bufnr, clients, ctx)
|
local function trigger(bufnr, clients, ctx)
|
||||||
reset_timer()
|
reset_timer()
|
||||||
Context:cancel_pending()
|
Context:cancel_pending()
|
||||||
@@ -809,6 +920,10 @@ local function trigger(bufnr, clients, ctx)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if ctx and ctx.triggerKind == protocol.CompletionTriggerKind.Invoked then
|
||||||
|
register_completedone(bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
local win = api.nvim_get_current_win()
|
local win = api.nvim_get_current_win()
|
||||||
local cursor_row = api.nvim_win_get_cursor(win)[1]
|
local cursor_row = api.nvim_win_get_cursor(win)[1]
|
||||||
local start_time = vim.uv.hrtime() --[[@as integer]]
|
local start_time = vim.uv.hrtime() --[[@as integer]]
|
||||||
@@ -953,95 +1068,6 @@ local function on_insert_leave()
|
|||||||
Context:reset()
|
Context:reset()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function on_complete_done()
|
|
||||||
local completed_item = api.nvim_get_vvar('completed_item')
|
|
||||||
if not completed_item or not completed_item.user_data or not completed_item.user_data.nvim then
|
|
||||||
Context:reset()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cursor_row, cursor_col = unpack(api.nvim_win_get_cursor(0)) --- @type integer, integer
|
|
||||||
cursor_row = cursor_row - 1
|
|
||||||
local completion_item = completed_item.user_data.nvim.lsp.completion_item --- @type lsp.CompletionItem
|
|
||||||
local client_id = completed_item.user_data.nvim.lsp.client_id --- @type integer
|
|
||||||
if not completion_item or not client_id then
|
|
||||||
Context:reset()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local bufnr = api.nvim_get_current_buf()
|
|
||||||
local expand_snippet = completion_item.insertTextFormat == protocol.InsertTextFormat.Snippet
|
|
||||||
and (completion_item.textEdit ~= nil or completion_item.insertText ~= nil)
|
|
||||||
|
|
||||||
Context:reset()
|
|
||||||
|
|
||||||
local client = lsp.get_client_by_id(client_id)
|
|
||||||
if not client then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local position_encoding = client.offset_encoding or 'utf-16'
|
|
||||||
local resolve_provider = (client.server_capabilities.completionProvider or {}).resolveProvider
|
|
||||||
|
|
||||||
local function clear_word()
|
|
||||||
if not expand_snippet then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Remove the already inserted word.
|
|
||||||
api.nvim_buf_set_text(
|
|
||||||
bufnr,
|
|
||||||
Context.cursor[1] - 1,
|
|
||||||
Context.cursor[2] - 1,
|
|
||||||
cursor_row,
|
|
||||||
cursor_col,
|
|
||||||
{ '' }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_snippet_and_command()
|
|
||||||
if expand_snippet then
|
|
||||||
apply_snippet(completion_item)
|
|
||||||
end
|
|
||||||
|
|
||||||
local command = completion_item.command
|
|
||||||
if command then
|
|
||||||
client:exec_cmd(command, { bufnr = bufnr })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then
|
|
||||||
clear_word()
|
|
||||||
lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, position_encoding)
|
|
||||||
apply_snippet_and_command()
|
|
||||||
elseif resolve_provider and type(completion_item) == 'table' then
|
|
||||||
local changedtick = vim.b[bufnr].changedtick
|
|
||||||
|
|
||||||
--- @param result lsp.CompletionItem
|
|
||||||
client:request('completionItem/resolve', completion_item, function(err, result)
|
|
||||||
if changedtick ~= vim.b[bufnr].changedtick then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
clear_word()
|
|
||||||
if err then
|
|
||||||
vim.notify_once(err.message, vim.log.levels.WARN)
|
|
||||||
elseif result then
|
|
||||||
if result.additionalTextEdits then
|
|
||||||
lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, position_encoding)
|
|
||||||
end
|
|
||||||
if result.command then
|
|
||||||
completion_item.command = result.command
|
|
||||||
end
|
|
||||||
end
|
|
||||||
apply_snippet_and_command()
|
|
||||||
end, bufnr)
|
|
||||||
else
|
|
||||||
clear_word()
|
|
||||||
apply_snippet_and_command()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param client_id integer
|
--- @param client_id integer
|
||||||
--- @param bufnr integer
|
--- @param bufnr integer
|
||||||
local function disable_completions(client_id, bufnr)
|
local function disable_completions(client_id, bufnr)
|
||||||
@@ -1090,7 +1116,7 @@ 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 = register_completedone(bufnr)
|
||||||
api.nvim_create_autocmd('LspDetach', {
|
api.nvim_create_autocmd('LspDetach', {
|
||||||
group = group,
|
group = group,
|
||||||
buffer = bufnr,
|
buffer = bufnr,
|
||||||
@@ -1099,16 +1125,6 @@ local function enable_completions(client_id, bufnr, opts)
|
|||||||
disable_completions(args.data.client_id, args.buf)
|
disable_completions(args.data.client_id, args.buf)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
api.nvim_create_autocmd('CompleteDone', {
|
|
||||||
group = group,
|
|
||||||
buffer = bufnr,
|
|
||||||
callback = function()
|
|
||||||
local reason = api.nvim_get_vvar('event').reason --- @type string
|
|
||||||
if reason == 'accept' then
|
|
||||||
on_complete_done()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
if opts.autotrigger then
|
if opts.autotrigger then
|
||||||
api.nvim_create_autocmd('InsertCharPre', {
|
api.nvim_create_autocmd('InsertCharPre', {
|
||||||
|
|||||||
@@ -1421,6 +1421,43 @@ describe('vim.lsp.completion: integration', function()
|
|||||||
{5:-- INSERT --} |
|
{5:-- INSERT --} |
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('omnifunc works without enable() #38252', function()
|
||||||
|
local completion_list = {
|
||||||
|
isIncomplete = false,
|
||||||
|
items = {
|
||||||
|
{ label = 'hello' },
|
||||||
|
{ label = 'hallo' },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
exec_lua(function()
|
||||||
|
local server = _G._create_server({
|
||||||
|
capabilities = {
|
||||||
|
completionProvider = {
|
||||||
|
triggerCharacters = { '.' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handlers = {
|
||||||
|
['textDocument/completion'] = function(_, _, callback)
|
||||||
|
callback(nil, completion_list)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local id = vim.lsp.start({
|
||||||
|
name = 'dummy',
|
||||||
|
cmd = server.cmd,
|
||||||
|
})
|
||||||
|
if id then
|
||||||
|
vim.lsp.buf_attach_client(bufnr, id)
|
||||||
|
vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
feed('ih<C-x><C-o>')
|
||||||
|
wait_for_pum()
|
||||||
|
feed('<C-y>')
|
||||||
|
eq('hallo', n.api.nvim_get_current_line())
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("vim.lsp.completion: omnifunc + 'autocomplete'", function()
|
describe("vim.lsp.completion: omnifunc + 'autocomplete'", function()
|
||||||
|
|||||||
Reference in New Issue
Block a user