Compare commits

...

426 Commits

Author SHA1 Message Date
ray-x cd98ca2c5b update floating width 2 years ago
ray-x 451d761f76 features side panel for symbol reference 2 years ago
sp4ke 8278f71878
make sure merge luadev config from user (#229)
Co-authored-by: spike <spike@w530>
2 years ago
ray-x 443fd4d585 issue #228 codelens sign missing 2 years ago
ray-x 10fd76d6d0 default severity 2 years ago
ray-x 8d00282020 volar and diangnostic severity level check 2 years ago
ray-x 9afcd37854 issue #227 2 years ago
ray-x c70d032b08 mason setup 2 years ago
ray-x fb6d19ddf4 issue #226 add example for mason setup 2 years ago
ray-x af7fc7d649 some updates for mason 2 years ago
ray-x d8f19f6fc1 range_code_action deprecated in neovim 0.8 2 years ago
ray-x df7f4d7229 better format ts_context text 2 years ago
ray-x fb87a0b1b7 long line improvement 2 years ago
ray-x 927c464b70 optimize for go and ts_contex 2 years ago
ray-x e9a40aca40 diagnostic: show line diagnostic: offset the floating windows to first error pos 2 years ago
ray-x edee3e39c6 merge changes for treesitter symbole context 2 years ago
sp4ke a7886fc055
Fix keymap buffer (#223)
* apply key mapping to selected buffer only

* <help> filetype should not be handled by navigator

Co-authored-by: spike <spike@w530>
2 years ago
yyk de018d9ae3
Fix invalid buffer in defer fn. (#221) 2 years ago
ray-x ef0cee5bbd unit tests 2 years ago
ray-x ab2e383b20 unit tests 2 years ago
ray-x 1b262556c0 reuse telecsope color scheme 2 years ago
rayx b4ff1c43b9
Update codelens.lua 2 years ago
ray-x ebd88f5d09 re-order code action so null-ls is not the first to see 2 years ago
ray-x 28d245510f using neovim 0.7 API 2 years ago
ray-x 213b30950e the missing options for treesitter folding 2 years ago
ray-x f05d2ec142 lint, expand tab to space for treesitter folding 2 years ago
ray-x 9f151cf718 definition preview, show more contents and allow scroll 2 years ago
ray-x b131251ad8 send floating view result to quickfix; de-duplicate references 2 years ago
ray-x 8978bdb1e3 issue #219 missing end 2 years ago
ray-x ae251289b3 issue #218 range format operator 2 years ago
ray-x a9c223cc22 de-duplicate 2 years ago
ray-x 0ab4b02ff7 update on_attach 2 years ago
ray-x 6c0e286170 update readme for on_attach 2 years ago
ray-x f24a337f5d switch between buffer type 2 years ago
ray-x 3d87ed3b87 issue #217 README missing symbols 2 years ago
ray-x 5713f79265 clear autocmd #191 2 years ago
ray-x 01ef2ce1a1 typo highlight 2 years ago
ray-x 547f2bc15d doc highlight config options update 2 years ago
ray-x dc0b8504bb allow document_highlight to be configurable, update REWADME 2 years ago
ray-x 486085070d #191 cleanup 2 years ago
ray-x bc356d555f pipeline neovim 0.7.0 2 years ago
ray-x 5b5a946d73 add neovim 0.7.1 to pipeline 2 years ago
ray-x fca2fa5aaa some version of neovim does not support underline etc 2 years ago
rayx 49dbca889d
Update clients.lua 2 years ago
ray-x 861ae038dd underdouble is not supported by 0.7.x 2 years ago
ray-x 814b712e91 no longer nvim 0.6.x 2 years ago
ray-x dcc40278c5 use create_autocmd API; split clients.lua into 3 files 2 years ago
ray-x 6736293182 split sumneko 2 years ago
ray-x 10ef469286 issue #214 lsp_installer loading 2 years ago
ray-x 4775d43218 .gitignore 2 years ago
ray-x 89dd02c927 using neovim 0.7.0 api for autocmd 2 years ago
ray-x bbdf86a66e refactor highlight setup. it is for #216 and also using API from neovim 0.7 now 2 years ago
ray-x ed47d386e6 chore selene setup 2 years ago
ray-x 480824d3f3 lint does not support lua5.1 JIT ... 2 years ago
ray-x c3ef990390 lint 2 years ago
ray-x de5c88c766 lint pipeline 2 years ago
ray-x 348ab9dced mason support #215 2 years ago
ray-x 967fd32bae prevent autocmd being call in a loop 2 years ago
ray-x 4aef8dc5f2 dartls installer not working #212 2 years ago
ray-x 0de6c290cf issue #211 tsserver not working 2 years ago
ray-x 9aad2978dc resend filetype if lsp not ready 2 years ago
ray-x 49c3e5cd4d prevent compare number to bool. As the name for `html` LSP is `html` 2 years ago
ray-x 47545e03c0 default border hl to FloatBorder 2 years ago
ray-x e5d16e4488 renaming highight group from GHxxx to Guihuaxxx 2 years ago
ray-x 3d9a9582dd bugfix #206 diagnostic failure 2 years ago
rayx 05dfed9ed9
issue #171 multiple lsp server started (#205)
* issue #171 multiple lsp server started

* neovim 0.6.1
2 years ago
ray-x 05753da8db add deprecate message 2 years ago
ray-x 4623677b2a issue #204 update doc for the breaking changes 2 years ago
ray-x 85e3f6e045 Allow user pass the lua-dev setup into navigator 2 years ago
ray-x c30ccd07c9 add options for lsp.buf.format()
update keymap doc
2 years ago
ray-x 4c5d0e67d0 remove table nil handling 2 years ago
ray-x be6da35767 improve treesitter folding 2 years ago
ray-x f567f1b99c dedup definition for script languages 2 years ago
ray-x a93f7cb20f dedup reference results 2 years ago
ray-x 289f67013d indent fallback if treesitter not found 2 years ago
ray-x 56b00b83d5 diagnostic func failed to keymap 2 years ago
ray-x 3fe9a876c4 bugfix hover keymaps not mapped correctly 2 years ago
rayx acca6009e0
[Breaking] using vim.keymap.set, breaking changes (#200)
* using vim.keymap.set, breaking changes

* update keymaps

* doc updates
2 years ago
ray-x 792fd2831a fix github workflow 2 years ago
ray-x a225d18eaf new tests for hierarchy, refactor reference_spec test 2 years ago
ray-x 7d84a9f0c0 issue #201 vfn nil 2 years ago
ray-x 22e858f261 #195 update doc for rust-tool, make client/bufnr require fields for mapping.setup(opts) when calling from rust/clangd on_attach 2 years ago
ray-x edba3efd1e Calltree updates 2 years ago
ray-x 724d5f3439 add scope info in treesitter panel 2 years ago
ray-x 3dc8c02c39 add scope info in panel 2 years ago
ray-x 91d1366b65 update treesitter for 0.6.1 pipeline 2 years ago
ray-x d951a5fcd2 fix for test failure 2 years ago
ray-x 68eb18c310 offset for definition preview 2 years ago
ray-x 8d77c3ab1e bug fix for #166 not all items shown in listview. Also add flag allow control when the ts info will be added
update readme
2 years ago
ray-x 309afcd681 update pipeline for neovim 0.7.2 2 years ago
rayx 9f7bd6ebff
Feature/198 calltree (#199)
* refactor hierarchy.lua

* show side panel for hierarchy

* allow call hierarchy to fold and expand to show call tree

* update command maps
2 years ago
ray-x aba0d89745 remove traceback logs 2 years ago
ray-x c1b0694bef map incoming/outgoing calls handler 2 years ago
ray-x 9dee73010b optimize treesitter symbol tree 2 years ago
ray-x 1b3a02df3e symbol indent level for embedded struct 2 years ago
ray-x f856fa7033 issue #195 update doc for rust-tools and clang-extensions 2 years ago
rayx 1f6103ed95
Side panel (#197)
* add sidepanel

* revert some changes and fix nil indent level

* Add side panel; bugfix for ctags
2 years ago
ray-x 7bfd9157fe update TS fold 2 years ago
ray-x 48e35f4e56 hold ctags windows untils ctags cmd finished 2 years ago
ray-x 27442d2784 simpily codeaction and range_code_action keymapping and code 2 years ago
ray-x 3ad93531b5 unit tests for treesitter 2 years ago
ray-x c15bae89ab doc updates for rust-tools #195 2 years ago
ray-x 61a82559d6 add treesitter tests 2 years ago
ray-x f8985d7aa2 allow skip lsp clients setup 2 years ago
ray-x 516d643ffe lsp installer update issue #181 2 years ago
ray-x 6b4cfa3d59 symbol layout update 2 years ago
ray-x cce0e90544 gui listview updates, allow listview cover more spaces 2 years ago
rayx 2f35446fbe
issue #192 pylsp range missing (#193)
* issue #192 pylsp range missing
2 years ago
ray-x 1908ea5175 improve line rendering when trim long text 2 years ago
ray-x 6c3ee44729 bugfix workspace symbol from multiple lsp clients 2 years ago
ray-x 6e937e9019 workspace symbole search improvements. allows to show interactive search results in the symbol list. 2 years ago
ray-x 01801ba8fa change ts not load notify level 2 years ago
ray-x d0ab595b93 code action flicker 2 years ago
ray-x 3f49769abc lint all codes 2 years ago
ray-x 32ddd66dd2 fix for issue#191 2 years ago
ray-x feb780fb10 reloaded client when failed 2 years ago
ray-x ea2c207ec9 updates for client loading 2 years ago
ray-x b249d1680a handle same lsp started multiple times (esp for those take long time to start ) issue #171 2 years ago
ray-x 13c3dd4072 disable logs when debug is not on 2 years ago
ray-x a0eff2c5a3 Merge branch 'serverCapcities' 2 years ago
ray-x 51a05252a5 reformat reference_spec 2 years ago
Michael Adler 2e96dcd327
fix(sumneko): fix incorrect usage of runtime.path (#189)
This is quite tricky to get right, because there are two ways for
sumneko to search for files, namely:

1. Lua.runtime.path

When using `require`, how to find the file based on the input name.
Setting this config to `?/init.lua` means that when you enter `require
'myfile'`, `${workspace}/myfile/init.lua` will be searched from the
loaded files.  if `runtime.pathStrict` is `false`,
`${workspace}/**/myfile/init.lua` will also be searched.  If you want to
load files outside the workspace, you need to set
`Lua.workspace.library` first.

2. Lua.workspace.library

In addition to the current workspace, which directories will load files
from. The files in these directories will be treated as externally
provided code libraries, and some features (such as renaming fields)
will not modify these files.

The crucial point is that `Lua.runtime.path` only applies to
the *current* workspace. Thus it makes no sense to add any absolute
directories here. Absolute directories must be added to
workspace.library, which is already the case. The default value provided
by sumneko is what you typically would expect, so I have switched to it.

References:

- 076dd3e5c4/locale/en-us/setting.lua (L5-L13)
- e62d964ff5/script/config/config.lua (L151)
2 years ago
Michael Adler ff4bcc6d83
doc(packer): depend on nvim-lspconfig (#188) 2 years ago
ray-x 8a32139e1a lsp installer path issue #187 2 years ago
rayx a73fb38ef9
update resolved_capacities -> server_capacities (#185)
* update resolved_capacities

* format renaming

* remove comments

* remove mk_handler. update documentFormator

* bumpup test image to ubuntu 22.04

* add logs when neovim is lower than 0.8
2 years ago
ray-x 96ca715ea6 add logs when neovim is lower than 0.8 2 years ago
ray-x 45385ccc9a bumpup test image to ubuntu 22.04 2 years ago
ray-x 4d93172915 remove mk_handler. update documentFormator 2 years ago
ray-x 0ac13663cf remove comments 2 years ago
ray-x 500553ae6b format renaming 2 years ago
rayx c416d99f6f
Merge branch 'master' into serverCapcities 2 years ago
ray-x d1836f4299 update resolved_capacities 2 years ago
rayx 5131b30ad7
clangd on_attach 2 years ago
ray-x eb75b09a33 issue #183 clangd missing bufnr 2 years ago
ray-x 93e28f36d0 issue #182 vim nottify in on_attach did not setup log level 2 years ago
ray-x 77b572dd5a terraform_lsp 2 years ago
rayx 794e86057d
Add funding 2 years ago
ray-x 03d0aaa05d add tflint 2 years ago
ray-x 9c67158d9c terraform filetype 2 years ago
ray-x 0c31d692ee https://github.com/ray-x/navigator.lua/pull/179 and issue #177 lsp codelens enable 2 years ago
ray-x 9ceeb41b6f issue #175 format on save 2 years ago
ray-x 0bdaf1f63c issue #175 ftdetect fix 2 years ago
ray-x c2c1c57136 terraform tf/tfvars, diagnostic show&hide, diagnostic default override 2 years ago
0x7a7a 595263e8b4
fix diagnostic cfg path error (#173)
* fix diagnostic cfg path error

* add defult lsp.diagnostic setting

* Update navigator.lua

Simplify logic

Co-authored-by: rayx <rayx.cn@gmail.com>
2 years ago
ray-x 2f3e2847a3 add neovim 0.7.0 in test pipeline 2 years ago
ray-x cd53b02f93 use native get_node_text 2 years ago
ray-x 15e6b315b7 add logs 2 years ago
ray-x 31f2f920e1 lsp encoding default value 2 years ago
ray-x ab4daceede updates for go 1.18 2 years ago
ray-x 23e25c298e issue #164 disable declaration only in reference 2 years ago
ray-x 8b43ed23e2 change the way checking total number of clients attached #167 2 years ago
ray-x 8dbac5efc9 issue #168 ccls highlight 2 years ago
ray-x d08d78f6a4 skip the reorder for codeaction. Null-ls is default to be in bottom of the list 2 years ago
ray-x 3d217bffce prevent recursion loop 2 years ago
ray-x 2ed33d7d60 stylua 2 years ago
ray-x e55ae08e9d bugfix #163 defination not found 2 years ago
ray-x d990da84a6 bugfix #161 callhierachy ccls assertion 2 years ago
ray-x bb018d541b issue #161 ccls failure 2 years ago
ray-x b69154f9ee nil handling 2 years ago
ray-x 5c8b3b9ca1 bugfix #157 failed to override clangd setup 2 years ago
ray-x 7f22411b1f issue #156 Can't disable virutal text 2 years ago
ray-x 77dd031f8a remove logs 2 years ago
ray-x d8e4787bfb lsp installer sample update 2 years ago
ray-x ab96133b47 support config from a lazy function 2 years ago
ray-x 34c3bc9c76 Merge branch 'master' into neovim_0.7 2 years ago
ray-x 46aeb778ce update pipeline 2 years ago
studierer 035917c57a
add more lsp (#152) 2 years ago
ray-x a316e7f449 Merge branch 'neovim_0.7' 2 years ago
ray-x 209e5321a8 add lsp_installer sample 2 years ago
ray-x 4018cab16d update README 2 years ago
ray-x 3e03e37d9f update terraform setup 2 years ago
ray-x 5773f66d14 terraform updates 2 years ago
ray-x 74eccbd799 Merge branch 'master' into neovim_0.7 2 years ago
ray-x 0346fc3c0f hash key update 2 years ago
ray-x b2d846647b Merge branch 'master' into neovim_0.7 2 years ago
ray-x cfe8738ad5 disable filetype 'gitcommit' 2 years ago
ray-x 982fe81922 Merge branch 'master' into neovim_0.7 2 years ago
ray-x d024335c91 update client loading 2 years ago
ray-x 3d6187e534 Merge branch 'master' into neovim_0.7 2 years ago
ray-x 41593cb7e4 remove json from auto highlight autocmd 2 years ago
ray-x 429cd16292 remove json from highlight autocmd 2 years ago
ray-x 2c7334e1b6 Merge branch 'master' into neovim_0.7 2 years ago
ray-x 3adbbaf509 remove terraform from auto highlight 2 years ago
ray-x 37e8b68506 doc updates 2 years ago
ray-x ff8ae83300 reverse sort lsp actions 2 years ago
Joseph DelCioppio b10964e5a2
Add support for ELixir LS (#149) 2 years ago
ray-x e4b2fc0afe merge master 2 years ago
ray-x 8e39f65f53 merge master 2 years ago
ray-x 2e43b8ee0a merge master 2 years ago
ray-x 2232317b0a combine on_attach 2 years ago
ray-x ff57a1f916 force keymap to be loaded when on_attach is not called it may releated to #130 2 years ago
ray-x 0f181f1d4e upadate sandbox README 2 years ago
ray-x 9ed6e64877 playground update 2 years ago
ray-x f0c5610a1f merge master 2 years ago
ray-x 92f56ffe4b update README.md 2 years ago
ray-x 76e0c8d3ff update README 2 years ago
ray-x c7872c83a8 merge master 2 years ago
ray-x bf2e867354 lint 2 years ago
ray-x 11d25933ad move capabilities checn around 2 years ago
ray-x 21ddd7f2f6 Merge branch 'master' into neovim_0.7 2 years ago
ray-x 8e9bf39f13 allow config lsp with function 2 years ago
ray-x ccb1bac250 lazy load with func for client config 2 years ago
ray-x 1feacfb4bd bumpup default neovim version 2 years ago
ray-x ded2a6510f bump up version requirement in readme 2 years ago
ray-x 62477b294e drop neovim 0.5 deprecated API 2 years ago
ray-x c03cbca758 update keymaps for gd 2 years ago
ray-x 2d5290d3d6 gd keymaps 2 years ago
David de Rosier dcabc38a12
Fixes problem with disabling icons not working (#142) (#143) 2 years ago
ray-x c9f34ac179 check doc highlight 2 years ago
ray-x 2344a9d611 override cfg.cmd setting only when installer is used and no valid cmd is provided 2 years ago
ray-x 1f3ae4a675 update readme of log path, remove lspinstaller logs 2 years ago
ray-x 45e0698d00 issue #137 lspinstaller API changed 2 years ago
ray-x 5b2e003258 disable trace 2 years ago
ray-x 72ed02f879 locations_to_items offset_encoding 2 years ago
ray-x 92296c9fc8 dochl client offset encoding 2 years ago
ray-x 96885ae509 add log message when bufnr is not provided 2 years ago
ray-x 4ca6b376a7 breaking changes: https://github.com/neovim/neovim/issues/14090, issue #136 2 years ago
ray-x da9448a88c neovim breaking changes: offset_encoding 2 years ago
ray-x 3bc154bbb3 issue #135 bugfix 2 years ago
ray-x 7273aaa1d9 Update README.md in playground 2 years ago
ray-x e83a017e61 update minium vimrc of playground 2 years ago
ray-x e80f004f5b issue #133 change gT map to <Leader>gt 2 years ago
ray-x ed1cedefb6 update navigator vimrc 2 years ago
ray-x e04914ab6c doc update for lsp_installer 2 years ago
ray-x 73dfd12f0f async handler check status before close 2 years ago
ray-x db4eb5f155 document update 2 years ago
ray-x bad19ebc84 lsp_signature setup was not documented in README. Add playground folder 2 years ago
ray-x 4144024068 allow to config when will the fuzzy finder prompt to be shown 2 years ago
ray-x f9af69e4bf update treesitter folding screenshot 2 years ago
ray-x 2ad5d1e1d0 update treesitter folding screenshot 2 years ago
ray-x a0679626e2 cleanup code 2 years ago
ray-x 1202df85cd update notify message when symbol not found #126 2 years ago
ray-x b89811b1b6 bugfix issue #126 2 years ago
ray-x c3f6b14233 #124 viewctlobject error 2 years ago
ray-x 23090c567c updates vim.notify 2 years ago
ray-x 9957766620 Merge branch 'master' of github.com:ray-x/navigator.lua 2 years ago
ray-x aa9b80e848 empty func 2 years ago
Ian C 6e9f20f586
fix typo (#122) 2 years ago
Ian C fcf7814c9a
fix typos (#121)
* fix typos

* fix typos
2 years ago
Ian C f3bf6ab4f6
fix typo (#120) 2 years ago
ray-x ecc1f2d28a update README.md 2 years ago
ray-x 4739722774 update README.md 2 years ago
ray-x 19969e3902 keymap bugfix #119 2 years ago
ray-x 47bcf183b8 nil check 2 years ago
ray-x 97df091a8a Merge branch 'master' of github.com:ray-x/navigator.lua 2 years ago
ray-x abf5de64fd update gui for workspace symbol 2 years ago
Beau McCartney b618358628
Update README.md (#118)
* Update README.md

Display the default nmap Gr, the async_ref function.

* Update README.md

Co-authored-by: rayx <rayx.cn@gmail.com>
2 years ago
ray-x 873135bf9a lsp def and lsp ref with async request 2 years ago
ray-x 0f653784ea Doc update 2 years ago
ray-x c528b58bb9 replace print to notify, add workspace gui 2 years ago
ray-x 85d907ffaf add lspkeymaphelp command to show the current keymapping 2 years ago
ray-x 5ab4dffba5 add gohtmltmpl in default setup 2 years ago
ray-x e869cedba3 cleanup 2 years ago
rayx 941b3fc80f
vim.ui.select adapt native codeaction and codelens apply actions (#117)
* doc update for lsp-installer

* use vim.ui.select and guihua ui patch

* adjust null-ls sequence
2 years ago
ray-x 4b1670423e issue #116 nil client 2 years ago
ray-x 5c13477220 dochighlight update 3 years ago
ray-x 1c67e790eb remove duplicated codes 3 years ago
ray-x 3c9ee3785c omnisharp default setup 3 years ago
ray-x 65701259c1 simplify gui; dochighlight 3 years ago
ray-x 03eef089c8 add doc/navigator.txt 3 years ago
ray-x 73f0041dc1 bugfix #115 incorrect bufnr 3 years ago
ray-x ce3c5fe2cb fix the referece floatwindow loading performance issue 3 years ago
ray-x ae13350ad1 add neovim 0.6.0 3 years ago
ray-x 2f44f0115a add servers options for #109 3 years ago
ray-x 9aec552ac1 Add warning messages if the user setup maybe deprecated or incorrect 3 years ago
Loreo13 3d6b24f919
Add root markers for clojure_lsp (#108)
* Add support for clojure_lsp

* Show clojure_lsp is supported in README.MD

* Add root_patterns for clojure_lsp
3 years ago
Loreo13 c85fcb202f
Add support for clojure_lsp (#107)
* Add support for clojure_lsp

* Show clojure_lsp is supported in README.MD
3 years ago
ray-x 315c284b08 Merge branch 'master' of github.com:ray-x/navigator.lua 3 years ago
ray-x f06b6b5a1a bugfix for test workflow 3 years ago
Mateusz Kubaszek 22da11569b
doc(refactor): Fix code in example reference to disable lsp or override settings (#106) 3 years ago
ray-x cdee0ec57b bugfix #105 3 years ago
ray-x 83b66dbea1 Add error check for config, Allow show additional info for diagnostic 3 years ago
ray-x 49fb9032d8 bugfix clangd #102 buffer not loaded empty diagnostic info 3 years ago
ray-x 388711d97d issue #101 disable diagnostic_scrollbar 3 years ago
ray-x 19df02d40d update README 3 years ago
ray-x 0b2e66fa98 disable packer filetype as default 3 years ago
ray-x 5423a6ff36 update readme 3 years ago
ray-x 99b7b1c502 issue #99 add texlab 3 years ago
ray-x db09db7ef3 neovim 0.6 update for highlight 3 years ago
ray-x 93b38136f8 README update 3 years ago
ray-x 342a429485 update disable filetype and README 3 years ago
ray-x 04cfdbd4d2 rls symbol 3 years ago
ray-x 04d8e71008 add setup option 3 years ago
ray-x a6f668e05f preselect 1st item in code action. lsp_signature hook 3 years ago
ray-x b196af9311 Merge branch 'codeaction_client' 3 years ago
ray-x f43b0aba0e add diagnostic toggle, adjust width of preview window 3 years ago
ray-x 63bbdf4cf2 update README.md issue#93 3 years ago
ray-x 5a84868481 using client number in code action 3 years ago
ray-x 4c7e7f683d update README.md 3 years ago
ray-x 427007ee9b bugfix issue #97 3 years ago
ray-x 262181246d update symbol preview with treesitter scope 3 years ago
TANIGUCHI Masaya d9fb882e98
Add markdown (#96) 3 years ago
ray-x 9b148290a4 update multigrid document 3 years ago
ray-x 552dc8901f update multigrid screenshot 3 years ago
ray-x 622804e674 client load table on filetype detected 3 years ago
ray-x c9514dffe9 support for lsp-installer (deprecate lsp-installer) issue #90 3 years ago
ray-x c890f3818d Merge branch 'nvim-lsp-installer' 3 years ago
ray-x 74ea1ddd73 document update 3 years ago
ray-x 062e7e4ffc document save & fmt aucmd 3 years ago
ray-x fab08151e6 refactor diagnostic https://github.com/neovim/neovim/pull/16057 3 years ago
ray-x 0c0801c92d fix for issue #92 default_mapping=false 3 years ago
ray-x 5083b07972 setup with lsp installer 3 years ago
ray-x b936cbb48d updates for lsp-installer 3 years ago
ray-x 87f4c2b664 deprecate lspinstall in faviour of lsp_installer 3 years ago
ray-x 7514301d80 bugfix issue #91 return if no result returns 3 years ago
ray-x d6b81ba8ab bug fix #91 outgoing_calls failure for pyright 3 years ago
ray-x 36c01adc2d buffer lsp reference highlight #88 3 years ago
ray-x 0a5db66968 format per buffer setup 3 years ago
ray-x c4bfcabd64 merge lsp_request check lsp cap before sending lsp request to client 3 years ago
ray-x 36683e3646 remove logs and update golang version 3 years ago
ray-x 3be242ac6c workflow update 3 years ago
ray-x 202628e695 path update 3 years ago
ray-x 035013ada4 install classic 3 years ago
ray-x dc9e50d432 remove update 3 years ago
ray-x 7925fd8439 install with snap 3 years ago
ray-x b98cfa2c40 snap install 3 years ago
ray-x f53d06b5fd workflow instruction 3 years ago
ray-x 1d2e6b5296 github workflows 3 years ago
ray-x ed834ecf0d diagnostic fix for #86 3 years ago
ray-x 1b3ba27403 bugfix start warning code action 3 years ago
ray-x b521c2e7bb update code_action/code_lens_action config. Move to lsp section 3 years ago
ray-x d932978441 update code lens action icon 3 years ago
ray-x 89b08bc968 svelte, codeaction remove empty, luadev #72 3 years ago
ray-x a952d694e1 lazyload for lua-dev 3 years ago
ray-x 1f3680d4b1 bugfix for codeAction newText 3 years ago
ray-x faab21a1a7 issue #82 3 years ago
ray-x c08cbdda89 lazy load for lua-dev 3 years ago
rayx 93c7eae2ca
Code action v2 (#81)
* bugfix for #71

* Better lspinstall support

* incoming_calls and outgoing_calls cause errors when results from LSP server have multiple lines #78

* remove logs

* update README.md

* defer format update

* lazyload lua-dev #72

* timer of filetype detect to 500

* document update

* update codeaction

* action command

* update code action details

* add svelteserver
3 years ago
ray-x 74af2c1a72 add svelteserver 3 years ago
ray-x 892804d51e update code action details 3 years ago
ray-x 00a015e27a action command 3 years ago
ray-x c55729e879 update codeaction 3 years ago
ray-x aa9133af7e document update 3 years ago
ray-x 687f78db5b timer of filetype detect to 500 3 years ago
ray-x 1ba70b81d5 lazyload lua-dev #72 3 years ago
ray-x 4e69764a52 defer format update 3 years ago
ray-x 9460b82d05 Merge branch 'master' into multi_loading 3 years ago
ray-x c035c225ea update README.md 3 years ago
ray-x d793df9b42 remove logs 3 years ago
ray-x 2afd51ae11 incoming_calls and outgoing_calls cause errors when results from LSP server have multiple lines #78 3 years ago
ray-x 0f2868c5e6 Better lspinstall support 3 years ago
spindensity 633c7da38f
Fix #73: Calling defintion_preview() on blank lines causes error (#74)
Co-authored-by: Jinsong Zhang <spindensity@gmail.com>
3 years ago
spindensity 5391bf6aec
Fix #75: The error handling code of implementation_handler() does not work (#76)
Co-authored-by: Jinsong Zhang <spindensity@gmail.com>
3 years ago
ray-x 3c0646074a bugfix for #71 3 years ago
ray-x a2ddb1ff4b logs cleanup. enable formatting hook only when cap formatting 3 years ago
ray-x 5e500bef98 document update 3 years ago
ray-x ecb25de260 rename disable_format_ft to disable_format_cap 3 years ago
ray-x 97b265a64d Update diagnostic virtual text marker 3 years ago
ray-x e56eeb4f88 issue #67 disable_lsp = 'all' causes errors 3 years ago
ray-x 386661a6d9 document update for GHListHl #66 3 years ago
ray-x 295b5e89ec cleanup logs, enable pyright format with black 3 years ago
ray-x 69578d6a3c Delete deprecated hover.lua, update floating win width, refresh diagnostic errors 3 years ago
rayx 79fee5dda8
Vim diagnostic refactor (#63)
* multigrid support

* Tuning diagnostic performance, add codelens inline hint function

* add ctx to error marker handler

* setup lsp_signature from navigator

* diagnostic refact PR https://github.com/neovim/neovim/pull/15585

* diagnostic api changes

* allow disable emoji/nerdfont icons setup

* improve diagnostic/codeaction/codelens preview popup; add seperate line

* severity_sort set to reverse

* prettier for markdown. code action virtual text show title
3 years ago
ray-x 90039247b4 jdtls, enable signature by default 3 years ago
rayx 91e22f5e71
Lv thread (#60)
* update tests, update total display

* not run ts def multiple time

* Thread enable for display and backend

* remove ::continue::

* README updates

* skip diagnostic in edit mode

* error marker uri nil handling

* disable debug

* debounce text change to 1s

* severity sort

* diagnostic skip loading files
3 years ago
ray-x 507ad0b146 error marker uri error 3 years ago
ray-x 26012cf9c1 Merge vim.loop changes (replacing vim.fn.getcwd) 3 years ago
ray-x 0dc5851d1c Update coedelens in README.md 3 years ago
ray-x 2cb2b23c6d Add split options when open preview file 3 years ago
ray-x 890b3c5f7f keymapping #57 3 years ago
ray-x 2a174d8eb4 typo, cleanup 3 years ago
ray-x 22028ff82e #57 refactor keymapping on lsp attach 3 years ago
ray-x d60b3c4024 keymapping for signature. Align to lspconfig default 3 years ago
rayx 08dba1beb6
Nvim 0 6 lsp signature changes (#56)
* bugfix diagnostic error

* bugfix for code lens for neovim 0.6

* gh test workflow

* bugfix github workflow
3 years ago
ray-x f33a3e8e7d allow preview more lines 3 years ago
ray-x b9f8ed3c55 show error messages 3 years ago
ray-x 56479a0339 add gitignore, remove logs and print statement 3 years ago
ray-x 2d1aa63b93 Merge branch 'master' of github.com:ray-x/navigator.lua 3 years ago
ray-x cde5c6be5c strange code action casued cursor drifting. disable codeaction in insert mode 3 years ago
Matt Bailey d771df0fa7
Name change lspinstall to nvim-lspinstall in lazy loader (#54)
On a clean install, packer compiles down to:

```lua
  ["nvim-lspinstall"] = {
    loaded = true,
    path = "..."
  },
```

rather than 

```lua
  ["nvim-lspinstall"] = {
    loaded = true,
    path = "..."
  },
```

without this change, I get a (cosmetic) error on every startup like this:

```
Error: attempted to load lspinstall which is not present in plugins table!                                                                                                                                                                                                                  
Error in packer_compiled: ...m/site/pack/packer/start/packer.nvim/lua/packer/load.lua:13: Error: attempted to load lspinstall which is not present in plugins table!                                                                                                                        
Please check your config for correctness
```
```
3 years ago
ray-x e36809759b clean up logs 3 years ago
ray-x e37b52ffc1 allow using number 1~9 to quick jump to item in the list 3 years ago
ray-x be888b94a1 #53 <BS> remap for rename 3 years ago
ray-x 5b65d8f389 Update document regarding code lens 3 years ago
ray-x 940349759b Feature: LRU cache for search result 3 years ago
ray-x d39cf20903 disable all lsp #49 by setting disable_lsp='all' 3 years ago
ray-x dd4b04159d lsp nerdfont for cmp 3 years ago
ray-x 01f97aa665 capabilities default. based on cmp nvim-lsp suggestions 3 years ago
Behzad 5c7925a54a
Fix for ]d being mapped to diagnostic.goto_next. It has been changed to diagnostic.goto_prev that should be the behaviour according to the readme. (#47) 3 years ago
rayx 0038f13c62
Workflow (#46)
* add github action and unit tests
3 years ago
ray-x 3f41ff3cd4 check gopls readiness 3 years ago
ray-x a3dd6ebcc4 spec test, allow gopls start 3 years ago
ray-x ce449c440b add lspconfig in tests 3 years ago
ray-x 1373bc3856 update ci and install gopls 3 years ago
ray-x 72e0b4e1e4 github workflow and reference spec test 3 years ago
ray-x b292c2b981 Merge branch 'master' of github.com:ray-x/navigator.lua 3 years ago
ray-x f5a1fc747b add diagnostic_virtual_text, #42 3 years ago
Beau acc0a727fe
Patch 1 (#44)
* Fix typos
3 years ago
ray-x aef51cc696 allow config codelens icon 3 years ago
ray-x 78e574cbcd codelens setup for gopls 3 years ago
ray-x 369233a758 change print info to log. codelens is not supported by lots of lsp 3 years ago
ray-x a200c1db2b Update UI for code lens 3 years ago
ray-x 58ac955777 codelens support, foldelsp 3 years ago
ray-x 48faeb69ab add autoload file 3 years ago
ray-x 51d32b26b9 Feature: Treesitter folding 3 years ago
ray-x d29ce05d16 New treesitter util helper to find node type in current line 3 years ago
ray-x 04cf51f6c8 feature: blur effect
feature: transparancy setup
3 years ago
ray-x 4f262e9909 deprecate pyls 3 years ago
ray-x 3c4838164b Update README.md 3 years ago
ray-x a1bd077939 bugfix #41 extra <cmd> in keymapping 3 years ago
ray-x d04159f633 bugfix #40 handle nil lsp setup 3 years ago
ray-x a85c3e89ae do not load lsp if server executable is not valid 3 years ago
ray-x 0584a5cd06 feature: diagnostic sign in scroll bar area enable by default 3 years ago
ray-x 7dd5d50691 update default treebindings #39 3 years ago
ray-x 4ff9aa47a3 diagnosticDelay and watchedFileDelay update for gopls (0.7.1) 3 years ago
ray-x ef995eeb7e border setup for diagnostic 3 years ago
ray-x f16a04482d add config for showing error in quickfix 3 years ago
ray-x 54906cebaf Diagnostic error per buffer 3 years ago
ray-x 96e9772d5b bugfix: virtual marker out of range for diag 3 years ago
ray-x 747db16f59 clean namespace, update doc 3 years ago
ray-x 2c11296b70 Feature: show diagnostics info summary in scrollbar area 3 years ago
ray-x d034edbe0e add lspinstall 3 years ago
ray-x 6c753645c3 close the quickfix if there is no diagnostic errors 3 years ago
ray-x 3d170771e9 update diangostic event handler 3 years ago
ray-x 1fa56d20a5 set_loclist update 3 years ago
ray-x 5b2dcb7cd0 Rename ToggleLspFmt to LspToggleFmt 3 years ago
ray-x 1ef00fe742 Add ToggleLspFmt to disable lsp format, bugfix for signature 3 years ago
ray-x fec4640966 bugfix: codeaction broken because guihua update 3 years ago
ray-x 5af79c7ff2 bugfix: #35 update fzy on_confirm, it might cause #35 3 years ago
ray-x 86f3963e45 update screenshort URL 3 years ago
ray-x fe936ee69a Merge branch 'master' of github.com:ray-x/navigator.lua 3 years ago
ray-x 3f6de1f686 document update, golsp setup 3 years ago
zootedb0t c4cf352798
Fix typo in README (#32) 3 years ago
ray-x 62178024fd add efm load option 3 years ago
ray-x 1df533bf5f [bugfix] lua sumneko go definition multi definitions uri nil 3 years ago
ray-x dcc616765e [Bugfix] Update default config regarding keymaps 3 years ago

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: ray-x

@ -0,0 +1,58 @@
name: Tests
on: [push, pull_request]
jobs:
unit_tests:
name: unit tests
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
url: https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz
manager: sudo snap
packages: go
- os: ubuntu-22.04
url: https://github.com/neovim/neovim/releases/download/v0.7.0/nvim-linux64.tar.gz
manager: sudo snap
packages: go
- os: ubuntu-22.04
url: https://github.com/neovim/neovim/releases/download/v0.7.2/nvim-linux64.tar.gz
manager: sudo snap
packages: go
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
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
with:
path: _neovim
key: ${{ runner.os }}-${{ matrix.url }}-${{ hashFiles('todays-date') }}
- name: Prepare
run: |
${{ matrix.manager }} install --classic ${{ matrix.packages }}
# sudo apt install go
test -d _neovim || {
mkdir -p _neovim
curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
}
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
git clone --depth 1 https://github.com/ray-x/guihua.lua ~/.local/share/nvim/site/pack/vendor/start/guihua.lua
git clone --depth 1 https://github.com/nvim-treesitter/nvim-treesitter ~/.local/share/nvim/site/pack/vendor/start/nvim-treesitter
git clone --depth 1 https://github.com/kyazdani42/nvim-web-devicons ~/.local/share/nvim/site/pack/vendor/start/nvim-web-devicons
git clone --depth 1 https://github.com/neovim/nvim-lspconfig ~/.local/share/nvim/site/pack/vendor/start/nvim-lspconfig
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
- name: Run tests
run: |
export PATH="${PWD}/_neovim/bin:${PATH}"
nvim --headless -u tests/minimal.vim -c "TSInstallSync go" -c "q"
make test

3
.gitignore vendored

@ -0,0 +1,3 @@
selene.toml
lua/navigator.lua.bak
tests/fixtures/tests

@ -0,0 +1,2 @@
test:
nvim --headless --noplugin -u tests/minimal.vim -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal.vim'}"

@ -1,39 +1,52 @@
# 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
#### Example: Javascript closure
The following screenshot shows javascript call tree 🌲 of variable `browser` insides a closure. This feature is similar to incoming & outgoing calls from LSP. It is designed for the symbol analysis.
![js_closure_call_tree](https://user-images.githubusercontent.com/1681295/119120589-cee23700-ba6f-11eb-95c5-b9ac8d445c31.jpg)
![navigator](https://user-images.githubusercontent.com/1681295/126022829-291a7a2e-4d24-4fde-8293-5ae61562e67d.jpg)
Explanation:
- The first line of floating windows shows there are 3 references for the symbol <span style="color:red"> *browser* </span> in closure.js
- The first reference of browser is an assignment, an emoji 📝 indicates the value is changed in this line. In many
cases, we search for references to find out when the value changed.
- The first line of floating windows shows there are 3 references for the symbol <span style="color:red"> _browser_ </span> in closure.js
- The first reference of browser is an assignment, an emoji 📝 indicates the value is changed in this line. In many
cases, we search for references to find out when the value changed.
- The second reference of `browser` is inside function `displayName` and `displayName` sit inside `makeFunc`, So you
will see ` displayName{} <- makeFunc{}`
will see ` displayName{} <- makeFunc{}`
- The third similar to the second, as var browser is on the right side of '=', the value not changed in this line
and emoji is not shown.
and emoji is not shown.
#### Example: C++ defination
#### Example: C++ definition
Another example for C++
C++ example: search reference and definition
![cpp_ref](https://user-images.githubusercontent.com/1681295/119215215-8bd7a080-bb0f-11eb-82fc-8cdf1955e6e7.jpg)
You may find that a 🦕 dinosaur(d) on the line of `Rectangle rect;` which means there is a defination (d for def) of rect in this line
You may find a 🦕 dinosaur(d) on the line of `Rectangle rect,` which means there is a definition (d for def) of rect in this line.
`<- f main()` means the definition is inside function main().
#### Golang struct type
Struct type references in multiple Go ﳑ files
![go_reference](https://user-images.githubusercontent.com/1681295/119123823-54b3b180-ba73-11eb-8790-097601e10f6a.gif)
This feature can provide you info in which function/class/method the variable was referenced. It is handy for a large
project where class/function definition is too long to fit into the preview window. Also provides a birdview of where the
variable is
project where class/function definition is too long to fit into the preview window. Also provides a bird's eye view of where the
variable is:
- Referenced
- Modified
- Defined
@ -42,44 +55,66 @@ variable is
# Features:
- LSP easy setup. Support the most commonly used lsp clients setup. Dynamic lsp activation based on buffer type. This
also enables you to handle workspace with mixed types of codes (e.g. Go + javascript + yml). A better default setup is
included for LSP clients.
also enables you to handle workspace with mixed types of codes (e.g. Go + javascript + yml). A better default setup is
included for LSP clients.
- Out of box experience. 10 lines of minimum vimrc can turn your neovim into a full-featured LSP & Treesitter powered IDE
- Unorthodox UI with floating windows, navigator provides a visual way to manage and navigate through symbols, diagnostic errors, reference etc. It covers
all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface
implementation.
- UI with floating windows, navigator provides a visual way to manage and navigate through symbols, diagnostic errors, reference etc. It covers
all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface
implementation.
- Code Action GUI
- Luv async thread and tasks
- Edit your code in preview window
- Async request with lsp.buf_request for reference search
- Treesitter symbol search. It is handy for large files (Some of LSP e.g. sumneko_lua, there is a 100kb file size limitation?)
- Treesitter symbol search. It is handy for large files (Some of LSP e.g. sumneko_lua, there is a 100kb file size limitation?). Also as LSP trying to hide details behind, Treesitter allows you to access all AST semantics.
- FZY search with either native C (if gcc installed) or Lua-JIT
- FZY search with Lua-JIT
- LSP multiple symbols highlight/marker and hop between document references
- LSP multiple symbol highlight and jump between references
- Preview definination/references
- Better navigation for diagnostic errors, Navigate through all files/buffers that contain errors/warnings
- Grouping references/implementation/incoming/outgoing based on file names.
- Treesitter based variable/function context analysis. It is 10x times faster compared to purely rely on LSP. In most
of the case, it takes treesitter less than 4 ms to read and render all nodes for a file of 1,000 LOC.
of the case, it takes treesitter less than 4 ms to read and render all nodes for a file of 1,000 LOC.
- The first plugin, IMO, allows you to search in all treesitter symbols in the workspace.
- Nerdfont, emoji for LSP and Treesitter kind
- Nerdfont, emoji for LSP and treesitter kind
- Optimize display (remove trailing bracket/space), display the caller of reference, de-duplicate lsp results (e.g reference
in the same line). Using treesitter for file preview highlighter etc
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 or LSP_fold folding algorithm. (It behaves similar to vs-code); dedicated 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.
- LRU cache for treesitter nodes
- Lazy loader friendly
- 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 providing.
I'd like to go beyond what the system is offering.
# Similar projects / special mentions:
@ -92,7 +127,7 @@ I'd like to go beyond what the system is providing.
# 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)).
@ -103,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
@ -130,14 +169,14 @@ 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 <<EOF
require'navigator'.setup()
EOF
@ -173,16 +212,37 @@ EOF
```
## Work with nvim-cmp and nvim-autopairs
The buffer type of navigator floating windows is `guihua`
I would suggest disable `guihua` for autocomplete.
e.g.
```lua
require('nvim-autopairs').setup{
disable_filetype = { "TelescopePrompt" , "guihua", "guihua_rust", "clap_input" },
if vim.o.ft == 'clap_input' and vim.o.ft == 'guihua' and vim.o.ft == 'guihua_rust' then
require'cmp'.setup.buffer { completion = {enable = false} }
end
-- or with autocmd
vim.cmd("autocmd FileType guihua lua require('cmp').setup.buffer { enabled = false }")
vim.cmd("autocmd FileType guihua_rust lua require('cmp').setup.buffer { enabled = false }")
...
}
```
## All configure options
Nondefault configuration example:
```lua
require.'navigator'.setup({
debug = false, -- log output
code_action_icon = " ",
require'navigator'.setup({
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
@ -197,26 +257,68 @@ require.'navigator'.setup({
-- end,
-- The attach code will apply to all LSP clients
default_mapping = true, -- set to false if you will remap every key
keymaps = {{key = "gK", func = "declaration()"}}, -- a list of key maps
ts_fold = false, -- modified version of treesitter folding
default_mapping = true, -- set to false if you will remap every key or if you using old version of nvim-
keymaps = {{key = "gK", func = vim.lsp.declaration, desc = 'declaration'}}, -- a list of key maps
-- 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
code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
treesitter_analysis_max_num = 100, -- how many items to run treesitter analysis
treesitter_analysis_condense = true, -- condense form for 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
-- 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 = " ",
code_action_icon = "🏏", -- note: need terminal support, for those not support unicode, might crash
-- Diagnostics
diagnostic_head = '🐛',
diagnostic_head_severity_1 = "🈲",
-- refer to lua/navigator.lua for more icons setups
},
lsp_installer = false, -- set to true if you would like use the lsp installed by williamboman/nvim-lsp-installer
mason = false, -- set to true if you would like use the lsp installed by williamboman/mason
lsp = {
format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc)
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},
document_highlight = true, -- LSP reference highlight,
-- it might already supported by you setup, e.g. LunarVim
format_on_save = true, -- set to false to disable lsp code format on save (if you are using prettier/efm/formater etc)
format_options = {async=false}, -- async: disable by default, the option used in vim.lsp.buf.format({async={true|false}, name = 'xxx'})
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
-- If you using null-ls and want null-ls format your code
-- you should disable all other lsp and allow only null-ls.
disable_lsp = {'pylsd', 'sqlls'}, -- a list of lsp server disabled for your project, e.g. denols and tsserver you may
--want to enable one lsp server at a time
-- to disable all default config and use your own lsp setup set
-- disable_lsp = 'all' and you may need to hook mapping.setup() in your on_attach
-- Default {}
diagnostic = {
underline = true,
virtual_text = true, -- show virtual for diagnostic message
update_in_insert = false, -- update diagnostic message in insert mode
},
diagnostic_scrollbar_sign = {'▃', '▆', '█'}, -- experimental: diagnostic status in scroll bar area; set to false to disable the diagnostic sign,
-- for other style, set to {'╍', 'ﮆ'} or {'-', '='}
diagnostic_virtual_text = true, -- show virtual for diagnostic message
diagnostic_update_in_insert = false, -- update diagnostic message in insert mode
disply_diagnostic_qf = true, -- always show quickfix if there are diagnostic errors, set to false if you want to
ignore it
tsserver = {
filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetypes
},
ctags ={
cmd = 'ctags',
tagfile = 'tags',
options = '-R --exclude=.git --exclude=node_modules --exclude=test --exclude=vendor --excmd=number',
},
gopls = { -- gopls setting
on_attach = function(client, bufnr) -- on_attach for gopls
-- your special on attach here
@ -228,90 +330,186 @@ require.'navigator'.setup({
gopls = {gofumpt = false} -- disable gofumpt etc,
}
},
-- the lsp setup can be a function, .e.g
gopls = function()
local go = pcall(require, "go")
if go then
local cfg = require("go.lsp").config()
cfg.on_attach = function(client)
client.server_capabilities.documentFormattingProvider = false -- efm/null-ls
end
return cfg
end
end,
sumneko_lua = {
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, 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},
}
})
```
The plugin can work with multiple LSP, e.g sqls+gopls+efm. But there are cases you may need to disable some of the
servers. (Prevent loading multiple LSP for same source code.) e.g. I saw strange behaviours when I use pyls+pyright+pyls_ms
### LSP clients
Built clients:
```lua
local servers = {
"angularls", "gopls", "tsserver", "flow", "bashls", "dockerls", "julials", "pylsp", "pyright",
"jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls",
"yamlls", "clangd", "ccls", "sqls", "denols", "graphql", "dartls", "dotls",
"kotlin_language_server", "nimls", "intelephense", "vuels", "phpactor", "omnisharp",
"r_language_server", "rust_analyzer", "terraformls", "svelte", "texlab", "clojure_lsp", "elixirls",
"sourcekit", "fsautocomplete", "vls", "hls"
}
```
Navigator will try to load avalible lsp server/client based on filetype. The clients has none default on_attach.
incremental sync and debounce is enabled by navigator. And the lsp
snippet will be enabled. So you could use COQ and nvim-cmp snippet expand.
Other than above setup, additional none default setup are used for following lsp:
- gopls
- clangd
- rust_analyzer
- sqls
- sumneko_lua
- pyright
- ccls
Please check [client setup](https://github.com/ray-x/navigator.lua/blob/26012cf9c172aa788a2e53018d94b32c5c75af75/lua/navigator/lspclient/clients.lua#L98-L234)
The plugin can work with multiple LSP, e.g sqls+gopls+efm. But there are cases you may need to disable some of the
servers. (Prevent loading multiple LSP for same source code.) e.g. I saw strange behaviours when I use
pylsp+pyright+jedi
together. If you have multiple similar LSP installed and have trouble with the plugin, please enable only one at a time.
To disable a LSP server, set `filetypes` to {} e.g.
#### Add your own servers
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
Alternatively, update following option in setup(if you do not want a PR):
```lua
require'navigator'setup{lsp={servers={'cmake', 'lexls'}}}
```
Above option add cmake and lexls to the default server list
require.'navigator'.setup({
pyls={filetype={}}
### Disable a lsp client loading from navigator
Note: If you have multiple lsp installed for same language, please only enable one at a time by disable others with e.g. `disable_lsp={'denols', 'clangd'}`
To disable a specific LSP, set `filetypes` to {} e.g.
```lua
require'navigator'.setup({
lsp={
pylsd={filetype={}}
}
})
```
### Default keymaps
| mode | key | function |
|--- |--- |--- |
| n | gr | show reference and context |
| i | \<m-k\> | signature help |
| n | gs | signature help |
| n | gW | workspace symbol |
| n | gD | declaration |
| n | g0 | document symbol |
| n | \<C-]\> | go to defination (if multiple show listview) |
| n | gp | defination |
| n | \<C-LeftMouse\> | definition|
| n | g\<LeftMouse\> | implementation|
| n | gT | treesitter document symbol |
| n | GT | treesitter symbol for all open buffers |
| n | K | hover doc |
| n | \<Space\>ca | code action (when you see 💡 ) |
| v | \<Space\>cA | range code action (when you see 💡 ) |
| n | \<Space\>rn | rename with floating window|
| n | \<Leader\>re | rename (lsp default)|
| n | \<Leader\>gi | incoming calls|
| n | \<Leader\>go | outgoing calls|
| n | gi | implementation |
| n | \<Sapce\> D | type definition |
| n | gL | show line diagnostic |
| n | gG | show diagnostic for all buffers |
| n | ]d | next diagnostic|
| n | [d | previous diagnostic|
| n | ]r | next treesitter reference/usage|
| n | [r | previous treesitter reference/usage|
| n | \<Sapce\> wa | add workspace folder|
| n | \<Sapce\> wr | remove workspace folder|
| n | \<Sapce\> wl | print workspace folder|
| n | \<Leader\>k | toggle reference highlight |
| i/n | \<C-p\> | previous item in list|
| i/n | \<C-n\> | next item in list|
| i/n | \<Up\> | previous item in list|
| i/n | \<Down\> | next item in list|
| i/n | \<C-o\> | open preview file in nvim/Apply action|
| n | \<Enter\> | open preview file in nvim/Apply action|
| i/n | \<C-b\> | previous page in listview|
| i/n | \<C-f\> | next page in listview|
| i/n | \<C-s\> | save the modification to preview window to file|
Or:
```lua
require'navigator'.setup({
lsp={
disable_lsp = {'pylsd', 'sqlls'},
}
})
```
### 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
| mode | key | function |
| ---- | --------------- | ---------------------------------------------------------- |
| n | gr | async references, definitions and context |
| n | \<Leader>gr | show reference and context |
| i | \<m-k\> | signature help |
| n | \<c-k\> | signature help |
| n | gW | workspace symbol |
| n | gD | declaration |
| n | gd | definition |
| n | g0 | document symbol |
| n | \<C-]\> | go to definition (if multiple show listview) |
| n | gp | definition preview (show Preview) |
| n | \<C-LeftMouse\> | definition |
| n | g\<LeftMouse\> | implementation |
| n | \<Leader>gt | treesitter document symbol |
| n | \<Leader\>gT | treesitter symbol for all open buffers |
| n | \<Leader\> ct | ctags symbol search |
| n | \<Leader\> cg | ctags symbol generate |
| n | K | hover doc |
| n | \<Space\>ca | code action (when you see 🏏 ) |
| n | \<Space\>la | code lens action (when you see a codelens indicator) |
| v | \<Space\>ca | range code action (when you see 🏏 ) |
| n | \<Space\>rn | rename with floating window |
| n | \<Leader\>re | rename (lsp default) |
| n | \<Leader\>gi | hierarchy incoming calls |
| n | \<Leader\>go | hierarchy outgoing calls |
| n | gi | implementation |
| n | \<Space\> D | type definition |
| n | gL | show line diagnostic |
| n | gG | show diagnostic for all buffers |
| n | ]d | next diagnostic |
| n | [d | previous diagnostic |
| n | \<Leader\> dt | diagnostic toggle(enable/disable) |
| n | ]r | next treesitter reference/usage |
| n | [r | previous treesitter reference/usage |
| n | \<Space\> wa | add workspace folder |
| n | \<Space\> wr | remove workspace folder |
| n | \<Space\> wl | print workspace folder |
| n | \<Leader\>k | toggle reference highlight |
| i/n | \<C-p\> | previous item in list |
| i/n | \<C-n\> | next item in list |
| i/n | number 1~9 | move to ith row/item in the list |
| i/n | \<Up\> | previous item in list |
| i/n | \<Down\> | next item in list |
| n | \<Ctrl-w\>j | move cursor to preview (windows move to bottom view point) |
| n | \<Ctrl-w\>k | move cursor to list (windows move to up view point) |
| i/n | \<C-o\> | open preview file in nvim/Apply action |
| n | \<C-v\> | open preview file in nvim with vsplit |
| n | \<C-s\> | open preview file in nvim with split |
| n | \<Enter\> | open preview file in nvim/Apply action |
| n | \<ESC\> | close listview of floating window |
| i/n | \<C-e\> | close listview of floating window |
| n | \<C-q\> | close listview and send results to quickfix |
| i/n | \<C-b\> | previous page in listview |
| i/n | \<C-f\> | next page in listview |
| i/n | \<C-s\> | save the modification to preview window to file |
### Colors/Highlight:
You can override default highlight GHListDark (listview) and GHTextViewDark (code view)
You can override default highlight GuihuaListDark (listview) and GuihuaTextViewDark (code view) and GuihuaListHl (select item)
e.g.
```vim
hi default GHTextViewDark guifg=#e0d8f4 guibg=#332e55
hi default GHListDark guifg=#e0d8f4 guibg=#103234
hi default GuihuaTextViewDark guifg=#e0d8f4 guibg=#332e55
hi default GuihuaListDark guifg=#e0d8f4 guibg=#103234
hi default GuihuaListHl guifg=#e0d8f4 guibg=#404254
```
There are other Lsp highlight been used in this plugin, e.g LspReferenceRead/Text/Write are used for document highlight,
There are other Lsp highlight been used in this plugin, e.g LspReferenceRead/Text/Write are used for document highlight,
LspDiagnosticsXXX are used for diagnostic. Please check highlight.lua and dochighlight.lua for more info.
## Dependency
- lspconfig
@ -324,6 +522,218 @@ 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).
## Integrat with mason (williamboman/mason.nvim) or lsp_installer (williamboman/nvim-lsp-installer, deprecated)
If you are using mason or lsp_installer and would like to use the lsp servers installed by lsp_installer. Please set
```lua
lsp_installer = true --lsp_installer users, deprecated
mason = true -- mason user
```
In the config. Also please setup the lsp server from installer setup with `server:setup{opts}`
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({
lsp_installer = true,
})
end,
})
```
for mason
```lua
use("williamboman/mason.nvim")
use({
"williamboman/mason-lspconfig.nvim",
config = function()
require("mason").setup()
require("mason-lspconfig").setup({})
end,
})
use({
"ray-x/navigator.lua",
requires = {
{ "ray-x/guihua.lua", run = "cd lua/fzy && make" },
{ "neovim/nvim-lspconfig" },
{ "nvim-treesitter/nvim-treesitter" },
},
config = function()
require("navigator").setup({
mason = true,
})
end,
})
```
Another way to setup mason is disable navigator lsp setup and using mason setup handlers, pylsp for example
```lua
use("williamboman/mason.nvim")
use({
"williamboman/mason-lspconfig.nvim",
config = function()
require("mason").setup()
require("mason-lspconfig").setup_handlers({
["pylsp"] = function()
require("lspconfig").pylsp.setup({
on_attach = function(client, bufnr)
require("navigator.lspclient.mapping").setup({ client = client, bufnr = bufnr }) -- setup navigator keymaps here,
require("navigator.dochighlight").documentHighlight(bufnr)
require("navigator.codeAction").code_action_prompt(bufnr)
end,
})
end,
})
require("mason-lspconfig").setup({})
end,
})
use({
"navigator.lua",
requires = {
{ "ray-x/guihua.lua", run = "cd lua/fzy && make" },
{ "nvim-lspconfig" },
{ "nvim-treesitter/nvim-treesitter" },
},
config = function()
require("navigator").setup({
mason = true,
lsp = { disable_lsp = { "pylsp" } }, -- disable pylsp setup from navigator
})
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
```lua
require'navigator'.setup({
-- lsp_installer = false -- default value is false
lsp = {
tsserver = { cmd = {'your tsserver installed by lsp_installer or mason'} }
-- e.g. tsserver = { cmd = {'/home/username/.local/share/nvim/mason/packages/typescript-language-server/node_modules/typescript/bin/tsserver'} }
}
})
```
example cmd setup (mac) for pyright :
```
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" }
-- or mason: cmd = { "/Users/username/.local/share/nvim/mason/packages/pyright/node_modules/pyright/index.js", "--stdio"}
}
}
}
```
The lsp servers installed by nvim-lsp-installer is in following dir
```lua
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
```lua
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)
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
* [lua-dev](https://github.com/folke/lua-dev.nvim) Dev setup for init.lua and plugin development. Navigator can
extend lua setup with lua-dev.
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
['lua-dev'] = { runtime_path=true } -- any non default lua-dev setups
},
})
require('rust-tools').setup({
server = {
on_attach = function(client, bufnr)
require('navigator.lspclient.mapping').setup({client=client, bufnr=bufnr}) -- setup navigator keymaps here,
require("navigator.dochighlight").documentHighlight(bufnr)
require('navigator.codeAction').code_action_prompt(bufnr)
-- 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,
require("navigator.dochighlight").documentHighlight(bufnr)
require('navigator.codeAction').code_action_prompt(bufnr)
-- 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.
@ -347,16 +757,29 @@ require'navigator'.setup({on_attach = function(client, bufnr) require 'illuminat
Highlight I am using:
* LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()`
That is where you saw the current symbol been highlighted.
- LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()`
That is where you saw the current symbol been highlighted.
* GHListDark and GHTextViewDark is used for floating listvew and TextView. They are be based on current background
(Normal) and PmenuSel
- GuihuaListDark and GuihuaTextViewDark is used for floating listvew and TextView. They are be based on current background
(Normal) and PmenuSel
* In future, I will use NormalFloat for floating view. But ATM, most of colorscheme does not define NormalFloat
- In future, I will use NormalFloat for floating view. But ATM, most of colorscheme does not define NormalFloat
You can override the above highlight to fit your current colorscheme
## commands
| command | function |
| ------------ | ------------------------- |
| 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 |
| NRefPanel | show symbol reference in side panel |
| TSymobls | treesitter symbol in side panel |
| Calltree {args} | lsp call hierarchy call tree, args: -i (incomming default), -o (outgoing) |
## Screenshots
@ -366,15 +789,42 @@ colorscheme: [aurora](https://github.com/ray-x/aurora)
Pls check the first part of README
### Document Symbol
### Definition preview
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
<img width="708" alt="image" src="https://user-images.githubusercontent.com/1681295/174791609-0023e68f-f1f4-4335-9ea2-d2360e9f0bfd.png">
<img width="733" alt="image" src="https://user-images.githubusercontent.com/1681295/174804579-26f87fbf-426b-46d0-a7a3-a5aab69c032f.png">
Calltree (Expandable LSP call hierarchy)
<img width="769" alt="image" src="https://user-images.githubusercontent.com/1681295/176998572-e39fc968-4c8c-475d-b3b8-fb7991663646.png">
### GUI and multigrid support
You can load a different font size for floating win
![multigrid2](https://user-images.githubusercontent.com/1681295/139196378-bf69ade9-c916-42a9-a91f-cccb39b9c4eb.jpg)
### Document Symbol and navigate through the list
![document symbol](https://github.com/ray-x/files/blob/master/img/navigator/doc_symbol.gif?raw=true)
![doc_symbol_and_navigate](https://user-images.githubusercontent.com/1681295/148642747-1870b1a4-67c2-4a0d-8a41-d462ecdc663e.gif)
The key binding to navigate in the list.
- up and down key
- `<Ctrl-f/b>` 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
![workspace symbol](https://github.com/ray-x/files/blob/master/img/navigator/workspace_symbol.gif?raw=true)
### highlight document symbol and jump between reference
![multiple_symbol_hi3](https://user-images.githubusercontent.com/1681295/120067627-f9f80680-c0bf-11eb-9216-18e5c8547f59.gif)
# Current symbol highlight and jump backward/forward between symbols
@ -385,6 +835,11 @@ Jump between symbols with treesitter (with `]r` and `[r`)
### Diagnostic
Visual studio code style show errors minimap in scroll bar area
(Check setup for `diagnostic_scrollbar_sign`)
![diagnostic_scroll_bar](https://user-images.githubusercontent.com/1681295/128736430-e365523d-810c-4c16-a3b4-c74969f45f0b.jpg)
Diagnostic in single bufer
![diagnostic](https://github.com/ray-x/files/blob/master/img/navigator/diag.jpg?raw=true)
@ -399,7 +854,6 @@ You can in place edit your code in floating window
https://user-images.githubusercontent.com/1681295/121832919-89cbc080-cd0e-11eb-9778-11d0f356b38d.mov
(Note: This feature only avalible in `find reference` and `find diagnostic`, You can not add/remove lines in floating window)
### Implementation
@ -414,6 +868,10 @@ https://user-images.githubusercontent.com/1681295/121832919-89cbc080-cd0e-11eb-9
![code actions](https://github.com/ray-x/files/blob/master/img/navigator/codeaction.jpg?raw=true)
### Symbol rename
![rename](https://user-images.githubusercontent.com/1681295/141081135-55f45c2d-28c6-4475-a083-e37dfabe9afd.jpg)
#### Fill struct with gopls
![code actions fill struct](https://github.com/ray-x/files/blob/master/img/navigator/fill_struct.gif?raw=true)
@ -435,19 +893,69 @@ Improved signature help with current parameter highlighted
![show_signature](https://github.com/ray-x/files/blob/master/img/navigator/show_signnature.gif?raw=true "show_signature")
### Call hierarchy (incomming/outgoing)
### Call hierarchy (incomming/outgoing calls)
![incomming](https://github.com/ray-x/files/blob/master/img/navigator/incomming.jpg?raw=true)
![incomming_calls](https://user-images.githubusercontent.com/1681295/142348079-49b71486-4f16-4f10-95c9-483aad11c262.jpg)
### Light bulb if codeAction available
![lightbulb](https://github.com/ray-x/files/blob/master/img/navigator/lightbulb.jpg?raw=true)
### Codelens
Codelens for gopls/golang. Garbage collection analyse:
![codelens](https://user-images.githubusercontent.com/1681295/132428956-7835bf30-2ed5-4871-b2d7-7fbad22f63e8.jpg)
Codelens for C++/ccls. Symbol reference
![codelens_cpp_ccls](https://user-images.githubusercontent.com/1681295/132429134-abc6547e-79cc-44a4-b7a9-23550b895e51.jpg)
### Predefined LSP symbol nerdfont/emoji
![nerdfont](https://github.com/ray-x/files/blob/master/img/navigator/icon_nerd.jpg?raw=true)
### VS-code style folding with treesitter
Folding is using a hacked version of treesitter folding. (option: ts_fold)
#### folding function
![image](https://user-images.githubusercontent.com/1681295/148491596-6cd6c507-c157-4536-b8c4-dc969436763a.png)
#### folding comments
Multiline comments can be folded as it is treated as a block
![image](https://user-images.githubusercontent.com/1681295/148491845-5ffb18ea-f05d-4229-aec3-aa635b3de814.png)
# Debug the plugin
One simple way to gether debug info and understand what is wrong is output the debug logs
```lua
require'navigator'.setup({
debug = false, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
})
```
```lua
-- a example of adding logs in the plugin
local log = require"navigator.util".log
local definition_hdlr = util.mk_handler(function(err, locations, ctx, _)
-- output your log
log('[definition] log for locations', locations, "and ctx", ctx)
if err ~= nil then
return
end
end
```
# Break changes and known issues
[known issues I am working on](https://github.com/ray-x/navigator.lua/issues/1)
# Todo
@ -456,3 +964,14 @@ Improved signature help with current parameter highlighted
- Async (some of the requests is slow on large codebases and might be good to use co-rountine)
- More clients. I use go, python, js/ts, java, c/cpp, lua most of the time. Did not test other languages (e.g dart, swift etc)
- Configuration options
# Errors and Bug Reporting
- Please double check your setup and check if minium setup works or not
- 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)

@ -0,0 +1,4 @@
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

@ -0,0 +1,3 @@
function! foldlsp#foldexpr()
return luaeval(printf('require"navigator.foldlsp".get_fold_indic(%d)', v:lnum))
endfunction

@ -0,0 +1,883 @@
navigator.txt
================================================================================
CONTENTS *navigator-contents*
1. Navigator.................................................|navigator-navigator|
1.1. Example: Javascript closure.|navigator-example:_javascript_closure|
1.2. Example: C++ definition.......|navigator-example:_c++_definition|
1.3. Golang struct type.................|navigator-golang_struct_type|
2. Features:.................................................|navigator-features:|
3. Why a new plugin...................................|navigator-why_a_new_plugin|
4. Similar projects / special mentions:.|navigator-similar_projects_/_special_mentions:|
5. Install.....................................................|navigator-install|
5.1. Setup...................................................|navigator-setup|
5.2. Sample vimrc turning your neovim into a full-featured IDE.|navigator-sample_vimrc_turning_your_neovim_into_a_full-featured_ide|
5.3. Work with nvim-cmp and nvim-autopairs.|navigator-work_with_nvim-cmp_and_nvim-autopairs|
5.4. All configure options...................|navigator-all_configure_options|
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. 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. 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|
5.10. commands............................................|navigator-commands|
5.11. Screenshots......................................|navigator-screenshots|
5.11.1. Reference....................................|navigator-reference|
5.11.2. Definition preview..................|navigator-definition_preview|
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|
6.3. Implementation.............................|navigator-implementation|
6.4. Fzy search in reference...........|navigator-fzy_search_in_reference|
6.5. Code actions.................................|navigator-code_actions|
6.6. Symbol rename...............................|navigator-symbol_rename|
6.6.1. Fill struct with gopls.......|navigator-fill_struct_with_gopls|
6.7. Code preview with highlight...|navigator-code_preview_with_highlight|
6.8. Treesitter symbol.......................|navigator-treesitter_symbol|
6.9. Signature help.............................|navigator-signature_help|
6.10. Call hierarchy (incomming/outgoing calls).|navigator-call_hierarchy_(incomming/outgoing_calls)|
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|
10. Errors and Bug Reporting..................|navigator-errors_and_bug_reporting|
================================================================================
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
EXAMPLE: JAVASCRIPT CLOSURE *navigator-example:_javascript_closure*
The following screenshot shows javascript call tree 🌲 of variable `browser` insides a closure. This feature is similar to incoming & outgoing calls from LSP. It is designed for the symbol analysis.
Explanation:
* The first line of floating windows shows there are 3 references for the symbol browser in closure.js
* The first reference of browser is an assignment, an emoji 📝 indicates the value is changed in this line. In many
cases, we search for references to find out when the value changed.
* The second reference of `browser` is inside function `displayName` and `displayName` sit inside `makeFunc`, So you
will see `displayName{} <- makeFunc{}`
* The third similar to the second, as var browser is on the right side of '=', the value not changed in this line
and emoji is not shown.
EXAMPLE: C++ DEFINITION *navigator-example:_c++_definition*
C++ example: search reference and definition
You may find a 🦕 dinosaur(d) on the line of `Rectangle rect,` which means there is a definition (d for def) of rect in this line.
`<- f main()` means the definition is inside function main().
GOLANG STRUCT TYPE *navigator-golang_struct_type*
Struct type references in multiple Go ﳑ files
This feature can provide you info in which function/class/method the variable was referenced. It is handy for a large
project where class/function definition is too long to fit into the preview window. Also provides a bird's eye view of where the
variable is:
* Referenced
* Modified
* Defined
* Called
================================================================================
FEATURES: *navigator-features:*
* LSP easy setup. Support the most commonly used lsp clients setup. Dynamic lsp activation based on buffer type. This
also enables you to handle workspace with mixed types of codes (e.g. Go + javascript + yml). A better default setup is
included for LSP clients.
* Out of box experience. 10 lines of minimum vimrc can turn your neovim into a full-featured LSP & Treesitter powered IDE
* UI with floating windows, navigator provides a visual way to manage and navigate through symbols, diagnostic errors, reference etc. It covers
all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface
implementation.
* Code Action GUI
* Luv async thread and tasks
* Edit your code in preview window
* Async request with lsp.buf_request for reference search
* Treesitter symbol search. It is handy for large files (Some of LSP e.g. sumneko_lua, there is a 100kb file size limitation?)
* FZY search with either native C (if gcc installed) or Lua-JIT
* LSP multiple symbol highlight/marker and hop between document references
* Preview definination/references
* Better navigation for diagnostic errors, Navigate through all files/buffers that contain errors/warnings
* Grouping references/implementation/incoming/outgoing based on file names.
* Treesitter based variable/function context analysis. It is 10x times faster compared to purely rely on LSP. In most
of the case, it takes treesitter less than 4 ms to read and render all nodes for a file of 1,000 LOC.
* The first plugin, IMO, allows you to search in all treesitter symbols in the workspace.
* Nerdfont, emoji for LSP and treesitter kind
* Optimize display (remove trailing bracket/space), display the caller of reference, de-duplicate lsp results (e.g reference
in the same line). Using treesitter for file preview highlighter etc
* ccls call hierarchy (Non-standard `ccls/call` API) supports
* 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
* Multigrid support (different font and detachable)
================================================================================
WHY A NEW PLUGIN *navigator-why_a_new_plugin*
I'd like to go beyond what the system is offering.
================================================================================
SIMILAR PROJECTS / SPECIAL MENTIONS: *navigator-similar_projects_/_special_mentions:*
* nvim-lsputils (https://github.com/RishabhRD/nvim-lsputils)
* nvim-fzy (https://github.com/mfussenegger/nvim-fzy.git)
* fuzzy (https://github.com/amirrezaask/fuzzy.nvim)
* lspsaga (https://github.com/glepnir/lspsaga.nvim)
* fzf-lsp lsp with fzf as gui backend (https://github.com/gfanto/fzf-lsp.nvim)
* nvim-treesitter-textobjects (https://github.com/nvim-treesitter/nvim-treesitter-textobjects)
================================================================================
INSTALL *navigator-install*
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)).
>
Plug 'neovim/nvim-lspconfig'
Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua'
<
Note: Highly recommend: 'nvim-treesitter/nvim-treesitter'
Packer
>
use({
'ray-x/navigator.lua',
requires = {
{ 'ray-x/guihua.lua', run = 'cd lua/fzy && make' },
{ 'neovim/nvim-lspconfig' },
},
})
<
--------------------------------------------------------------------------------
SETUP *navigator-setup*
Easy setup BOTH lspconfig and navigator with one liner. Navigator covers around 20 most used LSP setup.
>
lua require'navigator'.setup()
<
--------------------------------------------------------------------------------
SAMPLE VIMRC TURNING YOUR NEOVIM INTO A FULL-FEATURED IDE *navigator-sample_vimrc_turning_your_neovim_into_a_full-featured_ide*
>
call plug#begin('~/.vim/plugged')
Plug 'neovim/nvim-lspconfig'
Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua'
" 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 require('lspconfig'), navigator will configure it for you
lua <<EOF
require'navigator'.setup()
EOF
<
You can remove your lspconfig.lua and use the hooks of navigator.lua. As the
navigator will bind keys and handler for you. The LSP will be loaded lazily based on filetype.
A treesitter only mode. In some cases LSP is buggy or not available, you can also use treesitter
standalone
>
call plug#begin('~/.vim/plugged')
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...
" optional, if you need treesitter symbol support
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
" optional:
Plug 'nvim-treesitter/nvim-treesitter-refactor' " this provides "go to def" etc
call plug#end()
lua <<EOF
require'navigator'.setup()
EOF
<
--------------------------------------------------------------------------------
WORK WITH NVIM-CMP AND NVIM-AUTOPAIRS *navigator-work_with_nvim-cmp_and_nvim-autopairs*
The buffer type of navigator floating windows is `guihua`
I would suggest disable `guihua` for autocomplete.
e.g.
>
require('nvim-autopairs').setup{
disable_filetype = { "TelescopePrompt" , "guihua", "guihua_rust", "clap_input" },
if vim.o.ft == 'clap_input' and vim.o.ft == 'guihua' and vim.o.ft == 'guihua_rust' then
require'cmp'.setup.buffer { completion = {enable = false} }
end
-- or with autocmd
vim.cmd("autocmd FileType guihua lua require('cmp').setup.buffer { enabled = false }")
vim.cmd("autocmd FileType guihua_rust lua require('cmp').setup.buffer { enabled = false }")
...
}
<
--------------------------------------------------------------------------------
ALL CONFIGURE OPTIONS *navigator-all_configure_options*
Nondefault configuration example:
>
require'navigator'.setup({
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
border = {"╭", "─", "╮", "│", "╯", "─", "╰", "│"}, -- border style, can be one of 'none', 'single', 'double',
-- 'shadow', or a list of chars which defines the border
on_attach = function(client, bufnr)
-- your hook
end,
-- put a on_attach of your own here, e.g
-- function(client, bufnr)
-- -- the on_attach will be called at end of navigator on_attach
-- end,
-- The attach code will apply to all LSP clients
default_mapping = true, -- set to false if you will remap every key
keymaps = {{key = "gK", func = "declaration()"}}, -- a list of key maps
-- 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
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 = "🏏",
-- Diagnostics
diagnostic_head = '🐛',
diagnostic_head_severity_1 = "🈲",
-- refer to lua/navigator.lua for more icons setups
},
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)
disable_format_cap = {"sqls", "sumneko_lua", "gopls"}, -- a list of lsp disable format capacity (e.g. if you using efm or vim-codeformat etc), empty {} by default
disable_lsp = {'pylsd', 'sqlls'}, -- a list of lsp server disabled for your project, e.g. denols and tsserver you may
-- only want to enable one lsp server
-- to disable all default config and use your own lsp setup set
-- disable_lsp = 'all'
-- Default {}
diagnostic = {
underline = true,
virtual_text = true, -- show virtual for diagnostic message
update_in_insert = false, -- update diagnostic message in insert mode
},
diagnostic_scrollbar_sign = {'▃', '▆', '█'}, -- experimental: diagnostic status in scroll bar area; set to false to disable the diagnostic sign,
-- for other style, set to {'╍', 'ﮆ'} or {'-', '='}
diagnostic_virtual_text = true, -- show virtual for diagnostic message
diagnostic_update_in_insert = false, -- update diagnostic message in insert mode
disply_diagnostic_qf = true, -- always show quickfix if there are diagnostic errors, set to false if you want to
ignore it
tsserver = {
filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetypes
},
ctags ={
cmd = 'ctags',
tagfile = 'tags'
options = '-R --exclude=.git --exclude=node_modules --exclude=test --exclude=vendor --excmd=number'
}
gopls = { -- gopls setting
on_attach = function(client, bufnr) -- on_attach for gopls
-- your special on attach here
-- e.g. disable gopls format because a known issue https://github.com/golang/go/issues/45732
print("i am a hook, I will disable document format")
client.resolved_capabilities.document_formatting = false
end,
settings = {
gopls = {gofumpt = false} -- disable gofumpt etc,
}
},
sumneko_lua = {
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, 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},
}
})
<
LSP CLIENTS *navigator-lsp_clients*
Built clients:
>
local servers = {
"angularls", "gopls", "tsserver", "flow", "bashls", "dockerls", "julials", "pylsp", "pyright",
"jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls",
"yamlls", "clangd", "ccls", "sqls", "denols", "graphql", "dartls", "dotls",
"kotlin_language_server", "nimls", "intelephense", "vuels", "phpactor", "omnisharp",
"r_language_server", "rust_analyzer", "terraformls", "svelte", "texlab", "clojure_lsp", "elixirls",
"sourcekit", "fsautocomplete", "vls", "hls"
}
<
Navigator will try to load avalible lsp server/client based on filetype. The clients has none default on_attach.
incremental sync and debounce is enabled by navigator. And the lsp
snippet will be enabled. So you could use COQ and nvim-cmp snippet expand.
Other than above setup, additional none default setup are used for following lsp:
* gopls
* clangd
* rust_analyzer
* sqls
* sumneko_lua
* pyright
* ccls
Please check client setup (https://github.com/ray-x/navigator.lua/blob/26012cf9c172aa788a2e53018d94b32c5c75af75/lua/navigator/lspclient/clients.lua#L98-L234)
The plugin can work with multiple LSP, e.g sqls+gopls+efm. But there are cases you may need to disable some of the
servers. (Prevent loading multiple LSP for same source code.) e.g. I saw strange behaviours when I use
pylsp+pyright+jedi
together. If you have multiple similar LSP installed and have trouble with the plugin, please enable only one at a time.
ADD YOUR OWN SERVERS *navigator-add_your_own_servers*
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
Alternatively, update following option in setup(if you do not want a PR):
>
require'navigator'setup{lsp={servers={'cmake', 'lexls'}}}
<
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*
Note: If you have multiple lsp installed for same language, please only enable one at a time by disable others with e.g. `disable_lsp={'denols', 'clangd'}`
To disable a specific LSP, set `filetypes` to {} e.g.
>
require'navigator'.setup({
lsp={
pylsd={filetype={}}
}
})
<
Or:
>
require'navigator'.setup({
lsp={
disable_lsp = {'pylsd', 'sqlls'},
}
})
<
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 | async references, definitions and context |
| n | <Leader>gr | show reference and context |
| i | <m-k> | signature help |
| n | <c-k> | signature help |
| n | gW | workspace symbol |
| n | gD | declaration |
| n | gd | definition |
| n | g0 | document symbol |
| n | <C-]> | go to definition (if multiple show listview) |
| n | gp | definition preview (Go to Preview) |
| n | <C-LeftMouse> | definition |
| n | g<LeftMouse> | implementation |
| n | <Leader>gt | treesitter document symbol |
| n | <Leader>gT | treesitter symbol for all open buffers |
| n | <Leader> ct | ctags symbol search |
| n | <Leader> cg | ctags symbol generate |
| n | K | hover doc |
| n | <Space>ca | code action (when you see 🏏 ) |
| n | <Space>la | code lens action (when you see a codelens indicator) |
| v | <Space>ca | range code action (when you see 🏏 ) |
| n | <Space>rn | rename with floating window |
| n | <Leader>re | rename (lsp default) |
| n | <Leader>gi | hierarchy incoming calls |
| n | <Leader>go | hierarchy outgoing calls |
| n | gi | implementation |
| n | <Space> D | type definition |
| n | gL | show line diagnostic |
| n | gG | show diagnostic for all buffers |
| n | ]d | next diagnostic |
| n | [d | previous diagnostic |
| n | <Leader> dt | diagnostic toggle(enable/disable) |
| n | ]r | next treesitter reference/usage |
| n | [r | previous treesitter reference/usage |
| n | <Space> wa | add workspace folder |
| n | <Space> wr | remove workspace folder |
| n | <Space> wl | print workspace folder |
| n | <Leader>k | toggle reference highlight |
| i/n | <C-p> | previous item in list |
| i/n | <C-n> | next item in list |
| i/n | number 1~9 | move to ith row/item in the list |
| i/n | <Up> | previous item in list |
| i/n | <Down> | next item in list |
| n | <Ctrl-w>j | move cursor to preview (windows move to bottom view point) |
| n | <Ctrl-w>k | move cursor to list (windows move to up view point) |
| i/n | <C-o> | open preview file in nvim/Apply action |
| n | <C-v> | open preview file in nvim with vsplit |
| n | <C-s> | open preview file in nvim with split |
| n | <Enter> | open preview file in nvim/Apply action |
| n | <ESC> | close listview of floating window |
| i/n | <C-e> | close listview of floating window |
| i/n | <C-b> | previous page in listview |
| i/n | <C-f> | next page in listview |
| i/n | <C-s> | save the modification to preview window to file |
COLORS/HIGHLIGHT: *navigator-colors/highlight:*
You can override default highlight GuihuaListDark (listview) and GuihuaTextViewDark (code view) and GuihuaListHl (select item)
e.g.
>
hi default GuihuaTextViewDark guifg=#e0d8f4 guibg=#332e55
hi default GuihuaListDark guifg=#e0d8f4 guibg=#103234
hi default GuihuaListHl guifg=#e0d8f4 guibg=#404254
<
There are other Lsp highlight been used in this plugin, e.g LspReferenceRead/Text/Write are used for document highlight,
LspDiagnosticsXXX are used for diagnostic. Please check highlight.lua and dochighlight.lua for more info.
--------------------------------------------------------------------------------
DEPENDENCY *navigator-dependency*
* lspconfig
* guihua.lua (provides floating window, FZY)
* Optional:
* treesitter (list treesitter symbols, object analysis)
* lsp-signature (better signature help)
The plugin can be loaded lazily (packer `opt = true` ), And it will check if optional plugins existance and load those plugins only if they existed.
The terminal will need to be able to output nerdfont and emoji correctly. I am using Kitty with nerdfont (Victor Mono).
--------------------------------------------------------------------------------
INTEGRAT WITH LSP_INSTALLER (WILLIAMBOMAN/NVIM-LSP-INSTALLER) *navigator-integrat_with_lsp_installer_(williamboman/nvim-lsp-installer)*
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. 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,
})
<
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
>
require'navigator'.setup({
-- lsp_installer = false -- default value is false
lsp = {
tsserver = { cmd = {'your tsserver installed by lsp_installer'} }
}
})
<
example cmd setup (mac) for pyright :
>
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,
}
}
<
--------------------------------------------------------------------------------
USAGE *navigator-usage*
Please refer to lua/navigator/lspclient/mapping.lua on key mappings. Should be able to work out-of-box.
* Use <c-e> or `:q!` to kill the floating window
* (or <c-n>, <c-p>) to move
* <c-o> or <CR> to open location or apply code actions. Note: <CR> might be bound in insert mode by other plugins
--------------------------------------------------------------------------------
CONFIGURATION *navigator-configuration*
In `navigator.lua` there is a default configuration. You can override the values by passing your own values
e.g
>
-- The attach will be call at end of navigator on_attach()
require'navigator'.setup({on_attach = function(client, bufnr) require 'illuminate'.on_attach(client)})
<
--------------------------------------------------------------------------------
HIGHLIGHT *navigator-highlight*
Highlight I am using:
* LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()`
That is where you saw the current symbol been highlighted.
* GuihuaListDark and GuihuaTextViewDark is used for floating listvew and TextView. They are be based on current background
(Normal) and PmenuSel
* In future, I will use NormalFloat for floating view. But ATM, most of colorscheme does not define NormalFloat
You can override the above highlight to fit your current colorscheme
--------------------------------------------------------------------------------
COMMANDS *navigator-commands*
| 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 |
| NRefPanel |symbol reference 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.
:NRefPanel *:NRefPanel*
Symbol reference in side panel.
:Calltree [flags] *:Calltree*
Lsp call hierarchy call tree.
[flags]:
-i: incomming default
-o: outgoing
--------------------------------------------------------------------------------
SCREENSHOTS *navigator-screenshots*
colorscheme: aurora (https://github.com/ray-x/aurora)
REFERENCE *navigator-reference*
Pls check the first part of README
DEFINITION PREVIEW *navigator-definition_preview*
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 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
* `<Ctrl-f/b>` 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*
HIGHLIGHT DOCUMENT SYMBOL AND JUMP BETWEEN REFERENCE *navigator-highlight_document_symbol_and_jump_between_reference*
================================================================================
CURRENT SYMBOL HIGHLIGHT AND JUMP BACKWARD/FORWARD BETWEEN SYMBOLS *navigator-current_symbol_highlight_and_jump_backward/forward_between_symbols*
Document highlight provided by LSP.
Jump between symbols with treesitter (with `]r` and `[r`)
DIAGNOSTIC *navigator-diagnostic*
Visual studio code style show errors minimap in scroll bar area
(Check setup for `diagnostic_scrollbar_sign`)
Diagnostic in single bufer
Show diagnostic in all buffers
EDIT IN PREVIEW WINDOW *navigator-edit_in_preview_window*
You can in place edit your code in floating window
https://user-images.githubusercontent.com/1681295/121832919-89cbc080-cd0e-11eb-9778-11d0f356b38d.mov
(Note: This feature only avalible in `find reference` and `find diagnostic`, You can not add/remove lines in floating window)
IMPLEMENTATION *navigator-implementation*
FZY SEARCH IN REFERENCE *navigator-fzy_search_in_reference*
CODE ACTIONS *navigator-code_actions*
SYMBOL RENAME *navigator-symbol_rename*
FILL STRUCT WITH GOPLS *navigator-fill_struct_with_gopls*
CODE PREVIEW WITH HIGHLIGHT *navigator-code_preview_with_highlight*
TREESITTER SYMBOL *navigator-treesitter_symbol*
Treetsitter symbols in all buffers
SIGNATURE HELP *navigator-signature_help*
Improved signature help with current parameter highlighted
CALL HIERARCHY (INCOMMING/OUTGOING CALLS) *navigator-call_hierarchy_(incomming/outgoing_calls)*
LIGHT BULB IF CODEACTION AVAILABLE *navigator-light_bulb_if_codeaction_available*
CODELENS *navigator-codelens*
Codelens for gopls/golang. Garbage collection analyse:
Codelens for C++/ccls. Symbol reference
PREDEFINED LSP SYMBOL NERDFONT/EMOJI *navigator-predefined_lsp_symbol_nerdfont/emoji*
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*
One simple way to gether debug info and understand what is wrong is output the debug logs
>
require'navigator'.setup({
debug = false, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
})
<
>
-- a example of adding logs in the plugin
local log = require"navigator.util".log
local definition_hdlr = util.mk_handler(function(err, locations, ctx, _)
-- output your log
log('[definition] log for locations', locations, "and ctx", ctx)
if err ~= nil then
return
end
end
<
================================================================================
BREAK CHANGES AND KNOWN ISSUES *navigator-break_changes_and_known_issues*
known issues I am working on (https://github.com/ray-x/navigator.lua/issues/1)
================================================================================
TODO *navigator-todo*
* The project is in the early phase, bugs expected, PRs and suggestions are welcome
* Async (some of the requests is slow on large codebases and might be good to use co-rountine)
* More clients. I use go, python, js/ts, java, c/cpp, lua most of the time. Did not test other languages (e.g dart, swift etc)
* Configuration options
================================================================================
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.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)

@ -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

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="css/style.css" rel="stylesheet">
</head>
<body>
</body>
</html>

1
lua/.gitignore vendored

@ -1 +0,0 @@
TODO.txt

@ -1,84 +1,246 @@
local M = {}
local api = vim.api
local function warn(msg)
api.nvim_echo({ { 'WRN: ' .. msg, 'WarningMsg' } }, true, {})
end
local function info(msg)
if _NgConfigValues.debug then
api.nvim_echo({ { 'Info: ' .. msg } }, true, {})
end
end
_NgConfigValues = {
debug = false, -- log output not implemented
width = 0.6, -- valeu of cols TODO allow float e.g. 0.6
preview_height = 0.35,
height = 0.35,
debug = false, -- log output
width = 0.62, -- valeu of cols
height = 0.38, -- listview height
preview_height = 0.38,
preview_lines = 40, -- total lines in preview screen
preview_lines_before = 5, -- lines before the highlight line
default_mapping = true,
keymaps = {{key = "GR", func = "references()"}}, -- e.g keymaps={{key = "gr", func = "references()"}, }
combined_attach = "both", -- both: use both customized attach and navigator default attach, mine: only use my attach defined in vimrc
on_attach = nil,
-- function(client, bufnr)
-- -- your on_attach will be called at end of navigator on_attach
-- end,
code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
keymaps = {}, -- e.g keymaps={{key = "GR", func = vim.lsp.buf.references}, } this replace gr default mapping
external = nil, -- true: enable for goneovim multigrid otherwise false
border = 'single', -- border style, can be one of 'none', 'single', 'double', "shadow"
lines_show_prompt = 10, -- when the result list items number more than lines_show_prompt,
-- fuzzy finder prompt will be shown
combined_attach = 'both', -- both: use both customized attach and navigator default attach, mine: only use my attach defined in vimrc
on_attach = function(client, bufnr)
-- your on_attach will be called at end of navigator on_attach
end,
ts_fold = false,
treesitter_analysis = true, -- treesitter variable context
treesitter_analysis_max_num = 100, -- how many items to run treesitter analysis
treesitter_analysis_condense = true, -- short format of function
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',
options = '-R --exclude=.git --exclude=node_modules --exclude=test --exclude=vendor --excmd=number',
},
lsp = {
enable = true, -- if disabled make sure add require('navigator.lspclient.mapping').setup() in you on_attach
code_action = {
enable = true,
sign = true,
sign_priority = 40,
virtual_text = true,
virtual_text_icon = true,
},
document_highlight = true, -- highlight reference a symbol
code_lens_action = {
enable = true,
sign = true,
sign_priority = 40,
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)
format_options = { async = false }, -- async: disable by default, I saw something unexpected
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
-- only want to enable one lsp server
disply_diagnostic_qf = true, -- always show quickfix if there are diagnostic errors
diagnostic_load_files = false, -- lsp diagnostic errors list may contains uri that not opened yet set to true
-- to load those files
diagnostic_virtual_text = true, -- show virtual for diagnostic message
diagnostic_update_in_insert = false, -- update diagnostic message in insert mode
diagnostic_scrollbar_sign = { '', '', '' }, -- set to nil to disable, set to {'╍', 'ﮆ'} to enable diagnostic status in scroll bar area
tsserver = {
-- filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetype
},
['lua-dev'] = { -- navigator can use lua-dev settings to setup sumneko_lua
-- your setting for lua-dev here
-- navigator will setup lua-dev
},
sumneko_lua = {
-- sumneko_root_path = sumneko_root_path,
-- sumneko_binary = sumneko_binary,
-- cmd = {'lua-language-server'}
}
},
servers = {}, -- you can add additional lsp server so navigator will load the default for you
},
lsp_installer = false, -- set to true if you would like use the lsp installed by williamboman/nvim-lsp-installer
mason = false, -- set to true if you would like use the lsp installed by williamboman/mason
icons = {
icons = true, -- set to false to use system default ( if you using a terminal does not have nerd/icon)
-- Code action
code_action_icon = "",
code_action_icon = '🏏', -- "",
-- code lens
code_lens_action_icon = '👓',
-- Diagnostics
diagnostic_head = '🐛',
diagnostic_head_severity_1 = "🈲",
diagnostic_head_severity_2 = "☣️",
diagnostic_head_severity_3 = "👎",
diagnostic_head_description = "📛",
diagnostic_virtual_text = "🦊",
diagnostic_file = "🚑",
diagnostic_err = '📛',
diagnostic_warn = '👎',
diagnostic_info = [[👩]],
diagnostic_hint = [[💁]],
diagnostic_head_severity_1 = '🈲',
diagnostic_head_severity_2 = '☣️',
diagnostic_head_severity_3 = '👎',
diagnostic_head_description = '👹',
diagnostic_virtual_text = '🦊',
diagnostic_file = '🚑',
-- Values
value_changed = "📝",
value_definition = "🦕",
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
method = "ƒ ", -- "🍔", -- mac
["function"] = "", -- "🤣", -- Fun
parameter = "", -- Pi
associated = "🤝",
namespace = "🚀",
type = "",
field = "🏈"
var = '', -- "👹", -- Vampaire
method = 'ƒ ', -- "🍔", -- mac
['function'] = '', -- "🤣", -- Fun
parameter = '', -- Pi
associated = '🤝',
namespace = '🚀',
type = '',
field = '🏈',
module = '📦',
flag = '🎏',
},
treesitter_defult = "🌲"
}
treesitter_defult = '🌲',
doc_symbols = '',
},
}
vim.cmd("command! -nargs=0 LspLog call v:lua.open_lsp_log()")
vim.cmd("command! -nargs=0 LspRestart call v:lua.reload_lsp()")
M.deprecated = function(cfg)
if cfg.code_action_prompt then
warn('code_action_prompt moved to lsp.code_action')
end
if cfg.code_lens_action_prompt then
warn('code_lens_action_prompt moved to lsp.code_lens_action')
end
if cfg.lsp ~= nil and cfg.lsp.disable_format_ft ~= nil and cfg.lsp.disable_format_ft ~= {} then
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
end
local extend_config = function(opts)
opts = opts or {}
if next(opts) == nil then
return
end
if opts.debug then
_NgConfigValues.debug = opts.debug
end
-- enable logs
require('navigator.util').setup()
for key, value in pairs(opts) do
-- if _NgConfigValues[key] == nil then
-- error(string.format("[] Key %s not valid", key))
-- return
-- end
if type(_NgConfigValues[key]) == "table" then
for k, v in pairs(value) do
_NgConfigValues[key][k] = v
end
else
if _NgConfigValues[key] == nil then
warn(
string.format(
'[] Deprecated? Key %s is not in default setup, it could be incorrect to set to %s',
key,
vim.inspect(value)
)
)
_NgConfigValues[key] = value
-- return
else
if type(_NgConfigValues[key]) == 'table' then
if type(value) ~= 'table' then
info(
string.format(
'[] Reset type: Key %s setup value %s type %s , from %s',
key,
vim.inspect(value),
type(value),
vim.inspect(_NgConfigValues[key])
)
)
end
for k, v in pairs(value) do
if type(k) == 'number' then
-- replace all item in array
_NgConfigValues[key] = value
break
end
-- level 3
if type(_NgConfigValues[key][k]) == 'table' then
if type(v) == 'table' then
for k2, v2 in pairs(v) do
_NgConfigValues[key][k][k2] = v2
end
else
_NgConfigValues[key][k] = v
end
else
if _NgConfigValues[key][k] == nil then
if key == 'lsp' then
local lsp = require('navigator.lspclient.servers')
if not vim.tbl_contains(lsp or {}, k) and k ~= 'efm' and k ~= 'null-ls' then
info(string.format('[] extend LSP support for %s %s ', key, k))
end
elseif key == 'keymaps' then
info('keymap override' .. vim.inspect(v))
-- skip key check and allow mapping to handle that
else
warn(string.format('[] Key %s %s not valid', key, k))
end
-- return
end
_NgConfigValues[key][k] = v
end
end
else
_NgConfigValues[key] = value
end
end
end
if _NgConfigValues.sumneko_root_path or _NgConfigValues.sumneko_binary then
vim.notify("Please put sumneko setup in lsp['sumneko_lua']", vim.log.levels.WARN)
end
M.deprecated(opts)
end
M.config_values = function()
@ -86,26 +248,50 @@ M.config_values = function()
end
M.setup = function(cfg)
cfg = cfg or {}
extend_config(cfg)
-- local log = require"navigator.util".log
-- log(debug.traceback())
-- log(cfg, _NgConfigValues)
-- print("loading navigator")
local cmd_group = api.nvim_create_augroup('NGFtGroup', {})
api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
group = cmd_group,
pattern = '*',
callback = function()
require('navigator.lspclient.clients').on_filetype()
end,
})
require('navigator.lazyloader').init()
require('navigator.lspclient.clients').setup(_NgConfigValues)
require("navigator.lspclient.mapping").setup(_NgConfigValues)
require("navigator.reference")
require("navigator.definition")
require("navigator.hierarchy")
require("navigator.implementation")
-- log("navigator loader")
if _NgConfigValues.code_action_prompt.enable then
vim.cmd [[autocmd CursorHold,CursorHoldI * lua require'navigator.codeAction'.code_action_prompt()]]
require('navigator.reference')
require('navigator.definition')
require('navigator.hierarchy')
require('navigator.implementation')
cfg.lsp = cfg.lsp or _NgConfigValues.lsp
if _NgConfigValues.lsp.enable then
require('navigator.diagnostics').config(cfg.lsp.diagnostic)
end
-- vim.cmd("autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4")
if not _NgConfigValues.loaded then
vim.cmd([[autocmd FileType * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
_NgConfigValues.loaded = true
end
if _NgConfigValues.ts_fold == true then
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.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
return M

@ -1,38 +1,37 @@
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 partial = util.partial
local lsphelper = require "navigator.lspwrapper"
local cwd = vim.fn.getcwd(0)
local lsphelper = require('navigator.lspwrapper')
local cwd = vim.loop.cwd()
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 M = {}
local function call_hierarchy_handler(direction, err, api, result, _, _, error_message)
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(api, "dir", direction, "result", result, "err", err)
print("ERROR: " .. error_message)
log('hierarchy error', ctx, 'dir', direction, 'result', result, 'err', err)
vim.notify('ERROR: ' .. error_message, vim.lsp.log_levels.WARN)
return
end
-- local funcs = vim.lsp.util.locations_to_items(result)
-- log(funcs)
local items = {}
for _, call_hierarchy in pairs(result) do
local kind = ''
range = call_hierarchy.range
local range = call_hierarchy.range
local filename = assert(vim.uri_to_fname(call_hierarchy.uri))
local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1)
local bufnr = vim.uri_to_bufnr(call_hierarchy.uri)
local row = range.start.line
local line = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or {""})[1]
fn = ""
local line = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { '' })[1]
local fn = ''
if line ~= nil then
fn = line:sub(range.start.character, range['end'].character + 1)
end
@ -43,35 +42,36 @@ local function call_hierarchy_handler(direction, err, api, result, _, _, error_m
text = kind .. fn,
range = range,
lnum = range.start.line + 1,
col = range.start.character
col = range.start.character,
})
end
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_handler, 'from')
local call_hierarchy_handler_to = partial(call_hierarchy_handler, 'to')
local function incoming_calls_handler(bang, err, method, result, client_id, bufnr)
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
local results = call_hierarchy_handler_from(err, method, result, client_id, bufnr,
"Incoming calls not found")
local function incoming_calls_handler(_, 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 ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({items = results, ft = ft, api = ''})
end
local results = call_hierarchy_handler_from(err, result, ctx, cfg, 'Incoming calls not found')
local function outgoing_calls_handler(bang, err, method, result, client_id, bufnr)
local results = call_hierarchy_handler_to(err, method, result, client_id, bufnr,
"Outgoing calls not found")
local ft = vim.api.nvim_buf_get_option(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(_, 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(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
@ -83,16 +83,17 @@ function M.incoming_calls(bang, opts)
-- params['callee'] = true
log(params)
log(opts)
lsphelper.call_sync("$ccls/call", params, opts, partial(incoming_calls_handler, bang))
lsphelper.call_sync('$ccls/call', params, opts, partial(incoming_calls_handler, bang))
end
function M.outgoing_calls(bang, opts)
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
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
log(params)
lsphelper.call_sync("$ccls/call", params, opts, partial(outgoing_calls_handler, bang))
lsphelper.call_sync('$ccls/call', params, opts, partial(outgoing_calls_handler, bang))
end
M.incoming_calls_call = partial(M.incoming_calls, 0)

@ -1,76 +1,21 @@
local util = require "navigator.util"
local util = require('navigator.util')
local log = util.log
local trace = util.trace
local code_action = {}
local gui = require "navigator.gui"
local config = require("navigator").config_values()
-- local gui = require('navigator.gui')
local config = require('navigator').config_values()
local api = vim.api
function code_action.code_action_handler(err, _, actions, cid, bufnr, _, customSelectionHandler)
log(cid, bufnr)
if actions == nil or vim.tbl_isempty(actions) then
print("No code actions available")
return
end
local data = {"  Auto Fix <C-o> Apply <C-e> Exit"}
for i, action in ipairs(actions) do
local title = action.title:gsub("\r\n", "\\r\\n")
title = title:gsub("\n", "\\n")
title = string.format("[%d] %s", i, title)
table.insert(data, title)
end
local width = 0
for _, str in ipairs(data) do
if #str > width then
width = #str
end
end
local function apply_action(idx)
local action_chosen = actions[idx - 1]
local switch = string.format("silent b %d", bufnr)
if action_chosen.edit or type(action_chosen.command) == "table" then
if action_chosen.edit then
vim.lsp.util.apply_workspace_edit(action_chosen.edit)
end
if type(action_chosen.command) == "table" then
-- switch buff
vim.cmd(switch)
vim.lsp.buf.execute_command(action_chosen.command)
end
else
vim.cmd(switch)
vim.lsp.buf.execute_command(action_chosen)
end
local sign_name = 'NavigatorLightBulb'
trace(action_chosen)
end
--- `codeAction/resolve`
-- from neovim buf.lua, change vim.ui.select to gui
gui.new_list_view {
items = data,
width = width + 4,
loc = "top_center",
relative = "cursor",
rawdata = true,
data = data,
on_confirm = function(pos)
if pos < 2 then
pos = 2
end
apply_action(pos)
end,
on_move = function(pos)
if pos < 2 then
pos = 2
end
local l = data[pos]
return l
end
}
end
local diagnostic = vim.diagnostic or vim.lsp.diagnostic
-- https://github.com/glepnir/lspsaga.nvim/blob/main/lua/lspsaga/codeaction.lua
-- lspsaga has a clever design to inject code action indicator
local sign_group = "nvcodeaction"
local sign_group = 'nvcodeaction'
local get_namespace = function()
return api.nvim_create_namespace(sign_group)
end
@ -79,74 +24,83 @@ local get_current_winid = function()
return api.nvim_get_current_win()
end
local sign_name = "NavigatorLightBulb"
if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then
vim.fn.sign_define(sign_name, {text = config.icons.code_action_icon, texthl = "LspDiagnosticsSignHint"})
end
local function _update_virtual_text(line)
local function _update_virtual_text(line, actions)
local namespace = get_namespace()
pcall(api.nvim_buf_clear_namespace, 0, namespace, 0, -1)
if line then
local icon_with_indent = " " .. config.icons.code_action_icon
pcall(
api.nvim_buf_set_extmark,
0,
namespace,
line,
-1,
{
virt_text = {{icon_with_indent, "LspDiagnosticsSignHint"}},
virt_text_pos = "overlay",
hl_mode = "combine"
}
)
trace(line, actions)
local icon_with_indent = ' ' .. config.icons.code_action_icon
local title = actions[1].title
pcall(api.nvim_buf_set_extmark, 0, namespace, line, -1, {
virt_text = { { icon_with_indent .. title, 'LspDiagnosticsSignHint' } },
virt_text_pos = 'overlay',
hl_mode = 'combine',
})
end
end
local function _update_sign(line)
if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then
vim.fn.sign_define(sign_name, {
text = config.icons.code_action_icon,
texthl = 'LspDiagnosticsSignHint',
})
end
local winid = get_current_winid()
if code_action[winid] == nil then
code_action[winid] = {}
end
if code_action[winid].lightbulb_line ~= 0 then
vim.fn.sign_unplace(sign_group, {id = code_action[winid].lightbulb_line, buffer = "%"})
-- 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(
-- log("updatasign", line, sign_group, sign_name)
local id = vim.fn.sign_place(
line,
sign_group,
sign_name,
"%",
{lnum = line + 1, priority = config.code_action_prompt.sign_priority}
'%',
{ 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
local need_check_diagnostic = {
["go"] = true,
["python"] = true
}
-- local need_check_diagnostic = {["go"] = true, ["python"] = true}
local need_check_diagnostic = { ['python'] = true }
function code_action:render_action_virtual_text(line, diagnostics)
return function(_, _, actions)
if actions == nil or type(actions) ~= "table" or vim.tbl_isempty(actions) then
if config.code_action_prompt.virtual_text then
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
_update_virtual_text(nil)
end
if config.code_action_prompt.sign then
if config.lsp.code_action.sign then
_update_sign(nil)
end
else
if config.code_action_prompt.sign then
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
_update_sign(line)
@ -156,15 +110,15 @@ function code_action:render_action_virtual_text(line, diagnostics)
end
end
if config.code_action_prompt.virtual_text then
if config.lsp.code_action.virtual_text then
if need_check_diagnostic[vim.bo.filetype] then
if next(diagnostics) == nil then
_update_virtual_text(nil)
else
_update_virtual_text(line)
_update_virtual_text(line, actions)
end
else
_update_virtual_text(line)
_update_virtual_text(line, actions)
end
end
end
@ -172,47 +126,99 @@ function code_action:render_action_virtual_text(line, diagnostics)
end
local special_buffers = {
["LspSagaCodecode_action"] = true,
["lspsagafinder"] = true,
["NvimTree"] = true,
["vista"] = true,
["guihua"] = true,
["lspinfo"] = true,
["markdown"] = true,
["text"] = true
['lspsagafinder'] = true,
['NvimTree'] = true,
['vista'] = true,
['guihua'] = true,
['lspinfo'] = true,
['markdown'] = true,
['text'] = true,
}
-- local action_call_back = function (_,_)
-- return Action:action_callback()
-- end
local action_vritual_call_back = function(line, diagnostics)
local action_virtual_call_back = function(line, diagnostics)
return code_action:render_action_virtual_text(line, diagnostics)
end
local code_action_req = function(_call_back_fn, diagnostics)
local context = {diagnostics = diagnostics}
local context = { diagnostics = diagnostics }
local params = vim.lsp.util.make_range_params()
params.context = context
local line = params.range.start.line
local callback = _call_back_fn(line, diagnostics)
vim.lsp.buf_request(0, "textDocument/codeAction", params, callback)
vim.lsp.buf_request(0, 'textDocument/codeAction', params, callback)
end
-- code_action.code_action = function()
-- local diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
-- code_action_req(action_call_back, diagnostics)
-- end
local function sort_select(action_tuples, opts, on_user_choice)
if action_tuples ~= nil and action_tuples[1][2] ~= nil and action_tuples[1][2].command then
table.sort(action_tuples, function(a, b)
return a[1] > b[1]
end)
end
trace(action_tuples)
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 = sort_select
vim.lsp.buf.code_action()
vim.defer_fn(function()
vim.ui.select = original_select
end, 1000)
end
code_action.code_action_prompt = function()
code_action.range_code_action = function(startpos, endpos)
local context = {}
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
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
if vim.fn.has('nvim-0.8') then
vim.lsp.buf.code_action({context=context ,range={start = startpos, ['end'] = endpos}})
else
vim.lsp.buf.range_code_action(context, startpos, endpos)
end
vim.defer_fn(function()
vim.ui.select = original_select
vim.ui.input = original_input
end, 1000)
end
code_action.code_action_prompt = function(bufnr)
if special_buffers[vim.bo.filetype] then
log('skip buffer', vim.bo.filetype)
return
end
local diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
local diagnostics
if diagnostic.get_line_diagnostics then
-- old version
diagnostics = diagnostic.get_line_diagnostics()
else
local lnum = vim.api.nvim_win_get_cursor(0)[1] - 1
diagnostics = diagnostic.get(vim.api.nvim_get_current_buf(), { lnum = lnum })
end
local winid = get_current_winid()
code_action[winid] = code_action[winid] or {}
code_action[winid].lightbulb_line = code_action[winid].lightbulb_line or 0
code_action_req(action_vritual_call_back, diagnostics)
code_action_req(action_virtual_call_back, diagnostics)
end
return code_action

@ -0,0 +1,144 @@
-- codelenses
-- https://github.com/josa42/nvim-lsp-codelenses/blob/master/lua/jg/lsp/codelenses.lua
-- https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/codelens.lua
local codelens = require('vim.lsp.codelens')
local log = require('navigator.util').log
local trace = require('navigator.util').trace
-- trace = log
local lsphelper = require('navigator.lspwrapper')
local api = vim.api
local M = {}
local config = require('navigator').config_values()
local sign_name = 'NavigatorCodeLensLightBulb'
if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then
vim.fn.sign_define(sign_name, { text = config.icons.code_lens_action_icon, texthl = 'LspDiagnosticsSignHint' })
end
local sign_group = 'nvcodelensaction'
local get_current_winid = require('navigator.util').get_current_winid
local is_enabled = true
local code_lens_action = {}
local function _update_sign(line)
trace('update sign at line ', line)
local winid = get_current_winid()
if code_lens_action[winid] == nil then
code_lens_action[winid] = {}
end
if code_lens_action[winid].lightbulb_line ~= 0 then
vim.fn.sign_unplace(sign_group, { id = code_lens_action[winid].lightbulb_line, buffer = '%' })
end
if line then
-- log("updatasign", line, sign_group, sign_name)
vim.fn.sign_place(
line,
sign_group,
sign_name,
'%',
{ lnum = line + 1, priority = config.lsp.code_lens_action.sign_priority }
)
code_lens_action[winid].lightbulb_line = line
end
end
local codelens_hdlr = function(err, result, ctx, cfg)
trace(ctx, result)
M.codelens_ctx = ctx
if err or result == nil then
if err then
log('lsp code lens', vim.inspect(err), ctx, cfg)
end
return
end
trace('codelenes result', result)
for _, v in pairs(result) do
_update_sign(v.range.start.line)
end
end
function M.setup(bufnr)
log('setup for ****** ', bufnr)
vim.api.nvim_set_hl(0, 'LspCodeLens', { link = 'DiagnosticsHint', default = true })
vim.api.nvim_set_hl(0, 'LspCodeLensText', { link = 'DiagnosticsInformation', default = true })
vim.api.nvim_set_hl(0, 'LspCodeLensSign', { link = 'DiagnosticsInformation', default = true })
vim.api.nvim_set_hl(0, 'LspCodeLensSeparator', { link = 'Boolean', default = true })
vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI', 'InsertLeave' }, {
group = vim.api.nvim_create_augroup('nv__codelenses', {}),
buffer = bufnr or vim.api.nvim_win_get_buf(),
callback = function()
require('navigator.codelens').refresh()
end,
})
end
M.lsp_clients = {}
function M.refresh()
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('codeLensProvider') then
return
end
M.inline()
end
local virtual_types_ns = api.nvim_create_namespace('ng_virtual_types')
function M.disable()
local bufnr = vim.api.nvim_get_current_buf()
vim.api.nvim_buf_clear_namespace(bufnr, virtual_types_ns, 0, -1)
is_enabled = false
end
function M.run_action()
local original_select = vim.ui.select
vim.ui.select = require('guihua.gui').select
log('codelens action')
codelens.run()
vim.defer_fn(function()
vim.ui.select = original_select
end, 1000)
end
M.inline = function()
local lsp = vim.lsp
if is_enabled == false then
return
end
if vim.fn.getcmdwintype() == ':' then
return
end
if next(vim.lsp.buf_get_clients(0)) == nil then
return
end
local bufnr = api.nvim_get_current_buf()
local parameter = lsp.util.make_position_params()
local on_codelens = vim.lsp.handlers['textDocument/codeLens']
lsp.buf_request(bufnr, 'textDocument/codeLens', parameter, function(err, response, ctx, _)
-- Clear previous highlighting
api.nvim_buf_clear_namespace(bufnr, virtual_types_ns, 0, -1)
if response then
trace(response)
on_codelens(err, response, ctx, _)
codelens_hdlr (err, response, ctx, _)
end
end)
end
return M

@ -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(vim.api.nvim_get_option('columns') * width)
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 = 'GuihuaListDark',
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,
}

@ -0,0 +1,29 @@
local M = {}
function M.debounce_trailing(ms, fn)
local timer = vim.loop.new_timer()
return function(...)
local argv = {...}
timer:start(ms, 0, function()
timer:stop()
fn(unpack(argv))
end)
end
end
function M.throttle_leading(ms, fn)
local timer = vim.loop.new_timer()
local running = false
return function(...)
if not running then
timer:start(ms, 0, function()
running = false
timer:stop()
end)
running = true
fn(...)
end
end
end
return M

@ -1,33 +1,40 @@
local util = require "navigator.util"
local lsphelper = require "navigator.lspwrapper"
local util = require('navigator.util')
local lsphelper = require('navigator.lspwrapper')
local locations_to_items = lsphelper.locations_to_items
local gui = require "navigator.gui"
local gui = require('navigator.gui')
local log = util.log
local TextView = require("guihua.textview")
local trace = util.trace
local TextView = require('guihua.textview')
-- callback for lsp definition, implementation and declaration handler
local function definition_hdlr(err, _, locations, _, bufnr)
local definition_hdlr = function(err, locations, ctx, _)
-- log(locations)
if err ~= nil then
print(err)
vim.notify('Defination: ' .. tostring(err) .. vim.inspect(ctx), vim.lsp.log_levels.WARN)
return
end
if type(locations) == "number" then
if type(locations) == 'number' then
log(locations)
err("unable to handle request")
log('unable to handle request')
end
if locations == nil or vim.tbl_isempty(locations) then
print "Definition not found"
vim.notify('Definition not found')
return
end
local oe = require('navigator.util').encoding(ctx.client_id)
locations = util.dedup(locations)
log(locations)
log("found " .. #locations .. " locations")
if vim.tbl_islist(locations) then
if #locations > 1 then
local items = locations_to_items(locations)
gui.new_list_view({items = items, api = 'Definition'})
gui.new_list_view({ items = items, api = 'Definition' })
else
vim.lsp.util.jump_to_location(locations[1])
vim.lsp.util.jump_to_location(locations[1], oe)
end
else
vim.lsp.util.jump_to_location(locations)
vim.lsp.util.jump_to_location(locations, oe)
end
end
@ -37,13 +44,13 @@ local function get_symbol()
end
local function def_preview(timeout_ms)
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running")
local method = "textDocument/definition"
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)
if result == nil or vim.tbl_isempty(result) then
print("No result found: " .. method)
vim.notify('No result found: ' .. method, vim.lsp.log_levels.WARN)
return nil
end
@ -52,16 +59,22 @@ local function def_preview(timeout_ms)
-- result = {vim.tbl_deep_extend("force", {}, unpack(result))}
-- log("def-preview", result)
for key, value in pairs(result) do
if result[key] ~= nil then
if result[key] ~= nil and not vim.tbl_isempty(result[key]) then
table.insert(data, value.result[1])
end
end
local range = data[1].targetRange or data[1].range
if vim.tbl_isempty(data) then
vim.notify('No result found: ' .. method, vim.lsp.log_levels.WARN)
return nil
end
local range = data[1].targetRange or data[1].range or data[1].targetSelectionRange
local row = range.start.line
-- in case there are comments
row = math.max(row - 3, 1)
local delta = range.start.line - row + 1
local delta = range.start.line - row + 3
local uri = data[1].uri or data[1].targetUri
if not uri then
return
@ -70,8 +83,30 @@ local function def_preview(timeout_ms)
if not vim.api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
-- TODO: 12 should be an option
local definition = vim.api.nvim_buf_get_lines(bufnr, row, range["end"].line + 12, false)
local ok, parsers = pcall(require, 'nvim-treesitter.parsers')
-- TODO: 32/64 should be an option
local lines_num = 64
if range['end'] ~= nil then
lines_num = math.max(lines_num, range['end'].line - range.start.line + 4)
end
if ok then
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)
log(sr, er)
lines_num = math.max(lines_num, er - sr + 5) -- comments etc
end
-- TODO: 32 should be an option
local definition = vim.api.nvim_buf_get_lines(bufnr, row, range['end'].line + lines_num, false)
local def_line = vim.api.nvim_buf_get_lines(bufnr, range.start.line, range.start.line + 1, false)
for _ = 1, math.min(3, #definition), 1 do
if #definition[1] < 2 then
@ -82,43 +117,59 @@ local function def_preview(timeout_ms)
end
end
local width = 40
for key, value in pairs(definition) do
log(key, value, width)
width = math.max(width, #value)
width = math.min(90, width)
local maxwidth = math.floor( vim.api.nvim_get_option('columns') * 0.8)
for _, value in pairs(definition) do
-- log(key, value, width)
width = math.max(width, #value + 4)
width = math.min(maxwidth, width)
end
definition = vim.list_extend({"  [" .. get_symbol() .. "] Definition: "}, definition)
local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
definition = vim.list_extend({ '  [' .. get_symbol() .. '] Definition: ' }, definition)
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype')
-- TODO multiple resuts?
local opts = {
relative = "cursor",
style = "minimal",
relative = 'cursor',
style = 'minimal',
ft = filetype,
width = width,
rect = { width = width, height = math.min(#definition + 3, 16), pos_y = 2 }, -- TODO: 16 hardcoded
data = definition,
enter = true
enter = true,
border = _NgConfigValues.border or 'shadow',
}
TextView:new(opts)
delta = delta + 1 -- header
local cmd = "normal! " .. tostring(delta) .. "G"
local cmd = 'normal! ' .. tostring(delta) .. 'G'
vim.cmd(cmd)
vim.cmd('set cursorline')
if #def_line > 0 then
local niddle = require('guihua.util').add_escape(def_line[1])
-- log(def_line[1], niddle)
vim.fn.matchadd("Search", niddle)
vim.fn.matchadd('Search', niddle)
end
-- TODO:
-- https://github.com/oblitum/goyo.vim/blob/master/autoload/goyo.vim#L108-L135
end
vim.lsp.handlers["textDocument/definition"] = definition_hdlr
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, _, _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)
end
vim.lsp.handlers['textDocument/definition'] = definition_hdlr
return {
definition = def,
definition_handler = definition_hdlr,
definition_preview = def_preview,
declaration_handler = definition_hdlr,
typeDefinition_handler = definition_hdlr
typeDefinition_handler = definition_hdlr,
}

@ -1,31 +1,222 @@
local gui = require "navigator.gui"
local gui = require('navigator.gui')
local diagnostic_list = {}
local util = require "navigator.util"
local diagnostic = vim.diagnostic or vim.lsp.diagnostic
-- local hide = diagnostic.hide or diagnostic.clear
local util = require('navigator.util')
local log = util.log
local trace = require('guihua.log').trace
-- trace = log
local error = util.error
local path_sep = require('navigator.util').path_sep()
local path_cur = require('navigator.util').path_cur()
local empty = util.empty
local api = vim.api
_NG_VT_DIAG_NS = api.nvim_create_namespace('navigator_lua_diag')
local path_sep = require"navigator.util".path_sep()
local path_cur = require"navigator.util".path_cur()
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_hdlr = function(err, method, result, client_id, br, config)
-- log(result)
vim.lsp.diagnostic.on_publish_diagnostics(err, method, result, client_id, br, config)
local diag_map = {}
if vim.diagnostic then
diag_map = {
Error = vim.diagnostic.severity.ERROR,
Warning = vim.diagnostic.severity.WARN,
Info = vim.diagnostic.severity.Info,
Hint = vim.diagnostic.severity.Hint,
}
end
local diagnostic_cfg
local function get_count(bufnr, level)
if vim.diagnostic ~= nil then
return #diagnostic.get(bufnr, { severity = diag_map[level] })
else
return diagnostic.get_count(bufnr, level)
end
end
local function error_marker(result, ctx, config)
if
_NgConfigValues.lsp.diagnostic_scrollbar_sign == nil
or empty(_NgConfigValues.lsp.diagnostic_scrollbar_sign)
or _NgConfigValues.lsp.diagnostic_scrollbar_sign == false
then -- not enabled or already shown
return
end
vim.defer_fn(function()
if vim.tbl_isempty(result.diagnostics) then
return
end
local first_line = vim.fn.line('w0')
-- local rootfolder = vim.fn.expand('%:h:t') -- get the current file root folder
local bufnr = ctx.bufnr
if bufnr == nil then
bufnr = vim.uri_to_bufnr(result.uri)
end
local success, fname = pcall(api.nvim_buf_get_name, bufnr)
if not success then
return
end
local uri = vim.uri_from_fname(fname)
if uri ~= result.uri then
log('not same buf', ctx, result.uri, bufnr, vim.fn.bufnr())
return
end
if not api.nvim_buf_is_loaded(bufnr) then
trace('buf not loaded', bufnr)
return
end
trace('schedule callback', result, ctx, config)
trace('total diag ', #result.diagnostics, bufnr)
if result == nil or result.diagnostics == nil or #result.diagnostics == 0 then
local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]])
if diag_cnt == 0 and _NG_VT_DIAG_NS ~= nil then
log('great no errors')
api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
end
return
end
-- total line num of current buffer
-- local winid = vim.fn.win_getid(vim.fn.winnr())
-- local winid = api.nvim_get_current_win()
local total_num = api.nvim_buf_line_count(bufnr)
-- local total_num = vim.fn.getbufinfo(vim.fn.winbufnr(winid))[1].linecount
-- window size of current buffer
local stats = api.nvim_list_uis()[1]
-- local wwidth = stats.width;
local wheight = stats.height
if total_num <= wheight then
return
end
if _NG_VT_DIAG_NS == nil then
_NG_VT_DIAG_NS = api.nvim_create_namespace('navigator_lua_diag')
end
local pos = {}
local diags = result.diagnostics
for i, _ in ipairs(diags) do
if not diags[i].range then
diags[i].range = { start = { line = diags[i].lnum } }
end
end
table.sort(diags, function(a, b)
return a.range.start.line < b.range.start.line
end)
-- pos of virtual text
for _, diag in pairs(result.diagnostics) do
local p
if not diag.range then
diag.range = { start = { line = diag.lnum } }
end
if diag.range and diag.range.start and diag.range.start.line then
p = diag.range.start.line
p = util.round(p * wheight / math.max(wheight, total_num))
if pos[#pos] and pos[#pos].line == p then
local bar = _NgConfigValues.lsp.diagnostic_scrollbar_sign[2]
if pos[#pos] == bar then
bar = _NgConfigValues.lsp.diagnostic_scrollbar_sign[3]
end
pos[#pos] = { line = p, sign = bar, severity = math.min(diag.severity, pos[#pos].severity) }
else
table.insert(pos, {
line = p,
sign = _NgConfigValues.lsp.diagnostic_scrollbar_sign[1],
severity = diag.severity,
})
end
end
trace('pos, line:', p, diag.severity, diag.range)
end
if not vim.tbl_isempty(pos) then
api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
end
for _, s in pairs(pos) do
local hl = 'ErrorMsg'
if type(s.severity) == 'number' then
if s.severity == 2 then
hl = 'WarningMsg'
elseif s.severity >= 3 then
hl = 'DiagnosticInfo'
end
elseif type(s.severity) == 'string' then
if s.severity:lower() == 'warn' then
hl = 'WarningMsg'
end
end
local l = s.line + first_line
if l > total_num then
l = total_num
end
trace('add pos', s, bufnr)
api.nvim_buf_set_extmark(
bufnr,
_NG_VT_DIAG_NS,
l,
-1,
{ virt_text = { { s.sign, hl } }, virt_text_pos = 'right_align' }
)
end
end, 10) -- defer in 10ms
end
local update_err_marker_async = function()
local debounce = require('navigator.debounce').debounce_trailing
return debounce(400, error_marker)
end
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)
log(err, config, result)
return
end
local mode = api.nvim_get_mode().mode
if mode ~= 'n' and config.update_in_insert == false then
trace('skip sign update in insert mode')
end
local cwd = vim.fn.getcwd(0)
local cwd = vim.loop.cwd()
local ft = vim.bo.filetype
if diagnostic_list[ft] == nil then
diagnostic_list[vim.bo.filetype] = {}
end
-- vim.lsp.diagnostic.clear(vim.fn.bufnr(), client.id, nil, nil)
local client_id = ctx.client_id
local bufnr = ctx.bufnr or 0
if result.diagnostics ~= nil and result.diagnostics ~= {} then
trace('diagnostic', result.diagnostics, ctx, 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
trace('no result? ', diag_cnt)
return
end
-- trace("diag: ", mode, result, ctx, config)
if result and result.diagnostics then
local item_list = {}
for _, v in ipairs(result.diagnostics) do
local item = v
item.filename = assert(vim.uri_to_fname(uri))
@ -33,81 +224,334 @@ local diag_hdlr = function(err, method, result, client_id, br, config)
item.lnum = v.range.start.line + 1
item.col = v.range.start.character + 1
item.uri = uri
-- trace(item)
local head = _NgConfigValues.icons.diagnostic_head
if v.severity == 1 then
head = _NgConfigValues.icons.diagnostic_head_severity_1
if v.severity then
if v.severity == 1 then
head = _NgConfigValues.icons.diagnostic_head_severity_1
end
if v.severity == 2 then
head = _NgConfigValues.icons.diagnostic_head_severity_2
end
if v.severity > 2 then
head = _NgConfigValues.icons.diagnostic_head_severity_3
end
else
v.severity = 2
end
if v.severity == 2 then
head = _NgConfigValues.icons.diagnostic_head_severity_2
if v.relatedInformation and v.relatedInformation[1] then
local info = v.relatedInformation[1]
-- trace(info)
if info.message then
v.releated_msg = info.message
end
if info.location and info.location.range then
v.releated_lnum = info.location.range.start.line
end
end
if v.severity > 2 then
head = _NgConfigValues.icons.diagnostic_head_severity_3
local bufnr1 = vim.uri_to_bufnr(uri)
local loaded = api.nvim_buf_is_loaded(bufnr1)
if _NgConfigValues.diagnostic_load_files then
-- print('load buffers')
if not loaded then
vim.fn.bufload(bufnr1) -- this may slow down the neovim
end
local pos = v.range.start
local row = pos.line
local line = (api.nvim_buf_get_lines(bufnr1, row, row + 1, false) or { '' })[1]
if line ~= nil then
item.text = head .. line .. _NgConfigValues.icons.diagnostic_head_description .. v.message
else
error('diagnostic result empty line', v, row, bufnr1)
end
else
item.text = head .. _NgConfigValues.icons.diagnostic_head_description .. v.message
end
local bufnr = vim.uri_to_bufnr(uri)
if not vim.api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
if v.releated_msg then
item.text = item.text .. '; ' .. item.releated_msg
end
local pos = v.range.start
local row = pos.line
local line = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or {""})[1]
if line ~= nil then
item.text = head .. line .. _NgConfigValues.icons.diagnostic_head_description .. v.message
table.insert(item_list, item)
else
error("diagnostic result empty line", v, item)
if v.releated_lnum then
item.text = item.text .. ':' .. tostring(item.releated_lnum)
end
table.insert(item_list, item)
end
-- local old_items = vim.fn.getqflist()
diagnostic_list[ft][uri] = item_list
if diagnostic_list[ft][uri] == nil then
diagnostic_list[ft][uri] = {}
end
diagnostic_list[ft][uri][tostring(client_id)] = item_list
trace(uri, ft, diagnostic_list)
if not result.uri then
result.uri = uri
end
local marker = update_err_marker_async()
marker(result, ctx, config)
else
trace('great, no diag errors')
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 M = {}
-- vim.lsp.handlers["textDocument/publishDiagnostics"] =
M.diagnostic_handler = vim.lsp.with(diag_hdlr, {
-- Enable underline, use default values
underline = true,
-- Enable virtual text, override spacing to 0
virtual_text = {spacing = 0, prefix = _NgConfigValues.icons.diagnostic_virtual_text},
-- Use a function to dynamically turn signs off
-- and on, using buffer local variables
signs = true,
-- Disable a feature
update_in_insert = false
})
M.show_diagnostic = function()
vim.lsp.diagnostic.get_all()
local bufs = vim.api.nvim_list_bufs()
for _, buf in ipairs(bufs) do
local bname = vim.fn.bufname(buf)
if #bname > 0 and not util.exclude(bname) then
if vim.api.nvim_buf_is_loaded(buf) then
vim.lsp.diagnostic.get(buf, nil)
end
end
function M.setup()
if diagnostic_cfg ~= nil and diagnostic_cfg.float ~= nil then
return
end
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 = '',
},
}
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)
if _NgConfigValues.lsp.diagnostic_scrollbar_sign then
api.nvim_create_autocmd({ 'WinScrolled' }, {
group = api.nvim_create_augroup('NGWinScrolledGroup', {}),
pattern = '*',
callback = function()
require('navigator.diagnostics').update_err_marker()
end,
})
end
end
local function clear_diag_VT(bufnr) -- important for clearing out when no more errors
bufnr = bufnr or api.nvim_get_current_buf()
log(bufnr, _NG_VT_DIAG_NS)
if _NG_VT_DIAG_NS == nil then
return
end
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
clear_diag_VT()
end
end
M.toggle_diagnostics = function()
if M.diagnostic_enabled then
M.diagnostic_enabled = false
return vim.diagnostic.disable()
end
vim.diagnostic.enable()
M.diagnostic_enabled = true
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 _, items in pairs(results) do
for _, it in pairs(items) do
table.insert(display_items, it)
for _, client_items in pairs(results) do
for _, items in pairs(client_items) do
for _, it in pairs(items) do
table.insert(display_items, it)
end
end
end
-- log(display_items)
if #display_items > 0 then
gui.new_list_view({
local listview = gui.new_list_view({
items = display_items,
api = _NgConfigValues.icons.diagnostic_file .. _NgConfigValues.icons.diagnostic_head
.. " Diagnostic ",
enable_preview_edit = true
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)
if listview.bufnr then
api.nvim_buf_add_highlight(listview.bufnr, -1, 'Title', 0, 0, -1)
end
end
end
end
-- set loc list win
M.set_diag_loclist = function(bufnr)
bufnr = bufnr or api.nvim_get_current_buf()
local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]])
if diag_cnt == 0 then
log('great, no errors!')
return
end
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(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
diagnostic.set_loclist(cfg)
else
cfg.namespaces = diagnostic.get_namespaces()
diagnostic.setloclist(cfg)
end
else
vim.cmd('lclose')
end
end
end
-- TODO: callback when scroll
function M.update_err_marker()
trace('update err marker', _NG_VT_DIAG_NS)
if _NG_VT_DIAG_NS == nil then
-- nothing to update
return
end
local bufnr = api.nvim_get_current_buf()
local diag_cnt = get_count(bufnr, [[Error]])
+ get_count(bufnr, [[Warning]])
+ get_count(bufnr, [[Info]])
+ get_count(bufnr, [[Hint]])
-- redraw
if diag_cnt == 0 and _NG_VT_DIAG_NS ~= nil then
api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
trace('no errors')
return
end
api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
local errors = diagnostic.get(bufnr)
if #errors == 0 then
trace('no errors', errors)
return
end
local uri = vim.uri_from_bufnr(bufnr)
local result = { diagnostics = errors, uri = errors[1].uri or uri }
trace(result)
local marker = update_err_marker_async()
marker(result, { bufnr = bufnr, method = 'textDocument/publishDiagnostics' })
end
function M.get_line_diagnostic()
local lnum = api.nvim_win_get_cursor(0)[1] - 1
local diags = diagnostic.get(api.nvim_get_current_buf(), { lnum = lnum })
table.sort(diags, function(diag1, diag2)
return diag1.severity < diag2.severity
end)
return diags
end
function M.show_diagnostics(pos)
local bufnr = api.nvim_get_current_buf()
local lnum, col = unpack(api.nvim_win_get_cursor(0))
lnum = lnum - 1
local opt = { border = 'single', severity_sort = true }
if pos ~= nil and type(pos) == 'number' then
opt.scope = 'buffer'
else
if pos == true then
opt.scope = 'cursor'
else
opt.scope = 'line'
end
end
local diags = M.get_line_diagnostic()
if diags == nil or next(diags) == nil then
return
end
local diag1 = diags[1]
opt.offset_x = -1 * (col - diag1.col)
diagnostic.open_float(bufnr, opt)
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 = 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(buf)
log(buf, diagnostic)
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)
M.setup()
cfg = cfg or {}
log('diag config', cfg)
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

@ -1,16 +1,15 @@
local util = require "navigator.util"
local util = require('navigator.util')
local log = util.log
local trace = util.trace
local api = vim.api
local references = {}
_NG_hi_list = {}
_NG_current_symbol = ""
_NG_current_symbol = ''
_NG_ref_hi_idx = 1
-- extract symbol from cursor
local function get_symbol()
local currentWord = vim.fn.expand('<cword>')
return currentWord
return vim.fn.expand('<cword>')
end
local function add_locs(bufnr, result)
@ -18,17 +17,16 @@ local function add_locs(bufnr, result)
if #result < 1 then
return
end
symbol = string.format("%s_%i_%i_%i", symbol, bufnr, result[1].range.start.line,
result[1].range.start.character)
symbol = string.format('%s_%i_%i_%i', symbol, bufnr, result[1].range.start.line, result[1].range.start.character)
if _NG_hi_list[symbol] == nil then
_NG_hi_list[symbol] = {range = {}}
_NG_hi_list[symbol] = { range = {} }
end
if _NG_hi_list[symbol] ~= nil then
trace("already added", symbol)
trace('already added', symbol)
_NG_hi_list[symbol].range = {}
-- vim.fn.matchdelete(hid)
end
trace("add ", symbol)
trace('add ', symbol)
_NG_hi_list[symbol].range = result
_NG_current_symbol = symbol
end
@ -37,7 +35,7 @@ local function nohl()
for key, value in pairs(_NG_hi_list) do
if value.hi_ids ~= nil then
for _, v in ipairs(value.hi_ids) do
trace("delete", v)
trace('delete', v)
vim.fn.matchdelete(v)
end
_NG_hi_list[key].hi_ids = nil
@ -52,8 +50,8 @@ local function hi_symbol()
vim.lsp.buf.document_highlight()
symbol = _NG_current_symbol
end
if symbol == nil or symbol == "" then
log("nil symbol")
if symbol == nil or symbol == '' then
log('nil symbol')
return
end
@ -65,7 +63,7 @@ local function hi_symbol()
local range = _NG_hi_list[symbol].range or {}
if _NG_hi_list[symbol].hi_ids ~= nil then
for _, value in ipairs(_NG_hi_list[symbol].hi_ids) do
log("delete", value)
log('delete', value)
vim.fn.matchdelete(value)
end
_NG_hi_list[symbol].hi_ids = nil
@ -74,23 +72,23 @@ local function hi_symbol()
local cur_pos = vim.fn.getpos('.')
_NG_hi_list[symbol].hi_ids = {}
local totalref = #range
local cmd = string.format("%s/\\<%s\\>//gn", "%s", symbol_wd)
local cmd = string.format('%s/\\<%s\\>//gn', '%s', symbol_wd)
local total_match = 0
local match_result = vim.api.nvim_exec(cmd, true)
local p = match_result:find(" match")
vim.cmd("nohl")
local p = match_result:find(' match')
vim.cmd('nohl')
vim.fn.setpos('.', cur_pos)
if p ~= nil then
p = match_result:sub(1, p)
total_match = tonumber(p)
end
if total_match == totalref then -- same number as matchpos
trace(total_match, "use matchadd()")
trace(total_match, 'use matchadd()')
local k = range[1].kind
local hi_name = string.format("NGHiReference_%i_%i", _NG_ref_hi_idx, k)
local m = string.format("\\<%s\\>", symbol_wd)
local hi_name = string.format('NGHiReference_%i_%i', _NG_ref_hi_idx, k)
local m = string.format('\\<%s\\>', symbol_wd)
local r = vim.fn.matchadd(hi_name, m, 20)
trace("hi id", m, hi_name, r)
trace('hi id', m, hi_name, r)
table.insert(_NG_hi_list[symbol].hi_ids, r)
--
-- vim.fn.matchdelete(r)
@ -106,9 +104,9 @@ local function hi_symbol()
l = el
end
local w = value.range['end'].character - value.range.start.character
local hi_name = string.format("NGHiReference_%i_%i", _NG_ref_hi_idx, k)
trace(hi_name, {l, cs, w})
local m = vim.fn.matchaddpos(hi_name, {{l, cs, w}}, 10)
local hi_name = string.format('NGHiReference_%i_%i', _NG_ref_hi_idx, k)
trace(hi_name, { l, cs, w })
local m = vim.fn.matchaddpos(hi_name, { { l, cs, w } }, 10)
table.insert(_NG_hi_list[symbol].hi_ids, m)
end
end
@ -125,6 +123,9 @@ end
-- returns r1 < r2 based on start of range
local function before(r1, r2)
if not r1 or not r2 then
return false
end
if r1.start.line < r2.start.line then
return true
end
@ -137,24 +138,28 @@ local function before(r1, r2)
return false
end
local function handle_document_highlight(_, _, result, _, bufnr, _)
if not bufnr then
local handle_document_highlight = function(_, result, ctx)
trace(result, ctx)
if not ctx.bufnr then
log('ducment highlight error', result, ctx)
return
end
if type(result) ~= "table" then
vim.lsp.util.buf_clear_references(bufnr)
if type(result) ~= 'table' or vim.fn.empty(result) == 1 then
vim.lsp.util.buf_clear_references(ctx.bufnr)
return
end
table.sort(result, function(a, b)
return before(a.range, b.range)
end)
references[bufnr] = result
references[ctx.bufnr] = result
local client_id = ctx.client_id
vim.lsp.util.buf_highlight_references(ctx.bufnr, result, util.encoding(client_id))
end
-- modify from vim-illuminate
local function goto_adjent_reference(opt)
trace(opt)
opt = vim.tbl_extend("force", {forward = true, wrap = true}, opt or {})
opt = vim.tbl_extend('force', { forward = true, wrap = true }, opt or {})
local bufnr = vim.api.nvim_get_current_buf()
local refs = references[bufnr]
@ -165,7 +170,7 @@ local function goto_adjent_reference(opt)
local next = nil
local nexti = nil
local crow, ccol = unpack(vim.api.nvim_win_get_cursor(0))
local crange = {start = {line = crow - 1, character = ccol}}
local crange = { start = { line = crow - 1, character = ccol } }
for i, ref in ipairs(refs) do
local range = ref.range
@ -188,7 +193,7 @@ local function goto_adjent_reference(opt)
end
trace(next)
vim.api.nvim_win_set_cursor(0, {next.start.line + 1, next.start.character})
vim.api.nvim_win_set_cursor(0, { next.start.line + 1, next.start.character })
return next
end
@ -199,43 +204,66 @@ local function cmd_nohl()
end
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! * <buffer>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
augroup END
]], false)
vim.lsp.handlers["textDocument/documentHighlight"] =
function(err, _, result, _, bufnr)
if err then
print(err)
return
end
if not result then
return
end
trace("dochl", result)
bufnr = api.nvim_get_current_buf()
local nav_doc_hl = function(bufnr)
trace('nav_doc_hl', bufnr)
bufnr = bufnr or vim.api.nvim_get_current_buf()
local ref_params = vim.lsp.util.make_position_params()
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _)
if client.server_capabilities.documentHighlightProvider == true then
trace('sending doc highlight', client.name, bufnr)
client.request('textDocument/documentHighlight', ref_params, handle_document_highlight, bufnr)
end
end)
end
local function documentHighlight(bufnr)
bufnr = bufnr or api.nvim_get_current_buf()
if _NgConfigValues.lsp.document_highlight == true then
local group_name = string.format('%s%d', 'NGHiGroup', bufnr)
local cmd_group = api.nvim_create_augroup(group_name, {})
api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
group = cmd_group,
buffer = bufnr,
desc = 'document highlight',
callback = function()
require('navigator.dochighlight').nav_doc_hl(bufnr)
end,
})
api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
group = cmd_group,
buffer = bufnr,
desc = 'clear document highlight',
callback = function()
vim.lsp.util.buf_clear_references(bufnr)
vim.lsp.util.buf_highlight_references(bufnr, result)
bufnr = bufnr or 0
if type(result) ~= "table" then
vim.lsp.util.buf_clear_references(bufnr)
return
end
table.sort(result, function(a, b)
return before(a.range, b.range)
end)
references[bufnr] = result
add_locs(bufnr, result)
end
end,
})
end
vim.lsp.handlers['textDocument/documentHighlight'] = function(err, result, ctx)
local buffer = ctx.bufnr or api.nvim_get_current_buf()
if err then
vim.notify(err, vim.lsp.log_levels.ERROR)
return
end
if not result or not result[1] or not result[1]['range'] then
return
end
trace('dochl', result)
if type(result) ~= 'table' then
vim.lsp.util.buf_clear_references(buffer)
return
end
local client_id = ctx.client_id
vim.lsp.util.buf_clear_references(buffer)
vim.lsp.util.buf_highlight_references(buffer, result, util.encoding(client_id))
table.sort(result, function(a, b)
return before(a.range, b.range)
end)
references[buffer] = result
add_locs(buffer, result)
end
end
return {
@ -244,5 +272,6 @@ return {
handle_document_highlight = handle_document_highlight,
hi_symbol = hi_symbol,
nohl = nohl,
cmd_nohl = cmd_nohl
nav_doc_hl = nav_doc_hl,
cmd_nohl = cmd_nohl,
}

@ -0,0 +1,163 @@
local log = require('navigator.util').log
local lsp = vim.lsp
local api = vim.api
local M = {}
-- TODO: per-buffer fold table?
M.current_buf_folds = {}
-- Informative table keeping track of language servers that implement textDocument/foldingRange.
-- Not used at runtime (capability is resolved dynamically)
M.servers_supporting_folding = {
pylsp = true,
pyright = false,
sumneko_lua = true,
texlab = true,
clangd = false,
gopls = true,
julials = false,
}
M.active_folding_clients = {}
function M.on_attach()
M.setup_plugin()
M.update_folds()
end
function M.setup_plugin()
local cmd_group = api.nvim_create_augroup('NGFoldGroup', {})
api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost' }, {
group = cmd_group,
pattern = '*',
callback = function()
require('navigator.foldlsp').update_folds()
end,
})
local clients = vim.lsp.buf_get_clients(0)
for _, client in pairs(clients) do
local client_id = client['id']
if M.active_folding_clients[client_id] == nil then
local server_supports_folding = client['server_capabilities']['foldingRangeProvider'] or false
-- if not server_supports_folding then
-- api.nvim_command(string.format('echom "lsp-folding: %s does not provide folding requests"',
-- client['name']))
-- end
M.active_folding_clients[client_id] = server_supports_folding
end
end
end
function M.update_folds()
local current_window = api.nvim_get_current_win()
local in_diff_mode = api.nvim_win_get_option(current_window, 'diff')
if in_diff_mode then
-- In diff mode, use diff folding.
api.nvim_win_set_option(current_window, 'foldmethod', 'diff')
else
local clients = lsp.buf_get_clients(0)
for client_id, client in pairs(clients) do
if M.active_folding_clients[client_id] then
-- 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)
end
end
end
end
function M.debug_folds()
for _, table in ipairs(M.current_buf_folds) do
local start_line = table['startLine']
local end_line = table['endLine']
log('startline', start_line, 'endline', end_line)
end
end
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)
return
end
M.debug_folds()
local current_bufnr = api.nvim_get_current_buf()
-- Discard the folding result if buffer focus has changed since the request was
-- done.
if current_bufnr == ctx.bufnr then
for _, fold in ipairs(result) do
fold['startLine'] = M.adjust_foldstart(fold['startLine'])
fold['endLine'] = M.adjust_foldend(fold['endLine'])
end
table.sort(result, function(a, b)
return a['startLine'] < b['startLine']
end)
M.current_buf_folds = result
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', 'foldlsp#foldexpr()')
end
end
function M.adjust_foldstart(line_no)
return line_no + 1
end
function M.adjust_foldend(line_no)
local bufnr = api.nvim_get_current_buf()
local filetype = api.nvim_buf_get_option(bufnr, 'filetype')
if filetype == 'lua' then
return line_no + 2
else
return line_no + 1
end
end
function M.get_fold_indic(lnum)
local fold_level = 0
local is_foldstart = false
local is_foldend = false
for _, table in ipairs(M.current_buf_folds) do
local start_line = table['startLine']
local end_line = table['endLine']
-- can exit early b/c folds get pre-orderered manually
if lnum < start_line then
break
end
if lnum >= start_line and lnum <= end_line then
fold_level = fold_level + 1
if lnum == start_line then
is_foldstart = true
end
if lnum == end_line then
is_foldend = true
end
end
end
if is_foldend and is_foldstart then
-- If line marks both start and end of folds (like ``else`` statement),
-- merge the two folds into one by returning the current foldlevel
-- without any marker.
return fold_level
elseif is_foldstart then
return string.format('>%d', fold_level)
elseif is_foldend then
return string.format('<%d', fold_level)
else
return fold_level
end
end
return M

@ -0,0 +1,243 @@
-- 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')
local parsers = require('nvim-treesitter.parsers')
local get_node_at_line = require('navigator.treesitter').get_node_at_line
local M = {}
-- TODO: per-buffer fold table?
M.current_buf_folds = {}
function M.on_attach()
M.setup_fold()
-- M.update_folds()
end
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")
local ss, se = line:find('^%s*')
local spaces = line:sub(ss, se)
local tabspace = string.rep(' ', vim.o.tabstop)
spaces = spaces:gsub('\t', tabspace)
line = line:gsub('^%s*(.-)%s*$', '%1')
return spaces .. '' .. line .. ': ' .. line_count .. ' lines'
end
vim.opt.foldtext = NG_custom_fold_text()
vim.opt.fillchars = { eob = '-', fold = ' ' }
vim.opt.viewoptions:remove('options')
function M.setup_fold()
api.nvim_command('augroup FoldingCommand')
api.nvim_command('autocmd! * <buffer>')
api.nvim_command('augroup end')
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()
if not parsers.has_parser() then
api.nvim_win_set_option(current_window, 'foldmethod', 'indent')
log('fallback to indent folding')
return
end
log('setup treesitter folding')
api.nvim_win_set_option(current_window, 'foldmethod', 'expr')
api.nvim_win_set_option(current_window, 'foldexpr', 'folding#ngfoldexpr()')
end
local function get_fold_level(levels, lnum)
local prev_l = levels[lnum]
local prev_ln
if prev_l:find('>') then
prev_ln = tonumber(prev_l:sub(2))
else
prev_ln = tonumber(prev_l)
end
return prev_ln
end
-- This is cached on buf tick to avoid computing that multiple times
-- Especially not for every line in the file when `zx` is hit
local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
local max_fold_level = api.nvim_win_get_option(0, 'foldnestmax')
local trim_level = function(level)
if level > max_fold_level then
return max_fold_level
end
return level
end
local parser = parsers.get_parser(bufnr)
if not parser then
log('treesitter parser not loaded')
return {}
end
local matches = query.get_capture_matches_recursively(bufnr, function(lang)
if query.has_folds(lang) then
return '@fold', 'folds'
elseif query.has_locals(lang) then
return '@scope', 'locals'
end
end)
-- start..stop is an inclusive range
local start_counts = {}
local stop_counts = {}
local prev_start = -1
local prev_stop = -1
local min_fold_lines = api.nvim_win_get_option(0, 'foldminlines')
for _, node in ipairs(matches) do
local start, _, stop, stop_col = node.node:range()
if stop_col == 0 then
stop = stop - 1
end
local fold_length = stop - start + 1
local should_fold = fold_length > min_fold_lines
-- Fold only multiline nodes that are not exactly the same as previously met folds
-- Checking against just the previously found fold is sufficient if nodes
-- are returned in preorder or postorder when traversing tree
if should_fold and not (start == prev_start and stop == prev_stop) then
start_counts[start] = (start_counts[start] or 0) + 1
stop_counts[stop] = (stop_counts[stop] or 0) + 1
prev_start = start
prev_stop = stop
end
end
trace(start_counts)
trace(stop_counts)
local levels = {}
local current_level = 0
-- We now have the list of fold opening and closing, fill the gaps and mark where fold start
local pre_node
for lnum = 0, api.nvim_buf_line_count(bufnr) do
local node, _ = get_node_at_line(lnum + 1)
local comment = node:type() == 'comment'
local next_node, _ = get_node_at_line(lnum + 1)
local next_comment = node and node:type() == 'comment'
local last_trimmed_level = trim_level(current_level)
current_level = current_level + (start_counts[lnum] or 0)
local trimmed_level = trim_level(current_level)
local current_level2 = current_level - (stop_counts[lnum] or 0)
local next_trimmed_level = trim_level(current_level2)
trace(lnum, node:type(), node, last_trimmed_level, trimmed_level, next_trimmed_level)
if comment then
trace('comment node', trimmed_level)
-- if trimmed_level == 0 then
-- trimmed_level = 1
-- end
levels[lnum + 1] = tostring(trimmed_level + 2)
if pre_node and pre_node:type() ~= 'comment' then
levels[lnum + 1] = '>' .. tostring(trimmed_level + 2)
end
if next_node and next_node:type() ~= 'comment' then
levels[lnum + 1] = tostring(trimmed_level + 1)
end
else
-- Determine if it's the start/end of a fold
-- NB: vim's fold-expr interface does not have a mechanism to indicate that
-- two (or more) folds start at this line, so it cannot distinguish between
-- ( \n ( \n )) \n (( \n ) \n )
-- versus
-- ( \n ( \n ) \n ( \n ) \n )
-- If it did have such a mechansim, (trimmed_level - last_trimmed_level)
-- would be the correct number of starts to pass on.
if trimmed_level - last_trimmed_level > 0 then
if levels[lnum + 1] ~= '>' .. tostring(trimmed_level) then
levels[lnum + 1] = tostring(trimmed_level) -- hack do not fold current line as it is first in fold range
end
levels[lnum + 2] = '>' .. tostring(trimmed_level + 1) -- dirty hack fold start from next line
trace('fold start')
elseif trimmed_level - next_trimmed_level > 0 then -- last line in fold range
-- Ending marks tend to confuse vim more than it helps, particularly when
-- the fold level changes by at least 2; we can uncomment this if
-- vim's behavior gets fixed.
trace('fold end')
if levels[lnum + 1] then
trace('already set reset as fold is ending', levels[lnum + 1])
levels[lnum + 1] = tostring(trimmed_level + 1)
else
local prev_ln = get_fold_level(levels, lnum) - 1
if prev_ln == 0 then
prev_ln = 1
end
levels[lnum + 1] = tostring(prev_ln)
end
-- levels[lnum + 1] = tostring(trimmed_level + 1)
-- else
current_level = current_level - 1
else
trace('same')
if pre_node and pre_node:type() == 'comment' then
local prev_ln = get_fold_level(levels, lnum) - 1
levels[lnum + 1] = tostring(prev_ln)
else
local n = math.max(trimmed_level, 1)
if lnum > 1 then
if levels[lnum + 1] then
trace('already set', levels[lnum + 1])
else
local prev_l = levels[lnum]
if prev_l:find('>') then
levels[lnum + 1] = prev_l:sub(2)
else
levels[lnum + 1] = prev_l
end
end
else
levels[lnum + 1] = tostring(n)
end
end
end
trace(levels)
end
pre_node = node
end
trace(levels)
return levels
end)
function M.get_fold_indic(lnum)
if not parsers.has_parser() or not lnum then
return '0'
end
local buf = api.nvim_get_current_buf()
local shown = false
for i = 1, vim.fn.tabpagenr('$') do
for _, value in pairs(vim.fn.tabpagebuflist(i)) do
if value == buf then
shown = true
end
end
end
if not shown then
return '0'
end
local levels = folds_levels(buf) or {}
-- trace(lnum, levels[lnum]) -- TODO: comment it out in master
return levels[lnum] or '0'
end
return M

@ -1,24 +1,49 @@
-- 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
return {
format_hdl = function(err, _, result, _, bufnr) -- FIXME: bufnr is nil
format_hdl = function(err, result, ctx, _) -- FIXME: bufnr is nil
if err ~= nil or result == nil then
return
end
local util = require('navigator.util')
local log = util.log
local offset_encoding = util.encoding(vim.lsp.get_client_by_id(ctx.client_id))
-- If the buffer hasn't been modified before the formatting has finished,
-- update the buffer
if not vim.api.nvim_buf_get_option(bufnr, 'modified') then
local view = vim.fn.winsaveview()
vim.lsp.util.apply_text_edits(result, bufnr)
vim.fn.winrestview(view)
-- FIXME: commented out as a workaround
-- if bufnr == vim.api.nvim_get_current_buf() then
vim.api.nvim_command('noautocmd :update')
-- if not vim.api.nvim_buf_get_option(ctx.bufnr, 'modified') then
vim.defer_fn(function()
log('fmt callback')
if ctx.bufnr == vim.api.nvim_get_current_buf() or not vim.api.nvim_buf_get_option(ctx.bufnr, 'modified') then
local view = vim.fn.winsaveview()
vim.lsp.util.apply_text_edits(result, ctx.bufnr, offset_encoding)
vim.fn.winrestview(view)
-- FIXME: commented out as a workaround
-- if bufnr == vim.api.nvim_get_current_buf() then
vim.api.nvim_command('noautocmd :update')
-- Trigger post-formatting autocommand which can be used to refresh gitgutter
-- vim.api.nvim_command('silent doautocmd <nomodeline> User FormatterPost')
-- end
-- Trigger post-formatting autocommand which can be used to refresh gitgutter
vim.api.nvim_command('silent doautocmd <nomodeline> User FormatterPost')
-- end
end
end, 100)
end,
range_format = function()
local old_func = vim.go.operatorfunc
_G.op_func_formatting = function()
print('formatting range')
local start = vim.api.nvim_buf_get_mark(0, '[')
local finish = vim.api.nvim_buf_get_mark(0, ']')
print(vim.inspect(start), vim.inspect(finish))
vim.lsp.buf.range_formatting({}, start, finish)
vim.go.operatorfunc = old_func
_G.op_func_formatting = nil
end
end
vim.go.operatorfunc = 'v:lua.op_func_formatting'
vim.api.nvim_feedkeys('g@', 'n', false)
end,
}

@ -1,216 +1,68 @@
local M = {}
local ListView = require "guihua.listview"
local TextView = require "guihua.textview"
local util = require "navigator.util"
local log = require"navigator.util".log
local trace = require"navigator.util".trace
-- local ListView = require('guihua.listview')
-- local TextView = require('guihua.textview')
local util = require('navigator.util')
local log = util.log
local trace = require('navigator.util').trace
local api = vim.api
local active_list_view -- only one listview at a time
local top_center = require"guihua.location".top_center
function M.new_list_view(opts)
-- log(opts)
local config = require('navigator').config_values()
local path_sep = require"navigator.util".path_sep()
local path_cur = require"navigator.util".path_cur()
function M._preview_location(opts) -- location, width, pos_x, pos_y
local uri = opts.uri
if uri == nil then
log("invalid/nil uri ")
return
end
local bufnr = vim.uri_to_bufnr(uri)
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
--
if active_list_view ~= nil then
trace(active_list_view)
local winnr = active_list_view.win
local bufnr = active_list_view.buf
local display_range = opts.location.range
-- if range.start == nil then
-- print("error invalid range")
-- return
-- end
-- if range.start.line == nil then
-- range.start.line = range["end"].line - 1
-- opts.lnum = range["end"].line + 1
-- log(opts)
-- end
-- if range["end"].line == nil then
-- range["end"].line = range.start.line + 1
-- opts.lnum = range.start.line + 1
-- log(opts)
-- end
-- TODO: preview height
-- local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line, false)
--
local syntax = api.nvim_buf_get_option(bufnr, "ft")
if syntax == nil or #syntax < 1 then
syntax = "c"
end
-- trace(syntax, contents)
local win_opts = {
syntax = syntax,
width = opts.width,
height = display_range['end'].line - display_range.start.line + 1,
preview_height = opts.height or opts.preview_height,
pos_x = opts.offset_x,
pos_y = opts.offset_y,
range = opts.range,
display_range = display_range,
uri = uri,
allow_edit = opts.enable_edit
}
-- win_opts.items = contents
win_opts.hl_line = opts.lnum - display_range.start.line
if win_opts.hl_line < 0 then
win_opts.hl_line = 1
end
trace(opts.lnum, opts.range.start.line, win_opts.hl_line)
log(win_opts)
local w = TextView:new({
loc = "offset_center",
rect = {
height = win_opts.height, -- opts.preview_heigh or 12, -- TODO 12
width = win_opts.width,
pos_x = win_opts.pos_x,
pos_y = win_opts.pos_y
},
list_view_height = win_opts.height,
-- data = display_data,
relative = win_opts.relative,
-- data = opts.items, -- either items or uri
uri = win_opts.uri,
syntax = win_opts.syntax,
enter = win_opts.enter or false,
range = win_opts.range,
border = opts.border,
display_range = win_opts.display_range,
hl_line = win_opts.hl_line,
allow_edit = win_opts.allow_edit
})
return w
end
function M.preview_uri(opts) -- uri, width, line, col, offset_x, offset_y
local line_beg = opts.lnum - 1
if line_beg >= 2 then
line_beg = line_beg - 2
if bufnr and api.nvim_buf_is_valid(bufnr) and winnr and api.nvim_win_is_valid(winnr) then
log('list view already present')
return active_list_view
end
end
local loc = {uri = opts.uri, range = {start = {line = line_beg}}}
-- TODO: preview height
loc.range["end"] = {line = opts.lnum + opts.preview_height}
opts.location = loc
trace("uri", opts.uri, opts.lnum, opts.location.range.start.line, opts.location.range['end'].line)
return M._preview_location(opts)
end
function M.new_list_view(opts)
local config = require("navigator").config_values()
local items = opts.items
local data = {}
local wwidth = api.nvim_get_option("columns")
local loc = "top_center"
local mwidth = _NgConfigValues.width
local width = math.floor(wwidth * mwidth)
if config.width ~= nil and config.width > 0.3 and config.width < 0.99 then
width = math.floor(wwidth * config.width)
end
width = math.min(120, width)
local wheight = math.floor(1 + api.nvim_get_option("lines")
* (_NgConfigValues.height + _NgConfigValues.preview_height))
local pheight = math.floor(api.nvim_get_option("lines") * _NgConfigValues.preview_height)
local prompt = opts.prompt or false
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
data = items
opts.data = items
else
log(items)
data = require"navigator.render".prepare_for_render(items, opts)
opts.data = require('navigator.render').prepare_for_render(items, opts)
end
opts.border = config.border or 'shadow'
if vim.fn.hlID('TelescopePromptBorder') > 0 then
opts.border_hl = 'TelescopePromptBorder'
opts.list_hl = 'TelescopeNormal'
opts.bg_hl = 'TelescopePreviewNormal'
opts.sel_hl = 'TelescopeSelection'
else
opts.border_hl = 'FloatBorder'
opts.bg_hl = 'NormalFloat'
opts.list_hl = 'NormalFloat'
opts.sel_hl = 'PmenuSel'
end
if not items or vim.tbl_isempty(items) then
log('empty data return')
return
end
local border = _NgConfigValues.border or 'shadow'
if data and not vim.tbl_isempty(data) then
-- replace
-- TODO: 10 vimrc opt
if #data > 10 and opts.prompt == nil then
loc = "top_center"
prompt = true
end
local lheight = math.min(#data, math.floor(wheight * _NgConfigValues.height))
local r, _ = top_center(lheight, width)
local offset_y = r + lheight
-- style shadow took 1 lines
if border ~= 'none' then
if border == 'shadow' then
offset_y = offset_y + 1
else
offset_y = offset_y + 1 -- single?
end
end
-- if border is not set, this should be r+lheigh
if prompt then
offset_y = offset_y + 1 -- need to check this out
end
local idx = require"guihua.util".fzy_idx
return ListView:new({
loc = loc,
prompt = prompt,
relative = opts.relative,
style = opts.style,
api = opts.api,
rect = {height = lheight, width = width, pos_x = 0, pos_y = 0},
-- preview_height = pheight,
ft = opts.ft or 'guihua',
-- data = display_data,
data = data,
border = border,
on_confirm = opts.on_confirm or function(pos)
if pos == 0 then
pos = 1
end
local l = idx(data, pos)
if l.filename ~= nil then
log("openfile ", l.filename, l.lnum, l.col)
util.open_file_at(l.filename, l.lnum, l.col)
end
end,
on_move = opts.on_move or function(pos)
if pos == 0 then
pos = 1
end
opts.transparency = config.transparency
if #items >= config.lines_show_prompt then
opts.prompt = true
end
local l = idx(data, pos) -- bug it not work with fzy filter
trace(data)
trace("on move", pos, l)
trace("on move", pos, l.text or l, l.uri, l.filename)
-- todo fix
if l.uri == nil then
l.uri = "file:///" .. l.filename
end
return M.preview_uri({
uri = l.uri,
width = width,
height = lheight, -- this is to cal offset
preview_height = pheight,
lnum = l.lnum,
col = l.col,
range = l.range,
offset_x = 0,
offset_y = offset_y,
border = border,
enable_edit = opts.enable_preview_edit or false
})
end
})
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
return M
@ -224,7 +76,7 @@ return M
call_by = { <table 1> },
col = 40,
display_filename = "./curry.js",
filename = "/Users/ray.xu/lsp_test/js/curry.js",
filename = "/Users/username/lsp_test/js/curry.js",
lnum = 4,
range = {
end = {
@ -238,7 +90,7 @@ return M
},
rpath = "js/curry.js",
text = " (sum, element, index) => (sum += element * vector2[index]),",
uri = "file:///Users/ray.xu/lsp_test/js/curry.js"
uri = "file:///Users/username/lsp_test/js/curry.js"
}
--]]
@ -262,7 +114,7 @@ return M
} },
col = 22,
display_filename = "./curry.js",
filename = "/Users/ray.xu/lsp_test/js/curry.js",
filename = "/Users/username/lsp_test/js/curry.js",
lnum = 4,
range = {
end = {
@ -276,6 +128,6 @@ return M
},
rpath = "js/curry.js",
text = " 4: (sum, element, index) => (sum += element * vector   curriedDot()",
uri = "file:///Users/ray.xu/lsp_test/js/curry.js"
uri = "file:///Users/username/lsp_test/js/curry.js"
--
]]

@ -1,95 +1,312 @@
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 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 path_sep = require"navigator.util".path_sep()
local path_cur = require"navigator.util".path_cur()
local cwd = vim.fn.getcwd(0)
local M = {}
local outgoing_calls_handler
local incoming_calls_handler
local hierarchy_handler
local call_hierarchy
local function pick_call_hierarchy_item(call_hierarchy_items)
if not call_hierarchy_items then
return
end
if #call_hierarchy_items == 1 then
return call_hierarchy_items[1]
end
local items = {}
for i, item in pairs(call_hierarchy_items) do
local entry = item.detail or item.name
table.insert(items, string.format('%d. %s', i, entry))
end
local choice = vim.fn.inputlist(items)
if choice < 1 or choice > #items then
return
end
return choice
end
-- convert lsp result to navigator items
local function call_hierarchy_result_procesor(direction, err, result, ctx, config)
math.randomseed(os.clock() * 100000000000)
trace(direction, err, ctx, config)
trace(result)
if not result then
vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN)
return
end
-- trace('call_hierarchy', result)
local function call_hierarchy_handler(direction, err, _, result, _, _, error_message)
log('call_hierarchy')
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
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)
print("ERROR: " .. error_message)
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) .. ' '
end
for _, range in pairs(call_hierarchy_call.fromRanges) do
local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri))
local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1)
table.insert(items, {
uri = call_hierarchy_item.uri,
filename = filename,
display_filename = call_hierarchy_item.detail or display_filename,
text = kind .. call_hierarchy_item.name,
range = range,
lnum = range.start.line,
col = range.start.character
})
kind = require('navigator.lspclient.lspkind').symbol_kind(call_hierarchy_item.kind) .. ' '
end
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 = string.gsub(call_hierarchy_item.detail, '\n', '')
trace(call_hierarchy_item)
local disp_item = vim.tbl_deep_extend('force', {}, call_hierarchy_item)
disp_item = vim.tbl_deep_extend('force', disp_item, {
filename = filename,
display_filename = display_filename,
indent_level = ctx.depth or 1,
method = lsp_method[direction],
node_text = call_hierarchy_item.name,
type = kind,
id = math.random(1, 100000),
text = kind .. call_hierarchy_item.name .. '' .. call_hierarchy_item.detail,
lnum = call_hierarchy_item.selectionRange.start.line + 1,
col = call_hierarchy_item.selectionRange.start.character,
})
table.insert(items, disp_item)
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')
-- 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 {}
local opts = ctx.opts or {}
vim.validate({ handler = { handler, 'function' }, show = { show, 'function' }, api = { api, 'string' } })
local bufnr = ctx.bufnr or vim.api.nvim_get_current_buf()
assert(next(vim.lsp.buf_get_clients(bufnr)), 'Must have a client running to use lsp hierarchy')
local results = handler(err, result, ctx, cfg, 'Incoming calls not found')
local function incoming_calls_handler(bang, err, method, result, client_id, bufnr)
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
local results = call_hierarchy_handler_from(err, method, result, client_id, bufnr,
"Incoming calls not found")
local ft = vim.api.nvim_buf_get_option(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 ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({items = results, ft = ft, api = ''})
local make_params = function(uri, pos)
return {
textDocument = {
uri = uri,
},
position = pos,
}
end
local function outgoing_calls_handler(bang, err, method, result, client_id, bufnr)
local results = call_hierarchy_handler_to(err, method, result, client_id, bufnr,
"Outgoing calls not found")
local function display_panel(args)
-- args = {items=results, ft=ft, api=api}
log(args)
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({items = results, ft = ft, api = ''})
-- fzf_locations(bang, "", "Outgoing Calls", results, false)
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(buf)
log(buf)
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_tags")
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

@ -1,23 +0,0 @@
-- TODO: change background and use TextView?
local lsp = require("vim.lsp")
return { hover_handler = function(_, method, result)
vim.lsp.util.focusable_float(
method,
function()
if not (result and result.contents) then
return
end
local markdown_lines = lsp.util.convert_input_to_markdown_lines(result.contents)
markdown_lines = lsp.util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
return
end
local bnr, contents_winid, _, border_winid = vim.lsp.util.fancy_floating_markdown(markdown_lines)
lsp.util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, contents_winid)
lsp.util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, border_winid)
return bnr, contents_winid
end
)
end
}

@ -1,36 +1,35 @@
local util = require "navigator.util"
local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui"
local util = require('navigator.util')
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, _, bufnr, error_message)
local function location_handler(err, locations, ctx, _, msg)
if err ~= nil then
print("ERROR: " .. tostring(err) .. error_message)
vim.notify('ERROR: ' .. tostring(err) .. ' ' .. msg, vim.lsp.log_levels.WARN)
return
end
return locations_to_items(locations)
return locations_to_items(locations, ctx)
end
local function implementation_handler(bang, err, method, result, client_id, bufnr)
local results =
location_handler(err, method, result, client_id, bufnr, "Implementation not found")
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({items = results, ft = ft, api = 'Implementation'})
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
local params = vim.lsp.util.make_position_params()
log("impel params", params)
log('impel params', params)
util.call_sync("textDocument/implementation", params, opts, partial(implementation_handler, bang))
lsphelper.call_sync('textDocument/implementation', params, opts, partial(implementation_handler, bang))
end
M.implementation_call = partial(M.implementation, 0)

@ -0,0 +1,67 @@
return {
init = function()
local loader = nil
local log = require('navigator.util').log
-- packer only
if packer_plugins ~= nil then -- packer install
local lazy_plugins = {
['nvim-lspconfig'] = 'neovim/nvim-lspconfig',
['guihua.lua'] = 'ray-x/guihua.lua',
}
if _NgConfigValues.lsp_installer == true then
lazy_plugins['nvim-lsp-installer'] = 'williamboman/nvim-lsp-installer'
end
-- packer installed
loader = require('packer').loader
for plugin, url in pairs(lazy_plugins) do
if not packer_plugins[url] or not packer_plugins[url].loaded then
-- log("loading ", plugin)
loader(plugin)
end
end
else
loader = function(plugin)
local cmd = 'packadd ' .. plugin
vim.cmd(cmd)
end
end
if _NgConfigValues.lsp_installer == true then
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()
local configs = require('lspconfig/configs')
local servers = require('nvim-lsp-installer').get_installed_servers()
for _, server in pairs(servers) do
local cfg = require('navigator.lspclient.clients').get_cfg(server)
local lsp_inst_cfg = configs[server]
if lsp_inst_cfg and lsp_inst_cfg.document_config.default_config then
lsp_inst_cfg = lsp_inst_cfg.document_config.default_config
lsp_inst_cfg = vim.tbl_deep_extend('keep', lsp_inst_cfg, cfg)
require('lspconfig')[server].setup(lsp_inst_cfg)
end
end
end
end
end,
load = function(plugin_name, path)
local loader = nil
packer_plugins = packer_plugins or nil -- suppress warnings
-- packer only
if packer_plugins ~= nil then -- packer install
local lazy_plugins = {}
lazy_plugins[plugin_name] = path
loader = require('packer').loader
for plugin, _ in pairs(lazy_plugins) do
if packer_plugins[plugin] and packer_plugins[plugin].loaded == false then
-- log("loading ", plugin)
pcall(loader, plugin)
end
end
end
end,
}

@ -0,0 +1,176 @@
-- lua-lru, LRU cache in Lua
-- Copyright (c) 2015 Boris Nagaev
-- See the LICENSE file for terms of use.
--[[
The MIT License (MIT)
Copyright (c) 2015 Boris Nagaev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]--
local lru = {}
function lru.new(max_size, max_bytes)
assert(max_size >= 1, "max_size must be >= 1")
assert(not max_bytes or max_bytes >= 1, "max_bytes must be >= 1")
-- current size
local size = 0
local bytes_used = 0
-- map is a hash map from keys to tuples
-- tuple: value, prev, next, key
-- prev and next are pointers to tuples
local map = {}
-- indices of tuple
local VALUE = 1
local PREV = 2
local NEXT = 3
local KEY = 4
local BYTES = 5
-- newest and oldest are ends of double-linked list
local newest = nil -- first
local oldest = nil -- last
local removed_tuple -- created in del(), removed in set()
-- remove a tuple from linked list
local function cut(tuple)
local tuple_prev = tuple[PREV]
local tuple_next = tuple[NEXT]
tuple[PREV] = nil
tuple[NEXT] = nil
if tuple_prev and tuple_next then
tuple_prev[NEXT] = tuple_next
tuple_next[PREV] = tuple_prev
elseif tuple_prev then
-- tuple is the oldest element
tuple_prev[NEXT] = nil
oldest = tuple_prev
elseif tuple_next then
-- tuple is the newest element
tuple_next[PREV] = nil
newest = tuple_next
else
-- tuple is the only element
newest = nil
oldest = nil
end
end
-- insert a tuple to the newest end
local function setNewest(tuple)
if not newest then
newest = tuple
oldest = tuple
else
tuple[NEXT] = newest
newest[PREV] = tuple
newest = tuple
end
end
local function del(key, tuple)
map[key] = nil
cut(tuple)
size = size - 1
bytes_used = bytes_used - (tuple[BYTES] or 0)
removed_tuple = tuple
end
-- removes elemenets to provide enough memory
-- returns last removed element or nil
local function makeFreeSpace(bytes)
while size + 1 > max_size or (max_bytes and bytes_used + bytes > max_bytes) do
assert(oldest, "not enough storage for cache")
del(oldest[KEY], oldest)
end
end
local function get(_, key)
local tuple = map[key]
if not tuple then
return nil
end
cut(tuple)
setNewest(tuple)
return tuple[VALUE]
end
local function set(_, key, value, bytes)
local tuple = map[key]
if tuple then
del(key, tuple)
end
if value ~= nil then
-- the value is not removed
bytes = max_bytes and (bytes or #value) or 0
makeFreeSpace(bytes)
local tuple1 = removed_tuple or {}
map[key] = tuple1
tuple1[VALUE] = value
tuple1[KEY] = key
tuple1[BYTES] = max_bytes and bytes
size = size + 1
bytes_used = bytes_used + bytes
setNewest(tuple1)
else
assert(key ~= nil, "Key may not be nil")
end
removed_tuple = nil
end
local function delete(_, key)
return set(_, key, nil)
end
local function mynext(_, prev_key)
local tuple
if prev_key then
tuple = map[prev_key][NEXT]
else
tuple = newest
end
if tuple then
return tuple[KEY], tuple[VALUE]
else
return nil
end
end
-- returns iterator for keys and values
local function lru_pairs()
return mynext, nil, nil
end
local mt = {
__index = {get = get, set = set, delete = delete, pairs = lru_pairs},
__pairs = lru_pairs
}
return setmetatable({}, mt)
end
return lru

@ -1,57 +1,84 @@
local vim, api = vim, vim.api
local lsp = require("vim.lsp")
local lsp = require('vim.lsp')
local util = require "navigator.util"
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()<CR>", opts)
end
_NG_Attached = {}
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.log.levels.DEBUG)
end
local uri = vim.uri_from_bufnr(bufnr)
if uri == "file://" or uri == "file:///" or #uri < 11 then
log("skip for float buffer", uri)
return {error = "invalid file", result = nil}
if uri == 'file://' or uri == 'file:///' or #uri < 11 then
log('skip for float buffer', uri)
return { error = 'invalid file', result = nil }
end
log("attaching", bufnr, client.name, uri)
log('attaching: ', bufnr, client.name, uri)
trace(client)
_NG_Attached[client.name] = true
diagnostic_map(bufnr)
-- add highlight for Lspxxx
require"navigator.lspclient.highlight".add_highlight()
api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")
require('navigator.lspclient.highlight').add_highlight()
require('navigator.lspclient.highlight').diagnositc_config_sign()
api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
require("navigator.lspclient.mapping").setup({
require('navigator.lspclient.mapping').setup({
client = client,
bufnr = bufnr,
cap = client.resolved_capabilities
})
if client.resolved_capabilities.document_highlight then
require("navigator.dochighlight").documentHighlight()
if client.server_capabilities.documentHighlightProvider == true then
trace('attaching doc highlight: ', bufnr, client.name)
vim.defer_fn(function()
require('navigator.dochighlight').documentHighlight(bufnr)
end, 50) -- allow a bit time for it to settle down
else
log('skip doc highlight: ', bufnr, client.name)
end
require"navigator.lspclient.lspkind".init()
local config = require"navigator".config_values()
trace(client.name, "navigator on attach")
require('navigator.lspclient.lspkind').init()
local config = require('navigator').config_values()
trace(client.name, 'navigator on attach')
if config.on_attach ~= nil then
log(client.name, "customized attach for all clients")
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
require("navigator.lspclient.mapping").setup(_NgConfigValues)
if _NgConfigValues.lsp.code_action.enable then
if client.server_capabilities.codeActionProvider and client.name ~= 'null-ls' then
log('code action enabled for client', client.server_capabilities.codeActionProvider)
api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
group = api.nvim_create_augroup('NGCodeActGroup_'..tostring(bufnr), {}),
buffer = bufnr,
callback = function()
require('navigator.codeAction').code_action_prompt(bufnr)
end,
})
end
end
end
-- M.setup = function(cfg)

@ -1,263 +1,132 @@
-- todo allow config passed in
local log = require"navigator.util".log
local trace = require"navigator.util".trace
local uv = vim.loop
_Loading = false
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
local vfn = vim.fn
_NG_Loaded = {}
_LoadedFiletypes = {}
packer_plugins = packer_plugins or nil -- suppress warnings
_LoadedClients = {}
-- packer only
if packer_plugins ~= nil then
-- packer installed
local loader = require"packer".loader
if not packer_plugins["neovim/nvim-lspconfig"]
or not packer_plugins["neovim/nvim-lspconfig"].loaded then
loader("nvim-lspconfig")
end
if not packer_plugins["ray-x/guihua.lua"] or not packer_plugins["guihua.lua"].loaded then
loader("guihua.lua")
-- if lazyloading
end
end
local has_lsp, lspconfig = pcall(require, "lspconfig")
local highlight = require('navigator.lspclient.highlight')
local has_lsp, lspconfig = pcall(require, 'lspconfig')
if not has_lsp then
return {
setup = function()
print("loading lsp config failed LSP may not working correctly")
end
vim.notify('loading lsp config failed LSP may not working correctly', vim.lsp.log_levels.WARN)
end,
}
end
local highlight = require "navigator.lspclient.highlight"
local util = lspconfig.util
local config = require"navigator".config_values()
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',
'help',
'',
}
-- local cap = vim.lsp.protocol.make_client_capabilities()
local on_attach = require("navigator.lspclient.attach").on_attach
local on_attach = require('navigator.lspclient.attach').on_attach
-- gopls["ui.completion.usePlaceholders"] = true
-- lua setup
local library = {}
local path = vim.split(package.path, ";")
local luadevcfg = {
library = {
vimruntime = true, -- runtime path
types = true, -- full signature, docs and completion of vim.api, vim.treesitter, vim.lsp and others
plugins = { 'nvim-treesitter', 'plenary.nvim' },
},
lspconfig = {
-- cmd = {sumneko_binary},
on_attach = on_attach,
},
}
table.insert(path, "lua/?.lua")
table.insert(path, "lua/?/init.lua")
local luadev = {}
local user_luadev = _NgConfigValues.lsp['lua-dev']
if user_luadev then
luadev = vim.tbl_deep_extend('force', luadev, user_luadev)
end
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 function add(lib)
for _, p in pairs(vim.fn.expand(lib, false, true)) do
p = vim.loop.fs_realpath(p)
if p then
library[p] = true
end
end
if _NgConfigValues.mason then
require('navigator.lazyloader').load('mason.nvim', 'williamboman/mason.nvim')
require('navigator.lazyloader').load('mason-lspconfig.nvim', 'williamboman/mason-lspconfig.nvim')
end
-- add runtime
add("$VIMRUNTIME")
-- add your config
-- local home = vim.fn.expand("$HOME")
add(vim.fn.stdpath('config'))
-- add plugins it may be very slow to add all in path
-- if vim.fn.isdirectory(home .. "/.config/share/nvim/site/pack/packer") then
-- add(home .. "/.local/share/nvim/site/pack/packer/opt/*")
-- add(home .. "/.local/share/nvim/site/pack/packer/start/*")
-- end
library[vim.fn.expand("$VIMRUNTIME/lua")] = true
library[vim.fn.expand("$VIMRUNTIME/lua/vim")] = true
library[vim.fn.expand("$VIMRUNTIME/lua/vim/lsp")] = true
-- [vim.fn.expand("~/repos/nvim/lua")] = true
-- TODO remove onece PR #944 merged to lspconfig
local path_sep = require"navigator.util".path_sep()
local strip_dir_pat = path_sep .. "([^" .. path_sep .. "]+)$"
local strip_sep_pat = path_sep .. "$"
local dirname = function(path)
if not path or #path == 0 then
return
end
local result = path:gsub(strip_sep_pat, ""):gsub(strip_dir_pat, "")
if #result == 0 then
return "/"
local setups = require('navigator.lspclient.clients_default').defaults()
local servers = require('navigator.lspclient.servers')
local lsp_installer_servers = {}
local has_lspinst = false
local has_mason = false
has_lspinst, _ = pcall(require, 'nvim-lsp-installer')
if has_lspinst then
local srvs = require('nvim-lsp-installer.servers').get_installed_servers()
if #srvs > 0 then
lsp_installer_servers = srvs
end
return result
end
-- TODO end
local setups = {
gopls = {
on_attach = on_attach,
-- capabilities = cap,
filetypes = {"go", "gomod"},
message_level = vim.lsp.protocol.MessageType.Error,
cmd = {
"gopls", -- share the gopls instance if there is one already
"-remote=auto", --[[ debug options ]] --
-- "-logfile=auto",
-- "-debug=:0",
"-remote.debug=:0"
-- "-rpc.trace",
},
flags = {allow_incremental_sync = true, debounce_text_changes = 500},
settings = {
gopls = {
-- flags = {allow_incremental_sync = true, debounce_text_changes = 500},
-- not supported
analyses = {unusedparams = true, unreachable = false},
codelenses = {
generate = true, -- show the `go generate` lens.
gc_details = true -- // Show a code lens toggling the display of gc's choices.
},
usePlaceholders = true,
completeUnimported = true,
staticcheck = true,
matcher = "fuzzy",
experimentalDiagnosticsDelay = "500ms",
symbolMatcher = "fuzzy",
gofumpt = false, -- true, -- turn on for new repos, gofmpt is good but also create code turmoils
buildFlags = {"-tags", "integration"}
-- buildFlags = {"-tags", "functional"}
}
},
root_dir = function(fname)
return util.root_pattern("go.mod", ".git")(fname) or dirname(fname) -- util.path.dirname(fname)
end
},
clangd = {
flags = {allow_incremental_sync = true, debounce_text_changes = 500},
cmd = {
"clangd", "--background-index", "--suggest-missing-includes", "--clang-tidy",
"--header-insertion=iwyu"
},
filetypes = {"c", "cpp", "objc", "objcpp"},
on_attach = function(client)
client.resolved_capabilities.document_formatting = true
on_attach(client)
end
},
rust_analyzer = {
root_dir = function(fname)
return util.root_pattern("Cargo.toml", "rust-project.json", ".git")(fname)
or util.path.dirname(fname)
end,
filetypes = {"rust"},
message_level = vim.lsp.protocol.MessageType.error,
on_attach = on_attach,
settings = {
["rust-analyzer"] = {
assist = {importMergeBehavior = "last", importPrefix = "by_self"},
cargo = {loadOutDirsFromCheck = true},
procMacro = {enable = true}
}
},
flags = {allow_incremental_sync = true, debounce_text_changes = 500}
},
sqls = {
filetypes = {"sql"},
on_attach = function(client, bufnr)
client.resolved_capabilities.execute_command = true
highlight.diagnositc_config_sign()
require"sqls".setup {picker = "telescope"} -- or default
end,
flags = {allow_incremental_sync = true, debounce_text_changes = 500},
settings = {
cmd = {"sqls", "-config", "$HOME/.config/sqls/config.yml"}
-- alterantively:
-- connections = {
-- {
-- driver = 'postgresql',
-- datasourcename = 'host=127.0.0.1 port=5432 user=postgres password=password dbname=user_db sslmode=disable',
-- },
-- },
}
},
sumneko_lua = {
cmd = {"lua-language-server"},
filetypes = {"lua"},
on_attach = on_attach,
flags = {allow_incremental_sync = true, debounce_text_changes = 500},
settings = {
Lua = {
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,
-- Get the language server to recognize the `vim` global
globals = {"vim", "describe", "it", "before_each", "after_each", "teardown", "pending"}
},
completion = {callSnippet = "Both"},
workspace = {
-- Make the server aware of Neovim runtime files
library = library,
maxPreload = 2000,
preloadFileSize = 40000
},
telemetry = {enable = false}
}
},
on_new_config = function(cfg, root)
local libs = vim.tbl_deep_extend('force', {}, library)
libs[root] = nil
cfg.settings.Lua.workspace.library = libs
return cfg
end
},
pyright = {
on_attach = on_attach,
cmd = {"pyright-langserver", "--stdio"},
filetypes = {"python"},
flags = {allow_incremental_sync = true, debounce_text_changes = 500},
settings = {
python = {
analysis = {
autoSearchPaths = true,
useLibraryCodeForTypes = true,
diagnosticMode = "workspace"
}
}
}
},
ccls = {
on_attach = on_attach,
init_options = {
compilationDatabaseDirectory = "build",
root_dir = [[ util.root_pattern("compile_commands.json", "compile_flags.txt", "CMakeLists.txt", "Makefile", ".git") or util.path.dirname ]],
index = {threads = 2},
clang = {excludeArgs = {"-frounding-math"}}
},
flags = {allow_incremental_sync = true}
}
}
has_mason, _ = pcall(require, 'mason-lspconfig')
if has_mason then
local srvs=require'mason-lspconfig'.get_installed_servers()
if #srvs > 0 then
lsp_installer_servers = srvs
end
end
log("lsp_installer:", lsp_installer_servers)
local servers = {
"angularls", "gopls", "tsserver", "flow", "bashls", "dockerls", "julials", "pyls", "pyright",
"jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls",
"yamlls", "clangd", "ccls", "sqls", "denols", "graphql", "dartls", "dotls",
"kotlin_language_server", "nimls", "intelephense", "vuels", "phpactor", "omnisharp",
"r_language_server", "rust_analyzer", "terraformls"
}
if config.lsp.disable_lsp == 'all' then
config.lsp.disable_lsp = servers
end
local default_cfg = {
local ng_default_cfg = {
on_attach = on_attach,
flags = {allow_incremental_sync = true, debounce_text_changes = 500}
flags = { allow_incremental_sync = true, debounce_text_changes = 1000 },
}
-- check and load based on file type
local function load_cfg(ft, client, cfg, loaded)
local function load_cfg(ft, client, cfg, loaded, starting)
log(ft, client, loaded, starting)
trace(cfg)
if lspconfig[client] == nil then
log("not supported by nvim", client)
log('not supported by nvim', client)
return
end
local lspft = lspconfig[client].document_config.default_config.filetypes
local additional_ft = setups[client] and setups[client].filetypes or {}
local bufnr = vim.api.nvim_get_current_buf()
local cmd = cfg.cmd
vim.list_extend(lspft, additional_ft)
local should_load = false
if lspft ~= nil and #lspft > 0 then
@ -266,164 +135,549 @@ local function load_cfg(ft, client, cfg, loaded)
should_load = true
end
end
if should_load then
for _, c in pairs(loaded) do
if client == c then
-- loaded
trace(client, "already been loaded for", ft, loaded)
if should_load == false then
return
end
trace('lsp for client', client, cfg)
if cmd == nil or #cmd == 0 or vfn.executable(cmd[1]) == 0 then
log('lsp not installed for client', client, cmd, "fallback")
return
end
if _NG_Loaded == nil then
return log('_NG_Loaded not set')
end
for k, c in pairs(loaded) do
if client == k then
-- loaded
log(client, 'already been loaded for', ft, loaded, c)
if not _NG_Loaded[bufnr] or _NG_Loaded[bufnr] < 4 then
log('doautocmd filetype')
vim.defer_fn(function()
vim.cmd('doautocmd FileType')
_NG_Loaded[bufnr] = (_NG_Loaded[bufnr] or 0 ) + 1
end, 100)
return
end
end
end
if lspconfig[client] == nil then
error("client " .. client .. " not supported")
local clients = vim.lsp.buf_get_clients(0)
for _, c in pairs(clients or {}) do
log("lsp start up in progress client", client, c.name)
if c.name == client then
_NG_Loaded[bufnr] = 100
return
end
end
if starting and (starting.cnt or 0) > 0 then
log("lsp start up in progress", starting)
return vim.defer_fn(function()
load_cfg(ft, client, cfg, loaded, { cnt = starting.cnt - 1 })
end,
200)
end
if lspconfig[client] == nil then
error('client ' .. client .. ' not supported')
return
end
trace('load cfg', cfg)
log('lspconfig setup')
-- log(lspconfig.available_servers())
-- force reload with config
-- lets have a guard here
if not _NG_Loaded[client] then
log(client, 'loading for', ft, cfg)
log(lspconfig[client])
lspconfig[client].setup(cfg)
-- dont know why but 1st lsp client setup may fail.. could be a upstream defect
lspconfig[client].setup(cfg)
log(client, "loading for", ft)
_NG_Loaded[client] = true
vim.defer_fn(function()
log('send filetype event')
vim.cmd([[doautocmd Filetype]])
_NG_Loaded[bufnr] = (_NG_Loaded[bufnr] or 0 )+ 1
end, 400)
else
log('send filetype event')
if not _NG_Loaded[bufnr] or _NG_Loaded[bufnr] < 4 then
log('doautocmd filetype')
vim.defer_fn(function()
vim.cmd('doautocmd FileType')
_NG_Loaded[bufnr] = (_NG_Loaded[bufnr] or 0 ) + 1
end, 100)
end
end
end
-- need to verify the lsp server is up
end
local function wait_lsp_startup(ft, retry, lsp_opts)
retry = retry or false
local clients = vim.lsp.get_active_clients() or {}
local loaded = {}
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
capabilities.textDocument.completion.completionItem.insertReplaceSupport = true
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.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 path_sep = require('navigator.util').path_sep()
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
if lsp_opts[lspclient] ~= nil and lsp_opts[lspclient].filetypes ~= nil then
if not vim.tbl_contains(lsp_opts[lspclient].filetypes, ft) then
trace("ft", ft, "disabled for", lspclient)
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))
goto continue
end
end
-- for lazy loading
-- e.g. {lsp={tsserver=function() if tsver>'1.17' then return {xxx} else return {xxx} end}}
if type(user_lsp_opts[lspclient]) == 'function' then
user_lsp_opts[lspclient] = user_lsp_opts[lspclient]()
trace('loading from func:', user_lsp_opts[lspclient])
elseif user_lsp_opts[lspclient] ~= nil and user_lsp_opts[lspclient].filetypes ~= nil then
if not vim.tbl_contains(user_lsp_opts[lspclient].filetypes, ft) then
trace('ft', ft, 'disabled for', lspclient)
goto continue
end
end
local cfg = setups[lspclient] or default_cfg
if vim.tbl_contains(config.lsp.disable_lsp or {}, lspclient) then
log('disable lsp', lspclient)
goto continue
end
local default_config = {}
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
if lspconfig[lspclient].document_config and lspconfig[lspclient].document_config.default_config then
default_config = lspconfig[lspclient].document_config.default_config
else
vim.notify('missing document config for client: ' .. vim.inspect(lspclient), vim.lsp.log_levels.WARN)
goto continue
end
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)
if vim.tbl_contains(config.lsp.disable_format_cap or {}, lspclient) then
log('fileformat disabled for ', lspclient)
disable_fmt = true
end
cfg.capabilities = capabilities
if lsp_opts[lspclient] ~= nil then
local enable_fmt = not disable_fmt
if user_lsp_opts[lspclient] ~= nil then
-- log(lsp_opts[lspclient], cfg)
cfg = vim.tbl_deep_extend("force", cfg, lsp_opts[lspclient])
if _NgConfigValues.combined_attach == "both" or _NgConfigValues.combined_attach == nil then
cfg.on_attach = on_attach
elseif _NgConfigValues == "mine" then
cfg = vim.tbl_deep_extend('force', cfg, user_lsp_opts[lspclient])
-- 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)
setup_fmt(client, enable_fmt)
require('navigator.lspclient.mapping').setup({
client = client,
bufnr = bufnr,
cap = capabilities,
})
end
end
if config.combined_attach == 'their' then
cfg.on_attach = function(client, bufnr)
on_attach(client, bufnr)
config.on_attach(client, bufnr)
setup_fmt(client, enable_fmt)
require('navigator.lspclient.mapping').setup({
client = client,
bufnr = bufnr,
cap = capabilities,
})
end
end
if config.combined_attach == 'both' then
cfg.on_attach = function(client, bufnr)
if _NgConfigValues.on_attach then
_NgConfigValues.on_attach(client, bufnr)
setup_fmt(client, enable_fmt)
if config.on_attach and type(config.on_attach) == 'function' then
config.on_attach(client, bufnr)
end
if setups[lspclient] and setups[lspclient].on_attach then
setups[lspclient].on_attach(client, bufnr)
else
on_attach(client, bufnr)
end
require('navigator.lspclient.mapping').setup({
client = client,
bufnr = bufnr,
cap = capabilities,
})
end
end
cfg.on_init = function(client)
if client and client.config and client.config.settings then
client.notify(
'workspace/didChangeConfiguration',
{ settings = client.config.settings },
vim.lsp.log_levels.WARN
)
end
end
else
cfg.on_attach = function(client, bufnr)
on_attach(client, bufnr)
setup_fmt(client, enable_fmt)
end
end
log('loading', lspclient, 'name', lspconfig[lspclient].name, 'has lspinst', has_lspinst)
-- start up lsp
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)
if installed and installer_cfg then
local paths = installer_cfg:get_default_options().cmd_env and installer_cfg:get_default_options().cmd_env.PATH
if not paths then
-- for some reason lspinstaller does not install the binary, check default PATH
log('lsp installer does not install the lsp in its path, fallback')
return load_cfg(ft, lspclient, cfg, loaded)
end
paths = vim.split(paths, ':')
if vfn.empty(cfg.cmd) == 1 then
cfg.cmd = { installer_cfg.name }
end
if vfn.executable(cfg.cmd[1]) == 0 then
for _, path in ipairs(paths) do
log(path)
if vfn.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 has_mason and _NgConfigValues.mason then
local servers = require'mason-lspconfig'.get_installed_servers()
if not vim.tbl_contains(servers, lspconfig[lspclient].name) then
log('mason server not installed', lspconfig[lspclient].name)
-- return
end
local pkg_name = require "mason-lspconfig.mappings.server".lspconfig_to_package[lspconfig[lspclient].name]
local pkg = require "mason-registry".get_package(pkg_name)
log('lsp installer server config ' .. lspconfig[lspclient].name, pkg)
if pkg then
local path = pkg:get_install_path()
if not path then
-- for some reason lspinstaller does not install the binary, check default PATH
log('lsp installer does not install the lsp in its path, fallback')
return load_cfg(ft, lspclient, cfg, loaded)
end
cfg.cmd = cfg.cmd or {}
cfg.cmd[1] = table.concat({vfn.stdpath('data'), 'mason', 'bin', pkg.name}, path_sep)
if vfn.executable(cfg.cmd[1]) == 0 then
log('failed to find cmd', cfg.cmd[1], "fallback")
return load_cfg(ft, lspclient, cfg, loaded)
else
log('cmd installed', cfg.cmd)
end
end
end
load_cfg(ft, lspclient, cfg, loaded)
if vfn.executable(cfg.cmd[1]) == 0 then
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, _NG_Loaded[lspclient])
end
local starting = {}
if _NG_Loaded[lspclient] == true then
starting = { cnt = 1 }
end
load_cfg(ft, lspclient, cfg, loaded, starting)
-- load_cfg(ft, lspclient, {}, loaded)
::continue::
end
if not _NG_Loaded['null_ls'] then
local nulls_cfg = user_lsp_opts['null_ls']
if nulls_cfg then
local cfg = {}
cfg = vim.tbl_deep_extend('keep', cfg, nulls_cfg)
vim.defer_fn(function()
lspconfig['null-ls'].setup(cfg) -- adjust null_ls startup timing
end, 1000)
log('null-ls loading')
_NG_Loaded['null-ls'] = true
setups['null-ls'] = cfg
end
end
if not _NG_Loaded['efm'] then
local efm_cfg = user_lsp_opts['efm']
if efm_cfg then
local cfg = {}
cfg = vim.tbl_deep_extend('keep', cfg, efm_cfg)
cfg.on_attach = function(client, bufnr)
if efm_cfg.on_attach then
efm_cfg.on_attach(client, bufnr)
end
on_attach(client, bufnr)
end
lspconfig.efm.setup(cfg)
log('efm loading')
_NG_Loaded['efm'] = true
setups['efm'] = cfg
end
end
if not retry or ft == nil then
return
end
--
local timer = vim.loop.new_timer()
local i = 0
timer:start(1000, 200, function()
clients = vim.lsp.get_active_clients() or {}
i = i + 1
if i > 5 or #clients > 0 then
timer:close() -- Always close handles to avoid leaks.
log("active", #clients, i)
_Loading = false
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
local ng_setup = vim.deepcopy(setups[client])
ng_setup.cmd = nil
return ng_setup
else
return ng_cfg
end
end
local function ft_disabled(ft)
for i = 1, #disabled_ft do
if ft == disabled_ft[i] then
return true
end
-- giveup
-- _Loading = false
end)
end
end
local function setup(user_opts)
local function setup(user_opts, cnt)
user_opts = user_opts or {}
local ft = vim.bo.filetype
if _LoadedClients[ft] then
log("navigator is loaded for ft", ft)
return
end
if user_opts ~= nil then
log(user_opts)
local bufnr = user_opts.bufnr or vim.api.nvim_get_current_buf()
if ft == '' or ft == nil then
log('nil filetype, callback')
local ext = vfn.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
trace(debug.traceback())
user_opts = user_opts or _NgConfigValues -- incase setup was triggered from autocmd
local uri = vim.uri_from_bufnr(bufnr)
if _Loading == true then
if uri == 'file://' or uri == 'file:///' then
log('skip loading for ft ', ft, uri)
return
end
if ft == nil then
ft = vim.api.nvim_buf_get_option(0, "filetype")
if _LoadedFiletypes[ft .. tostring(bufnr)] == true then
log('navigator was loaded for ft', ft, bufnr)
return
end
if ft == nil or ft == "" then
log("nil filetype")
if ft_disabled(ft) then
trace('navigator disabled for ft or it is loaded', ft)
return
end
local retry = true
local disable_ft = {
"NvimTree", "guihua", "clap_input", "clap_spinner", "vista", "vista_kind", "TelescopePrompt",
"csv", "txt", "markdown", "defx"
}
for i = 1, #disable_ft do
if ft == disable_ft[i] or _LoadedClients[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
local bufnr = vim.fn.bufnr()
local uri = vim.uri_from_bufnr(bufnr)
trace(debug.traceback())
if uri == 'file://' or uri == 'file:///' then
log("skip loading for ft ", ft, uri)
return
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 or {}, vim.o.ft) then
log('client already loaded', client.name)
end
end
end
trace('setup', user_opts)
log("loading for ft ", ft, uri)
user_opts = vim.tbl_extend('keep', user_opts, config) -- incase setup was triggered from autocmd
log('running lsp setup', ft, bufnr)
local retry = true
log('loading for ft ', ft, uri)
highlight.diagnositc_config_sign()
highlight.add_highlight()
local lsp_opts = user_opts.lsp
_Loading = true
local lsp_opts = user_opts.lsp or {}
if vim.bo.filetype == 'lua' then
local slua = lsp_opts.sumneko_lua
if slua and not slua.cmd then
if slua.sumneko_root_path and slua.sumneko_binary then
lsp_opts.sumneko_lua.cmd = {
slua.sumneko_binary, "-E", slua.sumneko_root_path .. "/main.lua"
slua.sumneko_binary,
'-E',
slua.sumneko_root_path .. '/main.lua',
}
else
lsp_opts.sumneko_lua.cmd = {"lua-language-server"}
lsp_opts.sumneko_lua.cmd = { 'lua-language-server' }
end
end
end
wait_lsp_startup(ft, retry, lsp_opts)
lsp_startup(ft, retry, lsp_opts)
--- if code lens enabled
if _NgConfigValues.lsp.code_lens_action.enable then
require('navigator.codelens').setup(bufnr)
end
-- _LoadedFiletypes[ft .. tostring(bufnr)] = true -- may prevent lsp config when reboot lsp
end
local function on_filetype()
local bufnr = vim.api.nvim_get_current_buf()
local uri = vim.uri_from_bufnr(bufnr)
local ft = vim.bo.filetype
if ft == nil then
return
end
if uri == 'file://' or uri == 'file:///' then
trace('skip loading for ft ', ft, uri)
return
end
log (_NG_Loaded)
if _NG_Loaded[bufnr] and type(_NG_Loaded[bufnr]) == 'number' and _NG_Loaded[bufnr] > 1 then
log('navigator was loaded for ft', ft, bufnr)
return
end
-- on_filetype should only be trigger only once for each bufnr
if _NG_Loaded[bufnr] ~= nil and type(_NG_Loaded[bufnr] == 'number') then
_NG_Loaded[bufnr] = _NG_Loaded[bufnr] + 1 -- do not hook and trigger filetype event multiple times
end
if _NG_Loaded[bufnr] == true then
_NG_Loaded[bufnr] = 1 -- record the count
end
_LoadedClients[ft] = true
_Loading = false
-- as setup will send filetype event as well
log(uri)
-- if not _NgConfigValues.loaded then
-- vim.cmd([[autocmd FileType * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
-- _NgConfigValues.loaded = true
-- end
local wids = vfn.win_findbuf(bufnr)
if empty(wids) then
log('buf not shown return')
end
setup({ bufnr = bufnr })
_NG_Loaded[bufnr] = 1
end
return {setup = setup}
return {
setup = setup,
get_cfg = get_cfg,
add_servers = add_servers,
on_filetype = on_filetype,
disabled_ft = disabled_ft,
ft_disabled = ft_disabled,
}

@ -0,0 +1,191 @@
local M = {}
local vfn = vim.fn
M.defaults = function()
local has_lsp, lspconfig = pcall(require, 'lspconfig')
local highlight = require('navigator.lspclient.highlight')
if not has_lsp then
return {
setup = function()
vim.notify('loading lsp config failed LSP may not working correctly', vim.lsp.log_levels.WARN)
end,
}
end
local util = lspconfig.util
local on_attach = require('navigator.lspclient.attach').on_attach
local setups = {
clojure_lsp = {
root_dir = function(fname)
return util.root_pattern('deps.edn', 'build.boot', 'project.clj', 'shadow-cljs.edn', 'bb.edn', '.git')(fname)
or util.path.dirname(fname)
end,
on_attach = on_attach,
filetypes = { 'clojure', 'edn' },
message_level = vim.lsp.protocol.MessageType.error,
cmd = { 'clojure-lsp' },
},
elixirls = {
on_attach = on_attach,
filetypes = { 'elixir', 'eelixir' },
cmd = { 'elixir-ls' },
message_level = vim.lsp.protocol.MessageType.error,
settings = {
elixirLS = {
dialyzerEnabled = true,
fetchDeps = false,
},
},
root_dir = function(fname)
return util.root_pattern('mix.exs', '.git')(fname) or util.path.dirname(fname)
end,
},
gopls = {
on_attach = on_attach,
-- capabilities = cap,
filetypes = { 'go', 'gomod', 'gohtmltmpl', 'gotexttmpl' },
message_level = vim.lsp.protocol.MessageType.Error,
cmd = {
'gopls', -- share the gopls instance if there is one already
'-remote=auto', --[[ debug options ]] --
-- "-logfile=auto",
-- "-debug=:0",
'-remote.debug=:0',
-- "-rpc.trace",
},
flags = { allow_incremental_sync = true, debounce_text_changes = 1000 },
settings = {
gopls = {
-- more settings: https://github.com/golang/tools/blob/master/gopls/doc/settings.md
-- flags = {allow_incremental_sync = true, debounce_text_changes = 500},
-- not supported
analyses = { unusedparams = true, unreachable = false },
codelenses = {
generate = true, -- show the `go generate` lens.
gc_details = true, -- // Show a code lens toggling the display of gc's choices.
test = true,
tidy = true,
},
usePlaceholders = true,
completeUnimported = true,
staticcheck = true,
matcher = 'fuzzy',
diagnosticsDelay = '500ms',
experimentalWatchedFileDelay = '1000ms',
symbolMatcher = 'fuzzy',
gofumpt = false, -- true, -- turn on for new repos, gofmpt is good but also create code turmoils
buildFlags = { '-tags', 'integration' },
-- buildFlags = {"-tags", "functional"}
},
},
root_dir = function(fname)
return util.root_pattern('go.mod', '.git')(fname) or dirname(fname) -- util.path.dirname(fname)
end,
},
clangd = {
flags = { allow_incremental_sync = true, debounce_text_changes = 500 },
cmd = {
'clangd',
'--background-index',
'--suggest-missing-includes',
'--clang-tidy',
'--header-insertion=iwyu',
'--clang-tidy-checks=-*,llvm-*,clang-analyzer-*',
'--cross-file-rename',
},
filetypes = { 'c', 'cpp', 'objc', 'objcpp' },
on_attach = function(client, bufnr)
client.server_capabilities.documentFormattingProvider = client.server_capabilities.documentFormattingProvider
or true
on_attach(client, bufnr)
end,
},
rust_analyzer = {
root_dir = function(fname)
return util.root_pattern('Cargo.toml', 'rust-project.json', '.git')(fname) or util.path.dirname(fname)
end,
filetypes = { 'rust' },
message_level = vim.lsp.protocol.MessageType.error,
on_attach = on_attach,
settings = {
['rust-analyzer'] = {
assist = { importMergeBehavior = 'last', importPrefix = 'by_self' },
cargo = { loadOutDirsFromCheck = true },
procMacro = { enable = true },
},
},
flags = { allow_incremental_sync = true, debounce_text_changes = 500 },
},
sqls = {
filetypes = { 'sql' },
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,
flags = { allow_incremental_sync = true, debounce_text_changes = 500 },
settings = {
cmd = { 'sqls', '-config', '$HOME/.config/sqls/config.yml' },
-- alterantively:
-- connections = {
-- {
-- driver = 'postgresql',
-- datasourcename = 'host=127.0.0.1 port=5432 user=postgres password=password dbname=user_db sslmode=disable',
-- },
-- },
},
},
pyright = {
on_attach = on_attach,
cmd = { 'pyright-langserver', '--stdio' },
filetypes = { 'python' },
flags = { allow_incremental_sync = true, debounce_text_changes = 500 },
settings = {
python = {
formatting = { provider = 'black' },
analysis = {
autoSearchPaths = true,
useLibraryCodeForTypes = true,
diagnosticMode = 'workspace',
},
},
},
},
ccls = {
on_attach = on_attach,
init_options = {
compilationDatabaseDirectory = 'build',
root_dir = [[ util.root_pattern("compile_commands.json", "compile_flags.txt", "CMakeLists.txt", "Makefile", ".git") or util.path.dirname ]],
index = { threads = 2 },
clang = { excludeArgs = { '-frounding-math' } },
},
flags = { allow_incremental_sync = true },
},
jdtls = {
settings = {
java = { signatureHelp = { enabled = true }, contentProvider = { preferred = 'fernflower' } },
},
},
omnisharp = {
cmd = { 'omnisharp', '--languageserver', '--hostPID', tostring(vfn.getpid()) },
},
terraformls = {
filetypes = { 'terraform', 'tf' },
},
sourcekit = {
cmd = { 'sourcekit-lsp' },
filetypes = { 'swift' }, -- This is recommended if you have separate settings for clangd.
},
}
setups.sumneko_lua = require('navigator.lspclient.sumneko_lua').sumneko_lua()
return setups
end
return M

@ -1,12 +1,20 @@
local lsp = require("vim.lsp")
M = {}
local M = {}
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true
function M.reload_lsp()
vim.lsp.stop_client(vim.lsp.get_active_clients())
vim.cmd [[edit]]
vim.cmd("LspStop")
local timer = vim.loop.new_timer()
local i = 0
timer:start(500, 100, function()
if i >= 5 then
timer:close() -- Always close handles to avoid leaks.
end
i = i + 1
end)
vim.cmd("LspStart")
vim.cmd([[write]])
vim.cmd([[edit]])
end
function M.open_lsp_log()
@ -14,10 +22,4 @@ function M.open_lsp_log()
vim.cmd("edit " .. path)
end
vim.cmd("command! -nargs=0 LspLog call v:lua.open_lsp_log()")
vim.cmd("command! -nargs=0 LspRestart call v:lua.reload_lsp()")
local cfg = {}
require("lsp.clients").setup(cfg)
return M

@ -1,44 +1,72 @@
local M = {}
-- local log = require('navigator.util').log
local api = vim.api
local cmd_group = api.nvim_create_augroup('NGHiGroup', {})
-- lsp sign          ﮻         ﯭ        ﳀ  
function M.diagnositc_config_sign()
vim.fn.sign_define('LspDiagnosticsSignError',
{text = '', texthl = 'LspDiagnosticsSignError', linehl = '', numhl = ''})
vim.fn.sign_define('LspDiagnosticsSignWarning',
{text = '', texthl = 'LspDiagnosticsSignWarning', linehl = '', numhl = ''})
vim.fn.sign_define('LspDiagnosticsSignInformation', {
text = '',
texthl = 'LspDiagnosticsSignInformation',
linehl = '',
numhl = ''
})
vim.fn.sign_define('LspDiagnosticsSignHint',
{text = '💡', texthl = 'LspDiagnosticsSignHint', linehl = '', numhl = ''})
if M.configed then
return
end
local icons = _NgConfigValues.icons
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' })
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
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
M.configed = true
end
function M.add_highlight()
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' },
}
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 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'}
}
api.nvim_set_hl(0, 'DiagnosticUnderlineError', { link = 'SpellBad', default = true })
api.nvim_set_hl(0, 'DiagnosticUnderlineWarning', { link = 'SpellRare', default = true })
api.nvim_set_hl(0, 'DiagnosticUnderlineInformation', { link = 'SpellRare', default = true })
api.nvim_set_hl(0, 'DiagnosticUnderlineHint', { link = 'SpellRare', default = true })
api.nvim_set_hl(0, 'NGPreviewTitle', { link = 'Title', default = true })
api.nvim_set_hl(0, 'LspReferenceRead', { default = true, link = 'IncSearch'})
api.nvim_set_hl(0, 'LspReferenceText', { default = true, link = 'Visual'})
api.nvim_set_hl( 0, 'LspReferenceWrite', { default = true, link = 'Search'})
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])
vim.cmd(cmd)
local hlg = string.format('NGHiReference_%i_%i', i, j) -- , colors[i][j], colors[i][4]
api.nvim_set_hl(0, hlg, { fg = colors[i][j], bg = colors[i][4], default = true })
end
end
end
api.nvim_create_autocmd('ColorScheme', {
group = cmd_group,
pattern = '*',
callback = function()
M.add_highlight()
end,
})
return M

@ -1,76 +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.init() require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind end
function lspkind.init()
require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind
end
return lspkind

@ -1,228 +1,398 @@
local log = require"navigator.util".log
local function set_keymap(...)
vim.api.nvim_set_keymap(...)
end
local util = require('navigator.util')
local log = util.log
local trace = util.trace
local api = vim.api
local event_hdlrs = {
{ev = "BufWritePre", func = "diagnostic.set_loclist({open_loclist = false})"},
{ev = "CursorHold", func = "document_highlight()"},
{ev = "CursorHoldI", func = "document_highlight()"},
{ev = "CursorMoved", func = "clear_references()"}
}
if vim.lsp.buf.format == nil then
vim.lsp.buf.format = vim.lsp.buf.formatting
end
local double = {"", "", "", "", "", "", "", ""}
local single = {"", "", "", "", "", "", "", ""}
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 = "references()"},
{mode = "i", key = "<M-k>", func = "signature_help()"},
{key = "gs", func = "signature_help()"},
{key = "g0", func = "document_symbol()"},
{key = "gW", func = "workspace_symbol()"},
{key = "<c-]>", func = "definition()"},
{key = "gD", func = "declaration({ popup_opts = { border = 'single' }})"},
{key = "gp", func = "require('navigator.definition').definition_preview()"},
{key = "gT", func = "require('navigator.treesitter').buf_ts()"},
{key = "GT", func = "require('navigator.treesitter').bufs_ts()"},
{key = "K", func = "hover({ popup_opts = { border = single }})"},
{key = "<Space>ca", mode = "n", func = "code_action()"},
{key = "<Space>cA", mode = "v", func = "range_code_action()"},
{key = "<Leader>re", func = "rename()"},
{key = "<Space>rn", func = "require('navigator.rename').rename()"},
{key = "<Leader>gi", func = "incoming_calls()"},
{key = "<Leader>go", func = "outgoing_calls()"},
{key = "gi", func = "implementation()"},
{key = "<Space>D", func = "type_definition()"},
{key = "gL", func = "diagnostic.show_line_diagnostics({ popup_opts = { border = single }})"},
{key = "gG", func = "require('navigator.diagnostics').show_diagnostic()"},
{key = "]d", func = "diagnostic.goto_next({ popup_opts = { border = single }})"},
{key = "[d", func = "diagnostic.goto_next({ popup_opts = { border = single }})"},
{key = "]r", func = "require('navigator.treesitter').goto_next_usage()"},
{key = "[r", func = "require('navigator.treesitter').goto_previous_usage()"},
{key = "<C-LeftMouse>", func = "definition()"},
{key = "g<LeftMouse>", func = "implementation()"},
{key = "<Leader>k", func = "require('navigator.dochighlight').hi_symbol()"},
{key = '<Space>wa', func = '<cmd>lua vim.lsp.buf.add_workspace_folder()'},
{key = '<Space>wr', func = '<cmd>lua vim.lsp.buf.remove_workspace_folder()'},
{key = '<Space>wl', func = '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))'},
{ key = 'gr', func = require('navigator.reference').async_ref, desc = 'async_ref' },
{ key = '<Leader>gr', func = require('navigator.reference').reference, desc = 'reference' }, -- reference deprecated
{ mode = 'i', key = '<M-k>', func = vim.lsp.signature_help, desc = 'signature_help' },
{ key = '<c-k>', func = vim.lsp.buf.signature_help, desc = 'signature_help' },
{ key = 'g0', func = require('navigator.symbols').document_symbols, desc = 'document_symbols' },
{ key = 'gW', func = require('navigator.workspace').workspace_symbol_live, desc = 'workspace_symbol_live' },
{ key = '<c-]>', func = require('navigator.definition').definition, desc = 'definition' },
{ key = 'gd', func = require('navigator.definition').definition, desc = 'definition' },
{ key = 'gD', func = vim.lsp.buf.declaration, desc = 'declaration' },
{ key = 'gp', func = require('navigator.definition').definition_preview, desc = 'definition_preview' },
{ key = '<Leader>gt', func = require('navigator.treesitter').buf_ts, desc = 'buf_ts' },
{ key = '<Leader>gT', func = require('navigator.treesitter').bufs_ts, desc = 'bufs_ts' },
{ key = '<Leader>ct', func = require('navigator.ctags').ctags, desc = 'ctags' },
{ key = 'K', func = vim.lsp.buf.hover, desc = 'hover' },
{ key = '<Space>ca', mode = 'n', func = require('navigator.codeAction').code_action, desc = 'code_action' },
{
key = '<Space>ca',
mode = 'v',
func = require('navigator.codeAction').range_code_action,
desc = 'range_code_action',
},
-- { key = '<Leader>re', func = 'rename()' },
{ key = '<Space>rn', func = require('navigator.rename').rename, desc = 'rename' },
{ key = '<Leader>gi', func = vim.lsp.buf.incoming_calls, desc = 'incoming_calls' },
{ key = '<Leader>go', func = vim.lsp.buf.outgoing_calls, desc = 'outgoing_calls' },
{ key = 'gi', func = vim.lsp.buf.implementation, desc = 'implementation' },
{ key = '<Space>D', func = vim.lsp.buf.type_definition, desc = 'type_definition' },
{ key = 'gL', func = require('navigator.diagnostics').show_diagnostics, desc = 'show_diagnostics' },
{ key = 'gG', func = require('navigator.diagnostics').show_buf_diagnostics, desc = 'show_buf_diagnostics' },
{ key = '<Leader>dt', func = require('navigator.diagnostics').toggle_diagnostics, desc = 'toggle_diagnostics' },
{ key = ']d', func = vim.diagnostic.goto_next, desc = 'next diagnostics' },
{ key = '[d', func = vim.diagnostic.goto_prev, desc = 'prev diagnostics' },
{ key = ']O', func = vim.diagnostic.set_loclist, desc = 'diagnostics set loclist' },
{ key = ']r', func = require('navigator.treesitter').goto_next_usage, desc = 'goto_next_usage' },
{ key = '[r', func = require('navigator.treesitter').goto_previous_usage, desc = 'goto_previous_usage' },
{ key = '<C-LeftMouse>', func = vim.lsp.buf.definition, desc = 'definition' },
{ key = 'g<LeftMouse>', func = vim.lsp.buf.implementation, desc = 'implementation' },
{ key = '<Leader>k', func = require('navigator.dochighlight').hi_symbol, desc = 'hi_symbol' },
{ key = '<Space>wa', func = require('navigator.workspace').add_workspace_folder, desc = 'add_workspace_folder' },
{
key = '<Space>wr',
func = require('navigator.workspace').remove_workspace_folder,
desc = 'remove_workspace_folder',
},
{ key = '<Space>ff', func = vim.lsp.buf.format, mode = 'n', desc = 'format' },
{ key = '<Space>ff', func = vim.lsp.buf.range_formatting, mode = 'v', desc = 'range format' },
{
key = '<Space>gm',
func = require('navigator.formatting').range_format,
mode = 'n',
desc = 'range format operator e.g gmip',
},
{ key = '<Space>wl', func = require('navigator.workspace').list_workspace_folders, desc = 'list_workspace_folders' },
{ key = '<Space>la', mode = 'n', func = require('navigator.codelens').run_action, desc = 'run code lens action' },
}
local key_maps_help = {}
-- LuaFormatter on
local M = {}
local ccls_mappings = {
{key = "<Leader>gi", func = "require('navigator.cclshierarchy').incoming_calls()"},
{key = "<Leader>go", func = "require('navigator.cclshierarchy').outgoing_calls()"}
{ key = '<Leader>gi', func = require('navigator.cclshierarchy').incoming_calls, desc = 'incoming_calls' },
{ key = '<Leader>go', func = require('navigator.cclshierarchy').outgoing_calls, desc = 'outgoing_calls' },
}
local function set_mapping(user_opts)
local opts = {noremap = true, silent = true}
user_opts = user_opts or {}
local check_cap = function(opts)
-- log(vim.lsp.buf_get_clients(0))
local fmt, rfmt, ccls
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.documentRangeFormattingProvider then
rfmt = true
end
for _, value in pairs(vim.lsp.buf_get_clients(0)) do
trace(value)
if value ~= nil and value.server_capabilities == nil then
if value.server_capabilities.documentFormattingProvider then
fmt = true
end
if value.server_capabilities.documentRangeFormattingProvider then
rfmt = true
end
log('override ccls', value.config)
if value.config.name == 'ccls' then
ccls = true
end
end
end
return fmt, rfmt, ccls
end
local user_key = user_opts.keymaps or {}
local bufnr = user_opts.bufnr or 0
local function set_cmds(_)
local commands = {
[[command! -nargs=* Nctags lua require("navigator.ctags").ctags(<f-args>)]],
"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()<CR>",
"command! -nargs=0 LspKeymaps lua require'navigator.lspclient.mapping'.get_keymaps_help()<CR>",
"command! -nargs=0 LspSymbols lua require'navigator.symbols'.side_panel()<CR>",
"command! -nargs=0 TSymbols lua require'navigator.treesitter'.side_panel()<CR>",
"command! -nargs=0 NRefPanel lua require'navigator.reference'.side_panel()<CR>",
"command! -nargs=* Calltree lua require'navigator.hierarchy'.calltree(<f-args>)<CR>",
}
local function buf_set_keymap(...)
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
-- executed in on_attach context
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 = lsp_attach_info.bufnr or 0
local function del_keymap(mode, key, ...)
local ks = vim.api.nvim_buf_get_keymap(bufnr, mode)
if vim.tbl_contains(ks, key) then
vim.api.nvim_buf_del_keymap(bufnr, mode, key, ...)
end
end
local function set_keymap(...)
vim.api.nvim_buf_set_keymap(bufnr, ...)
end
-- local function buf_set_option(...)
-- vim.api.nvim_buf_set_option(bufnr, ...)
-- end
for _, v in pairs(user_key) do
local exists = false
for _, default in pairs(key_maps) do
if v.func == default.func and (not default.override) then
default.key, default.override, exists = v.key, true, true
break
local doc_fmt, range_fmt, ccls = check_cap(lsp_attach_info)
if ccls then
vim.list_extend(key_maps, ccls_mappings)
end
if _NgConfigValues.default_mapping ~= false then
for _, v in pairs(user_key) do
trace('binding', v)
local exists = false
for _, default in pairs(key_maps) do
if v.func == default.func and (v.mode or 'n') == (default.mode or 'n') and not default.override then
default.key, default.override, exists = v.key, true, true
break
end
end
if not exists then
table.insert(key_maps, v)
end
end
if not exists then
table.insert(key_maps, v)
end
else
key_maps = _NgConfigValues.keymaps or {}
log('setting maps to ', key_maps)
end
-- log(key_maps)
-- local key_opts = {vim.tbl_deep_extend("force", key_maps, unpack(result))}
local fmtkey, rfmtkey, nrfmtkey
require('navigator.formatting')
for _, value in pairs(key_maps) do
local f = "<Cmd>lua vim.lsp.buf." .. value.func .. "<CR>"
if string.find(value.func, "require") then
f = "<Cmd>lua " .. value.func .. "<CR>"
elseif string.find(value.func, "diagnostic") then
f = "<Cmd>lua vim.lsp." .. value.func .. "<CR>"
if value.doc then
vim.notify('doc field no longer supported in navigator mapping, use desc instead')
end
local k = value.key
local m = value.mode or "n"
set_keymap(m, k, f, opts)
end
if type(value.func) == 'string' then -- deprecated will remove when 0.8 is out
vim.notify('keymap config updated: ' .. value.key .. ' func ' .. value.func .. ' should be a function')
local f = '<Cmd>lua vim.lsp.buf.' .. value.func .. '<CR>'
if string.find(value.func, 'require') or string.find(value.func, 'vim.') then
f = '<Cmd>lua ' .. value.func .. '<CR>'
elseif string.find(value.func, 'diagnostic') then
local diagnostic = '<Cmd>lua vim.'
diagnostic = '<Cmd>lua vim.'
f = diagnostic .. value.func .. '<CR>'
end
-- format setup
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, 'format') then
fmtkey = value.key
end
trace('binding', k, f)
set_keymap(m, k, f, opts)
end
if type(value.func) == 'function' then -- new from 0.7.x
-- neovim 0.7.0
local range_fmt = false
local doc_fmt = false
local ccls = false
-- log(vim.lsp.buf_get_clients(0))
for _, value in pairs(vim.lsp.buf_get_clients(0)) do
if value == nil or value.resolved_capabilities == nil then
return
opts.buffer = key_maps.buffer or value.buffer
if value.desc then
opts.desc = value.desc
end
opts.buffer = bufnr
vim.keymap.set(value.mode or 'n', value.key, value.func, opts)
if string.find(value.desc, 'range format') and value.mode == 'v' then
rfmtkey = value.key
if string.find(value.desc, 'range format') and value.mode == 'n' then
nrfmtkey = value.key
elseif string.find(value.desc, 'format') then
fmtkey = value.key
end
end
end
if value.resolved_capabilities.document_formatting then
doc_fmt = true
end
for _, val in pairs(key_maps) do
local helper_msg = ''
if val.desc then
helper_msg = val.desc
elseif type(val.func) == 'string' then
helper_msg = val.func
end
if value.resolved_capabilities.document_range_formatting then
range_fmt = true
local item = (val.mode or 'n') .. '|' .. val.key .. '|' .. helper_msg
if not vim.tbl_contains(key_maps_help, item) then
table.insert(key_maps_help, (val.mode or 'n') .. '|' .. val.key .. '|' .. helper_msg)
end
end
-- log("override ccls", value.config)
if value.config.name == "ccls" then
-- if user_opts.cap.document_formatting then
if doc_fmt and _NgConfigValues.lsp.format_on_save then
local gn = api.nvim_create_augroup('NavAuGroupFormat', {})
local fopts = _NgConfigValues.lsp.format_options
ccls = true
if not fopts.async and vim.api.nvim_buf_line_count(0) > 4000 then
fopts.async = true
end
api.nvim_create_autocmd({ 'BufWritePre' }, {
group = gn,
buffer = bufnr,
callback = function()
trace('format' .. vim.inspect(fopts))
vim.lsp.buf.format(fopts)
end,
})
elseif fmtkey then
del_keymap('n', fmtkey)
end
if ccls then
-- log("override ccls", ccls_mappings)
for _, value in pairs(ccls_mappings) do
f = "<Cmd>lua " .. value.func .. "<CR>"
local k = value.key
local m = value.mode or "n"
set_keymap(m, k, f, opts)
end
if lsp_attach_info.cap and lsp_attach_info.cap.document_range_formatting then
log('formatting enabled', lsp_attach_info.cap)
end
-- if user_opts.cap.document_formatting then
if doc_fmt then
buf_set_keymap("n", "<space>ff", "<cmd>lua vim.lsp.buf.formatting()<CR>", opts)
if _NgConfigValues.lsp.format_on_save then
vim.cmd([[
aug NavigatorAuFormat
au!
autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting()
aug END
]])
end
if not range_fmt and rfmtkey then
del_keymap('v', rfmtkey)
end
-- if user_opts.cap.document_range_formatting then
if range_fmt then
buf_set_keymap("v", "<space>ff", "<cmd>lua vim.lsp.buf.range_formatting()<CR>", opts)
if not range_fmt and nrfmtkey then
del_keymap('n', nrfmtkey)
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)
vim.api.nvim_exec([[
aug NavigatorDocHlAu
au!
au CmdlineLeave : lua require('navigator.dochighlight').cmd_nohl()
aug END
]], false)
local function autocmd()
local gn = api.nvim_create_augroup('NavAuGroupDocHlAu', {})
api.nvim_create_autocmd({ 'BufWritePre' }, {
group = gn,
callback = require('navigator.dochighlight').cmd_nohl,
})
end
local function set_event_handler(user_opts)
user_opts = user_opts or {}
local file_types =
"c,cpp,h,go,python,vim,sh,javascript,html,css,lua,typescript,rust,javascriptreact,typescriptreact,json,yaml,kotlin,php,dart,nim,terraform"
-- local format_files = "c,cpp,h,go,python,vim,javascript,typescript" --html,css,
vim.api.nvim_command [[augroup nvim_lsp_autos]]
vim.api.nvim_command [[autocmd!]]
for _, value in pairs(event_hdlrs) do
local f = ""
if string.find(value.func, "diagnostic") then
f = "lua vim.lsp." .. value.func
else
f = "lua vim.lsp.buf." .. value.func
M.toggle_lspformat = function(on)
if on == nil then
_NgConfigValues.lsp.format_on_save = not _NgConfigValues.lsp.format_on_save
else
_NgConfigValues.lsp.format_on_save = on
end
if _NgConfigValues.lsp.format_on_save then
if on == nil then
vim.notify('format on save true', vim.lsp.log_levels.INFO)
end
local cmd = "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " .. value.ev
.. " <buffer> silent! " .. f
vim.api.nvim_command(cmd)
vim.cmd([[set eventignore-=BufWritePre]])
else
if on == nil then
vim.notify('format on save false', vim.lsp.log_levels.INFO)
end
vim.cmd([[set eventignore+=BufWritePre]])
end
vim.api.nvim_command([[augroup END]])
end
local M = {}
function M.setup(user_opts)
user_opts = user_opts or _NgConfigValues
if _NgConfigValues.default_mapping == true then
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()
autocmd(user_opts)
set_event_handler(user_opts)
local client = attach_opts.client or {}
local cap = client.server_capabilities or vim.lsp.protocol.make_client_capabilities()
local cap = user_opts.cap or vim.lsp.protocol.make_client_capabilities()
if cap.call_hierarchy or cap.callHierarchy then
vim.lsp.handlers["callHierarchy/incomingCalls"] =
require"navigator.hierarchy".incoming_calls_handler
vim.lsp.handlers["callHierarchy/outgoingCalls"] =
require"navigator.hierarchy".outgoing_calls_handler
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
vim.lsp.handlers["textDocument/references"] = require"navigator.reference".reference_handler
vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler
vim.lsp.handlers["textDocument/definition"] = require"navigator.definition".definition_handler
vim.lsp.handlers['textDocument/references'] = require('navigator.reference').reference_handler
-- vim.lsp.handlers["textDocument/codeAction"] = require"navigator.codeAction".code_action_handler
vim.lsp.handlers['textDocument/definition'] = require('navigator.definition').definition_handler
if cap.declaration then
vim.lsp.handlers["textDocument/declaration"] = require"navigator.definition".declaration_handler
if cap.declarationProvider then
vim.lsp.handlers['textDocument/declaration'] = require('navigator.definition').declaration_handler
end
vim.lsp.handlers["textDocument/typeDefinition"] =
require"navigator.definition".typeDefinition_handler
vim.lsp.handlers["textDocument/implementation"] =
require"navigator.implementation".implementation_handler
vim.lsp.handlers['textDocument/typeDefinition'] = require('navigator.definition').typeDefinition_handler
vim.lsp.handlers['textDocument/implementation'] = require('navigator.implementation').implementation_handler
vim.lsp.handlers["textDocument/documentSymbol"] =
require"navigator.symbols".document_symbol_handler
vim.lsp.handlers["workspace/symbol"] = require"navigator.symbols".workspace_symbol_handler
vim.lsp.handlers["textDocument/publishDiagnostics"] =
require"navigator.diagnostics".diagnostic_handler
-- vim.lsp.handlers['textDocument/documentSymbol'] = require('navigator.symbols').document_symbol_handler
vim.lsp.handlers['workspace/symbol'] = require('navigator.symbols').workspace_symbol_handler
vim.lsp.handlers['textDocument/publishDiagnostics'] = require('navigator.diagnostics').diagnostic_handler
-- TODO: when active signature merge to neovim, remove this setup:
local hassig, sig = pcall(require, "lsp_signature")
if not hassig then
vim.lsp.handlers["textDocument/signatureHelp"] = require"navigator.signature".signature_handler
if
_NgConfigValues.signature_help_cfg and #_NgConfigValues.signature_help_cfg > 0 or _NgConfigValues.lsp_signature_help
then
log('setup signature from navigator')
local hassig, sig = pcall(require, 'lsp_signature')
if hassig then
sig.setup(_NgConfigValues.signature_help_cfg or {})
end
else
vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(require('navigator.signature').signature_handler, {
border = { '', '', '', '', '', '', '', '' },
})
end
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {border = single})
vim.lsp.handlers["textDocument/formatting"] = require"navigator.formatting".format_hdl
api.nvim_create_autocmd({ 'BufWritePre' }, {
group = api.nvim_create_augroup('nvim_nv_event_autos', {}),
buffer = attach_opts.bufnr,
callback = function()
require('navigator.diagnostics').set_diag_loclist(attach_opts.bufnr)
end,
})
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
end
M.get_keymaps_help = function()
local ListView = require('guihua.listview')
local win = ListView:new({
loc = 'top_center',
border = 'none',
prompt = true,
enter = true,
rect = { height = 24, width = 50 },
data = key_maps_help,
})
return win
end
return M

@ -0,0 +1,47 @@
return {
'angularls',
'gopls',
'tsserver',
'flow',
'bashls',
'dockerls',
'julials',
'pylsp',
'pyright',
'jedi_language_server',
'jdtls',
'sumneko_lua',
'vimls',
'html',
'jsonls',
'solargraph',
'cssls',
'yamlls',
'clangd',
'ccls',
'sqls',
'denols',
'graphql',
'dartls',
'dotls',
'kotlin_language_server',
'nimls',
'intelephense',
'vuels',
'volar',
'phpactor',
'omnisharp',
'r_language_server',
'rust_analyzer',
'terraformls',
'svelte',
'texlab',
'clojure_lsp',
'elixirls',
'sourcekit',
'fsautocomplete',
'vls',
'hls',
'tflint',
'terraform_lsp',
}

@ -0,0 +1,89 @@
local vfn = vim.fn
local library = {}
local sumneko_cfg = {
cmd = { 'lua-language-server' },
filetypes = { 'lua' },
on_attach = on_attach,
flags = { allow_incremental_sync = true, debounce_text_changes = 500 },
settings = {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
version = 'LuaJIT',
},
diagnostics = {
enable = true,
-- Get the language server to recognize the `vim` global
globals = { 'vim', 'describe', 'it', 'before_each', 'after_each', 'teardown', 'pending' },
},
completion = { callSnippet = 'Both' },
workspace = {
-- Make the server aware of Neovim runtime files
library = library,
maxPreload = 2000,
preloadFileSize = 40000,
},
telemetry = { enable = false },
},
},
on_new_config = function(cfg, root)
local libs = vim.tbl_deep_extend('force', {}, library)
libs[root] = nil
cfg.settings.Lua.workspace.library = libs
return cfg
end,
}
local function add(lib)
for _, p in pairs(vfn.expand(lib, false, true)) do
p = vim.loop.fs_realpath(p)
if p then
library[p] = true
end
end
end
local function sumneko_lua()
-- add runtime
-- add plugins it may be very slow to add all in path
add('$VIMRUNTIME')
-- add your config
-- local home = vfn.expand("$HOME")
add(vfn.stdpath('config'))
library[vfn.expand('$VIMRUNTIME/lua')] = true
library[vfn.expand('$VIMRUNTIME/lua/vim')] = true
library[vfn.expand('$VIMRUNTIME/lua/vim/lsp')] = true
local on_attach = require('navigator.lspclient.attach').on_attach
local luadevcfg = {
library = {
vimruntime = true, -- runtime path
types = true, -- full signature, docs and completion of vim.api, vim.treesitter, vim.lsp and others
plugins = { 'nvim-treesitter', 'plenary.nvim' },
},
lspconfig = {
-- cmd = {sumneko_binary},
on_attach = on_attach,
},
}
local luadev = {}
local user_luadev = _NgConfigValues.lsp['lua-dev']
if user_luadev then
luadevcfg = vim.tbl_deep_extend('force', luadevcfg, user_luadev)
end
require('navigator.lazyloader').load('lua-dev.nvim', 'folke/lua-dev.nvim')
local ok, l = pcall(require, 'lua-dev')
if ok and l then
luadev = l.setup(luadevcfg)
end
sumneko_cfg = vim.tbl_deep_extend('force', sumneko_cfg, luadev)
return sumneko_cfg
end
return {
sumneko_lua = sumneko_lua,
}

@ -1,34 +1,35 @@
local M = {}
local util = require "navigator.util"
local gutil = require "guihua.util"
local lsp = require "vim.lsp"
local api = vim.api
local log = require"navigator.util".log
local lerr = require"navigator.util".error
local trace = require"navigator.util".trace
local symbol_kind = require"navigator.lspclient.lspkind".symbol_kind
local cwd = vim.fn.getcwd(0)
local is_win = vim.loop.os_uname().sysname:find("Windows")
local util = require('navigator.util')
local path_sep = require"navigator.util".path_sep()
local path_cur = require"navigator.util".path_cur()
cwd = gutil.add_pec(cwd)
ts_nodes = {}
ts_nodes_time = {}
local gutil = require('guihua.util')
local lsp = require('vim.lsp')
local api = vim.api
local log = require('navigator.util').log
local lerr = require('navigator.util').error
local trace = require('navigator.util').trace
local symbol_kind = require('navigator.lspclient.lspkind').symbol_kind
local cwd = vim.loop.cwd()
local TS_analysis_enabled = require"navigator".config_values().treesitter_analysis
local is_win = vim.loop.os_uname().sysname:find('Windows')
local path_sep = require('navigator.util').path_sep()
local path_cur = require('navigator.util').path_cur()
cwd = gutil.add_pec(cwd)
local ts_nodes = require('navigator.lru').new(1000, 1024 * 1024)
local ts_nodes_time = require('navigator.lru').new(1000)
local TS_analysis_enabled = require('navigator').config_values().treesitter_analysis
local nts = require('navigator.treesitter')
-- extract symbol from range
function M.get_symbol(text, range)
if range == nil then
return ""
return ''
end
return string.sub(text, range.start.character + 1, range['end'].character)
end
local function check_lhs(text, symbol)
local find = require'guihua.util'.word_find
local find = require('guihua.util').word_find
local s = find(text, symbol)
local eq = string.find(text, '=') or 0
local eq2 = string.find(text, '==') or 0
@ -38,7 +39,7 @@ local function check_lhs(text, symbol)
return false
end
if s < eq and eq ~= eq2 then
log(symbol, "modified")
trace(symbol, 'modified')
end
if eq == eq3 + 1 then
return false
@ -52,18 +53,20 @@ local function check_lhs(text, symbol)
end
function M.lines_from_locations(locations, include_filename)
local fnamemodify = (function(filename)
local fnamemodify = function(filename)
if include_filename then
return vim.fn.fnamemodify(filename, ":~:.") .. ":"
return vim.fn.fnamemodify(filename, ':~:.') .. ':'
else
return ""
return ''
end
end)
end
local lines = {}
for _, loc in ipairs(locations) do
table.insert(lines, (fnamemodify(loc["filename"]) .. loc["lnum"] .. ":" .. loc["col"] .. ": "
.. vim.trim(loc["text"])))
table.insert(
lines,
(fnamemodify(loc['filename']) .. loc['lnum'] .. ':' .. loc['col'] .. ': ' .. vim.trim(loc['text']))
)
end
return lines
@ -71,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
@ -81,23 +85,23 @@ function M.symbols_to_items(result)
item.name = result[i].name -- symbol name
item.text = result[i].name
if kind ~= nil then
item.text = kind .. ": " .. item.text
item.text = kind .. ': ' .. item.text
end
if not item.filename then
item.filename = vim.uri_to_fname(item.uri)
end
item.filename = vim.uri_to_fname(item.uri)
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)
log('range not set', result[i], item)
end
item.lnum = item.range.start.line + 1
if item.containerName ~= nil then
item.text = "" .. item.containerName .. item.text
item.text = '' .. item.containerName .. item.text
end
table.insert(locations, item)
end
end
-- local items = locations_to_items(locations)
-- log(locations[1])
return locations
end
@ -119,20 +123,20 @@ 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
goto continue
break
end
end
::continue::
if supported_client then
return true
else
if #clients == 0 then
print("LSP: no client attached")
log('LSP: no client attached')
else
print("LSP: server does not support " .. feature)
trace('LSP: server does not support ' .. feature)
end
return false
end
@ -141,89 +145,114 @@ 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)
handler(err, method, extract_result(results_lsp), nil, nil)
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 ts_enabled, _ = pcall(require, "nvim-treesitter.locals")
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
lerr("ts not enabled")
lerr('ts not enabled')
return nil
end
local ts_func = require"navigator.treesitter".buf_func
local ts_func = nts.buf_func
local bufnr = vim.uri_to_bufnr(uri)
local x = os.clock()
trace(ts_nodes)
if ts_nodes[uri] ~= nil then
local t = ts_nodes_time[uri]
local tsnodes = ts_nodes:get(uri)
if tsnodes ~= nil then
trace('get data from cache')
local t = ts_nodes_time:get(uri) or 0
local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname)
if modified <= t then
trace(t, modified)
return ts_nodes[uri]
return tsnodes
else
ts_nodes:delete(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)
trace('! load buf !', uri, bufnr)
vim.fn.bufload(bufnr)
-- vim.api.nvim_buf_detach(bufnr) -- if user opens the buffer later, it prevents user attach event
unload = true
end
local funcs = ts_func(bufnr)
if unload then
local cmd = string.format("bd %d", bufnr)
trace(cmd)
-- vim.cmd(cmd) -- todo: not sure if it is needed
end
ts_nodes[uri] = funcs
ts_nodes_time[uri] = os.time()
trace(funcs, ts_nodes)
trace(string.format("elapsed time: %.4f\n", os.clock() - x)) -- how long it tooks
return funcs
unload_bufnr = bufnr
end
ts_nodes:set(uri, funcs)
ts_nodes_time:set(uri, os.time())
trace(funcs, ts_nodes:get(uri))
trace(string.format('elapsed time: %.4f\n', os.clock() - x)) -- how long it tooks
return funcs, unload_bufnr
end
local function ts_defination(uri, range)
local ts_enabled, _ = pcall(require, "nvim-treesitter.locals")
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
lerr("ts not enabled")
lerr('ts not enabled')
return nil
end
local ts_def = require"navigator.treesitter".find_definition
local key = string.format('%s_%d_%d_%d', uri, range.start.line, range.start.character, range['end'].line)
local tsnodes = ts_nodes:get(key)
local ftime = ts_nodes_time:get(key)
local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname)
if tsnodes and modified <= ftime then
log('ts def from cache')
return tsnodes
end
if optional then
return
end
local ts_def = nts.find_definition
local bufnr = vim.uri_to_bufnr(uri)
local x = os.clock()
trace(ts_nodes)
local unload = false
if not api.nvim_buf_is_loaded(bufnr) then
log("! load buf !", uri, bufnr)
log('! load buf !', uri, bufnr)
vim.fn.bufload(bufnr)
unload = true
end
local def_range = ts_def(range, bufnr)
local def_range = ts_def(range, bufnr) or {}
if unload then
local cmd = string.format("bd %d", bufnr)
log(cmd)
-- vim.cmd(cmd) -- todo: not sure if it is needed
unload_bufnr = bufnr
end
trace(string.format(" ts def elapsed time: %.4f\n", os.clock() - x), def_range) -- how long it takes
return def_range
trace(string.format(' ts def elapsed time: %.4f\n', os.clock() - x), def_range) -- how long it takes
ts_nodes:set(key, def_range)
ts_nodes_time:set(key, x)
return def_range, unload_bufnr
end
local function find_ts_func_by_range(funcs, range)
log(funcs, range)
if funcs == nil or range == nil then
return nil
end
@ -232,24 +261,19 @@ local function find_ts_func_by_range(funcs, range)
for _, value in pairs(funcs) do
local func_range = value.node_scope
-- note treesitter is C style
if func_range and func_range.start.line <= range.start.line and func_range['end'].line
>= range['end'].line then
if func_range and func_range.start.line <= range.start.line and func_range['end'].line >= range['end'].line then
table.insert(result, value)
end
end
return result
end
function M.locations_to_items(locations)
if not locations or vim.tbl_isempty(locations) then
print("list not avalible")
return
end
local width = 4
local items = {} -- lsp.util.locations_to_items(locations)
-- items and locations may not matching
local function order_locations(locations)
table.sort(locations, function(i, j)
if i == 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
@ -259,51 +283,152 @@ function M.locations_to_items(locations)
return i.uri < j.uri
end
end)
return locations
end
local function slice_locations(locations, max_items)
local cut = -1
if #locations > max_items then
local uri = locations[max_items]
for i = max_items + 1, #locations do
if uri ~= locations[i] then
cut = i
break
end
end
end
local first_part, second_part = locations, {}
if cut > 1 and cut < #locations then
first_part = vim.list_slice(locations, 1, cut)
second_part = vim.list_slice(locations, cut + 1, #locations)
end
return first_part, second_part
end
-- local function test_locations()
-- local locations = {
-- { uri = '1', range = { start = { line = 1 } } },
-- { uri = '2', range = { start = { line = 2 } } },
-- { uri = '2', range = { start = { line = 3 } } },
-- { uri = '1', range = { start = { line = 3 } } },
-- { uri = '1', range = { start = { line = 4 } } },
-- { uri = '3', range = { start = { line = 4 } } },
-- { uri = '3', range = { start = { line = 4 } } },
-- }
-- local second_part
-- order_locations(locations)
-- local locations, second_part = slice_locations(locations, 3)
-- log(locations, second_part)
-- end
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)
ctx = ctx or {}
local max_items = ctx.max_items or 100000 --
local client_id = ctx.client_id or 1
local enc = util.encoding(client_id)
if not locations or vim.tbl_isempty(locations) then
vim.notify('list not avalible', vim.lsp.log_levels.WARN)
return
end
local width = 4
local items = {}
-- items and locations may not matching
local uri_def = {}
order_locations(locations)
local second_part
locations, second_part = slice_locations(locations, max_items)
trace(locations)
vim.cmd([[set eventignore+=FileType]])
local unload_bufnrs = {}
for i, loc in ipairs(locations) do
local funcs = nil
local item = lsp.util.locations_to_items({loc})[1]
item.uri = locations[i].uri
local item = lsp.util.locations_to_items({ loc }, enc)[1]
item.range = locations[i].range or locations[i].targetRange
item.uri = locations[i].uri or locations[i].targetUri
item.definition = locations[i].definition
item.range = locations[i].range
if is_win then
log(item.uri) -- file:///C:/path/to/file
log(cwd)
end
-- only load top 30 file.
local proj_file = item.uri:find(cwd) or is_win or i < 30
if TS_analysis_enabled and proj_file then
funcs = ts_functions(item.uri)
local proj_file = item.uri:find(cwd) or is_win or i < _NgConfigValues.treesitter_analysis_max_num
local unload, def
local context = ''
if TS_analysis_enabled and proj_file and not ctx.no_show then
local ts_context = nts.ref_context
local bufnr = vim.uri_to_bufnr(item.uri)
if not api.nvim_buf_is_loaded(bufnr) then
log('! load buf !', item.uri, bufnr)
vim.fn.bufload(bufnr)
unload = bufnr
end
context = ts_context({ bufnr = bufnr, pos = item.range }) or ''
log(context)
if uri_def[item.uri] == nil or uri_def[item.uri] == {} then
-- TODO: unload buffers
if unload then
table.insert(unload_bufnrs, unload)
unload = nil
end
if not uri_def[item.uri] then
-- find def in file
local def = ts_defination(item.uri, item.range)
def, unload = ts_definition(item.uri, item.range, 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
for i = 1, #items do
if items[i].uri == item.uri and items[i].range.start.line == def.start.line then
items[i].definition = true
for j = 1, #items do
if items[j].definition ~= nil then
if items[j].uri == item.uri and items[j].range.start.line == def.start.line then
items[j].definition = true
end
end
end
end
else
if uri_def[item.uri] == false then
uri_def[item.uri] = {} -- no def in file, TODO: it is tricky the definition is in another file and it is the
-- only occurrence
else
uri_def[item.uri] = false -- no def in file
end
end
if unload then
table.insert(unload_bufnrs, unload)
end
end
trace(uri_def[item.uri], item.range) -- set to log if need to get all in rnge
local def = uri_def[item.uri]
if def and def.start and item.range then
if def.start.line == item.range.start.line then
log("ts def in current line")
local def1 = uri_def[item.uri]
if def1 and def1.start and item.range then
if def1.start.line == item.range.start.line then
log('ts def in current line')
item.definition = true
end
end
end
item.filename = assert(vim.uri_to_fname(item.uri))
local filename = item.filename:gsub(cwd .. path_sep, path_cur, 1)
if ctx.no_show then
local shorten = require('guihua.util').shorten
filename = shorten(filename)
end
item.display_filename = filename or item.filename
item.call_by = find_ts_func_by_range(funcs, item.range)
item.call_by = context -- find_ts_func_by_range(funcs, item.range)
item.rpath = util.get_relative_path(cwd, item.filename)
width = math.max(width, #item.text)
item.symbol_name = M.get_symbol(item.text, item.range)
@ -312,18 +437,39 @@ function M.locations_to_items(locations)
table.insert(items, item)
end
trace(uri_def)
return items, width + 24 -- TODO handle long line?
-- defer release new open buffer
if #unload_bufnrs > 10 then -- load too many?
vim.defer_fn(function()
for i, bufnr_unload in ipairs(unload_bufnrs) do
if api.nvim_buf_is_loaded(bufnr_unload) and i > 10 then
api.nvim_buf_delete(bufnr_unload, { unload = true })
end
end
end, 100)
end
vim.cmd([[set eventignore-=FileType]])
trace(items)
return items, width + 30, second_part -- TODO handle long line?
end
function M.symbol_to_items(locations)
if not locations or vim.tbl_isempty(locations) then
print("list not avalible")
vim.notify('list not avalible', vim.lsp.log_levels.WARN)
return
end
local items = {} -- lsp.util.locations_to_items(locations)
local items = {}
-- items and locations may not matching
table.sort(locations, function(i, j)
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
@ -334,7 +480,7 @@ function M.symbol_to_items(locations)
end
end)
for i, _ in ipairs(locations) do
local item = {} -- lsp.util.locations_to_items({loc})[1]
local item = {}
item.uri = locations[i].uri
item.range = locations[i].range
item.filename = assert(vim.uri_to_fname(item.uri))
@ -348,4 +494,12 @@ function M.symbol_to_items(locations)
return items
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.request(method, ref_params, hdlr, bufnr)
end)
end
return M

@ -1,14 +1,13 @@
parameter
-- [[ -- parameter
{
position = {
character = 6,
line = 13
},
textDocument = {
uri = "file:///Users/ray.xu/lsp_test/go/interface.go"
uri = "file:///Users/username/lsp_test/go/interface.go"
}
}
} ]]
--[[ -- incomming/outgoing
@ -38,7 +37,7 @@ dir from result { {
line = 39
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
},
fromRanges = { {
end = {
@ -75,7 +74,7 @@ dir from result { {
line = 43
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
},
fromRanges = { {
end = {
@ -103,8 +102,8 @@ dir from result { {
-- [[ locations/reference from lsp
-- locations/reference from lsp
-- [[
{ {
range = {
["end"] = {
@ -116,7 +115,7 @@ dir from result { {
line = 26
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
}, {
range = {
["end"] = {
@ -128,9 +127,9 @@ dir from result { {
line = 35
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
} }
--]]
]] --
-- definition
@ -146,7 +145,7 @@ definition.lua:9: { {
line = 33
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
} }
@ -165,7 +164,7 @@ definition.lua:9: { {
line = 33
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
} }
}
}
@ -186,7 +185,7 @@ definition.lua:9: { {
line = 7
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
},
name = "command-line-arguments.geometry"
}, {
@ -203,7 +202,7 @@ definition.lua:9: { {
line = 12
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
},
name = "command-line-arguments.rect"
}, {
@ -220,7 +219,7 @@ definition.lua:9: { {
line = 43
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
},
name = "command-line-arguments.main"
}, {
@ -237,7 +236,7 @@ definition.lua:9: { {
line = 8
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
},
name = "command-line-arguments.geometry.area"
}, {
@ -254,7 +253,7 @@ definition.lua:9: { {
line = 13
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
},
name = "command-line-arguments.rect.width"
}}
@ -298,7 +297,7 @@ definition.lua:9: { {
severity = 1,
source = "compiler"
} },
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
uri = "file:///Users/username/lsp-test/go/interface.go"
}
@ -320,7 +319,7 @@ definition.lua:9: { {
}
} },
textDocument = {
uri = "file:///Users/ray.xu/lsp_test/go/ref.go",
uri = "file:///Users/username/lsp_test/go/ref.go",
version = 0
}
} }
@ -341,7 +340,7 @@ definition.lua:9: { {
line = 4
}
},
URI = "file:///Users/ray.xu/lsp_test/go/ref.go"
URI = "file:///Users/username/lsp_test/go/ref.go"
} },
command = "gopls.apply_fix",
title = "undeclared name: rect"
@ -383,7 +382,7 @@ definition.lua:9: { {
line = 4
}
},
URI = "file:///Users/ray.xu/lsp_test/go/ref.go"
URI = "file:///Users/username/lsp_test/go/ref.go"
} },
command = "gopls.apply_fix",
title = "Extract to function"
@ -433,7 +432,7 @@ definition.lua:9: { {
}
} },
textDocument = {
uri = "file:///Users/ray.xu/lsp_test/go/ref.go",
uri = "file:///Users/username/lsp_test/go/ref.go",
version = 0
}
} }
@ -457,7 +456,7 @@ definition.lua:9: { {
}
} },
textDocument = {
uri = "file:///Users/ray.xu/lsp_test/go/ref.go",
uri = "file:///Users/username/lsp_test/go/ref.go",
version = 0
}
} }
@ -969,3 +968,503 @@ definition.lua:9: { {
range = { 25, 32 }
},
}}
-- get diagnostics response
{ {
code = "UndeclaredName",
codeDescription = {
href = "https://pkg.go.dev/golang.org/x/tools/internal/typesinternal?utm_source=gopls#UndeclaredName"
},
col = 16,
display_filename = "./interface.go",
filename = "/Users/username/lsp_test/go/interface.go",
lnum = 38,
message = "undeclared name: geometry",
range = {
end = {
character = 23,
line = 37
},
start = {
character = 15,
line = 37
}
},
severity = 1,
source = "compiler",
tags = { 1 },
text = "🈲func measure(g geometry) int {📛undeclared name: geometry",
uri = "file:///Users/username/lsp_test/go/interface.go"
},{
code = "UndeclaredName",
codeDescription = {
href = "https://pkg.go.dev/golang.org/x/tools/internal/typesinternal?utm_source=gopls#UndeclaredName"
},
col = 9,
display_filename = "./interface.go",
filename = "/Users/username/lsp_test/go/interface.go",
lnum = 30,
message = "undeclared name: circle",
range = {
end = {
character = 14,
line = 29
},
start = {
character = 8,
line = 29
}
},
severity = 1,
source = "compiler",
tags = { 1 },
text = "🈲func (c circle) area() float64 {📛undeclared name: circle",
uri = "file:///Users/username/lsp_test/go/interface.go"
} }
-- code lens
{ {
command = {
arguments = { {
URIs = { "file:///Users/username/lsp_test/go/go.mod" }
} },
command = "gopls.tidy",
title = "Run go mod tidy"
},
range = {
end = {
character = 13,
line = 0
},
start = {
character = 0,
line = 0
}
}
}, {
command = {
arguments = { {
URI = "file:///Users/username/lsp_test/go/go.mod"
} },
command = "gopls.vendor",
title = "Create vendor directory"
},
range = {
end = {
character = 13,
line = 0
},
start = {
character = 0,
line = 0
}
}
}, {
command = {
arguments = { {
Modules = { "github.com/containerd/containerd", "github.com/docker/docker", "github.com/docker/go-connections", "github.com/fatih/gomodifytags", "github.com/google/go-cmp", "github.com/moby/term", "github.com/morikuni/aec", "github.com/sirupsen/logrus", "github.com/sourcegraph/jsonrpc2", "golang.org/x/net", "golang.org/x/sys", "golang.org/x/text", "golang.org/x/time", "golang.org/x/tools", "google.golang.org/grpc" },
URI = "file:///Users/username/lsp_test/go/go.mod"
} },
command = "gopls.check_upgrades",
title = "Check for upgrades"
},
range = {
end = {
character = 1,
line = 20
},
start = {
character = 0,
line = 4
}
}
}, {
command = {
arguments = { {
AddRequire = false,
GoCmdArgs = { "-d", "-u", "-t", "./..." },
URI = "file:///Users/username/lsp_test/go/go.mod"
} },
command = "gopls.upgrade_dependency",
title = "Upgrade transitive dependencies"
},
range = {
end = {
character = 1,
line = 20
},
start = {
character = 0,
line = 4
}
}
}, {
command = {
arguments = { {
AddRequire = false,
GoCmdArgs = { "-d", "github.com/containerd/containerd", "github.com/docker/docker", "github.com/docker/go-connections", "github.com/fatih/gomodifytags", "github.com/google/go-cmp", "github.com/moby/term", "github.com/morikuni/aec", "github.com/sirupsen/logrus", "github.com/sourcegraph/jsonrpc2", "golang.org/x/net", "golang.org/x/sys", "golang.org/x/text", "golang.org/x/time", "golang.org/x/tools", "google.golang.org/grpc" },
URI = "file:///Users/username/lsp_test/go/go.mod"
} },
command = "gopls.upgrade_dependency",
title = "Upgrade direct dependencies"
},
range = {
end = {
character = 1,
line = 20
},
start = {
character = 0,
line = 4
}
}
} }
-- rust code lens
{ {
result = { {
command = {
arguments = { {
args = {
cargoArgs = { "run", "--package", "hello", "--bin", "hello" },
cargoExtraArgs = {},
executableArgs = {},
workspaceRoot = "/Users/glsp_test/rust"
},
kind = "cargo",
label = "run hello",
location = {
targetRange = {
end = {
character = 1,
line = 68
},
start = {
character = 0,
line = 45
}
},
targetSelectionRange = {
end = {
character = 7,
line = 45
},
start = {
character = 3,
line = 45
}
},
targetUri = "file:///Users/glsp_test/rust/src/main.rs"
}
} },
command = "rust-analyzer.runSingle",
title = "▶︎ Run "
},
range = {
end = {
character = 7,
line = 45
},
start = {
character = 3,
line = 45
}
}
}, {
command = {
arguments = { {
args = {
cargoArgs = { "run", "--package", "hello", "--bin", "hello" },
cargoExtraArgs = {},
executableArgs = {},
workspaceRoot = "/Users/glsp_test/rust"
},
kind = "cargo",
label = "run hello",
location = {
targetRange = {
end = {
character = 1,
line = 68
},
start = {
character = 0,
line = 45
}
},
targetSelectionRange = {
end = {
character = 7,
line = 45
},
start = {
character = 3,
line = 45
}
},
targetUri = "file:///Users/glsp_test/rust/src/main.rs"
}
} },
command = "rust-analyzer.debugSingle",
title = "Debug"
},
range = {
end = {
character = 7,
line = 45
},
start = {
character = 3,
line = 45
}
}
}, {
data = {
impls = {
position = {
character = 6,
line = 2
},
textDocument = {
uri = "file:///Users/glsp_test/rust/src/main.rs"
}
}
},
range = {
end = {
character = 10,
line = 2
},
start = {
character = 6,
line = 2
}
}
}, {
data = {
impls = {
position = {
character = 7,
line = 28
},
textDocument = {
uri = "file:///Users/glsp_test/rust/src/main.rs"
}
}
},
range = {
end = {
character = 10,
line = 28
},
start = {
character = 7,
line = 28
}
}
}, {
data = {
impls = {
position = {
character = 7,
line = 31
},
textDocument = {
uri = "file:///Users/glsp_test/rust/src/main.rs"
}
}
},
range = {
end = {
character = 10,
line = 31
},
start = {
character = 7,
line = 31
}
}
} }
} }
type_definition = true,
workspace_folder_properties = {
changeNotifications = false,
supported = false
},
workspace_symbol = true
}
...ack/packer/opt/navigator.lua/lua/navigator/hierarchy.lua:13: call_hierarchy { {
fromRanges = { {
end = {
character = 68,
line = 53
},
start = {
character = 54,
line = 53
}
} },
to = {
detail = "pub fn unwrap_or_else<F>(self, f: F) -> T\nwhere\n F: FnOnce<(), Output = T>,",
kind = 12,
name = "unwrap_or_else",
range = {
end = {
character = 5,
line = 764
},
start = {
character = 4,
line = 748
}
},
selectionRange = {
end = {
character = 25,
line = 759
},
start = {
character = 11,
line = 759
}
},
uri = "file:///Users/g.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs"
}
}, {
fromRanges = { {
end = {
character = 33,
line = 54
},
start = {
character = 27,
line = 54
}
}, {
end = {
character = 28,
line = 55
},
start = {
character = 22,
line = 55
}
} },
to = {
detail = "pub const fn unwrap(self) -> T",
kind = 12,
name = "unwrap",
range = {
end = {
character = 5,
line = 723
},
start = {
character = 4,
line = 688
}
},
selectionRange = {
end = {
character = 23,
line = 718
},
start = {
character = 17,
line = 718
}
},
uri = "file:///Users/g.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs"
}
} }
call_hierarchy { {
from = {
detail = "fn test2()",
kind = 12,
name = "test2",
range = {
end = {
character = 20,
line = 43
},
start = {
character = 0,
line = 43
}
},
selectionRange = {
end = {
character = 8,
line = 43
},
start = {
character = 3,
line = 43
}
},
uri = "file:///Users/glsp_test/rust/src/main.rs"
},
fromRanges = { {
end = {
character = 16,
line = 43
},
start = {
character = 12,
line = 43
}
} }
}, {
from = {
detail = "fn test3()",
kind = 12,
name = "test3",
range = {
end = {
character = 29,
line = 44
},
start = {
character = 0,
line = 44
}
},
selectionRange = {
end = {
character = 8,
line = 44
},
start = {
character = 3,
line = 44
}
},
uri = "file:///Users/glsp_test/rust/src/main.rs"
},
fromRanges = { {
end = {
character = 16,
line = 44
},
start = {
character = 12,
line = 44
}
} }
} }

@ -1,60 +1,260 @@
local util = require "navigator.util"
local util = require('navigator.util')
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
-- 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
-- local partial = util.partial
-- local cwd = vim.fn.getcwd(0)
-- local cwd = vim.loop.cwd()
-- local lsphelper = require "navigator.lspwrapper"
local locations_to_items = lsphelper.locations_to_items
-- vim.api.nvim_set_option("navtator_options", {width = 90, height = 60, location = require "navigator.location".center})
-- local options = vim.g.navtator_options or {width = 60, height = 40, location = location.center}
local function ref_hdlr(err, api, locations, num, bufnr)
local M = {}
local ref_view = function(err, locations, ctx, cfg)
cfg = cfg or {}
local truncate = cfg and cfg.truncate or 20
local opts = {}
-- log("arg1", arg1)
-- log(api)
trace(locations)
trace('arg1', err, ctx, locations)
-- log(#locations, locations[1])
if ctx.combine then
-- wait for both reference and definition LSP request
if ctx.results == nil then
return
end
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 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 and references.result and #references.result > 0 then
local refs = references.result
vim.list_extend(locations, refs)
end
err = nil
trace(locations)
-- lets de-dup first 10 elements. some lsp does not recognize definition and reference difference
locations = util.dedup(locations)
trace(locations)
end
-- log("num", num)
-- log("bfnr", bufnr)
if err ~= nil then
print('ref callback error, lsp may not ready', err)
vim.notify(
'lsp ref callback error' .. vim.inspect(err) .. vim.inspect(ctx) .. vim.inspect(locations),
vim.lsp.log_levels.WARN
)
log('ref callback error, lsp may not ready', err, ctx, vim.inspect(locations))
return
end
if type(locations) ~= 'table' then
log(api)
log(locations)
log("num", num)
log("bfnr", bufnr)
error(locations)
log('ctx', ctx)
vim.notify('incorrect setup' .. vim.inspect(locations), vim.lsp.log_levels.WARN)
return
end
if locations == nil or vim.tbl_isempty(locations) then
print "References not found"
vim.notify('References not found', vim.lsp.log_levels.INFO)
return
end
local items, width = locations_to_items(locations)
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
ctx.max_items = truncate
local items, width, second_part = locations_to_items(locations, ctx)
local thread_items = vim.deepcopy(items)
log('splits: ', #items, #second_part)
local ft = vim.api.nvim_buf_get_option(ctx.bufnr or 0, 'ft')
local wwidth = vim.api.nvim_get_option("columns")
local wwidth = vim.api.nvim_get_option('columns')
local mwidth = _NgConfigValues.width
width = math.min(width + 30, 120, math.floor(wwidth * mwidth))
gui.new_list_view({
width = math.min(width + 30, math.floor(wwidth * mwidth))
-- log(items)
-- log(width)
opts = {
total = #locations,
items = items,
rawdata = false,
ft = ft,
width = width,
api = "Reference",
enable_preview_edit = true
})
api = 'Reference',
enable_preview_edit = true,
}
local listview
if not ctx.no_show then
listview = gui.new_list_view(opts)
if listview == nil then
vim.notify('failed to create preview windows', vim.lsp.log_levels.INFO)
return
end
end
if ctx.no_show then
opts.side_panel = true
local data = require('navigator.render').prepare_for_render(items, opts)
return data
end
-- trace("update items", listview.ctrl.class)
local nv_ref_async
nv_ref_async = vim.loop.new_async(vim.schedule_wrap(function()
log('$$$$$$$$ seperate thread... $$$$$$$$')
if vim.tbl_isempty(second_part) then
return
end
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)
else
log('invalid asy', nv_ref_async)
end
end))
vim.defer_fn(function()
vim.loop.new_thread(function(asy)
asy:send()
end, nv_ref_async)
end, 100)
return listview, items, width
end
local async_reference_request = function()
local ref_hdlr = function(err, locations, ctx, cfg)
_NgConfigValues.closer = nil
trace(err, locations, ctx, cfg)
if ctx.no_show then
return ref_view(err, locations, ctx, cfg)
end
M.async_hdlr = vim.loop.new_async(vim.schedule_wrap(function()
ref_view(err, locations, ctx, cfg)
if M.async_hdlr:is_active() then
M.async_hdlr:close()
end
end))
M.async_hdlr:send()
end
local async_ref = function()
local ref_params = vim.lsp.util.make_position_params()
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 {}
ctx.results = results
ctx.combine = true
ref_view(err, result, ctx, config)
end) -- return asyncresult, canceller
end
local ref_req = function()
if _NgConfigValues.closer ~= nil then
-- do not call it twice
_NgConfigValues.closer()
end
local ref_params = vim.lsp.util.make_position_params()
ref_params.context = {includeDeclaration = true}
lsp.call_async("textDocument/references", ref_params, ref_hdlr) -- return asyncresult, canceller
-- lsp.call_async("textDocument/definition", ref_params, ref_hdlr) -- return asyncresult, canceller
ref_params.context = { includeDeclaration = true }
-- lsp.call_async("textDocument/references", ref_params, ref_hdlr) -- return asyncresult, canceller
local bufnr = vim.api.nvim_get_current_buf()
log('bufnr', bufnr)
local ids, closer = vim.lsp.buf_request(bufnr, 'textDocument/references', ref_params, ref_hdlr)
log(ids)
_NgConfigValues.closer = closer
return ids, closer
end
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, _, _)
if client.server_capabilities.referencesProvider then
client.request('textDocument/references', ref_params, ref_hdlr, bufnr)
end
end)
end
local function side_panel()
local Panel = require('guihua.panel')
local currentWord = vim.fn.expand('<cword>')
local p = Panel:new({
scope = 'range',
header = '' .. currentWord .. ' ref ',
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 ref_params = vim.lsp.util.make_position_params()
local sync_req = require('navigator.lspwrapper').call_sync
return sync_req(
'textDocument/references',
ref_params,
{ timeout = 1000, bufnr = bufnr, no_show = true },
vim.lsp.with(function(err, locations, ctx, cfg)
cfg.side_panel = true
return ref_hdlr(err, locations, ctx, cfg)
end, { no_show = true })
)
end,
})
p:open(true)
end
return {reference_handler = ref_hdlr, show_reference = async_reference_request}
return {
side_panel = side_panel,
reference_handler = ref_hdlr,
reference = ref_req,
ref_view = ref_view,
async_ref = async_ref,
all_ref = ref,
}

@ -1,43 +1,16 @@
-- 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 util = require('navigator.util')
-- local rename_prompt = 'Rename -> '
M.rename = function()
local current_name = vim.fn.expand("<cword>")
local bufnr = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(bufnr, "buftype", "prompt")
vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe")
vim.api.nvim_buf_add_highlight(bufnr, -1, "NGPreviewTitle", 0, 0, #rename_prompt)
vim.fn.prompt_setprompt(bufnr, rename_prompt)
local width = #current_name + #rename_prompt + 10
local winnr = vim.api.nvim_open_win(bufnr, true, {
relative = "cursor",
width = width,
height = 1,
row = -3,
col = 1,
style = "minimal",
border = "single"
})
vim.api.nvim_win_set_option(winnr, "winhl", "Normal:Floating")
util.map("n", "<ESC>", "<cmd>bd!<CR>", {silent = true, buffer = true})
util.map({"n", "i"}, "<CR>", "<cmd>lua require('navigator.rename').callback()<CR>",
{silent = true, buffer = true})
util.map("i", "<BS>", "<ESC>xi", {silent = true, buffer = true})
vim.cmd(string.format("normal i%s", current_name))
end
local input = vim.ui.input
M.callback = function()
local new_name = vim.trim(vim.fn.getline("."):sub(#rename_prompt + 1, -1))
vim.cmd [[stopinsert]]
vim.cmd [[bd!]]
if #new_name == 0 or new_name == vim.fn.expand("<cword>") then
return
end
local params = vim.lsp.util.make_position_params()
params.newName = new_name
vim.lsp.buf_request(0, "textDocument/rename", params)
vim.ui.input = require('guihua.floating').input
vim.lsp.buf.rename()
vim.defer_fn(function()
vim.ui.input = input
end, 1000)
end
-- M.callback()
return M

@ -1,13 +1,18 @@
local log = require"guihua.log".info
local trace = require"guihua.log".trace
local util = require('navigator.util')
local log = util.log
local trace = util.trace
local clone = require('guihua.util').clone
local M = {}
local clone = require'guihua.util'.clone
local function filename(url)
return url:match("^.+/(.+)$") or url
if url == nil then
return ''
end
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
@ -35,7 +40,6 @@ local function get_pads(win_width, text, postfix)
i = i + rem * 10
-- log(i)
end
end
if i > 3 then
@ -49,78 +53,80 @@ end
function M.prepare_for_render(items, opts)
opts = opts or {}
if items == nil or #items < 1 then
print("no item found or empty fields")
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 icon = ""
local lspapi = opts.api or ""
local total = opts.total
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 math.floor(vim.api.nvim_get_option('columns') * 0.8)
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
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
if last_summary_idx == 1 then
lspapi_display = items[i].symbol_name .. ' ' .. lspapi_display
trace(items[1], lspapi_display, display_items[last_summary_idx])
end
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
space, trim = get_pads(opts.width, icon .. ' ' .. dfn, lspapi_display .. ' 12')
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
display_items[last_summary_idx].text = string.format("%s %s%s%s %i", icon,
display_items[last_summary_idx]
.display_filename, space,
lspapi_display, total_ref_in_file)
local api_disp = string.format(
'%s %s%s%s %i',
icon,
display_items[last_summary_idx].display_filename,
space,
lspapi_display,
total_ref_in_file
)
if total then
api_disp = api_disp .. ' of: ' .. tostring(total)
end
display_items[last_summary_idx].text = api_disp
total_ref_in_file = total_ref_in_file + 1
else
lspapi_display = lspapi
item = clone(items[i])
space, trim = get_pads(opts.width, icon .. ' ' .. item.display_filename,
lspapi_display .. ' 12')
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)
@ -129,58 +135,48 @@ 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
trace(ts_report, header_len)
item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '')
if item.call_by ~= nil and #item.call_by > 0 then
trace("call_by:", #item.call_by)
for _, value in pairs(item.call_by) do
if value.node_text then
local txt = value.node_text:gsub('%s*[%[%(%{]*%s*$', '')
local endwise = '{}'
if value.type == 'method' or value.type == 'function' then
endwise = '()'
local syb = items[i].symbol_name
if txt == items[i].symbol_name or (#txt > #syb and txt:sub(#txt - #syb + 1) == syb) then
if ts_report ~= _NgConfigValues.icons.value_definition .. ' ' then
ts_report = ts_report .. _NgConfigValues.icons.value_definition .. ' '
end
header_len = #ts_report + 1
else
ts_report = ts_report .. ''
end
end
if #ts_report > header_len then
ts_report = ts_report .. ''
end
ts_report = ts_report .. value.kind .. txt .. endwise
trace(item)
end
end
if item.call_by ~= nil and item.call_by ~= '' then
ts_report = ts_report .. '' .. item.call_by
end
if #ts_report > 1 then
space, trim = get_pads(win_width, item.text, ts_report)
local l = math.max(20, opts.width - math.min(20, #ts_report))
if trim and #item.text < l then
trim = false
end
if trim then
item.text = string.sub(item.text, 1, opts.width - 20) .. ""
item.text = string.sub(item.text, 1, l)
item.text = util.sub_match(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)
space = ' '
if #item.text + #ts_report >= win_width then
space = ' '
local len = math.max(win_width - #item.text - 4, 16)
trace('exceeding', #item.text, #ts_report, win_width, len)
ts_report = ts_report:sub(1, len)
else
local remain = win_width - #item.text - #ts_report
trace("remain", remain)
trace('remain', remain)
space = string.rep(' ', remain)
end
end

@ -1,11 +1,7 @@
local gui = require "navigator.gui"
local util = require "navigator.util"
local log = util.log
local partial = util.partial
local lsphelper = require "navigator.lspwrapper"
local cwd = vim.fn.getcwd(0)
local M = {}
--- navigator signature
local match_parameter = function(result)
local signatures = result.signatures
if #signatures < 1 then
@ -38,16 +34,25 @@ local match_parameter = function(result)
if type(nextParameter.label) == "string" then -- label = 'par1 int'
local i, j = label:find(nextParameter.label, 1, true)
if i ~= nil then
label = label:sub(1, i - 1) .. [[`]] .. label:sub(i, j) .. [[`]]
.. label:sub(j + 1, #label + 1)
label = label:sub(1, i - 1) .. [[`]] .. label:sub(i, j) .. [[`]] .. label:sub(j + 1, #label + 1)
signature.label = label
end
end
end
end
local function signature_handler(err, method, result, _, bufnr, config)
-- log(result)
local signature_handler = function(err, result, ctx, config)
if config == nil then
log("config nil")
end
if err then
vim.notify("signature help error: ".. vim.inspect(err) .. vim.inspect(result), ctx, config, vim.lsp.log_levels.WARN)
end
config = config or {}
if config.border == nil then
config.border = _NgConfigValues.border
end
if not (result and result.signatures and result.signatures[1]) then
return
end
@ -58,8 +63,7 @@ local function signature_handler(err, method, result, _, bufnr, config)
end
local syntax = vim.lsp.util.try_trim_markdown_code_blocks(lines)
config.focus_id = method .. "lsp_signature"
config.focus_id = ctx.bufnr .. "lsp_signature"
vim.lsp.util.open_floating_preview(lines, syntax, config)
end
return {signature_handler = signature_handler}

@ -1,91 +1,59 @@
local gui = require "navigator.gui"
local gui = require('navigator.gui')
local M = {}
local log = require"navigator.util".log
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 log = require('navigator.util').log
local trace = require('navigator.util').trace
local lsphelper = require('navigator.lspwrapper')
local symbol_kind = require('navigator.lspclient.lspkind').symbol_kind
local symbols_to_items = lsphelper.symbols_to_items
-- function M.document_symbols(opts)
-- assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running")
-- opts = opts or {}
-- local params = vim.lsp.util.make_position_params()
-- params.context = {includeDeclaration = true}
-- params.query = ""
-- local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/documentSymbol", params, opts.timeout or 3000)
-- local locations = {}
-- log(results_lsp)
-- for _, server_results in pairs(results_lsp) do
-- if server_results.result then
-- vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result) or {})
-- end
-- end
-- local lines = {}
--
-- for _, loc in ipairs(locations) do
-- table.insert(lines, string.format("%s:%s:%s", loc.filename, loc.lnum, loc.text))
-- end
-- if #lines > 0 then
-- gui.new_list_view({data = lines})
-- else
-- print("symbols not found")
-- end
-- end
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, _, _bufnr)
if client.server_capabilities.workspaceSymbolProvider then
client.request('workspace/symbol', params, M.workspace_symbol_handler, _bufnr)
end
end)
end
function M.workspace_symbols(opts)
function M.document_symbols(opts)
opts = opts or {}
assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running")
local lspopts = {
loc = "top_center",
loc = 'top_center',
prompt = true,
-- rawdata = true,
api = ""
api = '',
}
local bufnr = vim.api.nvim_get_current_buf()
vim.list_extend(lspopts, opts)
local params = vim.lsp.util.make_position_params()
params.context = {includeDeclaration = true}
params.query = opts.prompt or ""
local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params,
lspopts.timeout or 15000)
if not results_lsp or vim.tbl_isempty(results_lsp) then
print(bufnr, "symbol not found for buf")
return
end
-- result_lsp
local result = {}
for i = 1, #results_lsp do
if results_lsp[i] ~= nil and results_lsp[i].result ~= nil and #results_lsp[i].result > 0 then
result = results_lsp[i].result
params.context = { includeDeclaration = true }
params.query = opts.prompt or ''
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
local items = symbols_to_items(result)
-- log(#items, items[1])
local ft = vim.api.nvim_buf_get_option(0, "ft")
if #items > 0 then
lspopts.items = items
lspopts.ft = ft
gui.new_list_view(lspopts)
else
print("symbols not found")
end
end)
end
function M.document_symbol_handler(err, _, result, _, bufnr)
M.document_symbol_handler = function(err, result, ctx)
if err then
print(bufnr, "failed to get document symbol")
vim.notify('failed to get document symbol' .. vim.inspect(ctx), vim.lsp.log_levels.WARN)
end
local bufnr = ctx.bufnr or 0
local query = ' '
if ctx.params and ctx.params.query then
query = query .. ctx.params.query .. ' '
end
if not result or vim.tbl_isempty(result) then
print(bufnr, "symbol not found for buf")
vim.notify('symbol ' .. query .. ' not found for buf ' .. vim.inspect(ctx), vim.lsp.log_levels.WARN)
return
end
-- log(result)
local locations = {}
local fname = vim.fn.expand("%:p:f")
local fname = vim.fn.expand('%:p:f')
local uri = vim.uri_from_fname(fname)
-- vim.list_extend(locations, vim.lsp.util.symbols_to_items(result) or {})
log(result[1])
@ -94,18 +62,25 @@ function M.document_symbol_handler(err, _, result, _, bufnr)
item.kind = result[i].kind
local kind = symbol_kind(item.kind)
item.name = result[i].name
item.range = result[i].range
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 ""
if item.detail == "()" then
item.detail = "func"
item.detail = result[i].detail or ''
if item.detail == '()' then
item.detail = 'func'
end
item.lnum = result[i].range.start.line + 1
item.text = "[" .. kind .. "]" .. item.name .. " " .. item.detail
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
@ -113,92 +88,81 @@ function M.document_symbol_handler(err, _, result, _, bufnr)
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.detail = c.detail or ""
child.text = "  [" .. ckind .. "] " .. child.name .. " " .. child.detail
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")
-- trace(locations)
-- local items = locations_to_items(locations)
gui.new_list_view({items = locations, prompt = true, rawdata = true, ft = ft, api = ""})
-- if locations == nil or vim.tbl_isempty(locations) then
-- print "References not found"
-- return
-- end
-- local items = locations_to_items(locations)
-- gui.new_list_view({items = items})
-- local filename = vim.api.nvim_buf_get_name(bufnr)
-- local items = vim.lsp.util.symbols_to_items(result, bufnr)
-- local data = {}
-- for i, item in pairs(action.items) do
-- data[i] = item.text
-- if filename ~= item.filename then
-- local cwd = vim.fn.getcwd(0) .. "/"
-- local add = util.get_relative_path(cwd, item.filename)
-- data[i] = data[i] .. " - " .. add
-- end
-- item.text = nil
-- end
-- opts.data = data
local ft = vim.api.nvim_buf_get_option(bufnr, 'ft')
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
function M.workspace_symbol_handler(err, _, result, _, bufnr)
M.workspace_symbol_handler = function(err, result, ctx, cfg)
trace(err, result, ctx, cfg)
if err then
print(bufnr, "failed to get workspace symbol")
vim.notify('failed to get workspace symbol' .. vim.inspect(ctx), vim.lsp.log_levels.WARN)
end
local query = ' '
if ctx.params and ctx.params.query then
query = query .. ctx.params.query .. ' '
end
if not result or vim.tbl_isempty(result) then
print(bufnr, "symbol not found for buf")
log('symbol not found', ctx)
vim.notify('symbol' .. query .. 'not found for buf ' .. tostring(ctx.bufnr), vim.lsp.log_levels.WARN)
return
end
log(result[1])
local items = symbols_to_items(result)
log(items[1])
-- local locations = {}
-- for i = 1, #result do
-- local item = result[i].location or {}
-- item.kind = result[i].kind
-- item.containerName = result[i].containerName or ""
-- item.name = result[i].name
-- item.text = result[i].name
-- if #item.containerName > 0 then
-- item.text = item.text:gsub(item.containerName, "", 1)
-- end
-- table.insert(locations, item)
-- end
-- local items = locations_to_items(locations)
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({items = items, prompt = true, ft = ft, rowdata = true, api = ""})
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
-- if locations == nil or vim.tbl_isempty(locations) then
-- print "References not found"
-- return
-- end
-- local items = locations_to_items(locations)
-- gui.new_list_view({items = items})
-- local filename = vim.api.nvim_buf_get_name(bufnr)
-- local items = vim.lsp.util.symbols_to_items(result, bufnr)
-- local data = {}
-- for i, item in pairs(action.items) do
-- data[i] = item.text
-- if filename ~= item.filename then
-- local cwd = vim.fn.getcwd(0) .. "/"
-- local add = util.get_relative_path(cwd, item.filename)
-- data[i] = data[i] .. " - " .. add
-- end
-- item.text = nil
-- end
-- opts.data = data
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

@ -1,25 +1,32 @@
--- 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.fn.getcwd(0)
local log = require"navigator.util".log
local lerr = require"navigator.util".error
local trace = require"navigator.util".trace
local cwd = vim.loop.cwd()
local log = require('navigator.util').log
local lerr = require('navigator.util').error
local trace = function(...) end
trace = log
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
@ -40,56 +47,242 @@ 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
local function node_is_definination(node)
if node:parent() == nil then
return false
end
local nd_type = node:parent():type()
local decl = { 'short_var_declaration', 'short_var_declaration', 'declaration' }
if vim.tbl_contains(decl, nd_type) then
return true
end
if node:parent():parent() == nil then
return false
end
nd_type = node:parent():parent():type()
if vim.tbl_contains(decl, nd_type) then
return true
end
return false
end
-- use lsp range to find def
function M.find_definition(range, bufnr)
if not range or not range.start then
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])
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
local definition = locals.find_definition(node_at_point, bufnr)
if definition ~= node_at_point then
trace("err: def found:", definition:range(), definition:type())
if definition ~= node_at_point then -- NOTE: it may not worksfor some of languages. if def not found, ts
-- returns current node. if your node is def, then it also return self... then I have no idea weather it is
-- def or not
trace('info: def found:', definition:range(), definition:type())
local r, c = definition:range()
return { start = { line = r, character = c } }
elseif node_is_definination(node_at_point) then
trace('declaraction here ', definition:type())
local r, c = definition:range()
return {start = {line = r, character = c}}
return { start = { line = r, character = c } }
else
trace("err: def not found in ", bufnr)
trace('error: def not found in ', bufnr, definition:range(), definition:type(), definition:parent():type())
end
end
function M.get_tsnode_at_pos(pos, bufnr, ignore_injected_langs)
if not pos or not pos.start then
return
end
local cursor_range = { pos.start.line, pos.start.character }
local buf = bufnr
local root_lang_tree = parsers.get_parser(buf)
if not root_lang_tree then
return
end
local root
if ignore_injected_langs then
for _, tree in ipairs(root_lang_tree:trees()) do
local tree_root = tree:root()
if tree_root and ts_utils.is_in_node_range(tree_root, cursor_range[1], cursor_range[2]) then
root = tree_root
break
end
end
else
root = ts_utils.get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree)
end
if not root then
return
end
return root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2])
end
-- Trim spaces and opening brackets from end
local transform_line = function(line)
line = line:gsub("%s*[%[%(%{]*%s*$", "")
line = line:gsub("function", "")
line = line:gsub("func%w*%s+", "")
if _NgConfigValues.treesitter_analysis_condense then
line = line:gsub("%([%a%.,%s%[%]%*]+%)", "()")
-- this is for multi return
line = line:gsub("%([%a%.,%s%[%]%*]+%)", "()")
line = line:gsub("%(%)%s*%(%)", "()")
end
return line
end
function M.ref_context(opts)
if not parsers.has_parser() then
return
end
local options = opts or {}
local bufnr = options.bufnr or 0
local pos = options.pos
if not pos then
pos = {start = vim.lsp.util.make_position_params().position}
end
local indicator_size = options.indicator_size or 100
local type_patterns = options.type_patterns or { "class", "function", "method" }
local transform_fn = options.transform_fn or transform_line
local separator = options.separator or ""
local current_node = M.get_tsnode_at_pos(pos, bufnr)
if not current_node then
log('no node at pos', bufnr, pos)
return ""
end
local lines = {}
local expr = current_node
while expr do
local line = ts_utils._get_line_for_node(expr, type_patterns, transform_fn, bufnr)
log(line)
if line ~= "" and not vim.tbl_contains(lines, line) then
table.insert(lines, 1, line)
end
expr = expr:parent()
end
local text = table.concat(lines, separator)
local text_len = #text
if text_len > indicator_size then
local str = text:sub(1, text_len)
return util.sub_match(str)
end
return text
end
--- Get definitions of bufnr (unique and sorted by order of appearance).
--- 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 row, col, offset = node:start()
local erow, ecol, end_ = node:end_()
trace(node, match)
trace(row, col, erow, offset, 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[offset] = { 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 row, col, start = node:start()
trace(row, col, 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 row, col, start = node:start()
local p1, p1t = '', ''
local p2, p2t = '', ''
local p3, p3t = '', ''
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
if p2 and p2:parent() then
p3 = p2:parent()
p3t = p2:parent():type()
end
trace(row, col, start, def, node, full_match, match, p1t, p1, node:parent():start(), node:parent():type(), p2, p2t, p3, p3t)
if p1t == 'arrow_function' then
row, col, start = p1:start()
trace('arrow_function 1', row, col)
nodes_set[start] = { node = p1, type = p1t }
end
if p2t == 'arrow_function' then
row, col, start = p2:start()
trace('arrow_function 2', row, col)
nodes_set[start] = { node = p2, type = p2t }
end
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
@ -109,7 +302,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))
@ -119,14 +312,12 @@ 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)
if type == 'method' or type == 'function' and parent ~= nil then
if type == 'method' or type:find('function') and parent ~= nil then
trace(parent:type(), parent:range())
-- a function name
if parent:type() == 'function_name' then
@ -139,7 +330,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
@ -151,9 +342,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
@ -166,7 +356,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
@ -178,10 +368,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)
@ -199,13 +388,13 @@ 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}
opt.forward = false
end
bufnr = bufnr or api.nvim_get_current_buf()
local node_at_point = ts_utils.get_node_at_cursor()
@ -234,48 +423,100 @@ function M.goto_previous_usage(bufnr)
return M.goto_adjacent_usage(bufnr, -1)
end
local function key(fname, filter)
return fname .. vim.inspect(filter)
end
local function get_all_nodes(bufnr, filter, summary)
trace(bufnr, filter, summary)
bufnr = bufnr or 0
summary = summary or false
if not parsers.has_parser() then
print("ts not loaded")
end
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)
fname = vim.uri_to_fname(uri)
end
path_sep = require"navigator.util".path_sep()
path_cur = require"navigator.util".path_cur()
local ftime = vim.fn.getftime(fname)
local hash = key(fname, filter)
local result = lru:get(hash)
if result ~= nil and result.ftime == ftime then
trace('get data from cache', ftime, result)
return result.nodes, result.length
end
if result ~= nil and result.ftime ~= ftime then
lru:delete(hash)
end
trace(bufnr, filter, summary)
if not bufnr then
vim.notify('get_all_node invalid 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
if not require('navigator.lspclient.clients').ft_disabled(ft) then
-- vim.notify('ts not loaded ' .. ft, vim.lsp.log_levels.Debug)
log('ts not loaded ' .. ft)
end
return {}
end
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 {}
-- Force some types to act like they are parents
-- instead of neighbors of the next nodes.
local containers = {
["function"] = true,
["arrow_function"] = true,
["type"] = true,
["class"] = true,
["method"] = true
local containers = filter or {
['function'] = true,
['local_function'] = true,
['arrow_function'] = true,
['type'] = true,
['class'] = true,
['call_expression'] = true,
-- ['var'] = true,
['struct'] = true,
['method'] = true,
}
-- check and load buff
local should_unload = false
if not vim.api.nvim_buf_is_loaded(bufnr) then
should_unload = true
vim.fn.bufload(bufnr)
end
-- Step 2 find correct completions
local length = 10
local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos
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
-- trace(parent_def.type, parent_def.node:type(), vim.treesitter.get_node_text(parent_def.node, bufnr))
-- trace(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
-- trace('is parent', i, index)
break
else
-- trace('leave node', i, index)
parents[index] = nil
end
end
@ -288,15 +529,24 @@ 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]
local text = vim.treesitter.get_node_text(tsdata, bufnr) or ''
text = vim.split(text, '\n')[1] or ''
item.node_text = text
log(item.node_text)
local scope, is_func
if summary then
@ -304,43 +554,68 @@ local function get_all_nodes(bufnr, filter, summary)
else
scope, is_func = get_smallest_context(tsdata)
end
log(item, scope, is_func)
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
trace('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
@ -350,40 +625,77 @@ 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 }
lru:set(hash, nd)
if should_unload then
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,
['arrow_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
@ -410,34 +722,62 @@ function M.buf_func(bufnr)
return false
end)
end
log(all_nodes)
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
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
local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({
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()
@ -460,15 +800,131 @@ 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
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
-- log("not correct tree embedded or comment?", result:lang())
return parser
end
return result
end
end
return parser
end
function M.get_node_at_line(lnum)
if not parsers.has_parser() then
return
end
-- Get the position for the queried node
if lnum == nil then
local cursor = api.nvim_win_get_cursor(0)
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 }
-- Get the language tree with nodes inside the given range
local root = parsers.get_parser()
local ts_tree = node_in_range(root, range)
-- log(ts_tree:trees())
local tree = ts_tree:trees()[1]
local node = tree:root():named_descendant_for_range(unpack(range))
-- trace(node, node:type()) -- log all lines and all nodes
return node
end
local usage_namespace = vim.api.nvim_create_namespace('nvim-treesitter-usages')
function M.highlight_usages(bufnr)
M.clear_usage_highlights(bufnr)
local node_at_point = ts_utils.get_node_at_cursor()
local references = locals.get_references(bufnr)
if not node_at_point or not vim.tbl_contains(references, node_at_point) then
return
end
local def_node, scope = locals.find_definition(node_at_point, bufnr)
local usages = locals.find_usages(def_node, scope, 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')
end
end
if def_node ~= node_at_point then
ts_utils.highlight_node(def_node, bufnr, usage_namespace, 'TSDefinition')
end
end
function M.clear_usage_highlights(bufnr)
api.nvim_buf_clear_namespace(bufnr, usage_namespace, 0, -1)
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] }
log(cursor_range)
local root = ts_utils.get_root_for_position(unpack(cursor_range), parser)
if not root then
return
end
local node = root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2])
log(node, node:range())
return node
end
function M.get_node_scope(node)
-- local
local n = node
if n == nil then
return 0, 0, 0, 0
end
local sr, sc, er, ec = n:range()
log(n:range())
for _ = 1, 6 do
if n == nil then
return 0, 0, 0, 0
end
local nsr, nsc, ner, nec = n:range()
if nsr < sr then
log(sr, er)
break
end
sr, sc, er, ec = nsr, nsc, ner, nec
if n:parent() then
n = n:parent()
end
end
return sr, sc, er, ec
end
return M

@ -1,28 +1,38 @@
-- retreives data form file
-- and line to highlight
-- Some of function copied from https://github.com/RishabhRD/nvim-lsputils
local M = {log_path = vim.lsp.get_log_path()}
local M = { log_path = vim.lsp.get_log_path() }
-- local is_windows = uv.os_uname().version:match("Windows")
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")
local is_win = vim.loop.os_uname().sysname:find('Windows')
if is_win then
return "\\"
return '\\'
else
return "/"
return '/'
end
end
local path_sep = M.path_sep()
M.path_cur = function()
local is_win = vim.loop.os_uname().sysname:find("Windows")
local is_win = vim.loop.os_uname().sysname:find('Windows')
if is_win then
return ".\\"
return '.\\'
else
return "./"
return './'
end
end
M.round = function(x)
return math.max(0, math.floor(x - 0.5))
end
function M.get_data_from_file(filename, startLine)
local displayLine
if startLine < 3 then
@ -32,23 +42,46 @@ function M.get_data_from_file(filename, startLine)
startLine = startLine - 2
displayLine = 2
end
local uri = "file:///" .. filename
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
local len = #data
startLine = startLine + 1
for i = 1, len, 1 do
data[i] = startLine .. " " .. data[i]
data[i] = startLine .. ' ' .. data[i]
startLine = startLine + 1
end
end
return {data = data, line = displayLine}
return { data = data, line = displayLine }
end
function M.io_read(filename)
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)
@ -59,19 +92,19 @@ M.merge = function(t1, t2)
end
M.map = function(modes, key, result, options)
options = M.merge({noremap = true, silent = false, expr = false, nowait = false}, options or {})
options = M.merge({ noremap = true, silent = false, expr = false, nowait = false }, options or {})
local buffer = options.buffer
options.buffer = nil
if type(modes) ~= "table" then
modes = {modes}
if type(modes) ~= 'table' then
modes = { modes }
end
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
@ -106,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
@ -125,7 +164,7 @@ function M.get_relative_path(base_path, my_path)
end
cur = i
end
local data = ""
local data = ''
for i = cur + 1, my_len do
data = data .. my_data[i] .. path_sep
end
@ -133,21 +172,46 @@ function M.get_relative_path(base_path, my_path)
return data
end
local level = "error"
if _NgConfigValues.debug == true then
level = "debug"
elseif _NgConfigValues.debug == "trace" then
level = "trace"
M.log = function(...)
return { ... }
end
M.info = function(...)
return { ... }
end
M.trace = function(...)
return { ... }
end
M.warn = function(...)
return { ... }
end
M.error = function(...)
print(...)
end
local level = 'error'
local default_config = {plugin = "navigator", use_console = false, use_file = true, level = level}
M._log = require("guihua.log").new({level = default_config.level}, true)
function M.setup()
if _NgConfigValues.debug == true then
level = 'info'
elseif _NgConfigValues.debug == 'trace' then
level = 'trace'
end
local default_config = { use_console = false, use_file = true, level = level }
if _NgConfigValues.debug_console_output then
default_config.use_console = true
default_config.use_file = false
end
-- 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._log = require('guihua.log').new(default_config, true)
if _NgConfigValues.debug then
-- add log to you lsp.log
M.trace = M._log.trace
M.info = M._log.info
M.warn = M._log.warn
M.error = M._log.error
M.log = M.info
end
end
function M.fmt(...)
M._log.fmt_info(...)
@ -155,10 +219,10 @@ end
function M.split(inputstr, sep)
if sep == nil then
sep = "%s"
sep = '%s'
end
local t = {}
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do
table.insert(t, str)
end
return t
@ -168,29 +232,29 @@ function M.quickfix_extract(line)
-- check if it is a line of file pos been selected
local split = M.split
line = vim.trim(line)
local sep = split(line, " ")
local sep = split(line, ' ')
if #sep < 2 then
M.log(line)
return nil
end
sep = split(sep[1], ":")
sep = split(sep[1], ':')
if #sep < 3 then
M.log(line)
return nil
end
local location = {
uri = "file:///" .. sep[1],
range = {start = {line = sep[2] - 3 > 0 and sep[2] - 3 or 1}}
uri = 'file:///' .. sep[1],
range = { start = { line = sep[2] - 3 > 0 and sep[2] - 3 or 1 } },
}
location.range["end"] = {line = sep[2] + 15}
location.range['end'] = { line = sep[2] + 15 }
return location
end
function M.getArgs(inputstr)
local sep = "%s"
local sep = '%s'
local t = {}
local cmd
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do
if not cmd then
cmd = str
else
@ -201,47 +265,47 @@ function M.getArgs(inputstr)
end
function M.p(t)
print(vim.inspect(t))
vim.notify(vim.inspect(t), vim.log.levels.INFO)
end
function M.printError(msg)
vim.cmd("echohl ErrorMsg")
vim.cmd('echohl ErrorMsg')
vim.cmd(string.format([[echomsg '%s']], msg))
vim.cmd("echohl None")
vim.cmd('echohl None')
end
function M.reload()
vim.lsp.stop_client(vim.lsp.get_active_clients())
vim.cmd [[edit]]
vim.cmd([[edit]])
end
function M.open_log()
local path = vim.lsp.get_log_path()
vim.cmd("edit " .. path)
vim.cmd('edit ' .. path)
end
function table.pack(...)
return {n = select("#", ...), ...}
return { n = select('#', ...), ... }
end
function M.show(...)
local string = ""
local string = ''
local args = table.pack(...)
for i = 1, args.n do
string = string .. tostring(args[i]) .. "\t"
string = string .. tostring(args[i]) .. '\t'
end
return string .. "\n"
return string .. '\n'
end
function M.split2(s, sep)
local fields = {}
sep = sep or " "
local pattern = string.format("([^%s]+)", sep)
string.gsub(s, pattern, function(c)
sep = sep or ' '
local pattern = string.format('([^%s]+)', sep)
_ = string.gsub(s, pattern, function(c)
fields[#fields + 1] = c
end)
@ -268,30 +332,20 @@ 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)
vim.api.nvim_command(string.format("e! +%s %s", line, 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
function M.partial(func, arg)
return (function(...)
return func(arg, ...)
end)
end
local exclude_ft = {"scrollbar", "help", "NvimTree"}
local exclude_ft = { 'scrollbar', 'help', 'NvimTree' }
function M.exclude(fname)
for i = 1, #exclude_ft do
if string.find(fname, exclude_ft[i]) then
@ -305,18 +359,16 @@ end
-- name space search
local nss
local api = vim.api
local bufs
function M.set_virt_eol(bufnr, lnum, chunks, priority, id)
if nss == nil then
nss = api.nvim_create_namespace("navigator_search")
nss = api.nvim_create_namespace('navigator_search')
end
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
bufs[bufnr] = true
-- id may be nil
return api.nvim_buf_set_extmark(bufnr, nss, lnum, -1,
{id = id, virt_text = chunks, priority = priority})
return api.nvim_buf_set_extmark(bufnr, nss, lnum, -1, { id = id, virt_text = chunks, priority = priority })
end
function M.clear_buf(bufnr)
@ -340,4 +392,181 @@ function M.clear_all_buf()
bufs = {}
end
function M.get_current_winid()
return api.nvim_get_current_win()
end
function M.nvim_0_6_1()
if nvim_0_6_1 ~= nil then
return nvim_0_6_1
end
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
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(...)
return fn(...)
end
end
function M.partial(func, arg)
return function(...)
return func(arg, ...)
end
end
function M.partial2(func, arg1, arg2)
return function(...)
return func(arg1, arg2, ...)
end
end
function M.partial3(func, arg1, arg2, arg3)
return function(...)
return func(arg1, arg2, arg3, ...)
end
end
function M.partial4(func, arg1, arg2, arg3, arg4)
return function(...)
return func(arg1, arg2, arg3, arg4, ...)
end
end
function M.empty(t)
if t == nil then
return true
end
if type(t) ~= 'table' then
return false
end
return next(t) == nil
end
function M.encoding(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
return oe[1]
end
return oe
end
-- alternatively: use vim.notify("namespace does not exist or is anonymous", vim.log.levels.ERROR)
function M.warn(msg)
vim.notify('WRN: ' .. msg, vim.lsp.log_levels.WARN)
end
function M.error(msg)
vim.notify('ERR: ' .. msg, vim.lsp.log_levels.EROR)
end
function M.info(msg)
vim.notify('INF: ' .. msg, vim.lsp.log_levels.INFO)
end
function M.dedup(locations)
local m = 10
if m > #locations then
m = #locations
end
local dict = {}
local del = {}
for i = 1, m, 1 do
local value = locations[i]
local range = value.range or value.originSelectionRange or value.targetRange
if not range then
break
end
local key = (value.uri or range.uri or value.targetUri or '')
.. ':'
.. tostring(range.start.line)
.. ':'
.. tostring(range.start.character)
.. ':'
.. tostring(range['end'].line)
.. ':'
.. tostring(range['end'].character)
if dict[key] == nil then
dict[key] = i
else
local j = dict[key]
if not locations[j].definition then
table.insert(del, i)
else
table.insert(del, j)
end
end
end
table.sort(del)
for i = #del, 1, -1 do
M.log('remove ', del[i])
table.remove(locations, del[i])
end
return locations
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
function M.dirname(pathname)
local path_sep = require('navigator.util').path_sep()
local strip_dir_pat = path_sep .. '([^' .. path_sep .. ']+)$'
local strip_sep_pat = path_sep .. '$'
if not pathname or #pathname == 0 then
return
end
local result = pathname:gsub(strip_sep_pat, ''):gsub(strip_dir_pat, '')
if #result == 0 then
return '/'
end
return result
end
function M.sub_match(str)
local _, j = string.gsub(str, [["]], '')
if j % 2 == 1 then
str = str .. '"'
end
_, j = string.gsub(str, [[']], '')
if j % 2 == 1 then
str = str .. [[']]
end
str = str .. ''
return str
end
return M

@ -0,0 +1,104 @@
-- https://github.com/lukas-reineke/dotfiles/blob/master/vim/lua/lsp/rename.lua
local M = {}
local util = require('navigator.util')
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 = vfn.expand('%:p:h') }, function(inputs)
vim.lsp.buf.add_workspace_folder(inputs)
end)
end
M.remove_workspace_folder = function()
local select = require('guihua.gui').select
local folders = vim.lsp.buf.list_workspace_folders()
if #folders > 1 then
return select(folders, { prompt = 'select workspace to delete' }, function(workspace)
vim.lsp.buf.remove_workspace_folder(workspace)
end)
end
end
M.workspace_symbol = function()
local input = require('guihua.floating').input
input({ prompt = 'Search symbol: ', default = '' }, function(inputs)
util.log(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(vim.api.nvim_get_option('columns') * width)
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 = 'GuihuaListDark',
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
return require('navigator.gui').new_list_view({
items = folders,
border = 'single',
rawdata = true,
on_move = function() end,
})
end
end
return M

@ -0,0 +1,38 @@
# Sandbox/Tutorial
## introduction
The folder contains `init.lua`, whitch is a minium vimrc to setup following plugins. Those plugin are some of the
most used plugins for programmer.
- lspconfig
- treesitter
- navigator
- nvim-cmp
- luasnip
- aurora (colorscheme used in the screenshot)
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 if not existed in your PATH.
## run init.lua
```bash
cd py
neovim -u init.lua
```
Move your cursor around and try to
- Edit the code
- Check symbol reference with `<esc>gr`
- Check document symbol with `<esc>g0`
- treesitter symbole `<esc>gT`
- peek definition `<esc>gp`
- ...

@ -0,0 +1,8 @@
package main
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n-1) + Fib(n-2)
}

@ -0,0 +1,17 @@
package main
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestFib(t *testing.T) {
require.NoError(t, nil)
d := Fib(1)
fmt.Println(d)
if d != 1 {
t.Errorf("NewDog failled %v", d)
}
}

@ -0,0 +1,49 @@
package main
import (
// "net/http"
"net/http/httptest"
"time"
)
type Dog struct {
name string
age int
owner string
}
func NewDog(name string, age int) *Dog {
return &Dog{name: name, age: age}
}
// SetOwner
func (d *Dog) SetOwner(owner string) {
d.owner = owner
}
// SetDogName
func (d *Dog) SetDogName(name string) {
if d == nil {
d = NewDog(name, 0)
d.name = name
} else {
d.name = name
}
}
func (d *Dog) SetOwnerUtf8(name []byte) {
}
func fun1() {
}
func fun1_test() {
d := NewDog("", 1)
NewDog("abc", 12)
// fmt.Printf("abc", 1)
time.Date(12, 12, 12, 33, 12, 55, 22, nil)
d.SetOwnerUtf8([]byte{1})
w := httptest.NewRecorder()
w.Write([]byte{})
}

@ -0,0 +1,25 @@
package main
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestDog(t *testing.T) {
require.NoError(t, nil)
d := NewDog("Fibi", 4)
fmt.Println(d.name)
if d.name != "Fibi" {
t.Errorf("NewDog failled %v", d)
}
}
func TestCat(t *testing.T) {
d := NewDog("Fibi cat", 4)
fmt.Println(d.name)
if d.name != "Fibi cat" {
t.Errorf("NewDog failled %v", d)
}
}

@ -0,0 +1,26 @@
package main
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestCow(t *testing.T) {
require.NoError(t, nil)
d := NewDog("Fibi", 4)
fmt.Println(d.name)
if d.name != "Fibi" {
t.Errorf("NewDog failled %v", d)
}
}
func TestHorse(t *testing.T) {
d := NewDog("Fibi cat", 4)
fmt.Println(d.name)
if d.name != "Fibi cat" {
t.Errorf("NewDog failled %v", d)
}
}

@ -0,0 +1,67 @@
package main
import (
"fmt"
"math"
//"math"
)
type geometry interface {
area() float64
perim() float64
}
type rect struct {
width float64 `-line:"width"`
height float64 `-line:"height"`
}
type rect2 struct {
width int `yml:"width"`
height int `yml:"height"`
}
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perim() float64 {
return 2*r.width + 2*r.height
}
type circle struct {
radius float64
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
func measure(g geometry) int {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
return 1
}
func m2() {
measure(rect{width: 3})
}
func M2() {
measure(rect{width: 3})
}
func interfaceTest() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
d := circle{radius: 10}
fmt.Println()
fun2(d)
}

@ -0,0 +1,31 @@
package main
import (
"errors"
"fmt"
"io/fs"
"unsafe"
)
// main
// note: this is main func
func main() {
i := 32
i = i + 1
fmt.Println("hello, world", i)
var uns1 unsafe.Pointer
var x struct {
a int64
b bool
c string
}
const M, N = unsafe.Sizeof(x.c), unsafe.Sizeof(x)
fmt.Println(M, N, uns1) // 16 32
var perr *fs.PathError
if errors.As(nil, &perr) {
fmt.Println(perr.Path)
}
myfunc3("a", "b")
}

@ -0,0 +1,39 @@
package main
import "fmt"
// import "fmt"
type person struct {
name string
age int
}
type say interface {
hello() string
}
type strudent struct {
person struct {
name string
age int
}
}
func newPerson(name string) *person {
p := person{name: name}
fmt.Println("")
p.age = 42
return &p
}
func newPerson2(name, say string) {
fmt.Println(name, say)
}
func b() {
newPerson2("a", "say")
ret := measure(rect{width: 3})
fmt.Println(ret)
}

@ -0,0 +1,26 @@
package test
type Dog struct {
name string
age int
owner string
}
func NewDog(name string, age int) *Dog {
return &Dog{name: name, age: age}
}
// SetOwner
func (d *Dog) SetOwner(owner string) {
d.owner = owner
}
// SetName
func (d *Dog) SetName(name string) {
if d == nil {
d = NewDog(name, 0)
d.name = name
} else {
d.name = name
}
}

@ -0,0 +1,26 @@
package tekkkt
type Dog kkktruct {
name kkktring
age int
owner kkktring
}
func NewDog(name kkktring, age int) *Dog {
return &Dog{name: name, age: age}
}
// kkketOwner
func (d *Dog) kkketOwner(owner kkktring) {
d.owner = owner
}
// kkketName
func (d *Dog) kkketName(name kkktring) {
if d == nil {
d = NewDog(name, 0)
d.name = name
} elkkke {
d.name = name
}
}

@ -0,0 +1,22 @@
package test
import (
"fmt"
"testing"
)
func TestDog(t *testing.T) {
d := NewDog("Fibi", 4)
fmt.Println(d.name)
if d.name != "Fibi" {
t.Errorf("NewDog failled %v", d)
}
}
func TestCat(t *testing.T) {
d := NewDog("Fibi cat", 4)
fmt.Println(d.name)
if d.name != "Fibi cat" {
t.Errorf("NewDog failled %v", d)
}
}

@ -0,0 +1,70 @@
package main
import (
"fmt"
// "strings"
"time"
log "github.com/sirupsen/logrus"
)
// type Name2 struct {
// f1 string
// f2 int
// }
//
// type name4 struct {
// f1 string
// f2 int
// }
//
// type name5 struct {
// f1 string
// f2 int
// }
//
// func test2() {
// type some struct {
// Success bool `-line:"success"`
// Failure bool
// }
//
// // myfunc("aaa", "bbb")
// }
func myfunc3(v, v2 string) error {
time.After(time.Hour)
fmt.Println(v, v2)
// fmt.Println(kk)
//
time.Date(2020, 12, 11, 21, 11, 44, 12, nil)
time.Date(2020, 1, 11, 11, 11, 2, 1, nil)
time.Date(1111, 22, 11, 1, 1, 1, 1, nil)
time.Date(12345, 2333, 444, 555, 66, 1, 22, nil)
fmt.Println(`kkkkkk`)
log.Info(`abc`)
log.Infof(`log %s`, `def`)
log.Infof(`log %d`, 33)
return nil
}
// func myfunc4() {
// // myfunc("aaa", "bbb") // time.Date(12,11, )
// // myfunc("abc", "def")
// // myfunc("1", "2")
// }
//
// func mytest2() {
// i := 1
// log.Infof("%d", i)
// myfunc4()
// }
//
// func myfunc5() {
// hellostring := "hello"
// if strings.Contains(hellostring, "hello") {
// fmt.Println("it is there")
// }
// }

@ -0,0 +1,132 @@
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'
-- IMPORTANT: update the sumneko setup if you need lua language server
-- I installed it in '/github/sumneko/lua-language-server'
local sumneko_root_path = vim.fn.expand('$HOME') .. '/github/sumneko/lua-language-server'
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' },
settings = {
Lua = {
runtime = { version = 'LuaJIT', path = vim.split(package.path, ';') },
diagnostics = { enable = true },
},
},
}
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)
use({ 'wbthomason/packer.nvim' })
use({
'nvim-treesitter/nvim-treesitter',
config = function()
require('nvim-treesitter.configs').setup({
ensure_installed = { 'python', 'go', 'javascript' },
highlight = { enable = true },
})
end,
run = ':TSUpdate',
})
use({ 'neovim/nvim-lspconfig' })
use({ 'ray-x/lsp_signature.nvim' })
use({ 'ray-x/aurora' })
use({
-- 'ray-x/navigator.lua',
'~/github/ray-x/navigator.lua',
requires = { 'ray-x/guihua.lua', run = 'cd lua/fzy && make' },
config = function()
require('navigator').setup({
debug = true,
keymaps = {
{ key = 'gK', func = vim.lsp.buf.definition, doc = 'definition' },
{
key = '<leader>ld',
func = require('navigator.diagnostics').show_buf_diagnostics,
desc = 'show_buf_diagnostics',
},
},
icons = {
diagnostic_virtual_text = '',
},
})
end,
})
use({ 'L3MON4D3/LuaSnip' })
use({
'hrsh7th/nvim-cmp',
requires = {
'hrsh7th/cmp-nvim-lsp',
'saadparwaiz1/cmp_luasnip',
},
config = function()
local cmp = require('cmp')
local luasnip = require('luasnip')
cmp.setup({
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body)
end,
},
mapping = {
['<CR>'] = cmp.mapping.confirm({ select = true }),
['<Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.confirm({ select = true })
elseif luasnip.expand_or_locally_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { 'i', 's' }),
},
sources = {
{ name = 'nvim_lsp' },
{ name = 'buffer' },
},
})
require('cmp').setup.cmdline(':', {
sources = {
{ name = 'cmdline' },
},
})
require('cmp').setup.cmdline('/', {
sources = {
{ name = 'buffer' },
},
})
end,
})
end,
config = {
package_root = package_root,
compile_path = install_path .. '/plugin/packer_compiled.lua',
},
})
end
if vim.fn.isdirectory(install_path) == 0 then
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

@ -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

@ -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 = {
['<CR>'] = cmp.mapping.confirm({ select = true }),
['<Tab>'] = 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

@ -0,0 +1,7 @@
const sayHiToSomeone = (callback) => {
return callbcak();
};
sayHiToSomeone(()=> {
console.log("aaa")
})

@ -0,0 +1,12 @@
function makeFunc() {
var browser = 'Mozilla';
function displayName() {
alert(browser);
var message = 'hello ' + browser;
alert(message);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();

@ -0,0 +1,11 @@
function curriedDot(vector1) {
return function(vector2) {
return vector1.reduce(
(sum, element, index) => (sum += element * vector2[index]),
0
);
};
}
const sumElements = curriedDot([1, 1, 1]);
console.log()

@ -0,0 +1 @@
const time = new Date(12, 33, )

@ -0,0 +1,3 @@
console.log("abc");
var kingsglove = "abcdefg";
console.log()

@ -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 ])

@ -0,0 +1,79 @@
import math
import numpy as np
import os
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # creates a new empty list for each dog
def add_trick(self, trick):
self.tricks.append(trick)
d = Dog('Fido')
d.add_trick('roll over')
print(d.tricks)
def test_func():
k = [1, 2, 3]
sum = 0
for i in range(k, 1, 2):
sum += 1
print(sum)
def greet(greeting, name):
"""
This function greets to
the person passed in as
a parameter
"""
print(greeting + name + ". Good morning!")
# def greet(greeting, name, msg1, msg2):
# """
# This function greets to
# the person passed in as
# a parameter
# """
# print(greeting + name + ". Good morning!")
greet("a", "b")
def greet2():
print("whatever")
def greet3(name):
greet2()
greet("hey", "dude", "", "")
print("whatever" + name)
def greet3():
pass
greet2()
greet("name", "name")
greet3("name")
greet3("")
greet("1", "2")
def greeting(greet: int, *, g):
"""
This function greets to
the person passed in as
a parameter
"""
print(greet + g + ". Good morning!")
np.empty(1, order="F")
np.empty(1, order="F")

@ -0,0 +1,19 @@
import pandas as pd
import io
pow()
arg = 111
bufio = io
filename = 'my_excel.xls'
df = pd.read_excel(abc, defgh)

@ -0,0 +1,6 @@
std="lua52+vim"
[rules]
global_usage = "allow"
multiple_statements = "allow"
unused_variable = "allow"

@ -0,0 +1,4 @@
indent_type = "Spaces"
indent_width = 2
column_width = 120
quote_style = "AutoPreferSingle"

@ -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 not crash and show hierarchy', function()
vim.fn.setpos('.', { bufn, 24, 15, 0 })
local ret = require('navigator.hierarchy')._call_hierarchy()
vim.wait(400, function() end)
eq(ret, ret) -- make sure doesn't crash the result
end)
end)

@ -0,0 +1,3 @@
module github.com/navigator/tests
go 1.17

@ -0,0 +1,72 @@
package main
import (
"fmt"
"math"
//"math"
)
type geometry interface {
area() float64
perim() float64
}
type rect struct {
width float64 `-line:"width"`
height float64 `-line:"height"`
}
type rect2 struct {
width int `yml:"width"`
height int `yml:"height"`
}
func (r rect) area() float64 {
return r.width * r.height
}
func (r rect) perim() float64 {
return 2*r.width + 2*r.height
}
type circle struct {
radius float64
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
func measure(g geometry) int {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
return 1
}
func m2() {
measure(rect{width: 3})
}
func M2() {
measure(rect{width: 3})
}
func runinterface() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
d := circle{radius: 10}
fmt.Println(d)
}
func main() {
M2()
m2()
runinterface()
}

@ -0,0 +1,12 @@
package main
import "fmt"
func interfaceTest() {
r := rect{width: 3, height: 4}
c := circle{radius: 5}
measure(r)
measure(c)
d := circle{radius: 10}
fmt.Println(d)
}

@ -0,0 +1,50 @@
set rtp +=.
set rtp +=../plenary.nvim/
set rtp +=../nvim-treesitter/
set rtp +=../nvim-lspconfig/
set rtp +=../guihua.lua/
set rtp +=../navigator.lua/
runtime! plugin/plenary.vim
runtime! plugin/nvim-treesitter.vim
runtime! plugin/guihua.vim
runtime! plugin/navigator.vim
runtime! plugin/nvim-lspconfig.vim
set noswapfile
set nobackup
filetype indent off
set nowritebackup
set noautoindent
set nocindent
set nosmartindent
set indentexpr=
lua << EOF
_G.test_rename = true
_G.test_close = true
require("plenary/busted")
require'nvim-treesitter.configs'.setup {
ensure_installed = {"go"}, -- one of "all", "maintained" (parsers with maintainers), or a list of languages
highlight = {
enable = true, -- false will disable the whole extension
},
}
-- for testing load gopls ahead
require'lspconfig'.gopls.setup{}
require'navigator'.setup({
debug = false, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
icons={code_action_icon = " "},
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',
})
EOF

@ -0,0 +1,146 @@
local helpers = {}
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')
local nvim_6 = true
if debug.getinfo(vim.lsp.handlers.signature_help).nparams > 4 then
nvim_6 = false
end
local result = {
{
range = { ['end'] = { character = 6, line = 14 }, start = { character = 1, line = 14 } },
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go',
},
{
range = { ['end'] = { character = 15, line = 24 }, start = { character = 10, line = 24 } },
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go',
},
{
range = { ['end'] = { character = 17, line = 28 }, start = { character = 12, line = 28 } },
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go',
},
{
range = { ['end'] = { character = 19, line = 51 }, start = { character = 14, line = 51 } },
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go',
},
{
range = { ['end'] = { character = 19, line = 55 }, start = { character = 14, line = 55 } },
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go',
},
{
range = { ['end'] = { character = 16, line = 59 }, start = { character = 11, line = 59 } },
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go',
},
{
range = { ['end'] = { character = 16, line = 5 }, start = { character = 11, line = 5 } },
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface_test.go',
},
}
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.bo.filetype = 'go'
vim.lsp.buf.references()
eq(1, 1)
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)
local win, items, width
if nvim_6 then
win, items, width = require('navigator.reference').ref_view(nil, result, {
method = 'textDocument/references',
bufnr = 1,
client_id = 1,
}, {})
else
win, items, width = require('navigator.reference').reference_handler(nil, 'textDocument/references', result, 1, 1)
end
-- print('win', vim.inspect(win))
print('items', vim.inspect(items))
eq(win.ctrl.data[1].display_filename, './tests/fixtures/interface.go')
eq(win.ctrl.data[2].range.start.line, 14)
eq(items[1].display_filename, './tests/fixtures/interface.go')
-- eq(width, 60)
end)
it('reference handler should return items with thread', function()
vim.wait(200, function() end)
local win, items, width
if nvim_6 then
win, items, width = require('navigator.reference').ref_view(nil, result, {
method = 'textDocument/references',
bufnr = 1,
client_id = 1,
}, { truncate = 2 })
else
win, items, width = require('navigator.reference').reference_handler(nil, 'textDocument/references', result, 1, 1)
end
-- print('win', vim.inspect(win))
print('items', vim.inspect(items))
-- eq(win.ctrl.data, "./interface.go")
eq(win.ctrl.data[1].display_filename, './tests/fixtures/interface.go')
eq(win.ctrl.data[2].range.start.line, 14)
-- eq(items[1].display_filename, "./interface.go")
-- eq(width, 60)
end)
end)

@ -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)

@ -0,0 +1,20 @@
local func = function(p, uv)
local before = os.time()
local async
async = uv.new_async(function(a, b, c)
p('in async notify callback')
p(a, b, c)
uv.close(async)
end)
local args = {500, 'string', nil, false, 5, "helloworld", async}
local unpack = unpack or table.unpack
uv.new_thread(function(num, s, null, bool, five, hw, asy)
local uv2 = require 'luv'
uv2.async_send(asy, 'a', true, 250)
uv2.sleep(1000)
end, unpack(args)):join()
local elapsed = (os.time() - before) * 1000
assert(elapsed >= 1000, "elapsed should be at least delay ")
end
func(print, vim.loop)

@ -0,0 +1,58 @@
[selene]
base = "lua52"
name = "vim"
[vim]
any = true
[_G]
property = true
writable = "new-fields"
[_NgConfigValues]
any = true
# property = true
# writable = "full-write"
[debug]
property = true
[[describe.args]]
type = "string"
[[describe.args]]
type = "function"
[[it.args]]
type = "string"
[[it.args]]
type = "function"
[[before_each.args]]
type = "function"
[[after_each.args]]
type = "function"
[assert.is_not]
any = true
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
[[assert.equals.args]]
type = "any"
required = false
[[assert.same.args]]
type = "any"
[[assert.same.args]]
type = "any"
[[assert.truthy.args]]
type = "any"
[[assert.spy.args]]
type = "any"
[[assert.stub.args]]
type = "any"

@ -0,0 +1,47 @@
---
base: lua52
name: vim
globals:
_G:
property: new-fields
_NgConfigValues:
any: true
after_each:
args:
- type: function
assert.equals:
args:
- type: any
- type: any
- required: false
type: any
assert.is_not:
any: true
assert.same:
args:
- type: any
- type: any
assert.spy:
args:
- type: any
assert.stub:
args:
- type: any
assert.truthy:
args:
- type: any
before_each:
args:
- type: function
debug:
property: read-only
describe:
args:
- type: string
- type: function
it:
args:
- type: string
- type: function
vim:
any: true
Loading…
Cancel
Save