fix treesitter for C++. Add variable defination(C++, Go)

neovim_0_5
ray-x 3 years ago
parent fc37a3f9a2
commit 4e748eccab

@ -2,45 +2,57 @@
- Easy code navigation through LSP and 🌲🏡Treesitter symbols; view diagnostic errors. - 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 - A plugin combine LSP and treesitter parser together. Not only providing a better highlight but also help you analyse symbol context effectively.
and scope.
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) ![js_closure_call_tree](https://user-images.githubusercontent.com/1681295/119120589-cee23700-ba6f-11eb-95c5-b9ac8d445c31.jpg)
Explains: Explanation:
- First line of floating windows shows there are 3 references for the symbol <span style="color:red"> *browser* </span> in closure.js - The first line of floating windows shows there are 3 references for the symbol <span style="color:red"> *browser* </span> in closure.js
- The first reference of browser is an assigement, an emoji of 📝 indicates the value changed in this line. In many - The first reference of browser is an assignment, an emoji 📝 indicates the value changed in this line. In many
cases, we search for reference to find out where the value changed. 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 - The second reference of `browser` is inside function `displayName` and `displayName` sit inside `makeFunc`, So you
will see ` displayName{} <- makeFunc{}` 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 - 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. 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 Struct type references in multiple Go ﳑ files
![go_reference](https://user-images.githubusercontent.com/1681295/119123823-54b3b180-ba73-11eb-8790-097601e10f6a.gif) ![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 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 project where class/function definition is too long to fit into the preview window. Also provides a birdview of where the
variable is referenced. variable is
- Referenced
- Modified
- Defined
- called
# Features: # Features:
- LSP easy setup. Support the most commonly used lsp clients setup. Dynamic lsp activation based on buffer type. This - 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 - 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 - 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 commenly used search reference, to less commenly used search for interface all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface
implementation. implementation.
- Async request with lsp.buf_request for reference search - 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 - FZY search with Lua-JIT
@ -144,7 +156,7 @@ require.'navigator'.setup({
-- -- the on_attach will be called at end of navigator on_attach -- -- the on_attach will be called at end of navigator on_attach
-- end, -- 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_root_path = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server",
sumneko_binary = vim.fn.expand("$HOME") .. sumneko_binary = vim.fn.expand("$HOME") ..
"/github/sumneko/lua-language-server/bin/macOS/lua-language-server", "/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 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 ## Usage
@ -185,11 +209,11 @@ Please refer to lua/navigator/lspclient/mapping.lua on key mappings. Should be a
- Use \<c-e\> or `:q!` to kill the floating window - Use \<c-e\> or `:q!` to kill the floating window
- <up/down> (or \<c-n\>, \<c-p\>) to move - <up/down> (or \<c-n\>, \<c-p\>) to move
- \<c-o\> or \<CR\> to open location or apply code actions. Note: \<CR\> might be binded in insert mode by other plugins - \<c-o\> or \<CR\> to open location or apply code actions. Note: \<CR\> might be bound in insert mode by other plugins
## Configuration ## 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 e.g
@ -204,7 +228,7 @@ colorscheme: [aurora](https://github.com/ray-x/aurora)
### Reference ### Reference
Pls check first part of README Pls check the first part of README
### Document Symbol ### Document Symbol
@ -280,7 +304,7 @@ Improved signature help with current parameter highlighted
# Todo # 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) - 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) - 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 - Configuration options

@ -13,7 +13,7 @@ _NgConfigValues = {
sumneko_binary = vim.fn.expand("$HOME") .. sumneko_binary = vim.fn.expand("$HOME") ..
"/github/sumneko/lua-language-server/bin/macOS/lua-language-server", "/github/sumneko/lua-language-server/bin/macOS/lua-language-server",
code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true}, 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 = { lsp = {
format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc) format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc)
tsserver = { tsserver = {

@ -13,7 +13,7 @@ ts_nodes = {}
ts_nodes_time = {} ts_nodes_time = {}
local ts_enabled, _ = pcall(require, "nvim-treesitter.locals") 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 -- extract symbol from range
local function get_symbol(text, range) local function get_symbol(text, range)
@ -24,8 +24,15 @@ local function get_symbol(text, range)
end end
local function check_lhs(text, symbol) 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 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 return s < eq
end end
@ -136,7 +143,7 @@ function M.call_async(method, params, handler)
end end
local function ts_functions(uri) 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") lerr("ts not enabled")
return nil return nil
end end
@ -173,6 +180,32 @@ local function ts_functions(uri)
return funcs return funcs
end 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) local function find_ts_func_by_range(funcs, range)
if funcs == nil or range == nil then if funcs == nil or range == nil then
return nil return nil
@ -209,11 +242,30 @@ function M.locations_to_items(locations)
return i.uri < j.uri return i.uri < j.uri
end end
end) end)
local uri_def = {}
for i, loc in ipairs(locations) do for i, loc in ipairs(locations) do
local item = lsp.util.locations_to_items({loc})[1] local item = lsp.util.locations_to_items({loc})[1]
item.uri = locations[i].uri item.uri = locations[i].uri
local funcs = ts_functions(item.uri) local funcs = ts_functions(item.uri)
item.range = locations[i].range 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)) item.filename = assert(vim.uri_to_fname(item.uri))
local filename = item.filename:gsub(cwd .. "/", "./", 1) local filename = item.filename:gsub(cwd .. "/", "./", 1)
item.display_filename = filename or item.filename 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.symbol_name = get_symbol(item.text, item.range)
item.lhs = check_lhs(item.text, item.symbol_name) item.lhs = check_lhs(item.text, item.symbol_name)
end end
trace(uri_def)
return items, width + 24 -- TODO handle long line? return items, width + 24 -- TODO handle long line?
end end

@ -95,11 +95,16 @@ function M.prepare_for_render(items, opts)
item = clone(items[i]) item = clone(items[i])
item.text = require'navigator.util'.trim_and_pad(item.text) item.text = require'navigator.util'.trim_and_pad(item.text)
item.text = string.format("%4i: %s", item.lnum, item.text) item.text = string.format("%4i: %s", item.lnum, item.text)
local call_by = "" local ts_report = ""
if item.lhs then if item.lhs then
call_by = '📝 ' ts_report = '📝 '
end end
if item.definition then
ts_report = ts_report .. '🦕 '
end
trace(ts_report)
item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '') item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '')
if item.call_by ~= nil and #item.call_by > 0 then if item.call_by ~= nil and #item.call_by > 0 then
trace("call_by:", #item.call_by) trace("call_by:", #item.call_by)
@ -109,29 +114,29 @@ function M.prepare_for_render(items, opts)
local endwise = '{}' local endwise = '{}'
if value.type == 'method' or value.type == 'function' then if value.type == 'method' or value.type == 'function' then
endwise = '()' endwise = '()'
call_by = '' ts_report = ts_report .. ''
end end
if #call_by > 8 then if #ts_report > 8 then
call_by = call_by .. '' ts_report = ts_report .. ''
end end
call_by = call_by .. value.kind .. txt .. endwise ts_report = ts_report .. value.kind .. txt .. endwise
trace(item) trace(item)
end end
end end
end end
if #call_by > 1 then if #ts_report > 1 then
space = get_pads(win_width, item.text, call_by) space = get_pads(win_width, item.text, ts_report)
if #space + #item.text + #call_by >= win_width then if #space + #item.text + #ts_report >= win_width then
if #item.text + #call_by > win_width then if #item.text + #ts_report > win_width then
log("exceeding", #item.text, #call_by, win_width) log("exceeding", #item.text, #ts_report, win_width)
space = ' ' space = ' '
else else
local remain = win_width - #item.text - #call_by local remain = win_width - #item.text - #ts_report
log("remain", remain) log("remain", remain)
space = string.rep(' ', remain) space = string.rep(' ', remain)
end end
end end
item.text = item.text .. space .. call_by item.text = item.text .. space .. ts_report
end end
local tail = display_items[#display_items].text local tail = display_items[#display_items].text
if tail ~= item.text then -- deduplicate if tail ~= item.text then -- deduplicate

@ -57,24 +57,31 @@ end
-- use lsp range to find def -- use lsp range to find def
function M.find_definition(range, bufnr) 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 return
end end
bufnr = bufnr or api.nvim_get_current_buf() bufnr = bufnr or api.nvim_get_current_buf()
local cursor = {range.start.line, range.start.character} -- +1 or not? local parser = parsers.get_parser(bufnr)
local symbolpos = {range.start.line, range.start.character} -- +1 or not?
local node_at_point = ts_utils.get_node_at_cursor() 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 if not node_at_point then
lerr("no node at cursor") lerr("no node at cursor")
return return {}
end end
local definition = locals.find_definition(node_at_point, bufnr) local definition = locals.find_definition(node_at_point, bufnr)
log("def found:", definition, definition:range())
log(definition) if definition then
return local r, c = definition:range()
return {start = {line = r, character = c}}
end
return {}
end end
--- Get definitions of bufnr (unique and sorted by order of appearance). --- 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 if parent:type() == 'function_name_field' then
return parent:parent():parent(), true return parent:parent():parent(), true
end 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 return parent, true
end end
@ -151,8 +170,9 @@ local function get_scope(type, source)
trace(source, source:type()) trace(source, source:type())
return source, false return source, false
end end
else -- M.fun1 = function() end else
-- lets work up and see next node -- M.fun1 = function() end
-- lets work up and see next node, lua
local n = source local n = source
for i = 1, 4, 1 do for i = 1, 4, 1 do
if n == nil or n:parent() == nil then if n == nil or n:parent() == nil then

Loading…
Cancel
Save