mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 19:38:20 +00:00
fix(lsp): support multiple clients in typehierarchy
This commit is contained in:

committed by
Lewis Russell

parent
4c25e60767
commit
629a5b71b5
@@ -2677,7 +2677,7 @@ vim.ui.select({items}, {opts}, {on_choice}) *vim.ui.select()*
|
|||||||
item shape. Plugins reimplementing `vim.ui.select` may
|
item shape. Plugins reimplementing `vim.ui.select` may
|
||||||
wish to use this to infer the structure or semantics of
|
wish to use this to infer the structure or semantics of
|
||||||
`items`, or the context in which select() was called.
|
`items`, or the context in which select() was called.
|
||||||
• {on_choice} (`fun(item: any?, idx: integer?)`) Called once the user
|
• {on_choice} (`fun(item: T?, idx: integer?)`) Called once the user
|
||||||
made a choice. `idx` is the 1-based index of `item`
|
made a choice. `idx` is the 1-based index of `item`
|
||||||
within `items`. `nil` if the user aborted the dialog.
|
within `items`. `nil` if the user aborted the dialog.
|
||||||
|
|
||||||
|
@@ -86,6 +86,8 @@ LSP
|
|||||||
|vim.lsp.buf.type_definition()| and |vim.lsp.buf.implementation()| now
|
|vim.lsp.buf.type_definition()| and |vim.lsp.buf.implementation()| now
|
||||||
support merging the results of multiple clients but no longer trigger the
|
support merging the results of multiple clients but no longer trigger the
|
||||||
global handlers from `vim.lsp.handlers`
|
global handlers from `vim.lsp.handlers`
|
||||||
|
• |vim.lsp.buf.typehierarchy()| now passes the correct params for each
|
||||||
|
client request.
|
||||||
|
|
||||||
|
|
||||||
LUA
|
LUA
|
||||||
|
@@ -569,6 +569,23 @@ function M.document_symbol(opts)
|
|||||||
request_with_opts(ms.textDocument_documentSymbol, params, opts)
|
request_with_opts(ms.textDocument_documentSymbol, params, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param client_id integer
|
||||||
|
--- @param method string
|
||||||
|
--- @param params table
|
||||||
|
--- @param handler? lsp.Handler
|
||||||
|
--- @param bufnr? integer
|
||||||
|
local function request_with_id(client_id, method, params, handler, bufnr)
|
||||||
|
local client = vim.lsp.get_client_by_id(client_id)
|
||||||
|
if not client then
|
||||||
|
vim.notify(
|
||||||
|
string.format('Client with id=%d disappeared during call hierarchy request', client_id),
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
client.request(method, params, handler, bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
--- @param call_hierarchy_items lsp.CallHierarchyItem[]
|
--- @param call_hierarchy_items lsp.CallHierarchyItem[]
|
||||||
--- @return lsp.CallHierarchyItem?
|
--- @return lsp.CallHierarchyItem?
|
||||||
local function pick_call_hierarchy_item(call_hierarchy_items)
|
local function pick_call_hierarchy_item(call_hierarchy_items)
|
||||||
@@ -600,19 +617,11 @@ local function call_hierarchy(method)
|
|||||||
vim.notify('No item resolved', vim.log.levels.WARN)
|
vim.notify('No item resolved', vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local call_hierarchy_item = pick_call_hierarchy_item(result)
|
local item = pick_call_hierarchy_item(result)
|
||||||
if not call_hierarchy_item then
|
if not item then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
request_with_id(ctx.client_id, method, { item = item }, nil, ctx.bufnr)
|
||||||
if client then
|
|
||||||
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
|
|
||||||
else
|
|
||||||
vim.notify(
|
|
||||||
string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
|
|
||||||
vim.log.levels.WARN
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -630,78 +639,74 @@ function M.outgoing_calls()
|
|||||||
call_hierarchy(ms.callHierarchy_outgoingCalls)
|
call_hierarchy(ms.callHierarchy_outgoingCalls)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param item lsp.TypeHierarchyItem
|
||||||
|
local function format_type_hierarchy_item(item)
|
||||||
|
if not item.detail or #item.detail == 0 then
|
||||||
|
return item.name
|
||||||
|
end
|
||||||
|
return string.format('%s %s', item.name, item.detail)
|
||||||
|
end
|
||||||
|
|
||||||
--- Lists all the subtypes or supertypes of the symbol under the
|
--- Lists all the subtypes or supertypes of the symbol under the
|
||||||
--- cursor in the |quickfix| window. If the symbol can resolve to
|
--- cursor in the |quickfix| window. If the symbol can resolve to
|
||||||
--- multiple items, the user can pick one using |vim.ui.select()|.
|
--- multiple items, the user can pick one using |vim.ui.select()|.
|
||||||
---@param kind "subtypes"|"supertypes"
|
---@param kind "subtypes"|"supertypes"
|
||||||
function M.typehierarchy(kind)
|
function M.typehierarchy(kind)
|
||||||
local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes
|
local method = kind == 'subtypes' and ms.typeHierarchy_subtypes or ms.typeHierarchy_supertypes
|
||||||
|
|
||||||
--- Merge results from multiple clients into a single table. Client-ID is preserved.
|
|
||||||
---
|
|
||||||
--- @param results table<integer, {error: lsp.ResponseError?, result: lsp.TypeHierarchyItem[]?}>
|
|
||||||
--- @return [integer, lsp.TypeHierarchyItem][]
|
|
||||||
local function merge_results(results)
|
|
||||||
local merged_results = {}
|
|
||||||
for client_id, client_result in pairs(results) do
|
|
||||||
if client_result.error then
|
|
||||||
vim.notify(client_result.error.message, vim.log.levels.WARN)
|
|
||||||
elseif client_result.result then
|
|
||||||
for _, item in pairs(client_result.result) do
|
|
||||||
table.insert(merged_results, { client_id, item })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return merged_results
|
|
||||||
end
|
|
||||||
|
|
||||||
local bufnr = api.nvim_get_current_buf()
|
local bufnr = api.nvim_get_current_buf()
|
||||||
local params = util.make_position_params()
|
local clients = vim.lsp.get_clients({ bufnr = bufnr, method = method })
|
||||||
--- @param results table<integer, {error: lsp.ResponseError?, result: lsp.TypeHierarchyItem[]?}>
|
if not next(clients) then
|
||||||
vim.lsp.buf_request_all(bufnr, ms.textDocument_prepareTypeHierarchy, params, function(results)
|
vim.notify(vim.lsp._unsupported_method(method), vim.log.levels.WARN)
|
||||||
local merged_results = merge_results(results)
|
|
||||||
if #merged_results == 0 then
|
|
||||||
vim.notify('No items resolved', vim.log.levels.INFO)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if #merged_results == 1 then
|
local win = api.nvim_get_current_win()
|
||||||
local item = merged_results[1]
|
|
||||||
local client = vim.lsp.get_client_by_id(item[1])
|
--- @param results [integer, lsp.TypeHierarchyItem][]
|
||||||
if client then
|
local function on_response(results)
|
||||||
client.request(method, { item = item[2] }, nil, bufnr)
|
if #results == 0 then
|
||||||
|
vim.notify('No items resolved', vim.log.levels.INFO)
|
||||||
|
elseif #results == 1 then
|
||||||
|
local client_id, item = results[1][1], results[1][2]
|
||||||
|
request_with_id(client_id, method, { item = item }, nil, bufnr)
|
||||||
else
|
else
|
||||||
vim.notify(
|
vim.ui.select(results, {
|
||||||
string.format('Client with id=%d disappeared during call hierarchy request', item[1]),
|
|
||||||
vim.log.levels.WARN
|
|
||||||
)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local select_opts = {
|
|
||||||
prompt = 'Select a type hierarchy item:',
|
prompt = 'Select a type hierarchy item:',
|
||||||
kind = 'typehierarchy',
|
kind = 'typehierarchy',
|
||||||
format_item = function(item)
|
format_item = function(x)
|
||||||
if not item[2].detail or #item[2].detail == 0 then
|
return format_type_hierarchy_item(x[2])
|
||||||
return item[2].name
|
|
||||||
end
|
|
||||||
return string.format('%s %s', item[2].name, item[2].detail)
|
|
||||||
end,
|
end,
|
||||||
}
|
}, function(x)
|
||||||
|
if x then
|
||||||
|
local client_id, item = x[1], x[2]
|
||||||
|
request_with_id(client_id, method, { item = item }, nil, bufnr)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
vim.ui.select(merged_results, select_opts, function(item)
|
local results = {} --- @type [integer, lsp.TypeHierarchyItem][]
|
||||||
local client = vim.lsp.get_client_by_id(item[1])
|
|
||||||
if client then
|
local remaining = #clients
|
||||||
--- @type lsp.TypeHierarchyItem
|
|
||||||
client.request(method, { item = item[2] }, nil, bufnr)
|
for _, client in ipairs(clients) do
|
||||||
else
|
local params = util.make_position_params(win, client.offset_encoding)
|
||||||
vim.notify(
|
client.request(method, params, function(err, result, ctx)
|
||||||
string.format('Client with id=%d disappeared during call hierarchy request', item[1]),
|
--- @cast result lsp.TypeHierarchyItem[]?
|
||||||
vim.log.levels.WARN
|
if err then
|
||||||
)
|
vim.notify(err.message, vim.log.levels.WARN)
|
||||||
|
elseif result then
|
||||||
|
for _, item in pairs(result) do
|
||||||
|
results[#results + 1] = { ctx.client_id, item }
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
|
remaining = remaining - 1
|
||||||
|
if remaining == 0 then
|
||||||
|
on_response(results)
|
||||||
|
end
|
||||||
|
end, bufnr)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- List workspace folders.
|
--- List workspace folders.
|
||||||
|
@@ -20,7 +20,8 @@ local M = {}
|
|||||||
--- end)
|
--- end)
|
||||||
--- ```
|
--- ```
|
||||||
---
|
---
|
||||||
---@param items any[] Arbitrary items
|
---@generic T
|
||||||
|
---@param items T[] Arbitrary items
|
||||||
---@param opts table Additional options
|
---@param opts table Additional options
|
||||||
--- - prompt (string|nil)
|
--- - prompt (string|nil)
|
||||||
--- Text of the prompt. Defaults to `Select one of:`
|
--- Text of the prompt. Defaults to `Select one of:`
|
||||||
@@ -32,7 +33,7 @@ local M = {}
|
|||||||
--- Plugins reimplementing `vim.ui.select` may wish to
|
--- Plugins reimplementing `vim.ui.select` may wish to
|
||||||
--- use this to infer the structure or semantics of
|
--- use this to infer the structure or semantics of
|
||||||
--- `items`, or the context in which select() was called.
|
--- `items`, or the context in which select() was called.
|
||||||
---@param on_choice fun(item: any|nil, idx: integer|nil)
|
---@param on_choice fun(item: T|nil, idx: integer|nil)
|
||||||
--- Called once the user made a choice.
|
--- Called once the user made a choice.
|
||||||
--- `idx` is the 1-based index of `item` within `items`.
|
--- `idx` is the 1-based index of `item` within `items`.
|
||||||
--- `nil` if the user aborted the dialog.
|
--- `nil` if the user aborted the dialog.
|
||||||
|
Reference in New Issue
Block a user