Lv thread (#60)

* update tests, update total display

* not run ts def multiple time

* Thread enable for display and backend

* remove ::continue::

* README updates

* skip diagnostic in edit mode

* error marker uri nil handling

* disable debug

* debounce text change to 1s

* severity sort

* diagnostic skip loading files
neovim_0.6
rayx 3 years ago committed by GitHub
parent 507ad0b146
commit 91e22f5e71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -54,7 +54,7 @@ included for LSP clients.
all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface
implementation.
- Luv async job
- Luv async thread and tasks
- Edit your code in preview window
@ -62,7 +62,7 @@ implementation.
- Treesitter symbol search. It is handy for large files (Some of LSP e.g. sumneko_lua, there is a 100kb file size limitation?)
- FZY search with Lua-JIT
- FZY search with either native C (if gcc installed) or Lua-JIT
- LSP multiple symbol highlight/marker and hop between document references
@ -265,6 +265,37 @@ require'navigator'.setup({
```
### LSP clients
Built clients:
```lua
local servers = {
"angularls", "gopls", "tsserver", "flow", "bashls", "dockerls", "julials", "pylsp", "pyright",
"jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls",
"yamlls", "clangd", "ccls", "sqls", "denols", "graphql", "dartls", "dotls",
"kotlin_language_server", "nimls", "intelephense", "vuels", "phpactor", "omnisharp",
"r_language_server", "rust_analyzer", "terraformls"
}
```
Navigator will try to load avalible lsp server/client based on filetype. The clients has none default on_attach.
incremental sync and debounce is enabled by navigator. And the lsp
snippet will be enabled. So you could use COQ and nvim-cmp snippet expand.
Other than above setup, additional none default setup are used for following lsp:
* gopls
* clangd
* rust_analyzer
* sqls
* sumneko_lua
* pyright
* ccls
Please check [client setup](https://github.com/ray-x/navigator.lua/blob/26012cf9c172aa788a2e53018d94b32c5c75af75/lua/navigator/lspclient/clients.lua#L98-L234)
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.

@ -27,7 +27,8 @@ _NgConfigValues = {
code_lens = false,
-- only want to enable one lsp server
disply_diagnostic_qf = true, -- always show quickfix if there are diagnostic errors
diagnostic_load_files = false, -- lsp diagnostic errors list may contains uri that not opened yet set to true
-- to load those files
diagnostic_virtual_text = true, -- show virtual for diagnostic message
diagnostic_update_in_insert = false, -- update diagnostic message in insert mode
diagnostic_scrollbar_sign = {'', ''}, -- set to nil to disable, set to {'╍', 'ﮆ'} to enable diagnostic status in scroll bar area

@ -69,7 +69,7 @@ function M.setup()
local on_codelens = vim.lsp.handlers["textDocument/codeLens"]
vim.lsp.handlers["textDocument/codeLens"] = mk_handler(
function(err, result, ctx, cfg)
log(err, result, ctx.client_id, ctx.bufnr, cfg)
trace(err, result, ctx.client_id, ctx.bufnr, cfg)
if nvim_0_6() then
on_codelens(err, result, ctx, cfg)
codelens_hdlr(err, result, ctx, cfg)

@ -18,7 +18,8 @@ local function error_marker(result, client_id)
return
end
local first_line = vim.fn.line('w0')
local ft = vim.fn.expand('%:h:t') -- get the current file extension
-- local rootfolder = vim.fn.expand('%:h:t') -- get the current file root folder
trace(result)
local bufnr = vim.uri_to_bufnr(result.uri)
if bufnr ~= vim.api.nvim_get_current_buf() then
@ -106,16 +107,20 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config)
log(err, config)
return
end
if vim.fn.mode() ~= 'n' and config.update_in_insert == false then
log("skip in insert mode")
return
end
local cwd = vim.loop.cwd()
local ft = vim.bo.filetype
if diagnostic_list[ft] == nil then
diagnostic_list[vim.bo.filetype] = {}
end
-- vim.lsp.diagnostic.clear(vim.fn.bufnr(), client.id, nil, nil)
if util.nvim_0_6() then
vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config)
else
log("old version of lsp nvim 050")
vim.lsp.diagnostic.on_publish_diagnostics(err, _, result, ctx.client_id, _, config)
end
local uri = result.uri
@ -123,8 +128,11 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config)
log("diag", err, result)
return
end
-- log("diag: ", result, client_id)
if vim.fn.mode() ~= 'n' and config.update_in_insert == false then
log("skip in insert mode")
return
end
trace("diag: ", vim.fn.mode(), result, ctx, config)
if result and result.diagnostics then
local item_list = {}
for _, v in ipairs(result.diagnostics) do
@ -146,21 +154,30 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config)
end
local bufnr1 = vim.uri_to_bufnr(uri)
if not vim.api.nvim_buf_is_loaded(bufnr1) then
vim.fn.bufload(bufnr1)
end
local pos = v.range.start
local row = pos.line
local line = (vim.api.nvim_buf_get_lines(bufnr1, row, row + 1, false) or {""})[1]
if line ~= nil then
item.text = head .. line .. _NgConfigValues.icons.diagnostic_head_description .. v.message
table.insert(item_list, item)
else
error("diagnostic result empty line", v, row, bufnr1)
if _NgConfigValues.diagnostic_load_files then
vim.fn.bufload(bufnr1) -- this may slow down the neovim
local pos = v.range.start
local row = pos.line
local line = (vim.api.nvim_buf_get_lines(bufnr1, row, row + 1, false) or {""})[1]
if line ~= nil then
item.text = head .. line .. _NgConfigValues.icons.diagnostic_head_description
.. v.message
table.insert(item_list, item)
else
error("diagnostic result empty line", v, row, bufnr1)
end
else
item.text = head .. _NgConfigValues.icons.diagnostic_head_description .. v.message
table.insert(item_list, item)
end
end
end
-- local old_items = vim.fn.getqflist()
diagnostic_list[ft][uri] = item_list
result.uri = uri
if not result.uri then
result.uri = uri
end
error_marker(result, ctx.client_id)
else
vim.api.nvim_buf_clear_namespace(0, _NG_VT_NS, 0, -1)
@ -173,18 +190,21 @@ local M = {}
local diagnostic_cfg = {
-- Enable underline, use default values
underline = true,
-- Enable virtual text, override spacing to 0
virtual_text = {spacing = 0, prefix = _NgConfigValues.icons.diagnostic_virtual_text},
-- Enable virtual text, override spacing to 3 (prevent overlap)
virtual_text = {spacing = 3, prefix = _NgConfigValues.icons.diagnostic_virtual_text},
-- Use a function to dynamically turn signs off
-- and on, using buffer local variables
signs = true,
-- Disable a feature
update_in_insert = _NgConfigValues.lsp.diagnostic_update_in_insert or false
update_in_insert = _NgConfigValues.lsp.diagnostic_update_in_insert or false,
severity_sort = function(a, b)
return a.severity < b.severity
end
}
if _NgConfigValues.lsp.diagnostic_virtual_text == false then
diagnostic_cfg.virtual_text = false
end
-- vim.lsp.handlers["textDocument/publishDiagnostics"]
M.diagnostic_handler = vim.lsp.with(diag_hdlr, diagnostic_cfg)
@ -252,7 +272,7 @@ function M.update_err_marker()
-- nothing to update
return
end
local bufnr = vim.fn.bufnr()
local bufnr = vim.api.nvim_get_current_buf()
local diag_cnt = vim.lsp.diagnostic.get_count(bufnr, [[Error]])
+ vim.lsp.diagnostic.get_count(bufnr, [[Warning]])

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

@ -159,6 +159,17 @@ end)
function M.get_fold_indic(lnum)
local buf = api.nvim_get_current_buf()
local shown = false
for i = 1, vim.fn.tabpagenr('$') do
for key, value in pairs(vim.fn.tabpagebuflist(i)) do
if value == buf then
shown = true
end
end
end
if not shown then
return "0"
end
local levels = folds_levels(buf) or {}
-- log(lnum, levels[lnum]) -- TODO: comment it out in master

@ -140,80 +140,83 @@ function M.new_list_view(opts)
local border = _NgConfigValues.border or 'shadow'
if data and not vim.tbl_isempty(data) then
-- replace
-- TODO: 10 vimrc opt
if #data > 10 and opts.prompt == nil then
loc = "top_center"
prompt = true
end
if not data or vim.tbl_isempty(data) then
return
end
-- replace
-- TODO: 10 vimrc opt
if #data > 10 and opts.prompt == nil then
loc = "top_center"
prompt = true
end
local lheight = math.min(#data, math.floor(wheight * _NgConfigValues.height))
local lheight = math.min(#data, math.floor(wheight * _NgConfigValues.height))
local r, _ = top_center(lheight, width)
local r, _ = top_center(lheight, width)
local offset_y = r + lheight
-- style shadow took 1 lines
if border ~= 'none' then
if border == 'shadow' then
offset_y = offset_y + 1
else
offset_y = offset_y + 1 -- single?
end
end
-- if border is not set, this should be r+lheigh
if prompt then
offset_y = offset_y + 1 -- need to check this out
end
local idx = require"guihua.util".fzy_idx
local transparency = _NgConfigValues.transparency
if transparency == 100 then
transparency = nil
local offset_y = r + lheight
-- style shadow took 1 lines
if border ~= 'none' then
if border == 'shadow' then
offset_y = offset_y + 1
else
offset_y = offset_y + 1 -- single?
end
return ListView:new({
loc = loc,
prompt = prompt,
relative = opts.relative,
style = opts.style,
api = opts.api,
rect = {height = lheight, width = width, pos_x = 0, pos_y = 0},
-- preview_height = pheight,
ft = opts.ft or 'guihua',
-- data = display_data,
data = data,
border = border,
on_confirm = opts.on_confirm or function(item, split_opts)
log(split_opts)
split_opts = split_opts or {}
if item.filename ~= nil then
log("openfile ", item.filename, item.lnum, item.col)
util.open_file_at(item.filename, item.lnum, item.col, split_opts.split)
end
end,
transparency = transparency,
on_move = opts.on_move or function(item)
trace("on move", pos, item)
trace("on move", pos, item.text or item, item.uri, item.filename)
-- todo fix
if item.uri == nil then
item.uri = "file:///" .. item.filename
end
return M.preview_uri({
uri = item.uri,
width = width,
height = lheight, -- this is to cal offset
preview_height = pheight,
lnum = item.lnum,
col = item.col,
range = item.range,
offset_x = 0,
offset_y = offset_y,
border = border,
enable_edit = opts.enable_preview_edit or false
})
end
})
end
-- if border is not set, this should be r+lheigh
if prompt then
offset_y = offset_y + 1 -- need to check this out
end
local idx = require"guihua.util".fzy_idx
local transparency = _NgConfigValues.transparency
if transparency == 100 then
transparency = nil
end
return ListView:new({
loc = loc,
prompt = prompt,
relative = opts.relative,
style = opts.style,
api = opts.api,
total = opts.total,
rect = {height = lheight, width = width, pos_x = 0, pos_y = 0},
-- preview_height = pheight,
ft = opts.ft or 'guihua',
-- data = display_data,
data = data,
border = border,
on_confirm = opts.on_confirm or function(item, split_opts)
log(split_opts)
split_opts = split_opts or {}
if item.filename ~= nil then
log("openfile ", item.filename, item.lnum, item.col)
util.open_file_at(item.filename, item.lnum, item.col, split_opts.split)
end
end,
transparency = transparency,
on_move = opts.on_move or function(item)
trace("on move", item)
trace("on move", item.text or item, item.uri, item.filename)
-- todo fix
if item.uri == nil then
item.uri = "file:///" .. item.filename
end
return M.preview_uri({
uri = item.uri,
width = width,
height = lheight, -- this is to cal offset
preview_height = pheight,
lnum = item.lnum,
col = item.col,
range = item.range,
offset_x = 0,
offset_y = offset_y,
border = border,
enable_edit = opts.enable_preview_edit or false
})
end
})
end
return M

@ -94,7 +94,7 @@ local setups = {
-- "-rpc.trace",
},
flags = {allow_incremental_sync = true, debounce_text_changes = 500},
flags = {allow_incremental_sync = true, debounce_text_changes = 1000},
settings = {
gopls = {
-- more settings: https://github.com/golang/tools/blob/master/gopls/doc/settings.md
@ -112,7 +112,7 @@ local setups = {
staticcheck = true,
matcher = "fuzzy",
diagnosticsDelay = "500ms",
experimentalWatchedFileDelay = "100ms",
experimentalWatchedFileDelay = "1000ms",
symbolMatcher = "fuzzy",
gofumpt = false, -- true, -- turn on for new repos, gofmpt is good but also create code turmoils
buildFlags = {"-tags", "integration"}
@ -258,7 +258,7 @@ end
local default_cfg = {
on_attach = on_attach,
flags = {allow_incremental_sync = true, debounce_text_changes = 500}
flags = {allow_incremental_sync = true, debounce_text_changes = 1000}
}
-- check and load based on file type

@ -123,18 +123,17 @@ function M.check_capabilities(feature, client_id)
for _, client in pairs(clients) do
supported_client = client.resolved_capabilities[feature]
if supported_client then
goto continue
break
end
end
::continue::
if supported_client then
return true
else
if #clients == 0 then
print("LSP: no client attached")
log("LSP: no client attached")
else
log("LSP: server does not support " .. feature)
trace("LSP: server does not support " .. feature)
end
return false
end
@ -164,6 +163,7 @@ function M.call_async(method, params, handler)
end
local function ts_functions(uri)
local unload_bufnr
local ts_enabled, _ = pcall(require, "nvim-treesitter.locals")
if not ts_enabled or not TS_analysis_enabled then
lerr("ts not enabled")
@ -175,7 +175,7 @@ local function ts_functions(uri)
trace(ts_nodes)
local tsnodes = ts_nodes:get(uri)
if tsnodes ~= nil then
log("get data from cache")
trace("get data from cache")
local t = ts_nodes_time:get(uri) or 0
local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname)
@ -191,28 +191,40 @@ local function ts_functions(uri)
if not api.nvim_buf_is_loaded(bufnr) then
trace("! load buf !", uri, bufnr)
vim.fn.bufload(bufnr)
-- vim.api.nvim_buf_detach(bufnr) -- if user opens the buffer later, it prevents user attach event
unload = true
end
local funcs = ts_func(bufnr)
if unload then
local cmd = string.format("bd %d", bufnr)
trace(cmd)
-- vim.cmd(cmd) -- todo: not sure if it is needed
unload_bufnr = bufnr
end
ts_nodes:set(uri, funcs)
ts_nodes_time:set(uri, os.time())
trace(funcs, ts_nodes:get(uri))
trace(string.format("elapsed time: %.4f\n", os.clock() - x)) -- how long it tooks
return funcs
return funcs, unload_bufnr
end
local function ts_defination(uri, range)
local function ts_definition(uri, range)
local unload_bufnr
local ts_enabled, _ = pcall(require, "nvim-treesitter.locals")
if not ts_enabled or not TS_analysis_enabled then
lerr("ts not enabled")
return nil
end
local key = string.format('%s_%d_%d_%d', uri, range.start.line, range.start.character,
range['end'].line)
local tsnode = ts_nodes:get(key)
local ftime = ts_nodes_time:get(key)
local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname)
if tsnodes and modified <= ftime then
log('ts def from cache')
return tsnode
end
local ts_def = require"navigator.treesitter".find_definition
local bufnr = vim.uri_to_bufnr(uri)
local x = os.clock()
@ -224,14 +236,14 @@ local function ts_defination(uri, range)
unload = true
end
local def_range = ts_def(range, bufnr)
local def_range = ts_def(range, bufnr) or {}
if unload then
local cmd = string.format("bd %d", bufnr)
log(cmd)
-- vim.cmd(cmd) -- todo: not sure if it is needed
unload_bufnr = bufnr
end
trace(string.format(" ts def elapsed time: %.4f\n", os.clock() - x), def_range) -- how long it takes
return def_range
ts_nodes:set(key, def_range)
ts_nodes_time:set(key, x)
return def_range, unload_bufnr
end
local function find_ts_func_by_range(funcs, range)
@ -251,15 +263,7 @@ local function find_ts_func_by_range(funcs, range)
return result
end
function M.locations_to_items(locations)
if not locations or vim.tbl_isempty(locations) then
print("list not avalible")
return
end
local width = 4
local items = {} -- lsp.util.locations_to_items(locations)
-- items and locations may not matching
local function order_locations(locations)
table.sort(locations, function(i, j)
if i.uri == j.uri then
if i.range and i.range.start then
@ -270,9 +274,63 @@ function M.locations_to_items(locations)
return i.uri < j.uri
end
end)
return locations
end
local function slice_locations(locations, max_items)
local cut = -1
if #locations > max_items then
local uri = locations[max_items]
for i = max_items + 1, #locations do
if uri ~= locations[i] and not brk then
cut = i
break
end
end
end
local first_part, second_part = locations, {}
if cut > 1 and cut < #locations then
first_part = vim.list_slice(locations, 1, cut)
second_part = vim.list_slice(locations, cut + 1, #locations)
end
return first_part, second_part
end
local function test_locations()
local locations = {
{uri = '1', range = {start = {line = 1}}}, {uri = '2', range = {start = {line = 2}}},
{uri = '2', range = {start = {line = 3}}}, {uri = '1', range = {start = {line = 3}}},
{uri = '1', range = {start = {line = 4}}}, {uri = '3', range = {start = {line = 4}}},
{uri = '3', range = {start = {line = 4}}}
}
local second_part
order_locations(locations)
local locations, second_part = slice_locations(locations, 3)
log(locations, second_part)
end
function M.locations_to_items(locations, max_items)
max_items = max_items or 100000 --
if not locations or vim.tbl_isempty(locations) then
print("list not avalible")
return
end
local width = 4
local items = {} -- lsp.util.locations_to_items(locations)
-- items and locations may not matching
local uri_def = {}
order_locations(locations)
local second_part
locations, second_part = slice_locations(locations, max_items)
trace(locations)
local cut = -1
local unload_bufnrs = {}
for i, loc in ipairs(locations) do
local funcs = nil
local item = lsp.util.locations_to_items({loc})[1]
@ -286,12 +344,16 @@ function M.locations_to_items(locations)
end
-- only load top 30 file.
local proj_file = item.uri:find(cwd) or is_win or i < 30
local unload, def
if TS_analysis_enabled and proj_file then
funcs = ts_functions(item.uri)
funcs, unload = ts_functions(item.uri)
if uri_def[item.uri] == nil or uri_def[item.uri] == {} then
if unload then
table.insert(unload_bufnrs, unload)
end
if not uri_def[item.uri] then
-- find def in file
local def = ts_defination(item.uri, item.range)
def, unload = ts_definition(item.uri, item.range)
if def and def.start then
uri_def[item.uri] = def
if def.start then -- find for the 1st time
@ -301,6 +363,16 @@ function M.locations_to_items(locations)
end
end
end
else
if uri_def[item.uri] == false then
uri_def[item.uri] = {} -- no def in file, TODO: it is tricky the definition is in another file and it is the
-- only occurrence
else
uri_def[item.uri] = false -- no def in file
end
end
if unload then
table.insert(unload_bufnrs, unload)
end
end
trace(uri_def[item.uri], item.range) -- set to log if need to get all in rnge
@ -325,7 +397,19 @@ function M.locations_to_items(locations)
table.insert(items, item)
end
trace(uri_def)
return items, width + 24 -- TODO handle long line?
-- defer release new open buffer
if #unload_bufnrs > 10 then -- load too many?
vim.defer_fn(function()
for i, bufnr_unload in ipairs(unload_bufnrs) do
if api.nvim_buf_is_loaded(bufnr_unload) and i > 10 then
api.nvim_buf_delete(bufnr_unload, {unload = true})
end
end
end, 100)
end
return items, width + 24, second_part -- TODO handle long line?
end
function M.apply_action(action_chosen)

@ -5,15 +5,17 @@ local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui"
local lsp = require "navigator.lspwrapper"
local trace = require"navigator.util".trace
ListViewCtrl = ListViewCtrl or require('guihua.listviewctrl').ListViewCtrl
-- local partial = util.partial
-- local cwd = vim.loop.cwd()
-- local lsphelper = require "navigator.lspwrapper"
local locations_to_items = lsphelper.locations_to_items
local M = {}
local ref_view = function(err, locations, ctx, cfg)
local truncate = cfg and cfg.truncate or 20
local opts = {}
trace("arg1", err, ctx, locations)
log(ctx.api)
trace(locations)
-- log("num", num)
-- log("bfnr", bufnr)
@ -25,14 +27,17 @@ local ref_view = function(err, locations, ctx, cfg)
if type(locations) ~= 'table' then
log(locations)
log("ctx", ctx)
print("incorrect setup", location)
print("incorrect setup", locations)
return
end
if locations == nil or vim.tbl_isempty(locations) then
print "References not found"
return
end
local items, width = locations_to_items(locations)
local items, width, second_part = locations_to_items(locations, truncate)
local thread_items = vim.deepcopy(items)
log("splits: ", #items, #second_part)
local ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft")
@ -41,22 +46,50 @@ local ref_view = function(err, locations, ctx, cfg)
width = math.min(width + 30, 120, math.floor(wwidth * mwidth))
-- log(items)
-- log(width)
local listview = gui.new_list_view({
opts = {
total = #locations,
items = items,
ft = ft,
width = width,
api = "Reference",
enable_preview_edit = true
})
}
local listview = gui.new_list_view(opts)
trace("update items", listview.ctrl.class)
local nv_ref_async
nv_ref_async = vim.loop.new_async(vim.schedule_wrap(function()
log('$$$$$$$$ seperate thread... $$$$$$$$')
if vim.tbl_isempty(second_part) then
return
end
local items2 = locations_to_items(second_part)
vim.list_extend(thread_items, items2)
local data = require"navigator.render".prepare_for_render(thread_items, opts)
listview.ctrl:on_data_update(data)
if nv_ref_async then
vim.loop.close(nv_ref_async)
else
log("invalid asy", nv_ref_async)
end
end))
vim.defer_fn(function()
vim.loop.new_thread(function(asy)
asy:send()
end, nv_ref_async)
end, 100)
return listview, items, width
end
local M = {}
local ref_hdlr = mk_handler(function(err, locations, ctx, cfg)
local ref_hdlr = mk_handler(function(err, locations, ctx, cfg)
trace(err, locations, ctx, cfg)
M.async_hdlr = vim.loop.new_async(vim.schedule_wrap(function()
ref_view(err, locations, ctx, cfg)
M.async_hdlr:close()
end))
M.async_hdlr:send()

@ -56,6 +56,7 @@ function M.prepare_for_render(items, opts)
local display_items = {item}
local last_summary_idx = 1
local total_ref_in_file = 1
local total = opts.total
local icon = ""
local lspapi = opts.api or ""
@ -87,6 +88,7 @@ function M.prepare_for_render(items, opts)
local dfn = items[i].display_filename
if last_summary_idx == 1 then
lspapi_display = items[i].symbol_name .. ' ' .. lspapi_display
trace(items[1], lspapi_display, display_items[last_summary_idx])
end
@ -94,7 +96,7 @@ function M.prepare_for_render(items, opts)
-- trace(items[i], items[i].filename, last_summary_idx, display_items[last_summary_idx].filename)
-- TODO refact display_filename generate part
if items[i].filename == fn then
space, trim = get_pads(opts.width, icon .. ' ' .. dfn, lspapi_display .. ' 12')
space, trim = get_pads(opts.width, icon .. ' ' .. dfn, lspapi_display .. ' 14')
if trim and opts.width > 50 and #dfn > opts.width - 20 then
local fn1 = string.sub(dfn, 1, opts.width - 50)
local fn2 = string.sub(dfn, #dfn - 10, #dfn)
@ -102,10 +104,15 @@ function M.prepare_for_render(items, opts)
space = ' '
-- log("trim", fn1, fn2)
end
display_items[last_summary_idx].text = string.format("%s %s%s%s %i", icon,
display_items[last_summary_idx]
.display_filename, space,
lspapi_display, total_ref_in_file)
local api_disp = string.format("%s %s%s%s %i", icon,
display_items[last_summary_idx].display_filename, space,
lspapi_display, total_ref_in_file)
if total then
api_disp = api_disp .. ' of: ' .. tostring(total)
end
display_items[last_summary_idx].text = api_disp
total_ref_in_file = total_ref_in_file + 1
else

@ -47,6 +47,30 @@ function M.goto_definition(bufnr)
end
end
local function node_is_definination(node)
if node:parent() == nil then
return false
end
local nd_type = node:parent():type()
local decl = {'short_var_declaration', 'short_var_declaration', 'declaration'}
if vim.tbl_contains(decl, nd_type) then
return true
end
if node:parent():parent() == nil then
return false
end
nd_type = node:parent():parent():type()
if vim.tbl_contains(decl, nd_type) then
return true
end
return false
end
-- use lsp range to find def
function M.find_definition(range, bufnr)
if not range or not range.start then
@ -68,13 +92,19 @@ function M.find_definition(range, bufnr)
end
local definition = locals.find_definition(node_at_point, bufnr)
if definition ~= node_at_point then
trace("err: def found:", definition:range(), definition:type())
if definition ~= node_at_point then -- NOTE: it may not worksfor some of languages. if def not found, ts
-- returns current node. if your node is def, then it also return self... then I have no idea weather it is
-- def or not
trace("info: def found:", definition:range(), definition:type())
local r, c = definition:range()
return {start = {line = r, character = c}}
elseif node_is_definination(node_at_point) then
trace("declaraction here ", definition:type())
local r, c = definition:range()
return {start = {line = r, character = c}}
else
trace("err: def not found in ", bufnr)
trace("error: def not found in ", bufnr, definition:range(), definition:type(),
definition:parent():type())
end
end
@ -283,11 +313,21 @@ local function get_all_nodes(bufnr, filter, summary)
-- instead of neighbors of the next nodes.
local containers = {
["function"] = true,
["local_function"] = true,
["arrow_function"] = true,
["type"] = true,
["class"] = true,
["method"] = true
}
-- check and load buff
local should_unload = false
if not vim.api.nvim_buf_is_loaded(bufnr) then
should_unload = true
vim.fn.bufload(bufnr)
end
-- Step 2 find correct completions
local length = 10
local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos
@ -391,6 +431,9 @@ local function get_all_nodes(bufnr, filter, summary)
trace(all_nodes)
local nd = {nodes = all_nodes, ftime = vim.fn.getftime(fname), length = length}
lru:set(hash, nd)
if should_unload then
vim.api.nvim_buf_delete(bufnr, {unload = true})
end
return all_nodes, length
end
@ -401,6 +444,7 @@ function M.buf_func(bufnr)
end
bufnr = bufnr or api.nvim_get_current_buf()
local all_nodes, width = get_all_nodes(bufnr, {
["function"] = true,
["var"] = true,
@ -532,7 +576,7 @@ function M.get_node_at_line(lnum)
local node = tree:root():named_descendant_for_range(unpack(range))
trace(node, node:type())
-- trace(node, node:type()) -- log all lines and all nodes
return node
end

@ -13,6 +13,32 @@ describe("should run lsp reference", function()
if debug.getinfo(vim.lsp.handlers.signature_help).nparams > 4 then
nvim_6 = false
end
local result = {
{
range = {['end'] = {character = 6, line = 14}, start = {character = 1, line = 14}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 15, line = 24}, start = {character = 10, line = 24}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 17, line = 28}, start = {character = 12, line = 28}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 19, line = 51}, start = {character = 14, line = 51}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 19, line = 55}, start = {character = 14, line = 55}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 16, line = 59}, start = {character = 11, line = 59}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 16, line = 5}, start = {character = 11, line = 5}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface_test.go"
}
}
it("should show references", function()
local status = require("plenary.reload").reload_module("navigator")
@ -96,31 +122,6 @@ describe("should run lsp reference", function()
-- allow gopls start
vim.wait(200, function()
end)
local result = {
{
range = {['end'] = {character = 6, line = 14}, start = {character = 1, line = 14}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 15, line = 24}, start = {character = 10, line = 24}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 17, line = 28}, start = {character = 12, line = 28}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 19, line = 51}, start = {character = 14, line = 51}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 19, line = 55}, start = {character = 14, line = 55}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 16, line = 59}, start = {character = 11, line = 59}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, {
range = {['end'] = {character = 16, line = 5}, start = {character = 11, line = 5}},
uri = "file://" .. cur_dir .. "/tests/fixtures/interface_test.go"
}
}
local win, items, width
@ -144,6 +145,75 @@ describe("should run lsp reference", function()
eq(win.ctrl.data[2].range.start.line, 14)
eq(items[1].display_filename, "./interface.go")
-- eq(width, 60)
end)
it("reference handler should return items with thread", function()
local status = require("plenary.reload").reload_module("navigator")
local status = require("plenary.reload").reload_module("guihua")
vim.cmd([[packadd navigator.lua]])
vim.cmd([[packadd guihua.lua]])
local path = cur_dir .. "/tests/fixtures/interface.go" -- %:p:h ? %:p
print(path)
local cmd = " silent exe 'e " .. path .. "'"
vim.cmd(cmd)
vim.cmd([[cd %:p:h]])
local bufn = vim.fn.bufnr("")
vim.fn.setpos(".", {bufn, 15, 4, 0}) -- width
vim.bo.filetype = "go"
require'navigator'.setup({
debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
code_action_icon = "A ",
width = 0.75, -- max width ratio (number of cols for the floating window) / (window width)
height = 0.3, -- max list window height, 0.3 by default
preview_height = 0.35, -- max height of preview windows
debug_console_output = true,
border = 'none'
})
_NgConfigValues.debug_console_output = true
vim.bo.filetype = "go"
-- allow gopls start
for i = 1, 10 do
vim.wait(400, function()
end)
local clients = vim.lsp.get_active_clients()
print("clients ", #clients)
if #clients > 0 then
break
end
end
-- allow gopls start
vim.wait(200, function()
end)
local win, items, width
if nvim_6 then
win, items, width = require('navigator.reference').ref_view(nil, result, {
method = 'textDocument/references',
bufnr = 1,
client_id = 1
}, {truncate = 2})
else
win, items, width = require('navigator.reference').reference_handler(nil,
"textDocument/references",
result, 1, 1)
end
print("win", vim.inspect(win))
print("items", vim.inspect(items))
-- eq(win.ctrl.data, "./interface.go")
eq(win.ctrl.data[1].display_filename, "./interface.go")
eq(win.ctrl.data[2].range.start.line, 14)
-- eq(items[1].display_filename, "./interface.go")
-- eq(width, 60)
end)
end)

Loading…
Cancel
Save