diff --git a/README.md b/README.md
index 13070bc..173096c 100644
--- a/README.md
+++ b/README.md
@@ -100,6 +100,8 @@ variable is:
- Treesitter symbols sidebar, LSP document symbole sidebar. Both with preview and folding
+- Calltree: Display and expand Lsp incoming/outgoing calls hierarchy-tree with sidebar
+
- Fully support LSP CodeAction, CodeLens, CodeLens action. Help you improve code quality.
- LRU cache for treesitter nodes
@@ -108,6 +110,8 @@ variable is:
- Multigrid support (different font and detachable)
+- Side panel (sidebar) and floating windows
+
# Why a new plugin
I'd like to go beyond what the system is offering.
@@ -671,6 +675,12 @@ You can override the above highlight to fit your current colorscheme
| ------------ | ------------------------- |
| LspToggleFmt | toggle lsp auto format |
| LspKeymaps | show LSP releated keymaps |
+| Nctags {args} | show ctags symbols, args: -g regen ctags |
+| LspRestart | reload lsp |
+| LspToggleFmt | toggle lsp format |
+| LspSymbols | document symbol in side panel |
+| TSymobls | treesitter symbol in side panel |
+| Calltree {args} | lsp call hierarchy call tree, args: -i (incomming default), -o (outgoing) |
## Screenshots
@@ -691,6 +701,8 @@ Treesitter outline and Diagnostics
+Calltree (LSP call hierarchy)
+
### GUI and multigrid support
diff --git a/doc/navigator.txt b/doc/navigator.txt
index c34347b..60e1661 100644
--- a/doc/navigator.txt
+++ b/doc/navigator.txt
@@ -18,10 +18,12 @@ CONTENTS *navigator-content
5.4.1. LSP clients.................................|navigator-lsp_clients|
5.4.1.1. Add your own servers.........|navigator-add_your_own_servers|
5.4.2. Disable a lsp client loading from navigator.|navigator-disable_a_lsp_client_loading_from_navigator|
- 5.4.3. Default keymaps.........................|navigator-default_keymaps|
- 5.4.4. Colors/Highlight:.....................|navigator-colors/highlight:|
+ 5.4.3. Try it your self.......................|navigator-try_it_your_self|
+ 5.4.4. Default keymaps.........................|navigator-default_keymaps|
+ 5.4.5. Colors/Highlight:.....................|navigator-colors/highlight:|
5.5. Dependency.........................................|navigator-dependency|
- 5.6. Integration with lsp_installer (williamboman/nvim-lsp-installer).|navigator-integration_with_lsp_installer_(williamboman/nvim-lsp-installer)|
+ 5.6. Integrat with lsp_installer (williamboman/nvim-lsp-installer).|navigator-integrat_with_lsp_installer_(williamboman/nvim-lsp-installer)|
+ 5.6.1. Integration with other lsp plugins (e.g. rust-tools, go.nvim, clangd extension).|navigator-integration_with_other_lsp_plugins_(e.g._rust-tools,_go.nvim,_clangd_extension)|
5.7. Usage...................................................|navigator-usage|
5.8. Configuration...................................|navigator-configuration|
5.9. Highlight...........................................|navigator-highlight|
@@ -29,10 +31,11 @@ CONTENTS *navigator-content
5.11. Screenshots......................................|navigator-screenshots|
5.11.1. Reference....................................|navigator-reference|
5.11.2. Definition preview..................|navigator-definition_preview|
- 5.11.3. GUI and multigrid support....|navigator-gui_and_multigrid_support|
- 5.11.4. Document Symbol........................|navigator-document_symbol|
- 5.11.5. Workspace Symbol......................|navigator-workspace_symbol|
- 5.11.6. highlight document symbol and jump between reference.|navigator-highlight_document_symbol_and_jump_between_reference|
+ 5.11.3. Sidebar, folding, outline....|navigator-sidebar,_folding,_outline|
+ 5.11.4. GUI and multigrid support....|navigator-gui_and_multigrid_support|
+ 5.11.5. Document Symbol and navigate through the list.|navigator-document_symbol_and_navigate_through_the_list|
+ 5.11.6. Workspace Symbol......................|navigator-workspace_symbol|
+ 5.11.7. highlight document symbol and jump between reference.|navigator-highlight_document_symbol_and_jump_between_reference|
6. Current symbol highlight and jump backward/forward between symbols.|navigator-current_symbol_highlight_and_jump_backward/forward_between_symbols|
6.1. Diagnostic.....................................|navigator-diagnostic|
6.2. Edit in preview window.............|navigator-edit_in_preview_window|
@@ -48,6 +51,9 @@ CONTENTS *navigator-content
6.11. Light bulb if codeAction available.|navigator-light_bulb_if_codeaction_available|
6.12. Codelens........................................|navigator-codelens|
6.13. Predefined LSP symbol nerdfont/emoji.|navigator-predefined_lsp_symbol_nerdfont/emoji|
+ 6.14. VS-code style folding with treesitter.|navigator-vs-code_style_folding_with_treesitter|
+ 6.14.1. folding function..................|navigator-folding_function|
+ 6.14.2. folding comments..................|navigator-folding_comments|
7. Debug the plugin...................................|navigator-debug_the_plugin|
8. Break changes and known issues.......|navigator-break_changes_and_known_issues|
9. Todo...........................................................|navigator-todo|
@@ -56,8 +62,14 @@ CONTENTS *navigator-content
================================================================================
NAVIGATOR *navigator-navigator*
+* Source code analysis and navigate tool
* Easy code navigation, view diagnostic errors, see relationships of functions, variables
* A plugin combines the power of LSP and 🌲🏡 Treesitter together. Not only provids a better highlight but also help you analyse symbol context effectively.
+* ctags fuzzy search & build ctags symbols
+
+-
+
+* [](https://youtu.be/P1kd7Y8AatE)
Here are some examples
@@ -127,7 +139,8 @@ FEATURES: *navigator-features
* 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
* ccls call hierarchy (Non-standard `ccls/call` API) supports
-* Syntax folding based on treesitter folding algorithm. (It behaves similar to vs-code)
+* Syntax folding based on treesitter or LSP_fold folding algorithm. (It behaves similar to vs-code); comment folding
+* Treesitter symbols sidebar, LSP document symbole sidebar. Both with preview and folding
* Fully support LSP CodeAction, CodeLens, CodeLens action. Help you improve code quality.
* LRU cache for treesitter nodes
* Lazy loader friendly
@@ -151,7 +164,7 @@ SIMILAR PROJECTS / SPECIAL MENTIONS: *navigator-similar_projects_/_special_menti
================================================================================
INSTALL *navigator-install*
-Require nvim-0.6.1 or nightly
+Require nvim-0.6.1 or above, nightly (0.8) prefered
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(migrate from romgrk's project (romgrk/fzy-lua-native)).
@@ -161,11 +174,17 @@ The plugin depends on lspconfig and guihua.lua (https://github.com/ray-x/guihua.
Plug 'ray-x/navigator.lua'
<
-Note: Highly recommened: 'nvim-treesitter/nvim-treesitter'
+Note: Highly recommend: 'nvim-treesitter/nvim-treesitter'
Packer
>
- use {'ray-x/navigator.lua', requires = {'ray-x/guihua.lua', run = 'cd lua/fzy && make'}}
+ use({
+ 'ray-x/navigator.lua',
+ requires = {
+ { 'ray-x/guihua.lua', run = 'cd lua/fzy && make' },
+ { 'neovim/nvim-lspconfig' },
+ },
+ })
<
--------------------------------------------------------------------------------
@@ -183,11 +202,11 @@ SAMPLE VIMRC TURNING YOUR NEOVIM INTO A FULL-FEATURED IDE *navigator-sample_vimr
Plug 'neovim/nvim-lspconfig'
Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua'
- " Plug 'hrsh7th/nvim-compe' and other plugins you commenly use...
+ " Plug 'hrsh7th/nvim-cmp' and other plugins you commenly use...
" optional, if you need treesitter symbol support
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
call plug#end()
- " No need for rquire('lspconfig'), navigator will configure it for you
+ " No need for require('lspconfig'), navigator will configure it for you
lua <
require'navigator'.setup({
- debug = false, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
+ debug = false, -- log output, set to true and log path: ~/.cache/nvim/gh.log
width = 0.75, -- max width ratio (number of cols for the floating window) / (window width)
height = 0.3, -- max list window height, 0.3 by default
preview_height = 0.35, -- max height of preview windows
@@ -258,6 +277,9 @@ Nondefault configuration example:
-- please check mapping.lua for all keymaps
treesitter_analysis = true, -- treesitter variable context
transparency = 50, -- 0 ~ 100 blur the main window, 100: fully transparent, 0: opaque, set to nil or 100 to disable it
+ lsp_signature_help = true, -- if you would like to hook ray-x/lsp_signature plugin in navigator
+ -- setup here. if it is nil, navigator will not init signature help
+ signature_help_cfg = nil, -- if you would like to init ray-x/lsp_signature plugin in navigator, and pass in your own config to signature help
icons = {
-- Code action
code_action_icon = "🏏",
@@ -268,23 +290,37 @@ Nondefault configuration example:
},
lsp_installer = false, -- set to true if you would like use the lsp installed by williamboman/nvim-lsp-installer
lsp = {
+ enable = true, -- skip lsp setup if disabled make sure add require('navigator.lspclient.mapping').setup() in you
+ -- own on_attach
code_action = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
code_lens_action = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
- format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc)
+ format_on_save = true, -- set to false to disable lsp code format on save (if you are using prettier/efm/formater etc)
disable_format_cap = {"sqls", "sumneko_lua", "gopls"}, -- a list of lsp disable format capacity (e.g. if you using efm or vim-codeformat etc), empty {} by default
disable_lsp = {'pylsd', 'sqlls'}, -- a list of lsp server disabled for your project, e.g. denols and tsserver you may
-- only want to enable one lsp server
-- to disable all default config and use your own lsp setup set
-- disable_lsp = 'all'
-- Default {}
+ diagnostic = {
+ underline = true,
+ virtual_text = true, -- show virtual for diagnostic message
+ update_in_insert = false, -- update diagnostic message in insert mode
+ },
diagnostic_scrollbar_sign = {'▃', '▆', '█'}, -- experimental: diagnostic status in scroll bar area; set to false to disable the diagnostic sign,
-- for other style, set to {'╍', 'ﮆ'} or {'-', '='}
+ diagnostic_virtual_text = true, -- show virtual for diagnostic message
+ diagnostic_update_in_insert = false, -- update diagnostic message in insert mode
disply_diagnostic_qf = true, -- always show quickfix if there are diagnostic errors, set to false if you want to
ignore it
tsserver = {
filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetypes
},
+ ctags ={
+ cmd = 'ctags',
+ tagfile = 'tags'
+ options = '-R --exclude=.git --exclude=node_modules --exclude=test --exclude=vendor --excmd=number'
+ }
gopls = { -- gopls setting
on_attach = function(client, bufnr) -- on_attach for gopls
-- your special on attach here
@@ -300,7 +336,8 @@ Nondefault configuration example:
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",
},
- servers = {'cmake', 'ltex'}, -- by default empty, but if you whant navigator load e.g. `cmake` and `ltex` for you , you
+ servers = {'cmake', 'ltex'}, -- by default empty, and it should load all LSP clients avalible based on filetype
+ -- but if you whant navigator load e.g. `cmake` and `ltex` for you , you
-- can put them in the `servers` list and navigator will auto load them.
-- you could still specify the custom config like this
-- cmake = {filetypes = {'cmake', 'makefile'}, single_file_support = false},
@@ -317,7 +354,8 @@ Built clients:
"jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls",
"yamlls", "clangd", "ccls", "sqls", "denols", "graphql", "dartls", "dotls",
"kotlin_language_server", "nimls", "intelephense", "vuels", "phpactor", "omnisharp",
- "r_language_server", "rust_analyzer", "terraformls", "svelte", "texlab", "clojure_lsp"
+ "r_language_server", "rust_analyzer", "terraformls", "svelte", "texlab", "clojure_lsp", "elixirls",
+ "sourcekit", "fsautocomplete", "vls", "hls"
}
<
@@ -347,12 +385,12 @@ ADD YOUR OWN SERVERS *navigator-add_your_own_server
Above servers covered a small part neovim lspconfig support, You can still use lspconfig to add and config servers not
in the list. If you would like to add a server not in the list, you can check this PR https://github.com/ray-x/navigator.lua/pull/107
-Also, an option in setup:
+Alternatively, update following option in setup(if you do not want a PR):
>
require'navigator'setup{lsp={servers={'cmake', 'lexls'}}}
<
-Above example add cmake and lexls to the default server list
+Above option add cmake and lexls to the default server list
DISABLE A LSP CLIENT LOADING FROM NAVIGATOR *navigator-disable_a_lsp_client_loading_from_navigator*
@@ -375,11 +413,16 @@ Or:
})
<
+TRY IT YOUR SELF *navigator-try_it_your_self*
+
+In `playground` folder, there is a `init.lua` and source code for you to play with. Check playground/README.md (https://github.com/ray-x/navigator.lua/blob/master/playground/README.md) for more details
+
DEFAULT KEYMAPS *navigator-default_keymaps*
| mode | key | function |
| ---- | --------------- | ---------------------------------------------------------- |
-| n | gr | show reference and context |
+| n | gr | async references, definitions and context |
+| n | gr | show reference and context |
| i | | signature help |
| n | | signature help |
| n | gW | workspace symbol |
@@ -390,18 +433,20 @@ DEFAULT KEYMAPS *navigator-default_keymap
| n | gp | definition preview (Go to Preview) |
| n | | definition |
| n | g | implementation |
-| n | gT | treesitter document symbol |
+| n | gt | treesitter document symbol |
| n | gT | treesitter symbol for all open buffers |
+| n | ct | ctags symbol search |
+| n | cg | ctags symbol generate |
| n | K | hover doc |
| n | ca | code action (when you see 🏏 ) |
| n | la | code lens action (when you see a codelens indicator) |
-| v | cA | range code action (when you see 🏏 ) |
+| v | ca | range code action (when you see 🏏 ) |
| n | rn | rename with floating window |
| n | re | rename (lsp default) |
| n | gi | hierarchy incoming calls |
| n | go | hierarchy outgoing calls |
| n | gi | implementation |
-| n | D | type definition |
+| n | D | type definition |
| n | gL | show line diagnostic |
| n | gG | show diagnostic for all buffers |
| n | ]d | next diagnostic |
@@ -409,9 +454,9 @@ DEFAULT KEYMAPS *navigator-default_keymap
| n | dt | diagnostic toggle(enable/disable) |
| n | ]r | next treesitter reference/usage |
| n | [r | previous treesitter reference/usage |
-| n | wa | add workspace folder |
-| n | wr | remove workspace folder |
-| n | wl | print workspace folder |
+| n | wa | add workspace folder |
+| n | wr | remove workspace folder |
+| n | wl | print workspace folder |
| n | k | toggle reference highlight |
| i/n | | previous item in list |
| i/n | | next item in list |
@@ -458,19 +503,43 @@ The plugin can be loaded lazily (packer `opt = true` ), And it will check if opt
The terminal will need to be able to output nerdfont and emoji correctly. I am using Kitty with nerdfont (Victor Mono).
--------------------------------------------------------------------------------
-INTEGRATION WITH LSP_INSTALLER (WILLIAMBOMAN/NVIM-LSP-INSTALLER) *navigator-integration_with_lsp_installer_(williamboman/nvim-lsp-installer)*
+INTEGRAT WITH LSP_INSTALLER (WILLIAMBOMAN/NVIM-LSP-INSTALLER) *navigator-integrat_with_lsp_installer_(williamboman/nvim-lsp-installer)*
-If you'd like to only use the lsp servers installed by lsp_installer. Please set
+If you are using lsp_installer and would like to use the lsp servers installed by lsp_installer. Please set
>
lsp_installer = true
<
-In the config.
+In the config. Also please setup the lsp server from installer setup with `server:setup{opts}`
+
+example:
+>
+ use({
+ 'williamboman/nvim-lsp-installer',
+ config = function()
+ local lsp_installer = require('nvim-lsp-installer')
+ lsp_installer.setup{}
+ end,
+ })
+ use({
+ 'ray-x/navigator.lua',
+ config = function()
+ require('navigator').setup({
+ debug = true,
+ lsp_installer = true,
+ keymaps = { { key = 'gR', func = "require('navigator.reference').async_ref()" } },
+ })
+ end,
+ })
+<
-Navigator will startup the server installed by lsp-installer. Please do not call `server:setup{opts}` from lsp installer
+Please refer to lsp_installer_config (https://github.com/ray-x/navigator.lua/blob/master/playground/init_lsp_installer.lua)
+for more info
+
+Alternatively, Navigator can be used to startup the server installed by lsp-installer.
as it will override the navigator setup
-Also, could use following setups
+To start LSP installed by lsp_installer, please use following setups
>
require'navigator'.setup({
-- lsp_installer = false -- default value is false
@@ -482,7 +551,70 @@ Also, could use following setups
example cmd setup (mac) for pyright :
>
- cmd = { "/Users/username/.local/share/nvim/lsp_servers/python/node_modules/.bin/pyright-langserver", "--stdio" }
+ require'navigator'.setup({
+ -- lsp_installer = false -- default value is false
+ lsp = {
+ tsserver = {
+ cmd = { "/Users/username/.local/share/nvim/lsp_servers/python/node_modules/.bin/pyright-langserver", "--stdio" }
+ }
+ }
+ }
+<
+
+The lsp servers installed by nvim-lsp-installer is in following dir
+>
+ local path = require 'nvim-lsp-installer.path'
+ local install_root_dir = path.concat {vim.fn.stdpath 'data', 'lsp_servers'}
+<
+
+And you can setup binary full path to this: (e.g. with gopls)
+`install_root_dir .. '/go/gopls'` So the config is
+>
+ local path = require 'nvim-lsp-installer.path'
+ local install_root_dir = path.concat {vim.fn.stdpath 'data', 'lsp_servers'}
+ require'navigator'.setup({
+ -- lsp_installer = false -- default value is false
+ lsp = {
+ gopls = {
+ cmd = { install_root_dir .. '/go/gopls' }
+ }
+ }
+ }
+<
+
+Use lsp_installer configs
+You can delegate the lsp server setup to lsp_installer with `server:setup{opts}`
+Here is an example init_lsp_installer.lua (https://github.com/ray-x/navigator.lua/blob/master/playground/init_lsp_installer.lua)
+
+INTEGRATION WITH OTHER LSP PLUGINS (E.G. RUST-TOOLS, GO.NVIM, CLANGD EXTENSION) *navigator-integration_with_other_lsp_plugins_(e.g._rust-tools,_go.nvim,_clangd_extension)*
+
+There are lots of plugins provides lsp support
+go.nvim allow you either hook gopls from go.nvim or from navigator and it can export the lsp setup from go.nvim.
+
+rust-tools and clangd allow you to setup on_attach from config server
+Here is an example to setup rust with rust-tools
+>
+ require'navigator'.setup({
+ lsp = {
+ disable_lsp = { "rust_analyzer", "clangd" }, -- will not run rust_analyzer setup from navigator
+ }
+ })
+ require('rust-tools').setup({
+ server = {
+ on_attach = function(_, _)
+ require('navigator.lspclient.mapping').setup() -- setup navigator keymaps here,
+ -- otherwise, you can define your own commands to call navigator functions
+ end,
+ }
+ })
+ require("clangd_extensions").setup {
+ server = {
+ on_attach = function(_, _)
+ require('navigator.lspclient.mapping').setup() -- setup navigator keymaps here,
+ -- otherwise, you can define your own commands to call navigator functions
+ end,
+ }
+ }
<
--------------------------------------------------------------------------------
@@ -521,10 +653,41 @@ You can override the above highlight to fit your current colorscheme
--------------------------------------------------------------------------------
COMMANDS *navigator-commands*
-| command | function |
-| ------------ | ---------------------- |
-| LspToggleFmt | toggle lsp auto format |
-
+| command | function |
+| ------------ | ------------------------- |
+| LspToggleFmt | toggle lsp auto format |
+| LspKeymaps | show LSP releated keymaps |
+| Nctags {args} | show ctags symbols, args: -g regen ctags |
+| LspRestart | reload lsp |
+| LspSymbols | document symbol in side panel |
+| TSymobls | treesitter symbol in side panel |
+| CallTree {args} | lsp call hierarchy call tree, args: -i (incomming default), -o (outgoing) |
+
+:LspToggleFmt *:LspToggleFmt*
+ Toggle lsp auto format.
+
+:LspKeymaps *:LspKeymaps*
+ Show Lsp keymaps.
+
+:Nctags [flags] *:Nctags*
+ Show ctags symbols.
+ [flags]:
+ -g regen ctags
+
+:LspRestart *:LspRestart*
+ Restart Lsp.
+
+:LspSymbols *:LspSymbols*
+ Lsp document symbol in side panel.
+
+:TSSymbols *:TSSymbols*
+ Treesitter symbol in side panel.
+
+:Calltree [flags] *:Calltree*
+ Lsp call hierarchy call tree.
+ [flags]:
+ -i: incomming default
+ -o: outgoing
--------------------------------------------------------------------------------
SCREENSHOTS *navigator-screenshots*
@@ -540,15 +703,27 @@ Using treesitter and LSP to view the symbol definition
+SIDEBAR, FOLDING, OUTLINE *navigator-sidebar,_folding,_outline*
+
+Treesitter outline and Diagnostics
+
+
+
GUI AND MULTIGRID SUPPORT *navigator-gui_and_multigrid_support*
You can load a different font size for floating win
-DOCUMENT SYMBOL *navigator-document_symbol*
+DOCUMENT SYMBOL AND NAVIGATE THROUGH THE LIST *navigator-document_symbol_and_navigate_through_the_list*
+
+The key binding to navigate in the list.
+* up and down key
+* `` for page up and down
+* number key 1~9 go to the ith item.
+* If there are loads of results, would be good to use fzy search prompt to filter out the result you are interested.
WORKSPACE SYMBOL *navigator-workspace_symbol*
@@ -647,6 +822,16 @@ PREDEFINED LSP SYMBOL NERDFONT/EMOJI *navigator-predefined_lsp_symbol_nerdfont/e
+VS-CODE STYLE FOLDING WITH TREESITTER *navigator-vs-code_style_folding_with_treesitter*
+
+FOLDING FUNCTION *navigator-folding_function*
+
+
+
+FOLDING COMMENTS *navigator-folding_comments*
+
+
+
================================================================================
DEBUG THE PLUGIN *navigator-debug_the_plugin*
@@ -689,3 +874,6 @@ ERRORS AND BUG REPORTING *navigator-errors_and_bug_reportin
* Check console output
* Check `LspInfo` and treesitter status with `checkhealth`
* Turn on log and attach the log to your issue if possible you can remove any personal/company info in the log
+* Submit Issue with minium vimrc. Please check playground/init.lua as a vimrc template. !!!Please DONOT use a packer vimrc
+
+that installs everything to default folder!!! Also check this repo navigator bug report (https://github.com/fky2015/navigator.nvim-bug-report)
diff --git a/lua/navigator/hierarchy.lua b/lua/navigator/hierarchy.lua
index 98d2536..cfa9d81 100644
--- a/lua/navigator/hierarchy.lua
+++ b/lua/navigator/hierarchy.lua
@@ -1,27 +1,58 @@
local gui = require('navigator.gui')
local util = require('navigator.util')
local log = util.log
-local trace = util.log
+local trace = util.trace
local partial = util.partial
local lsphelper = require('navigator.lspwrapper')
local path_sep = require('navigator.util').path_sep()
local path_cur = require('navigator.util').path_cur()
local cwd = vim.loop.cwd()
+local in_method = 'callHierarchy/incomingCalls'
+local out_method = 'callHierarchy/outgoingCalls'
+
+local lsp_method = { to = out_method, from = in_method }
+local panel_method = { to = out_method, from = in_method }
+
local M = {}
local outgoing_calls_handler
local incoming_calls_handler
+local hierarchy_handler
+
+local call_hierarchy
+
+local function pick_call_hierarchy_item(call_hierarchy_items)
+ if not call_hierarchy_items then
+ return
+ end
+ if #call_hierarchy_items == 1 then
+ return call_hierarchy_items[1]
+ end
+ local items = {}
+ for i, item in pairs(call_hierarchy_items) do
+ local entry = item.detail or item.name
+ table.insert(items, string.format('%d. %s', i, entry))
+ end
+ local choice = vim.fn.inputlist(items)
+ if choice < 1 or choice > #items then
+ return
+ end
+ return choice
+end
-local function call_hierarchy_handler(direction, err, result, ctx, config)
- log(direction, err, result, ctx, config)
+-- convert lsp result to navigator items
+local function call_hierarchy_result_procesor(direction, err, result, ctx, config)
+ math.randomseed(os.clock() * 100000000000)
+ trace(direction, err, ctx, config)
+ trace(result)
if not result then
vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN)
return
end
-- trace('call_hierarchy', result)
- local bufnr = vim.api.nvim_get_current_buf()
- assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp_tags')
+ local bufnr = ctx.bufnr or vim.api.nvim_get_current_buf()
+ assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use call hierarchy')
if err ~= nil then
log('dir', direction, 'result', result, 'err', err, ctx)
vim.notify('ERROR: ' .. err, vim.lsp.log_levels.WARN)
@@ -30,9 +61,9 @@ local function call_hierarchy_handler(direction, err, result, ctx, config)
local items = ctx.items or {}
+ local kind = ' '
for _, call_hierarchy_result in pairs(result) do
local call_hierarchy_item = call_hierarchy_result[direction]
- local kind = ' '
if call_hierarchy_item.kind then
kind = require('navigator.lspclient.lspkind').symbol_kind(call_hierarchy_item.kind) .. ' '
end
@@ -40,103 +71,190 @@ local function call_hierarchy_handler(direction, err, result, ctx, config)
local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1)
call_hierarchy_item.detail = call_hierarchy_item.detail or ''
call_hierarchy_item.detail = string.gsub(call_hierarchy_item.detail, '\n', ' ↳ ')
- trace(result, call_hierarchy_item)
+ trace(call_hierarchy_item)
local disp_item = vim.tbl_deep_extend('force', {}, call_hierarchy_item)
disp_item = vim.tbl_deep_extend('force', disp_item, {
filename = filename,
display_filename = display_filename,
- indent = ctx.depth,
+ indent_level = ctx.depth or 1,
+ method = lsp_method[direction],
+ node_text = call_hierarchy_item.name,
+ type = kind,
+ id = math.random(1, 100000),
text = kind .. call_hierarchy_item.name .. ' ﰲ ' .. call_hierarchy_item.detail,
lnum = call_hierarchy_item.selectionRange.start.line + 1,
col = call_hierarchy_item.selectionRange.start.character,
})
table.insert(items, disp_item)
- if ctx.depth or 0 > 0 then
- local params = {
- position = {
- character = disp_item.selectionRange.start.character,
- line = disp_item.selectionRange.start.line,
- },
- textDocument = {
- uri = disp_item.uri,
- },
- }
- local api = 'callHierarchy/outgoingCalls'
- local handler = outgoing_calls_handler
- if direction == 'incoming' then
- api = 'callHierarchy/incomingCalls'
- handler = incoming_calls_handler
- end
- lsphelper.call_sync(
- api,
- params,
- ctx,
- vim.lsp.with(
- partial(handler, 0),
- { depth = ctx.depth - 1, direction = 'to', items = ctx.items, no_show = true }
- )
- )
- end
end
- log(items)
+ trace(items)
return items
end
-local call_hierarchy_handler_from = partial(call_hierarchy_handler, 'from')
-local call_hierarchy_handler_to = partial(call_hierarchy_handler, 'to')
+local call_hierarchy_handler_from = partial(call_hierarchy_result_procesor, 'from')
+local call_hierarchy_handler_to = partial(call_hierarchy_result_procesor, 'to')
-incoming_calls_handler = function(_, err, result, ctx, cfg)
- local bufnr = vim.api.nvim_get_current_buf()
+-- the handler that deal all lsp request
+hierarchy_handler = function(dir, handler, show, api, err, result, ctx, cfg)
+ trace(dir, handler, api, show, err, result, ctx, cfg)
+ ctx = ctx or {} -- can be nil if it is async call
+ cfg = cfg or {}
+ opts = ctx.opts or {}
+ vim.validate({ handler = { handler, 'function' }, show = { show, 'function' }, api = { api, 'string' } })
+ local bufnr = ctx.bufnr or vim.api.nvim_get_current_buf()
assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp hierarchy')
- local results = call_hierarchy_handler_from(err, result, ctx, cfg, 'Incoming calls not found')
+
+ local results = handler(err, result, ctx, cfg, 'Incoming calls not found')
local ft = vim.api.nvim_buf_get_option(ctx.bufnr or vim.api.nvim_get_current_buf(), 'ft')
if ctx.no_show then
return results
end
- local win = gui.new_list_view({ items = results, ft = ft, api = ' ' })
+ -- local panel = args.panel
+ -- local items = args.items
+ -- local parent_node = args.node
+ -- local section_id = args.section_id or 1
+ local show_args = {
+ items = results,
+ ft = ft,
+ api = api,
+ bufnr = bufnr,
+ panel = opts.panel,
+ parent_node = opts.parent_node,
+ }
+ local win = show(show_args)
return results, win
end
-outgoing_calls_handler = function(_, err, result, ctx, cfg)
- local results = call_hierarchy_handler_to(err, result, ctx, cfg, 'Outgoing calls not found')
-
- local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft')
- if ctx.no_show then
- return results
- end
- local win = gui.new_list_view({ items = results, ft = ft, api = ' ' })
- return result, win
+local make_params = function(uri, pos)
+ return {
+ textDocument = {
+ uri = uri,
+ },
+ position = pos,
+ }
end
-local function request(method, params, handler)
- return vim.lsp.buf_request(0, method, params, handler)
+local function display_panel(args)
+ -- args = {items=results, ft=ft, api=api}
+ log(args)
+
+ local Panel = require('guihua.panel')
+ local bufnr = args.bufnr or vim.api.nvim_get_current_buf()
+ local ft = args.ft or vim.api.nvim_buf_get_option(bufnr, 'buftype')
+ local items = args.items
+ local p = Panel:new({
+ header = args.header or 'Call Hierarchy',
+ render = function(bufnr)
+ return items
+ end,
+ fold = function(panel, node)
+ if node.expanded ~= nil then
+ node.expanded = not node.expanded
+ vim.cmd('normal! za')
+ else
+ expand(panel, node)
+ node.expanded = true
+ end
+ log('fold')
+ return node
+ end,
+ })
+ p:open(true)
end
-local function pick_call_hierarchy_item(call_hierarchy_items)
- if not call_hierarchy_items then
- return
- end
- if #call_hierarchy_items == 1 then
- return call_hierarchy_items[1]
+local function expand_item(args)
+ -- args = {items=results, ft=ft, api=api}
+ print('dispaly panel')
+ trace(args, args.parent_node)
+ local panel = args.panel
+ local items = args.items
+ local parent_node = args.parent_node
+ local section_id = args.section_id or 1
+
+ local sect
+ local sectid = 1
+ for i, s in pairs(panel.sections) do
+ if s.id == section_id then
+ sectid = i
+ break
+ end
end
- local items = {}
- for i, item in pairs(call_hierarchy_items) do
- local entry = item.detail or item.name
- table.insert(items, string.format('%d. %s', i, entry))
+ sect = panel.sections[sectid]
+ for i, node in pairs(sect.nodes) do
+ if node.id == parent_node.id then
+ for j in ipairs(items) do
+ items[j].indent_level = parent_node.indent_level + 1
+ table.insert(sect.nodes, i + j, args.items[j])
+ end
+ sect.nodes[i].expanded = true
+ sect.nodes[i].expandable = false
+ break
+ end
end
- local choice = vim.fn.inputlist(items)
- if choice < 1 or choice > #items then
- return
+ trace(panel.sections[sectid])
+ -- render the panel again
+ panel:redraw(false)
+end
+
+incoming_calls_handler = util.partial4(
+ hierarchy_handler,
+ 'from',
+ call_hierarchy_handler_from,
+ gui.new_list_view,
+ ' '
+)
+outgoing_calls_handler = util.partial4(hierarchy_handler, 'to', call_hierarchy_handler_to, gui.new_list_view, ' ')
+
+local incoming_calls_panel = util.partial4(
+ hierarchy_handler,
+ 'from',
+ call_hierarchy_handler_from,
+ display_panel,
+ ' '
+)
+local outgoing_calls_panel = util.partial4(hierarchy_handler, 'to', call_hierarchy_handler_to, display_panel, ' ')
+
+local incoming_calls_expand = util.partial4(hierarchy_handler, 'from', call_hierarchy_handler_from, expand_item, ' ')
+local outgoing_calls_expand = util.partial4(hierarchy_handler, 'to', call_hierarchy_handler_to, expand_item, ' ')
+
+function expand(panel, node)
+ trace(panel, node)
+ local params = make_params(node.uri, {
+ line = node.range.start.line,
+ character = node.range.start.character,
+ })
+ local handler = incoming_calls_expand
+ if node.api == out_method then
+ handler = outgoing_calls_expand
end
- return choice
+
+ local bufnr = vim.uri_to_bufnr(node.uri)
+ call_hierarchy(node.method, {
+ params = params,
+ panel = panel,
+ parent_node = node,
+ handler = handler,
+ bufnr = bufnr,
+ })
end
-local function call_hierarchy(method, opts)
- local params = vim.lsp.util.make_position_params()
+local request = vim.lsp.buf_request
+
+-- call_hierarchy with floating window
+call_hierarchy = function(method, opts)
+ trace(method, opts)
opts = opts or {}
+ local params = opts.params or vim.lsp.util.make_position_params()
+ local bufnr = opts.bufnr
+ local handler = function(err, result, ctx, cfg)
+ ctx.opts = opts
+ return opts.handler(err, result, ctx, cfg)
+ end
+ -- log(opts, params)
request(
+ bufnr,
'textDocument/prepareCallHierarchy',
params,
vim.lsp.with(function(err, result, ctx)
@@ -145,32 +263,47 @@ local function call_hierarchy(method, opts)
return
end
local call_hierarchy_item = pick_call_hierarchy_item(result)
- log('result', result, 'items', call_hierarchy_item)
local client = vim.lsp.get_client_by_id(ctx.client_id)
if client then
- client.request(method, { item = call_hierarchy_item }, nil, ctx.bufnr)
+ trace('result', result, 'items', call_hierarchy_item, method, ctx, client.name)
+ client.request(method, {
+ item = call_hierarchy_item,
+ args = {
+ method = method,
+ },
+ }, handler, ctx.bufnr)
else
- vim.notify(
- string.format('Client with id=%d disappeared during call hierarchy request', ctx.client_id),
- vim.log.levels.WARN
- )
+ vim.notify(string.format('Client with id=%d stopped', ctx.client_id), vim.log.levels.WARN)
end
end, { direction = method, depth = opts.depth })
)
end
function M.incoming_calls(opts)
- call_hierarchy('callHierarchy/incomingCalls', opts)
+ call_hierarchy(in_method, opts)
end
function M.outgoing_calls(opts)
- call_hierarchy('callHierarchy/outgoingCalls', opts)
+ call_hierarchy(out_method, opts)
end
-M.incoming_calls_call = partial(M.incoming_calls, 0)
-M.outgoing_calls_call = partial(M.outgoing_calls, 0)
+function M.incoming_calls_panel(opts)
+ opts = vim.tbl_extend('force', { handler = incoming_calls_panel }, opts or {})
+ call_hierarchy(in_method, opts)
+end
-M.incoming_calls_handler = partial(incoming_calls_handler, 0)
-M.outgoing_calls_handler = partial(outgoing_calls_handler, 0)
+function M.outgoing_calls_panel(opts)
+ opts = vim.tbl_extend('force', { handler = outgoing_calls_panel }, opts or {})
+ call_hierarchy(out_method, opts)
+end
+M.incoming_calls_handler = incoming_calls_handler
+M.outgoing_calls_handler = outgoing_calls_handler
+
+function M.calltree(args)
+ if args == '-o' then
+ return M.outgoing_calls_panel()
+ end
+ M.incoming_calls_panel()
+end
return M
diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua
index 85eb09c..51240cd 100644
--- a/lua/navigator/lspclient/mapping.lua
+++ b/lua/navigator/lspclient/mapping.lua
@@ -70,6 +70,7 @@ local commands = {
"command! -nargs=0 LspKeymaps lua require'navigator.lspclient.mapping'.get_keymaps_help()",
"command! -nargs=0 LspSymbols lua require'navigator.symbols'.side_panel()",
"command! -nargs=0 TSymbols lua require'navigator.treesitter'.side_panel()",
+ "command! -nargs=* Calltree lua require'navigator.hierarchy'.calltree()",
}
local key_maps_help = {}
diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua
index e0e09a6..63ed191 100644
--- a/lua/navigator/util.lua
+++ b/lua/navigator/util.lua
@@ -426,6 +426,24 @@ function M.partial(func, arg)
end
end
+function M.partial2(func, arg1, arg2)
+ return function(...)
+ return func(arg1, arg2, ...)
+ end
+end
+
+function M.partial3(func, arg1, arg2, arg3)
+ return function(...)
+ return func(arg1, arg2, arg3, ...)
+ end
+end
+
+function M.partial4(func, arg1, arg2, arg3, arg4)
+ return function(...)
+ return func(arg1, arg2, arg3, arg4, ...)
+ end
+end
+
function M.empty(t)
if t == nil then
return true