Skip to content

Commit

Permalink
feat(builtin.lsp): implement builtin handlers for lsp.(incoming|outgo…
Browse files Browse the repository at this point in the history
…ing)_calls

Fixes #863
  • Loading branch information
wilriker committed Nov 23, 2021
1 parent 6daf35c commit fc46ff3
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ Built-in functions. Ready to be bound to any key you like.
| Functions | Description |
|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
| `builtin.lsp_references` | Lists LSP references for word under the cursor |
| `builtin.lsp_incoming_calls` | Lists LSP incoming calls for word under the cursor |
| `builtin.lsp_outgoing_calls` | Lists LSP outgoing calls for word under the cursor |
| `builtin.lsp_document_symbols` | Lists LSP document symbols in the current buffer |
| `builtin.lsp_workspace_symbols` | Lists LSP document symbols in the current workspace |
| `builtin.lsp_dynamic_workspace_symbols` | Dynamically Lists LSP for all workspace symbols |
Expand Down
10 changes: 10 additions & 0 deletions lua/telescope/builtin/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,16 @@ builtin.jumplist = require_on_exported_call("telescope.builtin.internal").jumpli
---@field timeout number: timeout for the sync call (default: 10000)
builtin.lsp_references = require_on_exported_call("telescope.builtin.lsp").references

--- Lists LSP incoming calls for word under the cursor, jumps to reference on `<cr>`
---@param opts table: options to pass to the picker
---@field timeout number: timeout for the sync call (default: 10000)
builtin.lsp_incoming_calls = require_on_exported_call("telescope.builtin.lsp").incoming_calls

--- Lists LSP outgoing calls for word under the cursor, jumps to reference on `<cr>`
---@param opts table: options to pass to the picker
---@field timeout number: timeout for the sync call (default: 10000)
builtin.lsp_outgoing_calls = require_on_exported_call("telescope.builtin.lsp").outgoing_calls

--- Goto the definition of the word under the cursor, if there's only one, otherwise show all options in Telescope
---@param opts table: options to pass to the picker
---@field timeout number: timeout for the sync call (default: 10000)
Expand Down
102 changes: 102 additions & 0 deletions lua/telescope/builtin/lsp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,106 @@ lsp.references = function(opts)
}):find()
end

local function call_hierarchy(method, title, direction, opts, item, timeout)
local result, err = vim.lsp.buf_request_sync(0, method, { item = item }, timeout)
if err then
vim.api.nvim_err_writeln("Error handling " .. title .. ": " .. err)
return
end
if not result or vim.tbl_isempty(result) then
return
end
local locations = {}
for _, server_result in pairs(result) do
for _, ch_call in pairs(server_result.result) do
local ch_item = ch_call[direction]
for _, range in pairs(ch_call.fromRanges) do
table.insert(locations, {
filename = vim.uri_to_fname(ch_item.uri),
text = ch_item.name,
lnum = range.start.line + 1,
col = range.start.character + 1,
})
end
end
end
pickers.new(opts, {
prompt_title = title,
finder = finders.new_table {
results = locations,
entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts),
},
previewer = conf.qflist_previewer(opts),
sorter = conf.generic_sorter(opts),
}):find()
end

local function calls(opts, direction)
opts = opts or {}

local params = vim.lsp.util.make_position_params()
local timeout = opts.timeout or 10000
local start = os.time()
local result, err = vim.lsp.buf_request_sync(0, "textDocument/prepareCallHierarchy", params, timeout)
if err then
vim.api.nvim_err_writeln("Error when preparing call hierarchy: " .. err)
return
end
-- NOTE: os.time() and os.difftime() only have second precision so we might stop too early later on
local remaining = timeout - os.difftime(os.time(), start)

local flattened_results = {}
for _, server_results in pairs(result) do
if server_results.result then
for _, result_result in pairs(server_results.result) do
if not vim.tbl_islist(result_result) then
flattened_results = { result_result }
break
end

vim.list_extend(flattened_results, server_results.result)
end
end
end
if #flattened_results == 0 then
return
else
if #flattened_results == 1 then
if direction == "from" then
call_hierarchy(
"callHierarchy/incomingCalls",
"LSP Incoming Calls",
direction,
opts,
flattened_results[1],
remaining
)
else
call_hierarchy(
"callHierarchy/outgoingCalls",
"LSP Outgoing Calls",
direction,
opts,
flattened_results[1],
remaining
)
end
else
-- TODO: handle more than one candidate
-- Based on LSP spec there could be several candidates and the client hasn
-- to choose among them.
end
end
end

lsp.incoming_calls = function(opts)
calls(opts, "from")
end

lsp.outgoing_calls = function(opts)
calls(opts, "to")
end

local function list_or_jump(action, title, opts)
opts = opts or {}

Expand Down Expand Up @@ -486,6 +586,8 @@ local feature_map = {
["type_definitions"] = "type_definition",
["implementations"] = "implementation",
["workspace_symbols"] = "workspace_symbol",
["incoming_calls"] = "call_hierarchy",
["outgoing_calls"] = "call_hierarchy",
}

local function apply_checks(mod)
Expand Down

0 comments on commit fc46ff3

Please sign in to comment.