mirror of
https://github.com/neovim/neovim.git
synced 2026-06-16 00:31:16 +00:00
fix(lsp): filter code_action diagnostics to the cursor #38988
Problem:
Cursor-position `vim.lsp.buf.code_action()` requests include all diagnostics on the current line, so unrelated same-line diagnostics affect the returned actions.
Solution:
Filter same-line diagnostics to the cursor position for cursor-position requests.
(cherry picked from commit ecb8402197)
This commit is contained in:
committed by
github-actions[bot]
parent
93dc301781
commit
c4d3a3d363
@@ -1342,6 +1342,24 @@ local function on_code_action_results(results, opts)
|
||||
vim.ui.select(actions, select_opts, on_user_choice)
|
||||
end
|
||||
|
||||
---@param diagnostic vim.Diagnostic
|
||||
---@param bufnr integer
|
||||
---@param lnum integer
|
||||
---@param col integer
|
||||
---@return boolean
|
||||
local function diagnostic_contains_cursor(diagnostic, bufnr, lnum, col)
|
||||
local start = vim.pos(bufnr, diagnostic.lnum, diagnostic.col)
|
||||
local finish =
|
||||
vim.pos(bufnr, diagnostic.end_lnum or diagnostic.lnum, diagnostic.end_col or diagnostic.col)
|
||||
local cursor = vim.pos(bufnr, lnum, col)
|
||||
|
||||
if start == finish then
|
||||
return cursor == start
|
||||
end
|
||||
|
||||
return start <= cursor and cursor < finish
|
||||
end
|
||||
|
||||
--- Selects a code action (LSP: "textDocument/codeAction" request) available at cursor position.
|
||||
---
|
||||
---@param opts? vim.lsp.buf.code_action.Opts
|
||||
@@ -1363,6 +1381,13 @@ function M.code_action(opts)
|
||||
local mode = api.nvim_get_mode().mode
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
local win = api.nvim_get_current_win()
|
||||
local range = opts.range
|
||||
if range == nil and (mode == 'v' or mode == 'V') then
|
||||
range = range_from_selection(bufnr, mode)
|
||||
end
|
||||
local cursor = api.nvim_win_get_cursor(win)
|
||||
local lnum = cursor[1] - 1
|
||||
local col = cursor[2]
|
||||
local clients = lsp.get_clients({ bufnr = bufnr, method = 'textDocument/codeAction' })
|
||||
if not next(clients) then
|
||||
vim.notify(lsp._unsupported_method('textDocument/codeAction'), vim.log.levels.WARN)
|
||||
@@ -1373,15 +1398,11 @@ function M.code_action(opts)
|
||||
---@type lsp.CodeActionParams
|
||||
local params
|
||||
|
||||
if opts.range then
|
||||
assert(type(opts.range) == 'table', 'code_action range must be a table')
|
||||
local start = assert(opts.range.start, 'range must have a `start` property')
|
||||
local end_ = assert(opts.range['end'], 'range must have a `end` property')
|
||||
if range then
|
||||
assert(type(range) == 'table', 'code_action range must be a table')
|
||||
local start = assert(range.start, 'range must have a `start` property')
|
||||
local end_ = assert(range['end'], 'range must have a `end` property')
|
||||
params = util.make_given_range_params(start, end_, bufnr, client.offset_encoding)
|
||||
elseif mode == 'v' or mode == 'V' then
|
||||
local range = range_from_selection(bufnr, mode)
|
||||
params =
|
||||
util.make_given_range_params(range.start, range['end'], bufnr, client.offset_encoding)
|
||||
else
|
||||
params = util.make_range_params(win, client.offset_encoding)
|
||||
end
|
||||
@@ -1393,7 +1414,6 @@ function M.code_action(opts)
|
||||
else
|
||||
local ns_push = lsp.diagnostic.get_namespace(client.id)
|
||||
local diagnostics = {}
|
||||
local lnum = api.nvim_win_get_cursor(0)[1] - 1
|
||||
|
||||
client:_provider_foreach('textDocument/diagnostic', function(cap)
|
||||
local ns_pull = lsp.diagnostic.get_namespace(client.id, true, cap.identifier)
|
||||
@@ -1404,6 +1424,11 @@ function M.code_action(opts)
|
||||
end)
|
||||
|
||||
vim.list_extend(diagnostics, vim.diagnostic.get(bufnr, { namespace = ns_push, lnum = lnum }))
|
||||
if range == nil then
|
||||
diagnostics = vim.tbl_filter(function(diagnostic)
|
||||
return diagnostic_contains_cursor(diagnostic, bufnr, lnum, col)
|
||||
end, diagnostics)
|
||||
end
|
||||
params.context = vim.tbl_extend('force', context, {
|
||||
---@diagnostic disable-next-line: no-unknown
|
||||
diagnostics = vim.tbl_map(function(d)
|
||||
|
||||
@@ -969,6 +969,109 @@ describe('vim.lsp.buf', function()
|
||||
}
|
||||
end)
|
||||
|
||||
it('uses diagnostics at cursor position', function()
|
||||
exec_lua(create_server_definition)
|
||||
local severity = exec_lua(function()
|
||||
return vim.diagnostic.severity.ERROR
|
||||
end)
|
||||
local messages = exec_lua(function(severity_)
|
||||
local server = _G._create_server({
|
||||
capabilities = {
|
||||
codeActionProvider = true,
|
||||
},
|
||||
handlers = {
|
||||
['textDocument/codeAction'] = function(_, _, callback)
|
||||
callback(nil, {})
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { 'local first, second = 1, 2' })
|
||||
|
||||
local client_id = assert(vim.lsp.start({
|
||||
name = 'dummy',
|
||||
cmd = server.cmd,
|
||||
}))
|
||||
|
||||
local expected_messages = 2 -- initialize, initialized
|
||||
local function wait_for_messages()
|
||||
assert(
|
||||
vim.wait(200, function()
|
||||
return #server.messages == expected_messages
|
||||
end),
|
||||
'Timed out waiting for expected number of messages. Current messages seen so far: '
|
||||
.. vim.inspect(server.messages)
|
||||
)
|
||||
end
|
||||
|
||||
wait_for_messages()
|
||||
|
||||
vim.api.nvim_win_set_cursor(0, { 1, 15 })
|
||||
|
||||
local ns = vim.lsp.diagnostic.get_namespace(client_id)
|
||||
vim.diagnostic.set(ns, bufnr, {
|
||||
{
|
||||
lnum = 0,
|
||||
col = 6,
|
||||
end_lnum = 0,
|
||||
end_col = 11,
|
||||
message = 'first',
|
||||
severity = severity_,
|
||||
user_data = {
|
||||
lsp = {
|
||||
range = {
|
||||
start = { line = 0, character = 6 },
|
||||
['end'] = { line = 0, character = 11 },
|
||||
},
|
||||
message = 'first',
|
||||
severity = severity_,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
lnum = 0,
|
||||
col = 13,
|
||||
end_lnum = 0,
|
||||
end_col = 19,
|
||||
message = 'second',
|
||||
severity = severity_,
|
||||
user_data = {
|
||||
lsp = {
|
||||
range = {
|
||||
start = { line = 0, character = 13 },
|
||||
['end'] = { line = 0, character = 19 },
|
||||
},
|
||||
message = 'second',
|
||||
severity = severity_,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
vim.lsp.buf.code_action()
|
||||
|
||||
expected_messages = expected_messages + 1
|
||||
wait_for_messages()
|
||||
|
||||
vim.lsp.get_client_by_id(client_id):stop()
|
||||
|
||||
return server.messages
|
||||
end, severity)
|
||||
|
||||
eq('textDocument/codeAction', messages[3].method)
|
||||
eq({
|
||||
{
|
||||
range = {
|
||||
start = { line = 0, character = 13 },
|
||||
['end'] = { line = 0, character = 19 },
|
||||
},
|
||||
message = 'second',
|
||||
severity = severity,
|
||||
},
|
||||
}, messages[3].params.context.diagnostics)
|
||||
end)
|
||||
|
||||
it('fallback to command execution on resolve error', function()
|
||||
exec_lua(create_server_definition)
|
||||
local result = exec_lua(function()
|
||||
|
||||
Reference in New Issue
Block a user