Side panel (#197)

* add sidepanel

* revert some changes and fix nil indent level

* Add side panel; bugfix for ctags
pull/199/head
rayx 2 years ago committed by GitHub
parent 7bfd9157fe
commit 1f6103ed95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,9 @@
- A plugin combines the power of LSP and 🌲🏡 Treesitter together. Not only provids a better highlight but also help you analyse symbol context effectively. - A plugin combines the power of LSP and 🌲🏡 Treesitter together. Not only provids a better highlight but also help you analyse symbol context effectively.
- Fuzzy search & build ctags symbols - ctags fuzzy search & build ctags symbols
-
- [![a short intro of navigator](https://user-images.githubusercontent.com/1681295/147378905-51eede5f-e36d-48f4-9799-ae562949babe.jpeg)](https://youtu.be/P1kd7Y8AatE) - [![a short intro of navigator](https://user-images.githubusercontent.com/1681295/147378905-51eede5f-e36d-48f4-9799-ae562949babe.jpeg)](https://youtu.be/P1kd7Y8AatE)
@ -94,7 +96,9 @@ variable is:
- ccls call hierarchy (Non-standard `ccls/call` API) supports - ccls call hierarchy (Non-standard `ccls/call` API) supports
- Syntax folding based on treesitter folding algorithm. (It behaves similar to vs-code) - Syntax folding based on treesitter or LSP_fold folding algorithm. (It behaves similar to vs-code); comment folding
- Treesitter symbols sidebar, LSP document symbole sidebar. Both with preview and folding
- Fully support LSP CodeAction, CodeLens, CodeLens action. Help you improve code quality. - Fully support LSP CodeAction, CodeLens, CodeLens action. Help you improve code quality.
@ -673,6 +677,13 @@ Using treesitter and LSP to view the symbol definition
![image](https://user-images.githubusercontent.com/1681295/139771978-bbc970a5-be9f-42cf-8942-3477485bd89c.png) ![image](https://user-images.githubusercontent.com/1681295/139771978-bbc970a5-be9f-42cf-8942-3477485bd89c.png)
### Sidebar, folding, outline
Treesitter outline and Diagnostics
<img width="708" alt="image" src="https://user-images.githubusercontent.com/1681295/174791609-0023e68f-f1f4-4335-9ea2-d2360e9f0bfd.png">
<img width="733" alt="image" src="https://user-images.githubusercontent.com/1681295/174804579-26f87fbf-426b-46d0-a7a3-a5aab69c032f.png">
### GUI and multigrid support ### GUI and multigrid support
You can load a different font size for floating win You can load a different font size for floating win

@ -1,4 +1,4 @@
function! folding#foldexpr() function! folding#ngfoldexpr()
" return luaeval(printf('require"navigator.foldinglsp".get_fold_indic(%d)', v:lnum)) " return luaeval(printf('require"navigator.foldinglsp".get_fold_indic(%d)', v:lnum))
return luaeval(printf('require"navigator.foldts".get_fold_indic(%d)', v:lnum)) return luaeval(printf('require"navigator.foldts".get_fold_indic(%d)', v:lnum))
endfunction endfunction

@ -54,6 +54,8 @@ end
local function ctags_gen() local function ctags_gen()
local cmd = 'ctags' -- -x -n -u -f - ' .. vfn.expand('%:p') local cmd = 'ctags' -- -x -n -u -f - ' .. vfn.expand('%:p')
local output = _NgConfigValues.ctags.tagfile local output = _NgConfigValues.ctags.tagfile
-- rm file first
util.rm_file(output)
local options = '-R --exclude=.git --exclude=node_modules --exclude=test --exclude=vendor --excmd=number ' local options = '-R --exclude=.git --exclude=node_modules --exclude=test --exclude=vendor --excmd=number '
if _NgConfigValues.ctags then if _NgConfigValues.ctags then
cmd = _NgConfigValues.ctags.cmd cmd = _NgConfigValues.ctags.cmd
@ -64,6 +66,7 @@ local function ctags_gen()
options = options .. '--language=' .. lang options = options .. '--language=' .. lang
cmd = cmd .. ' ' .. options cmd = cmd .. ' ' .. options
cmd = string.format('%s -f %s %s --language=%s', cmd, output, options, lang) cmd = string.format('%s -f %s %s --language=%s', cmd, output, options, lang)
cmd = vim.split(cmd, ' ')
log(cmd) log(cmd)
vfn.jobstart(cmd, { vfn.jobstart(cmd, {
on_stdout = function(_, _, _) on_stdout = function(_, _, _)
@ -72,7 +75,7 @@ local function ctags_gen()
on_exit = function(_, data, _) -- id, data, event on_exit = function(_, data, _) -- id, data, event
-- log(vim.inspect(data) .. "exit") -- log(vim.inspect(data) .. "exit")
if data and data.code ~= 0 then if data and data ~= 0 then
return vim.notify(cmd .. ' failed ' .. tostring(data), vim.lsp.log_levels.ERROR) return vim.notify(cmd .. ' failed ' .. tostring(data), vim.lsp.log_levels.ERROR)
else else
vim.notify('ctags generated') vim.notify('ctags generated')
@ -108,10 +111,10 @@ local function ctags_symbols()
local ft = vim.o.ft local ft = vim.o.ft
local result = symbols_to_items(items) local result = symbols_to_items(items)
log(result)
if next(result) == nil then if next(result) == nil then
return vim.notify('no symbols found') return vim.notify('no symbols found')
end end
log(result[1])
local opt = { local opt = {
api = '', api = '',
ft = ft, ft = ft,

@ -350,8 +350,6 @@ end
M.show_buf_diagnostics = function() M.show_buf_diagnostics = function()
if diagnostic_list[vim.bo.filetype] ~= nil then if diagnostic_list[vim.bo.filetype] ~= nil then
-- log(diagnostic_list[vim.bo.filetype])
-- vim.fn.setqflist({}, " ", {title = "LSP", items = diagnostic_list[vim.bo.filetype]})
local results = diagnostic_list[vim.bo.filetype] local results = diagnostic_list[vim.bo.filetype]
local display_items = {} local display_items = {}
for _, client_items in pairs(results) do for _, client_items in pairs(results) do
@ -369,7 +367,7 @@ M.show_buf_diagnostics = function()
enable_preview_edit = true, enable_preview_edit = true,
}) })
if listview == nil then if listview == nil then
return log("nil listview") return log('nil listview')
end end
trace('new buffer', listview.bufnr) trace('new buffer', listview.bufnr)
if listview.bufnr then if listview.bufnr then
@ -471,6 +469,43 @@ function M.show_diagnostics(pos)
end end
end end
function M.treesitter_and_diag_panel()
local Panel = require('guihua.panel')
local ft = vim.bo.filetype
local results = diagnostic_list[ft]
log(diagnostic_list, ft)
local bufnr = vim.api.nvim_get_current_buf()
local p = Panel:new({
header = 'treesitter',
render = function(b)
log('render for ', bufnr, b)
return require('navigator.treesitter').all_ts_nodes(b)
end,
})
p:add_section({
header = 'diagnostic',
render = function(bufnr)
if diagnostic_list[ft] ~= nil then
local display_items = {}
for _, client_items in pairs(results) do
for _, items in pairs(client_items) do
for _, it in pairs(items) do
log(it)
table.insert(display_items, it)
end
end
end
return display_items
else
return {}
end
end,
})
p:open(true)
end
function M.config(cfg) function M.config(cfg)
cfg = cfg or {} cfg = cfg or {}
local default_cfg = { local default_cfg = {

@ -56,7 +56,6 @@ function M.setup_plugin()
M.active_folding_clients[client_id] = server_supports_folding M.active_folding_clients[client_id] = server_supports_folding
end end
end end
-- print(vim.inspect(M.active_folding_clients))
end end
function M.update_folds() function M.update_folds()

@ -44,7 +44,7 @@ function M.setup_fold()
local current_window = api.nvim_get_current_win() local current_window = api.nvim_get_current_win()
api.nvim_win_set_option(current_window, 'foldmethod', 'expr') api.nvim_win_set_option(current_window, 'foldmethod', 'expr')
api.nvim_win_set_option(current_window, 'foldexpr', 'folding#foldexpr()') api.nvim_win_set_option(current_window, 'foldexpr', 'folding#ngfoldexpr()')
end end
-- This is cached on buf tick to avoid computing that multiple times -- This is cached on buf tick to avoid computing that multiple times

@ -1,7 +1,7 @@
local gui = require('navigator.gui') local gui = require('navigator.gui')
local util = require('navigator.util') local util = require('navigator.util')
local log = util.log local log = util.log
local trace = util.trace local trace = util.log
local partial = util.partial local partial = util.partial
local lsphelper = require('navigator.lspwrapper') local lsphelper = require('navigator.lspwrapper')
@ -9,94 +9,162 @@ local path_sep = require('navigator.util').path_sep()
local path_cur = require('navigator.util').path_cur() local path_cur = require('navigator.util').path_cur()
local cwd = vim.loop.cwd() local cwd = vim.loop.cwd()
local M = {} local M = {}
local function call_hierarchy_handler(direction, err, result, ctx, _, error_message) local outgoing_calls_handler
local incoming_calls_handler
local function call_hierarchy_handler(direction, err, result, ctx, config)
log(direction, err, result, ctx, config)
if not result then if not result then
vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN) vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN)
return return
end end
trace('call_hierarchy', result) -- trace('call_hierarchy', result)
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp_tags') assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp_tags')
if err ~= nil then if err ~= nil then
log('dir', direction, 'result', result, 'err', err, ctx) log('dir', direction, 'result', result, 'err', err, ctx)
vim.notify('ERROR: ' .. error_message, vim.lsp.log_levels.WARN) vim.notify('ERROR: ' .. err, vim.lsp.log_levels.WARN)
return return
end end
local items = {} local items = ctx.items or {}
for _, call_hierarchy_call in pairs(result) do for _, call_hierarchy_result in pairs(result) do
local call_hierarchy_item = call_hierarchy_call[direction] local call_hierarchy_item = call_hierarchy_result[direction]
local kind = '' local kind = ''
if call_hierarchy_item.kind then if call_hierarchy_item.kind then
kind = require('navigator.lspclient.lspkind').symbol_kind(call_hierarchy_item.kind) .. ' ' kind = require('navigator.lspclient.lspkind').symbol_kind(call_hierarchy_item.kind) .. ' '
end end
-- for _, range in pairs(call_hierarchy_call.fromRanges) do
local range = call_hierarchy_item.range or call_hierarchy_item.selectionRange
local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)) local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri))
local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1) local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1)
call_hierarchy_item.detail = call_hierarchy_item.detail or '' call_hierarchy_item.detail = call_hierarchy_item.detail or ''
call_hierarchy_item.detail = string.gsub(call_hierarchy_item.detail, '\n', '') call_hierarchy_item.detail = string.gsub(call_hierarchy_item.detail, '\n', '')
trace(range, call_hierarchy_item) trace(result, call_hierarchy_item)
local disp_item = { local disp_item = vim.tbl_deep_extend('force', {}, call_hierarchy_item)
uri = call_hierarchy_item.uri, disp_item = vim.tbl_deep_extend('force', disp_item, {
filename = filename, filename = filename,
display_filename = display_filename, display_filename = display_filename,
indent = ctx.depth,
text = kind .. call_hierarchy_item.name .. '' .. call_hierarchy_item.detail, text = kind .. call_hierarchy_item.name .. '' .. call_hierarchy_item.detail,
range = range, lnum = call_hierarchy_item.selectionRange.start.line + 1,
lnum = range.start.line + 1, col = call_hierarchy_item.selectionRange.start.character,
col = range.start.character, })
}
table.insert(items, disp_item) table.insert(items, disp_item)
-- 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 end
log(items)
return items return items
end end
local call_hierarchy_handler_from = partial(call_hierarchy_handler, 'from') local call_hierarchy_handler_from = partial(call_hierarchy_handler, 'from')
local call_hierarchy_handler_to = partial(call_hierarchy_handler, 'to') local call_hierarchy_handler_to = partial(call_hierarchy_handler, 'to')
-- local function incoming_calls_handler(bang, err, result, ctx, cfg) incoming_calls_handler = function(_, err, result, ctx, cfg)
local function incoming_calls_handler(_, err, result, ctx, cfg)
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp hierarchy') 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 = call_hierarchy_handler_from(err, result, ctx, cfg, 'Incoming calls not found')
local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft') local ft = vim.api.nvim_buf_get_option(ctx.bufnr or vim.api.nvim_get_current_buf(), 'ft')
gui.new_list_view({ items = results, ft = ft, api = '' }) if ctx.no_show then
return results
end
local win = gui.new_list_view({ items = results, ft = ft, api = '' })
return results, win
end end
local function outgoing_calls_handler(_, err, result, ctx, cfg) outgoing_calls_handler = function(_, err, result, ctx, cfg)
local results = call_hierarchy_handler_to(err, result, ctx, cfg, 'Outgoing calls not found') local results = call_hierarchy_handler_to(err, result, ctx, cfg, 'Outgoing calls not found')
local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft') local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft')
gui.new_list_view({ items = results, ft = ft, api = '' }) if ctx.no_show then
-- fzf_locations(bang, "", "Outgoing Calls", results, false) return results
end
local win = gui.new_list_view({ items = results, ft = ft, api = '' })
return result, win
end end
function M.incoming_calls(bang, opts) local function request(method, params, handler)
local bufnr = vim.api.nvim_get_current_buf() return vim.lsp.buf_request(0, method, params, handler)
assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp hierarchy') end
if not lsphelper.check_capabilities('callHierarchyProvider') then
local function pick_call_hierarchy_item(call_hierarchy_items)
if not call_hierarchy_items then
return return
end 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(method, opts)
local params = vim.lsp.util.make_position_params() local params = vim.lsp.util.make_position_params()
lsphelper.call_sync('callHierarchy/incomingCalls', params, opts, partial(incoming_calls_handler, bang)) opts = opts or {}
request(
'textDocument/prepareCallHierarchy',
params,
vim.lsp.with(function(err, result, ctx)
if err then
vim.notify(err.message, vim.log.levels.WARN)
return
end
local call_hierarchy_item = pick_call_hierarchy_item(result)
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)
else
vim.notify(
string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
vim.log.levels.WARN
)
end
end, { direction = method, depth = opts.depth })
)
end end
function M.outgoing_calls(bang, opts) function M.incoming_calls(opts)
local bufnr = vim.api.nvim_get_current_buf() call_hierarchy('callHierarchy/incomingCalls', opts)
assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp_tags') end
if not lsphelper.check_capabilities('callHierarchyProvider') then
return
end
local params = vim.lsp.util.make_position_params() function M.outgoing_calls(opts)
lsphelper.call_sync('callHierarchy/outgoingCalls', params, opts, partial(outgoing_calls_handler, bang)) call_hierarchy('callHierarchy/outgoingCalls', opts)
end end
M.incoming_calls_call = partial(M.incoming_calls, 0) M.incoming_calls_call = partial(M.incoming_calls, 0)

