From 1f6103ed954d4e584561481ca9905dac666aec5b Mon Sep 17 00:00:00 2001 From: rayx Date: Wed, 22 Jun 2022 01:56:17 +1000 Subject: [PATCH] Side panel (#197) * add sidepanel * revert some changes and fix nil indent level * Add side panel; bugfix for ctags --- README.md | 15 ++- autoload/folding.vim | 2 +- lua/navigator/ctags.lua | 7 +- lua/navigator/diagnostics.lua | 41 +++++++- lua/navigator/foldlsp.lua | 1 - lua/navigator/foldts.lua | 2 +- lua/navigator/hierarchy.lua | 144 ++++++++++++++++++++-------- lua/navigator/lspclient/clients.lua | 64 ++++++++----- lua/navigator/lspclient/mapping.lua | 16 ++-- lua/navigator/lspwrapper.lua | 10 +- lua/navigator/sidepanel.lua | 0 lua/navigator/symbols.lua | 39 +++++++- lua/navigator/treesitter.lua | 117 ++++++++++++++++++---- lua/navigator/util.lua | 19 +++- 14 files changed, 370 insertions(+), 107 deletions(-) create mode 100644 lua/navigator/sidepanel.lua diff --git a/README.md b/README.md index 5132166..02f3cce 100644 --- a/README.md +++ b/README.md @@ -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. -- 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) @@ -94,7 +96,9 @@ variable is: - 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. @@ -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) +### Sidebar, folding, outline +Treesitter outline and Diagnostics +image +image + + + ### GUI and multigrid support You can load a different font size for floating win diff --git a/autoload/folding.vim b/autoload/folding.vim index bf2793a..89e30f9 100644 --- a/autoload/folding.vim +++ b/autoload/folding.vim @@ -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.foldts".get_fold_indic(%d)', v:lnum)) endfunction diff --git a/lua/navigator/ctags.lua b/lua/navigator/ctags.lua index 3d797e7..fc7f427 100644 --- a/lua/navigator/ctags.lua +++ b/lua/navigator/ctags.lua @@ -54,6 +54,8 @@ end local function ctags_gen() local cmd = 'ctags' -- -x -n -u -f - ' .. vfn.expand('%:p') 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 ' if _NgConfigValues.ctags then cmd = _NgConfigValues.ctags.cmd @@ -64,6 +66,7 @@ local function ctags_gen() options = options .. '--language=' .. lang cmd = cmd .. ' ' .. options cmd = string.format('%s -f %s %s --language=%s', cmd, output, options, lang) + cmd = vim.split(cmd, ' ') log(cmd) vfn.jobstart(cmd, { on_stdout = function(_, _, _) @@ -72,7 +75,7 @@ local function ctags_gen() on_exit = function(_, data, _) -- id, data, event -- 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) else vim.notify('ctags generated') @@ -108,10 +111,10 @@ local function ctags_symbols() local ft = vim.o.ft local result = symbols_to_items(items) - log(result) if next(result) == nil then return vim.notify('no symbols found') end + log(result[1]) local opt = { api = ' ', ft = ft, diff --git a/lua/navigator/diagnostics.lua b/lua/navigator/diagnostics.lua index 5e6aebc..2829026 100644 --- a/lua/navigator/diagnostics.lua +++ b/lua/navigator/diagnostics.lua @@ -350,8 +350,6 @@ end M.show_buf_diagnostics = function() 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 display_items = {} for _, client_items in pairs(results) do @@ -369,7 +367,7 @@ M.show_buf_diagnostics = function() enable_preview_edit = true, }) if listview == nil then - return log("nil listview") + return log('nil listview') end trace('new buffer', listview.bufnr) if listview.bufnr then @@ -471,6 +469,43 @@ function M.show_diagnostics(pos) 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) cfg = cfg or {} local default_cfg = { diff --git a/lua/navigator/foldlsp.lua b/lua/navigator/foldlsp.lua index 0bba4be..ce05ae7 100644 --- a/lua/navigator/foldlsp.lua +++ b/lua/navigator/foldlsp.lua @@ -56,7 +56,6 @@ function M.setup_plugin() M.active_folding_clients[client_id] = server_supports_folding end end - -- print(vim.inspect(M.active_folding_clients)) end function M.update_folds() diff --git a/lua/navigator/foldts.lua b/lua/navigator/foldts.lua index 42c2732..1219d93 100644 --- a/lua/navigator/foldts.lua +++ b/lua/navigator/foldts.lua @@ -44,7 +44,7 @@ function M.setup_fold() local current_window = api.nvim_get_current_win() 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 -- This is cached on buf tick to avoid computing that multiple times diff --git a/lua/navigator/hierarchy.lua b/lua/navigator/hierarchy.lua index 1e2e01e..98d2536 100644 --- a/lua/navigator/hierarchy.lua +++ b/lua/navigator/hierarchy.lua @@ -1,7 +1,7 @@ local gui = require('navigator.gui') local util = require('navigator.util') local log = util.log -local trace = util.trace +local trace = util.log local partial = util.partial 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 cwd = vim.loop.cwd() 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 vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN) return end - trace('call_hierarchy', result) + -- trace('call_hierarchy', result) 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') if err ~= nil then 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 end - local items = {} + local items = ctx.items or {} - for _, call_hierarchy_call in pairs(result) do - local call_hierarchy_item = call_hierarchy_call[direction] + 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 - -- 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 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(range, call_hierarchy_item) + trace(result, call_hierarchy_item) - local disp_item = { - uri = call_hierarchy_item.uri, + 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, text = kind .. call_hierarchy_item.name .. ' ﰲ ' .. call_hierarchy_item.detail, - range = range, - lnum = range.start.line + 1, - col = range.start.character, - } - + lnum = call_hierarchy_item.selectionRange.start.line + 1, + col = call_hierarchy_item.selectionRange.start.character, + }) 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 + 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 function incoming_calls_handler(bang, err, result, ctx, cfg) -local function incoming_calls_handler(_, err, result, ctx, cfg) +incoming_calls_handler = function(_, err, result, ctx, cfg) 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') 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') - gui.new_list_view({ items = results, ft = ft, api = ' ' }) + 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 = ' ' }) + return results, win 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 ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft') - gui.new_list_view({ items = results, ft = ft, api = ' ' }) - -- fzf_locations(bang, "", "Outgoing Calls", results, false) + if ctx.no_show then + return results + end + local win = gui.new_list_view({ items = results, ft = ft, api = ' ' }) + return result, win end -function M.incoming_calls(bang, opts) - 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') - if not lsphelper.check_capabilities('callHierarchyProvider') then +local function request(method, params, handler) + return vim.lsp.buf_request(0, method, params, handler) +end + +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(method, opts) 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 -function M.outgoing_calls(bang, opts) - 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') - if not lsphelper.check_capabilities('callHierarchyProvider') then - return - end +function M.incoming_calls(opts) + call_hierarchy('callHierarchy/incomingCalls', opts) +end - local params = vim.lsp.util.make_position_params() - lsphelper.call_sync('callHierarchy/outgoingCalls', params, opts, partial(outgoing_calls_handler, bang)) +function M.outgoing_calls(opts) + call_hierarchy('callHierarchy/outgoingCalls', opts) end M.incoming_calls_call = partial(M.incoming_calls, 0) diff --git a/lua/navigator/lspclient/clients.lua b/lua/navigator/lspclient/clients.lua index 678c3d4..098a61c 100644 --- a/lua/navigator/lspclient/clients.lua +++ b/lua/navigator/lspclient/clients.lua @@ -24,7 +24,25 @@ end local util = lspconfig.util 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 on_attach = require('navigator.lspclient.attach').on_attach -- gopls["ui.completion.usePlaceholders"] = true @@ -708,6 +726,14 @@ local function get_cfg(client) 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) user_opts = user_opts or {} local ft = vim.bo.filetype @@ -744,28 +770,10 @@ local function setup(user_opts, cnt) log('navigator was loaded for ft', ft, bufnr) return end - local disable_ft = { - 'NvimTree', - 'guihua', - 'clap_input', - '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 + + if ft_disabled(ft) then + trace('navigator disabled for ft or it is loaded', ft) + return end if _NgConfigValues.lsp.servers then add_servers(_NgConfigValues.lsp.servers) @@ -840,4 +848,12 @@ local function on_filetype() setup({ bufnr = bufnr }) 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, +} diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua index b04040f..5f8a386 100644 --- a/lua/navigator/lspclient/mapping.lua +++ b/lua/navigator/lspclient/mapping.lua @@ -63,11 +63,13 @@ local key_maps = { } local commands = { - [[command! -nargs=* Nctags lua require("navigator.ctags").ctags({})]], + [[command! -nargs=* Nctags lua require("navigator.ctags").ctags()]], "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 LspToggleFmt lua require'navigator.lspclient.mapping'.toggle_lspformat()", "command! -nargs=0 LspKeymaps lua require'navigator.lspclient.mapping'.get_keymaps_help()", + "command! -nargs=0 LspSymbols lua require'navigator.symbols'.side_panel()", + "command! -nargs=0 TSymbols lua require'navigator.treesitter'.side_panel()", } local key_maps_help = {} @@ -179,7 +181,7 @@ local function set_mapping(lsp_info) elseif string.find(value.func, 'format') then fmtkey = value.key end - log('binding', k, f) + trace('binding', k, f) set_keymap(m, k, f, opts) end @@ -279,12 +281,12 @@ function M.setup(user_opts) local client = user_opts.client or {} 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 - vim.lsp.handlers['callHierarchy/incomingCalls'] = require('navigator.hierarchy').incoming_calls_handler - vim.lsp.handlers['callHierarchy/outgoingCalls'] = require('navigator.hierarchy').outgoing_calls_handler - end + -- if cap.call_hierarchy or cap.callHierarchyProvider then + -- vim.lsp.handlers['callHierarchy/incomingCalls'] = require('navigator.hierarchy').incoming_calls_handler + -- vim.lsp.handlers['callHierarchy/outgoingCalls'] = require('navigator.hierarchy').outgoing_calls_handler + -- end vim.lsp.handlers['textDocument/references'] = require('navigator.reference').reference_handler -- vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler diff --git a/lua/navigator/lspwrapper.lua b/lua/navigator/lspwrapper.lua index 3404197..7825b1d 100644 --- a/lua/navigator/lspwrapper.lua +++ b/lua/navigator/lspwrapper.lua @@ -145,18 +145,20 @@ end function M.call_sync(method, params, opts, handler) params = params 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 -function M.call_async(method, params, handler) +function M.call_async(method, params, handler, bufnr) params = params or {} local callback = function(...) util.show(...) handler(...) end - return lsp.buf_request(0, method, params, callback) + bufnr = bufnr or 0 + return lsp.buf_request(bufnr, method, params, callback) -- results_lsp, canceller end diff --git a/lua/navigator/sidepanel.lua b/lua/navigator/sidepanel.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/navigator/symbols.lua b/lua/navigator/symbols.lua index 0ea683e..64798a0 100644 --- a/lua/navigator/symbols.lua +++ b/lua/navigator/symbols.lua @@ -11,7 +11,6 @@ function M.workspace_symbols(query) local bufnr = vim.api.nvim_get_current_buf() local params = { query = query } vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) - -- if client.resolved_capabilities.workspace_symbol then if client.server_capabilities.workspaceSymbolProvider then client.request('workspace/symbol', params, M.workspace_symbol_handler, _bufnr) end @@ -33,7 +32,6 @@ function M.document_symbols(opts) params.context = { includeDeclaration = true } params.query = opts.prompt or '' vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) - -- if client.resolved_capabilities.document_symbol then if client.server_capabilities.documentSymbolProvider then client.request('textDocument/documentSymbol', params, M.document_symbol_handler, _bufnr) end @@ -51,7 +49,7 @@ M.document_symbol_handler = function(err, result, ctx) end 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 end local locations = {} @@ -66,7 +64,7 @@ M.document_symbol_handler = function(err, result, ctx) item.name = result[i].name item.range = result[i].range or result[i].location.range if item.range == nil then - log("range missing in result", result[i]) + log('range missing in result', result[i]) end item.uri = uri 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.filename = fname + item.indent_level = 1 + + item.type = kind + item.node_text = item.name table.insert(locations, item) if result[i].children ~= nil then @@ -88,16 +90,23 @@ M.document_symbol_handler = function(err, result, ctx) child.name = c.name child.range = c.range or c.location.range local ckind = symbol_kind(child.kind) + + child.node_text = child.name + child.type = ckind child.selectionRange = c.selectionRange child.filename = fname child.uri = uri child.lnum = child.range.start.line + 1 child.detail = c.detail or '' + child.indent_level = 2 child.text = '  ' .. ckind .. '' .. child.name .. ' ' .. child.detail table.insert(locations, child) end end end + if ctx.no_show then + return locations + end local ft = vim.api.nvim_buf_get_option(bufnr, 'ft') 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 = ' ' }) 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 diff --git a/lua/navigator/treesitter.lua b/lua/navigator/treesitter.lua index 5179570..25e2202 100644 --- a/lua/navigator/treesitter.lua +++ b/lua/navigator/treesitter.lua @@ -110,7 +110,6 @@ end --- This function copy from treesitter/refactor/navigation.lua local function get_definitions(bufnr) local local_nodes = ts_locals.get_locals(bufnr) - -- Make sure the nodes are unique. local nodes_set = {} 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) end summary = summary or false + local ft = vim.api.nvim_buf_get_option(bufnr, 'filetype') 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 local path_sep = require('navigator.util').path_sep() @@ -311,7 +314,7 @@ local function get_all_nodes(bufnr, filter, summary) ['arrow_function'] = true, ['type'] = true, ['class'] = true, - ['var'] = true, + -- ['var'] = true, ['struct'] = true, ['method'] = true, } @@ -327,17 +330,31 @@ local function get_all_nodes(bufnr, filter, summary) -- Step 2 find correct completions local length = 10 local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos + local loaded_symbol = {} for _, def in ipairs(get_definitions(bufnr)) do local n = #parents for i = 1, n do local index = n + 1 - i 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 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 + log('is parent', i, index) break else + log('leave node', i, index) parents[index] = nil end end @@ -353,6 +370,10 @@ local function get_all_nodes(bufnr, filter, summary) trace(item.type, item.kind) goto continue end + + if item.type == 'associated' then + goto continue + end local tsdata = node.def if node.def == nil then @@ -369,20 +390,40 @@ local function get_all_nodes(bufnr, filter, summary) if is_func then -- hack for lua and maybe other language aswell 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) + 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) end end trace(item.node_text, item.kind, item.type) if scope ~= nil then - -- it is strange.. if not is_func and summary then + log(item.node_text, item.type) goto continue end item.node_scope = ts_utils.node_to_lsp_range(scope) end + if item.node_text == '_' then + goto continue + end if summary then if item.node_scope ~= nil then table.insert(all_nodes, item) @@ -394,7 +435,6 @@ local function get_all_nodes(bufnr, filter, summary) tsdata:type(), item.node_text, item.kind, - item.node_text, 'range', item.node_scope.start.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) local start_line_node, _, _ = tsdata:start() - if item.node_text == '_' then - goto continue - end - item.full_text = vim.trim(api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or '') + + local line_text = api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or '' + item.full_text = vim.trim(line_text) item.full_text = item.full_text:gsub('%s*[%[%(%{]*%s*$', '') item.uri = uri @@ -423,7 +462,23 @@ local function get_all_nodes(bufnr, filter, summary) indent = string.rep(' ', #parents - 1) .. ' ' end 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 all_nodes[#all_nodes].next_indent_level = #parents end @@ -432,7 +487,13 @@ local function get_all_nodes(bufnr, filter, summary) if #item.text > length then length = #item.text 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:: end end @@ -446,8 +507,12 @@ local function get_all_nodes(bufnr, filter, summary) end 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 - error('treesitter not loaded') + error('treesitter not loaded: ' .. ft) return end @@ -492,15 +557,33 @@ function M.buf_func(bufnr) return all_nodes, width end -function M.buf_ts() +function M.all_ts_nodes(bufnr) if ts_locals == nil then error('treesitter not loaded') return 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) + 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 listview = gui.new_list_view({ items = all_nodes, @@ -512,7 +595,7 @@ function M.buf_ts() width = width + 10, api = _NgConfigValues.icons.treesitter_defult, }) - return listview, all_nodes, width + return listview, all_nodes, width end M.get_all_nodes = get_all_nodes diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua index e456621..640f224 100644 --- a/lua/navigator/util.lua +++ b/lua/navigator/util.lua @@ -71,6 +71,9 @@ function M.io_read(filename, total) return content end +function M.rm_file(filename) + return os.remove(filename) +end function M.file_exists(name) local f = io.open(name, "r") @@ -417,9 +420,9 @@ function M.mk_handler(fn) end function M.partial(func, arg) - return (M.mk_handler(function(...) + return function(...) return func(arg, ...) - end)) + end end function M.empty(t) @@ -443,7 +446,7 @@ function M.encoding(client) end local oe = client.offset_encoding if oe == nil then - return 'utf-16' + return 'utf-8' end if type(oe) == 'table' then return oe[1] @@ -465,4 +468,14 @@ function M.info(msg) vim.notify('INF: ' .. msg, vim.lsp.log_levels.INFO) 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