merge treesitter branch. Improve reference search with treesitter scope. Better highlight in reference search result

neovim_0_5
ray-x 3 years ago
parent 7351a65917
commit 777acc4b92

@ -1,27 +1,67 @@
# Navigator # Navigator
Easy code navigation through LSP and 🌲🏡Treesitter symbols, diagnostic errors. - Easy code navigation through LSP and 🌲🏡Treesitter symbols; view diagnostic errors.
![document symbol](https://github.com/ray-x/files/blob/master/img/navigator/doc_symbol.gif?raw=true) - Put language server and tree sitter's parser together. Not only for better highlight but also display symbol context
and scope.
Here is an example
Show javascript call tree 🌲 of a variable inside a closure. Similar to incoming&outgoing calls from LSP. This feature
is designed for the symbol analysis.
![js_closure_call_tree](https://user-images.githubusercontent.com/1681295/119120589-cee23700-ba6f-11eb-95c5-b9ac8d445c31.jpg)
Explains:
- 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
cases, we search for reference 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.
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.
# Features: # Features:
- LSP easy setup. Support the most commonly used lsp clients setup. Dynamic lsp activation based on buffer type. - 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)
- 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
- 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
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 filas (Some of LSP e.g. sumneko_lua, there is a 100kb file size limition?)
- FZY search with Lua-JIT - FZY search with Lua-JIT
- Better navigation for diagnostic errors, Navigate through all files/buffers that contain errors/warnings - Better navigation for diagnostic errors, Navigate through all files/buffers that contain errors/warnings
- Grouping references/implementation/incomming/outgoing based on file names.
- Grouping references/implementation/incoming/outgoing based on file names.
- Treesitter based variable/function context analysis. It is 10x times faster compared to purely rely on LSP. In most
of the case, it takes treesitter less than 4 ms to read and render all nodes for a file of 1,000 LOC.
- The first plugin, IMO, that allows you to search in all treesitter symbols in the workspace.
- Nerdfont, emoji for LSP and Treesitter kind - Nerdfont, emoji for LSP and Treesitter kind
- Optimize display (remove trailing bracket/space), display the caller of reference, de-duplicate lsp results (e.g reference
in the same line). Using treesitter for file preview highlighter etc
# Why a new plugin # Why a new plugin
After installed a handful of lsp plugins, I still got ~800 loc for lsp and treesitter and still increasing because I need I'd like to go beyond what the system is providing.
to tune the lsp plugins to fit my requirements. Navigator.lua help user setup lspconfig with only a few lines of codes.
This plugin provides a visual way to manage and navigate through symbols, diagnostic errors, reference etc.
It also the first plugin, IMO, that allows you to search in all treesitter symbols in the workspace.
# Similar projects / special mentions: # Similar projects / special mentions:
@ -30,11 +70,14 @@ It also the first plugin, IMO, that allows you to search in all treesitter symbo
- [fuzzy](https://github.com/amirrezaask/fuzzy.nvim) - [fuzzy](https://github.com/amirrezaask/fuzzy.nvim)
- [lspsaga](https://github.com/glepnir/lspsaga.nvim) - [lspsaga](https://github.com/glepnir/lspsaga.nvim)
- [fzf-lsp lsp with fzf as gui backend](https://github.com/gfanto/fzf-lsp.nvim) - [fzf-lsp lsp with fzf as gui backend](https://github.com/gfanto/fzf-lsp.nvim)
- [nvim-treesitter-textobjects](https://github.com/nvim-treesitter/nvim-treesitter-textobjects)
# Install # Install
Require nvim-0.5.0 (a.k.a nightly)
You can remove your lspconfig setup and use this plugin. You can remove your lspconfig setup and use this plugin.
The plugin depends on lspconfig and [guihua.lua](https://github.com/ray-x/guihua.lua), which provides GUI and fzy support(thanks [romgrk](romgrk/fzy-lua-native)). The plugin depends on lspconfig and [guihua.lua](https://github.com/ray-x/guihua.lua), which provides GUI and fzy support(migrate from [romgrk's project](romgrk/fzy-lua-native)).
```vim ```vim
Plug 'neovim/nvim-lspconfig' Plug 'neovim/nvim-lspconfig'
@ -42,6 +85,8 @@ Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua' Plug 'ray-x/navigator.lua'
``` ```
Note: Highly recommened: 'nvim-treesitter/nvim-treesitter'
Packer Packer
```lua ```lua
@ -79,17 +124,56 @@ lua <<EOF
require'navigator'.setup() require'navigator'.setup()
EOF EOF
```
You can remove your lspconfig.lua and use the hooks of navigator.lua. As the
navigator will bind keys and handler for you. The LSP will be loaded lazily based on filetype.
Nondefault configuration example:
```lua
require.'navigator'.setup({
debug = false, -- log output not implemented
code_action_icon = " ",
width = 0.75, -- number of cols for the floating window
height = 0.3, -- preview window size, 0.3 by default
on_attach = nil,
-- put a on_attach of your own here, e.g
-- function(client, bufnr)
-- -- the on_attach will be called at end of navigator on_attach
-- end,
treesitter_call_tree = 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",
code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
lsp = {
format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc)
tsserver = {
filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetype
},
gopls = { -- gopls setting
settings = {
gopls = {gofumpt = false} -- disable gofumpt etc,
}
}
}
})
``` ```
Generally speaking, you could remove most part of your lspconfig.lua and use the hooks in navigator.lua. As the
navigator will bind keys and handler for you. The lsp will be loaded lazily based on filetype.
## Dependency ## Dependency
- lspconfig - lspconfig
- guihua.lua (provides floating window, FZY) - guihua.lua (provides floating window, FZY)
- Optional: - Optional:
- treesitter (list treesitter symbols) - treesitter (list treesitter symbols, object analysis)
- lsp-signature (better signature help) - lsp-signature (better signature help)
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.
@ -102,7 +186,7 @@ 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\> to open location or apply code actions - \<c-o\> or \<CR\> to open location or apply code actions. Note: \<CR\> might be binded in insert mode by other plugins
## Configuration ## Configuration
@ -121,8 +205,7 @@ colorscheme: [aurora](https://github.com/ray-x/aurora)
### Reference ### Reference
![reference](https://github.com/ray-x/files/blob/master/img/navigator/ref.gif?raw=true) Pls check first part of README
### Document Symbol ### Document Symbol
@ -132,7 +215,7 @@ colorscheme: [aurora](https://github.com/ray-x/aurora)
![workspace symbol](https://github.com/ray-x/files/blob/master/img/navigator/workspace_symbol.gif?raw=true) ![workspace symbol](https://github.com/ray-x/files/blob/master/img/navigator/workspace_symbol.gif?raw=true)
# Current symbol highlight and jump backword/forward between symbols # Current symbol highlight and jump backward/forward between symbols
Document highlight provided by LSP. Document highlight provided by LSP.
Jump between symbols between symbols with treesitter (with `]r` and `[r`) Jump between symbols between symbols with treesitter (with `]r` and `[r`)
@ -150,7 +233,7 @@ Show diagnostic in all buffers
### Implementation ### Implementation
![implementation](https://github.com/ray-x/files/blob/master/img/navigator/implemention.jpg?raw=true) ![implementation](https://user-images.githubusercontent.com/1681295/118735346-967e0580-b883-11eb-8c1e-88c5810f7e05.jpg?raw=true)
### Fzy search in reference ### Fzy search in reference
@ -160,17 +243,18 @@ Show diagnostic in all buffers
![code actions](https://github.com/ray-x/files/blob/master/img/navigator/codeaction.jpg?raw=true) ![code actions](https://github.com/ray-x/files/blob/master/img/navigator/codeaction.jpg?raw=true)
Fill struct with gopls #### Fill struct with gopls
![code actions fill struct](https://github.com/ray-x/files/blob/master/img/navigator/fill_struct.gif?raw=true) ![code actions fill struct](https://github.com/ray-x/files/blob/master/img/navigator/fill_struct.gif?raw=true)
### Code preview with highlight ### Code preview with highlight
![code preview](https://github.com/ray-x/files/blob/master/img/navigator/preview_with_hl.jpg?raw=true) ![treesitter_preview](https://user-images.githubusercontent.com/1681295/118900852-4bccbe00-b955-11eb-82f6-0747b1b64e7c.jpg)
### Treesitter symbol ### Treesitter symbol
Treetsitter symbols in all buffers Treetsitter symbols in all buffers
![treesitter](https://github.com/ray-x/files/blob/master/img/navigator/treesitter.jpg?raw=true) ![treesitter](https://user-images.githubusercontent.com/1681295/118734953-cc6eba00-b882-11eb-9db8-0a052630d57e.jpg?raw=true)
### Signature help ### Signature help
@ -184,7 +268,7 @@ Improved signature help with current parameter highlighted
![incomming](https://github.com/ray-x/files/blob/master/img/navigator/incomming.jpg?raw=true) ![incomming](https://github.com/ray-x/files/blob/master/img/navigator/incomming.jpg?raw=true)
### Light bulb when codeAction avalible ### Light bulb if codeAction available
![lightbulb](https://github.com/ray-x/files/blob/master/img/navigator/lightbulb.jpg?raw=true) ![lightbulb](https://github.com/ray-x/files/blob/master/img/navigator/lightbulb.jpg?raw=true)
@ -192,6 +276,9 @@ Improved signature help with current parameter highlighted
![nerdfont](https://github.com/ray-x/files/blob/master/img/navigator/icon_nerd.jpg?raw=true) ![nerdfont](https://github.com/ray-x/files/blob/master/img/navigator/icon_nerd.jpg?raw=true)
# Break changes and known issues
[known issues I am working on](https://github.com/ray-x/navigator.lua/issues/1)
# Todo # Todo
- Early phase, bugs expected, PR and suggestions are welcome - Early phase, bugs expected, PR and suggestions are welcome

@ -2,7 +2,8 @@ local M = {}
_NgConfigValues = { _NgConfigValues = {
debug = false, -- log output not implemented debug = false, -- log output not implemented
code_action_icon = "", code_action_icon = "",
width = nil, -- valeu of cols TODO allow float e.g. 0.6 width = 0.6, -- valeu of cols TODO allow float e.g. 0.6
preview_height = 0.35,
height = nil, height = nil,
on_attach = nil, on_attach = nil,
-- function(client, bufnr) -- function(client, bufnr)
@ -11,8 +12,15 @@ _NgConfigValues = {
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",
treesitter_call_tree = true, 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
lsp = {
format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc)
tsserver = {
filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetype
}
}
} }
vim.cmd("command! -nargs=0 LspLog call v:lua.open_lsp_log()") vim.cmd("command! -nargs=0 LspLog call v:lua.open_lsp_log()")
@ -20,26 +28,36 @@ vim.cmd("command! -nargs=0 LspRestart call v:lua.reload_lsp()")
local extend_config = function(opts) local extend_config = function(opts)
opts = opts or {} opts = opts or {}
if next(opts) == nil then return end if next(opts) == nil then
return
end
for key, value in pairs(opts) do for key, value in pairs(opts) do
if _NgConfigValues[key] == nil then -- if _NgConfigValues[key] == nil then
error(string.format("[] Key %s not valid", key)) -- error(string.format("[] Key %s not valid", key))
return -- return
end -- end
if type(M.config_values[key]) == "table" then if type(_NgConfigValues[key]) == "table" then
for k, v in pairs(value) do _NgConfigValues[key][k] = v end for k, v in pairs(value) do
_NgConfigValues[key][k] = v
end
else else
_NgConfigValues[key] = value _NgConfigValues[key] = value
end end
end end
end end
M.config_values = function() return _NgConfigValues end M.config_values = function()
return _NgConfigValues
end
M.setup = function(cfg) M.setup = function(cfg)
extend_config(cfg) extend_config(cfg)
-- local log = require"navigator.util".log
-- log(debug.traceback())
-- log(cfg, _NgConfigValues)
-- print("loading navigator") -- print("loading navigator")
require("navigator.lspclient").setup(_NgConfigValues) require('navigator.lspclient.clients').setup(_NgConfigValues)
require("navigator.lspclient.mapping").setup(_NgConfigValues)
require("navigator.reference") require("navigator.reference")
require("navigator.definition") require("navigator.definition")
require("navigator.hierarchy") require("navigator.hierarchy")
@ -49,6 +67,10 @@ M.setup = function(cfg)
vim.cmd [[autocmd CursorHold,CursorHoldI * lua require'navigator.codeAction'.code_action_prompt()]] vim.cmd [[autocmd CursorHold,CursorHoldI * lua require'navigator.codeAction'.code_action_prompt()]]
end end
-- vim.cmd("autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4") -- vim.cmd("autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4")
if not _NgConfigValues.loaded then
vim.cmd([[autocmd FileType * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
_NgConfigValues.loaded = true
end
end end
return M return M

@ -1,6 +1,6 @@
local util = require "navigator.util" local util = require "navigator.util"
local log = util.log local log = util.log
local verbose = util.verbose local trace = util.trace
local code_action = {} local code_action = {}
local gui = require "navigator.gui" local gui = require "navigator.gui"
local config = require("navigator").config_values() local config = require("navigator").config_values()
@ -42,7 +42,7 @@ function code_action.code_action_handler(err, _, actions, cid, bufnr, _, customS
vim.lsp.buf.execute_command(action_chosen) vim.lsp.buf.execute_command(action_chosen)
end end
verbose(action_chosen) trace(action_chosen)
end end
gui.new_list_view { gui.new_list_view {

@ -3,29 +3,10 @@ local ListView = require "guihua.listview"
local TextView = require "guihua.textview" local TextView = require "guihua.textview"
local util = require "navigator.util" local util = require "navigator.util"
local log = require"navigator.util".log local log = require"navigator.util".log
local verbose = require"navigator.util".verbose local trace = require"navigator.util".trace
local api = vim.api local api = vim.api
function M.new_preview(opts) local top_center = require"guihua.location".top_center
return TextView:new({
loc = "top_center",
rect = {
height = opts.height, -- opts.preview_heigh or 12, -- TODO 12
width = opts.width or 100,
pos_x = opts.pos_x or 0,
pos_y = opts.pos_y or 4
},
-- data = display_data,
relative = opts.relative,
-- data = opts.items, -- either items or uri
uri = opts.uri,
syntax = opts.syntax,
enter = opts.enter or false,
range = opts.range,
display_range = opts.display_range,
hl_line = opts.hl_line
})
end
function M._preview_location(opts) -- location, width, pos_x, pos_y function M._preview_location(opts) -- location, width, pos_x, pos_y
local uri = opts.location.uri local uri = opts.location.uri
@ -34,7 +15,9 @@ function M._preview_location(opts) -- location, width, pos_x, pos_y
return return
end end
local bufnr = vim.uri_to_bufnr(uri) local bufnr = vim.uri_to_bufnr(uri)
if not api.nvim_buf_is_loaded(bufnr) then vim.fn.bufload(bufnr) end if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
-- --
local display_range = opts.location.range local display_range = opts.location.range
@ -56,15 +39,18 @@ function M._preview_location(opts) -- location, width, pos_x, pos_y
-- local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line, false) -- local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line, false)
-- --
local syntax = api.nvim_buf_get_option(bufnr, "ft") local syntax = api.nvim_buf_get_option(bufnr, "ft")
if syntax == nil or #syntax < 1 then syntax = "c" end if syntax == nil or #syntax < 1 then
syntax = "c"
end
-- verbose(syntax, contents) -- trace(syntax, contents)
local win_opts = { local win_opts = {
syntax = syntax, syntax = syntax,
width = opts.width, width = opts.width,
height = display_range['end'].line - display_range.start.line + 1, height = display_range['end'].line - display_range.start.line + 1,
pos_x = opts.offset_x or 0, preview_height = opts.preview_height,
pos_y = opts.offset_y or 10, pos_x = opts.offset_x,
pos_y = opts.offset_y,
range = opts.range, range = opts.range,
display_range = display_range, display_range = display_range,
uri = uri, uri = uri,
@ -72,23 +58,44 @@ function M._preview_location(opts) -- location, width, pos_x, pos_y
} }
-- win_opts.items = contents -- win_opts.items = contents
win_opts.hl_line = opts.lnum - display_range.start.line win_opts.hl_line = opts.lnum - display_range.start.line
if win_opts.hl_line < 0 then win_opts.hl_line = 1 end if win_opts.hl_line < 0 then
verbose(opts.lnum, opts.range.start.line, win_opts.hl_line) win_opts.hl_line = 1
local w = M.new_preview(win_opts) end
trace(opts.lnum, opts.range.start.line, win_opts.hl_line)
local w = TextView:new({
loc = "offset_center",
rect = {
height = win_opts.height, -- opts.preview_heigh or 12, -- TODO 12
width = win_opts.width,
pos_x = win_opts.pos_x,
pos_y = win_opts.pos_y
},
list_view_height = win_opts.height,
-- data = display_data,
relative = win_opts.relative,
-- data = opts.items, -- either items or uri
uri = win_opts.uri,
syntax = win_opts.syntax,
enter = win_opts.enter or false,
range = win_opts.range,
display_range = win_opts.display_range,
hl_line = win_opts.hl_line
})
return w return w
end end
function M.preview_uri(opts) -- uri, width, line, col, offset_x, offset_y function M.preview_uri(opts) -- uri, width, line, col, offset_x, offset_y
local line_beg = opts.lnum - 1 local line_beg = opts.lnum - 1
if line_beg >= 2 then line_beg = line_beg - 2 end if line_beg >= 2 then
line_beg = line_beg - 2
end
local loc = {uri = opts.uri, range = {start = {line = line_beg}}} local loc = {uri = opts.uri, range = {start = {line = line_beg}}}
-- TODO: preview height -- TODO: preview height
loc.range["end"] = {line = opts.lnum + 12} loc.range["end"] = {line = opts.lnum + opts.preview_height}
opts.location = loc opts.location = loc
log("uri", opts.uri, opts.lnum, opts.location) trace("uri", opts.uri, opts.lnum, opts.location.range.start.line, opts.location.range['end'].line)
return M._preview_location(opts) return M._preview_location(opts)
end end
@ -99,58 +106,94 @@ function M.new_list_view(opts)
local data = {} local data = {}
local wwidth = api.nvim_get_option("columns") local wwidth = api.nvim_get_option("columns")
local width = math.min(opts.width or config.width or 120, math.floor(wwidth * 0.8))
local loc = "top_center"
local width = math.floor(wwidth * 0.75)
if config.width ~= nil and config.width > 0.3 and config.width < 0.99 then
width = math.floor(wwidth * config.width)
end
width = math.min(opts.width or 120, width)
opts.width = width
local wheight = config.height or math.floor(api.nvim_get_option("lines") * 0.8) local wheight = config.height or math.floor(api.nvim_get_option("lines") * 0.8)
local prompt = opts.prompt or false local prompt = opts.prompt or false
opts.width = width
if opts.rawdata then if opts.rawdata then
data = items data = items
else else
data = require"guihua.util".prepare_for_render(items, opts) data = require"navigator.render".prepare_for_render(items, opts)
end end
local border = _NgConfigValues.border or 'shadow'
if data and not vim.tbl_isempty(data) then if data and not vim.tbl_isempty(data) then
-- replace -- replace
-- TODO: 10 vimrc opt -- TODO: 10 vimrc opt
if #data > 10 and opts.prompt == nil then prompt = true end if #data > 10 and opts.prompt == nil then
loc = "top_center"
prompt = true
end
local lheight = math.min(#data, math.floor(wheight / 2))
local pheight = math.min(wheight - lheight, math.floor(wheight / 2))
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 height = math.min(#data, math.floor(wheight / 2))
local offset_y = height + 2 -- style shadow took 2 lines
if prompt then offset_y = offset_y + 1 end
return ListView:new({ return ListView:new({
loc = "top_center", loc = loc,
prompt = prompt, prompt = prompt,
relative = opts.relative, relative = opts.relative,
style = opts.style, style = opts.style,
api = opts.api, api = opts.api,
rect = {height = height, width = width, pos_x = 0, pos_y = 0}, rect = {height = lheight, width = width, pos_x = 0, pos_y = 0},
-- preview_height = pheight,
ft = opts.ft or 'guihua', ft = opts.ft or 'guihua',
-- data = display_data, -- data = display_data,
data = data, data = data,
on_confirm = opts.on_confirm or function(pos) on_confirm = opts.on_confirm or function(pos)
if pos == 0 then pos = 1 end if pos == 0 then
pos = 1
end
local l = data[pos] local l = data[pos]
if l.filename ~= nil then if l.filename ~= nil then
verbose("openfile ", l.filename, l.lnum, l.col) trace("openfile ", l.filename, l.lnum, l.col)
util.open_file_at(l.filename, l.lnum, l.col) util.open_file_at(l.filename, l.lnum, l.col)
end end
end, end,
on_move = opts.on_move or function(pos) on_move = opts.on_move or function(pos)
if pos == 0 then pos = 1 end if pos == 0 then
pos = 1
end
local l = data[pos] local l = data[pos]
verbose("on move", pos, l) trace("on move", pos, l)
verbose("on move", pos, l.text or l, l.uri, l.filename) trace("on move", pos, l.text or l, l.uri, l.filename)
-- todo fix -- todo fix
if l.uri == nil then l.uri = "file:///" .. l.filename end if l.uri == nil then
l.uri = "file:///" .. l.filename
end
return M.preview_uri({ return M.preview_uri({
uri = l.uri, uri = l.uri,
width = width, width = width,
height = lheight, -- this is to cal offset
preview_height = pheight,
lnum = l.lnum, lnum = l.lnum,
col = l.col, col = l.col,
range = l.range, range = l.range,
offset_x = 0, offset_x = 0,
offset_y = offset_y, offset_y = offset_y,
border = "double" border = border
}) })
end end
}) })

@ -6,25 +6,22 @@ local lsphelper = require "navigator.lspwrapper"
local cwd = vim.fn.getcwd(0) local cwd = vim.fn.getcwd(0)
local M = {} local M = {}
local function call_hierarchy_handler(direction, err, _, result, _, _, local function call_hierarchy_handler(direction, err, _, result, _, _, error_message)
error_message)
-- log('call_hierarchy') -- log('call_hierarchy')
assert(#vim.lsp.buf_get_clients() > 0, assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
"Must have a client running to use lsp_tags")
if err ~= nil then if err ~= nil then
log("dir", direction, "result", result, "err", err)
print("ERROR: " .. error_message) print("ERROR: " .. error_message)
return return
end end
-- log("dir", direction, "result", result)
local items = {} local items = {}
for _, call_hierarchy_call in pairs(result) do for _, call_hierarchy_call in pairs(result) do
local call_hierarchy_item = call_hierarchy_call[direction] local call_hierarchy_item = call_hierarchy_call[direction]
local kind = '' local kind = ''
if call_hierarchy_item.kind then if call_hierarchy_item.kind then
kind = require'navigator.lspclient.lspkind'.symbol_kind( kind = require'navigator.lspclient.lspkind'.symbol_kind(call_hierarchy_item.kind) .. ' '
call_hierarchy_item.kind) .. ' '
end end
for _, range in pairs(call_hierarchy_call.fromRanges) do for _, range in pairs(call_hierarchy_call.fromRanges) do
local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)) local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri))
@ -47,21 +44,18 @@ end
local call_hierarchy_handler_from = partial(call_hierarchy_handler, "from") local call_hierarchy_handler_from = partial(call_hierarchy_handler, "from")
local call_hierarchy_handler_to = partial(call_hierarchy_handler, "to") local call_hierarchy_handler_to = partial(call_hierarchy_handler, "to")
local function incoming_calls_handler(bang, err, method, result, client_id, local function incoming_calls_handler(bang, err, method, result, client_id, bufnr)
bufnr) assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
assert(#vim.lsp.buf_get_clients() > 0, local results = call_hierarchy_handler_from(err, method, result, client_id, bufnr,
"Must have a client running to use lsp_tags") "Incoming calls not found")
local results = call_hierarchy_handler_from(err, method, result, client_id,
bufnr, "Incoming calls not found")
local ft = vim.api.nvim_buf_get_option(bufnr, "ft") local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({items = results, ft = ft, api = ''}) gui.new_list_view({items = results, ft = ft, api = ''})
end end
local function outgoing_calls_handler(bang, err, method, result, client_id, local function outgoing_calls_handler(bang, err, method, result, client_id, bufnr)
bufnr) local results = call_hierarchy_handler_to(err, method, result, client_id, bufnr,
local results = call_hierarchy_handler_to(err, method, result, client_id, "Outgoing calls not found")
bufnr, "Outgoing calls not found")
local ft = vim.api.nvim_buf_get_option(bufnr, "ft") local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({items = results, ft = ft, api = ''}) gui.new_list_view({items = results, ft = ft, api = ''})
@ -69,23 +63,23 @@ local function outgoing_calls_handler(bang, err, method, result, client_id,
end end
function M.incoming_calls(bang, opts) function M.incoming_calls(bang, opts)
assert(#vim.lsp.buf_get_clients() > 0, assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
"Must have a client running to use lsp_tags") if not lsphelper.check_capabilities("call_hierarchy") then
if not lsphelper.check_capabilities("call_hierarchy") then return end return
end
local params = vim.lsp.util.make_position_params() local params = vim.lsp.util.make_position_params()
util.call_sync("callHierarchy/incomingCalls", params, opts, util.call_sync("callHierarchy/incomingCalls", params, opts, partial(incoming_calls_handler, bang))
partial(incoming_calls_handler, bang))
end end
function M.outgoing_calls(bang, opts) function M.outgoing_calls(bang, opts)
assert(#vim.lsp.buf_get_clients() > 0, assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
"Must have a client running to use lsp_tags") if not lsphelper.check_capabilities("call_hierarchy") then
if not lsphelper.check_capabilities("call_hierarchy") then return end return
end
local params = vim.lsp.util.make_position_params() local params = vim.lsp.util.make_position_params()
util.call_sync("callHierarchy/outgoingCalls", params, opts, util.call_sync("callHierarchy/outgoingCalls", params, opts, partial(outgoing_calls_handler, bang))
partial(outgoing_calls_handler, bang))
end end
M.incoming_calls_call = partial(M.incoming_calls, 0) M.incoming_calls_call = partial(M.incoming_calls, 0)

@ -3,7 +3,7 @@ local lsp = require("vim.lsp")
local util = require "navigator.util" local util = require "navigator.util"
local log = util.log local log = util.log
local verbose = util.verbose local trace = util.trace
local diagnostic_map = function(bufnr) local diagnostic_map = function(bufnr)
local opts = {noremap = true, silent = true} local opts = {noremap = true, silent = true}
@ -20,7 +20,7 @@ M.on_attach = function(client, bufnr)
return return
end end
log("attaching", bufnr) log("attaching", bufnr)
verbose(client) trace(client)
local hassig, sig = pcall(require, "lsp_signature") local hassig, sig = pcall(require, "lsp_signature")
if hassig then sig.on_attach() end if hassig then sig.on_attach() end
diagnostic_map(bufnr) diagnostic_map(bufnr)

@ -1,6 +1,6 @@
-- todo allow config passed in -- todo allow config passed in
local log = require"navigator.util".log local log = require"navigator.util".log
local verbose = require"navigator.util".verbose local trace = require"navigator.util".trace
_Loading = false _Loading = false
@ -8,7 +8,9 @@ if packer_plugins ~= nil then
-- packer installed -- packer installed
local loader = require"packer".loader local loader = require"packer".loader
if not packer_plugins["neovim/nvim-lspconfig"] or if not packer_plugins["neovim/nvim-lspconfig"] or
not packer_plugins["neovim/nvim-lspconfig"].loaded then loader("nvim-lspconfig") end not packer_plugins["neovim/nvim-lspconfig"].loaded then
loader("nvim-lspconfig")
end
if not packer_plugins["ray-x/guihua.lua"] or not packer_plugins["guihua.lua"].loaded then if not packer_plugins["ray-x/guihua.lua"] or not packer_plugins["guihua.lua"].loaded then
loader("guihua.lua") loader("guihua.lua")
-- if lazyloading -- if lazyloading
@ -16,13 +18,15 @@ if packer_plugins ~= nil then
end end
local has_lsp, lspconfig = pcall(require, "lspconfig") local has_lsp, lspconfig = pcall(require, "lspconfig")
if not has_lsp then error("loading lsp config") end if not has_lsp then
error("loading lsp config")
end
local highlight = require "navigator.lspclient.highlight" local highlight = require "navigator.lspclient.highlight"
local util = lspconfig.util local util = lspconfig.util
local config = require"navigator".config_values() local config = require"navigator".config_values()
local cap = vim.lsp.protocol.make_client_capabilities() -- local cap = vim.lsp.protocol.make_client_capabilities()
local on_attach = require("navigator.lspclient.attach").on_attach local on_attach = require("navigator.lspclient.attach").on_attach
-- gopls["ui.completion.usePlaceholders"] = true -- gopls["ui.completion.usePlaceholders"] = true
@ -48,7 +52,9 @@ add("$VIMRUNTIME")
-- add your config -- add your config
local home = vim.fn.expand("$HOME") local home = vim.fn.expand("$HOME")
if vim.fn.isdirectory(home .. "/.config/nvim") then add(home .. "/.config/nvim") end if vim.fn.isdirectory(home .. "/.config/nvim") then
add(home .. "/.config/nvim")
end
-- add plugins it may be very slow to add all in path -- add plugins it may be very slow to add all in path
-- if vim.fn.isdirectory(home .. "/.config/share/nvim/site/pack/packer") then -- if vim.fn.isdirectory(home .. "/.config/share/nvim/site/pack/packer") then
@ -64,7 +70,7 @@ library[vim.fn.expand("$VIMRUNTIME/lua/vim/lsp")] = true
local setups = { local setups = {
gopls = { gopls = {
on_attach = on_attach, on_attach = on_attach,
capabilities = cap, -- capabilities = cap,
filetypes = {"go", "gomod"}, filetypes = {"go", "gomod"},
message_level = vim.lsp.protocol.MessageType.Error, message_level = vim.lsp.protocol.MessageType.Error,
cmd = { cmd = {
@ -86,8 +92,9 @@ local setups = {
completeUnimported = true, completeUnimported = true,
staticcheck = true, staticcheck = true,
matcher = "fuzzy", matcher = "fuzzy",
experimentalDiagnosticsDelay = "500ms",
symbolMatcher = "fuzzy", symbolMatcher = "fuzzy",
gofumpt = true, gofumpt = false, -- true, -- turn on for new repos, gofmpt is good but also create code turmoils
buildFlags = {"-tags", "integration"} buildFlags = {"-tags", "integration"}
-- buildFlags = {"-tags", "functional"} -- buildFlags = {"-tags", "functional"}
} }
@ -204,22 +211,25 @@ local default_cfg = {on_attach = on_attach}
-- check and load based on file type -- check and load based on file type
local function load_cfg(ft, client, cfg, loaded) local function load_cfg(ft, client, cfg, loaded)
-- log("trying", client)
if lspconfig[client] == nil then if lspconfig[client] == nil then
log("not supported", client) log("not supported by nvim", client)
return return
end end
local lspft = lspconfig[client].document_config.default_config.filetypes local lspft = lspconfig[client].document_config.default_config.filetypes
local should_load = false local should_load = false
if lspft ~= nil and #lspft > 0 then if lspft ~= nil and #lspft > 0 then
for _, value in ipairs(lspft) do if ft == value then should_load = true end end for _, value in ipairs(lspft) do
if ft == value then
should_load = true
end
end
if should_load then if should_load then
for _, c in pairs(loaded) do for _, c in pairs(loaded) do
if client == c then if client == c then
-- loaded -- loaded
verbose(client, "already been loaded for", ft, loaded) trace(client, "already been loaded for", ft, loaded)
return return
end end
end end
@ -230,19 +240,36 @@ local function load_cfg(ft, client, cfg, loaded)
-- need to verify the lsp server is up -- need to verify the lsp server is up
end end
local function wait_lsp_startup(ft, retry) local function wait_lsp_startup(ft, retry, lsp_opts)
retry = retry or false retry = retry or false
local clients = vim.lsp.get_active_clients() or {} local clients = vim.lsp.get_active_clients() or {}
local loaded = {} local loaded = {}
for i = 1, 2 do for _ = 1, 2 do
for _, client in ipairs(clients) do for _, client in ipairs(clients) do
if client ~= nil then table.insert(loaded, client.name) end if client ~= nil then
table.insert(loaded, client.name)
end
end end
for _, lspclient in ipairs(servers) do for _, lspclient in ipairs(servers) do
if lsp_opts[lspclient] ~= nil and lsp_opts[lspclient].filetypes ~= nil then
if not vim.tbl_contains(lsp_opts[lspclient].filetypes, ft) then
trace("ft", ft, "disabled for", lspclient)
goto continue
end
end
local cfg = setups[lspclient] or default_cfg local cfg = setups[lspclient] or default_cfg
-- if user provides override values
-- if lsp_opts[lspclient] ~= nil and lsp_opts[lspclient] ~= nil then
-- local ret = vim.tbl_extend("force", cfg, lsp_opts[lspclient])
-- log(lsp_opts[lspclient].settings, cfg, ret)
-- end
load_cfg(ft, lspclient, cfg, loaded) load_cfg(ft, lspclient, cfg, loaded)
::continue::
end
if not retry or ft == nil then
return
end end
if not retry or ft == nil then return end
-- --
local timer = vim.loop.new_timer() local timer = vim.loop.new_timer()
local i = 0 local i = 0
@ -251,7 +278,7 @@ local function wait_lsp_startup(ft, retry)
i = i + 1 i = i + 1
if i > 5 or #clients > 0 then if i > 5 or #clients > 0 then
timer:close() -- Always close handles to avoid leaks. timer:close() -- Always close handles to avoid leaks.
verbose("active", #clients, i) trace("active", #clients, i)
_Loading = false _Loading = false
return true return true
end end
@ -260,18 +287,19 @@ local function wait_lsp_startup(ft, retry)
end end
end end
vim.cmd([[autocmd FileType * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
local function setup(user_opts) local function setup(user_opts)
verbose(debug.traceback())
if lspconfig == nil then log(user_opts)
error("lsp-config need installed and enabled") trace(debug.traceback())
user_opts = user_opts or _NgConfigValues -- incase setup was triggered from autocmd
if _Loading == true then
return return
end end
if _Loading == true then return end
local ft = vim.bo.filetype local ft = vim.bo.filetype
if ft == nil then ft = vim.api.nvim_buf_get_option(0, "filetype") end if ft == nil then
ft = vim.api.nvim_buf_get_option(0, "filetype")
end
if ft == nil or ft == "" then if ft == nil or ft == "" then
log("nil filetype") log("nil filetype")
@ -284,24 +312,32 @@ local function setup(user_opts)
} }
for i = 1, #disable_ft do for i = 1, #disable_ft do
if ft == disable_ft[i] then if ft == disable_ft[i] then
log("navigator disabled for ft", ft) trace("navigator disabled for ft", ft)
return return
end end
end end
local bufnr = vim.fn.bufnr() local bufnr = vim.fn.bufnr()
local uri = vim.uri_from_bufnr(bufnr) local uri = vim.uri_from_bufnr(bufnr)
log("loading for ft ", ft, uri)
if uri == 'file://' or uri == 'file:///' then if uri == 'file://' or uri == 'file:///' then
log("skip loading for ft ", ft, uri) log("skip loading for ft ", ft, uri)
return return
end end
log('setup', user_opts)
log("loading for ft ", ft, uri)
highlight.diagnositc_config_sign() highlight.diagnositc_config_sign()
highlight.add_highlight() highlight.add_highlight()
local lsp_opts = user_opts.lsp
_Loading = true _Loading = true
wait_lsp_startup(ft, retry) wait_lsp_startup(ft, retry, lsp_opts)
_Loading = false _Loading = false
-- if not _NgConfigValues.loaded then
-- vim.cmd([[autocmd FileType * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
-- _NgConfigValues.loaded = true
-- end
end end
return {setup = setup, cap = cap} return {setup = setup, cap = cap}

@ -1,9 +0,0 @@
-- local log = require "navigator.util".log
local M = {}
M.setup = function(cfg)
cfg = cfg or {}
require('navigator.lspclient.clients').setup(cfg)
require("navigator.lspclient.mapping").setup(cfg)
end
return M

@ -1,5 +1,7 @@
local log = require"navigator.util".log local log = require"navigator.util".log
local function set_keymap(...) vim.api.nvim_set_keymap(...) end local function set_keymap(...)
vim.api.nvim_set_keymap(...)
end
local event_hdlrs = { local event_hdlrs = {
{ev = "BufWritePre", func = "diagnostic.set_loclist({open_loclist = false})"}, {ev = "BufWritePre", func = "diagnostic.set_loclist({open_loclist = false})"},
@ -9,23 +11,31 @@ local event_hdlrs = {
} }
local key_maps = { local key_maps = {
{key = "gr", func = "references()"}, {mode = "i", key = "<M-k>", func = "signature_help()"}, {key = "gr", func = "references()"},
{key = "gs", func = "signature_help()"}, {key = "g0", func = "document_symbol()"}, {mode = "i", key = "<M-k>", func = "signature_help()"},
{key = "gW", func = "workspace_symbol()"}, {key = "<c-]>", func = "definition()"}, {key = "gs", func = "signature_help()"},
{key = "gD", func = "declaration()"}, {key = "g0", func = "document_symbol()"},
{key = "gW", func = "workspace_symbol()"},
{key = "<c-]>", func = "definition()"}, {key = "gD", func = "declaration()"},
{key = "gp", func = "require('navigator.definition').definition_preview()"}, {key = "gp", func = "require('navigator.definition').definition_preview()"},
{key = "gT", func = "require('navigator.treesitter').buf_ts()"}, {key = "gT", func = "require('navigator.treesitter').buf_ts()"},
{key = "GT", func = "require('navigator.treesitter').bufs_ts()"}, {key = "K", func = "hover()"}, {key = "GT", func = "require('navigator.treesitter').bufs_ts()"},
{key = "K", func = "hover()"},
{key = "ga", mode = "n", func = "code_action()"}, {key = "ga", mode = "n", func = "code_action()"},
{key = "ga", mode = "v", func = "range_code_action()"}, {key = "<Leader>re", func = "rename()"}, {key = "ga", mode = "v", func = "range_code_action()"},
{key = "<Leader>gi", func = "incoming_calls()"}, {key = "<Leader>go", func = "outgoing_calls()"}, {key = "<Leader>re", func = "rename()"},
{key = "gi", func = "implementation()"}, {key = "gt", func = "type_definition()"}, {key = "<Leader>gi", func = "incoming_calls()"},
{key = "<Leader>go", func = "outgoing_calls()"},
{key = "gi", func = "implementation()"},
{key = "gt", func = "type_definition()"},
{key = "gL", func = "diagnostic.show_line_diagnostics()"}, {key = "gL", func = "diagnostic.show_line_diagnostics()"},
{key = "gG", func = "require('navigator.diagnostics').show_diagnostic()"}, {key = "gG", func = "require('navigator.diagnostics').show_diagnostic()"},
{key = "]d", func = "diagnostic.goto_next()"}, {key = "[d", func = "diagnostic.goto_prev()"}, {key = "]d", func = "diagnostic.goto_next()"},
{key = "[d", func = "diagnostic.goto_prev()"},
{key = "]r", func = "require('navigator.treesitter').goto_next_usage()"}, {key = "]r", func = "require('navigator.treesitter').goto_next_usage()"},
{key = "[r", func = "require('navigator.treesitter').goto_previous_usage()"}, {key = "[r", func = "require('navigator.treesitter').goto_previous_usage()"},
{key = "<C-LeftMouse>", func = "definition()"}, {key = "g<LeftMouse>", func = "implementation()"} {key = "<C-LeftMouse>", func = "definition()"},
{key = "g<LeftMouse>", func = "implementation()"}
} }
local function set_mapping(user_opts) local function set_mapping(user_opts)
@ -35,7 +45,9 @@ local function set_mapping(user_opts)
local user_key = user_opts.keymaps or {} local user_key = user_opts.keymaps or {}
local bufnr = user_opts.bufnr or 0 local bufnr = user_opts.bufnr or 0
local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end local function buf_set_keymap(...)
vim.api.nvim_buf_set_keymap(bufnr, ...)
end
-- local function buf_set_option(...) -- local function buf_set_option(...)
-- vim.api.nvim_buf_set_option(bufnr, ...) -- vim.api.nvim_buf_set_option(bufnr, ...)
@ -71,23 +83,30 @@ local function set_mapping(user_opts)
for _, value in pairs(vim.lsp.buf_get_clients(0)) do for _, value in pairs(vim.lsp.buf_get_clients(0)) do
if value == nil or value.resolved_capabilities == nil then return end if value == nil or value.resolved_capabilities == nil then return end
if value.resolved_capabilities.document_formatting then doc_fmt = true end if value.resolved_capabilities.document_formatting then doc_fmt = true end
if value.resolved_capabilities.document_range_formatting then range_fmt = true end if value.resolved_capabilities.document_range_formatting then
range_fmt = true
end
end end
-- if user_opts.cap.document_formatting then -- if user_opts.cap.document_formatting then
if doc_fmt then if doc_fmt then
buf_set_keymap("n", "<space>ff", "<cmd>lua vim.lsp.buf.formatting()<CR>", opts) buf_set_keymap("n", "<space>ff", "<cmd>lua vim.lsp.buf.formatting()<CR>",
vim.cmd([[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting()]]) opts)
if _NgConfigValues.lsp.format_on_save then
vim.cmd([[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting()]])
end
end end
-- if user_opts.cap.document_range_formatting then -- if user_opts.cap.document_range_formatting then
if range_fmt then if range_fmt then
buf_set_keymap("v", "<space>ff", "<cmd>lua vim.lsp.buf.range_formatting()<CR>", opts) buf_set_keymap("v", "<space>ff",
"<cmd>lua vim.lsp.buf.range_formatting()<CR>", opts)
end end
end end
local function set_event_handler(user_opts) local function set_event_handler(user_opts)
user_opts = user_opts or {} user_opts = user_opts or {}
local file_types = "c,cpp,h,go,python,vim,sh,javascript,html,css,lua,typescript,rust" local file_types =
"c,cpp,h,go,python,vim,sh,javascript,html,css,lua,typescript,rust,javascriptreact,typescriptreact,json,yaml,kotlin,php,dart,nim,terraform"
-- local format_files = "c,cpp,h,go,python,vim,javascript,typescript" --html,css, -- local format_files = "c,cpp,h,go,python,vim,javascript,typescript" --html,css,
vim.api.nvim_command [[augroup nvim_lsp_autos]] vim.api.nvim_command [[augroup nvim_lsp_autos]]
vim.api.nvim_command [[autocmd!]] vim.api.nvim_command [[autocmd!]]
@ -99,8 +118,9 @@ local function set_event_handler(user_opts)
else else
f = "lua vim.lsp.buf." .. value.func f = "lua vim.lsp.buf." .. value.func
end end
local cmd = "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " .. value.ev .. local cmd =
" <buffer> silent! " .. f "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " ..
value.ev .. " <buffer> silent! " .. f
vim.api.nvim_command(cmd) vim.api.nvim_command(cmd)
end end
vim.api.nvim_command([[augroup END]]) vim.api.nvim_command([[augroup END]])
@ -109,12 +129,11 @@ end
local M = {} local M = {}
function M.setup(user_opts) function M.setup(user_opts)
user_opts = user_opts or {}
user_opts.cap = vim.lsp.protocol.make_client_capabilities()
set_mapping(user_opts) set_mapping(user_opts)
set_event_handler(user_opts) set_event_handler(user_opts)
local cap = user_opts.cap or {}
local cap = user_opts.cap or vim.lsp.protocol.make_client_capabilities()
if cap.call_hierarchy or cap.callHierarchy then if cap.call_hierarchy or cap.callHierarchy then
vim.lsp.handlers["callHierarchy/incomingCalls"] = vim.lsp.handlers["callHierarchy/incomingCalls"] =
require"navigator.hierarchy".incoming_calls_handler require"navigator.hierarchy".incoming_calls_handler
@ -122,12 +141,16 @@ function M.setup(user_opts)
require"navigator.hierarchy".outgoing_calls_handler require"navigator.hierarchy".outgoing_calls_handler
end end
vim.lsp.handlers["textDocument/references"] = require"navigator.reference".reference_handler vim.lsp.handlers["textDocument/references"] =
vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler require"navigator.reference".reference_handler
vim.lsp.handlers["textDocument/definition"] = require"navigator.definition".definition_handler vim.lsp.handlers["textDocument/codeAction"] =
require"navigator.codeAction".code_action_handler
vim.lsp.handlers["textDocument/definition"] =
require"navigator.definition".definition_handler
if cap.declaration then if cap.declaration then
vim.lsp.handlers["textDocument/declaration"] = require"navigator.definition".declaration_handler vim.lsp.handlers["textDocument/declaration"] =
require"navigator.definition".declaration_handler
end end
vim.lsp.handlers["textDocument/typeDefinition"] = vim.lsp.handlers["textDocument/typeDefinition"] =
@ -137,13 +160,15 @@ function M.setup(user_opts)
vim.lsp.handlers["textDocument/documentSymbol"] = vim.lsp.handlers["textDocument/documentSymbol"] =
require"navigator.symbols".document_symbol_handler require"navigator.symbols".document_symbol_handler
vim.lsp.handlers["workspace/symbol"] = require"navigator.symbols".workspace_symbol_handler vim.lsp.handlers["workspace/symbol"] =
require"navigator.symbols".workspace_symbol_handler
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.handlers["textDocument/publishDiagnostics"] =
require"navigator.diagnostics".diagnostic_handler require"navigator.diagnostics".diagnostic_handler
local hassig, sig = pcall(require, "lsp_signature") local hassig, sig = pcall(require, "lsp_signature")
if not hassig then if not hassig then
vim.lsp.handlers["textDocument/signatureHelp"] = require"navigator.signature".signature_handler vim.lsp.handlers["textDocument/signatureHelp"] =
require"navigator.signature".signature_handler
end end
-- vim.lsp.handlers["textDocument/hover"] = require 'navigator.hover'.hover_handler -- vim.lsp.handlers["textDocument/hover"] = require 'navigator.hover'.hover_handler

@ -4,16 +4,30 @@ local gutil = require "guihua.util"
local lsp = require "vim.lsp" local lsp = require "vim.lsp"
local api = vim.api local api = vim.api
local log = require"navigator.util".log local log = require"navigator.util".log
local verbose = require"navigator.util".verbose local trace = require"navigator.util".trace
local symbol_kind = require"navigator.lspclient.lspkind".symbol_kind local symbol_kind = require"navigator.lspclient.lspkind".symbol_kind
local cwd = vim.fn.getcwd(0) local cwd = vim.fn.getcwd(0)
cwd = gutil.add_pec(cwd) cwd = gutil.add_pec(cwd)
ts_nodes = {} ts_nodes = {}
ts_nodes_time = {} ts_nodes_time = {}
local ts_enabled, ts_locals = pcall(require, "nvim-treesitter.locals") local ts_enabled, _ = pcall(require, "nvim-treesitter.locals")
local calltree_enabled = require"navigator".config_values().treesitter_call_tree local calltree_enabled = require"navigator".config_values().treesitter_call_tree
-- extract symbol from range
local function get_symbol(text, range)
if range == nil then
return ""
end
return string.sub(text, range.start.character + 1, range['end'].character)
end
local function check_lhs(text, symbol)
local s = string.find(text, symbol)
local eq = string.find(text, '=') or 0
return s < eq
end
function M.lines_from_locations(locations, include_filename) function M.lines_from_locations(locations, include_filename)
local fnamemodify = (function(filename) local fnamemodify = (function(filename)
if include_filename then if include_filename then
@ -43,7 +57,9 @@ function M.symbols_to_items(result)
local kind = symbol_kind(item.kind) local kind = symbol_kind(item.kind)
item.name = result[i].name -- symbol name item.name = result[i].name -- symbol name
item.text = result[i].name item.text = result[i].name
if kind ~= nil then item.text = kind .. ": " .. item.text end if kind ~= nil then
item.text = kind .. ": " .. item.text
end
item.filename = vim.uri_to_fname(item.uri) item.filename = vim.uri_to_fname(item.uri)
item.display_filename = item.filename:gsub(cwd .. "/", "./", 1) item.display_filename = item.filename:gsub(cwd .. "/", "./", 1)
@ -52,7 +68,9 @@ function M.symbols_to_items(result)
end end
item.lnum = item.range.start.line + 1 item.lnum = item.range.start.line + 1
if item.containerName ~= nil then item.text = "" .. item.containerName .. item.text end if item.containerName ~= nil then
item.text = "" .. item.containerName .. item.text
end
table.insert(locations, item) table.insert(locations, item)
end end
end end
@ -65,9 +83,10 @@ local function extract_result(results_lsp)
if results_lsp then if results_lsp then
local results = {} local results = {}
for _, server_results in pairs(results_lsp) do for _, server_results in pairs(results_lsp) do
if server_results.result then vim.list_extend(results, server_results.result) end if server_results.result then
vim.list_extend(results, server_results.result)
end
end end
return results return results
end end
end end
@ -78,7 +97,9 @@ function M.check_capabilities(feature, client_id)
local supported_client = false local supported_client = false
for _, client in pairs(clients) do for _, client in pairs(clients) do
supported_client = client.resolved_capabilities[feature] supported_client = client.resolved_capabilities[feature]
if supported_client then goto continue end if supported_client then
goto continue
end
end end
::continue:: ::continue::
@ -114,15 +135,21 @@ 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 return nil end if not ts_enabled or not calltree_enabled then
return nil
end
local ts_func = require"navigator.treesitter".buf_func local ts_func = require"navigator.treesitter".buf_func
local bufnr = vim.uri_to_bufnr(uri) local bufnr = vim.uri_to_bufnr(uri)
local x = os.clock() local x = os.clock()
trace(ts_nodes)
if ts_nodes[uri] ~= nil then if ts_nodes[uri] ~= nil then
local t = ts_nodes_time[uri] local t = ts_nodes_time[uri]
local fname = vim.uri_to_fname(uri) local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname) local modified = vim.fn.getftime(fname)
if modified < t then return ts_nodes[uri] end if modified <= t then
trace(t, modified)
return ts_nodes[uri]
end
end end
local unload = false local unload = false
if not api.nvim_buf_is_loaded(bufnr) then if not api.nvim_buf_is_loaded(bufnr) then
@ -139,20 +166,24 @@ local function ts_functions(uri)
end end
ts_nodes[uri] = funcs ts_nodes[uri] = funcs
ts_nodes_time[uri] = os.time() ts_nodes_time[uri] = os.time()
verbose(funcs) trace(funcs, ts_nodes)
log(string.format("elapsed time: %.4f\n", os.clock() - x)) log(string.format("elapsed time: %.4f\n", os.clock() - x))
return funcs return funcs
end 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 return nil end if funcs == nil or range == nil then
return nil
end
local result = {} local result = {}
verbose(funcs, range) trace(funcs, range)
for _, value in pairs(funcs) do for _, value in pairs(funcs) do
local func_range = value.node_scope local func_range = value.node_scope
-- note treesitter is C style -- note treesitter is C style
if func_range and func_range.start.line + 1 <= range.start.line and func_range['end'].line + 1 >= if func_range and func_range.start.line <= range.start.line and func_range['end'].line >=
range['end'].line then table.insert(result, value) end range['end'].line then
table.insert(result, value)
end
end end
return result return result
end end
@ -168,7 +199,9 @@ function M.locations_to_items(locations)
-- items and locations may not matching -- items and locations may not matching
table.sort(locations, function(i, j) table.sort(locations, function(i, j)
if i.uri == j.uri then if i.uri == j.uri then
if i.range and i.range.start then return i.range.start.line < j.range.start.line end if i.range and i.range.start then
return i.range.start.line < j.range.start.line
end
return false return false
else else
return i.uri < j.uri return i.uri < j.uri
@ -186,9 +219,10 @@ function M.locations_to_items(locations)
item.rpath = util.get_relative_path(cwd, item.filename) item.rpath = util.get_relative_path(cwd, item.filename)
table.insert(items, item) table.insert(items, item)
width = math.max(width, #item.text) width = math.max(width, #item.text)
item.symbol_name = get_symbol(item.text, item.range)
item.lhs = check_lhs(item.text, item.symbol_name)
end end
return items, width + 24 -- TODO handle long line?
return items, width + 15
end end
function M.symbol_to_items(locations) function M.symbol_to_items(locations)
@ -201,7 +235,9 @@ function M.symbol_to_items(locations)
-- items and locations may not matching -- items and locations may not matching
table.sort(locations, function(i, j) table.sort(locations, function(i, j)
if i.uri == j.uri then if i.uri == j.uri then
if i.range and i.range.start then return i.range.start.line < j.range.start.line end if i.range and i.range.start then
return i.range.start.line < j.range.start.line
end
return false return false
else else
return i.uri < j.uri return i.uri < j.uri

@ -3,7 +3,7 @@ local log = util.log
local lsphelper = require "navigator.lspwrapper" local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui" local gui = require "navigator.gui"
local lsp = require "navigator.lspwrapper" local lsp = require "navigator.lspwrapper"
local verbose = require"navigator.util".verbose local trace = require"navigator.util".trace
-- local log = util.log -- local log = util.log
-- local partial = util.partial -- local partial = util.partial
-- local cwd = vim.fn.getcwd(0) -- local cwd = vim.fn.getcwd(0)
@ -16,7 +16,7 @@ local function ref_hdlr(err, api, locations, num, bufnr)
local opts = {} local opts = {}
-- log("arg1", arg1) -- log("arg1", arg1)
-- log(api) -- log(api)
-- log(locations) trace(locations)
-- log("num", num) -- log("num", num)
-- log("bfnr", bufnr) -- log("bfnr", bufnr)
if err ~= nil then if err ~= nil then
@ -24,7 +24,6 @@ local function ref_hdlr(err, api, locations, num, bufnr)
return return
end end
if type(locations) ~= 'table' then if type(locations) ~= 'table' then
log("arg1", arg1)
log(api) log(api)
log(locations) log(locations)
log("num", num) log("num", num)
@ -36,10 +35,11 @@ local function ref_hdlr(err, api, locations, num, bufnr)
return return
end end
local items, width = locations_to_items(locations) local items, width = locations_to_items(locations)
local ft = vim.api.nvim_buf_get_option(bufnr, "ft") local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
local wwidth = vim.api.nvim_get_option("columns") local wwidth = vim.api.nvim_get_option("columns")
width = math.min(width + 24 or 120, math.floor(wwidth * 0.8)) width = math.min(width + 30, 120, math.floor(wwidth * 0.8))
gui.new_list_view({items = items, ft = ft, width = width, api = "Reference"}) gui.new_list_view({items = items, ft = ft, width = width, api = "Reference"})
end end

@ -0,0 +1,149 @@
local log = require"guihua.log".info
local trace = require"guihua.log".trace
local M = {}
local clone = require'guihua.util'.clone
local function filename(url)
return url:match("^.+/(.+)$") or url
end
local function extension(url)
local ext = url:match("^.+(%..+)$") or "txt"
return string.sub(ext, 2)
end
local function get_pads(win_width, text, postfix)
local margin = win_width - #text - #postfix
if margin < 0 then
if #postfix > 1 then
text = text:sub(1, #text - 20)
end
end
local sz = #text
if sz < 30 then
sz = 30
end
local space = ''
local i = math.floor((sz + 10) / 10)
i = i * 10 - #text
space = string.rep(' ', i)
trace(text, i, postfix, win_width)
return space
end
function M.prepare_for_render(items, opts)
opts = opts or {}
if items == nil or #items < 1 then
print("no item found or empty fields")
return
end
local item = clone(items[1])
local display_items = {item}
local last_summary_idx = 1
local total_ref_in_file = 1
local icon = ""
local lspapi = opts.api or ""
local ok, devicons = pcall(require, "nvim-web-devicons")
if ok then
local fn = filename(items[1].filename)
local ext = extension(fn)
icon = devicons.get_icon(fn, ext) or icon
end
local call_by_presented = false
opts.width = opts.width or 100
local win_width = opts.width - 2 -- buf
for i = 1, #items do
if items[i].call_by and #items[i].call_by > 0 then
call_by_presented = true
end
end
for i = 1, #items do
local space = ''
local lspapi_display = lspapi
items[i].symbol_name = items[i].symbol_name or "" -- some LSP API does not have range for this
if last_summary_idx == 1 then
lspapi_display = items[i].symbol_name .. ' ' .. lspapi_display
trace(items[1], lspapi_display)
end
-- trace(items[i], items[i].filename, last_summary_idx, display_items[last_summary_idx].filename)
if items[i].filename == display_items[last_summary_idx].filename then
space = get_pads(opts.width, icon .. ' ' .. display_items[last_summary_idx].display_filename,
lspapi_display .. ' 12')
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)
total_ref_in_file = total_ref_in_file + 1
else
lspapi_display = lspapi
item = clone(items[i])
space = get_pads(opts.width, icon .. ' ' .. item.display_filename, lspapi_display .. ' 12')
item.text = string.format("%s %s%s%s 1", icon, item.display_filename, space, lspapi_display)
trace(item.text)
table.insert(display_items, item)
total_ref_in_file = 1
last_summary_idx = #display_items
end
-- content of code lines
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 = ""
if item.lhs then
call_by = '📝 '
end
item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '')
if item.call_by ~= nil and #item.call_by > 0 then
trace("call_by:", #item.call_by)
for _, value in pairs(item.call_by) do
if value.node_text then
local txt = value.node_text:gsub('%s*[%[%(%{]*%s*$', '')
local endwise = '{}'
if value.type == 'method' or value.type == 'function' then
endwise = '()'
call_by = ''
end
if #call_by > 8 then
call_by = call_by .. ''
end
call_by = call_by .. 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)
space = ' '
else
local remain = win_width - #item.text - #call_by
log("remain", remain)
space = string.rep(' ', remain)
end
end
item.text = item.text .. space .. call_by
end
local tail = display_items[#display_items].text
if tail ~= item.text then -- deduplicate
trace(item.text)
trace(item.call_by)
table.insert(display_items, item)
end
end
-- display_items[last_summary_idx].text=string.format("%s [%i]", display_items[last_summary_idx].filename,
-- total_ref_in_file)
return display_items
end
return M

@ -55,8 +55,9 @@ function M.workspace_symbols(opts)
-- result_lsp -- result_lsp
local result = {} local result = {}
for i = 1, #results_lsp do for i = 1, #results_lsp do
if results_lsp[i] ~= nil and results_lsp[i].result ~= nil and if results_lsp[i] ~= nil and results_lsp[i].result ~= nil and #results_lsp[i].result > 0 then
#results_lsp[i].result > 0 then result = results_lsp[i].result end result = results_lsp[i].result
end
end end
local items = symbols_to_items(result) local items = symbols_to_items(result)
@ -115,23 +116,16 @@ function M.document_symbol_handler(err, _, result, _, bufnr)
child.uri = uri child.uri = uri
child.lnum = c.range.start.line + 1 child.lnum = c.range.start.line + 1
child.detail = c.detail or "" child.detail = c.detail or ""
child.text = "  [" .. ckind .. "] " .. child.detail .. " " .. child.text = "  [" .. ckind .. "] " .. child.detail .. " " .. child.name
child.name
table.insert(locations, child) table.insert(locations, child)
end end
end end
end end
local ft = vim.api.nvim_buf_get_option(bufnr, "ft") local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
-- verbose(locations) -- trace(locations)
-- local items = locations_to_items(locations) -- local items = locations_to_items(locations)
gui.new_list_view({ gui.new_list_view({items = locations, prompt = true, rawdata = true, ft = ft, api = ""})
items = locations,
prompt = true,
rawdata = true,
ft = ft,
api = ""
})
-- if locations == nil or vim.tbl_isempty(locations) then -- if locations == nil or vim.tbl_isempty(locations) then
-- print "References not found" -- print "References not found"
@ -178,13 +172,7 @@ function M.workspace_symbol_handler(err, _, result, _, bufnr)
-- local items = locations_to_items(locations) -- local items = locations_to_items(locations)
local ft = vim.api.nvim_buf_get_option(bufnr, "ft") local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({ gui.new_list_view({items = items, prompt = true, ft = ft, rowdata = true, api = ""})
items = items,
prompt = true,
ft = ft,
rowdata = true,
api = ""
})
-- if locations == nil or vim.tbl_isempty(locations) then -- if locations == nil or vim.tbl_isempty(locations) then
-- print "References not found" -- print "References not found"

@ -2,7 +2,9 @@ local gui = require "navigator.gui"
local ok, ts_locals = pcall(require, "nvim-treesitter.locals") local ok, ts_locals = pcall(require, "nvim-treesitter.locals")
if not ok then error("treesitter not installed") end if not ok then
error("treesitter not installed")
end
local parsers = require "nvim-treesitter.parsers" local parsers = require "nvim-treesitter.parsers"
local ts_utils = require "nvim-treesitter.ts_utils" local ts_utils = require "nvim-treesitter.ts_utils"
@ -13,7 +15,7 @@ local M = {}
local cwd = vim.fn.getcwd(0) local cwd = vim.fn.getcwd(0)
local log = require"navigator.util".log local log = require"navigator.util".log
local verbose = require"navigator.util".verbose local trace = require"navigator.util".trace
local match_kinds = { local match_kinds = {
var = "", -- "👹", -- Vampaire var = "", -- "👹", -- Vampaire
@ -69,44 +71,78 @@ local function prepare_node(node, kind)
if node.node then if node.node then
table.insert(matches, {kind = get_icon(kind), def = node.node, type = kind}) table.insert(matches, {kind = get_icon(kind), def = node.node, type = kind})
else else
for name, item in pairs(node) do vim.list_extend(matches, prepare_node(item, name)) end for name, item in pairs(node) do
vim.list_extend(matches, prepare_node(item, name))
end
end end
return matches return matches
end end
local function get_var_context(source) local function get_scope(type, source)
local sbl, sbc, sel, sec = source:range() local sbl, sbc, sel, sec = source:range()
local current = source local current = source
local result = current local result = current
local next = ts_utils.get_next_node(source) local next = ts_utils.get_next_node(source)
local parent = current:parent() local parent = current:parent()
trace(source:type(), source:range(), parent)
if type == 'method' or type == 'function' and parent ~= nil then
trace(parent:type(), parent:range())
-- a function name
if parent:type() == 'function_name' then
-- up one level
return parent:parent(), true
end
if parent:type() == 'function_name_field' then
return parent:parent():parent(), true
end
return parent, true
end
if next == nil or parent == nil then return end if type == "var" and next ~= nil then
if next:type() == "function" or next:type() == "arrow_function" then if next:type() == "function" or next:type() == "arrow_function" or next:type() ==
log(current:type(), current:range()) "function_definition" then
return parent trace(current:type(), current:range())
else return next, true
return source elseif parent:type() == 'function_declaration' then
return parent, true
else
trace(source, source:type())
return source, false
end
else -- M.fun1 = function() end
-- lets work up and see next node
local n = source
for i = 1, 4, 1 do
if n == nil or n:parent() == nil then
break
end
n = n:parent()
next = ts_utils.get_next_node(n)
if next ~= nil and next:type() == 'function_definition' then
return next, true
end
end
end
if source:type() == "type_identifier" then
return source:parent(), true
end end
-- while current ~= nil do
-- log(current:type(), current:range())
-- if current:type() == "variable_declarator" or current:type() == "function_declaration" then
-- return current
-- end
-- -- local bl, bc, el, ec = current:range()
-- -- if bl == sbl and bc == sbc and el >= sel and ec >= sec then result = current end
-- current = current:parent()
-- end
-- log(current)
end end
local function get_smallest_context(source) local function get_smallest_context(source)
local scopes = ts_locals.get_scopes() local scopes = ts_locals.get_scopes()
for key, value in pairs(scopes) do
trace(key, value)
end
local current = source local current = source
while current ~= nil and not vim.tbl_contains(scopes, current) do current = current:parent() end while current ~= nil and not vim.tbl_contains(scopes, current) do
log(current) current = current:parent()
if current ~= nil then return current end end
return get_var_context(source) if current ~= nil then
return current, true
end
-- if source:type() == "identifier" then return get_var_context(source) end -- if source:type() == "identifier" then return get_var_context(source) end
end end
@ -115,7 +151,9 @@ local lsp_reference = require"navigator.dochighlight".goto_adjent_reference
function M.goto_adjacent_usage(bufnr, delta) function M.goto_adjacent_usage(bufnr, delta)
local opt = {forward = true} local opt = {forward = true}
-- log(delta) -- log(delta)
if delta < 0 then opt = {forward = false} end if delta < 0 then
opt = {forward = false}
end
bufnr = bufnr or api.nvim_get_current_buf() bufnr = bufnr or api.nvim_get_current_buf()
local node_at_point = ts_utils.get_node_at_cursor() local node_at_point = ts_utils.get_node_at_cursor()
if not node_at_point then if not node_at_point then
@ -136,13 +174,20 @@ function M.goto_adjacent_usage(bufnr, delta)
ts_utils.goto_node(usages[target_index]) ts_utils.goto_node(usages[target_index])
end end
function M.goto_next_usage(bufnr) return M.goto_adjacent_usage(bufnr, 1) end function M.goto_next_usage(bufnr)
function M.goto_previous_usage(bufnr) return M.goto_adjacent_usage(bufnr, -1) end return M.goto_adjacent_usage(bufnr, 1)
end
function M.goto_previous_usage(bufnr)
return M.goto_adjacent_usage(bufnr, -1)
end
local function get_all_nodes(bufnr, filter, summary) local function get_all_nodes(bufnr, filter, summary)
trace(bufnr, filter, summary)
bufnr = bufnr or 0 bufnr = bufnr or 0
summary = summary or false summary = summary or false
if not parsers.has_parser() then print("ts not loaded") end if not parsers.has_parser() then
print("ts not loaded")
end
local fname = vim.fn.expand("%:p:f") local fname = vim.fn.expand("%:p:f")
local uri = vim.uri_from_fname(fname) local uri = vim.uri_from_fname(fname)
if bufnr ~= 0 then if bufnr ~= 0 then
@ -186,27 +231,56 @@ local function get_all_nodes(bufnr, filter, summary)
for _, node in ipairs(nodes) do for _, node in ipairs(nodes) do
item.kind = node.kind item.kind = node.kind
item.type = node.type item.type = node.type
if filter ~= nil and not filter[item.type] then
trace(item.type, item.kind)
goto continue
end
local tsdata = node.def local tsdata = node.def
log(item.type, tsdata:type()) if node.def == nil then
if node.def == nil then goto continue end goto continue
end
item.node_text = ts_utils.get_node_text(tsdata, bufnr)[1] item.node_text = ts_utils.get_node_text(tsdata, bufnr)[1]
local scope, is_func
local scope = get_smallest_context(tsdata) if summary then
scope, is_func = get_scope(item.type, tsdata)
else
scope, is_func = get_smallest_context(tsdata)
end
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
item.node_text = ts_utils.get_node_text(parent, bufnr)[1]
log(parent:type(), item.node_text)
end
end
log(item.node_text, item.kind, item.type)
if scope ~= nil then if scope ~= nil then
-- it is strange.. -- it is strange..
log(item.node_text, item.kind, item.type) if not is_func and summary then
goto continue
end
item.node_scope = ts_utils.node_to_lsp_range(scope) item.node_scope = ts_utils.node_to_lsp_range(scope)
end end
if filter ~= nil and not filter[item.type] then goto continue end
if summary then if summary then
if item.node_scope ~= nil then table.insert(all_nodes, item) end if item.node_scope ~= nil then
table.insert(all_nodes, item)
end
log(item.type, tsdata:type(), item.node_text, item.kind, item.node_text, item.node_scope)
goto continue goto continue
end end
item.range = ts_utils.node_to_lsp_range(tsdata) item.range = ts_utils.node_to_lsp_range(tsdata)
local start_line_node, _, _ = tsdata:start() local start_line_node, _, _ = tsdata:start()
if item.node_text == "_" then goto continue end 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, item.full_text = vim.trim(api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1,
false)[1] or "") false)[1] or "")
item.uri = uri item.uri = uri
@ -217,16 +291,20 @@ local function get_all_nodes(bufnr, filter, summary)
item.lnum = item.lnum + 1 item.lnum = item.lnum + 1
item.col = item.col + 1 item.col = item.col + 1
local indent = "" local indent = ""
if #parents > 1 then indent = string.rep(" ", #parents - 1) .. "" end if #parents > 1 then
indent = string.rep(" ", #parents - 1) .. ""
end
item.text = string.format(" %s %s%-10s\t %s", item.kind, indent, item.node_text, item.text = string.format(" %s %s%-10s\t %s", item.kind, indent, item.node_text,
item.full_text) item.full_text)
if #item.text > length then length = #item.text end if #item.text > length then
length = #item.text
end
table.insert(all_nodes, item) table.insert(all_nodes, item)
::continue:: ::continue::
end end
end end
verbose(all_nodes) trace(all_nodes)
return all_nodes, length return all_nodes, length
end end
@ -244,20 +322,36 @@ function M.buf_func(bufnr)
["class"] = true, ["class"] = true,
["type"] = true ["type"] = true
}, true) }, true)
table.sort(all_nodes, function(i, j) if #all_nodes < 1 then
if i.range and j.range then log("no node found for ", bufnr)
if i.range.start.line == j.range.start.line then return
return i.range['end'].line < j.range['end'].line end
else
return i.range.start.line < j.range.start.line
end
end
return false
end)
verbose(all_nodes, width) if all_nodes[1].node_scope then
table.sort(all_nodes, function(i, j)
if i.node_scope and j.node_scope then
if i.node_scope['end'].line == j.node_scope['end'].line then
return i.node_scope.start.line > j.node_scope.start.line
else
return i.node_scope['end'].line < j.node_scope['end'].line
end
end
return false
end)
else
table.sort(all_nodes, function(i, j)
if i.range and j.range then
if i.range['end'].line == j.range['end'].line then
return i.range.start.line > j.range.start.line
else
return i.range['end'].line < j.range['end'].line
end
end
return false
end)
end
return all_nodes return all_nodes, width
end end
@ -295,14 +389,16 @@ function M.bufs_ts()
if vim.api.nvim_buf_is_loaded(buf) then if vim.api.nvim_buf_is_loaded(buf) then
local all_nodes, length = get_all_nodes(buf) local all_nodes, length = get_all_nodes(buf)
if all_nodes ~= nil then if all_nodes ~= nil then
if length > max_length then max_length = length end if length > max_length then
max_length = length
end
vim.list_extend(ts_opened, all_nodes) vim.list_extend(ts_opened, all_nodes)
end end
end end
end end
end end
if #ts_opened > 1 then if #ts_opened > 1 then
verbose(ts_opened) trace(ts_opened)
local ft = vim.api.nvim_buf_get_option(0, "ft") local ft = vim.api.nvim_buf_get_option(0, "ft")
gui.new_list_view({ gui.new_list_view({

@ -41,7 +41,9 @@ end
local function getDir(path) local function getDir(path)
local data = {} local data = {}
local len = #path local len = #path
if len <= 1 then return nil end if len <= 1 then
return nil
end
local last_index = 1 local last_index = 1
for i = 2, len do for i = 2, len do
local cur_char = path:sub(i, i) local cur_char = path:sub(i, i)
@ -60,17 +62,25 @@ function M.get_relative_path(base_path, my_path)
local base_len = #base_data local base_len = #base_data
local my_len = #my_data local my_len = #my_data
if base_len > my_len then return my_path end if base_len > my_len then
return my_path
end
if base_data[1] ~= my_data[1] then return my_path end if base_data[1] ~= my_data[1] then
return my_path
end
local cur = 0 local cur = 0
for i = 1, base_len do for i = 1, base_len do
if base_data[i] ~= my_data[i] then break end if base_data[i] ~= my_data[i] then
break
end
cur = i cur = i
end end
local data = "" local data = ""
for i = cur + 1, my_len do data = data .. my_data[i] .. "/" end for i = cur + 1, my_len do
data = data .. my_data[i] .. "/"
end
data = data .. M.get_base(my_path) data = data .. M.get_base(my_path)
return data return data
end end
@ -81,23 +91,28 @@ M._log = require("guihua.log").new({level = default_config.level}, true)
-- add log to you lsp.log -- add log to you lsp.log
M.log = M._log.info M.log = M._log.info
M.verbose = M._log.debug M.trace = M._log.trace
M.error = M._log.error
function M.fmt(...) M._log.fmt_info(...) end function M.fmt(...)
M._log.fmt_info(...)
end
function M.split(inputstr, sep) function M.split(inputstr, sep)
if sep == nil then sep = "%s" end if sep == nil then
sep = "%s"
end
local t = {} local t = {}
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do table.insert(t, str) end for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
table.insert(t, str)
end
return t return t
end end
function M.trim_space(s) return s:match("^%s*(.-)%s*$") end
function M.quickfix_extract(line) function M.quickfix_extract(line)
-- check if it is a line of file pos been selected -- check if it is a line of file pos been selected
local split = M.split local split = M.split
line = M.trim_space(line) line = vim.trim(line)
local sep = split(line, " ") local sep = split(line, " ")
if #sep < 2 then if #sep < 2 then
M.log(line) M.log(line)
@ -130,7 +145,9 @@ function M.getArgs(inputstr)
return cmd, t return cmd, t
end end
function M.p(t) print(vim.inspect(t)) end function M.p(t)
print(vim.inspect(t))
end
function M.printError(msg) function M.printError(msg)
vim.cmd("echohl ErrorMsg") vim.cmd("echohl ErrorMsg")
@ -148,14 +165,18 @@ function M.open_log()
vim.cmd("edit " .. path) vim.cmd("edit " .. path)
end end
function table.pack(...) return {n = select("#", ...), ...} end function table.pack(...)
return {n = select("#", ...), ...}
end
function M.show(...) function M.show(...)
local string = "" local string = ""
local args = table.pack(...) local args = table.pack(...)
for i = 1, args.n do string = string .. tostring(args[i]) .. "\t" end for i = 1, args.n do
string = string .. tostring(args[i]) .. "\t"
end
return string .. "\n" return string .. "\n"
end end
@ -165,12 +186,35 @@ function M.split2(s, sep)
sep = sep or " " sep = sep or " "
local pattern = string.format("([^%s]+)", sep) local pattern = string.format("([^%s]+)", sep)
string.gsub(s, pattern, function(c) fields[#fields + 1] = c end) string.gsub(s, pattern, function(c)
fields[#fields + 1] = c
end)
return fields return fields
end end
M.open_file = function(filename) vim.api.nvim_command(string.format("e! %s", filename)) end function M.trim_and_pad(txt)
local len = #txt
if len <= 1 then
return
end
local tab_en = txt[1] == '\t' or false
txt = vim.trim(txt)
if tab_en then
if len - txt > 2 then
return ' ' .. txt
end
if len - txt > 0 then
return ' ' .. txt
end
end
local rep = math.min(12, len - #txt)
return string.rep(' ', rep / 4) .. txt
end
M.open_file = function(filename)
vim.api.nvim_command(string.format("e! %s", filename))
end
M.open_file_at = function(filename, line, col) M.open_file_at = function(filename, line, col)
vim.api.nvim_command(string.format("e! +%s %s", line, filename)) vim.api.nvim_command(string.format("e! +%s %s", line, filename))
@ -178,13 +222,27 @@ M.open_file_at = function(filename, line, col)
vim.api.nvim_command(string.format("normal! %dl", col - 1)) vim.api.nvim_command(string.format("normal! %dl", col - 1))
end end
function M.exists(var) for k, _ in pairs(_G) do if k == var then return true end end end function M.exists(var)
for k, _ in pairs(_G) do
if k == var then
return true
end
end
end
function M.partial(func, arg) return (function(...) return func(arg, ...) end) end function M.partial(func, arg)
return (function(...)
return func(arg, ...)
end)
end
local exclude_ft = {"scrollbar", "help", "NvimTree"} local exclude_ft = {"scrollbar", "help", "NvimTree"}
function M.exclude(fname) function M.exclude(fname)
for i = 1, #exclude_ft do if string.find(fname, exclude_ft[i]) then return true end end for i = 1, #exclude_ft do
if string.find(fname, exclude_ft[i]) then
return true
end
end
return false return false
end end
@ -196,7 +254,9 @@ local api = vim.api
local bufs local bufs
function M.set_virt_eol(bufnr, lnum, chunks, priority, id) function M.set_virt_eol(bufnr, lnum, chunks, priority, id)
if nss == nil then nss = api.nvim_create_namespace("navigator_search") end if nss == nil then
nss = api.nvim_create_namespace("navigator_search")
end
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
bufs[bufnr] = true bufs[bufnr] = true
-- id may be nil -- id may be nil
@ -205,7 +265,9 @@ function M.set_virt_eol(bufnr, lnum, chunks, priority, id)
end end
function M.clear_buf(bufnr) function M.clear_buf(bufnr)
if not bufnr then return end if not bufnr then
return
end
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
if bufs[bufnr] then if bufs[bufnr] then
if api.nvim_buf_is_valid(bufnr) then if api.nvim_buf_is_valid(bufnr) then
@ -217,7 +279,9 @@ function M.clear_buf(bufnr)
end end
function M.clear_all_buf() function M.clear_all_buf()
for bufnr in pairs(bufs) do M.clear_buf(bufnr) end for bufnr in pairs(bufs) do
M.clear_buf(bufnr)
end
bufs = {} bufs = {}
end end

Loading…
Cancel
Save