diff --git a/README.md b/README.md index 699c87a..97a1c67 100644 --- a/README.md +++ b/README.md @@ -2,45 +2,57 @@ - Easy code navigation through LSP and 🌲🏑Treesitter symbols; view diagnostic errors. -- Combine LSP and treesitter parser together. Not only providing better highlight but also help you analysis symbol context -and scope. +- A plugin combine LSP and treesitter parser together. Not only providing a better highlight but also help you analyse symbol context effectively. -Here is an example +Here are examples -Following screen shot shows javascript call tree 🌲 of variable `browser` insides a closure. This feature is similar to incoming&outgoing calls from LSP. It is designed for the symbol analysis. +#### Example: Javascripts closure + +The following screenshot shows javascript call tree 🌲 of variable `browser` insides a closure. This feature is similar to incoming&outgoing calls from LSP. It is designed for the symbol analysis. ![js_closure_call_tree](https://user-images.githubusercontent.com/1681295/119120589-cee23700-ba6f-11eb-95c5-b9ac8d445c31.jpg) -Explains: -- First line of floating windows shows there are 3 references for the symbol *browser* in closure.js -- The first reference of browser is an assigement, an emoji of πŸ“ indicates the value changed in this line. In many -cases, we search for reference to find out where the value changed. +Explanation: +- The first line of floating windows shows there are 3 references for the symbol *browser* in closure.js +- The first reference of browser is an assignment, an emoji πŸ“ indicates the value changed in this line. In many +cases, we search for references to find out where the value changed. - The second reference of `browser` is inside function `displayName` and `displayName` sit inside `makeFunc`, So you will see ` displayName{} <- makeFunc{}` - The third similar to the second, as var browser is on the right side of '=', the value not changed in this line and emoji is not shown. +#### Example: C++ defination + +Another example for C++ +![cpp_ref](https://user-images.githubusercontent.com/1681295/119215215-8bd7a080-bb0f-11eb-82fc-8cdf1955e6e7.jpg) +You may find that a πŸ¦• dinosaur(d) on the line of `Rectangle rect;` which means there is a defination (d for def) of rect in this line + +#### Golang struct type Struct type references in multiple Go ﳑ files ![go_reference](https://user-images.githubusercontent.com/1681295/119123823-54b3b180-ba73-11eb-8790-097601e10f6a.gif) This feature can provide you info in which function/class/method the variable was referenced. It is handy for large -project where class/function defination is too long to fit into preview window. Also provides a birdview of where the -variable is referenced. +project where class/function definition is too long to fit into the preview window. Also provides a birdview of where the +variable is +- Referenced +- Modified +- Defined +- called # Features: - LSP easy setup. Support the most commonly used lsp clients setup. Dynamic lsp activation based on buffer type. This -also enable you handle workspace combine mix types of codes (e.g. Go + javascript + yml) +also enables you to handle workspace combine mixed types of codes (e.g. Go + javascript + yml) - Out of box experience. 10 lines of minimum vimrc can turn your neovim into a full-featured LSP & Treesitter powered IDE -- Unorthodox UI with floating windows, navigator provides a visual way to manage and navigate through symbols, diagnostic errors, reference etc. Is covers -all features(handler) provided by LSP from commenly used search reference, to less commenly used search for interface +- Unorthodox UI with floating windows, navigator provides a visual way to manage and navigate through symbols, diagnostic errors, reference etc. It covers +all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface implementation. - Async request with lsp.buf_request for reference search -- Treesitter symbol search. It is handy for large filas (Some of LSP e.g. sumneko_lua, there is a 100kb file size limition?) +- Treesitter symbol search. It is handy for large files (Some of LSP e.g. sumneko_lua, there is a 100kb file size limition?) - FZY search with Lua-JIT @@ -144,7 +156,7 @@ require.'navigator'.setup({ -- -- the on_attach will be called at end of navigator on_attach -- end, - treesitter_call_tree = true, -- treesitter variable context + treesitter_analysis = true, -- treesitter variable context sumneko_root_path = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server", sumneko_binary = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server/bin/macOS/lua-language-server", @@ -164,6 +176,18 @@ require.'navigator'.setup({ }) +``` + +The plugin can work with multiple LSP, e.g sqls+gopls+efm. But there are cases you may need to disable some of the +servers. (Prevent loading multiple LSP for same source code.) e.g. I saw strange behaviours when I use pyls+pyright+pyls_ms +together. If you have multiple similar LSP installed and have trouble with the plugin, please enable only one at a time. +To disable a LSP server, set `filetypes` to {} e.g. +```lua + +require.'navigator'.setup({ + pyls={filetype={}} +}) + ``` @@ -177,7 +201,7 @@ require.'navigator'.setup({ The plugin can be loaded lazily (packer `opt = true` ), And it will check if optional plugins existance and load those plugins only if they existed. -The termianl will need to be able to output nerdfont and emoji correctly. I am using Kitty with nerdfont (Victor Mono). +The terminal will need to be able to output nerdfont and emoji correctly. I am using Kitty with nerdfont (Victor Mono). ## Usage @@ -185,11 +209,11 @@ Please refer to lua/navigator/lspclient/mapping.lua on key mappings. Should be a - Use \ or `:q!` to kill the floating window - (or \, \) to move -- \ or \ to open location or apply code actions. Note: \ might be binded in insert mode by other plugins +- \ or \ to open location or apply code actions. Note: \ might be bound in insert mode by other plugins ## Configuration -In `navigator.lua` there is a default configration. You can override the values by pass you own values +In `navigator.lua` there is a default configuration. You can override the values by passing your own values e.g @@ -204,7 +228,7 @@ colorscheme: [aurora](https://github.com/ray-x/aurora) ### Reference -Pls check first part of README +Pls check the first part of README ### Document Symbol @@ -280,7 +304,7 @@ Improved signature help with current parameter highlighted # Todo -- Early phase, bugs expected, PR and suggestions are welcome +- The project is in the early phase, bugs expected, PRs and suggestions are welcome - Async (some of the requests is slow on large codebases and might be good to use co-rountine) - More clients. I use go, python, js/ts, java, c/cpp, lua most of the time. Did not test other languages (e.g dart, swift etc) - Configuration options diff --git a/lua/navigator.lua b/lua/navigator.lua index c0adf2e..85c86ea 100644 --- a/lua/navigator.lua +++ b/lua/navigator.lua @@ -13,7 +13,7 @@ _NgConfigValues = { sumneko_binary = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server/bin/macOS/lua-language-server", code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true}, - treesitter_call_tree = true, -- treesitter variable context + treesitter_analysis = true, -- treesitter variable context lsp = { format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc) tsserver = { diff --git a/lua/navigator/lspwrapper.lua b/lua/navigator/lspwrapper.lua index 63daf89..feb8b41 100644 --- a/lua/navigator/lspwrapper.lua +++ b/lua/navigator/lspwrapper.lua @@ -13,7 +13,7 @@ ts_nodes = {} ts_nodes_time = {} local ts_enabled, _ = pcall(require, "nvim-treesitter.locals") -local calltree_enabled = require"navigator".config_values().treesitter_call_tree +local TS_analysis_enabled = require"navigator".config_values().treesitter_analysis -- extract symbol from range local function get_symbol(text, range) @@ -24,8 +24,15 @@ local function get_symbol(text, range) end local function check_lhs(text, symbol) - local s = string.find(text, symbol) + local find = require'guihua.util'.word_find + local s = find(text, symbol) local eq = string.find(text, '=') or 0 + if not s or not eq then + return false + end + if s < eq then + log(symbol, "modified") + end return s < eq end @@ -136,7 +143,7 @@ function M.call_async(method, params, handler) end local function ts_functions(uri) - if not ts_enabled or not calltree_enabled then + if not ts_enabled or not TS_analysis_enabled then lerr("ts not enabled") return nil end @@ -173,6 +180,32 @@ local function ts_functions(uri) return funcs end +local function ts_defination(uri, range) + if not ts_enabled or not TS_analysis_enabled then + lerr("ts not enabled") + return nil + end + local ts_def = require"navigator.treesitter".find_definition + local bufnr = vim.uri_to_bufnr(uri) + local x = os.clock() + trace(ts_nodes) + local unload = false + if not api.nvim_buf_is_loaded(bufnr) then + log("! load buf !", uri, bufnr) + vim.fn.bufload(bufnr) + unload = true + end + + local def_range = ts_def(range, bufnr) + if unload then + local cmd = string.format("bd %d", bufnr) + log(cmd) + -- vim.cmd(cmd) -- todo: not sure if it is needed + end + log(string.format(" ts def elapsed time: %.4f\n", os.clock() - x), def_range) + return def_range +end + local function find_ts_func_by_range(funcs, range) if funcs == nil or range == nil then return nil @@ -209,11 +242,30 @@ function M.locations_to_items(locations) return i.uri < j.uri end end) + local uri_def = {} for i, loc in ipairs(locations) do local item = lsp.util.locations_to_items({loc})[1] item.uri = locations[i].uri local funcs = ts_functions(item.uri) + item.range = locations[i].range + + if TS_analysis_enabled then + if uri_def[item.uri] == nil then + -- find def in file + local def = ts_defination(item.uri, item.range) + uri_def[item.uri] = def or {} + end + log(uri_def[item.uri], item.range) + local def = uri_def[item.uri] + if def.start and item.range then + if def.start.line == item.range.start.line then + log("ts def found") + item.definition = true + end + end + end + item.filename = assert(vim.uri_to_fname(item.uri)) local filename = item.filename:gsub(cwd .. "/", "./", 1) item.display_filename = filename or item.filename @@ -224,6 +276,7 @@ function M.locations_to_items(locations) item.symbol_name = get_symbol(item.text, item.range) item.lhs = check_lhs(item.text, item.symbol_name) end + trace(uri_def) return items, width + 24 -- TODO handle long line? end diff --git a/lua/navigator/render.lua b/lua/navigator/render.lua index 0336497..9dca803 100644 --- a/lua/navigator/render.lua +++ b/lua/navigator/render.lua @@ -95,11 +95,16 @@ function M.prepare_for_render(items, opts) item = clone(items[i]) item.text = require'navigator.util'.trim_and_pad(item.text) item.text = string.format("%4i: %s", item.lnum, item.text) - local call_by = "" + local ts_report = "" if item.lhs then - call_by = 'πŸ“ ' + ts_report = 'πŸ“ ' end + if item.definition then + ts_report = ts_report .. 'πŸ¦• ' + end + trace(ts_report) + item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '') if item.call_by ~= nil and #item.call_by > 0 then trace("call_by:", #item.call_by) @@ -109,29 +114,29 @@ function M.prepare_for_render(items, opts) local endwise = '{}' if value.type == 'method' or value.type == 'function' then endwise = '()' - call_by = 'ο£Ά ' + ts_report = ts_report .. 'ο£Ά ' end - if #call_by > 8 then - call_by = call_by .. ' ο•Œ ' + if #ts_report > 8 then + ts_report = ts_report .. ' ο•Œ ' end - call_by = call_by .. value.kind .. txt .. endwise + ts_report = ts_report .. value.kind .. txt .. endwise trace(item) end end end - if #call_by > 1 then - space = get_pads(win_width, item.text, call_by) - if #space + #item.text + #call_by >= win_width then - if #item.text + #call_by > win_width then - log("exceeding", #item.text, #call_by, win_width) + if #ts_report > 1 then + space = get_pads(win_width, item.text, ts_report) + if #space + #item.text + #ts_report >= win_width then + if #item.text + #ts_report > win_width then + log("exceeding", #item.text, #ts_report, win_width) space = ' ' else - local remain = win_width - #item.text - #call_by + local remain = win_width - #item.text - #ts_report log("remain", remain) space = string.rep(' ', remain) end end - item.text = item.text .. space .. call_by + item.text = item.text .. space .. ts_report end local tail = display_items[#display_items].text if tail ~= item.text then -- deduplicate diff --git a/lua/navigator/treesitter.lua b/lua/navigator/treesitter.lua index 6a6ba2e..ab04cb4 100644 --- a/lua/navigator/treesitter.lua +++ b/lua/navigator/treesitter.lua @@ -57,24 +57,31 @@ end -- use lsp range to find def function M.find_definition(range, bufnr) - if not range then - + if not range or not range.start then + lerr("find_def incorrect range", range) return end bufnr = bufnr or api.nvim_get_current_buf() - local cursor = {range.start.line, range.start.character} -- +1 or not? - - local node_at_point = ts_utils.get_node_at_cursor() - + local parser = parsers.get_parser(bufnr) + local symbolpos = {range.start.line, range.start.character} -- +1 or not? + local root = ts_utils.get_root_for_position(range.start.line, range.start.character, parser) + if not root then + return {} + end + local node_at_point = root:named_descendant_for_range(symbolpos[1], symbolpos[2], symbolpos[1], + symbolpos[2]) if not node_at_point then lerr("no node at cursor") - return + return {} end local definition = locals.find_definition(node_at_point, bufnr) - - log(definition) - return + log("def found:", definition, definition:range()) + if definition then + local r, c = definition:range() + return {start = {line = r, character = c}} + end + return {} end --- Get definitions of bufnr (unique and sorted by order of appearance). @@ -137,6 +144,18 @@ local function get_scope(type, source) if parent:type() == 'function_name_field' then return parent:parent():parent(), true end + + -- for C++ + local n = source + for i = 1, 4, 1 do + if n == nil or n:parent() == nil then + break + end + n = n:parent() + if n:type() == 'function_definition' then + return n, true + end + end return parent, true end @@ -151,8 +170,9 @@ local function get_scope(type, source) trace(source, source:type()) return source, false end - else -- M.fun1 = function() end - -- lets work up and see next node + else + -- M.fun1 = function() end + -- lets work up and see next node, lua local n = source for i = 1, 4, 1 do if n == nil or n:parent() == nil then