diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..033a5f4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: ray-x diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7f3e38..5cdfefc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,23 +10,23 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-20.04 + - os: ubuntu-22.04 url: https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz manager: sudo snap packages: go - - os: ubuntu-20.04 - url: https://github.com/neovim/neovim/releases/download/v0.5.1/nvim-linux64.tar.gz + - os: ubuntu-22.04 + url: https://github.com/neovim/neovim/releases/download/v0.7.2/nvim-linux64.tar.gz manager: sudo snap packages: go - - os: ubuntu-20.04 - url: https://github.com/neovim/neovim/releases/download/v0.6.0/nvim-linux64.tar.gz + - os: ubuntu-22.04 + url: https://github.com/neovim/neovim/releases/download/v0.6.1/nvim-linux64.tar.gz manager: sudo snap packages: go steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "^1.17.2" # The Go version to download (if necessary) and use. + go-version: "^1.18.1" # The Go version to download (if necessary) and use. - run: date +%F > todays-date - name: Restore cache for today's nightly. uses: actions/cache@v2 @@ -42,7 +42,7 @@ jobs: mkdir -p _neovim curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim" } - GO111MODULE=on go get golang.org/x/tools/gopls@latest + GO111MODULE=on go install golang.org/x/tools/gopls@latest mkdir -p ~/.local/share/nvim/site/pack/vendor/start git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim diff --git a/README.md b/README.md index 9edcb0f..cae5ab3 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ # 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 + +- + - [![a short intro of navigator](https://user-images.githubusercontent.com/1681295/147378905-51eede5f-e36d-48f4-9799-ae562949babe.jpeg)](https://youtu.be/P1kd7Y8AatE) Here are some examples @@ -91,7 +96,11 @@ variable is: - 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 + +- Calltree: Display and expand Lsp incoming/outgoing calls hierarchy-tree with sidebar - Fully support LSP CodeAction, CodeLens, CodeLens action. Help you improve code quality. @@ -101,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. @@ -116,7 +127,7 @@ I'd like to go beyond what the system is offering. # Install -Require nvim-0.5.0 (a.k.a 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)). @@ -127,14 +138,18 @@ Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' } Plug 'ray-x/navigator.lua' ``` -Note: Highly recommened: 'nvim-treesitter/nvim-treesitter' +Note: Highly recommend: 'nvim-treesitter/nvim-treesitter' Packer ```lua - -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' }, + }, +}) ``` ## Setup @@ -247,6 +262,8 @@ require'navigator'.setup({ -- this kepmap gK will override "gD" mapping function declaration() in default kepmap -- please check mapping.lua for all keymaps treesitter_analysis = true, -- treesitter variable context + treesitter_analysis_max_num = 100, -- how many items to run treesitter analysis + -- this value prevent slow in large projects, e.g. found 100000 reference in a project 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 @@ -262,6 +279,8 @@ require'navigator'.setup({ }, 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 disable lsp code format on save (if you are using prettier/efm/formater etc) @@ -271,6 +290,12 @@ require'navigator'.setup({ -- 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 @@ -281,6 +306,11 @@ require'navigator'.setup({ 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 @@ -317,7 +347,8 @@ local servers = { "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", "elixirls" + "r_language_server", "rust_analyzer", "terraformls", "svelte", "texlab", "clojure_lsp", "elixirls", + "sourcekit", "fsautocomplete", "vls", "hls" } ``` @@ -389,8 +420,8 @@ In `playground` folder, there is a `init.lua` and source code for you to play wi | mode | key | function | | ---- | --------------- | ---------------------------------------------------------- | -| n | gr | show reference and context | -| n | Gr | async references, definitions and context (experiential) | +| n | gr | async references, definitions and context | +| n | \gr | show reference and context | | i | \ | signature help | | n | \ | signature help | | n | gW | workspace symbol | @@ -403,10 +434,12 @@ In `playground` folder, there is a `init.lua` and source code for you to play wi | n | g\ | implementation | | 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 | @@ -479,7 +512,33 @@ lsp_installer = true In the config. Also please setup the lsp server from installer setup with `server:setup{opts}` -Alternatively, Navigator can be used to startup the server installed by lsp-installer. Please do not call `server:setup{opts}` from lsp installer +example: +```lua + 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, + }) + +``` + +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 To start LSP installed by lsp_installer, please use following setups @@ -538,6 +597,47 @@ require'navigator'.setup({ ``` +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) +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 + +```lua +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(client, bufnr) + require('navigator.lspclient.mapping').setup({client=client, bufnr=bufnr}) -- setup navigator keymaps here, + -- otherwise, you can define your own commands to call navigator functions + end, + } +}) + +require("clangd_extensions").setup { + server = { + on_attach = function(client, bufnr) + require('navigator.lspclient.mapping').setup({client=client, bufnr=bufnr}) -- setup navigator keymaps here, + -- otherwise, you can define your own commands to call navigator functions + end, + } +} + +``` + + + ## Usage Please refer to lua/navigator/lspclient/mapping.lua on key mappings. Should be able to work out-of-box. @@ -577,6 +677,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 @@ -592,6 +698,14 @@ Using treesitter and LSP to view the symbol definition ![image](https://user-images.githubusercontent.com/1681295/139771978-bbc970a5-be9f-42cf-8942-3477485bd89c.png) +### Sidebar, folding, outline +Treesitter outline and Diagnostics +image +image + +Calltree (Expandable LSP call hierarchy) +image + ### GUI and multigrid support You can load a different font size for floating win @@ -754,7 +868,7 @@ end # Errors and Bug Reporting - Please double check your setup and check if minium setup works or not -- It should works for 0.5.1, neovim 0.6.x prefered. +- It should works for 0.6.1, neovim 0.7.x prefered. - 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 diff --git a/autoload/folding.vim b/autoload/folding.vim index bf2793a..89e30f9 100644 --- a/autoload/folding.vim +++ b/autoload/folding.vim @@ -1,4 +1,4 @@ -function! folding#foldexpr() +function! folding#ngfoldexpr() " return luaeval(printf('require"navigator.foldinglsp".get_fold_indic(%d)', v:lnum)) return luaeval(printf('require"navigator.foldts".get_fold_indic(%d)', v:lnum)) endfunction diff --git a/doc/navigator.txt b/doc/navigator.txt index 9112195..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.5.0 (a.k.a 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,15 +290,22 @@ 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 @@ -287,6 +316,11 @@ Nondefault configuration example: 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 @@ -302,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}, @@ -319,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" } < @@ -349,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* @@ -377,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 | @@ -392,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 | @@ -411,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 | @@ -460,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 @@ -484,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, + } + } < -------------------------------------------------------------------------------- @@ -523,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* @@ -542,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* @@ -649,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* @@ -687,8 +870,10 @@ TODO *navigator-tod ERRORS AND BUG REPORTING *navigator-errors_and_bug_reporting* * Please double check your setup and check if minium setup works or not -* It should works for 0.5.1, neovim 0.6.x prefered. +* It should works for 0.6.1, neovim 0.7.x prefered. * 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/ftdetect/tf.vim b/ftdetect/tf.vim new file mode 100644 index 0000000..09a5729 --- /dev/null +++ b/ftdetect/tf.vim @@ -0,0 +1,4 @@ +autocmd BufRead,BufNewFile *.tf,*.tfvars set filetype=terraform +autocmd BufRead,BufNewFile *.tfstate,*.tfstate.backup set filetype=json +autocmd BufRead,BufNewFile *.hcl set filetype=hcl +autocmd BufRead,BufNewFile .terraformrc,terraform.rc set filetype=hcl diff --git a/lua/navigator.lua b/lua/navigator.lua old mode 100644 new mode 100755 index 249c86d..403b3c7 --- a/lua/navigator.lua +++ b/lua/navigator.lua @@ -27,14 +27,15 @@ _NgConfigValues = { -- your on_attach will be called at end of navigator on_attach end, ts_fold = false, - -- code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true}, - -- code_lens_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true}, treesitter_analysis = true, -- treesitter variable context + treesitter_analysis_max_num = 100, -- how many items to run treesitter analysis transparency = 50, -- 0 ~ 100 blur the main window, 100: fully transparent, 0: opaque, set to nil 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 = { debug = false }, -- if you would like to init ray-x/lsp_signature plugin in navigator, pass in signature help + ctags = { cmd = 'ctags', tagfile = '.tags' }, lsp = { + enable = true, -- if disabled make sure add require('navigator.lspclient.mapping').setup() in you on_attach code_action = { enable = true, sign = true, @@ -49,10 +50,16 @@ _NgConfigValues = { virtual_text = true, virtual_text_icon = true, }, + diagnostic = { + underline = true, + virtual_text = { spacing = 3, source = true }, -- show virtual for diagnostic message + update_in_insert = false, -- update diagnostic message in insert mode + severity_sort = { reverse = true }, + }, format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc) + disable_nulls_codeaction_sign = true, -- do not show nulls codeactions (as it will alway has a valid action) disable_format_cap = {}, -- a list of lsp disable file format (e.g. if you using efm or vim-codeformat etc), empty by default disable_lsp = {}, -- a list of lsp server disabled for your project, e.g. denols and tsserver you may - code_lens = false, -- only want to enable one lsp server disply_diagnostic_qf = true, -- always show quickfix if there are diagnostic errors diagnostic_load_files = false, -- lsp diagnostic errors list may contains uri that not opened yet set to true @@ -94,6 +101,15 @@ _NgConfigValues = { -- Values value_changed = '๐Ÿ“', value_definition = '๐Ÿถ๐Ÿก', -- it is easier to see than ๐Ÿฆ• + side_panel = { + section_separator = '๏››', + line_num_left = '๎‚ฒ', + line_num_right = '๎‚ฐ', + inner_node = 'โ”œโ—‹', + outer_node = 'โ•ฐโ—‹', + bracket_left = 'โŸช', + bracket_right = 'โŸซ', + }, -- Treesitter match_kinds = { var = '๎ž› ', -- "๐Ÿ‘น", -- Vampaire @@ -104,18 +120,15 @@ _NgConfigValues = { namespace = '๐Ÿš€', type = '๏ ‹ ', field = '๐Ÿˆ', + module = '๐Ÿ“ฆ', + flag = '๐ŸŽ', }, treesitter_defult = '๐ŸŒฒ', + doc_symbols = '๎œ–', }, } -vim.cmd("command! -nargs=0 LspLog lua require'navigator.lspclient.config'.open_lsp_log()") -vim.cmd("command! -nargs=0 LspRestart lua require'navigator.lspclient.config'.reload_lsp()") -vim.cmd("command! -nargs=0 LspToggleFmt lua require'navigator.lspclient.mapping'.toggle_lspformat()") -vim.cmd("command! -nargs=0 LspKeymaps lua require'navigator.lspclient.mapping'.get_keymaps_help()") - M.deprecated = function(cfg) - local warn = require('navigator.util').warn if cfg.code_action_prompt then warn('code_action_prompt moved to lsp.code_action') end @@ -127,6 +140,10 @@ M.deprecated = function(cfg) warn('disable_format_ft renamed to disable_format_cap') end + if cfg.lsp ~= nil and cfg.lsp.code_lens == true then + warn('code_lens moved to lsp.code_lens_action') + end + if cfg.lspinstall ~= nil then warn('lspinstall deprecated, please use lsp-installer instead or use "lspinstall" branch') end @@ -137,9 +154,8 @@ local extend_config = function(opts) if next(opts) == nil then return end - if opts.lsp and opts.lsp.servers then - require('navigator.lspclient.clients').add_servers(opts.lsp.servers) - opts.lsp.server = nil + if opts.debug then + _NgConfigValues.debug = opts.debug end for key, value in pairs(opts) do if _NgConfigValues[key] == nil then @@ -185,7 +201,7 @@ local extend_config = function(opts) if key == 'lsp' then local lsp = require('navigator.lspclient.clients').lsp if not vim.tbl_contains(lsp or {}, k) and k ~= 'efm' and k ~= 'null-ls' then - info(string.format('[๏ข] extend LSP support for %s ', k)) + info(string.format('[๏ข] extend LSP support for %s %s ', key, k)) end elseif key == 'keymaps' then info('keymap override') @@ -215,31 +231,42 @@ M.config_values = function() end M.setup = function(cfg) + cfg = cfg or {} extend_config(cfg) vim.cmd([[autocmd FileType,BufEnter * lua require'navigator.lspclient.clients'.on_filetype()]]) -- BufWinEnter BufNewFile,BufRead ? - -- local log = require"navigator.util".log - -- log(debug.traceback()) - -- log(cfg, _NgConfigValues) - -- print("loading navigator") require('navigator.lazyloader').init() require('navigator.lspclient.clients').setup(_NgConfigValues) - -- keymaps should be added to on_attach. in case on_attach is not called - -- require('navigator.lspclient.mapping').setup(_NgConfigValues) + require('navigator.reference') require('navigator.definition') require('navigator.hierarchy') require('navigator.implementation') - -- log("navigator loader") - - -- vim.cmd("autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4") + cfg.lsp = cfg.lsp or _NgConfigValues.lsp + if cfg.lsp.enable then + require('navigator.diagnostics').config(cfg.lsp.diagnostic) + end if not _NgConfigValues.loaded then _NgConfigValues.loaded = true end if _NgConfigValues.ts_fold == true then - require('navigator.foldts').on_attach() + local ok, _ = pcall(require, 'nvim-treesitter') + if ok then + require('navigator.foldts').on_attach() + end + end + + local _start_client = vim.lsp.start_client + vim.lsp.start_client = function(lsp_config) + -- add highlight for Lspxxx + require('navigator.dochighlight').documentHighlight() + require('navigator.lspclient.highlight').add_highlight() + require('navigator.lspclient.highlight').diagnositc_config_sign() + -- require('navigator.lspclient.mapping').setup() + require('navigator.lspclient.lspkind').init() + return _start_client(lsp_config) end end diff --git a/lua/navigator/cclshierarchy.lua b/lua/navigator/cclshierarchy.lua index cfdd4d2..5dbc94f 100644 --- a/lua/navigator/cclshierarchy.lua +++ b/lua/navigator/cclshierarchy.lua @@ -11,9 +11,9 @@ local M = {} local function call_hierarchy_handler(direction, err, result, ctx, cfg, error_message) log('call_hierarchy') - log('call_hierarchy', direction, err, result) + log('call_hierarchy', direction, err, result, ctx, cfg) - assert(#vim.lsp.buf_get_clients() > 0, 'Must have a client running to use lsp_tags') + assert(next(vim.lsp.buf_get_clients()), 'Must have a client running to use lsp_tags') if err ~= nil then log('hierarchy error', ctx, 'dir', direction, 'result', result, 'err', err) vim.notify('ERROR: ' .. error_message, vim.lsp.log_levels.WARN) @@ -51,25 +51,27 @@ 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, result, ctx, cfg) - assert(#vim.lsp.buf_get_clients() > 0, 'Must have a client running to use lsp_tags') +local function incoming_calls_handler(_, err, result, ctx, cfg) + 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 results = call_hierarchy_handler_from(err, result, ctx, cfg, 'Incoming calls not found') - local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft') - gui.new_list_view({ items = results, ft = ft, api = '๏ฃถ ' }) + local ft = vim.api.nvim_buf_get_option(ctx.bufnr or 0, 'ft') + gui.new_list_view({ items = results, ft = ft or 'cpp', api = '๏ฃถ ' }) end -- err, method, result, client_id, bufnr -local function outgoing_calls_handler(bang, err, result, ctx, cfg) +local function outgoing_calls_handler(_, 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') - gui.new_list_view({ items = results, ft = ft, api = '๏ฃบ ' }) + local ft = vim.api.nvim_buf_get_option(ctx.bufnr or 0, 'ft') + gui.new_list_view({ items = results, ft = ft or 'cpp', api = '๏ฃบ ' }) -- fzf_locations(bang, "", "Outgoing Calls", results, false) end function M.incoming_calls(bang, opts) - assert(#vim.lsp.buf_get_clients() > 0, 'Must have a client running to use lsp_tags') + 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') -- if not lsphelper.check_capabilities("call_hierarchy") then -- return -- end @@ -85,7 +87,8 @@ function M.incoming_calls(bang, opts) end function M.outgoing_calls(bang, opts) - assert(#vim.lsp.buf_get_clients() > 0, 'Must have a client running to use lsp_tags') + 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 params = vim.lsp.util.make_position_params() params['levels'] = 2 params['callee'] = true diff --git a/lua/navigator/codeAction.lua b/lua/navigator/codeAction.lua index d8c6d2f..97c1d69 100644 --- a/lua/navigator/codeAction.lua +++ b/lua/navigator/codeAction.lua @@ -2,7 +2,7 @@ local util = require('navigator.util') local log = util.log local trace = util.trace local code_action = {} -local gui = require('navigator.gui') +-- local gui = require('navigator.gui') local config = require('navigator').config_values() local api = vim.api @@ -52,20 +52,24 @@ local function _update_sign(line) if code_action[winid] == nil then code_action[winid] = {} end - if code_action[winid].lightbulb_line ~= 0 then + -- only show code action on the current line, remove all others + if code_action[winid].lightbulb_line and code_action[winid].lightbulb_line > 0 then vim.fn.sign_unplace(sign_group, { id = code_action[winid].lightbulb_line, buffer = '%' }) + + log('sign removed', line) end if line then -- log("updatasign", line, sign_group, sign_name) - vim.fn.sign_place( + local id = vim.fn.sign_place( line, sign_group, sign_name, '%', { lnum = line + 1, priority = config.lsp.code_action.sign_priority } ) - code_action[winid].lightbulb_line = line + code_action[winid].lightbulb_line = id + log('sign updated', id) end end @@ -74,6 +78,14 @@ local need_check_diagnostic = { ['python'] = true } function code_action:render_action_virtual_text(line, diagnostics) return function(err, actions, context) + trace(actions, context) + if context and context.client_id then + local cname = vim.lsp.get_active_clients({ id = context.client_id })[1].name + if cname == 'null-ls' and _NgConfigValues.lsp.disable_nulls_codeaction_sign then + return + end + end + -- if nul-ls enabled, some of the lsp may not send valid code action, if actions == nil or type(actions) ~= 'table' or vim.tbl_isempty(actions) then -- no actions cleanup if config.lsp.code_action.virtual_text then @@ -84,12 +96,13 @@ function code_action:render_action_virtual_text(line, diagnostics) end else trace(err, line, diagnostics, actions, context) + if config.lsp.code_action.sign then if need_check_diagnostic[vim.bo.filetype] then if next(diagnostics) == nil then + -- no diagnostic, no code action sign.. _update_sign(nil) else - -- no diagnostic, no code action sign.. _update_sign(line) end else @@ -138,9 +151,16 @@ local code_action_req = function(_call_back_fn, diagnostics) vim.lsp.buf_request(0, 'textDocument/codeAction', params, callback) end +local function sort_select(action_tuples, opts, on_user_choice) + -- table.sort(action_tuples, function(a, b) + -- return a[1] > b[1] + -- end) + require('guihua.gui').select(action_tuples, opts, on_user_choice) +end + code_action.code_action = function() local original_select = vim.ui.select - vim.ui.select = require('guihua.gui').select + vim.ui.select = sort_select log('codeaction') @@ -153,16 +173,27 @@ end code_action.range_code_action = function(startpos, endpos) local context = {} context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() - local params = util.make_given_range_params(startpos, endpos) + + local bufnr = vim.api.nvim_get_current_buf() + startpos = startpos or api.nvim_buf_get_mark(bufnr, '<') + endpos = endpos or api.nvim_buf_get_mark(bufnr, '>') + log(startpos, endpos) + local params = vim.lsp.util.make_given_range_params(startpos, endpos) params.context = context local original_select = vim.ui.select vim.ui.select = require('guihua.gui').select + local original_input = vim.ui.input + vim.ui.input = require('guihua.input').input vim.lsp.buf.range_code_action(context, startpos, endpos) vim.defer_fn(function() vim.ui.select = original_select end, 1000) + + vim.defer_fn(function() + vim.ui.input = original_input + end, 1000) end code_action.code_action_prompt = function() diff --git a/lua/navigator/codelens.lua b/lua/navigator/codelens.lua index 927220e..7adda45 100644 --- a/lua/navigator/codelens.lua +++ b/lua/navigator/codelens.lua @@ -4,13 +4,10 @@ local codelens = require('vim.lsp.codelens') local log = require('navigator.util').log -local mk_handler = require('navigator.util').mk_handler -local nvim_0_6 = require('navigator.util').nvim_0_6 local trace = require('navigator.util').trace local lsphelper = require('navigator.lspwrapper') local api = vim.api -local gui = require('navigator.gui') local M = {} local config = require('navigator').config_values() @@ -49,8 +46,8 @@ local function _update_sign(line) end end -local codelens_hdlr = mk_handler(function(err, result, ctx, cfg) - log(ctx, result) +local codelens_hdlr = function(err, result, ctx, cfg) + trace(ctx, result) M.codelens_ctx = ctx if err or result == nil then if err then @@ -62,7 +59,7 @@ local codelens_hdlr = mk_handler(function(err, result, ctx, cfg) for _, v in pairs(result) do _update_sign(v.range.start.line) end -end) +end function M.setup() vim.cmd('highlight! link LspCodeLens LspDiagnosticsHint') @@ -75,28 +72,23 @@ function M.setup() vim.cmd("autocmd BufEnter,CursorHold,InsertLeave lua require('navigator.codelens').refresh()") vim.cmd('augroup end') local on_codelens = vim.lsp.handlers['textDocument/codeLens'] - vim.lsp.handlers['textDocument/codeLens'] = mk_handler(function(err, result, ctx, cfg) + vim.lsp.handlers['textDocument/codeLens'] = function(err, result, ctx, cfg) -- trace(err, result, ctx.client_id, ctx.bufnr, cfg or {}) cfg = cfg or {} ctx = ctx or { bufnr = vim.api.nvim_get_current_buf() } - if nvim_0_6() then - on_codelens(err, result, ctx, cfg) - codelens_hdlr(err, result, ctx, cfg) - else - on_codelens(err, ctx.method, result, ctx.client_id, ctx.bufnr) - codelens_hdlr(err, nil, result, ctx.client_id or 0, ctx.bufnr or 0) - end - end) + on_codelens(err, result, ctx, cfg) + codelens_hdlr(err, result, ctx, cfg) + end end M.lsp_clients = {} function M.refresh() - if #vim.lsp.buf_get_clients() < 1 then + if next(vim.lsp.buf_get_clients(0)) == nil then log('Must have a client running to use lsp code action') return end - if not lsphelper.check_capabilities('code_lens') then + if not lsphelper.check_capabilities('codeLensProvider') then return end vim.lsp.codelens.refresh() @@ -110,20 +102,16 @@ function M.disable() is_enabled = false end - function M.run_action() local original_select = vim.ui.select - vim.ui.select = require("guihua.gui").select + vim.ui.select = require('guihua.gui').select log('codeaction') codelens.run() - vim.defer_fn( - function () - vim.ui.select = original_select - end, 1000 - ) - + vim.defer_fn(function() + vim.ui.select = original_select + end, 1000) end M.inline = function() @@ -134,7 +122,8 @@ M.inline = function() if vim.fn.getcmdwintype() == ':' then return end - if #vim.lsp.buf_get_clients() == 0 then + + if next(vim.lsp.buf_get_clients(0)) == nil then return end diff --git a/lua/navigator/ctags.lua b/lua/navigator/ctags.lua new file mode 100644 index 0000000..fc7f427 --- /dev/null +++ b/lua/navigator/ctags.lua @@ -0,0 +1,177 @@ +local type_to_lspkind = { c = 5, m = 7, f = 6, s = 5 } +local util = require('navigator.util') +local log = util.log +local sep = util.path_sep() +local vfn = vim.fn +local cur_dir = vfn.getcwd() + +-- convert ctags line to lsp entry +local function entry_to_item(entry) + local item = {} + item.name, item.filename, item.line, item.remain = string.match(entry, '(.*)\t(.*)\t(%d+);(.*)') + local type = 'combine' + item.remain = item.remain or '' + if item.remain:sub(1, 1) == [["]] then + type = 'number' + end + if item.name == nil or item.filename == nil then + return + end + + if type == 'combine' then + -- '/^type ServerResponse struct {$/;"\ts\tpackage:client' + item.inline, item.type, item.containerName, item.ref = string.match(item.remain, '/^(.*)$/;"\t(%a)\t(.+)') + else + -- '"\tm\tstruct:store.Customer\ttyperef:typename:string' + item.type, item.containerName, item.ref = string.match(item.remain, '"\t(%a)\t(.+)') + end + item.kind = type_to_lspkind[item.type] or 13 + item.lnum = tonumber(item.line) - 1 + item.location = { + uri = 'file://' .. cur_dir .. sep .. item.filename, + range = { + start = { line = item.lnum, character = 0 }, + ['end'] = { line = item.lnum, character = 0 }, + }, + } + + item.uri = 'file://' .. cur_dir .. sep .. item.filename + item.range = { + start = { line = item.lnum, character = 0 }, + ['end'] = { line = item.lnum, character = 0 }, + } + + -- item.detail = (item.containerName or '') .. (item.ref or '') + -- item.text = '[' .. kind .. ']' .. item.name .. ' ' .. item.detail + + if item.lnum == nil then + vim.notify('incorrect ctags format, need run ctag with "-excmd=number|combine" option') + end + item.remain = nil + return item +end + +local function ctags_gen() + local cmd = 'ctags' -- -x -n -u -f - ' .. vfn.expand('%:p') + local output = _NgConfigValues.ctags.tagfile + -- rm file first + util.rm_file(output) + local options = '-R --exclude=.git --exclude=node_modules --exclude=test --exclude=vendor --excmd=number ' + if _NgConfigValues.ctags then + cmd = _NgConfigValues.ctags.cmd + options = _NgConfigValues.ctags.options or options + end + + local lang = vim.o.ft + options = options .. '--language=' .. lang + cmd = cmd .. ' ' .. options + cmd = string.format('%s -f %s %s --language=%s', cmd, output, options, lang) + cmd = vim.split(cmd, ' ') + log(cmd) + vfn.jobstart(cmd, { + on_stdout = function(_, _, _) + vim.notify('ctags completed') + end, + + on_exit = function(_, data, _) -- id, data, event + -- log(vim.inspect(data) .. "exit") + if data and data ~= 0 then + return vim.notify(cmd .. ' failed ' .. tostring(data), vim.lsp.log_levels.ERROR) + else + vim.notify('ctags generated') + end + end, + }) +end + +local symbols_to_items = require('navigator.lspwrapper').symbols_to_items +local function ctags_symbols() + local height = _NgConfigValues.height or 0.4 + local width = _NgConfigValues.width or 0.7 + height = math.floor(height * vfn.winheight('%')) + width = math.floor(width * vfn.winwidth('%')) + local items = {} + local ctags_file = _NgConfigValues.ctags.tagfile + if not util.file_exists(ctags_file) then + ctags_gen() + vim.cmd('sleep 200m') + end + local cnts = util.io_read(ctags_file) + if cnts == nil then + return vim.notify('ctags file ' .. ctags_file .. ' not found') + end + cnts = vfn.split(cnts, '\n') + for _, value in pairs(cnts) do + local it = entry_to_item(value) + if it then + table.insert(items, it) + end + end + cnts = nil + + local ft = vim.o.ft + local result = symbols_to_items(items) + if next(result) == nil then + return vim.notify('no symbols found') + end + log(result[1]) + local opt = { + api = '๏€ซ ', + ft = ft, + bg = 'GHListDark', + data = result, + items = result, + enter = true, + loc = 'top_center', + transparency = 50, + prompt = true, + rawdata = true, + rect = { height = height, pos_x = 0, pos_y = 0, width = width }, + } + + require('navigator.gui').new_list_view(opt) +end + +-- gen_ctags() + +local function ctags(...) + local gen = select(1, ...) + log(gen) + if gen == '-g' then + ctags_gen() + vim.cmd('sleep 200m') + ctags_symbols() + else + ctags_symbols() + end +end + +local function testitem() + local e = [[ServerResponse internal/clients/server.go /^type ServerResponse struct {$/;" s package:client]] + local ecombine = [[ServerResponse internal/clients/server.go 5;/^type ServerResponse struct {$/;" s package:client]] + local enumber = [[CustomerID internal/store/models.go 17;" m struct:store.Customer typeref:typename:string]] + local enumber2 = [[CustomerDescription internal/controllers/customer.go 27;" c package:controllers]] + local enumber3 = [[add_servers lua/navigator/lspclient/clients.lua 680;" f]] + local i = entry_to_item(ecombine) + print(vim.inspect(i)) + + i = entry_to_item(enumber) + print(vim.inspect(i)) + + i = entry_to_item(enumber2) + print(vim.inspect(i)) + + i = entry_to_item(enumber3) + print(vim.inspect(i)) + i = entry_to_item(e) + print(vim.inspect(i)) +end +-- testitem() +-- gen_ctags() +-- ctags_symbols() + +return { + ctags_gen = ctags_gen, + ctags = ctags, + ctags_symbols = ctags_symbols, +} diff --git a/lua/navigator/definition.lua b/lua/navigator/definition.lua index 56792b5..b3b114e 100644 --- a/lua/navigator/definition.lua +++ b/lua/navigator/definition.lua @@ -5,10 +5,10 @@ local gui = require('navigator.gui') local log = util.log local TextView = require('guihua.textview') -- callback for lsp definition, implementation and declaration handler -local definition_hdlr = util.mk_handler(function(err, locations, ctx, _) +local definition_hdlr = function(err, locations, ctx, _) -- log(locations) if err ~= nil then - vim.notify('Defination: ', tostring(err) .. vim.inspect(ctx), vim.lsp.log_levels.WARN) + vim.notify('Defination: ' .. tostring(err) .. vim.inspect(ctx), vim.lsp.log_levels.WARN) return end if type(locations) == 'number' then @@ -31,7 +31,7 @@ local definition_hdlr = util.mk_handler(function(err, locations, ctx, _) else vim.lsp.util.jump_to_location(locations, oe) end -end) +end local function get_symbol() local currentWord = vim.fn.expand('') @@ -39,7 +39,7 @@ local function get_symbol() end local function def_preview(timeout_ms) - assert(#vim.lsp.buf_get_clients() > 0, 'Must have a client running') + assert(next(vim.lsp.buf_get_clients(0)), 'Must have a client running') local method = 'textDocument/definition' local params = vim.lsp.util.make_position_params() local result = vim.lsp.buf_request_sync(0, method, params, timeout_ms or 1000) @@ -85,6 +85,9 @@ local function def_preview(timeout_ms) local ts = require('navigator.treesitter') local root = parsers.get_parser(bufnr) log(range) + if ts == nil then + return + end local def_node = ts.get_node_at_pos({ range['start'].line, range['start'].character }, root) local sr, _, er, _ = ts.get_node_scope(def_node) @@ -105,7 +108,7 @@ local function def_preview(timeout_ms) end local width = 40 local maxwidth = math.floor(vim.fn.winwidth(0) * 4 / 5) - for key, value in pairs(definition) do + for _, value in pairs(definition) do -- log(key, value, width) width = math.max(width, #value + 4) width = math.min(maxwidth, width) @@ -118,7 +121,7 @@ local function def_preview(timeout_ms) relative = 'cursor', style = 'minimal', ft = filetype, - rect = { width = width, height = #definition + 3 }, + rect = { width = width, height = #definition + 3, pos_y = 2 }, data = definition, enter = true, border = _NgConfigValues.border or 'shadow', @@ -143,8 +146,9 @@ local def = function() local bufnr = vim.api.nvim_get_current_buf() local ref_params = vim.lsp.util.make_position_params() - vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr) - if client.resolved_capabilities.goto_definition then + vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) + -- if client.resolved_capabilities.goto_definition then + if client.server_capabilities.definitionProvider then client.request('textDocument/definition', ref_params, definition_hdlr, _bufnr) end end) diff --git a/lua/navigator/diagnostics.lua b/lua/navigator/diagnostics.lua index 8a24e0d..2829026 100644 --- a/lua/navigator/diagnostics.lua +++ b/lua/navigator/diagnostics.lua @@ -9,19 +9,13 @@ local trace = require('guihua.log').trace -- trace = log local error = util.error local path_sep = require('navigator.util').path_sep() -local mk_handler = require('navigator.util').mk_handler local path_cur = require('navigator.util').path_cur() local empty = util.empty -diagnostic_list[vim.bo.filetype] = {} -local function clear_diag_VT(bufnr) -- important for clearing out when no more errors - log(bufnr, _NG_VT_DIAG_NS) - if bufnr == nil or _NG_VT_DIAG_NS == nil then - return - end - vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) - _NG_VT_DIAG_NS = nil +if not util.nvim_0_6_1() then + util.warn('Navigator 0.4+ only support nvim-0.6+, please use Navigator 0.3.x or a newer version of neovim') end +diagnostic_list[vim.bo.filetype] = {} local diag_map = {} if vim.diagnostic then @@ -33,6 +27,25 @@ if vim.diagnostic then } end +local diagnostic_cfg = { + -- Enable underline, use default values + underline = _NgConfigValues.lsp.diagnostic.underline, + -- Enable virtual + -- Use a function to dynamically turn signs off + -- and on, using buffer local variables + signs = true, + update_in_insert = _NgConfigValues.lsp.diagnostic.update_in_insert or false, + severity_sort = _NgConfigValues.lsp.diagnostic.severity_sort, + float = { + focusable = false, + style = 'minimal', + border = 'rounded', + source = 'always', + header = '', + prefix = '', + }, +} + local function get_count(bufnr, level) if vim.diagnostic ~= nil then return #diagnostic.get(bufnr, { severity = diag_map[level] }) @@ -145,7 +158,7 @@ local function error_marker(result, ctx, config) if not vim.tbl_isempty(pos) then vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) end - for i, s in pairs(pos) do + for _, s in pairs(pos) do local hl = 'ErrorMsg' if type(s.severity) == 'number' then if s.severity == 2 then @@ -180,8 +193,9 @@ local update_err_marker_async = function() return debounce(400, error_marker) end -local diag_hdlr = mk_handler(function(err, result, ctx, config) +local diag_hdlr = function(err, result, ctx, config) require('navigator.lspclient.highlight').diagnositc_config_sign() + config = config or diagnostic_cfg if err ~= nil then log(err, config, result) return @@ -203,19 +217,14 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config) trace('diagnostic', result.diagnostics, ctx, config) end - if util.nvim_0_6() then - trace(err, result, ctx, config) - vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config) - else - log('old version of lsp nvim 050') - vim.lsp.diagnostic.on_publish_diagnostics(err, _, result, ctx.client_id, _, config) - end + trace(err, result, ctx, config) + vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config) local uri = result.uri local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) if empty(result.diagnostics) and diag_cnt > 0 then - log('no result? ', diag_cnt) + trace('no result? ', diag_cnt) return end -- trace("diag: ", mode, result, ctx, config) @@ -295,39 +304,38 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config) vim.api.nvim_buf_clear_namespace(0, _NG_VT_DIAG_NS, 0, -1) _NG_VT_DIAG_NS = nil end -end) - -local diag_hdlr_async = function() - local debounce = require('navigator.debounce').debounce_trailing - return debounce(100, diag_hdlr) end +-- local diag_hdlr_async = function() +-- local debounce = require('navigator.debounce').debounce_trailing +-- return debounce(100, diag_hdlr) +-- end + local M = {} -local diagnostic_cfg = { - -- Enable underline, use default values - underline = true, - -- Enable virtual text, override spacing to 3 (prevent overlap) - virtual_text = { - spacing = 3, - prefix = _NgConfigValues.icons.icons and _NgConfigValues.icons.diagnostic_virtual_text or "" }, - -- Use a function to dynamically turn signs off - -- and on, using buffer local variables - signs = true, - update_in_insert = _NgConfigValues.lsp.diagnostic_update_in_insert or false, - severity_sort = { reverse = true }, -} -if _NgConfigValues.lsp.diagnostic_virtual_text == false then - diagnostic_cfg.virtual_text = false +diagnostic_cfg.virtual_text = _NgConfigValues.lsp.diagnostic.virtual_text +if type(_NgConfigValues.lsp.diagnostic.virtual_text) == 'table' then + diagnostic_cfg.virtual_text.prefix = _NgConfigValues.icons.diagnostic_virtual_text end - -- vim.lsp.handlers["textDocument/publishDiagnostics"] M.diagnostic_handler = vim.lsp.with(diag_hdlr, diagnostic_cfg) +vim.diagnostic.config(diagnostic_cfg) + +local function clear_diag_VT(bufnr) -- important for clearing out when no more errors + bufnr = bufnr or vim.api.nvim_get_current_buf() + log(bufnr, _NG_VT_DIAG_NS) + if _NG_VT_DIAG_NS == nil then + return + end + + vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) + _NG_VT_DIAG_NS = nil +end + M.hide_diagnostic = function() if _NG_VT_DIAG_NS then - vim.api.nvim_buf_clear_namespace(0, _NG_VT_DIAG_NS, 0, -1) - _NG_VT_DIAG_NS = nil + clear_diag_VT() end end @@ -342,8 +350,6 @@ end M.show_buf_diagnostics = function() if diagnostic_list[vim.bo.filetype] ~= nil then - -- log(diagnostic_list[vim.bo.filetype]) - -- vim.fn.setqflist({}, " ", {title = "LSP", items = diagnostic_list[vim.bo.filetype]}) local results = diagnostic_list[vim.bo.filetype] local display_items = {} for _, client_items in pairs(results) do @@ -360,8 +366,13 @@ M.show_buf_diagnostics = function() api = _NgConfigValues.icons.diagnostic_file .. _NgConfigValues.icons.diagnostic_head .. ' Diagnostic ', enable_preview_edit = true, }) + if listview == nil then + return log('nil listview') + end trace('new buffer', listview.bufnr) - vim.api.nvim_buf_add_highlight(listview.bufnr, -1, 'Title', 0, 0, -1) + if listview.bufnr then + vim.api.nvim_buf_add_highlight(listview.bufnr, -1, 'Title', 0, 0, -1) + end end end end @@ -374,14 +385,15 @@ M.set_diag_loclist = function() log('great, no errors!') return end - local clients = vim.lsp.buf_get_clients(0) + + local clients = vim.lsp.buf_get_clients(bufnr) local cfg = { open = diag_cnt > 0 } for _, client in pairs(clients) do cfg.client_id = client['id'] break end - if not vim.tbl_isempty(vim.lsp.buf_get_clients(0)) then + if not vim.tbl_isempty(vim.lsp.buf_get_clients(bufnr)) then local err_cnt = get_count(0, [[Error]]) if err_cnt > 0 and _NgConfigValues.lsp.disply_diagnostic_qf then if diagnostic.set_loclist then @@ -431,11 +443,6 @@ function M.update_err_marker() marker(result, { bufnr = bufnr, method = 'textDocument/publishDiagnostics' }) end --- TODO: update the marker -if _NgConfigValues.diagnostic_scrollbar_sign then - vim.notify('config deprecated, set lsp.diagnostic_scrollbar_sign instead', vim.lsp.log_levels.WARN) -end - if _NgConfigValues.lsp.diagnostic_scrollbar_sign then vim.cmd([[autocmd WinScrolled * lua require'navigator.diagnostics'.update_err_marker()]]) end @@ -462,4 +469,53 @@ function M.show_diagnostics(pos) end end +function M.treesitter_and_diag_panel() + local Panel = require('guihua.panel') + + local ft = vim.bo.filetype + local results = diagnostic_list[ft] + log(diagnostic_list, ft) + + local bufnr = vim.api.nvim_get_current_buf() + local p = Panel:new({ + header = 'treesitter', + render = function(b) + log('render for ', bufnr, b) + return require('navigator.treesitter').all_ts_nodes(b) + end, + }) + p:add_section({ + header = 'diagnostic', + render = function(bufnr) + if diagnostic_list[ft] ~= nil then + local display_items = {} + for _, client_items in pairs(results) do + for _, items in pairs(client_items) do + for _, it in pairs(items) do + log(it) + table.insert(display_items, it) + end + end + end + return display_items + else + return {} + end + end, + }) + p:open(true) +end + +function M.config(cfg) + cfg = cfg or {} + local default_cfg = { + underline = true, + virtual_text = true, + signs = { _NgConfigValues.icons.diagnostic_err }, + update_in_insert = false, + } + cfg = vim.tbl_extend('keep', cfg, default_cfg) + vim.diagnostic.config(cfg) +end + return M diff --git a/lua/navigator/dochighlight.lua b/lua/navigator/dochighlight.lua index a00cbde..f2025e8 100644 --- a/lua/navigator/dochighlight.lua +++ b/lua/navigator/dochighlight.lua @@ -1,7 +1,6 @@ local util = require('navigator.util') local log = util.log local trace = util.trace -local mk_handler = util.mk_handler local api = vim.api local references = {} _NG_hi_list = {} @@ -139,7 +138,7 @@ local function before(r1, r2) return false end -local handle_document_highlight = mk_handler(function(_, result, ctx) +local handle_document_highlight = function(_, result, ctx) trace(result, ctx) if not ctx.bufnr then log('ducment highlight error', result, ctx) @@ -157,7 +156,7 @@ local handle_document_highlight = mk_handler(function(_, result, ctx) references[ctx.bufnr] = result local client_id = ctx.client_id vim.lsp.util.buf_highlight_references(ctx.bufnr, result, util.encoding(client_id)) -end) +end -- modify from vim-illuminate local function goto_adjent_reference(opt) trace(opt) @@ -206,11 +205,11 @@ local function cmd_nohl() end end -_G.nav_doc_hl = function() +local nav_doc_hl = function() local bufnr = vim.api.nvim_get_current_buf() local ref_params = vim.lsp.util.make_position_params() - vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, bufnr) - if client.resolved_capabilities.document_highlight then + vim.lsp.for_each_buffer_client(bufnr, function(client, _, _) + if client.server_capabilities.documentHighlightProvider == true then client.request('textDocument/documentHighlight', ref_params, handle_document_highlight, bufnr) end end) @@ -219,20 +218,15 @@ end local function documentHighlight() api.nvim_exec( [[ - autocmd ColorScheme * | - hi default LspReferenceRead cterm=bold gui=Bold ctermbg=yellow guifg=yellow guibg=purple4 | - hi default LspReferenceText cterm=bold gui=Bold ctermbg=red guifg=SlateBlue guibg=MidnightBlue | - hi default LspReferenceWrite cterm=bold gui=Bold,Italic ctermbg=red guifg=DarkSlateBlue guibg=MistyRose - augroup lsp_document_highlight autocmd! * - autocmd CursorHold lua nav_doc_hl() + autocmd CursorHold,CursorHoldI lua require('navigator.dochighlight').nav_doc_hl() autocmd CursorMoved lua vim.lsp.buf.clear_references() augroup END ]], false ) - vim.lsp.handlers['textDocument/documentHighlight'] = mk_handler(function(err, result, ctx) + vim.lsp.handlers['textDocument/documentHighlight'] = function(err, result, ctx) local bufnr = ctx.bufnr or api.nvim_get_current_buf() if err then vim.notify(err, vim.lsp.log_levels.ERROR) @@ -255,7 +249,7 @@ local function documentHighlight() end) references[bufnr] = result add_locs(bufnr, result) - end) + end end return { @@ -264,5 +258,6 @@ return { handle_document_highlight = handle_document_highlight, hi_symbol = hi_symbol, nohl = nohl, + nav_doc_hl = nav_doc_hl, cmd_nohl = cmd_nohl, } diff --git a/lua/navigator/foldlsp.lua b/lua/navigator/foldlsp.lua index b826046..ce05ae7 100644 --- a/lua/navigator/foldlsp.lua +++ b/lua/navigator/foldlsp.lua @@ -1,5 +1,4 @@ -local log = require"navigator.util".log -local mk_handler = require"navigator.util".mk_handler +local log = require('navigator.util').log local lsp = vim.lsp local api = vim.api @@ -18,7 +17,7 @@ M.servers_supporting_folding = { texlab = true, clangd = false, gopls = true, - julials = false + julials = false, } M.active_folding_clients = {} @@ -29,11 +28,11 @@ function M.on_attach() end function M.setup_plugin() - api.nvim_command("augroup FoldingCommand") - api.nvim_command("autocmd! * ") + api.nvim_command('augroup FoldingCommand') + api.nvim_command('autocmd! * ') api.nvim_command("autocmd BufEnter lua require'navigator.foldlsp'.update_folds()") api.nvim_command("autocmd BufWritePost lua require'navigator.foldlsp'.update_folds()") - api.nvim_command("augroup end") + api.nvim_command('augroup end') -- vim.cmd([[ -- @@ -43,7 +42,7 @@ function M.setup_plugin() -- -- ]]) - local clients = vim.lsp.buf_get_clients() + local clients = vim.lsp.buf_get_clients(0) for _, client in pairs(clients) do local client_id = client['id'] @@ -57,7 +56,6 @@ function M.setup_plugin() M.active_folding_clients[client_id] = server_supports_folding end end - -- print(vim.inspect(M.active_folding_clients)) end function M.update_folds() @@ -73,9 +71,8 @@ function M.update_folds() -- XXX: better to pass callback in this method or add it directly in the config? -- client.config.callbacks['textDocument/foldingRange'] = M.fold_handler local current_bufnr = api.nvim_get_current_buf() - local params = {uri = vim.uri_from_bufnr(current_bufnr)} - client.request('textDocument/foldingRange', {textDocument = params}, M.fold_handler, - current_bufnr) + local params = { uri = vim.uri_from_bufnr(current_bufnr) } + client.request('textDocument/foldingRange', { textDocument = params }, M.fold_handler, current_bufnr) end end end @@ -89,11 +86,11 @@ function M.debug_folds() end end -M.fold_handler = mk_handler(function(err, result, ctx, config) +M.fold_handler = function(err, result, ctx, _) -- params: err, method, result, client_id, bufnr -- XXX: handle err? if err or result == nil or #result == 0 then - vim.notify(string.format("%s %s ", tostring(err), vim.inspect(ctx)), vim.lsp.log_levels.WARN) + vim.notify(string.format('%s %s ', tostring(err), vim.inspect(ctx)), vim.lsp.log_levels.WARN) return end M.debug_folds() @@ -113,7 +110,7 @@ M.fold_handler = mk_handler(function(err, result, ctx, config) api.nvim_win_set_option(current_window, 'foldmethod', 'expr') api.nvim_win_set_option(current_window, 'foldexpr', 'foldlsp#foldexpr()') end -end) +end function M.adjust_foldstart(line_no) return line_no + 1 @@ -160,13 +157,12 @@ function M.get_fold_indic(lnum) -- without any marker. return fold_level elseif is_foldstart then - return string.format(">%d", fold_level) + return string.format('>%d', fold_level) elseif is_foldend then - return string.format("<%d", fold_level) + return string.format('<%d', fold_level) else return fold_level end - end return M diff --git a/lua/navigator/foldts.lua b/lua/navigator/foldts.lua index d96fe0f..1219d93 100644 --- a/lua/navigator/foldts.lua +++ b/lua/navigator/foldts.lua @@ -1,6 +1,7 @@ -- NOTE: this file is a modified version of fold.lua from nvim-treesitter local log = require('navigator.util').log +local trace = require('navigator.util').trace local api = vim.api local tsutils = require('nvim-treesitter.ts_utils') local query = require('nvim-treesitter.query') @@ -16,15 +17,14 @@ function M.on_attach() -- M.update_folds() end -function _G.custom_fold_text() +function NG_custom_fold_text() local line = vim.fn.getline(vim.v.foldstart) local line_count = vim.v.foldend - vim.v.foldstart + 1 -- log("" .. line .. " // " .. line_count .. " lines") return ' โšก' .. line .. ': ' .. line_count .. ' lines' end -vim.opt.foldtext = custom_fold_text() - +vim.opt.foldtext = NG_custom_fold_text() vim.opt.fillchars = { eob = '-', fold = ' ' } vim.opt.viewoptions:remove('options') @@ -38,13 +38,13 @@ function M.setup_fold() api.nvim_command('augroup FoldingCommand') api.nvim_command('autocmd! * ') api.nvim_command('augroup end') - vim.opt.foldtext = 'v:lua.custom_fold_text()' + vim.opt.foldtext = 'v:lua.NG_custom_fold_text()' vim.opt.fillchars = { eob = '-', fold = ' ' } vim.opt.viewoptions:remove('options') local current_window = api.nvim_get_current_win() api.nvim_win_set_option(current_window, 'foldmethod', 'expr') - api.nvim_win_set_option(current_window, 'foldexpr', 'folding#foldexpr()') + api.nvim_win_set_option(current_window, 'foldexpr', 'folding#ngfoldexpr()') end -- This is cached on buf tick to avoid computing that multiple times @@ -61,7 +61,7 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr) local parser = parsers.get_parser(bufnr) if not parser then - warn('treesitter parser not loaded') + log('treesitter parser not loaded') return {} end @@ -148,12 +148,12 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr) levels[lnum + 1] = tostring(trimmed_level) else -- if levels[lnum + 1] == nil then - levels[lnum + 1] = tostring(trimmed_level + 1) + levels[lnum + 1] = tostring(trimmed_level + 1) -- end end end end - log(levels) + trace(levels) return levels end) diff --git a/lua/navigator/formatting.lua b/lua/navigator/formatting.lua index ad9ce6c..9ad1cc8 100644 --- a/lua/navigator/formatting.lua +++ b/lua/navigator/formatting.lua @@ -1,8 +1,7 @@ -- https://github.com/wention/dotfiles/blob/master/.config/nvim/lua/config/lsp.lua -- https://github.com/lukas-reineke/dotfiles/blob/master/vim/lua/lsp/handlers.lua -local mk_handler = require('navigator.util').mk_handler return { - format_hdl = mk_handler(function(err, result, ctx, cfg) -- FIXME: bufnr is nil + format_hdl = function(err, result, ctx, _) -- FIXME: bufnr is nil if err ~= nil or result == nil then return end @@ -31,5 +30,5 @@ return { -- end end end, 100) - end), + end, } diff --git a/lua/navigator/gui.lua b/lua/navigator/gui.lua index c9b20dc..24c7259 100644 --- a/lua/navigator/gui.lua +++ b/lua/navigator/gui.lua @@ -8,11 +8,11 @@ local api = vim.api local active_list_view -- only one listview at a time function M.new_list_view(opts) - log(opts) + -- log(opts) local config = require('navigator').config_values() if active_list_view ~= nil then - log(active_list_view) + trace(active_list_view) local winnr = active_list_view.win local bufnr = active_list_view.buf @@ -23,40 +23,40 @@ function M.new_list_view(opts) end local items = opts.items - opts.min_width = opts.min_width or 0.3 - opts.min_height = opts.min_height or 0.3 - - opts.height_ratio = config.height - opts.width_ratio = config.width - opts.preview_height_ratio = _NgConfigValues.preview_height or 0.3 - opts.preview_lines = _NgConfigValues.preview_lines + opts.height_ratio = opts.height or config.height + opts.width_ratio = opts.height or config.width + opts.preview_height_ratio = opts.preview_height or config.preview_height + opts.preview_lines = config.preview_lines if opts.rawdata then opts.data = items else opts.data = require('navigator.render').prepare_for_render(items, opts) end - opts.border = _NgConfigValues.border or 'shadow' + opts.border = config.border or 'shadow' + if vim.fn.hlID('TelescopePromptBorder') > 0 then + opts.border_hl = 'TelescopePromptBorder' + end if not items or vim.tbl_isempty(items) then log('empty data return') return end - opts.transparency = _NgConfigValues.transparency - if #items >= _NgConfigValues.lines_show_prompt then + opts.transparency = config.transparency + if #items >= config.lines_show_prompt then opts.prompt = true end - opts.external = _NgConfigValues.external - opts.preview_lines_before = 3 - log(opts) + opts.external = config.external + opts.preview_lines_before = 4 + if _NgConfigValues.debug then + local logopts = { items = {}, data = {} } + logopts = vim.tbl_deep_extend('keep', logopts, opts) + log(logopts) + end active_list_view = require('guihua.gui').new_list_view(opts) return active_list_view end -function M.select(items, opts, on_choice) - return -end - return M -- Doc diff --git a/lua/navigator/hierarchy.lua b/lua/navigator/hierarchy.lua index d71532a..da55047 100644 --- a/lua/navigator/hierarchy.lua +++ b/lua/navigator/hierarchy.lua @@ -1,102 +1,311 @@ -local gui = require "navigator.gui" -local util = require "navigator.util" +local gui = require('navigator.gui') +local util = require('navigator.util') local log = util.log local trace = util.trace local partial = util.partial -local lsphelper = require "navigator.lspwrapper" +local lsphelper = require('navigator.lspwrapper') -local path_sep = require"navigator.util".path_sep() -local path_cur = require"navigator.util".path_cur() +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 function call_hierarchy_handler(direction, err, result, ctx, cfg, error_message) +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 + +-- 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) + vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN) return end - trace('call_hierarchy', result) - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags") + -- trace('call_hierarchy', result) + + 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: " .. error_message, vim.lsp.log_levels.WARN) + log('dir', direction, 'result', result, 'err', err, ctx) + vim.notify('ERROR: ' .. err, vim.lsp.log_levels.WARN) return end - local items = {} + local items = ctx.items or {} - for _, call_hierarchy_call in pairs(result) do - local call_hierarchy_item = call_hierarchy_call[direction] - local kind = '๏‚š ' + local kind = '๏‚š ' + for _, call_hierarchy_result in pairs(result) do + local call_hierarchy_item = call_hierarchy_result[direction] 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 - range = call_hierarchy_item.range or call_hierarchy_item.selectionRange local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)) local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1) - call_hierarchy_item.detail = call_hierarchy_item.detail or "" - call_hierarchy_item.detail = call_hierarchy_item.detail:gsub("\n", " โ†ณ ") - trace(range, call_hierarchy_item) + call_hierarchy_item.detail = call_hierarchy_item.detail or '' + call_hierarchy_item.detail = string.gsub(call_hierarchy_item.detail, '\n', ' โ†ณ ') + trace(call_hierarchy_item) - local disp_item = { - uri = call_hierarchy_item.uri, + 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_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, - range = range, - lnum = range.start.line + 1, - col = range.start.character - } - + lnum = call_hierarchy_item.selectionRange.start.line + 1, + col = call_hierarchy_item.selectionRange.start.character, + }) table.insert(items, disp_item) - -- end end + 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') -local function incoming_calls_handler(bang, err, result, ctx, cfg) - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp hierarchy") - local results = call_hierarchy_handler_from(err, result, ctx, cfg, "Incoming calls not found") +-- 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 ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft") - gui.new_list_view({items = results, ft = ft, api = '๏ฃถ '}) + 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 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 -local function outgoing_calls_handler(bang, err, result, ctx, cfg) - local results = call_hierarchy_handler_to(err, result, ctx, cfg, "Outgoing calls not found") +local make_params = function(uri, pos) + return { + textDocument = { + uri = uri, + }, + position = pos, + } +end - local ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft") - gui.new_list_view({items = results, ft = ft, api = '๏ฃบ '}) - -- fzf_locations(bang, "", "Outgoing Calls", results, false) +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 -function M.incoming_calls(bang, opts) - assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp hierarchy") - if not lsphelper.check_capabilities("call_hierarchy") then - return +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 + 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 + 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 - local params = vim.lsp.util.make_position_params() - lsphelper.call_sync("callHierarchy/incomingCalls", params, opts, partial(incoming_calls_handler, bang)) + local bufnr = vim.uri_to_bufnr(node.uri) + call_hierarchy(node.method, { + params = params, + panel = panel, + parent_node = node, + handler = handler, + bufnr = bufnr, + }) 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 +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) + return request( + bufnr, + 'textDocument/prepareCallHierarchy', + params, + vim.lsp.with(function(err, result, ctx) + if err then + vim.notify(err.message, vim.log.levels.WARN) + return + end + local call_hierarchy_item = pick_call_hierarchy_item(result) + local client = vim.lsp.get_client_by_id(ctx.client_id) + if client then + 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 stopped', ctx.client_id), vim.log.levels.WARN) + end + end, { direction = method, depth = opts.depth }) + ) +end + +function M.incoming_calls(opts) + call_hierarchy(in_method, opts) +end - local params = vim.lsp.util.make_position_params() - lsphelper.call_sync("callHierarchy/outgoingCalls", params, opts, partial(outgoing_calls_handler, bang)) +function M.outgoing_calls(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 + +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 = partial(incoming_calls_handler, 0) -M.outgoing_calls_handler = partial(outgoing_calls_handler, 0) +M.incoming_calls_handler = incoming_calls_handler +M.outgoing_calls_handler = outgoing_calls_handler +-- for testing +M._call_hierarchy = call_hierarchy +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/implementation.lua b/lua/navigator/implementation.lua index 6096e60..f19ccdb 100644 --- a/lua/navigator/implementation.lua +++ b/lua/navigator/implementation.lua @@ -1,14 +1,13 @@ local util = require('navigator.util') -local mk_handler = util.mk_handler local lsphelper = require('navigator.lspwrapper') local gui = require('navigator.gui') local M = {} -local location = require('guihua.location') +-- local location = require('guihua.location') local partial = util.partial local locations_to_items = lsphelper.locations_to_items local log = util.log -- dataformat should be same as reference -local function location_handler(err, locations, ctx, cfg, msg) +local function location_handler(err, locations, ctx, _, msg) if err ~= nil then vim.notify('ERROR: ' .. tostring(err) .. ' ' .. msg, vim.lsp.log_levels.WARN) return @@ -16,14 +15,14 @@ local function location_handler(err, locations, ctx, cfg, msg) return locations_to_items(locations, ctx) end -local function implementation_handler(bang, err, result, ctx, cfg) +local function implementation_handler(_, err, result, ctx, cfg) local results = location_handler(err, result, ctx, cfg, 'Implementation not found') local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft') gui.new_list_view({ items = results, ft = ft, api = 'Implementation' }) end function M.implementation(bang, opts) - if not lsphelper.check_capabilities('implementation') then + if not lsphelper.check_capabilities('implementationProvider') then return end diff --git a/lua/navigator/lazyloader.lua b/lua/navigator/lazyloader.lua index c72022f..84f87a4 100644 --- a/lua/navigator/lazyloader.lua +++ b/lua/navigator/lazyloader.lua @@ -22,10 +22,16 @@ return { loader(plugin) end end + else + loader = function(plugin) + local cmd = 'packadd ' .. plugin + vim.cmd(cmd) + end end if _NgConfigValues.lsp_installer == true then - local has_lspinst, lspinst = pcall(require, 'lsp_installer') + vim.cmd('packadd nvim-lsp-installer') + local has_lspinst, lspinst = pcall(require, 'nvim-lsp-installer') log('lsp_installer installed', has_lspinst) if has_lspinst then lspinst.setup() @@ -51,7 +57,7 @@ return { local lazy_plugins = {} lazy_plugins[plugin_name] = path loader = require('packer').loader - for plugin, url in pairs(lazy_plugins) do + for plugin, _ in pairs(lazy_plugins) do if packer_plugins[plugin] and packer_plugins[plugin].loaded == false then -- log("loading ", plugin) pcall(loader, plugin) diff --git a/lua/navigator/lspclient/attach.lua b/lua/navigator/lspclient/attach.lua index 2c9ef45..6895ce0 100644 --- a/lua/navigator/lspclient/attach.lua +++ b/lua/navigator/lspclient/attach.lua @@ -5,18 +5,13 @@ local util = require('navigator.util') local log = util.log local trace = util.trace -local diagnostic_map = function(bufnr) - local opts = { noremap = true, silent = true } - api.nvim_buf_set_keymap(bufnr, 'n', ']O', ':lua vim.lsp.diagnostic.set_loclist()', opts) -end - local M = {} M.on_attach = function(client, bufnr) bufnr = bufnr or 0 if bufnr == 0 then - vim.notify('no bufnr provided from LSP ', client.name) + vim.notify('no bufnr provided from LSP ' .. client.name, vim.log.levels.DEBUG) end local uri = vim.uri_from_bufnr(bufnr) @@ -29,7 +24,6 @@ M.on_attach = function(client, bufnr) trace(client) - diagnostic_map(bufnr) -- add highlight for Lspxxx require('navigator.lspclient.highlight').add_highlight() require('navigator.lspclient.highlight').diagnositc_config_sign() @@ -38,10 +32,9 @@ M.on_attach = function(client, bufnr) require('navigator.lspclient.mapping').setup({ client = client, bufnr = bufnr, - cap = client.resolved_capabilities, }) - if client.resolved_capabilities.document_highlight then + if client.server_capabilities.documentHighlightProvider == true then require('navigator.dochighlight').documentHighlight() end @@ -53,15 +46,25 @@ M.on_attach = function(client, bufnr) log(client.name, 'customized attach for all clients') config.on_attach(client, bufnr) end - if config.lsp and config.lsp[client.name] and config.lsp[client.name].on_attach ~= nil then - log('lsp client specific attach for', client.name) - config.lsp[client.name].on_attach(client, bufnr) + if config.lsp and config.lsp[client.name] then + if type(config.lsp[client.name]) == 'function' then + local attach = config.lsp[client.name]().on_attach + if attach then + attach(client, bufnr) + end + elseif config.lsp[client.name].on_attach ~= nil then + log(client.name, 'customized attach for this client') + log('lsp client specific attach for', client.name) + config.lsp[client.name].on_attach(client, bufnr) + end end if _NgConfigValues.lsp.code_action.enable then - if client.resolved_capabilities.code_action then - log('code action enabled for client', client.resolved_capabilities.code_action) + if client.server_capabilities.codeActionProvider and client.name ~= 'null-ls' then + log('code action enabled for client', client.server_capabilities.codeActionProvider) + api.nvim_command('augroup NCodeAction') vim.cmd([[autocmd CursorHold,CursorHoldI lua require'navigator.codeAction'.code_action_prompt()]]) + api.nvim_command('augroup end') end end end diff --git a/lua/navigator/lspclient/clients.lua b/lua/navigator/lspclient/clients.lua index e88eca0..098a61c 100644 --- a/lua/navigator/lspclient/clients.lua +++ b/lua/navigator/lspclient/clients.lua @@ -1,9 +1,9 @@ -local util = require('navigator.util') -local log = util.log -local trace = util.trace -local uv = vim.loop -local empty = util.empty -local warn = util.warn +-- todo allow config passed in +local ng_util = require('navigator.util') +local log = ng_util.log +local trace = ng_util.trace +local empty = ng_util.empty +local warn = ng_util.warn _NG_Loaded = {} _LoadedFiletypes = {} @@ -24,7 +24,25 @@ end local util = lspconfig.util local config = require('navigator').config_values() - +local disabled_ft = { + 'NvimTree', + 'guihua', + 'clap_input', + 'clap_spinner', + 'vista', + 'vista_kind', + 'TelescopePrompt', + 'guihua_rust', + 'csv', + 'txt', + 'defx', + 'packer', + 'gitcommit', + 'windline', + 'notify', + 'nofile', + '', +} -- local cap = vim.lsp.protocol.make_client_capabilities() local on_attach = require('navigator.lspclient.attach').on_attach -- gopls["ui.completion.usePlaceholders"] = true @@ -46,16 +64,14 @@ local luadevcfg = { local luadev = {} require('navigator.lazyloader').load('lua-dev.nvim', 'folke/lua-dev.nvim') +if _NgConfigValues.lsp_installer then + require('navigator.lazyloader').load('nvim-lsp-installer', 'williamboman/nvim-lsp-installer') +end local ok, l = pcall(require, 'lua-dev') if ok and l then luadev = l.setup(luadevcfg) end -local path = vim.split(package.path, ';') - -table.insert(path, 'lua/?.lua') -table.insert(path, 'lua/?/init.lua') - local function add(lib) for _, p in pairs(vim.fn.expand(lib, false, true)) do p = vim.loop.fs_realpath(p) @@ -113,17 +129,17 @@ local setups = { elixirls = { on_attach = on_attach, - filetypes = { 'elixir', 'eelixir'}, - cmd = {'elixir-ls'}, + filetypes = { 'elixir', 'eelixir' }, + cmd = { 'elixir-ls' }, message_level = vim.lsp.protocol.MessageType.error, settings = { elixirLS = { - dialyzerEnabled = true, fetchDeps = false - } + dialyzerEnabled = true, + fetchDeps = false, + }, }, root_dir = function(fname) - return util.root_pattern('mix.exs', '.git')(fname) - or util.path.dirname(fname) + return util.root_pattern('mix.exs', '.git')(fname) or util.path.dirname(fname) end, }, @@ -182,9 +198,10 @@ local setups = { '--cross-file-rename', }, filetypes = { 'c', 'cpp', 'objc', 'objcpp' }, - on_attach = function(client) - client.resolved_capabilities.document_formatting = true - on_attach(client) + on_attach = function(client, bufnr) + client.server_capabilities.documentFormattingProvider = client.server_capabilities.documentFormattingProvider + or true + on_attach(client, bufnr) end, }, rust_analyzer = { @@ -205,8 +222,8 @@ local setups = { }, sqls = { filetypes = { 'sql' }, - on_attach = function(client, bufnr) - client.resolved_capabilities.execute_command = true + on_attach = function(client, _) + client.server_capabilities.executeCommandProvider = client.server_capabilities.documentFormattingProvider or true highlight.diagnositc_config_sign() require('sqls').setup({ picker = 'telescope' }) -- or default end, @@ -232,8 +249,6 @@ local setups = { runtime = { -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim) version = 'LuaJIT', - -- Setup your lua path - path = vim.split(package.path, ';'), }, diagnostics = { enable = true, @@ -291,6 +306,14 @@ local setups = { omnisharp = { cmd = { 'omnisharp', '--languageserver', '--hostPID', tostring(vim.fn.getpid()) }, }, + terraformls = { + filetypes = { 'terraform', 'tf' }, + }, + + sourcekit = { + cmd = { 'sourcekit-lsp' }, + filetypes = { 'swift' }, -- This is recommended if you have separate settings for clangd. + }, } setups.sumneko_lua = vim.tbl_deep_extend('force', luadev, setups.sumneko_lua) @@ -333,7 +356,13 @@ local servers = { 'svelte', 'texlab', 'clojure_lsp', - 'elixirls' + 'elixirls', + 'sourcekit', + 'fsautocomplete', + 'vls', + 'hls', + 'tflint', + 'terraform_lsp', } local lsp_installer_servers = {} @@ -359,16 +388,8 @@ local ng_default_cfg = { flags = { allow_incremental_sync = true, debounce_text_changes = 1000 }, } -local configs = {} - -- check and load based on file type local function load_cfg(ft, client, cfg, loaded) - -- if _NG_LSPCfgSetup ~= true then - -- log(lspconfig_setup) - -- lspconfig_setup(cfg) - -- _NG_LSPCfgSetup = true - -- end - log(ft, client, loaded) trace(cfg) if lspconfig[client] == nil then @@ -377,7 +398,9 @@ local function load_cfg(ft, client, cfg, loaded) end local lspft = lspconfig[client].document_config.default_config.filetypes + local additional_ft = setups[client] and setups[client].filetypes or {} local cmd = cfg.cmd + vim.list_extend(lspft, additional_ft) local should_load = false if lspft ~= nil and #lspft > 0 then @@ -396,10 +419,10 @@ local function load_cfg(ft, client, cfg, loaded) return end - for _, c in pairs(loaded) do - if client == c then + for k, c in pairs(loaded) do + if client == k then -- loaded - trace(client, 'already been loaded for', ft, loaded) + log(client, 'already been loaded for', ft, loaded, c) return end end @@ -414,16 +437,30 @@ local function load_cfg(ft, client, cfg, loaded) -- log(lspconfig.available_servers()) -- force reload with config lspconfig[client].setup(cfg) - vim.defer_fn(function() - vim.cmd([[doautocmd FileType ]] .. ft) - end, 100) log(client, 'loading for', ft) end -- need to verify the lsp server is up end -local function update_capabilities() +local function setup_fmt(client, enabled) + if not require('navigator.util').nvim_0_8() then + if enabled == false then + client.resolved_capabilities.document_formatting = enabled + else + client.resolved_capabilities.document_formatting = client.resolved_capabilities.document_formatting or enabled + end + end + if enabled == false then + client.server_capabilities.documentFormattingProvider = false + else + client.server_capabilities.documentFormattingProvider = client.server_capabilities.documentFormattingProvider + or enabled + end +end + +local function update_capabilities() + trace(vim.o.ft, 'lsp startup') local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities.textDocument.completion.completionItem.snippetSupport = true capabilities.textDocument.completion.completionItem.preselectSupport = true @@ -431,36 +468,36 @@ local function update_capabilities() capabilities.textDocument.completion.completionItem.labelDetailsSupport = true capabilities.textDocument.completion.completionItem.deprecatedSupport = true capabilities.textDocument.completion.completionItem.commitCharactersSupport = true - -- capabilities.textDocument.completion.completionItem.tagSupport = { valueSet = { 1 } } + capabilities.textDocument.completion.completionItem.tagSupport = { valueSet = { 1 } } capabilities.textDocument.completion.completionItem.resolveSupport = { properties = { 'documentation', 'detail', 'additionalTextEdits' }, } capabilities.workspace.configuration = true return capabilities - end -- run setup for lsp clients + +local loaded = {} local function lsp_startup(ft, retry, user_lsp_opts) retry = retry or false - local clients = vim.lsp.get_active_clients() or {} - local loaded = {} local capabilities = update_capabilities() - for _, client in ipairs(clients) do - if client ~= nil then - table.insert(loaded, client.name) - end - end for _, lspclient in ipairs(servers) do + local clients = vim.lsp.get_active_clients() or {} + for _, client in ipairs(clients) do + if client ~= nil then + loaded[client.name] = client.id + end + end -- check should load lsp if type(lspclient) == 'table' then if lspclient.name then lspclient = lspclient.name else - warn('incorrect set for lspclient', vim.inspect(lspclient)) + warn('incorrect set for lspclient' .. vim.inspect(lspclient)) goto continue end end @@ -477,22 +514,18 @@ local function lsp_startup(ft, retry, user_lsp_opts) end end - if _NG_Loaded[lspclient] then - log('client loaded', lspclient) - end - if vim.tbl_contains(config.lsp.disable_lsp or {}, lspclient) then log('disable lsp', lspclient) goto continue end local default_config = {} - log(lspclient) if lspconfig[lspclient] == nil then vim.notify( 'lspclient' .. vim.inspect(lspclient) .. 'no longer support by lspconfig, please submit an issue', vim.lsp.log_levels.WARN ) + log('lspclient', lspclient, 'not supported') goto continue end @@ -505,15 +538,18 @@ local function lsp_startup(ft, retry, user_lsp_opts) default_config = vim.tbl_deep_extend('force', default_config, ng_default_cfg) local cfg = setups[lspclient] or {} + cfg = vim.tbl_deep_extend('keep', cfg, default_config) -- filetype disabled if not vim.tbl_contains(cfg.filetypes or {}, ft) then trace('ft', ft, 'disabled for', lspclient) + goto continue end local disable_fmt = false + log(lspclient) -- if user provides override values cfg.capabilities = capabilities log(lspclient, config.lsp.disable_format_cap) @@ -526,19 +562,17 @@ local function lsp_startup(ft, retry, user_lsp_opts) if user_lsp_opts[lspclient] ~= nil then -- log(lsp_opts[lspclient], cfg) cfg = vim.tbl_deep_extend('force', cfg, user_lsp_opts[lspclient]) - if config.combined_attach == nil then - cfg.on_attach = function(client, bufnr) - on_attach(client, bufnr) - client.resolved_capabilities.document_formatting = enable_fmt - end - end + -- if config.combined_attach == nil then + -- setup_fmt(client, enable_fmt) + -- end if config.combined_attach == 'mine' then if config.on_attach == nil then error('on attach not provided') end cfg.on_attach = function(client, bufnr) config.on_attach(client, bufnr) - client.resolved_capabilities.document_formatting = enable_fmt + + setup_fmt(client, enable_fmt) require('navigator.lspclient.mapping').setup({ client = client, bufnr = bufnr, @@ -550,7 +584,7 @@ local function lsp_startup(ft, retry, user_lsp_opts) cfg.on_attach = function(client, bufnr) on_attach(client, bufnr) config.on_attach(client, bufnr) - client.resolved_capabilities.document_formatting = enable_fmt + setup_fmt(client, enable_fmt) require('navigator.lspclient.mapping').setup({ client = client, bufnr = bufnr, @@ -560,7 +594,8 @@ local function lsp_startup(ft, retry, user_lsp_opts) end if config.combined_attach == 'both' then cfg.on_attach = function(client, bufnr) - client.resolved_capabilities.document_formatting = enable_fmt + setup_fmt(client, enable_fmt) + if config.on_attach and type(config.on_attach) == 'function' then config.on_attach(client, bufnr) end @@ -588,7 +623,8 @@ local function lsp_startup(ft, retry, user_lsp_opts) else cfg.on_attach = function(client, bufnr) on_attach(client, bufnr) - client.resolved_capabilities.document_formatting = enable_fmt + + setup_fmt(client, enable_fmt) end end @@ -597,21 +633,37 @@ local function lsp_startup(ft, retry, user_lsp_opts) if has_lspinst and _NgConfigValues.lsp_installer then local installed, installer_cfg = require('nvim-lsp-installer.servers').get_server(lspconfig[lspclient].name) - log('lsp installer server config' .. lspconfig[lspclient].name , installer_cfg) + log('lsp installer server config ' .. lspconfig[lspclient].name, installer_cfg) if installed and installer_cfg then - log('options', installer_cfg:get_default_options()) - -- if cfg.cmd / {lsp_server_name, arg} not present or lsp_server_name is not in PATH - if vim.fn.empty(cfg.cmd) == 1 or vim.fn.executable(cfg.cmd[1] or '') == 0 then - cfg.cmd = { installer_cfg.root_dir .. path_sep .. installer_cfg.name } + local paths = installer_cfg:get_default_options().cmd_env.PATH + paths = vim.split(paths, ':') + if vim.fn.empty(cfg.cmd) == 1 then + cfg.cmd = { installer_cfg.name } + end + + if vim.fn.executable(cfg.cmd[1]) == 0 then + for _, path in ipairs(paths) do + log(path) + if vim.fn.isdirectory(path) == 1 and string.find(path, installer_cfg.root_dir) then + cfg.cmd[1] = path .. path_sep .. cfg.cmd[1] + log(cfg.cmd) + break + end + end log('update cmd', cfg.cmd) + else + log('cmd installed', cfg.cmd) end end end if vim.fn.executable(cfg.cmd[1]) == 0 then - vim.notify('lsp server not installed in path ' .. vim.inspect(cfg.cmd), vim.lsp.log_levels.WARN) + log('lsp server not installed in path ' .. lspclient .. vim.inspect(cfg.cmd), vim.lsp.log_levels.WARN) end + if _NG_Loaded[lspclient] then + log('client loaded ?', lspclient) + end load_cfg(ft, lspclient, cfg, loaded) _NG_Loaded[lspclient] = true @@ -629,7 +681,7 @@ local function lsp_startup(ft, retry, user_lsp_opts) end, 1000) log('null-ls loading') _NG_Loaded['null-ls'] = true - configs['null-ls'] = cfg + setups['null-ls'] = cfg end end @@ -648,7 +700,7 @@ local function lsp_startup(ft, retry, user_lsp_opts) lspconfig.efm.setup(cfg) log('efm loading') _NG_Loaded['efm'] = true - configs['efm'] = cfg + setups['efm'] = cfg end end @@ -657,6 +709,12 @@ local function lsp_startup(ft, retry, user_lsp_opts) end end +-- append lsps to servers +local function add_servers(lsps) + vim.validate({ lsps = { lsps, 't' } }) + vim.list_extend(servers, lsps) +end + local function get_cfg(client) local ng_cfg = ng_default_cfg if setups[client] ~= nil then @@ -668,78 +726,76 @@ local function get_cfg(client) end end -local function setup(user_opts) - local ft = vim.bo.filetype +local function ft_disabled(ft) + for i = 1, #disabled_ft do + if ft == disabled_ft[i] then + return true + end + end +end +local function setup(user_opts, cnt) + user_opts = user_opts or {} + local ft = vim.bo.filetype local bufnr = user_opts.bufnr or vim.api.nvim_get_current_buf() + if ft == '' or ft == nil then + log('nil filetype, callback') + local ext = vim.fn.expand('%:e') + if ext ~= '' then + cnt = cnt or 0 + local opts = vim.deepcopy(user_opts) + if cnt > 3 then + log('failed to load filetype, skip') + return + else + cnt = cnt + 1 + end + vim.defer_fn(function() + log('defer_fn', ext, ft) + setup(opts, cnt) + end, 200) + return + else + log('no filetype, no ext return') + return + end + end local uri = vim.uri_from_bufnr(bufnr) if uri == 'file://' or uri == 'file:///' then log('skip loading for ft ', ft, uri) return end - log(user_opts) - log(uri) - if _LoadedFiletypes[ft .. tostring(bufnr)] then - log('navigator was loaded for ft', ft) + if _LoadedFiletypes[ft .. tostring(bufnr)] == true then + log('navigator was loaded for ft', ft, bufnr) return end - local disable_ft = { - 'NvimTree', - 'guihua', - 'clap_input', - 'clap_spinner', - 'vista', - 'vista_kind', - 'TelescopePrompt', - 'guihua_rust', - 'csv', - 'txt', - 'defx', - 'packer', - 'gitcommit', - } - for i = 1, #disable_ft do - if ft == disable_ft[i] or _LoadedFiletypes[ft] then - trace('navigator disabled for ft or it is loaded', ft) - return - end - end - if user_opts ~= nil then - log('navigator user setup', user_opts) - else - user_opts = {} + if ft_disabled(ft) then + trace('navigator disabled for ft or it is loaded', ft) + return + end + if _NgConfigValues.lsp.servers then + add_servers(_NgConfigValues.lsp.servers) + _NgConfigValues.lsp.servers = nil end + trace(debug.traceback()) local clients = vim.lsp.buf_get_clients(bufnr) for key, client in pairs(clients) do - if client.name ~= "null_ls" and client.name ~= "efm" then - if vim.tbl_contains(client.filetypes, vim.o.ft) then + if client.name ~= 'null_ls' and client.name ~= 'efm' then + if vim.tbl_contains(client.filetypes or {}, vim.o.ft) then log('client already loaded', client.name) end end end - _LoadedFiletypes[ft] = true - user_opts = vim.list_extend(user_opts, config) -- incase setup was triggered from autocmd - - if ft == nil then - ft = vim.api.nvim_buf_get_option(bufnr, 'filetype') - end + user_opts = vim.tbl_extend('keep', user_opts, config) -- incase setup was triggered from autocmd - if ft == nil or ft == '' then - log('nil filetype, callback') - vim.cmd([[e]]) - vim.defer_fn(function() - setup(user_opts) - end, 200) - return - end + log('running lsp setup', ft, bufnr) local retry = true - trace('setup', user_opts) log('loading for ft ', ft, uri) highlight.diagnositc_config_sign() highlight.add_highlight() @@ -762,17 +818,12 @@ local function setup(user_opts) lsp_startup(ft, retry, lsp_opts) - --- if code line enabled - if _NgConfigValues.lsp.code_lens then + --- if code lens enabled + if _NgConfigValues.lsp.code_lens_action.enable then require('navigator.codelens').setup() end -end - --- append lsps to servers -local function add_servers(lsps) - vim.validate({ lsps = { lsps, 't' } }) - vim.list_extend(servers, lsps) + -- _LoadedFiletypes[ft .. tostring(bufnr)] = true -- may prevent lsp config when reboot lsp end local function on_filetype() @@ -794,7 +845,15 @@ local function on_filetype() if empty(wids) then log('buf not shown return') end - setup({bufnr=bufnr}) + setup({ bufnr = bufnr }) end -return { setup = setup, get_cfg = get_cfg, lsp = servers, add_servers = add_servers, on_filetype = on_filetype } +return { + setup = setup, + get_cfg = get_cfg, + lsp = servers, + add_servers = add_servers, + on_filetype = on_filetype, + disabled_ft = disabled_ft, + ft_disabled = ft_disabled, +} diff --git a/lua/navigator/lspclient/config.lua b/lua/navigator/lspclient/config.lua index aa2cd2c..c930aba 100644 --- a/lua/navigator/lspclient/config.lua +++ b/lua/navigator/lspclient/config.lua @@ -1,6 +1,6 @@ local lsp = require("vim.lsp") -M = {} +local M = {} local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities.textDocument.completion.completionItem.snippetSupport = true diff --git a/lua/navigator/lspclient/highlight.lua b/lua/navigator/lspclient/highlight.lua index 46417cc..5a4eb99 100644 --- a/lua/navigator/lspclient/highlight.lua +++ b/lua/navigator/lspclient/highlight.lua @@ -1,6 +1,6 @@ local M = {} -local log = require"navigator.util".log +-- local log = require('navigator.util').log local api = vim.api -- lsp sign ๏— ๏š ๏‘ฎ ๏„ด ๏„™ ๏‰› ๏‰™ ๏‚ค ๏„ง ๏ฎป ๏”ธ ๏—ฅ ๏—ฃ ๏ƒบ ๏†ˆ ๏š ๏ƒบ ๎ˆก ๏ฏญ ๎Šก ๏ฑ ๏ Ÿ ๎ˆต ๎˜† ๏™Ž ๏‘‰ ๏ณ€ ๏’— ๎ŠŽ @@ -10,80 +10,56 @@ function M.diagnositc_config_sign() end local icons = _NgConfigValues.icons - local sign_name = "NavigatorLightBulb" + local sign_name = 'NavigatorLightBulb' if vim.fn.sign_getdefined(sign_name).text == nil then + vim.fn.sign_define(sign_name, { text = icons.code_action_icon, texthl = 'LspDiagnosticsSignHint' }) - vim.fn - .sign_define(sign_name, {text = icons.code_action_icon, texthl = "LspDiagnosticsSignHint"}) - - sign_name = "NavigatorCodeLensLightBulb" - vim.fn.sign_define(sign_name, - {text = icons.code_lens_action_icon, texthl = "LspDiagnosticsSignHint"}) + sign_name = 'NavigatorCodeLensLightBulb' + vim.fn.sign_define(sign_name, { text = icons.code_lens_action_icon, texthl = 'LspDiagnosticsSignHint' }) end - local e, w, i, h = icons.diagnostic_err, icons.diagnostic_warn, icons.diagnostic_info, - icons.diagnostic_hint - if vim.diagnostic ~= nil then - local t = vim.fn.sign_getdefined('DiagnosticSignWarn') - if (vim.tbl_isempty(t) or t[1].text == "W ") and icons.icons == true then - - vim.fn.sign_define('DiagnosticSignError', - {text = e, texthl = 'DiagnosticError', linehl = '', numhl = ''}) - vim.fn.sign_define('DiagnosticSignWarn', - {text = w, texthl = 'DiagnosticWarn', linehl = '', numhl = ''}) - vim.fn.sign_define('DiagnosticSignInfo', - {text = i, texthl = 'DiagnosticInfo', linehl = '', numhl = ''}) - vim.fn.sign_define('DiagnosticSignHint', - {text = h, texthl = 'DiagnosticHint', linehl = '', numhl = ''}) + local e, w, i, h = icons.diagnostic_err, icons.diagnostic_warn, icons.diagnostic_info, icons.diagnostic_hint + local t = vim.fn.sign_getdefined('DiagnosticSignWarn') + if vim.tbl_isempty(t) or t[1].text == 'W ' and icons.icons == true then + vim.fn.sign_define('DiagnosticSignError', { text = e, texthl = 'DiagnosticError', linehl = '', numhl = '' }) + vim.fn.sign_define('DiagnosticSignWarn', { text = w, texthl = 'DiagnosticWarn', linehl = '', numhl = '' }) + vim.fn.sign_define('DiagnosticSignInfo', { text = i, texthl = 'DiagnosticInfo', linehl = '', numhl = '' }) + vim.fn.sign_define('DiagnosticSignHint', { text = h, texthl = 'DiagnosticHint', linehl = '', numhl = '' }) - t = vim.fn.sign_getdefined('DiagnosticSignWarn') - end - else - local t = vim.fn.sign_getdefined('LspDiagnosticSignWarn') - if (vim.tbl_isempty(t) or t[1].text == "W ") and icons.icons == true then - vim.fn.sign_define('LspDiagnosticsSignError', - {text = e, texthl = 'LspDiagnosticsSignError', linehl = '', numhl = ''}) - vim.fn.sign_define('LspDiagnosticsSignWarning', - {text = w, texthl = 'LspDiagnosticsSignWarning', linehl = '', numhl = ''}) - vim.fn.sign_define('LspDiagnosticsSignInformation', { - text = i, - texthl = 'LspDiagnosticsSignInformation', - linehl = '', - numhl = '' - }) - vim.fn.sign_define('LspDiagnosticsSignHint', - {text = h, texthl = 'LspDiagnosticsSignHint', linehl = '', numhl = ''}) - end + t = vim.fn.sign_getdefined('DiagnosticSignWarn') end M.configed = true end function M.add_highlight() - -- lsp system default - api.nvim_command("hi! link LspDiagnosticsUnderlineError SpellBad") - api.nvim_command("hi! link LspDiagnosticsUnderlineWarning SpellRare") - api.nvim_command("hi! link LspDiagnosticsUnderlineInformation SpellRare") - api.nvim_command("hi! link LspDiagnosticsUnderlineHint SpellRare") - - api.nvim_command("hi! link DiagnosticUnderlineError SpellBad") - api.nvim_command("hi! link DiagnosticUnderlineWarning SpellRare") - api.nvim_command("hi! link DiagnosticUnderlineInformation SpellRare") - api.nvim_command("hi! link DiagnosticUnderlineHint SpellRare") - api.nvim_command("hi def link NGPreviewTitle Title") + api.nvim_command('hi! link DiagnosticUnderlineError SpellBad') + api.nvim_command('hi! link DiagnosticUnderlineWarning SpellRare') + api.nvim_command('hi! link DiagnosticUnderlineInformation SpellRare') + api.nvim_command('hi! link DiagnosticUnderlineHint SpellRare') + api.nvim_command('hi def link NGPreviewTitle Title') local colors = { - {'#aefe00', '#aede00', '#aebe00', '#4e7efe'}, {'#ff00e0', '#df00e0', '#af00e0', '#fedefe'}, - {'#1000ef', '#2000df', '#2000cf', '#f0f040'}, {'#d8a8a3', '#c8a8a3', '#b8a8a3', '#4e2c33'}, - {'#ffa724', '#efa024', '#dfa724', '#0040ff'}, {'#afdc2b', '#09dc4b', '#08d04b', '#ef4f8f'} + { '#aefe00', '#aede00', '#aebe00', '#4e7efe' }, + { '#ff00e0', '#df00e0', '#af00e0', '#fedefe' }, + { '#1000ef', '#2000df', '#2000cf', '#f0f040' }, + { '#d8a8a3', '#c8a8a3', '#b8a8a3', '#4e2c33' }, + { '#ffa724', '#efa024', '#dfa724', '#0040ff' }, + { '#afdc2b', '#09dc4b', '#08d04b', '#ef4f8f' }, } for i = 1, #colors do for j = 1, 3 do - local cmd = string.format("hi! default NGHiReference_%i_%i guibg=%s guifg=%s ", i, j, - colors[i][j], colors[i][4]) + local cmd = string.format('hi! default NGHiReference_%i_%i guibg=%s guifg=%s ', i, j, colors[i][j], colors[i][4]) vim.cmd(cmd) end end + + vim.cmd([[ + autocmd ColorScheme * | + hi default LspReferenceRead cterm=bold gui=Bold ctermbg=yellow guifg=yellow guibg=purple4 | + hi default LspReferenceText cterm=bold gui=Bold ctermbg=red guifg=SlateBlue guibg=MidnightBlue | + hi default LspReferenceWrite cterm=bold gui=Bold,Italic ctermbg=red guifg=DarkSlateBlue guibg=MistyRose + ]]) end return M diff --git a/lua/navigator/lspclient/lspkind.lua b/lua/navigator/lspclient/lspkind.lua index 1c75611..2484b91 100644 --- a/lua/navigator/lspclient/lspkind.lua +++ b/lua/navigator/lspclient/lspkind.lua @@ -1,78 +1,135 @@ local kind_symbols = { - Text = "๏žƒ", - Method = "ฦ’", - Function = "๏‚š", - Constructor = "๎ˆ", - Field = "๏ดฒ", - Variable = "๎ž›", - Class = "๏ญ„", - Interface = "๏จ ", - Module = "๏ฃ–", - Property = "๎˜ค", - Unit = "๏ฅฌ", - Value = "๏ขŸ", - Enum = "ไบ†", - Keyword = "๏ …", - Snippet = "๏—", - Color = "๎ˆซ", - File = "๏…›", - Reference = "๏’", - Folder = "๏„•", - EnumMember = "๏…", - Constant = "๎ˆฌ", - Struct = "๏†ณ ", - Event = "๏ณ…", - Operator = "๏š”", - TypeParameter = "๏‹… ", - Default = "๎˜’" + Text = '๏žƒ', + Method = 'ฦ’', + Function = '๏‚š', + Constructor = '๎ˆ', + Field = '๏ดฒ', + Variable = '๎ž›', + Class = '๏ญ„', + Interface = '๏จ ', + Module = '๏ฃ–', + Property = '๎˜ค', + Unit = '๏ฅฌ', + Value = '๏ขŸ', + Enum = 'ไบ†', + Keyword = '๏ …', + Snippet = '๏—', + Color = '๎ˆซ', + File = '๏…›', + Reference = '๏’', + Folder = '๏„•', + EnumMember = '๏…', + Constant = '๎ˆฌ', + Struct = '๏†ณ ', + Event = '๏ณ…', + Operator = '๏š”', + TypeParameter = '๏‹… ', + Default = '๎˜’', } local CompletionItemKind = { - "๏žƒ ", "๐”‰ ", "โ“• ", "๎ˆ ", "๏ดฒ ", "๎ž› ", "๎ƒ ", "๏ฐฎ ", "๏ฃ– ", "๎˜ค ", "๏‘ต ", "๏ขŸ ", "๐•ฐ ", "๏ … ", - "๏ฌŒ ", "๎ˆซ ", "๏…› ", "๎‡ ", "๏„• ", "๏… ", "๎ˆฌ ", "๏ƒŠ ", "๏ณ… ", "๎† ", "๏‹… ", "๎˜’ " + '๏žƒ ', + '๐”‰ ', + 'โ“• ', + '๎ˆ ', + '๏ดฒ ', + '๎ž› ', + '๎ƒ ', + '๏ฐฎ ', + '๏ฃ– ', + '๎˜ค ', + '๏‘ต ', + '๏ขŸ ', + '๐•ฐ ', + '๏ … ', + '๏ฌŒ ', + '๎ˆซ ', + '๏…› ', + '๎‡ ', + '๏„• ', + '๏… ', + '๎ˆฌ ', + '๏ƒŠ ', + '๏ณ… ', + '๎† ', + '๏‹… ', + '๎˜’ ', } -- A symbol kind. -local SymbolKind = { - File = 1, - Module = 2, - Namespace = 3, - Package = 4, - Class = 5, - Method = 6, - Property = 7, - Field = 8, - Constructor = 9, - Enum = 10, - Interface = 11, - Function = 12, - Variable = 13, - Constant = 14, - String = 15, - Number = 16, - Boolean = 17, - Array = 18, - Object = 19, - Key = 20, - Null = 21, - EnumMember = 22, - Struct = 23, - Event = 24, - Operator = 25, - TypeParameter = 26 -} +-- local SymbolKind = { +-- File = 1, +-- Module = 2, +-- Namespace = 3, +-- Package = 4, +-- Class = 5, +-- Method = 6, +-- Property = 7, +-- Field = 8, +-- Constructor = 9, +-- Enum = 10, +-- Interface = 11, +-- Function = 12, +-- Variable = 13, +-- Constant = 14, +-- String = 15, +-- Number = 16, +-- Boolean = 17, +-- Array = 18, +-- Object = 19, +-- Key = 20, +-- Null = 21, +-- EnumMember = 22, +-- Struct = 23, +-- Event = 24, +-- Operator = 25, +-- TypeParameter = 26 +-- } local SymbolItemKind = { - "๏…› ", "๏ฃ– ", "๏†— ", "๏’‡ ", "๏ญ„ ", "ฦ’ ", "๎˜ค ", "๏ดฒ ", "๎ˆ ", "๐•ฐ ", "๏จ ", "๏‚š ", "๎ž› ", "๎ˆฌ ", "๏ƒŒ ", - "๏ขŸ ", "๏† ", "๏‘’ ", "๏›ค ", "๏‚„ ", "๏ณ  ", "๏ขช ", "๏†ณ ", "๏ณ… ", "๏š” ", "๎ฒ ", "๎˜’ " + '๏…› ', + '๏ฃ– ', + '๏†— ', + '๏’‡ ', + '๏ญ„ ', + 'ฦ’ ', + '๎˜ค ', + '๏ดฒ ', + '๎ˆ ', + '๐•ฐ ', + '๏จ ', + '๏‚š ', + '๎ž› ', + '๎ˆฌ ', + '๏ƒŒ ', + '๏ขŸ ', + '๏† ', + '๏‘’ ', + '๏›ค ', + '๏‚„ ', + '๏ณ  ', + '๏ขช ', + '๏†ณ ', + '๏ณ… ', + '๏š” ', + '๎ฒ ', + '๎˜’ ', } local lspkind = {} -function lspkind.comp_kind(kind) return CompletionItemKind[kind] or "๎˜’" end +function lspkind.comp_kind(kind) + return CompletionItemKind[kind] or '๎˜’' +end -function lspkind.symbol_kind(kind) return SymbolItemKind[kind] or "๎˜’" end +function lspkind.symbol_kind(kind) + return SymbolItemKind[kind] or '๎˜’' +end -function lspkind.cmp_kind(kind) return kind_symbols[kind] or "๎˜’" end +function lspkind.cmp_kind(kind) + return kind_symbols[kind] or '๎˜’' +end -function lspkind.init() require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind end +function lspkind.init() + require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind +end return lspkind diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua index c5c7296..482f62f 100644 --- a/lua/navigator/lspclient/mapping.lua +++ b/lua/navigator/lspclient/mapping.lua @@ -1,6 +1,6 @@ -local log = require('navigator.util').log -local trace = require('navigator.util').trace - +local util = require('navigator.util') +local log = util.log +local trace = util.trace local event_hdlrs = { { ev = 'BufWritePre', func = [[require "navigator.diagnostics".set_diag_loclist()]] }, { ev = 'CursorHold', func = 'document_highlight()' }, @@ -8,26 +8,34 @@ local event_hdlrs = { { ev = 'CursorMoved', func = 'clear_references()' }, } +if vim.lsp.buf.format == nil then + vim.lsp.buf.format = vim.lsp.buf.formatting +end + +if vim.diagnostic == nil then + util.error('Please update nvim to 0.6.1+') +end local double = { 'โ•”', 'โ•', 'โ•—', 'โ•‘', 'โ•', 'โ•', 'โ•š', 'โ•‘' } local single = { 'โ•ญ', 'โ”€', 'โ•ฎ', 'โ”‚', 'โ•ฏ', 'โ”€', 'โ•ฐ', 'โ”‚' } -- TODO https://github.com/neovim/neovim/pull/16591 use vimkeymap.set/del -- LuaFormatter off local key_maps = { - { key = 'gr', func = "require('navigator.reference').reference()" }, - { key = 'Gr', func = "require('navigator.reference').async_ref()" }, + { key = 'gr', func = "require('navigator.reference').async_ref()" }, + { key = 'gr', func = "require('navigator.reference').reference()" }, -- reference deprecated { mode = 'i', key = '', func = 'signature_help()' }, { key = '', func = 'signature_help()' }, { key = 'g0', func = "require('navigator.symbols').document_symbols()" }, - { key = 'gW', func = "require('navigator.workspace').workspace_symbol()" }, + { key = 'gW', func = "require('navigator.workspace').workspace_symbol_live()" }, { key = '', func = "require('navigator.definition').definition()" }, { key = 'gd', func = "require('navigator.definition').definition()" }, { key = 'gD', func = "declaration({ border = 'rounded', max_width = 80 })" }, { 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 = 'ct', func = "require('navigator.ctags').ctags()" }, { key = 'K', func = 'hover({ popup_opts = { border = single, max_width = 80 }})' }, { key = 'ca', mode = 'n', func = "require('navigator.codeAction').code_action()" }, - { key = 'cA', mode = 'v', func = 'range_code_action()' }, + { key = 'ca', mode = 'v', func = "require('navigator.codeAction').range_code_action()" }, -- { key = 're', func = 'rename()' }, { key = 'rn', func = "require('navigator.rename').rename()" }, { key = 'gi', func = 'incoming_calls()' }, @@ -39,6 +47,7 @@ local key_maps = { { key = 'dt', func = "require('navigator.diagnostics').toggle_diagnostics()" }, { key = ']d', func = "diagnostic.goto_next({ border = 'rounded', max_width = 80})" }, { key = '[d', func = "diagnostic.goto_prev({ border = 'rounded', max_width = 80})" }, + { key = ']O', func = 'diagnostic.set_loclist()' }, { key = ']r', func = "require('navigator.treesitter').goto_next_usage()" }, { key = '[r', func = "require('navigator.treesitter').goto_previous_usage()" }, { key = '', func = 'definition()' }, @@ -46,12 +55,23 @@ local key_maps = { { key = 'k', func = "require('navigator.dochighlight').hi_symbol()" }, { key = 'wa', func = "require('navigator.workspace').add_workspace_folder()" }, { key = 'wr', func = "require('navigator.workspace').remove_workspace_folder()" }, - { key = 'ff', func = 'formatting()', mode = 'n' }, + { key = 'ff', func = 'format({async = true})', mode = 'n' }, { key = 'ff', func = 'range_formatting()', mode = 'v' }, { key = 'wl', func = "require('navigator.workspace').list_workspace_folders()" }, { key = 'la', mode = 'n', func = "require('navigator.codelens').run_action()" }, } +local commands = { + [[command! -nargs=* Nctags lua require("navigator.ctags").ctags()]], + "command! -nargs=0 LspLog lua require'navigator.lspclient.config'.open_lsp_log()", + "command! -nargs=0 LspRestart lua require'navigator.lspclient.config'.reload_lsp()", + "command! -nargs=0 LspToggleFmt lua require'navigator.lspclient.mapping'.toggle_lspformat()", + "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 = {} -- LuaFormatter on local M = {} @@ -61,22 +81,28 @@ local ccls_mappings = { { key = 'go', func = "require('navigator.cclshierarchy').outgoing_calls()" }, } -local check_cap = function(cap) +local check_cap = function(opts) -- log(vim.lsp.buf_get_clients(0)) local fmt, rfmt, ccls - if cap and cap.document_formatting then + local cap = opts.cap + if cap == nil then + if opts.client and opts.client.server_capabilities then + cap = opts.client.server_capabilities + end + end + if cap and cap.documentFormattingProvider then fmt = true end - if cap and cap.document_range_formatting then + if cap and cap.documentRangeFormattingProvider then rfmt = true end for _, value in pairs(vim.lsp.buf_get_clients(0)) do trace(value) - if value ~= nil and value.resolved_capabilities == nil then - if value.resolved_capabilities.document_formatting then + if value ~= nil and value.server_capabilities == nil then + if value.server_capabilities.documentFormattingProvider then fmt = true end - if value.resolved_capabilities.document_range_formatting then + if value.server_capabilities.documentRangeFormattingProvider then rfmt = true end @@ -89,13 +115,23 @@ local check_cap = function(cap) return fmt, rfmt, ccls end -local function set_mapping(user_opts) - log('setup mapping') - local opts = { noremap = true, silent = true } - user_opts = user_opts or {} +local function set_cmds(_) + for _, value in pairs(commands) do + vim.cmd(value) + end +end +-- should works for both 1)attach from known lsp client or from a disabled lsp client +local function set_mapping(lsp_attach_info) + local opts = { noremap = true, silent = true } + vim.validate({ + lsp_attach_info = { lsp_attach_info, 'table' }, + }) + if _NgConfigValues.debug then + log('setup mapping for client', lsp_attach_info.client.name, lsp_attach_info.client.cmd) + end local user_key = _NgConfigValues.keymaps or {} - local bufnr = user_opts.bufnr or 0 + local bufnr = lsp_attach_info.bufnr or 0 local function del_keymap(...) vim.api.nvim_buf_del_keymap(bufnr, ...) @@ -107,7 +143,7 @@ local function set_mapping(user_opts) -- local function buf_set_option(...) -- vim.api.nvim_buf_set_option(bufnr, ...) -- end - local doc_fmt, range_fmt, ccls = check_cap(user_opts.cap) + local doc_fmt, range_fmt, ccls = check_cap(lsp_attach_info) if ccls then vim.list_extend(key_maps, ccls_mappings) @@ -134,25 +170,23 @@ local function set_mapping(user_opts) local fmtkey, rfmtkey for _, value in pairs(key_maps) do local f = 'lua vim.lsp.buf.' .. value.func .. '' - if string.find(value.func, 'require') then + if string.find(value.func, 'require') or string.find(value.func, 'vim.') then f = 'lua ' .. value.func .. '' elseif string.find(value.func, 'diagnostic') then local diagnostic = 'lua vim.' - if vim.lsp.diagnostic ~= nil then - diagnostic = 'lua vim.lsp.' - end + diagnostic = 'lua vim.' f = diagnostic .. value.func .. '' - elseif string.find(value.func, 'vim.') then - f = 'lua ' .. value.func .. '' + -- elseif string.find(value.func, 'vim.') then + -- f = 'lua ' .. value.func .. '' end local k = value.key local m = value.mode or 'n' if string.find(value.func, 'range_formatting') then rfmtkey = value.key - elseif string.find(value.func, 'formatting') then + elseif string.find(value.func, 'format') then fmtkey = value.key end - log('binding', k, f) + trace('binding', k, f) set_keymap(m, k, f, opts) end @@ -166,24 +200,25 @@ local function set_mapping(user_opts) vim.cmd([[ aug NavigatorAuFormat au! - autocmd BufWritePre lua vim.lsp.buf.formatting() + autocmd BufWritePre lua vim.lsp.buf.format({async = true}) aug END ]]) elseif fmtkey then del_keymap('n', fmtkey) end - if user_opts.cap and user_opts.cap.document_range_formatting then - log('formatting enabled', user_opts.cap) + + if lsp_attach_info.cap and lsp_attach_info.cap.document_range_formatting then + log('formatting enabled', lsp_attach_info.cap) end if not range_fmt and rfmtkey then del_keymap('v', rfmtkey) end - log('enable format ', doc_fmt, range_fmt) + log('enable format ', doc_fmt, range_fmt, _NgConfigValues.lsp.format_on_save) end -local function autocmd(user_opts) +local function autocmd() vim.api.nvim_exec( [[ aug NavigatorDocHlAu @@ -240,17 +275,26 @@ M.toggle_lspformat = function(on) end end -function M.setup(user_opts) - user_opts = user_opts or _NgConfigValues - set_mapping(user_opts) +function M.setup(attach_opts) + if not attach_opts or not attach_opts.client then + vim.notify( + 'please call require"navigator.mapping".setup({bufnr=bufnr, client=client}) inside on_attach(client,bufnr)', + vim.lsp.log_levels.WARN + ) + end + attach_opts = attach_opts or { bufnr = 0, client = {}, cap = {} } + set_mapping(attach_opts) + set_cmds(attach_opts) - autocmd(user_opts) - set_event_handler(user_opts) + autocmd() + set_event_handler(attach_opts) - local cap = user_opts.cap or vim.lsp.protocol.make_client_capabilities() - log('lsp cap:', cap) + local client = attach_opts.client or {} + local cap = client.server_capabilities or vim.lsp.protocol.make_client_capabilities() - if cap.call_hierarchy or cap.callHierarchy then + log('lsp cap:', cap.codeActionProvider) + + if cap.call_hierarchy or cap.callHierarchyProvider then vim.lsp.handlers['callHierarchy/incomingCalls'] = require('navigator.hierarchy').incoming_calls_handler vim.lsp.handlers['callHierarchy/outgoingCalls'] = require('navigator.hierarchy').outgoing_calls_handler end @@ -259,7 +303,7 @@ function M.setup(user_opts) -- vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler vim.lsp.handlers['textDocument/definition'] = require('navigator.definition').definition_handler - if cap.declaration then + if cap.declarationProvider then vim.lsp.handlers['textDocument/declaration'] = require('navigator.definition').declaration_handler end @@ -286,8 +330,12 @@ function M.setup(user_opts) }) end - vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { border = single }) - if cap.document_formatting then + local border_style = single + if _NgConfigValues.border == 'double' then + border_style = double + end + vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { border = border_style }) + if cap.documentFormattingProvider then log('formatting enabled setup hdl') vim.lsp.handlers['textDocument/formatting'] = require('navigator.formatting').format_hdl end diff --git a/lua/navigator/lspwrapper.lua b/lua/navigator/lspwrapper.lua index 889278d..0c27e34 100644 --- a/lua/navigator/lspwrapper.lua +++ b/lua/navigator/lspwrapper.lua @@ -1,7 +1,6 @@ local M = {} local util = require('navigator.util') -local nvim_0_6 = util.nvim_0_6() local gutil = require('guihua.util') local lsp = require('vim.lsp') @@ -75,7 +74,8 @@ end function M.symbols_to_items(result) local locations = {} - -- log(result) + result = result or {} + log(#result) for i = 1, #result do local item = result[i].location if item ~= nil and item.range ~= nil then @@ -87,8 +87,9 @@ function M.symbols_to_items(result) if kind ~= nil then item.text = kind .. ': ' .. item.text end - item.filename = vim.uri_to_fname(item.uri) - + if not item.filename then + item.filename = vim.uri_to_fname(item.uri) + end item.display_filename = item.filename:gsub(cwd .. path_sep, path_cur, 1) if item.range == nil or item.range.start == nil then log('range not set', result[i], item) @@ -122,7 +123,8 @@ function M.check_capabilities(feature, client_id) local supported_client = false for _, client in pairs(clients) do - supported_client = client.resolved_capabilities[feature] + -- supported_client = client.resolved_capabilities[feature] + supported_client = client.server_capabilities[feature] if supported_client then break end @@ -143,26 +145,24 @@ end function M.call_sync(method, params, opts, handler) params = params or {} opts = opts or {} - local results_lsp, err = lsp.buf_request_sync(0, method, params, opts.timeout or vim.g.navtator_timeout or 1000) + log(method, params) + local results_lsp, err = lsp.buf_request_sync(opts.bufnr or 0, method, params, opts.timeout or 1000) - if nvim_0_6() then - handler(err, extract_result(results_lsp), { method = method }, nil) - else - handler(err, method, extract_result(results_lsp), nil, nil) - end + return handler(err, extract_result(results_lsp), { method = method, no_show = opts.no_show }, nil) end -function M.call_async(method, params, handler) +function M.call_async(method, params, handler, bufnr) params = params or {} local callback = function(...) util.show(...) handler(...) end - return lsp.buf_request(0, method, params, callback) + bufnr = bufnr or 0 + return lsp.buf_request(bufnr, method, params, callback) -- results_lsp, canceller end -local function ts_functions(uri) +local function ts_functions(uri, optional) local unload_bufnr local ts_enabled, _ = pcall(require, 'nvim-treesitter.locals') if not ts_enabled or not TS_analysis_enabled then @@ -187,6 +187,9 @@ local function ts_functions(uri) ts_nodes_time:delete(uri) end end + if optional then + return + end local unload = false if not api.nvim_buf_is_loaded(bufnr) then trace('! load buf !', uri, bufnr) @@ -206,7 +209,7 @@ local function ts_functions(uri) return funcs, unload_bufnr end -local function ts_definition(uri, range) +local function ts_definition(uri, range, optional) local unload_bufnr local ts_enabled, _ = pcall(require, 'nvim-treesitter.locals') if not ts_enabled or not TS_analysis_enabled then @@ -224,6 +227,9 @@ local function ts_definition(uri, range) log('ts def from cache') return tsnodes end + if optional then + return + end local ts_def = require('navigator.treesitter').find_definition local bufnr = vim.uri_to_bufnr(uri) local x = os.clock() @@ -263,6 +269,10 @@ end local function order_locations(locations) table.sort(locations, function(i, j) + if i == nil or j == nil or i.uri == nil or j.uri == nil then + -- log(i, j) + return false + end if i.uri == j.uri then if i.range and i.range.start then return i.range.start.line < j.range.start.line @@ -294,20 +304,27 @@ local function slice_locations(locations, max_items) return first_part, second_part end -local function test_locations() - local locations = { - { uri = '1', range = { start = { line = 1 } } }, - { uri = '2', range = { start = { line = 2 } } }, - { uri = '2', range = { start = { line = 3 } } }, - { uri = '1', range = { start = { line = 3 } } }, - { uri = '1', range = { start = { line = 4 } } }, - { uri = '3', range = { start = { line = 4 } } }, - { uri = '3', range = { start = { line = 4 } } }, - } - local second_part - order_locations(locations) - local locations, second_part = slice_locations(locations, 3) - log(locations, second_part) +-- local function test_locations() +-- local locations = { +-- { uri = '1', range = { start = { line = 1 } } }, +-- { uri = '2', range = { start = { line = 2 } } }, +-- { uri = '2', range = { start = { line = 3 } } }, +-- { uri = '1', range = { start = { line = 3 } } }, +-- { uri = '1', range = { start = { line = 4 } } }, +-- { uri = '3', range = { start = { line = 4 } } }, +-- { uri = '3', range = { start = { line = 4 } } }, +-- } +-- local second_part +-- order_locations(locations) +-- local locations, second_part = slice_locations(locations, 3) +-- log(locations, second_part) +-- end + +local function ts_optional(i, unload_buf_size) + if unload_buf_size then + return unload_buf_size > _NgConfigValues.treesitter_analysis_max_num + end + return i > _NgConfigValues.treesitter_analysis_max_num end function M.locations_to_items(locations, ctx) @@ -337,9 +354,9 @@ function M.locations_to_items(locations, ctx) for i, loc in ipairs(locations) do local funcs = nil local item = lsp.util.locations_to_items({ loc }, enc)[1] - -- log(item) item.range = locations[i].range or locations[i].targetRange item.uri = locations[i].uri or locations[i].targetUri + item.definition = locations[i].definition if is_win then log(item.uri) -- file:///C:/path/to/file @@ -349,14 +366,14 @@ function M.locations_to_items(locations, ctx) local proj_file = item.uri:find(cwd) or is_win or i < 30 local unload, def if TS_analysis_enabled and proj_file then - funcs, unload = ts_functions(item.uri) + funcs, unload = ts_functions(item.uri, ts_optional(i, #unload_bufnrs)) if unload then table.insert(unload_bufnrs, unload) end if not uri_def[item.uri] then -- find def in file - def, unload = ts_definition(item.uri, item.range) + def, unload = ts_definition(item.uri, item.range, ts_optional(i, #unload_bufnrs)) if def and def.start then uri_def[item.uri] = def if def.start then -- find for the 1st time @@ -416,7 +433,8 @@ function M.locations_to_items(locations, ctx) vim.cmd([[set eventignore-=FileType]]) - return items, width + 24, second_part -- TODO handle long line? + trace(items) + return items, width + 30, second_part -- TODO handle long line? end function M.symbol_to_items(locations) @@ -431,6 +449,9 @@ function M.symbol_to_items(locations) if i.definition then return true end + if j.definition then + return false + end if i.uri == j.uri then if i.range and i.range.start then return i.range.start.line < j.range.start.line @@ -458,7 +479,7 @@ end function M.request(method, hdlr) -- e.g textDocument/reference local bufnr = vim.api.nvim_get_current_buf() local ref_params = vim.lsp.util.make_position_params() - vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr) + vim.lsp.for_each_buffer_client(bufnr, function(client, _, _) client.request(method, ref_params, hdlr, bufnr) end) end diff --git a/lua/navigator/reference.lua b/lua/navigator/reference.lua index bc2d4f0..5571bd3 100644 --- a/lua/navigator/reference.lua +++ b/lua/navigator/reference.lua @@ -1,11 +1,9 @@ local util = require('navigator.util') -local mk_handler = util.mk_handler local log = util.log local lsphelper = require('navigator.lspwrapper') local gui = require('navigator.gui') local lsp = require('navigator.lspwrapper') local trace = require('navigator.util').trace -ListViewCtrl = ListViewCtrl or require('guihua.listviewctrl').ListViewCtrl -- local partial = util.partial -- local cwd = vim.loop.cwd() -- local lsphelper = require "navigator.lspwrapper" @@ -16,35 +14,51 @@ local ref_view = function(err, locations, ctx, cfg) local truncate = cfg and cfg.truncate or 20 local opts = {} trace('arg1', err, ctx, locations) - trace(locations) + log(#locations, locations[1]) if ctx.combine then - -- wait for both request + -- wait for both reference and definition LSP request if ctx.results == nil then return end - if #ctx.results.definitions.result == nil or ctx.results.references.result == nil then + if (ctx.results.definitions == nil) or (ctx.results.references == nil) then log('not all requests returned') return end local definitions = ctx.results.definitions local references = ctx.results.references + if _NgConfigValues.debug then + local logctx = { results = {} } + logctx = vim.tbl_extend('keep', logctx, ctx) + log(logctx, 'result size', 'def', #ctx.results.definitions, 'ref', #ctx.results.references) + end if definitions.error and references.error then vim.notify('lsp ref callback error' .. vim.inspect(ctx.result), vim.lsp.log_levels.WARN) end locations = {} - if definitions.result then + if definitions and definitions.result then for i, _ in ipairs(definitions.result) do definitions.result[i].definition = true end vim.list_extend(locations, definitions.result) end - if references.result then - vim.list_extend(locations, references.result) + if references and references.result and #references.result > 0 then + local refs = references.result + for _, value in pairs(locations) do + local vrange = value.range or { start = { line = 0 }, ['end'] = { line = 0 } } + for i = 1, #refs, 1 do + local rg = refs[i].range or {} + trace(value, refs[i]) + trace(rg, vrange) + if rg.start.line == vrange.start.line and rg['end'].line == vrange['end'].line then + table.remove(refs, i) + break + end + end + end + vim.list_extend(locations, refs) end - ctx = references.ctx or definitions.ctx err = nil - cfg = references.config or definitions.config - trace(ctx, locations) + trace(locations) end -- log("num", num) -- log("bfnr", bufnr) @@ -63,7 +77,7 @@ local ref_view = function(err, locations, ctx, cfg) return end if locations == nil or vim.tbl_isempty(locations) then - vim.notify('References not found', vim.lsp.log_levels.WARN) + vim.notify('References not found', vim.lsp.log_levels.INFO) return end @@ -101,11 +115,13 @@ local ref_view = function(err, locations, ctx, cfg) if vim.tbl_isempty(second_part) then return end + ctx.max_items = #second_part local items2 = locations_to_items(second_part, ctx) vim.list_extend(thread_items, items2) local data = require('navigator.render').prepare_for_render(thread_items, opts) + log('thread data size', #data) listview.ctrl:on_data_update(data) if nv_ref_async then vim.loop.close(nv_ref_async) @@ -123,7 +139,7 @@ local ref_view = function(err, locations, ctx, cfg) return listview, items, width end -local ref_hdlr = mk_handler(function(err, locations, ctx, cfg) +local ref_hdlr = function(err, locations, ctx, cfg) _NgConfigValues.closer = nil trace(err, locations, ctx, cfg) M.async_hdlr = vim.loop.new_async(vim.schedule_wrap(function() @@ -133,21 +149,36 @@ local ref_hdlr = mk_handler(function(err, locations, ctx, cfg) end end)) M.async_hdlr:send() -end) +end local async_ref = function() local ref_params = vim.lsp.util.make_position_params() - local results = { definitions = {}, references = {} } - ref_params.context = { includeDeclaration = false } + local results = {} lsp.call_async('textDocument/definition', ref_params, function(err, result, ctx, config) trace(err, result, ctx, config) + if err ~= nil or result == nil then + log('failed to get def', err, result, ctx, config) + result = {} + end + for i = 1, #result do + if result[i].range == nil and result[i].targetRange then + result[i].range = result[i].targetRange + end + end results.definitions = { error = err, result = result, ctx = ctx, config = config } + log(result) ctx = ctx or {} ctx.results = results ctx.combine = true ref_view(err, result, ctx, config) end) -- return asyncresult, canceller + + ref_params.context = { includeDeclaration = false } lsp.call_async('textDocument/references', ref_params, function(err, result, ctx, config) + if err ~= nil or result == nil then + log('failed to get ref', err, result, ctx, config) + result = {} + end trace(err, result, ctx, config) results.references = { error = err, result = result, ctx = ctx, config = config } ctx = ctx or {} @@ -178,20 +209,17 @@ local ref = function() local bufnr = vim.api.nvim_get_current_buf() local ref_params = vim.lsp.util.make_position_params() - vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, bufnr) - if client.resolved_capabilities.find_references then + vim.lsp.for_each_buffer_client(bufnr, function(client, _, _) + if client.server_capabilities.referencesProvider then client.request('textDocument/references', ref_params, ref_hdlr, bufnr) end end) end return { - reference_handler = ref_hdlr, - reference = ref_req, - ref_view = ref_view, - async_ref = async_ref, + all_ref = ref, } diff --git a/lua/navigator/render.lua b/lua/navigator/render.lua index de66bc3..8259eed 100644 --- a/lua/navigator/render.lua +++ b/lua/navigator/render.lua @@ -1,16 +1,16 @@ -local log = require"guihua.log".info -local trace = require"guihua.log".trace +local log = require('guihua.log').info +local trace = require('guihua.log').trace local M = {} -local clone = require'guihua.util'.clone +local clone = require('guihua.util').clone local function filename(url) if url == nil then return '' end - return url:match("^.+/(.+)$") or url + return url:match('^.+/(.+)$') or url end local function extension(url) - local ext = url:match("^.+(%..+)$") or "txt" + local ext = url:match('^.+(%..+)$') or 'txt' return string.sub(ext, 2) end @@ -38,7 +38,6 @@ local function get_pads(win_width, text, postfix) i = i + rem * 10 -- log(i) end - end if i > 3 then @@ -52,40 +51,32 @@ end function M.prepare_for_render(items, opts) opts = opts or {} if items == nil or #items < 1 then - vim.notify("no item found or empty fields", vim.lsp.log_levels.INFO) + vim.notify('no item found or empty fields', vim.lsp.log_levels.INFO) return end local item = clone(items[1]) - local display_items = {item} + local display_items = { item } local last_summary_idx = 1 local total_ref_in_file = 1 local total = opts.total - local icon = "๏„• " - local lspapi = opts.api or "โˆ‘" + local icon = '๏„• ' + local lspapi = opts.api or 'โˆ‘' - local ok, devicons = pcall(require, "nvim-web-devicons") + 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 - local width = 100 - opts.width = opts.width or width + -- local call_by_presented = false + opts.width = opts.width or 100 local win_width = opts.width -- buf - for i = 1, #items do - if items[i].call_by and #items[i].call_by > 0 then - call_by_presented = true - end - end - -- log(items[1]) - for i = 1, #items do local space local trim local lspapi_display = lspapi - items[i].symbol_name = items[i].symbol_name or "" -- some LSP API does not have range for this + items[i].symbol_name = items[i].symbol_name or '' -- some LSP API does not have range for this local fn = display_items[last_summary_idx].filename local dfn = items[i].display_filename @@ -98,18 +89,23 @@ function M.prepare_for_render(items, opts) display_items[last_summary_idx].filename_only = true -- trace(items[i], items[i].filename, last_summary_idx, display_items[last_summary_idx].filename) -- TODO refact display_filename generate part - if items[i].filename == fn then + if items[i].filename == fn or opts.hide_filename then space, trim = get_pads(opts.width, icon .. ' ' .. dfn, lspapi_display .. ' 14 of 33 ') if trim and opts.width > 50 and #dfn > opts.width - 20 then local fn1 = string.sub(dfn, 1, opts.width - 50) local fn2 = string.sub(dfn, #dfn - 10, #dfn) - display_items[last_summary_idx].display_filename = fn1 .. "๏›—" .. fn2 + display_items[last_summary_idx].display_filename = fn1 .. '๏›—' .. fn2 space = ' ' -- log("trim", fn1, fn2) end - local api_disp = string.format("%s %s%s%s %i", icon, - display_items[last_summary_idx].display_filename, space, - lspapi_display, total_ref_in_file) + local api_disp = string.format( + '%s %s%s%s %i', + icon, + display_items[last_summary_idx].display_filename, + space, + lspapi_display, + total_ref_in_file + ) if total then api_disp = api_disp .. ' of: ' .. tostring(total) @@ -118,20 +114,17 @@ function M.prepare_for_render(items, opts) display_items[last_summary_idx].text = api_disp total_ref_in_file = total_ref_in_file + 1 else - lspapi_display = lspapi item = clone(items[i]) - space, trim = get_pads(opts.width, icon .. ' ' .. item.display_filename, - lspapi_display .. ' 12 of 33') + space, trim = get_pads(opts.width, icon .. ' ' .. item.display_filename, lspapi_display .. ' 12 of 33') if trim and opts.width > 52 and #item.display_filename > opts.width - 20 then - item.display_filename = string.sub(item.display_filename, 1, opts.width - 52) .. "๏›—" - .. string.sub(item.display_filename, - #item.display_filename - 10, - #item.display_filename) + item.display_filename = string.sub(item.display_filename, 1, opts.width - 52) + .. '๏›—' + .. string.sub(item.display_filename, #item.display_filename - 10, #item.display_filename) space = ' ' end - item.text = string.format("%s %s%s%s 1", icon, item.display_filename, space, lspapi_display) + item.text = string.format('%s %s%s%s 1', icon, item.display_filename, space, lspapi_display) trace(item.text) table.insert(display_items, item) @@ -140,14 +133,17 @@ function M.prepare_for_render(items, opts) 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 ts_report = "" + item.text = require('navigator.util').trim_and_pad(item.text) + item.text = string.format('%4i: %s', item.lnum, item.text) + local ts_report = '' if item.lhs then ts_report = _NgConfigValues.icons.value_changed end + -- log(item.text, item.symbol_name, item.uri) + -- log(item.text) if item.definition then + log('definition', item) ts_report = ts_report .. _NgConfigValues.icons.value_definition .. ' ' end local header_len = #ts_report + 4 -- magic number 2 @@ -155,7 +151,7 @@ function M.prepare_for_render(items, opts) item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '') if item.call_by ~= nil and #item.call_by > 0 then - trace("call_by:", #item.call_by) + 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*$', '') @@ -183,15 +179,26 @@ function M.prepare_for_render(items, opts) if #ts_report > 1 then space, trim = get_pads(win_width, item.text, ts_report) if trim then - item.text = string.sub(item.text, 1, opts.width - 20) .. "๏›—" + local ts_r = ts_report or '' + item.text = string.sub(item.text, 1, math.max(1, opts.width - math.max(20, #ts_r))) + local _, j = string.gsub(item.text, [["]], '') + if j % 2 == 1 then + item.text = item.text .. '"' + end + _, j = string.gsub(item.text, [[']], '') + if j % 2 == 1 then + item.text = item.text .. [[']] + end + item.text = item.text .. '๏›—' + -- let check if there are unmatched "/' end if #space + #item.text + #ts_report >= win_width then if #item.text + #ts_report > win_width then - trace("exceeding", #item.text, #ts_report, win_width) + trace('exceeding', #item.text, #ts_report, win_width) space = ' ' else local remain = win_width - #item.text - #ts_report - trace("remain", remain) + trace('remain', remain) space = string.rep(' ', remain) end end diff --git a/lua/navigator/sidepanel.lua b/lua/navigator/sidepanel.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/navigator/signature.lua b/lua/navigator/signature.lua index 62afa6e..453fc5d 100644 --- a/lua/navigator/signature.lua +++ b/lua/navigator/signature.lua @@ -1,11 +1,5 @@ -local gui = require "navigator.gui" local util = require "navigator.util" -local mk_handler = util.mk_handler local log = util.log -local partial = util.partial -local lsphelper = require "navigator.lspwrapper" -local cwd = vim.loop.cwd() -local M = {} --- navigator signature local match_parameter = function(result) @@ -47,7 +41,7 @@ local match_parameter = function(result) end end -local signature_handler = mk_handler(function(err, result, ctx, config) +local signature_handler = function(err, result, ctx, config) if config == nil then log("config nil") end @@ -71,5 +65,5 @@ local signature_handler = mk_handler(function(err, result, ctx, config) local syntax = vim.lsp.util.try_trim_markdown_code_blocks(lines) config.focus_id = ctx.bufnr .. "lsp_signature" vim.lsp.util.open_floating_preview(lines, syntax, config) -end) +end return {signature_handler = signature_handler} diff --git a/lua/navigator/symbols.lua b/lua/navigator/symbols.lua index 6f121bc..baef5cb 100644 --- a/lua/navigator/symbols.lua +++ b/lua/navigator/symbols.lua @@ -2,10 +2,7 @@ local gui = require('navigator.gui') local M = {} local log = require('navigator.util').log local trace = require('navigator.util').trace -local mk_handler = require('navigator.util').mk_handler local lsphelper = require('navigator.lspwrapper') -local locations_to_items = lsphelper.locations_to_items -local clone = require('guihua.util').clone local symbol_kind = require('navigator.lspclient.lspkind').symbol_kind local symbols_to_items = lsphelper.symbols_to_items @@ -13,8 +10,8 @@ function M.workspace_symbols(query) query = query or pcall(vim.fn.input, 'Query: ') local bufnr = vim.api.nvim_get_current_buf() local params = { query = query } - vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr) - if client.resolved_capabilities.workspace_symbol then + vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) + if client.server_capabilities.workspaceSymbolProvider then client.request('workspace/symbol', params, M.workspace_symbol_handler, _bufnr) end end) @@ -34,14 +31,14 @@ function M.document_symbols(opts) local params = vim.lsp.util.make_position_params() params.context = { includeDeclaration = true } params.query = opts.prompt or '' - vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr) - if client.resolved_capabilities.document_symbol then + vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) + if client.server_capabilities.documentSymbolProvider then client.request('textDocument/documentSymbol', params, M.document_symbol_handler, _bufnr) end end) end -M.document_symbol_handler = mk_handler(function(err, result, ctx) +M.document_symbol_handler = function(err, result, ctx) if err then vim.notify('failed to get document symbol' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) end @@ -52,7 +49,7 @@ M.document_symbol_handler = mk_handler(function(err, result, ctx) end if not result or vim.tbl_isempty(result) then - vim.notify('symbol' .. query .. 'not found for buf' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) + vim.notify('symbol ' .. query .. ' not found for buf ' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) return end local locations = {} @@ -66,6 +63,9 @@ M.document_symbol_handler = mk_handler(function(err, result, ctx) local kind = symbol_kind(item.kind) item.name = result[i].name item.range = result[i].range or result[i].location.range + if item.range == nil then + log('range missing in result', result[i]) + end item.uri = uri item.selectionRange = result[i].selectionRange item.detail = result[i].detail or '' @@ -73,10 +73,14 @@ M.document_symbol_handler = mk_handler(function(err, result, ctx) item.detail = 'func' end - item.lnum = result[i].range.start.line + 1 + item.lnum = item.range.start.line + 1 item.text = '[' .. kind .. ']' .. item.name .. ' ' .. item.detail item.filename = fname + item.indent_level = 1 + + item.type = kind + item.node_text = item.name table.insert(locations, item) if result[i].children ~= nil then @@ -84,24 +88,39 @@ M.document_symbol_handler = mk_handler(function(err, result, ctx) local child = {} child.kind = c.kind child.name = c.name - child.range = c.range + child.range = c.range or c.location.range local ckind = symbol_kind(child.kind) + + child.node_text = child.name + child.type = ckind child.selectionRange = c.selectionRange child.filename = fname child.uri = uri - child.lnum = c.range.start.line + 1 + child.lnum = child.range.start.line + 1 child.detail = c.detail or '' + child.indent_level = item.indent_level + 1 child.text = ' ๏ ‘ ' .. ckind .. '' .. child.name .. ' ' .. child.detail table.insert(locations, child) end end end + if ctx.no_show then + return locations + end local ft = vim.api.nvim_buf_get_option(bufnr, 'ft') - gui.new_list_view({ items = locations, prompt = true, rawdata = true, ft = ft, api = '๏ ฐ ' }) -end) + gui.new_list_view({ + items = locations, + prompt = true, + rawdata = true, + height = 0.62, + preview_height = 0.1, + ft = ft, + api = _NgConfigValues.icons.doc_symbol, + }) +end -M.workspace_symbol_handler = mk_handler(function(err, result, ctx, cfg) +M.workspace_symbol_handler = function(err, result, ctx, cfg) trace(err, result, ctx, cfg) if err then vim.notify('failed to get workspace symbol' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) @@ -121,6 +140,29 @@ M.workspace_symbol_handler = mk_handler(function(err, result, ctx, cfg) local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft') gui.new_list_view({ items = items, prompt = true, ft = ft, rowdata = true, api = '๏ ฐ ' }) -end) +end + +function M.side_panel() + local Panel = require('guihua.panel') + local buf = vim.api.nvim_get_current_buf() + local p = Panel:new({ + scope = 'range', + render = function(bufnr) + local ft = vim.api.nvim_buf_get_option(bufnr, 'buftype') + if ft == 'nofile' or ft == 'guihua' or ft == 'prompt' then + return + end + local params = vim.lsp.util.make_range_params() + local sync_req = require('navigator.lspwrapper').call_sync + return sync_req( + 'textDocument/documentSymbol', + params, + { timeout = 1000, bufnr = bufnr, no_show = true }, + vim.lsp.with(M.document_symbol_handler, { no_show = true }) + ) + end, + }) + p:open(true) +end return M diff --git a/lua/navigator/treesitter.lua b/lua/navigator/treesitter.lua index 0a5e0de..951306b 100644 --- a/lua/navigator/treesitter.lua +++ b/lua/navigator/treesitter.lua @@ -1,28 +1,31 @@ --- Note: some of the functions/code coped from treesitter/refactor/navigation.lua and may be modified -- to fit in navigator.lua -local gui = require "navigator.gui" +local gui = require('navigator.gui') local fn = vim.fn local lru = require('navigator.lru').new(500, 1024 * 1024) -local ok, ts_locals = pcall(require, "nvim-treesitter.locals") +local ok, ts_locals = pcall(require, 'nvim-treesitter.locals') if not ok then - error("treesitter not installed") + error('treesitter not installed') return nil end -local parsers = require "nvim-treesitter.parsers" -local utils = require "nvim-treesitter.utils" -local locals = require 'nvim-treesitter.locals' -local ts_utils = require 'nvim-treesitter.ts_utils' +local parsers = require('nvim-treesitter.parsers') +local utils = require('nvim-treesitter.utils') +local locals = require('nvim-treesitter.locals') +local ts_utils = require('nvim-treesitter.ts_utils') local api = vim.api -local util = require "navigator.util" +local util = require('navigator.util') local M = {} local cwd = vim.loop.cwd() -local log = require"navigator.util".log -local lerr = require"navigator.util".error -local trace = require"navigator.util".trace +local log = require('navigator.util').log +local lerr = require('navigator.util').error +local trace = function(...) end +if vim.fn.has('nvim-0.7') == 1 then + local trace = require('navigator.util').trace +end local get_icon = function(kind) if kind == nil or _NgConfigValues.icons.match_kinds[kind] == nil then @@ -43,7 +46,7 @@ function M.goto_definition(bufnr) local definition = locals.find_definition(node_at_point, bufnr) if definition ~= node_at_point then - log("def found:", definition:range()) + log('def found:', definition:range()) ts_utils.goto_node(definition) end end @@ -53,7 +56,7 @@ local function node_is_definination(node) return false end local nd_type = node:parent():type() - local decl = {'short_var_declaration', 'short_var_declaration', 'declaration'} + local decl = { 'short_var_declaration', 'short_var_declaration', 'declaration' } if vim.tbl_contains(decl, nd_type) then return true @@ -69,25 +72,24 @@ local function node_is_definination(node) end return false - end -- use lsp range to find def function M.find_definition(range, bufnr) if not range or not range.start then - lerr("find_def incorrect range", range) + lerr('find_def incorrect range', range) return end bufnr = bufnr or api.nvim_get_current_buf() local parser = parsers.get_parser(bufnr) - local symbolpos = {range.start.line, range.start.character} -- +1 or not? + local symbolpos = { range.start.line, range.start.character } -- +1 or not? local root = ts_utils.get_root_for_position(range.start.line, range.start.character, parser) if not root then return end local node_at_point = root:named_descendant_for_range(symbolpos[1], symbolpos[2], symbolpos[1], symbolpos[2]) if not node_at_point then - lerr("no node at cursor") + lerr('no node at cursor') return end @@ -95,15 +97,15 @@ function M.find_definition(range, bufnr) if definition ~= node_at_point then -- NOTE: it may not worksfor some of languages. if def not found, ts -- returns current node. if your node is def, then it also return self... then I have no idea weather it is -- def or not - trace("info: def found:", definition:range(), definition:type()) + trace('info: def found:', definition:range(), definition:type()) local r, c = definition:range() - return {start = {line = r, character = c}} + return { start = { line = r, character = c } } elseif node_is_definination(node_at_point) then - trace("declaraction here ", definition:type()) + trace('declaraction here ', definition:type()) local r, c = definition:range() - return {start = {line = r, character = c}} + return { start = { line = r, character = c } } else - trace("error: def not found in ", bufnr, definition:range(), definition:type(), definition:parent():type()) + trace('error: def not found in ', bufnr, definition:range(), definition:type(), definition:parent():type()) end end @@ -111,16 +113,69 @@ end --- This function copy from treesitter/refactor/navigation.lua local function get_definitions(bufnr) local local_nodes = ts_locals.get_locals(bufnr) - -- Make sure the nodes are unique. local nodes_set = {} for _, loc in ipairs(local_nodes) do + trace(loc) if loc.definition then ts_locals.recurse_local_nodes(loc.definition, function(_, node, _, match) -- lua doesn't compare tables by value, -- use the value from byte count instead. - local _, _, start = node:start() - nodes_set[start] = {node = node, type = match or ""} + local k, l, start = node:start() + trace(node, match) + trace(k, l, start, node:parent(), node:parent():start(), node:parent():type()) + + if node and node:parent() and string.find(node:parent():type(), 'parameter_declaration') then + log('parameter_declaration skip') + return + end + nodes_set[start] = { node = node, type = match or '' } + end) + end + + if loc.method then -- for go + ts_locals.recurse_local_nodes(loc.method, function(def, node, full_match, match) + local k, l, start = node:start() + + trace(k, l, start, def, node, full_match, match, node:parent(), node:parent():start(), node:parent():type()) + if node:type() == 'field_identifier' and nodes_set[start] == nil then + nodes_set[start] = { node = node, type = 'method' } + end + end) + end + if loc.interface then -- for go using interface can output full method definition + ts_locals.recurse_local_nodes(loc.interface, function(def, node, full_match, match) + local k, l, start = node:start() + trace(k, l, start, def, node, full_match, match, node:parent(), node:parent():start(), node:parent():type()) + if nodes_set[start] == nil then + nodes_set[start] = { node = node, type = match or '' } + end + end) + end + if loc.reference then -- for go + ts_locals.recurse_local_nodes(loc.reference, function(def, node, full_match, match) + local k, l, start = node:start() + local p1, p1t = '', '' + local p2, p2t = '', '' + if node:parent() and node:parent():parent() then + p1 = node:parent() + p1t = node:parent():type() + p2 = node:parent():parent() + p2t = node:parent():parent():type() + end + trace(k, l, start, def, node, full_match, match, p1t, p1, node:parent():start(), node:parent():type(), p2, p2t) + if nodes_set[start] == nil then + if -- qualified_type : e.g. io.Reader inside interface + node:parent() + and node:parent():parent() + and node:type() == 'type_identifier' + and node:parent():type() == 'qualified_type' + and string.find(node:parent():parent():type(), 'interface') + then + log('add node', node) + nodes_set[start] = { node = node, type = match or 'field' } + end + end end) end end @@ -140,7 +195,7 @@ local function prepare_node(node, kind) local matches = {} kind = kind or node.type if node.node then - table.insert(matches, {kind = get_icon(kind), def = node.node, type = kind}) + table.insert(matches, { kind = get_icon(kind), def = node.node, type = kind }) else for name, item in pairs(node) do vim.list_extend(matches, prepare_node(item, name)) @@ -150,9 +205,7 @@ local function prepare_node(node, kind) end 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) @@ -170,7 +223,7 @@ local function get_scope(type, source) -- for C++ local n = source - for i = 1, 4, 1 do + for _ = 1, 4, 1 do if n == nil or n:parent() == nil then break end @@ -182,8 +235,8 @@ local function get_scope(type, source) return parent, true end - if type == "var" and next ~= nil then - if next:type() == "function" or next:type() == "arrow_function" or next:type() == "function_definition" then + 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 @@ -196,7 +249,7 @@ local function get_scope(type, source) -- M.fun1 = function() end -- lets work up and see next node, lua local n = source - for i = 1, 4, 1 do + for _ = 1, 4, 1 do if n == nil or n:parent() == nil then break end @@ -208,10 +261,9 @@ local function get_scope(type, source) end end - if source:type() == "type_identifier" then + if source:type() == 'type_identifier' then return source:parent(), true end - end local function get_smallest_context(source) @@ -229,10 +281,10 @@ local function get_smallest_context(source) -- if source:type() == "identifier" then return get_var_context(source) end end -local lsp_reference = require"navigator.dochighlight".goto_adjent_reference +local lsp_reference = require('navigator.dochighlight').goto_adjent_reference function M.goto_adjacent_usage(bufnr, delta) - local opt = {forward = true} + local opt = { forward = true } -- log(delta) if delta < 0 then opt.forward = false @@ -269,7 +321,7 @@ local function key(fname, filter) end local function get_all_nodes(bufnr, filter, summary) - local fname = vim.fn.expand("%:p:f") + local fname = vim.fn.expand('%:p:f') local uri = vim.uri_from_fname(fname) if bufnr ~= 0 then uri = vim.uri_from_bufnr(bufnr) @@ -282,7 +334,8 @@ local function get_all_nodes(bufnr, filter, summary) local result = lru:get(hash) if result ~= nil and result.ftime == ftime then - log("get data from cache") + trace('get data from cache', ftime, result) + return result.nodes, result.length end @@ -292,31 +345,36 @@ local function get_all_nodes(bufnr, filter, summary) trace(bufnr, filter, summary) if not bufnr then - vim.notify("get_all_node invalide bufnr", vim.lsp.log_levels.WARN) + vim.notify('get_all_node invalide bufnr', vim.lsp.log_levels.WARN) end summary = summary or false + local ft = vim.api.nvim_buf_get_option(bufnr, 'filetype') if not parsers.has_parser() then - vim.notify("ts not loaded", vim.lsp.log_levels.WARN) + if not require('navigator.lspclient.clients').ft_disabled(ft) then + vim.notify('ts not loaded ' .. ft, vim.lsp.log_levels.Debug) + end + return {} end - local path_sep = require"navigator.util".path_sep() - local path_cur = require"navigator.util".path_cur() + local path_sep = require('navigator.util').path_sep() + local path_cur = require('navigator.util').path_cur() local display_filename = fname:gsub(cwd .. path_sep, path_cur, 1) local all_nodes = {} -- Support completion-nvim customized label map - local customized_labels = vim.g.completion_customize_lsp_label or {} + -- local customized_labels = vim.g.completion_customize_lsp_label or {} -- Force some types to act like they are parents -- instead of neighbors of the next nodes. local containers = { - ["function"] = true, - ["local_function"] = true, - ["arrow_function"] = true, - ["type"] = true, - ["class"] = true, - ["struct"] = true, - ["method"] = true + ['function'] = true, + ['local_function'] = true, + ['arrow_function'] = true, + ['type'] = true, + ['class'] = true, + -- ['var'] = true, + ['struct'] = true, + ['method'] = true, } -- check and load buff @@ -330,15 +388,31 @@ local function get_all_nodes(bufnr, filter, summary) -- Step 2 find correct completions local length = 10 local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos + local loaded_symbol = {} for _, def in ipairs(get_definitions(bufnr)) do local n = #parents for i = 1, n do local index = n + 1 - i local parent_def = parents[index] - if ts_utils.is_parent(parent_def.node, def.node) - or (containers[parent_def.type] and ts_utils.is_parent(parent_def.node:parent(), def.node)) then + log(parent_def.type, parent_def.node:type(), vim.treesitter.get_node_text(parent_def.node, bufnr)) + log(def.node:type(), vim.treesitter.get_node_text(def.node, bufnr)) + if + ts_utils.is_parent(parent_def.node, def.node) + or ( + containers[parent_def.type] + and ( + ts_utils.is_parent(parent_def.node:parent(), def.node) + or ( + parent_def.node:parent():type():find('dot_index') + and ts_utils.is_parent(parent_def.node:parent():parent(), def.node) + ) + ) + ) + then + log('is parent', i, index) break else + log('leave node', i, index) parents[index] = nil end end @@ -351,15 +425,21 @@ local function get_all_nodes(bufnr, filter, summary) item.type = node.type if filter ~= nil and not filter[item.type] then - trace(item.type, item.kind) + trace('skipped', item.type, item.kind) + goto continue + end + + if item.type == 'associated' then + trace('skipped', item.type, item.kind) goto continue end local tsdata = node.def if node.def == nil then + trace('skipped', item.type, item.kind) goto continue end - item.node_text = ts_utils.get_node_text(tsdata, bufnr)[1] + item.node_text = vim.treesitter.get_node_text(tsdata, bufnr) or '' local scope, is_func if summary then @@ -370,38 +450,64 @@ local function get_all_nodes(bufnr, filter, summary) 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] + if parent ~= nil and _NgConfigValues.debug == 'trace' then -- for github action failure + trace(parent:type(), vim.treesitter.get_node_text(parent, bufnr):sub(1, 30), item.node_text, item.type) + end + if + parent ~= nil + and ( + parent:type() == 'function_name' + -- or parent:type() == 'function' + -- or parent:type() == 'function_declaration' -- this bring in too much info + or parent:type() == 'method_name' + or parent:type() == 'function_name_field' + ) + then + -- replace function name + item.node_text = vim.treesitter.get_node_text(parent, bufnr) + local cut = item.node_text:find('[\n\r]') + if cut then + item.node_text = item.node_text:sub(1, cut - 1) + end log(parent:type(), item.node_text) end end trace(item.node_text, item.kind, item.type) if scope ~= nil then - -- it is strange.. if not is_func and summary then + log('skipped', item.node_text, item.type) goto continue end item.node_scope = ts_utils.node_to_lsp_range(scope) end + if item.node_text and vim.trim(item.node_text) == '_' then + goto continue + end if summary then if item.node_scope ~= nil then table.insert(all_nodes, item) end if item.node_scope then - trace(item.type, tsdata:type(), item.node_text, item.kind, item.node_text, "range", - item.node_scope.start.line, item.node_scope['end'].line) -- set to log if need to trace result + trace( + item.type, + tsdata:type(), + item.node_text, + item.kind, + 'range', + item.node_scope.start.line, + item.node_scope['end'].line + ) -- set to log if need to trace result end 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 - item.full_text = vim.trim(api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or "") + + local line_text = api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or '' + item.full_text = vim.trim(line_text) item.full_text = item.full_text:gsub('%s*[%[%(%{]*%s*$', '') item.uri = uri @@ -411,45 +517,76 @@ local function get_all_nodes(bufnr, filter, summary) item.lnum, item.col, _ = def.node:start() item.lnum = item.lnum + 1 item.col = item.col + 1 - local indent = "" + local indent = '' if #parents > 1 then - indent = string.rep(" ", #parents - 1) .. "๎˜ก " + indent = string.rep(' ', #parents - 1) .. '๎˜ก ' + end + item.indent = indent + item.indent_level = #parents -- maybe use real indent level ? + if item.indent_level <= 1 then + local sp = string.match(line_text, '(%s*)') + log(line_text, #sp) + if sp then + local indent_level = #sp / (vim.o.shiftwidth or 4) + 1 + item.indent_level = math.max(item.indent_level, indent_level) + end + end + if #parents > 0 then + log(parents[1].type, vim.treesitter.get_node_text(parents[1].node, bufnr)) + if parents[2] then + log(parents[2].type, vim.treesitter.get_node_text(parents[2].node, bufnr)) + end + else + log('root node') + end + if #all_nodes >= 1 then + all_nodes[#all_nodes].next_indent_level = #parents end - item.text = string.format(" %s %s%-10s\t %s", item.kind, indent, item.node_text, item.full_text) + 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 - table.insert(all_nodes, item) + if + loaded_symbol[item.node_text .. item.kind] == nil + or not util.range_inside(loaded_symbol[item.node_text .. item.kind], item.node_scope) + then + table.insert(all_nodes, item) + loaded_symbol[item.node_text .. item.kind] = item.node_scope + end ::continue:: end end trace(all_nodes) - local nd = {nodes = all_nodes, ftime = vim.fn.getftime(fname), length = length} + local nd = { nodes = all_nodes, ftime = vim.fn.getftime(fname), length = length } lru:set(hash, nd) if should_unload then - vim.api.nvim_buf_delete(bufnr, {unload = true}) + vim.api.nvim_buf_delete(bufnr, { unload = true }) end return all_nodes, length end function M.buf_func(bufnr) + local ft = vim.api.nvim_buf_get_option(bufnr, 'buftype') + if vim.api.nvim_buf_get_option(bufnr, 'buftype') == 'nofile' then + return + end if not ok or ts_locals == nil then - error("treesitter not loaded") + error('treesitter not loaded: ' .. ft) return end bufnr = bufnr or api.nvim_get_current_buf() local all_nodes, width = get_all_nodes(bufnr, { - ["function"] = true, - ["var"] = true, - ["method"] = true, - ["class"] = true, - ["type"] = true + ['function'] = true, + ['var'] = true, + ['method'] = true, + ['class'] = true, + ['type'] = true, }, true) if #all_nodes < 1 then - trace("no node found for ", bufnr) -- set to log + trace('no node found for ', bufnr) -- set to log return end @@ -478,34 +615,59 @@ function M.buf_func(bufnr) end return all_nodes, width - end -function M.buf_ts() +function M.all_ts_nodes(bufnr) if ts_locals == nil then - error("treesitter not loaded") + error('treesitter not loaded') return end - local bufnr = api.nvim_get_current_buf() + bufnr = bufnr or api.nvim_get_current_buf() local all_nodes, width = get_all_nodes(bufnr) + return all_nodes, width +end - local ft = vim.api.nvim_buf_get_option(bufnr, "ft") - gui.new_list_view({ +function M.side_panel() + Panel = require('guihua.panel') + local bufnr = api.nvim_get_current_buf() + local panel = Panel:new({ + header = 'treesitter', + render = function(b) + local ft = vim.api.nvim_buf_get_option(b, 'buftype') + log('render for ', bufnr, b) + if ft == 'nofile' or ft == 'guihua' then + b = bufnr + end + return require('navigator.treesitter').all_ts_nodes(b) + end, + scope = 'node_scope' + }) + panel:open(true) +end + +function M.buf_ts() + local all_nodes, width = M.all_ts_nodes() + local bufnr = api.nvim_get_current_buf() + local ft = vim.api.nvim_buf_get_option(bufnr, 'ft') + local listview = gui.new_list_view({ items = all_nodes, prompt = true, ft = ft, rawdata = true, + height = 0.62, + preview_height = 0.12, width = width + 10, - api = _NgConfigValues.icons.treesitter_defult + api = _NgConfigValues.icons.treesitter_defult, }) + return listview, all_nodes, width end M.get_all_nodes = get_all_nodes function M.bufs_ts() if ts_locals == nil then - error("treesitter not loaded") + error('treesitter not loaded') return end local bufs = vim.api.nvim_list_bufs() @@ -528,13 +690,15 @@ function M.bufs_ts() if #ts_opened > 1 then trace(ts_opened) - local ft = vim.api.nvim_buf_get_option(0, "ft") + local ft = vim.api.nvim_buf_get_option(0, 'ft') gui.new_list_view({ items = ts_opened, prompt = true, ft = ft, + height = 0.62, + preview_height = 0.12, width = max_length + 10, - api = _NgConfigValues.icons.treesitter_defult + api = _NgConfigValues.icons.treesitter_defult, }) end end @@ -543,7 +707,7 @@ local function node_in_range(parser, range) for _, child in pairs(parser._children) do if child:contains(range) then local result = node_in_range(child, range) - if not vim.tbl_contains({vim.bo.filetype}, result:lang()) then + if not vim.tbl_contains({ vim.bo.filetype }, result:lang()) then -- log("not correct tree embedded or comment?", result:lang()) return parser end @@ -564,7 +728,7 @@ function M.get_node_at_line(lnum) lnum = cursor[1] end local first_non_whitespace_col = fn.match(fn.getline(lnum), '\\S') - local range = {lnum - 1, first_non_whitespace_col, lnum - 1, first_non_whitespace_col} + local range = { lnum - 1, first_non_whitespace_col, lnum - 1, first_non_whitespace_col } -- Get the language tree with nodes inside the given range local root = parsers.get_parser() @@ -578,7 +742,7 @@ function M.get_node_at_line(lnum) return node end -local usage_namespace = vim.api.nvim_create_namespace("nvim-treesitter-usages") +local usage_namespace = vim.api.nvim_create_namespace('nvim-treesitter-usages') function M.highlight_usages(bufnr) M.clear_usage_highlights(bufnr) @@ -595,12 +759,12 @@ function M.highlight_usages(bufnr) for _, usage_node in ipairs(usages) do if usage_node ~= node_at_point then - ts_utils.highlight_node(usage_node, bufnr, usage_namespace, "TSDefinitionUsage") + ts_utils.highlight_node(usage_node, bufnr, usage_namespace, 'TSDefinitionUsage') end end if def_node ~= node_at_point then - ts_utils.highlight_node(def_node, bufnr, usage_namespace, "TSDefinition") + ts_utils.highlight_node(def_node, bufnr, usage_namespace, 'TSDefinition') end end @@ -610,7 +774,7 @@ end function M.get_node_at_pos(pos, parser) -- local cursor = api.nvim_win_get_cursor(winnr or 0) - local cursor_range = {pos[1], pos[2]} + local cursor_range = { pos[1], pos[2] } log(cursor_range) local root = ts_utils.get_root_for_position(unpack(cursor_range), parser) @@ -651,7 +815,6 @@ function M.get_node_scope(node) end return sr, sc, er, ec - end return M diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua index 78cf6c7..63ed191 100644 --- a/lua/navigator/util.lua +++ b/lua/navigator/util.lua @@ -3,8 +3,11 @@ -- Some of function copied from https://github.com/RishabhRD/nvim-lsputils local M = { log_path = vim.lsp.get_log_path() } -- local is_windows = uv.os_uname().version:match("Windows") - -local nvim_0_6 +local guihua = require('guihua.util') +local nvim_0_6_1 +local nvim_0_8 +local vfn = vim.fn +local api = vim.api M.path_sep = function() local is_win = vim.loop.os_uname().sysname:find('Windows') @@ -41,10 +44,10 @@ function M.get_data_from_file(filename, startLine) end local uri = 'file:///' .. filename local bufnr = vim.uri_to_bufnr(uri) - if not vim.api.nvim_buf_is_loaded(bufnr) then - vim.fn.bufload(bufnr) + if not api.nvim_buf_is_loaded(bufnr) then + vfn.bufload(bufnr) end - local data = vim.api.nvim_buf_get_lines(bufnr, startLine, startLine + 8, false) + local data = api.nvim_buf_get_lines(bufnr, startLine, startLine + 8, false) if data == nil or vim.tbl_isempty(data) then startLine = nil else @@ -58,6 +61,29 @@ function M.get_data_from_file(filename, startLine) return { data = data, line = displayLine } end +function M.io_read(filename, total) + local f = io.open(filename, 'r') + if f == nil then + return nil + end + local content = f:read('*a') -- *a or *all reads the whole file + f:close() + return content +end + +function M.rm_file(filename) + return os.remove(filename) +end + +function M.file_exists(name) + local f = io.open(name, 'r') + if f ~= nil then + io.close(f) + return true + end + return false +end + M.merge = function(t1, t2) for k, v in pairs(t2) do t1[k] = v @@ -76,9 +102,9 @@ M.map = function(modes, key, result, options) for i = 1, #modes do if buffer then - vim.api.nvim_buf_set_keymap(0, modes[i], key, result, options) + api.nvim_buf_set_keymap(0, modes[i], key, result, options) else - vim.api.nvim_set_keymap(modes[i], key, result, options) + api.nvim_set_keymap(modes[i], key, result, options) end end end @@ -113,7 +139,13 @@ end function M.get_relative_path(base_path, my_path) local base_data = getDir(base_path) + if base_data == nil then + return + end local my_data = getDir(my_path) + if my_data == nil then + return + end local base_len = #base_data local my_len = #my_data @@ -151,13 +183,31 @@ if _NgConfigValues.debug_console_output then default_config.use_console = true default_config.use_file = false end + M._log = require('guihua.log').new(default_config, true) +if _NgConfigValues.debug then + -- add log to you lsp.log --- add log to you lsp.log -M.log = M._log.info -M.info = M._log.info -M.trace = M._log.trace -M.error = M._log.error + M.trace = M._log.trace + M.info = M._log.info + M.warn = M._log.warn + M.error = M._log.error + M.log = M.info +else + M.log = function(...) + return { ... } + end + M.info = function(...) + return { ... } + end + M.trace = function(...) + return { ... } + end + M.warn = function(...) + return { ... } + end + M.error = M._log.error +end function M.fmt(...) M._log.fmt_info(...) @@ -251,7 +301,7 @@ function M.split2(s, sep) sep = sep or ' ' local pattern = string.format('([^%s]+)', sep) - string.gsub(s, pattern, function(c) + _ = string.gsub(s, pattern, function(c) fields[#fields + 1] = c end) @@ -278,30 +328,18 @@ function M.trim_and_pad(txt) end M.open_file = function(filename) - vim.api.nvim_command(string.format('e! %s', filename)) + api.nvim_command(string.format('e! %s', filename)) end -M.open_file_at = function(filename, line, col, split) - if split == nil then - -- code - vim.api.nvim_command(string.format('e! +%s %s', line, filename)) - elseif split == 'v' then - vim.api.nvim_command(string.format('vsp! +%s %s', line, filename)) - elseif split == 's' then - vim.api.nvim_command(string.format('sp! +%s %s', line, filename)) - end - -- vim.api.nvim_command(string.format("e! %s", filename)) - col = col or 1 - vim.fn.cursor(line, col) -end +M.open_file_at = guihua.open_file_at -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 local exclude_ft = { 'scrollbar', 'help', 'NvimTree' } function M.exclude(fname) @@ -317,7 +355,6 @@ end -- name space search local nss -local api = vim.api local bufs function M.set_virt_eol(bufnr, lnum, chunks, priority, id) @@ -355,40 +392,56 @@ function M.get_current_winid() return api.nvim_get_current_win() end -function M.nvim_0_6() - if nvim_0_6 ~= nil then - return nvim_0_6 +function M.nvim_0_6_1() + if nvim_0_6_1 ~= nil then + return nvim_0_6_1 end - if debug.getinfo(vim.lsp.handlers.signature_help).nparams == 4 then - nvim_0_6 = true - else - nvim_0_6 = false + nvim_0_6_1 = vfn.has('nvim-0.6.1') == 1 + if nvim_0_6_1 == false then + M.warn('Please use navigator 0.3 version for neovim version < 0.6.1') + end + return nvim_0_6_1 +end + +function M.nvim_0_8() + if nvim_0_8 ~= nil then + return nvim_0_8 end - return nvim_0_6 + nvim_0_8 = vfn.has('nvim-0.8') == 1 + if nvim_0_8 == false then + M.log('Please use navigator 0.4 version for neovim version < 0.8') + end + return nvim_0_8 end function M.mk_handler(fn) return function(...) - local config_or_client_id = select(4, ...) - local is_new = M.nvim_0_6() - if is_new then - return fn(...) - else - local err = select(1, ...) - local method = select(2, ...) - local result = select(3, ...) - local client_id = select(4, ...) - local bufnr = select(5, ...) - local config = select(6, ...) - return fn(err, result, { method = method, client_id = client_id, bufnr = bufnr }, config) - end + return fn(...) end end function M.partial(func, arg) - return (M.mk_handler(function(...) + return function(...) return func(arg, ...) - end)) + 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) @@ -403,18 +456,19 @@ function M.empty(t) end function M.encoding(client) - if type(client) ~= 'table' then - if client == nil then - client = 1 - end - client = vim.lsp.get_client_by_id(client) + if client == nil then + client = 1 + end + + if type(client) == 'number' then + client = vim.lsp.get_client_by_id(client) or {} end local oe = client.offset_encoding if oe == nil then return 'utf-8' end if type(oe) == 'table' then - oe = oe[1] or 'utf-8' + return oe[1] end return oe end @@ -422,15 +476,25 @@ end -- alternatively: use vim.notify("namespace does not exist or is anonymous", vim.log.levels.ERROR) function M.warn(msg) - vim.api.nvim_echo({ { 'WRN: ' .. msg, 'WarningMsg' } }, true, {}) + vim.notify('WRN: ' .. msg, vim.lsp.log_levels.WARN) end function M.error(msg) - vim.api.nvim_echo({ { 'ERR: ' .. msg, 'ErrorMsg' } }, true, {}) + vim.notify('ERR: ' .. msg, vim.lsp.log_levels.EROR) end function M.info(msg) - vim.api.nvim_echo({ { 'Info: ' .. msg } }, true, {}) + vim.notify('INF: ' .. msg, vim.lsp.log_levels.INFO) +end + +function M.range_inside(outer, inner) + if outer == nil or inner == nil then + return false + end + if outer.start == nil or outer['end'] == nil or inner.start == nil or inner['end'] == nil then + return false + end + return outer.start.line <= inner.start.line and outer['end'].line >= inner['end'].line end return M diff --git a/lua/navigator/workspace.lua b/lua/navigator/workspace.lua index e7ca4d9..ca483f7 100644 --- a/lua/navigator/workspace.lua +++ b/lua/navigator/workspace.lua @@ -1,12 +1,15 @@ -- https://github.com/lukas-reineke/dotfiles/blob/master/vim/lua/lsp/rename.lua local M = {} local util = require('navigator.util') --- local rename_prompt = 'Rename -> ' +local gutil = require('guihua.util') +local lsphelper = require('navigator.lspwrapper') +local symbols_to_items = lsphelper.symbols_to_items +local vfn = vim.fn M.add_workspace_folder = function() util.log(vim.ui.input) local input = require('guihua.floating').input - input({prompt = 'Workspace To Add: ', default = vim.fn.expand('%:p:h')}, function(inputs) + input({ prompt = 'Workspace To Add: ', default = vfn.expand('%:p:h') }, function(inputs) vim.lsp.buf.add_workspace_folder(inputs) end) end @@ -16,7 +19,7 @@ M.remove_workspace_folder = function() local folders = vim.lsp.buf.list_workspace_folders() if #folders > 1 then - select(folders, {prompt = 'select workspace to delete'}, function(workspace) + select(folders, { prompt = 'select workspace to delete' }, function(workspace) vim.lsp.buf.remove_workspace_folder(workspace) end) end @@ -24,13 +27,68 @@ end M.workspace_symbol = function() local input = require('guihua.floating').input - input({prompt = 'Find symbol: ', default = ''}, function(inputs) + input({ prompt = 'Search symbol: ', default = '' }, function(inputs) util.log(inputs) - print(inputs) vim.lsp.buf.workspace_symbol(inputs) end) end +function M.workspace_symbol_live() + local height = _NgConfigValues.height or 0.4 + height = math.floor(height * vfn.winheight('%')) + local width = _NgConfigValues.width or 0.7 + width = math.floor(width * vfn.winwidth('%')) + local bufnr = vim.api.nvim_get_current_buf() + local ft = vim.o.ft + local data = { { text = 'input the symbol name to start fuzzy search' } } + for _ = 1, height do + table.insert(data, { text = '' }) + end + local ListView = require('guihua.listview') + local opt = { + api = '๏ ฐ ', + bg = 'GHListDark', + data = data, + items = data, + enter = true, + ft = ft, + loc = 'top_center', + transparency = 50, + prompt = true, + on_confirm = function(item) + vim.defer_fn(function() + if item and item.name then + require('navigator.symbols').workspace_symbols(item.name) + end + end, 10) + end, + on_input_filter = function(text) + local params = { query = text or '#' } + local results = vim.lsp.buf_request_sync(bufnr, 'workspace/symbol', params) + local result + for _, r in pairs(results) do + -- util.log(r) + if r.result then + result = r.result + break + end + end + if not result then + result = {} + end + + local items = symbols_to_items(result) + items = gutil.dedup(items, 'name', 'kind') + return items + end, + rect = { height = height, pos_x = 0, pos_y = 0, width = width }, + } + + local win = ListView:new(opt) + win:on_draw({}) + -- require('guihua.gui').new_list_view(opt) +end + M.list_workspace_folders = function() local folders = vim.lsp.buf.list_workspace_folders() if #folders > 0 then @@ -38,8 +96,7 @@ M.list_workspace_folders = function() items = folders, border = 'single', rawdata = true, - on_move = function(...) - end + on_move = function() end, }) end end diff --git a/playground/README.md b/playground/README.md index e929157..5f73db5 100644 --- a/playground/README.md +++ b/playground/README.md @@ -12,13 +12,14 @@ most used plugins for programmer. - luasnip - aurora (colorscheme used in the screenshot) -There also three folder `js`, `go`, `py`. Those folder have some basic source code you can play with. +There are three folders `js`, `go`, `py`. Those folders have some basic source code you can play with. +The init will install the plugins in ``/tmp/nvim`` folder. It will not affect your current setup. ## Install LSP The playground has js, py, go folder, so you can install either one your self in your PATH. If you want to try lua, Please check sumneko setup in init.lua make sure it pointed to correct path. By default it -potint to ~/github/sumneko +potint to ~/github/sumneko if not existed in your PATH. ## run init.lua diff --git a/playground/init.lua b/playground/init.lua index fb29b5f..e73ce97 100644 --- a/playground/init.lua +++ b/playground/init.lua @@ -10,7 +10,7 @@ local sumneko_root_path = vim.fn.expand('$HOME') .. '/github/sumneko/lua-languag local sumneko_binary = vim.fn.expand('$HOME') .. '/github/sumneko/lua-language-server/bin/macOS/lua-language-server' local lua_cfg = { - cmd = { sumneko_binary, '-E', sumneko_root_path .. '/main.lua' }, + -- cmd = { sumneko_binary, '-E', sumneko_root_path .. '/main.lua' }, settings = { Lua = { runtime = { version = 'LuaJIT', path = vim.split(package.path, ';') }, @@ -19,6 +19,10 @@ local lua_cfg = { }, } +if vim.fn.executable('lua-language-server') == 0 then + lua_cfg.cmd = { sumneko_binary, '-E', sumneko_root_path .. '/main.lua' } +end + local function load_plugins() require('packer').startup({ function(use) @@ -38,7 +42,7 @@ local function load_plugins() use({ 'ray-x/aurora' }) use({ 'ray-x/navigator.lua', - -- '~/github/navigator.lua', + -- '~/github/ray-x/navigator.lua', requires = { 'ray-x/guihua.lua', run = 'cd lua/fzy && make' }, config = function() require('navigator').setup({ diff --git a/playground/init_lsp_installer.lua b/playground/init_lsp_installer.lua new file mode 100644 index 0000000..7d4c81f --- /dev/null +++ b/playground/init_lsp_installer.lua @@ -0,0 +1,61 @@ +vim.cmd([[set runtimepath=$VIMRUNTIME]]) +vim.cmd([[set packpath=/tmp/nvim/site]]) + +local package_root = '/tmp/nvim/site/pack' +local install_path = package_root .. '/packer/start/packer.nvim' +vim.g.coq_settings = { + ['auto_start'] = 'shut-up', +} + +local function load_plugins() + require('packer').startup({ + function(use) + use('wbthomason/packer.nvim') + use('neovim/nvim-lspconfig') + 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, + }) + use('ray-x/guihua.lua') + -- -- COQ (Autocompletion) + use('ms-jpq/coq_nvim') + use('ms-jpq/coq.artifacts') + use('ms-jpq/coq.thirdparty') + use('ray-x/aurora') + end, + config = { + package_root = package_root, + compile_path = install_path .. '/plugin/packer_compiled.lua', + }, + }) + -- navigator/LSP setup +end + +if vim.fn.isdirectory(install_path) == 0 then + print('install packer') + vim.fn.system({ + 'git', + 'clone', + 'https://github.com/wbthomason/packer.nvim', + install_path, + }) + load_plugins() + require('packer').sync() + vim.cmd('colorscheme aurora') +else + load_plugins() + vim.cmd('colorscheme aurora') +end diff --git a/playground/init_lsp_installer_cmp.lua b/playground/init_lsp_installer_cmp.lua new file mode 100644 index 0000000..b824c76 --- /dev/null +++ b/playground/init_lsp_installer_cmp.lua @@ -0,0 +1,79 @@ +vim.cmd([[set runtimepath=$VIMRUNTIME]]) +vim.cmd([[set packpath=/tmp/nvim/site]]) + +local package_root = '/tmp/nvim/site/pack' +local install_path = package_root .. '/packer/start/packer.nvim' + +local function load_plugins() + require('packer').startup({ + function(use) + use('wbthomason/packer.nvim') + use('neovim/nvim-lspconfig') + use({ + 'williamboman/nvim-lsp-installer', + config = function() + require('nvim-lsp-installer').setup({}) + end, + }) + use({ + 'ray-x/navigator.lua', + -- '~/github/ray-x/navigator.lua', + config = function() + require('navigator').setup({ + debug = true, + lsp_installer = true, + keymaps = { { key = 'gR', func = "require('navigator.reference').async_ref()" } }, + }) + end, + }) + use('ray-x/guihua.lua') + + use({ + 'hrsh7th/nvim-cmp', + requires = { + 'hrsh7th/cmp-nvim-lsp', + }, + config = function() + local cmp = require('cmp') + cmp.setup({ + mapping = { + [''] = cmp.mapping.confirm({ select = true }), + [''] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.confirm({ select = true }) + else + fallback() + end + end, { 'i', 's' }), + }, + sources = { + { name = 'nvim_lsp' }, + }, + }) + end, + }) + use('ray-x/aurora') + end, + config = { + package_root = package_root, + compile_path = install_path .. '/plugin/packer_compiled.lua', + }, + }) + -- navigator/LSP setup +end + +if vim.fn.isdirectory(install_path) == 0 then + print('install packer') + vim.fn.system({ + 'git', + 'clone', + 'https://github.com/wbthomason/packer.nvim', + install_path, + }) + load_plugins() + require('packer').sync() + vim.cmd('colorscheme aurora') +else + load_plugins() + vim.cmd('colorscheme aurora') +end diff --git a/playground/py/go.py b/playground/py/go.py new file mode 100644 index 0000000..8fb8de7 --- /dev/null +++ b/playground/py/go.py @@ -0,0 +1,14 @@ +from random import shuffle +a = list(range(5)) + +def go(beg, c, b): + if beg >= len(a): + print(a ) + for i in range(beg, len(a)): + a[beg], a[i] = a[i], a[beg] + go(beg + 1) + a[beg], a[i] = a[i], a[beg] + print(a, b) + +go(0, 1, 4) +shuffle([1, 2,3 ]) diff --git a/tests/call_hierarchy_spec.lua b/tests/call_hierarchy_spec.lua new file mode 100644 index 0000000..bc570c9 --- /dev/null +++ b/tests/call_hierarchy_spec.lua @@ -0,0 +1,69 @@ +local busted = require('plenary/busted') + +local eq = assert.are.same +local cur_dir = vim.fn.expand('%:p:h') +-- local status = require("plenary.reload").reload_module("go.nvim") +-- status = require("plenary.reload").reload_module("nvim-treesitter") + +-- local ulog = require('go.utils').log +describe('should run lsp call hierarchy', function() + vim.cmd([[packadd navigator.lua]]) + vim.cmd([[packadd guihua.lua]]) + local status = require('plenary.reload').reload_module('navigator') + status = require('plenary.reload').reload_module('guihua') + status = require('plenary.reload').reload_module('lspconfig') + + local path = cur_dir .. '/tests/fixtures/interface.go' -- %:p:h ? %:p + local cmd = " silent exe 'e " .. path .. "'" + vim.cmd(cmd) + vim.cmd([[cd %:p:h]]) + local bufn = vim.fn.bufnr('') + require('navigator').setup({ + debug = true, -- log output, set to true and log path: ~/.local/share/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 + border = 'none', + }) + + -- allow gopls start + for _ = 1, 20 do + vim.wait(400, function() end) + local found = false + for _, client in ipairs(vim.lsp.get_active_clients()) do + if client.name == 'gopls' then + found = true + break + end + end + if found then + break + end + end + + it('should show panel', function() + vim.fn.setpos('.', { bufn, 24, 15, 0 }) + require('navigator.hierarchy').incoming_calls_panel() + + vim.wait(300, function() end) + + local panel = require('guihua.panel').debug() + eq(panel.name, 'Panel') + + vim.wait(500, function() end) + panel = require('guihua.panel').debug() + print(vim.inspect(panel)) + -- eq( + -- panel.activePanel.sections[1].header[1], + -- 'โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€Call Hierarchyโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€' + -- ) + -- eq(panel.activePanel.sections[1].nodes[1].name, 'measure') + end) + + it('should show hierarchy', function() + vim.fn.setpos('.', { bufn, 24, 15, 0 }) + local ret = require('navigator.hierarchy')._call_hierarchy() + vim.wait(400, function() end) + eq(ret, {}) + end) +end) diff --git a/tests/reference_spec.lua b/tests/reference_spec.lua index 2386c65..2dd1b63 100644 --- a/tests/reference_spec.lua +++ b/tests/reference_spec.lua @@ -1,13 +1,13 @@ local helpers = {} -local busted = require("plenary/busted") +local busted = require('plenary/busted') local eq = assert.are.same -local cur_dir = vim.fn.expand("%:p:h") +local cur_dir = vim.fn.expand('%:p:h') -- local status = require("plenary.reload").reload_module("go.nvim") -- status = require("plenary.reload").reload_module("nvim-treesitter") -- local ulog = require('go.utils').log -describe("should run lsp reference", function() +describe('should run lsp reference', function() -- vim.fn.readfile('minimal.vim') local nvim_6 = true if debug.getinfo(vim.lsp.handlers.signature_help).nparams > 4 then @@ -15,113 +15,90 @@ describe("should run lsp reference", function() end local result = { { - range = {['end'] = {character = 6, line = 14}, start = {character = 1, line = 14}}, - uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go" - }, { - range = {['end'] = {character = 15, line = 24}, start = {character = 10, line = 24}}, - uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go" - }, { - range = {['end'] = {character = 17, line = 28}, start = {character = 12, line = 28}}, - uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go" - }, { - range = {['end'] = {character = 19, line = 51}, start = {character = 14, line = 51}}, - uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go" - }, { - range = {['end'] = {character = 19, line = 55}, start = {character = 14, line = 55}}, - uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go" - }, { - range = {['end'] = {character = 16, line = 59}, start = {character = 11, line = 59}}, - - uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go" - }, { - range = {['end'] = {character = 16, line = 5}, start = {character = 11, line = 5}}, - uri = "file://" .. cur_dir .. "/tests/fixtures/interface_test.go" - } - } - - it("should show references", function() - - local status = require("plenary.reload").reload_module("navigator") - local status = require("plenary.reload").reload_module("guihua") - local status = require("plenary.reload").reload_module("lspconfig") - - vim.cmd([[packadd navigator.lua]]) - vim.cmd([[packadd guihua.lua]]) - local path = cur_dir .. "/tests/fixtures/interface.go" -- %:p:h ? %:p - local cmd = " silent exe 'e " .. path .. "'" - vim.cmd(cmd) - vim.cmd([[cd %:p:h]]) - local bufn = vim.fn.bufnr("") - -- require'lspconfig'.gopls.setup {} - require'navigator'.setup({ - debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log - icons = {code_action_icon = "A "}, - width = 0.75, -- max width ratio (number of cols for the floating window) / (window width) - height = 0.3, -- max list window height, 0.3 by default - preview_height = 0.35, -- max height of preview windows - border = 'none' - }) + range = { ['end'] = { character = 6, line = 14 }, start = { character = 1, line = 14 } }, + uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', + }, + { + range = { ['end'] = { character = 15, line = 24 }, start = { character = 10, line = 24 } }, + uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', + }, + { + range = { ['end'] = { character = 17, line = 28 }, start = { character = 12, line = 28 } }, + uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', + }, + { + range = { ['end'] = { character = 19, line = 51 }, start = { character = 14, line = 51 } }, + uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', + }, + { + range = { ['end'] = { character = 19, line = 55 }, start = { character = 14, line = 55 } }, + uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', + }, + { + range = { ['end'] = { character = 16, line = 59 }, start = { character = 11, line = 59 } }, - -- allow gopls start - for i = 1, 10 do - vim.wait(400, function() - end) - local clients = vim.lsp.get_active_clients() - print("lsp clients: ", #clients) - if #clients > 0 then + uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', + }, + { + range = { ['end'] = { character = 16, line = 5 }, start = { character = 11, line = 5 } }, + uri = 'file://' .. cur_dir .. '/tests/fixtures/interface_test.go', + }, + } + local status = require('plenary.reload').reload_module('navigator') + status = require('plenary.reload').reload_module('guihua') + status = require('plenary.reload').reload_module('lspconfig') + + vim.cmd([[packadd navigator.lua]]) + vim.cmd([[packadd guihua.lua]]) + local path = cur_dir .. '/tests/fixtures/interface.go' -- %:p:h ? %:p + local cmd = " silent exe 'e " .. path .. "'" + vim.cmd(cmd) + vim.cmd([[cd %:p:h]]) + local bufn = vim.fn.bufnr('') + -- require'lspconfig'.gopls.setup {} + require('navigator').setup({ + debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log + icons = { code_action_icon = 'A ' }, + width = 0.75, -- max width ratio (number of cols for the floating window) / (window width) + height = 0.3, -- max list window height, 0.3 by default + preview_height = 0.35, -- max height of preview windows + border = 'none', + }) + + if vim.fn.has('nvim-0.7') then + _NgConfigValues.treesitter_analysis = true + else + _NgConfigValues.treesitter_analysis = false + end + -- allow gopls start + + for _ = 1, 20 do + vim.wait(400, function() end) + local found = false + for _, client in ipairs(vim.lsp.get_active_clients()) do + if client.name == 'gopls' then + found = true break end end + if found then + break + end + end + it('should show references', function() + vim.fn.setpos('.', { bufn, 15, 4, 0 }) -- width - vim.fn.setpos(".", {bufn, 15, 4, 0}) -- width - - vim.bo.filetype = "go" - -- vim.lsp.buf.references() + vim.bo.filetype = 'go' + vim.lsp.buf.references() eq(1, 1) end) - it("reference handler should return items", function() - - local status = require("plenary.reload").reload_module("navigator") - local status = require("plenary.reload").reload_module("guihua") - vim.cmd([[packadd navigator.lua]]) - vim.cmd([[packadd guihua.lua]]) - local path = cur_dir .. "/tests/fixtures/interface.go" -- %:p:h ? %:p - print(path) - local cmd = " silent exe 'e " .. path .. "'" - vim.cmd(cmd) - -- vim.cmd([[cd %:p:h]]) - local bufn = vim.fn.bufnr("") - - vim.fn.setpos(".", {bufn, 15, 4, 0}) -- width - - vim.bo.filetype = "go" - require'navigator'.setup({ - debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log - icons = {code_action_icon = "A "}, - width = 0.75, -- max width ratio (number of cols for the floating window) / (window width) - height = 0.3, -- max list window height, 0.3 by default - preview_height = 0.35, -- max height of preview windows - debug_console_output = true, - border = 'none' - }) - - _NgConfigValues.debug_console_output = true - - vim.bo.filetype = "go" - -- allow gopls start - for i = 1, 10 do - vim.wait(400, function() - end) - local clients = vim.lsp.get_active_clients() - print("clients ", #clients) - if #clients > 0 then - break - end - end + it('reference handler should return items', function() + vim.fn.setpos('.', { bufn, 15, 4, 0 }) -- width + + vim.bo.filetype = 'go' -- allow gopls start - vim.wait(200, function() - end) + vim.wait(200, function() end) local win, items, width @@ -129,65 +106,22 @@ describe("should run lsp reference", function() win, items, width = require('navigator.reference').ref_view(nil, result, { method = 'textDocument/references', bufnr = 1, - client_id = 1 + client_id = 1, }, {}) - else - win, items, width = require('navigator.reference').reference_handler(nil, "textDocument/references", result, 1, 1) - + win, items, width = require('navigator.reference').reference_handler(nil, 'textDocument/references', result, 1, 1) end - print("win", vim.inspect(win)) - print("items", vim.inspect(items)) - eq(win.ctrl.data[1].display_filename, "./interface.go") + -- print('win', vim.inspect(win)) + print('items', vim.inspect(items)) + eq(win.ctrl.data[1].display_filename, './interface.go') eq(win.ctrl.data[2].range.start.line, 14) - eq(items[1].display_filename, "./interface.go") + eq(items[1].display_filename, './interface.go') -- eq(width, 60) end) - it("reference handler should return items with thread", function() - - local status = require("plenary.reload").reload_module("navigator") - local status = require("plenary.reload").reload_module("guihua") - vim.cmd([[packadd navigator.lua]]) - vim.cmd([[packadd guihua.lua]]) - local path = cur_dir .. "/tests/fixtures/interface.go" -- %:p:h ? %:p - print(path) - local cmd = " silent exe 'e " .. path .. "'" - vim.cmd(cmd) - vim.cmd([[cd %:p:h]]) - local bufn = vim.fn.bufnr("") - - vim.fn.setpos(".", {bufn, 15, 4, 0}) -- width - - vim.bo.filetype = "go" - require'navigator'.setup({ - debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log - icons = {code_action_icon = "A "}, - width = 0.75, -- max width ratio (number of cols for the floating window) / (window width) - height = 0.3, -- max list window height, 0.3 by default - preview_height = 0.35, -- max height of preview windows - debug_console_output = true, - border = 'none' - }) - - _NgConfigValues.debug_console_output = true - - vim.bo.filetype = "go" - -- allow gopls start - for i = 1, 10 do - vim.wait(400, function() - end) - local clients = vim.lsp.get_active_clients() - print("clients ", #clients) - if #clients > 0 then - break - end - end - - -- allow gopls start - vim.wait(200, function() - end) + it('reference handler should return items with thread', function() + vim.wait(200, function() end) local win, items, width @@ -195,18 +129,15 @@ describe("should run lsp reference", function() win, items, width = require('navigator.reference').ref_view(nil, result, { method = 'textDocument/references', bufnr = 1, - client_id = 1 - }, {truncate = 2}) - + client_id = 1, + }, { truncate = 2 }) else - win, items, width = require('navigator.reference').reference_handler(nil, "textDocument/references", result, 1, 1) - + win, items, width = require('navigator.reference').reference_handler(nil, 'textDocument/references', result, 1, 1) end - - print("win", vim.inspect(win)) - print("items", vim.inspect(items)) + -- print('win', vim.inspect(win)) + print('items', vim.inspect(items)) -- eq(win.ctrl.data, "./interface.go") - eq(win.ctrl.data[1].display_filename, "./interface.go") + eq(win.ctrl.data[1].display_filename, './interface.go') eq(win.ctrl.data[2].range.start.line, 14) -- eq(items[1].display_filename, "./interface.go") diff --git a/tests/treesitter_spec.lua b/tests/treesitter_spec.lua new file mode 100644 index 0000000..81e805e --- /dev/null +++ b/tests/treesitter_spec.lua @@ -0,0 +1,217 @@ +local golden_result = { + { + col = 9, + display_filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + full_text = 'package main', + kind = '๐Ÿš€', + lnum = 1, + node_scope = { + ['end'] = { + character = 0, + line = 12, + }, + start = { + character = 0, + line = 0, + }, + }, + node_text = 'main', + indent = '', + range = { + ['end'] = { + character = 12, + line = 0, + }, + start = { + character = 8, + line = 0, + }, + }, + text = ' ๐Ÿš€ main \t package main', + type = 'namespace', + uri = 'file:///tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + }, + { + col = 6, + display_filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + full_text = 'func interfaceTest()', + kind = '๏‚š ', + lnum = 5, + indent = '', + node_scope = { + ['end'] = { + character = 1, + line = 11, + }, + start = { + character = 0, + line = 4, + }, + }, + node_text = 'interfaceTest', + range = { + ['end'] = { + character = 18, + line = 4, + }, + start = { + character = 5, + line = 4, + }, + }, + text = ' ๏‚š interfaceTest\t func interfaceTest()', + type = 'function', + uri = 'file:///tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + }, + { + col = 2, + display_filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + full_text = 'r := rect{width: 3, height: 4}', + kind = '๎ž› ', + lnum = 6, + node_scope = { + ['end'] = { + character = 1, + line = 11, + }, + start = { + character = 21, + line = 4, + }, + }, + + indent = ' ', + node_text = 'r', + range = { + ['end'] = { + character = 2, + line = 5, + }, + start = { + character = 1, + line = 5, + }, + }, + text = ' ๎ž› ๎˜ก r \t r := rect{width: 3, height: 4}', + type = 'var', + uri = 'file:///tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + }, + { + col = 2, + display_filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + full_text = 'c := circle{radius: 5}', + kind = '๎ž› ', + lnum = 7, + node_scope = { + ['end'] = { + character = 1, + line = 11, + }, + start = { + character = 21, + line = 4, + }, + }, + node_text = 'c', + indent = ' ', + range = { + ['end'] = { + character = 2, + line = 6, + }, + start = { + character = 1, + line = 6, + }, + }, + text = ' ๎ž› ๎˜ก c \t c := circle{radius: 5}', + type = 'var', + uri = 'file:///tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + }, + { + col = 2, + display_filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + filename = '/tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + full_text = 'd := circle{radius: 10}', + kind = '๎ž› ', + lnum = 10, + indent = ' ', + node_scope = { + ['end'] = { + character = 1, + line = 11, + }, + start = { + character = 21, + line = 4, + }, + }, + node_text = 'd', + range = { + ['end'] = { + character = 2, + line = 9, + }, + start = { + character = 1, + line = 9, + }, + }, + text = ' ๎ž› ๎˜ก d \t d := circle{radius: 10}', + type = 'var', + uri = 'file:///tmp/github/ray-x/navigator.lua/tests/fixtures/interface_test.go', + }, +} + +print(golden_result[1].node_text) + +local busted = require('plenary/busted') + +local eq = assert.are.same +local cur_dir = vim.fn.expand('%:p:h') +-- local status = require("plenary.reload").reload_module("go.nvim") +-- status = require("plenary.reload").reload_module("nvim-treesitter") + +-- local ulog = require('go.utils').log +describe('should run lsp reference', function() + -- vim.fn.readfile('minimal.vim') + it('should show ts nodes', function() + local status = require('plenary.reload').reload_module('navigator') + local status = require('plenary.reload').reload_module('guihua') + local status = require('plenary.reload').reload_module('lspconfig') + + vim.cmd([[packadd nvim-lspconfig]]) + vim.cmd([[packadd navigator.lua]]) + vim.cmd([[packadd guihua.lua]]) + local path = cur_dir .. '/tests/fixtures/interface_test.go' -- %:p:h ? %:p + local cmd = " silent exe 'e " .. path .. "'" + vim.cmd(cmd) + vim.cmd([[cd %:p:h]]) + local bufn = vim.fn.bufnr('') + -- require'lspconfig'.gopls.setup {} + require('navigator').setup({ + debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log + }) + + -- allow gopls start + for i = 1, 10 do + vim.wait(400, function() end) + local clients = vim.lsp.get_active_clients() + print('lsp clients: ', #clients) + if #clients > 0 then + break + end + end + + vim.fn.setpos('.', { bufn, 15, 4, 0 }) -- width + + vim.bo.filetype = 'go' + local view, items, w = require('navigator.treesitter').buf_ts() + eq(items[1].node_text, golden_result[1].node_text) + eq(items[2].node_text, golden_result[2].node_text) + end) +end)