Skip to content

Commit

Permalink
feat(lsp): add vim.lsp.buf.subtypes(), vim.lsp.buf.supertypes() (#28388)
Browse files Browse the repository at this point in the history
Co-authored-by: Mathias Fußenegger <mfussenegger@users.noreply.github.com>
Co-authored-by: Maria José Solano <majosolano99@gmail.com>
  • Loading branch information
3 people authored Apr 20, 2024
1 parent fd085d9 commit f190f75
Show file tree
Hide file tree
Showing 6 changed files with 577 additions and 0 deletions.
13 changes: 13 additions & 0 deletions runtime/doc/lsp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ won't run if your server doesn't support them.
- textDocument/hover
- textDocument/implementation*
- textDocument/inlayHint
- textDocument/prepareTypeHierarchy
- textDocument/publishDiagnostics
- textDocument/rangeFormatting
- textDocument/references
Expand All @@ -190,6 +191,8 @@ won't run if your server doesn't support them.
- textDocument/semanticTokens/full/delta
- textDocument/signatureHelp
- textDocument/typeDefinition*
- typeHierarchy/subtypes
- typeHierarchy/supertypes
- window/logMessage
- window/showMessage
- window/showDocument
Expand Down Expand Up @@ -1428,6 +1431,16 @@ signature_help() *vim.lsp.buf.signature_help()*
Displays signature information about the symbol under the cursor in a
floating window.

subtypes() *vim.lsp.buf.subtypes()*
Lists all the subtypes of the symbol under the cursor in the |quickfix|
window. If the symbol can resolve to multiple items, the user can pick one
using |vim.ui.select()|.

supertypes() *vim.lsp.buf.supertypes()*
Lists all the supertypes of the symbol under the cursor in the |quickfix|
window. If the symbol can resolve to multiple items, the user can pick one
using |vim.ui.select()|.

type_definition({options}) *vim.lsp.buf.type_definition()*
Jumps to the definition of the type of the symbol under the cursor.

Expand Down
2 changes: 2 additions & 0 deletions runtime/doc/news.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ The following new APIs and features were added.
https://microsoft.github.io/language-server-protocol/specification/#textDocument_inlayHint
• Implemented pull diagnostic textDocument/diagnostic: |vim.lsp.diagnostic.on_diagnostic()|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_diagnostic
• Implemented LSP type hierarchy: |vim.lsp.buf.supertypes()| and |vim.lsp.buf.subtypes()|
https://microsoft.github.io/language-server-protocol/specification/#textDocument_prepareTypeHierarchy
|vim.lsp.status()| consumes the last progress messages as a string.
• LSP client now always saves and restores named buffer marks when applying
text edits.
Expand Down
3 changes: 3 additions & 0 deletions runtime/lua/vim/lsp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ lsp._request_name_to_capability = {
[ms.textDocument_prepareCallHierarchy] = { 'callHierarchyProvider' },
[ms.callHierarchy_incomingCalls] = { 'callHierarchyProvider' },
[ms.callHierarchy_outgoingCalls] = { 'callHierarchyProvider' },
[ms.textDocument_prepareTypeHierarchy] = { 'typeHierarchyProvider' },
[ms.typeHierarchy_subtypes] = { 'typeHierarchyProvider' },
[ms.typeHierarchy_supertypes] = { 'typeHierarchyProvider' },
[ms.textDocument_rename] = { 'renameProvider' },
[ms.textDocument_prepareRename] = { 'renameProvider', 'prepareProvider' },
[ms.textDocument_codeAction] = { 'codeActionProvider' },
Expand Down
84 changes: 84 additions & 0 deletions runtime/lua/vim/lsp/buf.lua
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,90 @@ function M.outgoing_calls()
call_hierarchy(ms.callHierarchy_outgoingCalls)
end

--- @param method string
local function type_hierarchy(method)
--- Merge results from multiple clients into a single table. Client-ID is preserved.
---
--- @param results table<integer, {error: lsp.ResponseError, result: 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 params = util.make_position_params()
--- @param results table<integer, {error: lsp.ResponseError, result: lsp.TypeHierarchyItem[]?}>
vim.lsp.buf_request_all(bufnr, ms.textDocument_prepareTypeHierarchy, params, function(results)
local merged_results = merge_results(results)
if #merged_results == 0 then
vim.notify('No items resolved', vim.log.levels.INFO)
return
end

if #merged_results == 1 then
--- @type {integer, lsp.TypeHierarchyItem}
local item = merged_results[1]
local client = vim.lsp.get_client_by_id(item[1])
if client then
--- @type lsp.TypeHierarchyItem
client.request(method, { item = item[2] }, nil, bufnr)
else
vim.notify(
string.format('Client with id=%d disappeared during call hierarchy request', item[1]),
vim.log.levels.WARN
)
end
else
local opts = {
prompt = 'Select a type hierarchy item:',
kind = 'typehierarchy',
format_item = function(item)
if not item[2].detail or #item[2].detail == 0 then
return item[2].name
end
return string.format('%s %s', item[2].name, item[2].detail)
end,
}

vim.ui.select(merged_results, opts, function(item)
local client = vim.lsp.get_client_by_id(item[1])
if client then
--- @type lsp.TypeHierarchyItem
client.request(method, { item = item[2] }, nil, bufnr)
else
vim.notify(
string.format('Client with id=%d disappeared during call hierarchy request', item[1]),
vim.log.levels.WARN
)
end
end)
end
end)
end

--- Lists all the subtypes of the symbol under the
--- cursor in the |quickfix| window. If the symbol can resolve to
--- multiple items, the user can pick one using |vim.ui.select()|.
function M.subtypes()
type_hierarchy(ms.typeHierarchy_subtypes)
end

--- Lists all the supertypes of the symbol under the
--- cursor in the |quickfix| window. If the symbol can resolve to
--- multiple items, the user can pick one using |vim.ui.select()|.
function M.supertypes()
type_hierarchy(ms.typeHierarchy_supertypes)
end

--- List workspace folders.
---
function M.list_workspace_folders()
Expand Down
39 changes: 39 additions & 0 deletions runtime/lua/vim/lsp/handlers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,45 @@ M[ms.callHierarchy_incomingCalls] = make_call_hierarchy_handler('from')
--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#callHierarchy_outgoingCalls
M[ms.callHierarchy_outgoingCalls] = make_call_hierarchy_handler('to')

--- Displays type hierarchy in the quickfix window.
local function make_type_hierarchy_handler()
--- @param result lsp.TypeHierarchyItem[]
return function(_, result, ctx, _)
if not result then
return
end
local function format_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
local client = assert(vim.lsp.get_client_by_id(ctx.client_id))
local items = {}
for _, type_hierarchy_item in pairs(result) do
local col = util._get_line_byte_from_position(
ctx.bufnr,
type_hierarchy_item.range.start,
client.offset_encoding
)
table.insert(items, {
filename = assert(vim.uri_to_fname(type_hierarchy_item.uri)),
text = format_item(type_hierarchy_item),
lnum = type_hierarchy_item.range.start.line + 1,
col = col + 1,
})
end
vim.fn.setqflist({}, ' ', { title = 'LSP type hierarchy', items = items })
api.nvim_command('botright copen')
end
end

--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_incomingCalls
M[ms.typeHierarchy_subtypes] = make_type_hierarchy_handler()

--- @see # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#typeHierarchy_outgoingCalls
M[ms.typeHierarchy_supertypes] = make_type_hierarchy_handler()

--- @see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage
--- @param result lsp.LogMessageParams
M[ms.window_logMessage] = function(_, result, ctx, _)
Expand Down
Loading

0 comments on commit f190f75

Please sign in to comment.