@ -24,7 +24,25 @@ end
local util = lspconfig.util local util = lspconfig.util
local config = require('navigator').config_values() local config = require('navigator').config_values()
local disabled_ft = {
'NvimTree',
'guihua',
'clap_input',
'clap_spinner',
'vista',
'vista_kind',
'TelescopePrompt',
'guihua_rust',
'csv',
'txt',
'defx',
'packer',
'gitcommit',
'windline',
'notify',
'nofile',
'',
}
-- local cap = vim.lsp.protocol.make_client_capabilities() -- local cap = vim.lsp.protocol.make_client_capabilities()
local on_attach = require('navigator.lspclient.attach').on_attach local on_attach = require('navigator.lspclient.attach').on_attach
-- gopls["ui.completion.usePlaceholders"] = true -- gopls["ui.completion.usePlaceholders"] = true
@ -708,6 +726,14 @@ local function get_cfg(client)
end end
end end
local function ft_disabled(ft)
for i = 1, #disabled_ft do
if ft == disabled_ft[i] then
return true
end
end
end
local function setup(user_opts, cnt) local function setup(user_opts, cnt)
user_opts = user_opts or {} user_opts = user_opts or {}
local ft = vim.bo.filetype local ft = vim.bo.filetype
@ -744,28 +770,10 @@ local function setup(user_opts, cnt)
log('navigator was loaded for ft', ft, bufnr) log('navigator was loaded for ft', ft, bufnr)
return return
end end
local disable_ft = {
'NvimTree', if ft_disabled(ft) then
'guihua', trace('navigator disabled for ft or it is loaded', ft)
'clap_input', return
'clap_spinner',
'vista',
'vista_kind',
'TelescopePrompt',
'guihua_rust',
'csv',
'txt',
'defx',
'packer',
'gitcommit',
'windline',
'notify',
}
for i = 1, #disable_ft do
if ft == disable_ft[i] then
trace('navigator disabled for ft or it is loaded', ft)
return
end
end end
if _NgConfigValues.lsp.servers then if _NgConfigValues.lsp.servers then
add_servers(_NgConfigValues.lsp.servers) add_servers(_NgConfigValues.lsp.servers)
@ -840,4 +848,12 @@ local function on_filetype()
setup({ bufnr = bufnr }) setup({ bufnr = bufnr })
end end
return { setup = setup, get_cfg = get_cfg, lsp = servers, add_servers = add_servers, on_filetype = on_filetype } return {
setup = setup,
get_cfg = get_cfg,
lsp = servers,
add_servers = add_servers,
on_filetype = on_filetype,
disabled_ft = disabled_ft,
ft_disabled = ft_disabled,
}

