aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lsp.txt26
-rw-r--r--runtime/lua/vim/lsp/buf.lua56
-rw-r--r--runtime/lua/vim/lsp/protocol.lua1
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua30
-rw-r--r--test/functional/plugin/lsp_spec.lua36
5 files changed, 125 insertions, 24 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index b704d2d6e8..bf4c2b59d1 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -969,18 +969,28 @@ add_workspace_folder({workspace_folder})
clear_references() *vim.lsp.buf.clear_references()*
Removes document highlights from current buffer.
-code_action({context}) *vim.lsp.buf.code_action()*
+code_action({options}) *vim.lsp.buf.code_action()*
Selects a code action available at the current cursor
position.
Parameters: ~
- {context} table|nil `CodeActionContext` of the LSP specification:
- • diagnostics: (table|nil) LSP`Diagnostic[]` . Inferred from the current position if not
- provided.
- • only: (string|nil) LSP `CodeActionKind` used
- to filter the code actions. Most language
- servers support values like `refactor` or
- `quickfix`.
+ {options} table|nil Optional table which holds the
+ following optional fields:
+ • context (table|nil): Corresponds to `CodeActionContext` of the LSP specification:
+ • diagnostics (table|nil): LSP`Diagnostic[]` . Inferred from the current position if not
+ provided.
+ • only (string|nil): LSP `CodeActionKind`
+ used to filter the code actions. Most
+ language servers support values like
+ `refactor` or `quickfix`.
+
+ • filter (function|nil): Predicate function
+ taking an `CodeAction` and returning a
+ boolean.
+ • apply (boolean|nil): When set to `true`, and
+ there is just one remaining action (after
+ filtering), the action is applied without
+ user query.
See also: ~
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index eb7ec579f1..00919c6b04 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -491,11 +491,14 @@ end
--- from multiple clients to have 1 single UI prompt for the user, yet we still
--- need to be able to link a `CodeAction|Command` to the right client for
--- `codeAction/resolve`
-local function on_code_action_results(results, ctx)
+local function on_code_action_results(results, ctx, options)
local action_tuples = {}
+ local filter = options and options.filter
for client_id, result in pairs(results) do
for _, action in pairs(result.result or {}) do
- table.insert(action_tuples, { client_id, action })
+ if not filter or filter(action) then
+ table.insert(action_tuples, { client_id, action })
+ end
end
end
if #action_tuples == 0 then
@@ -557,6 +560,13 @@ local function on_code_action_results(results, ctx)
end
end
+ -- If options.apply is given, and there are just one remaining code action,
+ -- apply it directly without querying the user.
+ if options and options.apply and #action_tuples == 1 then
+ on_user_choice(action_tuples[1])
+ return
+ end
+
vim.ui.select(action_tuples, {
prompt = 'Code actions:',
kind = 'codeaction',
@@ -571,35 +581,49 @@ end
--- Requests code actions from all clients and calls the handler exactly once
--- with all aggregated results
---@private
-local function code_action_request(params)
+local function code_action_request(params, options)
local bufnr = vim.api.nvim_get_current_buf()
local method = 'textDocument/codeAction'
vim.lsp.buf_request_all(bufnr, method, params, function(results)
- on_code_action_results(results, { bufnr = bufnr, method = method, params = params })
+ local ctx = { bufnr = bufnr, method = method, params = params}
+ on_code_action_results(results, ctx, options)
end)
end
--- Selects a code action available at the current
--- cursor position.
---
----@param context table|nil `CodeActionContext` of the LSP specification:
---- - diagnostics: (table|nil)
---- LSP `Diagnostic[]`. Inferred from the current
---- position if not provided.
---- - only: (string|nil)
---- LSP `CodeActionKind` used to filter the code actions.
---- Most language servers support values like `refactor`
---- or `quickfix`.
+---@param options table|nil Optional table which holds the following optional fields:
+--- - context (table|nil):
+--- Corresponds to `CodeActionContext` of the LSP specification:
+--- - diagnostics (table|nil):
+--- LSP `Diagnostic[]`. Inferred from the current
+--- position if not provided.
+--- - only (string|nil):
+--- LSP `CodeActionKind` used to filter the code actions.
+--- Most language servers support values like `refactor`
+--- or `quickfix`.
+--- - filter (function|nil):
+--- Predicate function taking an `CodeAction` and returning a boolean.
+--- - apply (boolean|nil):
+--- When set to `true`, and there is just one remaining action
+--- (after filtering), the action is applied without user query.
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction
-function M.code_action(context)
- validate { context = { context, 't', true } }
- context = context or {}
+function M.code_action(options)
+ validate { options = { options, 't', true } }
+ options = options or {}
+ -- Detect old API call code_action(context) which should now be
+ -- code_action({ context = context} )
+ if options.diagnostics or options.only then
+ options = { options = options }
+ end
+ local context = options.context or {}
if not context.diagnostics then
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
end
local params = util.make_range_params()
params.context = context
- code_action_request(params)
+ code_action_request(params, options)
end
--- Performs |vim.lsp.buf.code_action()| for a given range.
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 86c9e2fd58..2cd728ea36 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -645,6 +645,7 @@ function protocol.make_client_capabilities()
end)();
};
};
+ isPreferredSupport = true;
dataSupport = true;
resolveSupport = {
properties = { 'edit', }
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index 7ff3713d41..3375a5dda9 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -673,6 +673,36 @@ function tests.code_action_with_resolve()
}
end
+function tests.code_action_filter()
+ skeleton {
+ on_init = function()
+ return {
+ capabilities = {
+ codeActionProvider = {
+ resolveProvider = false
+ }
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ local action = {
+ title = 'Action 1',
+ command = 'command'
+ }
+ local preferred_action = {
+ title = 'Action 2',
+ isPreferred = true,
+ command = 'preferred_command',
+ }
+ expect_request('textDocument/codeAction', function()
+ return nil, { action, preferred_action, }
+ end)
+ notify('shutdown')
+ end;
+ }
+end
+
function tests.clientside_commands()
skeleton {
on_init = function()
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 436b431e38..03ed167bac 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -2665,6 +2665,42 @@ describe('LSP', function()
end
}
end)
+ it('Filters and automatically applies action if requested', function()
+ local client
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ test_rpc_server {
+ test_name = 'code_action_filter',
+ on_init = function(client_)
+ client = client_
+ end,
+ on_setup = function()
+ end,
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end,
+ on_handler = function(err, result, ctx)
+ eq(table.remove(expected_handlers), {err, result, ctx})
+ if ctx.method == 'start' then
+ exec_lua([[
+ vim.lsp.commands['preferred_command'] = function(cmd)
+ vim.lsp.commands['executed_preferred'] = function()
+ end
+ end
+ local bufnr = vim.api.nvim_get_current_buf()
+ vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID)
+ vim.lsp.buf.code_action({ filter = function(a) return a.isPreferred end, apply = true, })
+ ]])
+ elseif ctx.method == 'shutdown' then
+ eq('function', exec_lua[[return type(vim.lsp.commands['executed_preferred'])]])
+ client.stop()
+ end
+ end
+ }
+ end)
end)
describe('vim.lsp.commands', function()
it('Accepts only string keys', function()