|
|
|
@ -11,9 +11,33 @@ local cwd = vim.loop.cwd()
|
|
|
|
|
local M = {}
|
|
|
|
|
local outgoing_calls_handler
|
|
|
|
|
local incoming_calls_handler
|
|
|
|
|
local hierarchy_handler
|
|
|
|
|
|
|
|
|
|
local function call_hierarchy_handler(direction, err, result, ctx, config)
|
|
|
|
|
local outgoing_calls_panel_creator
|
|
|
|
|
local incoming_calls_panel_creator
|
|
|
|
|
|
|
|
|
|
local function pick_call_hierarchy_item(call_hierarchy_items)
|
|
|
|
|
if not call_hierarchy_items then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
if #call_hierarchy_items == 1 then
|
|
|
|
|
return call_hierarchy_items[1]
|
|
|
|
|
end
|
|
|
|
|
local items = {}
|
|
|
|
|
for i, item in pairs(call_hierarchy_items) do
|
|
|
|
|
local entry = item.detail or item.name
|
|
|
|
|
table.insert(items, string.format('%d. %s', i, entry))
|
|
|
|
|
end
|
|
|
|
|
local choice = vim.fn.inputlist(items)
|
|
|
|
|
if choice < 1 or choice > #items then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
return choice
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function call_hierarchy_result_procesor(direction, err, result, ctx, config)
|
|
|
|
|
log(direction, err, result, ctx, config)
|
|
|
|
|
trace(result)
|
|
|
|
|
if not result then
|
|
|
|
|
vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN)
|
|
|
|
|
return
|
|
|
|
@ -30,9 +54,9 @@ local function call_hierarchy_handler(direction, err, result, ctx, config)
|
|
|
|
|
|
|
|
|
|
local items = ctx.items or {}
|
|
|
|
|
|
|
|
|
|
local kind = ' '
|
|
|
|
|
for _, call_hierarchy_result in pairs(result) do
|
|
|
|
|
local call_hierarchy_item = call_hierarchy_result[direction]
|
|
|
|
|
local kind = ' '
|
|
|
|
|
if call_hierarchy_item.kind then
|
|
|
|
|
kind = require('navigator.lspclient.lspkind').symbol_kind(call_hierarchy_item.kind) .. ' '
|
|
|
|
|
end
|
|
|
|
@ -40,102 +64,191 @@ local function call_hierarchy_handler(direction, err, result, ctx, config)
|
|
|
|
|
local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1)
|
|
|
|
|
call_hierarchy_item.detail = call_hierarchy_item.detail or ''
|
|
|
|
|
call_hierarchy_item.detail = string.gsub(call_hierarchy_item.detail, '\n', ' ↳ ')
|
|
|
|
|
trace(result, call_hierarchy_item)
|
|
|
|
|
trace(call_hierarchy_item)
|
|
|
|
|
|
|
|
|
|
local disp_item = vim.tbl_deep_extend('force', {}, call_hierarchy_item)
|
|
|
|
|
disp_item = vim.tbl_deep_extend('force', disp_item, {
|
|
|
|
|
filename = filename,
|
|
|
|
|
display_filename = display_filename,
|
|
|
|
|
indent = ctx.depth,
|
|
|
|
|
indent_level = ctx.depth,
|
|
|
|
|
node_text = call_hierarchy_item.name,
|
|
|
|
|
type = kind,
|
|
|
|
|
text = kind .. call_hierarchy_item.name .. ' ﰲ ' .. call_hierarchy_item.detail,
|
|
|
|
|
lnum = call_hierarchy_item.selectionRange.start.line + 1,
|
|
|
|
|
col = call_hierarchy_item.selectionRange.start.character,
|
|
|
|
|
})
|
|
|
|
|
table.insert(items, disp_item)
|
|
|
|
|
if ctx.depth or 0 > 0 then
|
|
|
|
|
local params = {
|
|
|
|
|
position = {
|
|
|
|
|
character = disp_item.selectionRange.start.character,
|
|
|
|
|
line = disp_item.selectionRange.start.line,
|
|
|
|
|
},
|
|
|
|
|
textDocument = {
|
|
|
|
|
uri = disp_item.uri,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
local api = 'callHierarchy/outgoingCalls'
|
|
|
|
|
local handler = outgoing_calls_handler
|
|
|
|
|
if direction == 'incoming' then
|
|
|
|
|
api = 'callHierarchy/incomingCalls'
|
|
|
|
|
handler = incoming_calls_handler
|
|
|
|
|
end
|
|
|
|
|
lsphelper.call_sync(
|
|
|
|
|
api,
|
|
|
|
|
params,
|
|
|
|
|
ctx,
|
|
|
|
|
vim.lsp.with(
|
|
|
|
|
partial(handler, 0),
|
|
|
|
|
{ depth = ctx.depth - 1, direction = 'to', items = ctx.items, no_show = true }
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
-- if ctx.depth or 0 > 0 then
|
|
|
|
|
-- local params = {
|
|
|
|
|
-- position = {
|
|
|
|
|
-- character = disp_item.selectionRange.start.character,
|
|
|
|
|
-- line = disp_item.selectionRange.start.line,
|
|
|
|
|
-- },
|
|
|
|
|
-- textDocument = {
|
|
|
|
|
-- uri = disp_item.uri,
|
|
|
|
|
-- },
|
|
|
|
|
-- }
|
|
|
|
|
-- local api = 'callHierarchy/outgoingCalls'
|
|
|
|
|
-- local handler = outgoing_calls_handler
|
|
|
|
|
-- if direction == 'incoming' then
|
|
|
|
|
-- api = 'callHierarchy/incomingCalls'
|
|
|
|
|
-- handler = incoming_calls_handler
|
|
|
|
|
-- end
|
|
|
|
|
-- lsphelper.call_sync(
|
|
|
|
|
-- api,
|
|
|
|
|
-- params,
|
|
|
|
|
-- ctx,
|
|
|
|
|
-- vim.lsp.with(
|
|
|
|
|
-- partial(handler, 0),
|
|
|
|
|
-- { depth = ctx.depth - 1, direction = 'to', items = ctx.items, no_show = true }
|
|
|
|
|
-- )
|
|
|
|
|
-- )
|
|
|
|
|
-- end
|
|
|
|
|
end
|
|
|
|
|
log(items)
|
|
|
|
|
return items
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local call_hierarchy_handler_from = partial(call_hierarchy_handler, 'from')
|
|
|
|
|
local call_hierarchy_handler_to = partial(call_hierarchy_handler, 'to')
|
|
|
|
|
local call_hierarchy_handler_from = partial(call_hierarchy_result_procesor, 'from')
|
|
|
|
|
local call_hierarchy_handler_to = partial(call_hierarchy_result_procesor, 'to')
|
|
|
|
|
|
|
|
|
|
incoming_calls_handler = function(_, err, result, ctx, cfg)
|
|
|
|
|
local bufnr = vim.api.nvim_get_current_buf()
|
|
|
|
|
hierarchy_handler = function(dir, handler, show, api, err, result, ctx, cfg)
|
|
|
|
|
log(dir, handler, api, show, err, result, ctx, cfg)
|
|
|
|
|
ctx = ctx or {} -- can be nil if it is async call
|
|
|
|
|
cfg = cfg or {}
|
|
|
|
|
vim.validate({ handler = { handler, 'function' }, show = { show, 'function' }, api = { api, 'string' } })
|
|
|
|
|
local bufnr = ctx.bufnr or vim.api.nvim_get_current_buf()
|
|
|
|
|
assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp hierarchy')
|
|
|
|
|
local results = call_hierarchy_handler_from(err, result, ctx, cfg, 'Incoming calls not found')
|
|
|
|
|
local results = handler(err, result, ctx, cfg, 'Incoming calls not found')
|
|
|
|
|
|
|
|
|
|
local ft = vim.api.nvim_buf_get_option(ctx.bufnr or vim.api.nvim_get_current_buf(), 'ft')
|
|
|
|
|
if ctx.no_show then
|
|
|
|
|
return results
|
|
|
|
|
end
|
|
|
|
|
local win = gui.new_list_view({ items = results, ft = ft, api = ' ' })
|
|
|
|
|
local show_args = { items = results, ft = ft, api = api, bufnr = bufnr }
|
|
|
|
|
local win = show({ items = results, ft = ft, api = api, bufnr = bufnr })
|
|
|
|
|
return results, win
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
outgoing_calls_handler = function(_, err, result, ctx, cfg)
|
|
|
|
|
local results = call_hierarchy_handler_to(err, result, ctx, cfg, 'Outgoing calls not found')
|
|
|
|
|
function M.get_children(params, direction, callback)
|
|
|
|
|
vim.lsp.buf_request(nil, 'textDocument/prepareCallHierarchy', params, function(err, result)
|
|
|
|
|
if err then
|
|
|
|
|
vim.notify('Prepare error' .. tostring(err), vim.log.levels.ERROR, {
|
|
|
|
|
title = 'Hierarchy prepare',
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft')
|
|
|
|
|
if ctx.no_show then
|
|
|
|
|
return results
|
|
|
|
|
end
|
|
|
|
|
local win = gui.new_list_view({ items = results, ft = ft, api = ' ' })
|
|
|
|
|
return result, win
|
|
|
|
|
local call_hierarchy_item = pick_call_hierarchy_item(result)
|
|
|
|
|
|
|
|
|
|
local method = 'callHierarchy/incomingCalls'
|
|
|
|
|
local title = 'LSP Incoming Calls'
|
|
|
|
|
|
|
|
|
|
if direction == 'outgoing' then
|
|
|
|
|
method = 'callHierarchy/outgoingCalls'
|
|
|
|
|
title = 'LSP Outgoing Calls'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
M.call_hierarchy({}, method, title, call_hierarchy_item, callback)
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function request(method, params, handler)
|
|
|
|
|
return vim.lsp.buf_request(0, method, params, handler)
|
|
|
|
|
local make_params = function(uri, pos)
|
|
|
|
|
return {
|
|
|
|
|
textDocument = {
|
|
|
|
|
uri = uri,
|
|
|
|
|
},
|
|
|
|
|
position = pos,
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
function M.expand(node)
|
|
|
|
|
local line = vim.api.nvim_exec("echo line('.')", true)
|
|
|
|
|
|
|
|
|
|
local function pick_call_hierarchy_item(call_hierarchy_items)
|
|
|
|
|
if not call_hierarchy_items then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
if #call_hierarchy_items == 1 then
|
|
|
|
|
return call_hierarchy_items[1]
|
|
|
|
|
end
|
|
|
|
|
local items = {}
|
|
|
|
|
for i, item in pairs(call_hierarchy_items) do
|
|
|
|
|
local entry = item.detail or item.name
|
|
|
|
|
table.insert(items, string.format('%d. %s', i, entry))
|
|
|
|
|
end
|
|
|
|
|
local choice = vim.fn.inputlist(items)
|
|
|
|
|
if choice < 1 or choice > #items then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
return choice
|
|
|
|
|
local params = make_params(node.uri, {
|
|
|
|
|
line = node.range.start.line,
|
|
|
|
|
character = node.range.start.character,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
H.get_children(params, t.direction, function(result)
|
|
|
|
|
node.status = 'open'
|
|
|
|
|
if result ~= nil and #result > 0 then -- no incoming
|
|
|
|
|
for _, item in ipairs(result) do
|
|
|
|
|
local child
|
|
|
|
|
if t.direction == 'outgoing' then
|
|
|
|
|
child = t.create_node(item.to.name, item.to.kind, item.to.uri, item.to.detail, item.to.range, item.fromRanges)
|
|
|
|
|
else
|
|
|
|
|
child = t.create_node(
|
|
|
|
|
item.from.name,
|
|
|
|
|
item.from.kind,
|
|
|
|
|
item.from.uri,
|
|
|
|
|
item.from.detail,
|
|
|
|
|
item.from.range,
|
|
|
|
|
item.fromRanges
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
table.insert(node.children, child)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
w.create_window()
|
|
|
|
|
vim.cmd('execute "normal! ' .. line .. 'G"')
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function display_panel(args)
|
|
|
|
|
-- args = {items=results, ft=ft, api=api}
|
|
|
|
|
print('dispaly panel')
|
|
|
|
|
log(args)
|
|
|
|
|
|
|
|
|
|
local Panel = require('guihua.panel')
|
|
|
|
|
local bufnr = args.bufnr or vim.api.nvim_get_current_buf()
|
|
|
|
|
local ft = args.ft or vim.api.nvim_buf_get_option(bufnr, 'buftype')
|
|
|
|
|
local items = args.items
|
|
|
|
|
local p = Panel:new({
|
|
|
|
|
header = args.header or 'Call Hierarchy',
|
|
|
|
|
render = function(bufnr)
|
|
|
|
|
return items
|
|
|
|
|
end,
|
|
|
|
|
fold = function(node)
|
|
|
|
|
if node.expanded or node.expandable then
|
|
|
|
|
return vim.cmd('normal! za')
|
|
|
|
|
end
|
|
|
|
|
-- new node
|
|
|
|
|
M.expand()
|
|
|
|
|
return node
|
|
|
|
|
end,
|
|
|
|
|
})
|
|
|
|
|
p:open(true)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
incoming_calls_handler = util.partial4(
|
|
|
|
|
hierarchy_handler,
|
|
|
|
|
'from',
|
|
|
|
|
call_hierarchy_handler_from,
|
|
|
|
|
gui.new_list_view,
|
|
|
|
|
' '
|
|
|
|
|
)
|
|
|
|
|
outgoing_calls_handler = util.partial4(hierarchy_handler, 'to', call_hierarchy_handler_to, gui.new_list_view, ' ')
|
|
|
|
|
|
|
|
|
|
local incoming_calls_panel = util.partial4(
|
|
|
|
|
hierarchy_handler,
|
|
|
|
|
'from',
|
|
|
|
|
call_hierarchy_handler_from,
|
|
|
|
|
display_panel,
|
|
|
|
|
' '
|
|
|
|
|
)
|
|
|
|
|
local outgoing_calls_panel = util.partial4(hierarchy_handler, 'to', call_hierarchy_handler_to, display_panel, ' ')
|
|
|
|
|
|
|
|
|
|
local function request(method, params, handler)
|
|
|
|
|
return vim.lsp.buf_request(0, method, params, handler)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
-- call_hierarchy with floating window
|
|
|
|
|
local function call_hierarchy(method, opts)
|
|
|
|
|
local params = vim.lsp.util.make_position_params()
|
|
|
|
|
opts = opts or {}
|
|
|
|
|
local handler = opts.handler -- we can pass in customer handler
|
|
|
|
|
log(opts)
|
|
|
|
|
request(
|
|
|
|
|
'textDocument/prepareCallHierarchy',
|
|
|
|
|
params,
|
|
|
|
@ -148,7 +261,7 @@ local function call_hierarchy(method, opts)
|
|
|
|
|
log('result', result, 'items', call_hierarchy_item)
|
|
|
|
|
local client = vim.lsp.get_client_by_id(ctx.client_id)
|
|
|
|
|
if client then
|
|
|
|
|
client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
|
|
|
|
|
client.request(method, { item = call_hierarchy_item, args = { dir = 'in' } }, handler, ctx.bufnr)
|
|
|
|
|
else
|
|
|
|
|
vim.notify(
|
|
|
|
|
string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
|
|
|
|
@ -159,18 +272,27 @@ local function call_hierarchy(method, opts)
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local in_method = 'callHierarchy/incomingCalls'
|
|
|
|
|
local out_method = 'callHierarchy/outgoingCalls'
|
|
|
|
|
|
|
|
|
|
function M.incoming_calls(opts)
|
|
|
|
|
call_hierarchy('callHierarchy/incomingCalls', opts)
|
|
|
|
|
call_hierarchy(in_method, opts)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function M.outgoing_calls(opts)
|
|
|
|
|
call_hierarchy('callHierarchy/outgoingCalls', opts)
|
|
|
|
|
call_hierarchy(out_method, opts)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
M.incoming_calls_call = partial(M.incoming_calls, 0)
|
|
|
|
|
M.outgoing_calls_call = partial(M.outgoing_calls, 0)
|
|
|
|
|
function M.incoming_calls_panel(opts)
|
|
|
|
|
opts = vim.tbl_extend('force', { handler = incoming_calls_panel }, opts or {})
|
|
|
|
|
call_hierarchy(in_method, opts)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
M.incoming_calls_handler = partial(incoming_calls_handler, 0)
|
|
|
|
|
M.outgoing_calls_handler = partial(outgoing_calls_handler, 0)
|
|
|
|
|
function M.outgoing_calls_panel(opts)
|
|
|
|
|
opts = vim.tbl_extend('force', { handler = outgoing_calls_panel }, opts or {})
|
|
|
|
|
call_hierarchy(out_method, opts)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
M.incoming_calls_handler = incoming_calls_handler
|
|
|
|
|
M.outgoing_calls_handler = outgoing_calls_handler
|
|
|
|
|
return M
|
|
|
|
|