@ -63,11 +63,13 @@ local key_maps = {
} }
local commands = { local commands = {
[[command! -nargs=* Nctags lua require("navigator.ctags").ctags({<f-args>})]], [[command! -nargs=* Nctags lua require("navigator.ctags").ctags(<f-args>)]],
"command! -nargs=0 LspLog lua require'navigator.lspclient.config'.open_lsp_log()", "command! -nargs=0 LspLog lua require'navigator.lspclient.config'.open_lsp_log()",
"command! -nargs=0 LspRestart lua require'navigator.lspclient.config'.reload_lsp()", "command! -nargs=0 LspRestart lua require'navigator.lspclient.config'.reload_lsp()",
"command! -nargs=0 LspToggleFmt lua require'navigator.lspclient.mapping'.toggle_lspformat()<CR>", "command! -nargs=0 LspToggleFmt lua require'navigator.lspclient.mapping'.toggle_lspformat()<CR>",
"command! -nargs=0 LspKeymaps lua require'navigator.lspclient.mapping'.get_keymaps_help()<CR>", "command! -nargs=0 LspKeymaps lua require'navigator.lspclient.mapping'.get_keymaps_help()<CR>",
"command! -nargs=0 LspSymbols lua require'navigator.symbols'.side_panel()<CR>",
"command! -nargs=0 TSymbols lua require'navigator.treesitter'.side_panel()<CR>",
} }
local key_maps_help = {} local key_maps_help = {}
@ -179,7 +181,7 @@ local function set_mapping(lsp_info)
elseif string.find(value.func, 'format') then elseif string.find(value.func, 'format') then
fmtkey = value.key fmtkey = value.key
end end
log('binding', k, f) trace('binding', k, f)
set_keymap(m, k, f, opts) set_keymap(m, k, f, opts)
end end
@ -279,12 +281,12 @@ function M.setup(user_opts)
local client = user_opts.client or {} local client = user_opts.client or {}
local cap = client.server_capabilities or vim.lsp.protocol.make_client_capabilities() local cap = client.server_capabilities or vim.lsp.protocol.make_client_capabilities()
log('lsp cap:', cap) log('lsp cap:', cap.codeActionProvider)
if cap.call_hierarchy or cap.callHierarchyProvider then -- if cap.call_hierarchy or cap.callHierarchyProvider then
vim.lsp.handlers['callHierarchy/incomingCalls'] = require('navigator.hierarchy').incoming_calls_handler -- vim.lsp.handlers['callHierarchy/incomingCalls'] = require('navigator.hierarchy').incoming_calls_handler
vim.lsp.handlers['callHierarchy/outgoingCalls'] = require('navigator.hierarchy').outgoing_calls_handler -- vim.lsp.handlers['callHierarchy/outgoingCalls'] = require('navigator.hierarchy').outgoing_calls_handler
end -- end
vim.lsp.handlers['textDocument/references'] = require('navigator.reference').reference_handler vim.lsp.handlers['textDocument/references'] = require('navigator.reference').reference_handler
-- vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler -- vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler

