aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaria José Solano <majosolano99@gmail.com>2025-03-10 09:20:27 -0700
committerGitHub <noreply@github.com>2025-03-10 09:20:27 -0700
commit3b0fe2659e74276e995655d1f8954b48005292e6 (patch)
tree2468cf6b51d31ac239ad9330de3486a60265a3a3
parent67c39f5ecae0edc3777b2db0f7e637cafa097d05 (diff)
downloadrneovim-3b0fe2659e74276e995655d1f8954b48005292e6.tar.gz
rneovim-3b0fe2659e74276e995655d1f8954b48005292e6.tar.bz2
rneovim-3b0fe2659e74276e995655d1f8954b48005292e6.zip
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.
-rw-r--r--runtime/doc/lsp.txt7
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--runtime/lua/vim/lsp/completion.lua46
-rw-r--r--runtime/lua/vim/lsp/protocol.lua4
-rw-r--r--test/functional/plugin/lsp/completion_spec.lua67
5 files changed, 113 insertions, 13 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 23780dd177..4c3fdcee6a 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1869,9 +1869,14 @@ enable({enable}, {client_id}, {bufnr}, {opts})
• {opts} (`vim.lsp.completion.BufferOpts?`) See
|vim.lsp.completion.BufferOpts|.
-trigger() *vim.lsp.completion.trigger()*
+trigger({opts}) *vim.lsp.completion.trigger()*
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*
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 5a3e35a3dd..5c377a12da 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -313,6 +313,8 @@ LSP
• |vim.lsp.enable()| has been added to enable servers.
• |vim.lsp.buf.code_action()| resolves the `command` property during the
`codeAction/resolve` request.
+• The `textDocument/completion` request now includes the completion context in
+ its parameters.
LUA
diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua
index 7b8ce02726..675921638d 100644
--- a/runtime/lua/vim/lsp/completion.lua
+++ b/runtime/lua/vim/lsp/completion.lua
@@ -424,9 +424,10 @@ end
--- @param clients table<integer, vim.lsp.Client> # keys != client_id
--- @param bufnr integer
--- @param win integer
+--- @param ctx? lsp.CompletionContext
--- @param callback fun(responses: table<integer, { err: lsp.ResponseError, result: vim.lsp.CompletionResult }>)
--- @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 request_ids = {} --- @type table<integer, integer>
local remaining_requests = vim.tbl_count(clients)
@@ -434,6 +435,8 @@ local function request(clients, bufnr, win, callback)
for _, client in pairs(clients) do
local client_id = client.id
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)
responses[client_id] = { err = err, result = result }
remaining_requests = remaining_requests - 1
@@ -457,7 +460,10 @@ local function request(clients, bufnr, win, callback)
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()
Context:cancel_pending()
@@ -473,7 +479,7 @@ local function trigger(bufnr, clients)
local start_time = vim.uv.hrtime()
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()
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()
local debounce_ms = next_debounce()
+ local ctx = { triggerKind = protocol.CompletionTriggerKind.TriggerForIncompleteCompletions }
if debounce_ms == 0 then
- vim.schedule(M.trigger)
+ vim.schedule(function()
+ M.trigger(ctx)
+ end)
else
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
@@ -545,7 +560,11 @@ local function on_insert_char_pre(handle)
completion_timer:start(25, 0, function()
reset_timer()
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
@@ -771,11 +790,20 @@ function M.enable(enable, client_id, bufnr, opts)
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.
-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 clients = (buf_handles[bufnr] or {}).clients or {}
- trigger(bufnr, clients)
+
+ trigger(bufnr, clients, ctx)
end
--- Implements 'omnifunc' compatible LSP completion.
@@ -800,7 +828,7 @@ function M._omnifunc(findstart, base)
return findstart == 1 and -1 or {}
end
- trigger(bufnr, clients)
+ trigger(bufnr, clients, { triggerKind = protocol.CompletionTriggerKind.Invoked })
-- Return -2 to signal that we should continue completion so that we can
-- async complete.
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index feff9adbd0..ececc41cee 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -476,9 +476,7 @@ function protocol.make_client_capabilities()
'data',
},
},
-
- -- TODO(tjdevries): Implement this
- contextSupport = false,
+ contextSupport = true,
},
declaration = {
linkSupport = true,
diff --git a/test/functional/plugin/lsp/completion_spec.lua b/test/functional/plugin/lsp/completion_spec.lua
index c5fa411efe..8d362642de 100644
--- a/test/functional/plugin/lsp/completion_spec.lua
+++ b/test/functional/plugin/lsp/completion_spec.lua
@@ -1127,6 +1127,73 @@ describe('vim.lsp.completion: protocol', function()
eq('foo', matches[1].abbr)
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)
describe('vim.lsp.completion: integration', function()