mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
feat(lsp): support completion context #32793
Problem: vim.lsp.completion with "autotrigger" enabled, does not send completion context, even though it has all the necessary info. Solution: Include the context for "autotrigger". trigger() also optionally accepts context when manually invoked.
This commit is contained in:

committed by
GitHub

parent
67c39f5eca
commit
3b0fe2659e
@@ -1869,9 +1869,14 @@ enable({enable}, {client_id}, {bufnr}, {opts})
|
|||||||
• {opts} (`vim.lsp.completion.BufferOpts?`) See
|
• {opts} (`vim.lsp.completion.BufferOpts?`) See
|
||||||
|vim.lsp.completion.BufferOpts|.
|
|vim.lsp.completion.BufferOpts|.
|
||||||
|
|
||||||
trigger() *vim.lsp.completion.trigger()*
|
trigger({opts}) *vim.lsp.completion.trigger()*
|
||||||
Triggers LSP completion once in the current buffer.
|
Triggers LSP completion once in the current buffer.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {opts} (`table?`) A table with the following fields:
|
||||||
|
• {ctx}? (`lsp.CompletionContext`) Completion context.
|
||||||
|
Defaults to a trigger kind of `invoked`.
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Lua module: vim.lsp.inlay_hint *lsp-inlay_hint*
|
Lua module: vim.lsp.inlay_hint *lsp-inlay_hint*
|
||||||
|
@@ -313,6 +313,8 @@ LSP
|
|||||||
• |vim.lsp.enable()| has been added to enable servers.
|
• |vim.lsp.enable()| has been added to enable servers.
|
||||||
• |vim.lsp.buf.code_action()| resolves the `command` property during the
|
• |vim.lsp.buf.code_action()| resolves the `command` property during the
|
||||||
`codeAction/resolve` request.
|
`codeAction/resolve` request.
|
||||||
|
• The `textDocument/completion` request now includes the completion context in
|
||||||
|
its parameters.
|
||||||
|
|
||||||
LUA
|
LUA
|
||||||
|
|
||||||
|
@@ -424,9 +424,10 @@ end
|
|||||||
--- @param clients table<integer, vim.lsp.Client> # keys != client_id
|
--- @param clients table<integer, vim.lsp.Client> # keys != client_id
|
||||||
--- @param bufnr integer
|
--- @param bufnr integer
|
||||||
--- @param win integer
|
--- @param win integer
|
||||||
|
--- @param ctx? lsp.CompletionContext
|
||||||
--- @param callback fun(responses: table<integer, { err: lsp.ResponseError, result: vim.lsp.CompletionResult }>)
|
--- @param callback fun(responses: table<integer, { err: lsp.ResponseError, result: vim.lsp.CompletionResult }>)
|
||||||
--- @return function # Cancellation function
|
--- @return function # Cancellation function
|
||||||
local function request(clients, bufnr, win, callback)
|
local function request(clients, bufnr, win, ctx, callback)
|
||||||
local responses = {} --- @type table<integer, { err: lsp.ResponseError, result: any }>
|
local responses = {} --- @type table<integer, { err: lsp.ResponseError, result: any }>
|
||||||
local request_ids = {} --- @type table<integer, integer>
|
local request_ids = {} --- @type table<integer, integer>
|
||||||
local remaining_requests = vim.tbl_count(clients)
|
local remaining_requests = vim.tbl_count(clients)
|
||||||
@@ -434,6 +435,8 @@ local function request(clients, bufnr, win, callback)
|
|||||||
for _, client in pairs(clients) do
|
for _, client in pairs(clients) do
|
||||||
local client_id = client.id
|
local client_id = client.id
|
||||||
local params = lsp.util.make_position_params(win, client.offset_encoding)
|
local params = lsp.util.make_position_params(win, client.offset_encoding)
|
||||||
|
--- @cast params lsp.CompletionParams
|
||||||
|
params.context = ctx
|
||||||
local ok, request_id = client:request(ms.textDocument_completion, params, function(err, result)
|
local ok, request_id = client:request(ms.textDocument_completion, params, function(err, result)
|
||||||
responses[client_id] = { err = err, result = result }
|
responses[client_id] = { err = err, result = result }
|
||||||
remaining_requests = remaining_requests - 1
|
remaining_requests = remaining_requests - 1
|
||||||
@@ -457,7 +460,10 @@ local function request(clients, bufnr, win, callback)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function trigger(bufnr, clients)
|
--- @param bufnr integer
|
||||||
|
--- @param clients vim.lsp.Client[]
|
||||||
|
--- @param ctx? lsp.CompletionContext
|
||||||
|
local function trigger(bufnr, clients, ctx)
|
||||||
reset_timer()
|
reset_timer()
|
||||||
Context:cancel_pending()
|
Context:cancel_pending()
|
||||||
|
|
||||||
@@ -473,7 +479,7 @@ local function trigger(bufnr, clients)
|
|||||||
local start_time = vim.uv.hrtime()
|
local start_time = vim.uv.hrtime()
|
||||||
Context.last_request_time = start_time
|
Context.last_request_time = start_time
|
||||||
|
|
||||||
local cancel_request = request(clients, bufnr, win, function(responses)
|
local cancel_request = request(clients, bufnr, win, ctx, function(responses)
|
||||||
local end_time = vim.uv.hrtime()
|
local end_time = vim.uv.hrtime()
|
||||||
rtt_ms = compute_new_average((end_time - start_time) * ns_to_ms)
|
rtt_ms = compute_new_average((end_time - start_time) * ns_to_ms)
|
||||||
|
|
||||||
@@ -527,11 +533,20 @@ local function on_insert_char_pre(handle)
|
|||||||
reset_timer()
|
reset_timer()
|
||||||
|
|
||||||
local debounce_ms = next_debounce()
|
local debounce_ms = next_debounce()
|
||||||
|
local ctx = { triggerKind = protocol.CompletionTriggerKind.TriggerForIncompleteCompletions }
|
||||||
if debounce_ms == 0 then
|
if debounce_ms == 0 then
|
||||||
vim.schedule(M.trigger)
|
vim.schedule(function()
|
||||||
|
M.trigger(ctx)
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
completion_timer = new_timer()
|
completion_timer = new_timer()
|
||||||
completion_timer:start(debounce_ms, 0, vim.schedule_wrap(M.trigger))
|
completion_timer:start(
|
||||||
|
debounce_ms,
|
||||||
|
0,
|
||||||
|
vim.schedule_wrap(function()
|
||||||
|
M.trigger(ctx)
|
||||||
|
end)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -545,7 +560,11 @@ local function on_insert_char_pre(handle)
|
|||||||
completion_timer:start(25, 0, function()
|
completion_timer:start(25, 0, function()
|
||||||
reset_timer()
|
reset_timer()
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
trigger(api.nvim_get_current_buf(), matched_clients)
|
trigger(
|
||||||
|
api.nvim_get_current_buf(),
|
||||||
|
matched_clients,
|
||||||
|
{ triggerKind = protocol.CompletionTriggerKind.TriggerCharacter, triggerCharacter = char }
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
@@ -771,11 +790,20 @@ function M.enable(enable, client_id, bufnr, opts)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @inlinedoc
|
||||||
|
--- @class vim.lsp.completion.trigger.Opts
|
||||||
|
--- @field ctx? lsp.CompletionContext Completion context. Defaults to a trigger kind of `invoked`.
|
||||||
|
|
||||||
--- Triggers LSP completion once in the current buffer.
|
--- Triggers LSP completion once in the current buffer.
|
||||||
function M.trigger()
|
---
|
||||||
|
--- @param opts? vim.lsp.completion.trigger.Opts
|
||||||
|
function M.trigger(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local ctx = opts.ctx or { triggerKind = protocol.CompletionTriggerKind.Invoked }
|
||||||
local bufnr = api.nvim_get_current_buf()
|
local bufnr = api.nvim_get_current_buf()
|
||||||
local clients = (buf_handles[bufnr] or {}).clients or {}
|
local clients = (buf_handles[bufnr] or {}).clients or {}
|
||||||
trigger(bufnr, clients)
|
|
||||||
|
trigger(bufnr, clients, ctx)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Implements 'omnifunc' compatible LSP completion.
|
--- Implements 'omnifunc' compatible LSP completion.
|
||||||
@@ -800,7 +828,7 @@ function M._omnifunc(findstart, base)
|
|||||||
return findstart == 1 and -1 or {}
|
return findstart == 1 and -1 or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
trigger(bufnr, clients)
|
trigger(bufnr, clients, { triggerKind = protocol.CompletionTriggerKind.Invoked })
|
||||||
|
|
||||||
-- Return -2 to signal that we should continue completion so that we can
|
-- Return -2 to signal that we should continue completion so that we can
|
||||||
-- async complete.
|
-- async complete.
|
||||||
|
@@ -476,9 +476,7 @@ function protocol.make_client_capabilities()
|
|||||||
'data',
|
'data',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
contextSupport = true,
|
||||||
-- TODO(tjdevries): Implement this
|
|
||||||
contextSupport = false,
|
|
||||||
},
|
},
|
||||||
declaration = {
|
declaration = {
|
||||||
linkSupport = true,
|
linkSupport = true,
|
||||||
|
@@ -1127,6 +1127,73 @@ describe('vim.lsp.completion: protocol', function()
|
|||||||
eq('foo', matches[1].abbr)
|
eq('foo', matches[1].abbr)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('sends completion context when invoked', function()
|
||||||
|
local params = exec_lua(function()
|
||||||
|
local params
|
||||||
|
local server = _G._create_server({
|
||||||
|
capabilities = {
|
||||||
|
completionProvider = true,
|
||||||
|
},
|
||||||
|
handlers = {
|
||||||
|
['textDocument/completion'] = function(_, params0, callback)
|
||||||
|
params = params0
|
||||||
|
callback(nil, nil)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
vim.api.nvim_win_set_buf(0, bufnr)
|
||||||
|
vim.lsp.start({
|
||||||
|
name = 'dummy',
|
||||||
|
cmd = server.cmd,
|
||||||
|
on_attach = function(client, bufnr0)
|
||||||
|
vim.lsp.completion.enable(true, client.id, bufnr0)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
vim.lsp.completion.trigger()
|
||||||
|
|
||||||
|
return params
|
||||||
|
end)
|
||||||
|
|
||||||
|
eq({ triggerKind = 1 }, params.context)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('sends completion context with trigger characters', function()
|
||||||
|
exec_lua(function()
|
||||||
|
local server = _G._create_server({
|
||||||
|
capabilities = {
|
||||||
|
completionProvider = {
|
||||||
|
triggerCharacters = { 'h' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handlers = {
|
||||||
|
['textDocument/completion'] = function(_, params, callback)
|
||||||
|
_G.params = params
|
||||||
|
callback(nil, { isIncomplete = false, items = { label = 'hello' } })
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
vim.api.nvim_win_set_buf(0, bufnr)
|
||||||
|
vim.lsp.start({
|
||||||
|
name = 'dummy',
|
||||||
|
cmd = server.cmd,
|
||||||
|
on_attach = function(client, bufnr0)
|
||||||
|
vim.lsp.completion.enable(true, client.id, bufnr0, { autotrigger = true })
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|
||||||
|
feed('ih')
|
||||||
|
|
||||||
|
retry(100, nil, function()
|
||||||
|
eq({ triggerKind = 2, triggerCharacter = 'h' }, exec_lua('return _G.params.context'))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('vim.lsp.completion: integration', function()
|
describe('vim.lsp.completion: integration', function()
|
||||||
|
Reference in New Issue
Block a user