diff options
author | Maria José Solano <majosolano99@gmail.com> | 2025-03-10 09:20:27 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-10 09:20:27 -0700 |
commit | 3b0fe2659e74276e995655d1f8954b48005292e6 (patch) | |
tree | 2468cf6b51d31ac239ad9330de3486a60265a3a3 | |
parent | 67c39f5ecae0edc3777b2db0f7e637cafa097d05 (diff) | |
download | rneovim-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.txt | 7 | ||||
-rw-r--r-- | runtime/doc/news.txt | 2 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/completion.lua | 46 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/protocol.lua | 4 | ||||
-rw-r--r-- | test/functional/plugin/lsp/completion_spec.lua | 67 |
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() |