@ -145,18 +145,20 @@ end
function M.call_sync(method, params, opts, handler) function M.call_sync(method, params, opts, handler)
params = params or {} params = params or {}
opts = opts or {} opts = opts or {}
local results_lsp, err = lsp.buf_request_sync(0, method, params, opts.timeout or vim.g.navtator_timeout or 1000) log(method, params)
local results_lsp, err = lsp.buf_request_sync(opts.bufnr or 0, method, params, opts.timeout or 1000)
handler(err, extract_result(results_lsp), { method = method }, nil) return handler(err, extract_result(results_lsp), { method = method, no_show = opts.no_show }, nil)
end end
function M.call_async(method, params, handler) function M.call_async(method, params, handler, bufnr)
params = params or {} params = params or {}
local callback = function(...) local callback = function(...)
util.show(...) util.show(...)
handler(...) handler(...)
end end
return lsp.buf_request(0, method, params, callback) bufnr = bufnr or 0
return lsp.buf_request(bufnr, method, params, callback)
-- results_lsp, canceller -- results_lsp, canceller
end end

@ -11,7 +11,6 @@ function M.workspace_symbols(query)
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local params = { query = query } local params = { query = query }
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr)
-- if client.resolved_capabilities.workspace_symbol then
if client.server_capabilities.workspaceSymbolProvider then if client.server_capabilities.workspaceSymbolProvider then
client.request('workspace/symbol', params, M.workspace_symbol_handler, _bufnr) client.request('workspace/symbol', params, M.workspace_symbol_handler, _bufnr)
end end
@ -33,7 +32,6 @@ function M.document_symbols(opts)
params.context = { includeDeclaration = true } params.context = { includeDeclaration = true }
params.query = opts.prompt or '' params.query = opts.prompt or ''
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr)
-- if client.resolved_capabilities.document_symbol then
if client.server_capabilities.documentSymbolProvider then if client.server_capabilities.documentSymbolProvider then
client.request('textDocument/documentSymbol', params, M.document_symbol_handler, _bufnr) client.request('textDocument/documentSymbol', params, M.document_symbol_handler, _bufnr)
end end
@ -51,7 +49,7 @@ M.document_symbol_handler = function(err, result, ctx)
end end
if not result or vim.tbl_isempty(result) then if not result or vim.tbl_isempty(result) then
vim.notify('symbol' .. query .. 'not found for buf' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) vim.notify('symbol ' .. query .. ' not found for buf ' .. vim.inspect(ctx), vim.lsp.log_levels.WARN)
return return
end end
local locations = {} local locations = {}
@ -66,7 +64,7 @@ M.document_symbol_handler = function(err, result, ctx)
item.name = result[i].name item.name = result[i].name
item.range = result[i].range or result[i].location.range item.range = result[i].range or result[i].location.range
if item.range == nil then if item.range == nil then
log("range missing in result", result[i]) log('range missing in result', result[i])
end end
item.uri = uri item.uri = uri
item.selectionRange = result[i].selectionRange item.selectionRange = result[i].selectionRange
@ -79,6 +77,10 @@ M.document_symbol_handler = function(err, result, ctx)
item.text = '[' .. kind .. ']' .. item.name .. ' ' .. item.detail item.text = '[' .. kind .. ']' .. item.name .. ' ' .. item.detail
item.filename = fname item.filename = fname
item.indent_level = 1
item.type = kind
item.node_text = item.name
table.insert(locations, item) table.insert(locations, item)
if result[i].children ~= nil then if result[i].children ~= nil then
@ -88,16 +90,23 @@ M.document_symbol_handler = function(err, result, ctx)
child.name = c.name child.name = c.name
child.range = c.range or c.location.range child.range = c.range or c.location.range
local ckind = symbol_kind(child.kind) local ckind = symbol_kind(child.kind)
child.node_text = child.name
child.type = ckind
child.selectionRange = c.selectionRange child.selectionRange = c.selectionRange
child.filename = fname child.filename = fname
child.uri = uri child.uri = uri
child.lnum = child.range.start.line + 1 child.lnum = child.range.start.line + 1
child.detail = c.detail or '' child.detail = c.detail or ''
child.indent_level = 2
child.text = '' .. ckind .. '' .. child.name .. ' ' .. child.detail child.text = '' .. ckind .. '' .. child.name .. ' ' .. child.detail
table.insert(locations, child) table.insert(locations, child)
end end
end end
end end
if ctx.no_show then
return locations
end
local ft = vim.api.nvim_buf_get_option(bufnr, 'ft') local ft = vim.api.nvim_buf_get_option(bufnr, 'ft')
gui.new_list_view({ gui.new_list_view({
@ -133,4 +142,26 @@ M.workspace_symbol_handler = function(err, result, ctx, cfg)
gui.new_list_view({ items = items, prompt = true, ft = ft, rowdata = true, api = '' }) gui.new_list_view({ items = items, prompt = true, ft = ft, rowdata = true, api = '' })
end end
function M.side_panel()
local Panel = require('guihua.panel')
local buf = vim.api.nvim_get_current_buf()
local p = Panel:new({
render = function(bufnr)
local ft = vim.api.nvim_buf_get_option(bufnr, 'buftype')
if ft == 'nofile' or ft == 'guihua' or ft == 'prompt' then
return
end
local params = vim.lsp.util.make_range_params()
local sync_req = require('navigator.lspwrapper').call_sync
return sync_req(
'textDocument/documentSymbol',
params,
{ timeout = 1000, bufnr = bufnr, no_show = true },
vim.lsp.with(M.document_symbol_handler, { no_show = true })
)
end,
})
p:open(true)
end
return M return M

@ -110,7 +110,6 @@ end
--- This function copy from treesitter/refactor/navigation.lua --- This function copy from treesitter/refactor/navigation.lua
local function get_definitions(bufnr) local function get_definitions(bufnr)
local local_nodes = ts_locals.get_locals(bufnr) local local_nodes = ts_locals.get_locals(bufnr)
-- Make sure the nodes are unique. -- Make sure the nodes are unique.
local nodes_set = {} local nodes_set = {}
for _, loc in ipairs(local_nodes) do for _, loc in ipairs(local_nodes) do
@ -291,8 +290,12 @@ local function get_all_nodes(bufnr, filter, summary)
vim.notify('get_all_node invalide bufnr', vim.lsp.log_levels.WARN) vim.notify('get_all_node invalide bufnr', vim.lsp.log_levels.WARN)
end end
summary = summary or false summary = summary or false
local ft = vim.api.nvim_buf_get_option(bufnr, 'filetype')
if not parsers.has_parser() then if not parsers.has_parser() then
vim.notify('ts not loaded', vim.lsp.log_levels.Debug) if not require('navigator.lspclient.clients').ft_disabled(ft) then
vim.notify('ts not loaded ' .. ft, vim.lsp.log_levels.Debug)
end
return {}
end end
local path_sep = require('navigator.util').path_sep() local path_sep = require('navigator.util').path_sep()
@ -311,7 +314,7 @@ local function get_all_nodes(bufnr, filter, summary)
['arrow_function'] = true, ['arrow_function'] = true,
['type'] = true, ['type'] = true,
['class'] = true, ['class'] = true,
['var'] = true, -- ['var'] = true,
['struct'] = true, ['struct'] = true,
['method'] = true, ['method'] = true,
} }
@ -327,17 +330,31 @@ local function get_all_nodes(bufnr, filter, summary)
-- Step 2 find correct completions -- Step 2 find correct completions
local length = 10 local length = 10
local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos
local loaded_symbol = {}
for _, def in ipairs(get_definitions(bufnr)) do for _, def in ipairs(get_definitions(bufnr)) do
local n = #parents local n = #parents
for i = 1, n do for i = 1, n do
local index = n + 1 - i local index = n + 1 - i
local parent_def = parents[index] local parent_def = parents[index]
log(parent_def.type, parent_def.node:type(), vim.treesitter.get_node_text(parent_def.node, bufnr))
log(def.node:type(), vim.treesitter.get_node_text(def.node, bufnr))
if if
ts_utils.is_parent(parent_def.node, def.node) ts_utils.is_parent(parent_def.node, def.node)
or (containers[parent_def.type] and ts_utils.is_parent(parent_def.node:parent(), def.node)) or (
containers[parent_def.type]
and (
ts_utils.is_parent(parent_def.node:parent(), def.node)
or (
parent_def.node:parent():type():find('dot_index')
and ts_utils.is_parent(parent_def.node:parent():parent(), def.node)
)
)
)
then then
log('is parent', i, index)
break break
else else
log('leave node', i, index)
parents[index] = nil parents[index] = nil
end end
end end
@ -353,6 +370,10 @@ local function get_all_nodes(bufnr, filter, summary)
trace(item.type, item.kind) trace(item.type, item.kind)
goto continue goto continue
end end
if item.type == 'associated' then
goto continue
end
local tsdata = node.def local tsdata = node.def
if node.def == nil then if node.def == nil then
@ -369,20 +390,40 @@ local function get_all_nodes(bufnr, filter, summary)
if is_func then if is_func then
-- hack for lua and maybe other language aswell -- hack for lua and maybe other language aswell
local parent = tsdata:parent() local parent = tsdata:parent()
if parent ~= nil and parent:type() == 'function_name' or parent:type() == 'function_name_field' then if parent ~= nil then
log(parent:type(), vim.treesitter.get_node_text(parent, bufnr), item.node_text, item.type)
end
if
parent ~= nil
and (
parent:type() == 'function_name'
-- or parent:type() == 'function'
-- or parent:type() == 'function_declaration' -- this bring in too much info
or parent:type() == 'method_name'
or parent:type() == 'function_name_field'
)
then
-- replace function name
item.node_text = vim.treesitter.get_node_text(parent, bufnr) item.node_text = vim.treesitter.get_node_text(parent, bufnr)
local cut = item.node_text:find('[\n\r]')
if cut then
item.node_text = item.node_text:sub(1, cut - 1)
end
log(parent:type(), item.node_text) log(parent:type(), item.node_text)
end end
end end
trace(item.node_text, item.kind, item.type) trace(item.node_text, item.kind, item.type)
if scope ~= nil then if scope ~= nil then
-- it is strange..
if not is_func and summary then if not is_func and summary then
log(item.node_text, item.type)
goto continue goto continue
end end
item.node_scope = ts_utils.node_to_lsp_range(scope) item.node_scope = ts_utils.node_to_lsp_range(scope)
end end
if item.node_text == '_' then
goto continue
end
if summary then if summary then
if item.node_scope ~= nil then if item.node_scope ~= nil then
table.insert(all_nodes, item) table.insert(all_nodes, item)
@ -394,7 +435,6 @@ local function get_all_nodes(bufnr, filter, summary)
tsdata:type(), tsdata:type(),
item.node_text, item.node_text,
item.kind, item.kind,
item.node_text,
'range', 'range',
item.node_scope.start.line, item.node_scope.start.line,
item.node_scope['end'].line item.node_scope['end'].line
@ -405,10 +445,9 @@ local function get_all_nodes(bufnr, filter, summary)
item.range = ts_utils.node_to_lsp_range(tsdata) item.range = ts_utils.node_to_lsp_range(tsdata)
local start_line_node, _, _ = tsdata:start() local start_line_node, _, _ = tsdata:start()
if item.node_text == '_' then
goto continue local line_text = api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or ''
end item.full_text = vim.trim(line_text)
item.full_text = vim.trim(api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or '')
item.full_text = item.full_text:gsub('%s*[%[%(%{]*%s*$', '') item.full_text = item.full_text:gsub('%s*[%[%(%{]*%s*$', '')
item.uri = uri item.uri = uri
@ -423,7 +462,23 @@ local function get_all_nodes(bufnr, filter, summary)
indent = string.rep(' ', #parents - 1) .. '' indent = string.rep(' ', #parents - 1) .. ''
end end
item.indent = indent item.indent = indent
item.indent_level = #parents item.indent_level = #parents -- maybe use real indent level ?
if item.indent_level <= 1 then
local sp = string.match(line_text, '(%s*)')
log(line_text, #sp)
if sp then
local indent_level = #sp / (vim.o.shiftwidth or 4) + 1
item.indent_level = math.max(item.indent_level, indent_level)
end
end
if #parents > 0 then
log(parents[1].type, vim.treesitter.get_node_text(parents[1].node, bufnr))
if parents[2] then
log(parents[2].type, vim.treesitter.get_node_text(parents[2].node, bufnr))
end
else
log('root node')
end
if #all_nodes >= 1 then if #all_nodes >= 1 then
all_nodes[#all_nodes].next_indent_level = #parents all_nodes[#all_nodes].next_indent_level = #parents
end end
@ -432,7 +487,13 @@ local function get_all_nodes(bufnr, filter, summary)
if #item.text > length then if #item.text > length then
length = #item.text length = #item.text
end end
table.insert(all_nodes, item) if
loaded_symbol[item.node_text .. item.kind] == nil
or not util.range_inside(loaded_symbol[item.node_text .. item.kind], item.node_scope)
then
table.insert(all_nodes, item)
loaded_symbol[item.node_text .. item.kind] = item.node_scope
end
::continue:: ::continue::
end end
end end
@ -446,8 +507,12 @@ local function get_all_nodes(bufnr, filter, summary)
end end
function M.buf_func(bufnr) function M.buf_func(bufnr)
local ft = vim.api.nvim_buf_get_option(bufnr, 'buftype')
if vim.api.nvim_buf_get_option(bufnr, 'buftype') == 'nofile' then
return
end
if not ok or ts_locals == nil then if not ok or ts_locals == nil then
error('treesitter not loaded') error('treesitter not loaded: ' .. ft)
return return
end end
@ -492,15 +557,33 @@ function M.buf_func(bufnr)
return all_nodes, width return all_nodes, width
end end
function M.buf_ts() function M.all_ts_nodes(bufnr)
if ts_locals == nil then if ts_locals == nil then
error('treesitter not loaded') error('treesitter not loaded')
return return
end end
local bufnr = api.nvim_get_current_buf() local bufnr = bufnr or api.nvim_get_current_buf()
local all_nodes, width = get_all_nodes(bufnr) local all_nodes, width = get_all_nodes(bufnr)
return all_nodes, width
end
function M.side_panel()
Panel = require('guihua.panel')
local bufnr = api.nvim_get_current_buf()
local p = Panel:new({
header = 'treesitter',
render = function(b)
log('render for ', bufnr, b)
return require('navigator.treesitter').all_ts_nodes(b)
end,
})
p:open(true)
end
function M.buf_ts()
local all_nodes, width = M.all_ts_nodes()
local bufnr = api.nvim_get_current_buf()
local ft = vim.api.nvim_buf_get_option(bufnr, 'ft') local ft = vim.api.nvim_buf_get_option(bufnr, 'ft')
local listview = gui.new_list_view({ local listview = gui.new_list_view({
items = all_nodes, items = all_nodes,
@ -512,7 +595,7 @@ function M.buf_ts()
width = width + 10, width = width + 10,
api = _NgConfigValues.icons.treesitter_defult, api = _NgConfigValues.icons.treesitter_defult,
}) })
return listview, all_nodes, width return listview, all_nodes, width
end end
M.get_all_nodes = get_all_nodes M.get_all_nodes = get_all_nodes

@ -71,6 +71,9 @@ function M.io_read(filename, total)
return content return content
end end
function M.rm_file(filename)
return os.remove(filename)
end
function M.file_exists(name) function M.file_exists(name)
local f = io.open(name, "r") local f = io.open(name, "r")
@ -417,9 +420,9 @@ function M.mk_handler(fn)
end end
function M.partial(func, arg) function M.partial(func, arg)
return (M.mk_handler(function(...) return function(...)
return func(arg, ...) return func(arg, ...)
end)) end
end end
function M.empty(t) function M.empty(t)
@ -443,7 +446,7 @@ function M.encoding(client)
end end
local oe = client.offset_encoding local oe = client.offset_encoding
if oe == nil then if oe == nil then
return 'utf-16' return 'utf-8'
end end
if type(oe) == 'table' then if type(oe) == 'table' then
return oe[1] return oe[1]
@ -465,4 +468,14 @@ function M.info(msg)
vim.notify('INF: ' .. msg, vim.lsp.log_levels.INFO) vim.notify('INF: ' .. msg, vim.lsp.log_levels.INFO)
end end
function M.range_inside(outer, inner)
if outer == nil or inner == nil then
return false
end
if outer.start == nil or outer['end'] == nil or inner.start == nil or inner['end'] == nil then
return false
end
return outer.start.line <= inner.start.line and outer['end'].line >= inner['end'].line
end
return M return M

Loading…
Cancel
Save