diff --git a/README.md b/README.md
index 06c526f..9571ab0 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,67 @@
# 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 *browser* in closure.js
+- The first reference of browser is an assigement, an emoji of 📝 indicates the value changed in this line. In many
+cases, we search for reference to find out where the value changed.
+- 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:
-- 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
-- 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
+
- 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
+
- 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
+- 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
-After installed a handful of lsp plugins, I still got ~800 loc for lsp and treesitter and still increasing because I need
-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.
+I'd like to go beyond what the system is providing.
# 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)
- [lspsaga](https://github.com/glepnir/lspsaga.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
+Require nvim-0.5.0 (a.k.a nightly)
+
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
Plug 'neovim/nvim-lspconfig'
@@ -42,6 +85,8 @@ Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua'
```
+Note: Highly recommened: 'nvim-treesitter/nvim-treesitter'
+
Packer
```lua
@@ -79,17 +124,56 @@ lua < or `:q!` to kill the floating window
- (or \, \) to move
-- \ to open location or apply code actions
+- \ or \ to open location or apply code actions. Note: \ might be binded in insert mode by other plugins
## Configuration
@@ -121,8 +205,7 @@ colorscheme: [aurora](https://github.com/ray-x/aurora)
### Reference
-![reference](https://github.com/ray-x/files/blob/master/img/navigator/ref.gif?raw=true)
-
+Pls check first part of README
### 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)
-# Current symbol highlight and jump backword/forward between symbols
+# Current symbol highlight and jump backward/forward between symbols
Document highlight provided by LSP.
Jump between symbols between symbols with treesitter (with `]r` and `[r`)
@@ -150,7 +233,7 @@ Show diagnostic in all buffers
### 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
@@ -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)
-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 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
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
@@ -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)
-### 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)
@@ -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)
+# Break changes and known issues
+[known issues I am working on](https://github.com/ray-x/navigator.lua/issues/1)
+
# Todo
- Early phase, bugs expected, PR and suggestions are welcome
diff --git a/lua/navigator.lua b/lua/navigator.lua
index e70fe10..c0adf2e 100644
--- a/lua/navigator.lua
+++ b/lua/navigator.lua
@@ -2,7 +2,8 @@ local M = {}
_NgConfigValues = {
debug = false, -- log output not implemented
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,
on_attach = nil,
-- function(client, bufnr)
@@ -11,8 +12,15 @@ _NgConfigValues = {
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",
- 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()")
@@ -20,26 +28,36 @@ vim.cmd("command! -nargs=0 LspRestart call v:lua.reload_lsp()")
local extend_config = function(opts)
opts = opts or {}
- if next(opts) == nil then return end
+ if next(opts) == nil then
+ return
+ end
for key, value in pairs(opts) do
- if _NgConfigValues[key] == nil then
- error(string.format("[] Key %s not valid", key))
- return
- end
- if type(M.config_values[key]) == "table" then
- for k, v in pairs(value) do _NgConfigValues[key][k] = v end
+ -- if _NgConfigValues[key] == nil then
+ -- error(string.format("[] Key %s not valid", key))
+ -- return
+ -- end
+ if type(_NgConfigValues[key]) == "table" then
+ for k, v in pairs(value) do
+ _NgConfigValues[key][k] = v
+ end
else
_NgConfigValues[key] = value
end
end
end
-M.config_values = function() return _NgConfigValues end
+M.config_values = function()
+ return _NgConfigValues
+end
M.setup = function(cfg)
extend_config(cfg)
+ -- local log = require"navigator.util".log
+ -- log(debug.traceback())
+ -- log(cfg, _NgConfigValues)
-- 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.definition")
require("navigator.hierarchy")
@@ -49,6 +67,10 @@ M.setup = function(cfg)
vim.cmd [[autocmd CursorHold,CursorHoldI * lua require'navigator.codeAction'.code_action_prompt()]]
end
-- 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
return M
diff --git a/lua/navigator/codeAction.lua b/lua/navigator/codeAction.lua
index 33a7ad0..b6be639 100644
--- a/lua/navigator/codeAction.lua
+++ b/lua/navigator/codeAction.lua
@@ -1,6 +1,6 @@
local util = require "navigator.util"
local log = util.log
-local verbose = util.verbose
+local trace = util.trace
local code_action = {}
local gui = require "navigator.gui"
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)
end
- verbose(action_chosen)
+ trace(action_chosen)
end
gui.new_list_view {
diff --git a/lua/navigator/gui.lua b/lua/navigator/gui.lua
index b4e2fcf..9bccbe7 100644
--- a/lua/navigator/gui.lua
+++ b/lua/navigator/gui.lua
@@ -3,29 +3,10 @@ local ListView = require "guihua.listview"
local TextView = require "guihua.textview"
local util = require "navigator.util"
local log = require"navigator.util".log
-local verbose = require"navigator.util".verbose
+local trace = require"navigator.util".trace
local api = vim.api
-function M.new_preview(opts)
- 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
+local top_center = require"guihua.location".top_center
function M._preview_location(opts) -- location, width, pos_x, pos_y
local uri = opts.location.uri
@@ -34,7 +15,9 @@ function M._preview_location(opts) -- location, width, pos_x, pos_y
return
end
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
@@ -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 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 = {
syntax = syntax,
width = opts.width,
height = display_range['end'].line - display_range.start.line + 1,
- pos_x = opts.offset_x or 0,
- pos_y = opts.offset_y or 10,
+ preview_height = opts.preview_height,
+ pos_x = opts.offset_x,
+ pos_y = opts.offset_y,
range = opts.range,
display_range = display_range,
uri = uri,
@@ -72,23 +58,44 @@ function M._preview_location(opts) -- location, width, pos_x, pos_y
}
-- win_opts.items = contents
win_opts.hl_line = opts.lnum - display_range.start.line
- if win_opts.hl_line < 0 then win_opts.hl_line = 1 end
- verbose(opts.lnum, opts.range.start.line, win_opts.hl_line)
- local w = M.new_preview(win_opts)
-
+ if win_opts.hl_line < 0 then
+ win_opts.hl_line = 1
+ 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
end
function M.preview_uri(opts) -- uri, width, line, col, offset_x, offset_y
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}}}
-- TODO: preview height
- loc.range["end"] = {line = opts.lnum + 12}
+ loc.range["end"] = {line = opts.lnum + opts.preview_height}
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)
end
@@ -99,58 +106,94 @@ function M.new_list_view(opts)
local data = {}
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 prompt = opts.prompt or false
- opts.width = width
if opts.rawdata then
data = items
else
- data = require"guihua.util".prepare_for_render(items, opts)
+ data = require"navigator.render".prepare_for_render(items, opts)
end
+ 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 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({
- loc = "top_center",
+ loc = loc,
prompt = prompt,
relative = opts.relative,
style = opts.style,
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',
-- data = display_data,
data = data,
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]
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)
end
end,
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]
- verbose("on move", pos, l)
- verbose("on move", pos, l.text or l, l.uri, l.filename)
+ trace("on move", pos, l)
+ trace("on move", pos, l.text or l, l.uri, l.filename)
-- 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({
uri = l.uri,
width = width,
+ height = lheight, -- this is to cal offset
+ preview_height = pheight,
lnum = l.lnum,
col = l.col,
range = l.range,
offset_x = 0,
offset_y = offset_y,
- border = "double"
+ border = border
})
end
})
diff --git a/lua/navigator/hierarchy.lua b/lua/navigator/hierarchy.lua
index 2186b62..dcc4a49 100644
--- a/lua/navigator/hierarchy.lua
+++ b/lua/navigator/hierarchy.lua
@@ -6,25 +6,22 @@ local lsphelper = require "navigator.lspwrapper"
local cwd = vim.fn.getcwd(0)
local M = {}
-local function call_hierarchy_handler(direction, err, _, result, _, _,
- error_message)
+local function call_hierarchy_handler(direction, err, _, result, _, _, error_message)
-- log('call_hierarchy')
- assert(#vim.lsp.buf_get_clients() > 0,
- "Must have a client running to use lsp_tags")
+ assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
if err ~= nil then
+ log("dir", direction, "result", result, "err", err)
print("ERROR: " .. error_message)
return
end
- -- log("dir", direction, "result", result)
local items = {}
for _, call_hierarchy_call in pairs(result) do
local call_hierarchy_item = call_hierarchy_call[direction]
local kind = ' '
if call_hierarchy_item.kind then
- kind = require'navigator.lspclient.lspkind'.symbol_kind(
- call_hierarchy_item.kind) .. ' '
+ kind = require'navigator.lspclient.lspkind'.symbol_kind(call_hierarchy_item.kind) .. ' '
end
for _, range in pairs(call_hierarchy_call.fromRanges) do
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_to = partial(call_hierarchy_handler, "to")
-local function incoming_calls_handler(bang, err, method, result, client_id,
- bufnr)
- assert(#vim.lsp.buf_get_clients() > 0,
- "Must have a client running to use lsp_tags")
- local results = call_hierarchy_handler_from(err, method, result, client_id,
- bufnr, "Incoming calls not found")
+local function incoming_calls_handler(bang, err, method, result, client_id, bufnr)
+ assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
+ 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")
gui.new_list_view({items = results, ft = ft, api = ' '})
end
-local function outgoing_calls_handler(bang, err, method, result, client_id,
- bufnr)
- local results = call_hierarchy_handler_to(err, method, result, client_id,
- bufnr, "Outgoing calls not found")
+local function outgoing_calls_handler(bang, err, method, result, client_id, bufnr)
+ local results = call_hierarchy_handler_to(err, method, result, client_id, bufnr,
+ "Outgoing calls not found")
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
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
function M.incoming_calls(bang, opts)
- assert(#vim.lsp.buf_get_clients() > 0,
- "Must have a client running to use lsp_tags")
- if not lsphelper.check_capabilities("call_hierarchy") then return end
+ assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
+ if not lsphelper.check_capabilities("call_hierarchy") then
+ return
+ end
local params = vim.lsp.util.make_position_params()
- util.call_sync("callHierarchy/incomingCalls", params, opts,
- partial(incoming_calls_handler, bang))
+ util.call_sync("callHierarchy/incomingCalls", params, opts, partial(incoming_calls_handler, bang))
end
function M.outgoing_calls(bang, opts)
- assert(#vim.lsp.buf_get_clients() > 0,
- "Must have a client running to use lsp_tags")
- if not lsphelper.check_capabilities("call_hierarchy") then return end
+ assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
+ if not lsphelper.check_capabilities("call_hierarchy") then
+ return
+ end
local params = vim.lsp.util.make_position_params()
- util.call_sync("callHierarchy/outgoingCalls", params, opts,
- partial(outgoing_calls_handler, bang))
+ util.call_sync("callHierarchy/outgoingCalls", params, opts, partial(outgoing_calls_handler, bang))
end
M.incoming_calls_call = partial(M.incoming_calls, 0)
diff --git a/lua/navigator/lspclient/attach.lua b/lua/navigator/lspclient/attach.lua
index ec39633..163d3de 100644
--- a/lua/navigator/lspclient/attach.lua
+++ b/lua/navigator/lspclient/attach.lua
@@ -3,7 +3,7 @@ local lsp = require("vim.lsp")
local util = require "navigator.util"
local log = util.log
-local verbose = util.verbose
+local trace = util.trace
local diagnostic_map = function(bufnr)
local opts = {noremap = true, silent = true}
@@ -20,7 +20,7 @@ M.on_attach = function(client, bufnr)
return
end
log("attaching", bufnr)
- verbose(client)
+ trace(client)
local hassig, sig = pcall(require, "lsp_signature")
if hassig then sig.on_attach() end
diagnostic_map(bufnr)
diff --git a/lua/navigator/lspclient/clients.lua b/lua/navigator/lspclient/clients.lua
index e6174fb..e3cf3ba 100644
--- a/lua/navigator/lspclient/clients.lua
+++ b/lua/navigator/lspclient/clients.lua
@@ -1,6 +1,6 @@
-- todo allow config passed in
local log = require"navigator.util".log
-local verbose = require"navigator.util".verbose
+local trace = require"navigator.util".trace
_Loading = false
@@ -8,7 +8,9 @@ if packer_plugins ~= nil then
-- packer installed
local loader = require"packer".loader
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
loader("guihua.lua")
-- if lazyloading
@@ -16,13 +18,15 @@ if packer_plugins ~= nil then
end
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 util = lspconfig.util
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
-- gopls["ui.completion.usePlaceholders"] = true
@@ -48,7 +52,9 @@ add("$VIMRUNTIME")
-- add your config
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
-- 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 = {
gopls = {
on_attach = on_attach,
- capabilities = cap,
+ -- capabilities = cap,
filetypes = {"go", "gomod"},
message_level = vim.lsp.protocol.MessageType.Error,
cmd = {
@@ -86,8 +92,9 @@ local setups = {
completeUnimported = true,
staticcheck = true,
matcher = "fuzzy",
+ experimentalDiagnosticsDelay = "500ms",
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", "functional"}
}
@@ -204,22 +211,25 @@ local default_cfg = {on_attach = on_attach}
-- check and load based on file type
local function load_cfg(ft, client, cfg, loaded)
- -- log("trying", client)
if lspconfig[client] == nil then
- log("not supported", client)
+ log("not supported by nvim", client)
return
end
local lspft = lspconfig[client].document_config.default_config.filetypes
local should_load = false
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
for _, c in pairs(loaded) do
if client == c then
-- loaded
- verbose(client, "already been loaded for", ft, loaded)
+ trace(client, "already been loaded for", ft, loaded)
return
end
end
@@ -230,19 +240,36 @@ local function load_cfg(ft, client, cfg, loaded)
-- need to verify the lsp server is up
end
-local function wait_lsp_startup(ft, retry)
+local function wait_lsp_startup(ft, retry, lsp_opts)
retry = retry or false
local clients = vim.lsp.get_active_clients() or {}
local loaded = {}
- for i = 1, 2 do
+ for _ = 1, 2 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
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
+ -- 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)
+ ::continue::
+ end
+ if not retry or ft == nil then
+ return
end
- if not retry or ft == nil then return end
--
local timer = vim.loop.new_timer()
local i = 0
@@ -251,7 +278,7 @@ local function wait_lsp_startup(ft, retry)
i = i + 1
if i > 5 or #clients > 0 then
timer:close() -- Always close handles to avoid leaks.
- verbose("active", #clients, i)
+ trace("active", #clients, i)
_Loading = false
return true
end
@@ -260,18 +287,19 @@ local function wait_lsp_startup(ft, retry)
end
end
-vim.cmd([[autocmd FileType * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
-
local function setup(user_opts)
- verbose(debug.traceback())
- if lspconfig == nil then
- error("lsp-config need installed and enabled")
+
+ log(user_opts)
+ trace(debug.traceback())
+ user_opts = user_opts or _NgConfigValues -- incase setup was triggered from autocmd
+
+ if _Loading == true then
return
end
-
- if _Loading == true then return end
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
log("nil filetype")
@@ -284,24 +312,32 @@ local function setup(user_opts)
}
for i = 1, #disable_ft do
if ft == disable_ft[i] then
- log("navigator disabled for ft", ft)
+ trace("navigator disabled for ft", ft)
return
end
end
+
local bufnr = vim.fn.bufnr()
local uri = vim.uri_from_bufnr(bufnr)
- log("loading for ft ", ft, uri)
if uri == 'file://' or uri == 'file:///' then
log("skip loading for ft ", ft, uri)
return
end
+ log('setup', user_opts)
+ log("loading for ft ", ft, uri)
highlight.diagnositc_config_sign()
highlight.add_highlight()
-
+ local lsp_opts = user_opts.lsp
_Loading = true
- wait_lsp_startup(ft, retry)
+ wait_lsp_startup(ft, retry, lsp_opts)
+
_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
return {setup = setup, cap = cap}
diff --git a/lua/navigator/lspclient/init.lua b/lua/navigator/lspclient/init.lua
deleted file mode 100644
index 9b25568..0000000
--- a/lua/navigator/lspclient/init.lua
+++ /dev/null
@@ -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
diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua
index 96fdd11..89ad603 100644
--- a/lua/navigator/lspclient/mapping.lua
+++ b/lua/navigator/lspclient/mapping.lua
@@ -1,5 +1,7 @@
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 = {
{ev = "BufWritePre", func = "diagnostic.set_loclist({open_loclist = false})"},
@@ -9,23 +11,31 @@ local event_hdlrs = {
}
local key_maps = {
- {key = "gr", func = "references()"}, {mode = "i", key = "", func = "signature_help()"},
- {key = "gs", func = "signature_help()"}, {key = "g0", func = "document_symbol()"},
- {key = "gW", func = "workspace_symbol()"}, {key = "", func = "definition()"},
- {key = "gD", func = "declaration()"},
+ {key = "gr", func = "references()"},
+ {mode = "i", key = "", func = "signature_help()"},
+ {key = "gs", func = "signature_help()"},
+ {key = "g0", func = "document_symbol()"},
+ {key = "gW", func = "workspace_symbol()"},
+ {key = "", func = "definition()"}, {key = "gD", func = "declaration()"},
{key = "gp", func = "require('navigator.definition').definition_preview()"},
{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 = "v", func = "range_code_action()"}, {key = "re", func = "rename()"},
- {key = "gi", func = "incoming_calls()"}, {key = "go", func = "outgoing_calls()"},
- {key = "gi", func = "implementation()"}, {key = "gt", func = "type_definition()"},
+ {key = "ga", mode = "v", func = "range_code_action()"},
+ {key = "re", func = "rename()"},
+ {key = "gi", func = "incoming_calls()"},
+ {key = "go", func = "outgoing_calls()"},
+ {key = "gi", func = "implementation()"},
+ {key = "gt", func = "type_definition()"},
{key = "gL", func = "diagnostic.show_line_diagnostics()"},
{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_previous_usage()"},
- {key = "", func = "definition()"}, {key = "g", func = "implementation()"}
+ {key = "", func = "definition()"},
+ {key = "g", func = "implementation()"}
}
local function set_mapping(user_opts)
@@ -35,7 +45,9 @@ local function set_mapping(user_opts)
local user_key = user_opts.keymaps or {}
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(...)
-- 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
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_range_formatting then range_fmt = true end
+ if value.resolved_capabilities.document_range_formatting then
+ range_fmt = true
+ end
end
-- if user_opts.cap.document_formatting then
if doc_fmt then
- buf_set_keymap("n", "ff", "lua vim.lsp.buf.formatting()", opts)
- vim.cmd([[autocmd BufWritePre lua vim.lsp.buf.formatting()]])
+ buf_set_keymap("n", "ff", "lua vim.lsp.buf.formatting()",
+ opts)
+ if _NgConfigValues.lsp.format_on_save then
+ vim.cmd([[autocmd BufWritePre lua vim.lsp.buf.formatting()]])
+ end
end
-- if user_opts.cap.document_range_formatting then
if range_fmt then
- buf_set_keymap("v", "ff", "lua vim.lsp.buf.range_formatting()", opts)
+ buf_set_keymap("v", "ff",
+ "lua vim.lsp.buf.range_formatting()", opts)
end
end
local function set_event_handler(user_opts)
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,
vim.api.nvim_command [[augroup nvim_lsp_autos]]
vim.api.nvim_command [[autocmd!]]
@@ -99,8 +118,9 @@ local function set_event_handler(user_opts)
else
f = "lua vim.lsp.buf." .. value.func
end
- local cmd = "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " .. value.ev ..
- " silent! " .. f
+ local cmd =
+ "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " ..
+ value.ev .. " silent! " .. f
vim.api.nvim_command(cmd)
end
vim.api.nvim_command([[augroup END]])
@@ -109,12 +129,11 @@ end
local M = {}
function M.setup(user_opts)
- user_opts = user_opts or {}
- user_opts.cap = vim.lsp.protocol.make_client_capabilities()
set_mapping(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
vim.lsp.handlers["callHierarchy/incomingCalls"] =
require"navigator.hierarchy".incoming_calls_handler
@@ -122,12 +141,16 @@ function M.setup(user_opts)
require"navigator.hierarchy".outgoing_calls_handler
end
- vim.lsp.handlers["textDocument/references"] = require"navigator.reference".reference_handler
- vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler
- vim.lsp.handlers["textDocument/definition"] = require"navigator.definition".definition_handler
+ vim.lsp.handlers["textDocument/references"] =
+ require"navigator.reference".reference_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
- vim.lsp.handlers["textDocument/declaration"] = require"navigator.definition".declaration_handler
+ vim.lsp.handlers["textDocument/declaration"] =
+ require"navigator.definition".declaration_handler
end
vim.lsp.handlers["textDocument/typeDefinition"] =
@@ -137,13 +160,15 @@ function M.setup(user_opts)
vim.lsp.handlers["textDocument/documentSymbol"] =
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"] =
require"navigator.diagnostics".diagnostic_handler
local hassig, sig = pcall(require, "lsp_signature")
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
-- vim.lsp.handlers["textDocument/hover"] = require 'navigator.hover'.hover_handler
diff --git a/lua/navigator/lspwrapper.lua b/lua/navigator/lspwrapper.lua
index c8d5088..8843841 100644
--- a/lua/navigator/lspwrapper.lua
+++ b/lua/navigator/lspwrapper.lua
@@ -4,16 +4,30 @@ local gutil = require "guihua.util"
local lsp = require "vim.lsp"
local api = vim.api
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 cwd = vim.fn.getcwd(0)
cwd = gutil.add_pec(cwd)
ts_nodes = {}
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
+-- 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)
local fnamemodify = (function(filename)
if include_filename then
@@ -43,7 +57,9 @@ function M.symbols_to_items(result)
local kind = symbol_kind(item.kind)
item.name = result[i].name -- symbol 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.display_filename = item.filename:gsub(cwd .. "/", "./", 1)
@@ -52,7 +68,9 @@ function M.symbols_to_items(result)
end
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)
end
end
@@ -65,9 +83,10 @@ local function extract_result(results_lsp)
if results_lsp then
local results = {}
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
-
return results
end
end
@@ -78,7 +97,9 @@ function M.check_capabilities(feature, client_id)
local supported_client = false
for _, client in pairs(clients) do
supported_client = client.resolved_capabilities[feature]
- if supported_client then goto continue end
+ if supported_client then
+ goto continue
+ end
end
::continue::
@@ -114,15 +135,21 @@ function M.call_async(method, params, handler)
end
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 bufnr = vim.uri_to_bufnr(uri)
local x = os.clock()
+ trace(ts_nodes)
if ts_nodes[uri] ~= nil then
local t = ts_nodes_time[uri]
local fname = vim.uri_to_fname(uri)
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
local unload = false
if not api.nvim_buf_is_loaded(bufnr) then
@@ -139,20 +166,24 @@ local function ts_functions(uri)
end
ts_nodes[uri] = funcs
ts_nodes_time[uri] = os.time()
- verbose(funcs)
+ trace(funcs, ts_nodes)
log(string.format("elapsed time: %.4f\n", os.clock() - x))
return funcs
end
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 = {}
- verbose(funcs, range)
+ trace(funcs, range)
for _, value in pairs(funcs) do
local func_range = value.node_scope
-- note treesitter is C style
- if func_range and func_range.start.line + 1 <= range.start.line and func_range['end'].line + 1 >=
- range['end'].line then table.insert(result, value) end
+ 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
end
return result
end
@@ -168,7 +199,9 @@ function M.locations_to_items(locations)
-- items and locations may not matching
table.sort(locations, function(i, j)
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
else
return i.uri < j.uri
@@ -186,9 +219,10 @@ function M.locations_to_items(locations)
item.rpath = util.get_relative_path(cwd, item.filename)
table.insert(items, item)
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
-
- return items, width + 15
+ return items, width + 24 -- TODO handle long line?
end
function M.symbol_to_items(locations)
@@ -201,7 +235,9 @@ function M.symbol_to_items(locations)
-- items and locations may not matching
table.sort(locations, function(i, j)
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
else
return i.uri < j.uri
diff --git a/lua/navigator/reference.lua b/lua/navigator/reference.lua
index 9c57b02..cbf36e7 100644
--- a/lua/navigator/reference.lua
+++ b/lua/navigator/reference.lua
@@ -3,7 +3,7 @@ local log = util.log
local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui"
local lsp = require "navigator.lspwrapper"
-local verbose = require"navigator.util".verbose
+local trace = require"navigator.util".trace
-- local log = util.log
-- local partial = util.partial
-- local cwd = vim.fn.getcwd(0)
@@ -16,7 +16,7 @@ local function ref_hdlr(err, api, locations, num, bufnr)
local opts = {}
-- log("arg1", arg1)
-- log(api)
- -- log(locations)
+ trace(locations)
-- log("num", num)
-- log("bfnr", bufnr)
if err ~= nil then
@@ -24,7 +24,6 @@ local function ref_hdlr(err, api, locations, num, bufnr)
return
end
if type(locations) ~= 'table' then
- log("arg1", arg1)
log(api)
log(locations)
log("num", num)
@@ -36,10 +35,11 @@ local function ref_hdlr(err, api, locations, num, bufnr)
return
end
local items, width = locations_to_items(locations)
+
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
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"})
end
diff --git a/lua/navigator/render.lua b/lua/navigator/render.lua
new file mode 100644
index 0000000..0336497
--- /dev/null
+++ b/lua/navigator/render.lua
@@ -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
diff --git a/lua/navigator/symbols.lua b/lua/navigator/symbols.lua
index 2934071..03bc7a2 100644
--- a/lua/navigator/symbols.lua
+++ b/lua/navigator/symbols.lua
@@ -55,8 +55,9 @@ function M.workspace_symbols(opts)
-- result_lsp
local result = {}
for i = 1, #results_lsp do
- if results_lsp[i] ~= nil and results_lsp[i].result ~= nil and
- #results_lsp[i].result > 0 then result = results_lsp[i].result end
+ if results_lsp[i] ~= nil and results_lsp[i].result ~= nil and #results_lsp[i].result > 0 then
+ result = results_lsp[i].result
+ end
end
local items = symbols_to_items(result)
@@ -115,23 +116,16 @@ function M.document_symbol_handler(err, _, result, _, bufnr)
child.uri = uri
child.lnum = c.range.start.line + 1
child.detail = c.detail or ""
- child.text = " [" .. ckind .. "] " .. child.detail .. " " ..
- child.name
+ child.text = " [" .. ckind .. "] " .. child.detail .. " " .. child.name
table.insert(locations, child)
end
end
end
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
- -- verbose(locations)
+ -- trace(locations)
-- local items = locations_to_items(locations)
- gui.new_list_view({
- items = locations,
- prompt = true,
- rawdata = true,
- ft = ft,
- api = " "
- })
+ gui.new_list_view({items = locations, prompt = true, rawdata = true, ft = ft, api = " "})
-- if locations == nil or vim.tbl_isempty(locations) then
-- print "References not found"
@@ -178,13 +172,7 @@ function M.workspace_symbol_handler(err, _, result, _, bufnr)
-- local items = locations_to_items(locations)
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
- gui.new_list_view({
- items = items,
- prompt = true,
- ft = ft,
- rowdata = true,
- api = " "
- })
+ gui.new_list_view({items = items, prompt = true, ft = ft, rowdata = true, api = " "})
-- if locations == nil or vim.tbl_isempty(locations) then
-- print "References not found"
diff --git a/lua/navigator/treesitter.lua b/lua/navigator/treesitter.lua
index f087996..a135ecc 100644
--- a/lua/navigator/treesitter.lua
+++ b/lua/navigator/treesitter.lua
@@ -2,7 +2,9 @@ local gui = require "navigator.gui"
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 ts_utils = require "nvim-treesitter.ts_utils"
@@ -13,7 +15,7 @@ local M = {}
local cwd = vim.fn.getcwd(0)
local log = require"navigator.util".log
-local verbose = require"navigator.util".verbose
+local trace = require"navigator.util".trace
local match_kinds = {
var = " ", -- "👹", -- Vampaire
@@ -69,44 +71,78 @@ local function prepare_node(node, kind)
if node.node then
table.insert(matches, {kind = get_icon(kind), def = node.node, type = kind})
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
return matches
end
-local function get_var_context(source)
+local function get_scope(type, source)
local sbl, sbc, sel, sec = source:range()
local current = source
local result = current
local next = ts_utils.get_next_node(source)
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 next:type() == "function" or next:type() == "arrow_function" then
- log(current:type(), current:range())
- return parent
- else
- return source
+ if type == "var" and next ~= nil then
+ if next:type() == "function" or next:type() == "arrow_function" or next:type() ==
+ "function_definition" then
+ trace(current:type(), current:range())
+ return next, true
+ 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
- -- 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
local function get_smallest_context(source)
local scopes = ts_locals.get_scopes()
+ for key, value in pairs(scopes) do
+ trace(key, value)
+ end
local current = source
- while current ~= nil and not vim.tbl_contains(scopes, current) do current = current:parent() end
- log(current)
- if current ~= nil then return current end
- return get_var_context(source)
+ while current ~= nil and not vim.tbl_contains(scopes, current) do
+ current = current:parent()
+ end
+ if current ~= nil then
+ return current, true
+ end
-- if source:type() == "identifier" then return get_var_context(source) end
end
@@ -115,7 +151,9 @@ local lsp_reference = require"navigator.dochighlight".goto_adjent_reference
function M.goto_adjacent_usage(bufnr, delta)
local opt = {forward = true}
-- 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()
local node_at_point = ts_utils.get_node_at_cursor()
if not node_at_point then
@@ -136,13 +174,20 @@ function M.goto_adjacent_usage(bufnr, delta)
ts_utils.goto_node(usages[target_index])
end
-function M.goto_next_usage(bufnr) return M.goto_adjacent_usage(bufnr, 1) end
-function M.goto_previous_usage(bufnr) return M.goto_adjacent_usage(bufnr, -1) end
+function M.goto_next_usage(bufnr)
+ 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)
+ trace(bufnr, filter, summary)
bufnr = bufnr or 0
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 uri = vim.uri_from_fname(fname)
if bufnr ~= 0 then
@@ -186,27 +231,56 @@ local function get_all_nodes(bufnr, filter, summary)
for _, node in ipairs(nodes) do
item.kind = node.kind
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
- log(item.type, tsdata:type())
- if node.def == nil then goto continue end
+ if node.def == nil then
+ goto continue
+ end
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
-- 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)
end
- if filter ~= nil and not filter[item.type] then goto continue end
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
end
item.range = ts_utils.node_to_lsp_range(tsdata)
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,
false)[1] or "")
item.uri = uri
@@ -217,16 +291,20 @@ local function get_all_nodes(bufnr, filter, summary)
item.lnum = item.lnum + 1
item.col = item.col + 1
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.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)
::continue::
end
end
- verbose(all_nodes)
+ trace(all_nodes)
return all_nodes, length
end
@@ -244,20 +322,36 @@ function M.buf_func(bufnr)
["class"] = true,
["type"] = true
}, true)
- table.sort(all_nodes, function(i, j)
- if i.range and j.range then
- if i.range.start.line == j.range.start.line then
- return i.range['end'].line < j.range['end'].line
- else
- return i.range.start.line < j.range.start.line
- end
- end
- return false
- end)
+ if #all_nodes < 1 then
+ log("no node found for ", bufnr)
+ return
+ 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
@@ -295,14 +389,16 @@ function M.bufs_ts()
if vim.api.nvim_buf_is_loaded(buf) then
local all_nodes, length = get_all_nodes(buf)
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)
end
end
end
end
if #ts_opened > 1 then
- verbose(ts_opened)
+ trace(ts_opened)
local ft = vim.api.nvim_buf_get_option(0, "ft")
gui.new_list_view({
diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua
index 824fca4..11546f5 100644
--- a/lua/navigator/util.lua
+++ b/lua/navigator/util.lua
@@ -41,7 +41,9 @@ end
local function getDir(path)
local data = {}
local len = #path
- if len <= 1 then return nil end
+ if len <= 1 then
+ return nil
+ end
local last_index = 1
for i = 2, len do
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 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
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
end
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)
return data
end
@@ -81,23 +91,28 @@ M._log = require("guihua.log").new({level = default_config.level}, true)
-- add log to you lsp.log
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)
- if sep == nil then sep = "%s" end
+ if sep == nil then
+ sep = "%s"
+ end
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
end
-function M.trim_space(s) return s:match("^%s*(.-)%s*$") end
-
function M.quickfix_extract(line)
-- check if it is a line of file pos been selected
local split = M.split
- line = M.trim_space(line)
+ line = vim.trim(line)
local sep = split(line, " ")
if #sep < 2 then
M.log(line)
@@ -130,7 +145,9 @@ function M.getArgs(inputstr)
return cmd, t
end
-function M.p(t) print(vim.inspect(t)) end
+function M.p(t)
+ print(vim.inspect(t))
+end
function M.printError(msg)
vim.cmd("echohl ErrorMsg")
@@ -148,14 +165,18 @@ function M.open_log()
vim.cmd("edit " .. path)
end
-function table.pack(...) return {n = select("#", ...), ...} end
+function table.pack(...)
+ return {n = select("#", ...), ...}
+end
function M.show(...)
local string = ""
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"
end
@@ -165,12 +186,35 @@ function M.split2(s, sep)
sep = sep or " "
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
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)
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))
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"}
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
end
@@ -196,7 +254,9 @@ local api = vim.api
local bufs
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
bufs[bufnr] = true
-- id may be nil
@@ -205,7 +265,9 @@ function M.set_virt_eol(bufnr, lnum, chunks, priority, id)
end
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
if bufs[bufnr] then
if api.nvim_buf_is_valid(bufnr) then
@@ -217,7 +279,9 @@ function M.clear_buf(bufnr)
end
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 = {}
end