mirror of
https://github.com/neovim/neovim
synced 2025-07-18 10:11:50 +00:00
fix(lsp): do not invoke handlers for unsupported methods (#15926)
Closes https://github.com/neovim/neovim/issues/15174 Instead of invoking handlers with unsupported methods, pre-compute which clients support a given method and only notify the user if no clients support the given method.
This commit is contained in:
committed by
GitHub
parent
b3e0d6708e
commit
d288daac2b
@ -86,7 +86,7 @@ end
|
|||||||
function lsp._unsupported_method(method)
|
function lsp._unsupported_method(method)
|
||||||
local msg = string.format("method %s is not supported by any of the servers registered for the current buffer", method)
|
local msg = string.format("method %s is not supported by any of the servers registered for the current buffer", method)
|
||||||
log.warn(msg)
|
log.warn(msg)
|
||||||
return lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound, msg)
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@ -131,15 +131,24 @@ local all_client_active_buffers = {}
|
|||||||
---@param bufnr (Number) of buffer
|
---@param bufnr (Number) of buffer
|
||||||
---@param fn (function({client}, {client_id}, {bufnr}) Function to run on
|
---@param fn (function({client}, {client_id}, {bufnr}) Function to run on
|
||||||
---each client attached to that buffer.
|
---each client attached to that buffer.
|
||||||
local function for_each_buffer_client(bufnr, fn)
|
---@param restrict_client_ids table list of client ids on which to restrict function application.
|
||||||
|
local function for_each_buffer_client(bufnr, fn, restrict_client_ids)
|
||||||
validate {
|
validate {
|
||||||
fn = { fn, 'f' };
|
fn = { fn, 'f' };
|
||||||
|
restrict_client_ids = { restrict_client_ids, 't' , true};
|
||||||
}
|
}
|
||||||
bufnr = resolve_bufnr(bufnr)
|
bufnr = resolve_bufnr(bufnr)
|
||||||
local client_ids = all_buffer_active_clients[bufnr]
|
local client_ids = all_buffer_active_clients[bufnr]
|
||||||
if not client_ids or tbl_isempty(client_ids) then
|
if not client_ids or tbl_isempty(client_ids) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if restrict_client_ids and #restrict_client_ids > 0 then
|
||||||
|
client_ids = vim.tbl_filter(function(item)
|
||||||
|
return vim.tbl_contains(restrict_client_ids, item)
|
||||||
|
end, vim.tbl_keys(client_ids))
|
||||||
|
end
|
||||||
|
|
||||||
for client_id in pairs(client_ids) do
|
for client_id in pairs(client_ids) do
|
||||||
local client = active_clients[client_id]
|
local client = active_clients[client_id]
|
||||||
if client then
|
if client then
|
||||||
@ -1255,32 +1264,32 @@ function lsp.buf_request(bufnr, method, params, handler)
|
|||||||
method = { method, 's' };
|
method = { method, 's' };
|
||||||
handler = { handler, 'f', true };
|
handler = { handler, 'f', true };
|
||||||
}
|
}
|
||||||
local client_request_ids = {}
|
|
||||||
|
|
||||||
|
local supported_clients = {}
|
||||||
local method_supported = false
|
local method_supported = false
|
||||||
for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr)
|
for_each_buffer_client(bufnr, function(client, client_id)
|
||||||
if client.supports_method(method) then
|
if client.supports_method(method) then
|
||||||
method_supported = true
|
method_supported = true
|
||||||
local request_success, request_id = client.request(method, params, handler, resolved_bufnr)
|
table.insert(supported_clients, client_id)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- if has client but no clients support the given method, notify the user
|
||||||
|
if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then
|
||||||
|
vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR)
|
||||||
|
vim.api.nvim_command("redraw")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local client_request_ids = {}
|
||||||
|
for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr)
|
||||||
|
local request_success, request_id = client.request(method, params, handler, resolved_bufnr)
|
||||||
-- This could only fail if the client shut down in the time since we looked
|
-- This could only fail if the client shut down in the time since we looked
|
||||||
-- it up and we did the request, which should be rare.
|
-- it up and we did the request, which should be rare.
|
||||||
if request_success then
|
if request_success then
|
||||||
client_request_ids[client_id] = request_id
|
client_request_ids[client_id] = request_id
|
||||||
end
|
end
|
||||||
end
|
end, supported_clients)
|
||||||
end)
|
|
||||||
|
|
||||||
-- if has client but no clients support the given method, call the callback with the proper
|
|
||||||
-- error message.
|
|
||||||
if not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported then
|
|
||||||
local unsupported_err = lsp._unsupported_method(method)
|
|
||||||
handler = handler or lsp.handlers[method]
|
|
||||||
if handler then
|
|
||||||
handler(unsupported_err, nil, {method=method, bufnr=bufnr})
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local function _cancel_all_requests()
|
local function _cancel_all_requests()
|
||||||
for client_id, request_id in pairs(client_request_ids) do
|
for client_id, request_id in pairs(client_request_ids) do
|
||||||
@ -1309,12 +1318,13 @@ function lsp.buf_request_all(bufnr, method, params, callback)
|
|||||||
local request_results = {}
|
local request_results = {}
|
||||||
local result_count = 0
|
local result_count = 0
|
||||||
local expected_result_count = 0
|
local expected_result_count = 0
|
||||||
local cancel, client_request_ids
|
|
||||||
|
|
||||||
local set_expected_result_count = once(function()
|
local set_expected_result_count = once(function ()
|
||||||
for _ in pairs(client_request_ids) do
|
for_each_buffer_client(bufnr, function(client)
|
||||||
expected_result_count = expected_result_count + 1
|
if client.supports_method(method) then
|
||||||
end
|
expected_result_count = expected_result_count + 1
|
||||||
|
end
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function _sync_handler(err, result, ctx)
|
local function _sync_handler(err, result, ctx)
|
||||||
@ -1327,7 +1337,7 @@ function lsp.buf_request_all(bufnr, method, params, callback)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
client_request_ids, cancel = lsp.buf_request(bufnr, method, params, _sync_handler)
|
local _, cancel = lsp.buf_request(bufnr, method, params, _sync_handler)
|
||||||
|
|
||||||
return cancel
|
return cancel
|
||||||
end
|
end
|
||||||
|
@ -405,7 +405,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('should call unsupported_method when trying to call an unsupported method', function()
|
it('should not call unsupported_method when trying to call an unsupported method', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{NIL, {}, {method="shutdown", client_id=1}};
|
{NIL, {}, {method="shutdown", client_id=1}};
|
||||||
}
|
}
|
||||||
@ -415,24 +415,12 @@ describe('LSP', function()
|
|||||||
exec_lua([=[
|
exec_lua([=[
|
||||||
BUFFER = vim.api.nvim_get_current_buf()
|
BUFFER = vim.api.nvim_get_current_buf()
|
||||||
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
|
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
|
||||||
vim.lsp.handlers['textDocument/typeDefinition'] = function(err, result, ctx)
|
vim.lsp.handlers['textDocument/typeDefinition'] = function() end
|
||||||
local method = ctx.method
|
|
||||||
vim.lsp._last_lsp_handler = { err = err; method = method }
|
|
||||||
end
|
|
||||||
vim.lsp._unsupported_method = function(method)
|
|
||||||
vim.lsp._last_unsupported_method = method
|
|
||||||
return 'fake-error'
|
|
||||||
end
|
|
||||||
vim.lsp.buf.type_definition()
|
|
||||||
]=])
|
]=])
|
||||||
end;
|
end;
|
||||||
on_init = function(client)
|
on_init = function(client)
|
||||||
client.stop()
|
client.stop()
|
||||||
local method = exec_lua("return vim.lsp._last_unsupported_method")
|
exec_lua("vim.lsp.buf.type_definition()")
|
||||||
eq("textDocument/typeDefinition", method)
|
|
||||||
local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_handler")
|
|
||||||
eq("fake-error", lsp_cb_call.err)
|
|
||||||
eq("textDocument/typeDefinition", lsp_cb_call.method)
|
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
vim.api.nvim_command(BUFFER.."bwipeout")
|
vim.api.nvim_command(BUFFER.."bwipeout")
|
||||||
]]
|
]]
|
||||||
@ -447,7 +435,7 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function()
|
it('should not call unsupported_method when no client and trying to call an unsupported method', function()
|
||||||
local expected_handlers = {
|
local expected_handlers = {
|
||||||
{NIL, {}, {method="shutdown", client_id=1}};
|
{NIL, {}, {method="shutdown", client_id=1}};
|
||||||
}
|
}
|
||||||
@ -455,20 +443,12 @@ describe('LSP', function()
|
|||||||
test_name = "capabilities_for_client_supports_method";
|
test_name = "capabilities_for_client_supports_method";
|
||||||
on_setup = function()
|
on_setup = function()
|
||||||
exec_lua([=[
|
exec_lua([=[
|
||||||
vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method)
|
vim.lsp.handlers['textDocument/typeDefinition'] = function() end
|
||||||
vim.lsp._last_lsp_handler = { err = err; method = method }
|
|
||||||
end
|
|
||||||
vim.lsp._unsupported_method = function(method)
|
|
||||||
vim.lsp._last_unsupported_method = method
|
|
||||||
return 'fake-error'
|
|
||||||
end
|
|
||||||
vim.lsp.buf.type_definition()
|
|
||||||
]=])
|
]=])
|
||||||
end;
|
end;
|
||||||
on_init = function(client)
|
on_init = function(client)
|
||||||
client.stop()
|
client.stop()
|
||||||
eq(NIL, exec_lua("return vim.lsp._last_unsupported_method"))
|
exec_lua("vim.lsp.buf.type_definition()")
|
||||||
eq(NIL, exec_lua("return vim.lsp._last_lsp_handler"))
|
|
||||||
end;
|
end;
|
||||||
on_exit = function(code, signal)
|
on_exit = function(code, signal)
|
||||||
eq(0, code, "exit code", fake_lsp_logfile)
|
eq(0, code, "exit code", fake_lsp_logfile)
|
||||||
|
Reference in New Issue
Block a user