Compare commits

..

No commits in common. 'master' and 'v0.2.2' have entirely different histories.

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

@ -10,23 +10,19 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: ubuntu-22.04 - os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz url: https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz
manager: sudo snap manager: sudo snap
packages: go packages: go
- os: ubuntu-22.04 - os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/download/v0.7.0/nvim-linux64.tar.gz url: https://github.com/neovim/neovim/releases/download/v0.5.1/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 manager: sudo snap
packages: go packages: go
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-go@v2 - uses: actions/setup-go@v2
with: with:
go-version: "^1.18.1" # The Go version to download (if necessary) and use. go-version: "^1.17.2" # The Go version to download (if necessary) and use.
- run: date +%F > todays-date - run: date +%F > todays-date
- name: Restore cache for today's nightly. - name: Restore cache for today's nightly.
uses: actions/cache@v2 uses: actions/cache@v2
@ -42,7 +38,7 @@ jobs:
mkdir -p _neovim mkdir -p _neovim
curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim" curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
} }
GO111MODULE=on go install golang.org/x/tools/gopls@latest GO111MODULE=on go get golang.org/x/tools/gopls@latest
mkdir -p ~/.local/share/nvim/site/pack/vendor/start 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/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim

2
.gitignore vendored

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

@ -1,16 +1,9 @@
# Navigator # Navigator
- Source code analysis and navigate tool
- Easy code navigation, view diagnostic errors, see relationships of functions, variables - 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. - 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 Here are some examples
#### Example: Javascript closure #### Example: Javascript closure
@ -64,19 +57,17 @@ variable is:
all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface
implementation. implementation.
- Code Action GUI
- Luv async thread and tasks - Luv async thread and tasks
- Edit your code in preview window - Edit your code in preview window
- Async request with lsp.buf_request for reference search - Async request with lsp.buf_request for reference search
- Treesitter symbol search. It is handy for large 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. - 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 - FZY search with either native C (if gcc installed) or Lua-JIT
- LSP multiple symbols highlight/marker and hop between document references - LSP multiple symbol highlight/marker and hop between document references
- Preview definination/references - Preview definination/references
@ -96,11 +87,7 @@ variable is:
- ccls call hierarchy (Non-standard `ccls/call` API) supports - 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. - Syntax folding based on treesitter folding algorithm. (It behaves similar to vs-code)
- 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. - Fully support LSP CodeAction, CodeLens, CodeLens action. Help you improve code quality.
@ -110,8 +97,6 @@ variable is:
- Multigrid support (different font and detachable) - Multigrid support (different font and detachable)
- Side panel (sidebar) and floating windows
# Why a new plugin # Why a new plugin
I'd like to go beyond what the system is offering. I'd like to go beyond what the system is offering.
@ -127,7 +112,7 @@ I'd like to go beyond what the system is offering.
# Install # Install
Require nvim-0.6.1 or above, nightly (0.8) prefered Require nvim-0.5.0 (a.k.a nightly)
You can remove your lspconfig setup and use this plugin. You can remove your lspconfig setup and use this plugin.
The plugin depends on lspconfig and [guihua.lua](https://github.com/ray-x/guihua.lua), which provides GUI and fzy support(migrate from [romgrk's project](romgrk/fzy-lua-native)). The plugin depends on lspconfig and [guihua.lua](https://github.com/ray-x/guihua.lua), which provides GUI and fzy support(migrate from [romgrk's project](romgrk/fzy-lua-native)).
@ -138,18 +123,14 @@ Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua' Plug 'ray-x/navigator.lua'
``` ```
Note: Highly recommend: 'nvim-treesitter/nvim-treesitter' Note: Highly recommened: 'nvim-treesitter/nvim-treesitter'
Packer Packer
```lua ```lua
use({
'ray-x/navigator.lua', use {'ray-x/navigator.lua', requires = {'ray-x/guihua.lua', run = 'cd lua/fzy && make'}}
requires = {
{ 'ray-x/guihua.lua', run = 'cd lua/fzy && make' },
{ 'neovim/nvim-lspconfig' },
},
})
``` ```
## Setup ## Setup
@ -169,14 +150,14 @@ Plug 'neovim/nvim-lspconfig'
Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' } Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua' Plug 'ray-x/navigator.lua'
" Plug 'hrsh7th/nvim-cmp' and other plugins you commenly use... " Plug 'hrsh7th/nvim-compe' and other plugins you commenly use...
" optional, if you need treesitter symbol support " optional, if you need treesitter symbol support
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'} Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
call plug#end() call plug#end()
" No need for require('lspconfig'), navigator will configure it for you " No need for rquire('lspconfig'), navigator will configure it for you
lua <<EOF lua <<EOF
require'navigator'.setup() require'navigator'.setup()
EOF EOF
@ -220,16 +201,11 @@ e.g.
```lua ```lua
require('nvim-autopairs').setup{ require('nvim-autopairs').setup{
disable_filetype = { "TelescopePrompt" , "guihua", "guihua_rust", "clap_input" }, disable_filetype = { "TelescopePrompt" , "guihua", "clap_input" },
if vim.o.ft == 'clap_input' and vim.o.ft == 'guihua' and vim.o.ft == 'guihua_rust' then if vim.o.ft ~= 'clap_input' and vim.o.ft ~= 'guihua' then
require'cmp'.setup.buffer { completion = {enable = false} } require'cmp'.setup.buffer { completion = {enable = false} }
end 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 }")
... ...
} }
@ -242,7 +218,7 @@ Nondefault configuration example:
```lua ```lua
require'navigator'.setup({ require'navigator'.setup({
debug = false, -- log output, set to true and log path: ~/.cache/nvim/gh.log debug = false, -- 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) 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 height = 0.3, -- max list window height, 0.3 by default
preview_height = 0.35, -- max height of preview windows preview_height = 0.35, -- max height of preview windows
@ -257,54 +233,32 @@ require'navigator'.setup({
-- end, -- end,
-- The attach code will apply to all LSP clients -- The attach code will apply to all LSP clients
ts_fold = false, -- modified version of treesitter folding default_mapping = true, -- set to false if you will remap every key
default_mapping = true, -- set to false if you will remap every key or if you using old version of nvim- keymaps = {{key = "gK", func = "declaration()"}}, -- a list of key maps
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 -- this kepmap gK will override "gD" mapping function declaration() in default kepmap
-- please check mapping.lua for all keymaps -- please check mapping.lua for all keymaps
treesitter_analysis = true, -- treesitter variable context treesitter_analysis = true, -- treesitter variable context
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 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 = { icons = {
-- Code action -- Code action
code_action_icon = "🏏", -- note: need terminal support, for those not support unicode, might crash code_action_icon = "🏏",
-- Diagnostics -- Diagnostics
diagnostic_head = '🐛', diagnostic_head = '🐛',
diagnostic_head_severity_1 = "🈲", diagnostic_head_severity_1 = "🈲",
-- refer to lua/navigator.lua for more icons setups -- 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_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 = { 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_action = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
code_lens_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, format_on_save = true, -- set to false to disasble lsp code format on save (if you are using prettier/efm/formater etc)
-- 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 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 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 -- only want to enable one lsp server
-- to disable all default config and use your own lsp setup set -- 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 -- disable_lsp = 'all'
-- Default {} -- Default {}
diagnostic = { diagnostic_scroll_bar_sign = {'▃', '▆', '█'}, -- experimental: diagnostic status in scroll bar area; set to nil to disable the diagnostic sign,
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 {'-', '='} -- for other style, set to {'╍', 'ﮆ'} or {'-', '='}
diagnostic_virtual_text = true, -- show virtual for diagnostic message diagnostic_virtual_text = true, -- show virtual for diagnostic message
diagnostic_update_in_insert = false, -- update diagnostic message in insert mode diagnostic_update_in_insert = false, -- update diagnostic message in insert mode
@ -314,11 +268,6 @@ require'navigator'.setup({
filetypes = {'typescript'} -- disable javascript etc, filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetypes -- 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 gopls = { -- gopls setting
on_attach = function(client, bufnr) -- on_attach for gopls on_attach = function(client, bufnr) -- on_attach for gopls
-- your special on attach here -- your special on attach here
@ -330,27 +279,10 @@ require'navigator'.setup({
gopls = {gofumpt = false} -- disable gofumpt etc, 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_lua = {
sumneko_root_path = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server", sumneko_root_path = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server",
sumneko_binary = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server/bin/macOS/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},
} }
}) })
@ -367,8 +299,7 @@ local servers = {
"jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls", "jedi_language_server", "jdtls", "sumneko_lua", "vimls", "html", "jsonls", "solargraph", "cssls",
"yamlls", "clangd", "ccls", "sqls", "denols", "graphql", "dartls", "dotls", "yamlls", "clangd", "ccls", "sqls", "denols", "graphql", "dartls", "dotls",
"kotlin_language_server", "nimls", "intelephense", "vuels", "phpactor", "omnisharp", "kotlin_language_server", "nimls", "intelephense", "vuels", "phpactor", "omnisharp",
"r_language_server", "rust_analyzer", "terraformls", "svelte", "texlab", "clojure_lsp", "elixirls", "r_language_server", "rust_analyzer", "terraformls"
"sourcekit", "fsautocomplete", "vls", "hls"
} }
``` ```
@ -394,30 +325,15 @@ servers. (Prevent loading multiple LSP for same source code.) e.g. I saw strange
pylsp+pyright+jedi pylsp+pyright+jedi
together. If you have multiple similar LSP installed and have trouble with the plugin, please enable only one at a time. 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 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'}`
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
### Disable a lsp client loading from 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. To disable a specific LSP, set `filetypes` to {} e.g.
```lua ```lua
require'navigator'.setup({ require'navigator'.setup({
lsp={
pylsd={filetype={}} pylsd={filetype={}}
}
}) })
``` ```
@ -426,22 +342,15 @@ Or:
```lua ```lua
require'navigator'.setup({ require'navigator'.setup({
lsp={
disable_lsp = {'pylsd', 'sqlls'}, 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 ### Default keymaps
| mode | key | function | | mode | key | function |
| ---- | --------------- | ---------------------------------------------------------- | | ---- | --------------- | ---------------------------------------------------------- |
| n | gr | async references, definitions and context | | n | gr | show reference and context |
| n | \<Leader>gr | show reference and context |
| i | \<m-k\> | signature help | | i | \<m-k\> | signature help |
| n | \<c-k\> | signature help | | n | \<c-k\> | signature help |
| n | gW | workspace symbol | | n | gW | workspace symbol |
@ -449,23 +358,21 @@ In `playground` folder, there is a `init.lua` and source code for you to play wi
| n | gd | definition | | n | gd | definition |
| n | g0 | document symbol | | n | g0 | document symbol |
| n | \<C-]\> | go to definition (if multiple show listview) | | n | \<C-]\> | go to definition (if multiple show listview) |
| n | gp | definition preview (show Preview) | | n | gp | definition preview (Go to Preview) |
| n | \<C-LeftMouse\> | definition | | n | \<C-LeftMouse\> | definition |
| n | g\<LeftMouse\> | implementation | | n | g\<LeftMouse\> | implementation |
| n | \<Leader>gt | treesitter document symbol | | n | gT | treesitter document symbol |
| n | \<Leader\>gT | treesitter symbol for all open buffers | | 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 | K | hover doc |
| n | \<Space\>ca | code action (when you see 🏏 ) | | n | \<Space\>ca | code action (when you see 💡 ) |
| n | \<Space\>la | code lens action (when you see a codelens indicator) | | n | \<Space\>la | code lens action (when you see a codelens indicator) |
| v | \<Space\>ca | range code action (when you see 🏏 ) | | v | \<Space\>cA | range code action (when you see 💡 ) |
| n | \<Space\>rn | rename with floating window | | n | \<Space\>rn | rename with floating window |
| n | \<Leader\>re | rename (lsp default) | | n | \<Leader\>re | rename (lsp default) |
| n | \<Leader\>gi | hierarchy incoming calls | | n | \<Leader\>gi | incoming calls |
| n | \<Leader\>go | hierarchy outgoing calls | | n | \<Leader\>go | outgoing calls |
| n | gi | implementation | | n | gi | implementation |
| n | \<Space\> D | type definition | | n | \<Sapce\> D | type definition |
| n | gL | show line diagnostic | | n | gL | show line diagnostic |
| n | gG | show diagnostic for all buffers | | n | gG | show diagnostic for all buffers |
| n | ]d | next diagnostic | | n | ]d | next diagnostic |
@ -473,9 +380,9 @@ In `playground` folder, there is a `init.lua` and source code for you to play wi
| n | \<Leader\> dt | diagnostic toggle(enable/disable) | | n | \<Leader\> dt | diagnostic toggle(enable/disable) |
| n | ]r | next treesitter reference/usage | | n | ]r | next treesitter reference/usage |
| n | [r | previous treesitter reference/usage | | n | [r | previous treesitter reference/usage |
| n | \<Space\> wa | add workspace folder | | n | \<Sapce\> wa | add workspace folder |
| n | \<Space\> wr | remove workspace folder | | n | \<Sapce\> wr | remove workspace folder |
| n | \<Space\> wl | print workspace folder | | n | \<Sapce\> wl | print workspace folder |
| n | \<Leader\>k | toggle reference highlight | | n | \<Leader\>k | toggle reference highlight |
| i/n | \<C-p\> | previous item in list | | i/n | \<C-p\> | previous item in list |
| i/n | \<C-n\> | next item in list | | i/n | \<C-n\> | next item in list |
@ -490,21 +397,20 @@ In `playground` folder, there is a `init.lua` and source code for you to play wi
| n | \<Enter\> | open preview file in nvim/Apply action | | n | \<Enter\> | open preview file in nvim/Apply action |
| n | \<ESC\> | close listview of floating window | | n | \<ESC\> | close listview of floating window |
| i/n | \<C-e\> | 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-b\> | previous page in listview |
| i/n | \<C-f\> | next page in listview | | i/n | \<C-f\> | next page in listview |
| i/n | \<C-s\> | save the modification to preview window to file | | i/n | \<C-s\> | save the modification to preview window to file |
### Colors/Highlight: ### Colors/Highlight:
You can override default highlight GuihuaListDark (listview) and GuihuaTextViewDark (code view) and GuihuaListHl (select item) You can override default highlight GHListDark (listview) and GHTextViewDark (code view) and GHListHl (select item)
e.g. e.g.
```vim ```vim
hi default GuihuaTextViewDark guifg=#e0d8f4 guibg=#332e55 hi default GHTextViewDark guifg=#e0d8f4 guibg=#332e55
hi default GuihuaListDark guifg=#e0d8f4 guibg=#103234 hi default GHListDark guifg=#e0d8f4 guibg=#103234
hi default GuihuaListHl guifg=#e0d8f4 guibg=#404254 hi default GHListHl 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,
@ -522,121 +428,28 @@ 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). 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) ## Integration with lsp_installer (williamboman/nvim-lsp-installer)
If you are using mason or lsp_installer and would like to use the lsp servers installed by lsp_installer. Please set
```lua If you'd like to only use the lsp servers installed by lsp_installer. Please set
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 ```lua
use("williamboman/mason.nvim") lsp_installer = true
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,
})
``` ```
In the config.
Please refer to [lsp_installer_config](https://github.com/ray-x/navigator.lua/blob/master/playground/init_lsp_installer.lua) Navigator will startup the server installed by lsp-installer. Please do not call `server:setup{opts}` from lsp installer
for more info
Alternatively, Navigator can be used to startup the server installed by lsp-installer.
as it will override the navigator setup as it will override the navigator setup
To start LSP installed by lsp_installer, please use following setups Also, could use following setups
```lua ```lua
require'navigator'.setup({ require'navigator'.setup({
-- lsp_installer = false -- default value is false -- lsp_installer = false -- default value is false
lsp = { lsp = {
tsserver = { cmd = {'your tsserver installed by lsp_installer or mason'} } tsserver = { cmd = {'your tsserver installed by lsp_installer'} }
-- e.g. tsserver = { cmd = {'/home/username/.local/share/nvim/mason/packages/typescript-language-server/node_modules/typescript/bin/tsserver'} }
} }
}) })
@ -645,95 +458,10 @@ require'navigator'.setup({
example cmd setup (mac) for pyright : 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" } 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 ## Usage
Please refer to lua/navigator/lspclient/mapping.lua on key mappings. Should be able to work out-of-box. Please refer to lua/navigator/lspclient/mapping.lua on key mappings. Should be able to work out-of-box.
@ -760,7 +488,7 @@ Highlight I am using:
- LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()` - 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. 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 - GHListDark and GHTextViewDark is used for floating listvew and TextView. They are be based on current background
(Normal) and PmenuSel (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
@ -770,16 +498,8 @@ You can override the above highlight to fit your current colorscheme
## commands ## commands
| command | function | | command | function |
| ------------ | ------------------------- | | ------------ | ---------------------- |
| LspToggleFmt | toggle lsp auto format | | 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 ## Screenshots
@ -795,29 +515,15 @@ Using treesitter and LSP to view the symbol definition
![image](https://user-images.githubusercontent.com/1681295/139771978-bbc970a5-be9f-42cf-8942-3477485bd89c.png) ![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 ### GUI and multigrid support
You can load a different font size for floating win You can load a different font size for floating win
![multigrid2](https://user-images.githubusercontent.com/1681295/139196378-bf69ade9-c916-42a9-a91f-cccb39b9c4eb.jpg) ![multigrid2](https://user-images.githubusercontent.com/1681295/139196378-bf69ade9-c916-42a9-a91f-cccb39b9c4eb.jpg)
### Document Symbol and navigate through the list ### Document Symbol
![doc_symbol_and_navigate](https://user-images.githubusercontent.com/1681295/148642747-1870b1a4-67c2-4a0d-8a41-d462ecdc663e.gif) ![document symbol](https://github.com/ray-x/files/blob/master/img/navigator/doc_symbol.gif?raw=true)
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
@ -868,10 +574,6 @@ 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) ![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 #### Fill struct with gopls
![code actions fill struct](https://github.com/ray-x/files/blob/master/img/navigator/fill_struct.gif?raw=true) ![code actions fill struct](https://github.com/ray-x/files/blob/master/img/navigator/fill_struct.gif?raw=true)
@ -893,9 +595,9 @@ 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") ![show_signature](https://github.com/ray-x/files/blob/master/img/navigator/show_signnature.gif?raw=true "show_signature")
### Call hierarchy (incomming/outgoing calls) ### Call hierarchy (incomming/outgoing)
![incomming_calls](https://user-images.githubusercontent.com/1681295/142348079-49b71486-4f16-4f10-95c9-483aad11c262.jpg) ![incomming](https://github.com/ray-x/files/blob/master/img/navigator/incomming.jpg?raw=true)
### Light bulb if codeAction available ### Light bulb if codeAction available
@ -915,19 +617,6 @@ Codelens for C++/ccls. Symbol reference
![nerdfont](https://github.com/ray-x/files/blob/master/img/navigator/icon_nerd.jpg?raw=true) ![nerdfont](https://github.com/ray-x/files/blob/master/img/navigator/icon_nerd.jpg?raw=true)
### 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 # Debug the plugin
One simple way to gether debug info and understand what is wrong is output the debug logs One simple way to gether debug info and understand what is wrong is output the debug logs
@ -964,14 +653,3 @@ end
- Async (some of the requests is slow on large codebases and might be good to use co-rountine) - 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) - 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 - 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)

@ -1,4 +1,4 @@
function! folding#ngfoldexpr() function! folding#foldexpr()
" return luaeval(printf('require"navigator.foldinglsp".get_fold_indic(%d)', v:lnum)) " return luaeval(printf('require"navigator.foldinglsp".get_fold_indic(%d)', v:lnum))
return luaeval(printf('require"navigator.foldts".get_fold_indic(%d)', v:lnum)) return luaeval(printf('require"navigator.foldts".get_fold_indic(%d)', v:lnum))
endfunction endfunction

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

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

@ -1,12 +0,0 @@
<!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,75 +1,48 @@
local M = {} 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 = { _NgConfigValues = {
debug = false, -- log output debug = false, -- log output not implemented
width = 0.62, -- valeu of cols width = 0.62, -- valeu of cols
height = 0.38, -- listview height height = 0.38, -- listview height
preview_height = 0.38, preview_height = 0.38,
preview_lines = 40, -- total lines in preview screen preview_lines = 40, -- total lines in preview screen
preview_lines_before = 5, -- lines before the highlight line preview_lines_before = 5, -- lines before the highlight line
default_mapping = true, default_mapping = true,
keymaps = {}, -- e.g keymaps={{key = "GR", func = vim.lsp.buf.references}, } this replace gr default mapping keymaps = {}, -- e.g keymaps={{key = "GR", func = "references()"}, } this replace gr default mapping
external = nil, -- true: enable for goneovim multigrid otherwise false external = nil, -- true: enable for goneovim multigrid otherwise false
border = 'single', -- border style, can be one of 'none', 'single', 'double', "shadow" 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, combined_attach = "both", -- both: use both customized attach and navigator default attach, mine: only use my attach defined in vimrc
-- fuzzy finder prompt will be shown on_attach = nil,
combined_attach = 'both', -- both: use both customized attach and navigator default attach, mine: only use my attach defined in vimrc -- function(client, bufnr)
on_attach = function(client, bufnr) -- -- your on_attach will be called at end of navigator on_attach
-- your on_attach will be called at end of navigator on_attach -- end,
end,
ts_fold = false, ts_fold = false,
-- code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
-- code_lens_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true},
treesitter_analysis = true, -- treesitter variable context treesitter_analysis = 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 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 signature_help_cfg = nil, -- if you would like to init lsp_signature plugin in navigator, pass in signature help
-- setup here. if it is nil, navigator will not init signature help -- 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 = { lsp = {
enable = true, -- if disabled make sure add require('navigator.lspclient.mapping').setup() in you on_attach
code_action = { code_action = {
enable = true, enable = true,
sign = true, sign = true,
sign_priority = 40, sign_priority = 40,
virtual_text = true, virtual_text = true,
virtual_text_icon = true, virtual_text_icon = true
}, },
document_highlight = true, -- highlight reference a symbol
code_lens_action = { code_lens_action = {
enable = true, enable = true,
sign = true, sign = true,
sign_priority = 40, sign_priority = 40,
virtual_text = true, virtual_text = true,
virtual_text_icon = 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_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_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 disable_lsp = nil, -- a list of lsp server disabled for your project, e.g. denols and tsserver you may
code_lens = false,
-- only want to enable one lsp server -- only want to enable one lsp server
disply_diagnostic_qf = true, -- always show quickfix if there are diagnostic errors 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 diagnostic_load_files = false, -- lsp diagnostic errors list may contains uri that not opened yet set to true
@ -81,84 +54,67 @@ _NgConfigValues = {
-- filetypes = {'typescript'} -- disable javascript etc, -- filetypes = {'typescript'} -- disable javascript etc,
-- set to {} to disable the lspclient for all filetype -- 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_lua = {
-- sumneko_root_path = sumneko_root_path, -- sumneko_root_path = sumneko_root_path,
-- sumneko_binary = sumneko_binary, -- sumneko_binary = sumneko_binary,
-- cmd = {'lua-language-server'} -- 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 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 = {
icons = true, -- set to false to use system default ( if you using a terminal does not have nerd/icon) icons = true, -- set to false to use system default ( if you using a terminal does not have nerd/icon)
-- Code action -- Code action
code_action_icon = '🏏', -- "", code_action_icon = "🏏", -- "",
-- code lens -- code lens
code_lens_action_icon = '👓', code_lens_action_icon = "👓",
-- Diagnostics -- Diagnostics
diagnostic_head = '🐛', diagnostic_head = '🐛',
diagnostic_err = '📛', diagnostic_err = "📛",
diagnostic_warn = '👎', diagnostic_warn = "👎",
diagnostic_info = [[👩]], diagnostic_info = [[👩]],
diagnostic_hint = [[💁]], diagnostic_hint = [[💁]],
diagnostic_head_severity_1 = '🈲', diagnostic_head_severity_1 = "🈲",
diagnostic_head_severity_2 = '☣️', diagnostic_head_severity_2 = "☣️",
diagnostic_head_severity_3 = '👎', diagnostic_head_severity_3 = "👎",
diagnostic_head_description = '👹', diagnostic_head_description = "👹",
diagnostic_virtual_text = '🦊', diagnostic_virtual_text = "🦊",
diagnostic_file = '🚑', diagnostic_file = "🚑",
-- Values -- Values
value_changed = '📝', value_changed = "📝",
value_definition = '🐶🍡', -- it is easier to see than 🦕 value_definition = "🦕",
side_panel = {
section_separator = '',
line_num_left = '',
line_num_right = '',
inner_node = '├○',
outer_node = '╰○',
bracket_left = '',
bracket_right = '',
},
-- Treesitter -- Treesitter
match_kinds = { match_kinds = {
var = '', -- "👹", -- Vampaire var = "", -- "👹", -- Vampaire
method = 'ƒ ', -- "🍔", -- mac method = "ƒ ", -- "🍔", -- mac
['function'] = '', -- "🤣", -- Fun ["function"] = "", -- "🤣", -- Fun
parameter = '', -- Pi parameter = "", -- Pi
associated = '🤝', associated = "🤝",
namespace = '🚀', namespace = "🚀",
type = '', type = "",
field = '🏈', field = "🏈"
module = '📦',
flag = '🎏',
},
treesitter_defult = '🌲',
doc_symbols = '',
}, },
treesitter_defult = "🌲"
}
} }
vim.cmd("command! -nargs=0 LspLog lua require'navigator.lspclient.config'.open_lsp_log()")
vim.cmd("command! -nargs=0 LspRestart lua require'navigator.lspclient.config'.reload_lsp()")
vim.cmd("command! -nargs=0 LspToggleFmt lua require'navigator.lspclient.mapping'.toggle_lspformat()<CR>")
M.deprecated = function(cfg) M.deprecated = function(cfg)
local warn = require'navigator.util'.warn
if cfg.code_action_prompt then if cfg.code_action_prompt then
warn('code_action_prompt moved to lsp.code_action') warn("code_action_prompt moved to lsp.code_action")
end end
if cfg.code_lens_action_prompt then if cfg.code_lens_action_prompt then
warn('code_lens_action_prompt moved to lsp.code_lens_action') warn("code_lens_action_prompt moved to lsp.code_lens_action")
end end
if cfg.lsp ~= nil and cfg.lsp.disable_format_ft ~= nil and cfg.lsp.disable_format_ft ~= {} then 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') warn('disable_format_ft renamed to disable_format_cap')
end 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 if cfg.lspinstall ~= nil then
warn('lspinstall deprecated, please use lsp-installer instead or use "lspinstall" branch') warn('lspinstall deprecated, please use lsp-installer instead or use "lspinstall" branch')
end end
@ -169,73 +125,26 @@ local extend_config = function(opts)
if next(opts) == nil then if next(opts) == nil then
return return
end end
if opts.debug then
_NgConfigValues.debug = opts.debug
end
-- enable logs
require('navigator.util').setup()
for key, value in pairs(opts) do for key, value in pairs(opts) do
if _NgConfigValues[key] == nil then -- if _NgConfigValues[key] == nil then
warn( -- error(string.format("[] Key %s not valid", key))
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 -- return
else -- end
if type(_NgConfigValues[key]) == 'table' then 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 for k, v in pairs(value) do
if type(k) == 'number' then
-- replace all item in array
_NgConfigValues[key] = value
break
end
-- level 3 -- level 3
if type(_NgConfigValues[key][k]) == 'table' then if type(_NgConfigValues[key][k]) == "table" then
if type(v) == 'table' then
for k2, v2 in pairs(v) do for k2, v2 in pairs(v) do
_NgConfigValues[key][k][k2] = v2 _NgConfigValues[key][k][k2] = v2
end end
else else
_NgConfigValues[key][k] = v _NgConfigValues[key][k] = v
end 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 end
else else
_NgConfigValues[key] = value _NgConfigValues[key] = value
end end
end end
end
if _NgConfigValues.sumneko_root_path or _NgConfigValues.sumneko_binary then if _NgConfigValues.sumneko_root_path or _NgConfigValues.sumneko_binary then
vim.notify("Please put sumneko setup in lsp['sumneko_lua']", vim.log.levels.WARN) vim.notify("Please put sumneko setup in lsp['sumneko_lua']", vim.log.levels.WARN)
end end
@ -248,50 +157,32 @@ M.config_values = function()
end end
M.setup = function(cfg) M.setup = function(cfg)
cfg = cfg or {}
extend_config(cfg) extend_config(cfg)
local cmd_group = api.nvim_create_augroup('NGFtGroup', {}) vim.cmd([[autocmd FileType,BufEnter * lua require'navigator.lspclient.clients'.setup()]]) -- BufWinEnter BufNewFile,BufRead ?
api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, { -- local log = require"navigator.util".log
group = cmd_group, -- log(debug.traceback())
pattern = '*', -- log(cfg, _NgConfigValues)
callback = function() -- print("loading navigator")
require('navigator.lspclient.clients').on_filetype()
end,
})
require('navigator.lazyloader').init() require('navigator.lazyloader').init()
require('navigator.lspclient.clients').setup(_NgConfigValues) require('navigator.lspclient.clients').setup(_NgConfigValues)
-- require("navigator.lspclient.mapping").setup(_NgConfigValues)
require("navigator.reference")
require("navigator.definition")
require("navigator.hierarchy")
require("navigator.implementation")
require('navigator.reference') -- log("navigator loader")
require('navigator.definition')
require('navigator.hierarchy')
require('navigator.implementation')
cfg.lsp = cfg.lsp or _NgConfigValues.lsp -- vim.cmd("autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4")
if _NgConfigValues.lsp.enable then
require('navigator.diagnostics').config(cfg.lsp.diagnostic)
end
if not _NgConfigValues.loaded then if not _NgConfigValues.loaded then
_NgConfigValues.loaded = true _NgConfigValues.loaded = true
end end
if _NgConfigValues.ts_fold == true then if _NgConfigValues.ts_fold == true then
local ok, _ = pcall(require, 'nvim-treesitter')
if ok then
require('navigator.foldts').on_attach() require('navigator.foldts').on_attach()
end 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 end
return M return M

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

@ -1,21 +1,138 @@
local util = require('navigator.util') local util = require "navigator.util"
local log = util.log local log = util.log
local trace = util.trace local trace = util.trace
local code_action = {} local code_action = {}
-- local gui = require('navigator.gui') local gui = require "navigator.gui"
local config = require('navigator').config_values() local config = require("navigator").config_values()
local api = vim.api local api = vim.api
-- trace = log
local sign_name = 'NavigatorLightBulb' local sign_name = "NavigatorLightBulb"
--- `codeAction/resolve` --- `codeAction/resolve`
-- from neovim buf.lua, change vim.ui.select to gui -- from neovim buf.lua, change vim.ui.select to gui
local function on_code_action_results(results, ctx)
local trace = log
local action_tuples = {}
local data = {"  Auto Fix <C-o> Apply <C-e> Exit"}
for client_id, result in pairs(results) do
for i, action in pairs(result.result or {}) do
local title = 'apply action'
trace(action)
if action.edit and action.edit.title then
local edit = action.edit
title = edit.title:gsub("\r\n", "")
title = title:gsub("\n", "")
elseif action.title then
title = action.title:gsub("\r\n", "")
title = title:gsub("\n", "")
elseif action.command and action.command.title then
title = action.command.title:gsub("\n", "")
title = title:gsub("\n", "")
end
local edit = action.edit or {}
-- trace(edit.documentChanges)
if edit.documentChanges or edit.changes then
local changes = edit.documentChanges or edit.changes
-- trace(action.edit.documentChanges)
for _, change in pairs(changes or {}) do
-- trace(change)
if change.edits then
title = title .. " [newText:]"
for _, ed in pairs(change.edits) do
-- trace(ed)
if ed.newText and ed.newText ~= "" then
local newText = ed.newText:gsub("\n\t", "")
newText = newText:gsub("\n", "")
title = title .. " (" .. newText
if ed.range then
title = title .. " line: " .. tostring(ed.range.start.line) .. ")"
else
title = title .. ")"
end
end
end
elseif change.newText and change.newText ~= "" then
local newText = change.newText:gsub("\"\n\t\"", "")
newText = newText:gsub("\n", "")
title = title .. " (newText: " .. newText
if change.range then
title = title .. " line: " .. tostring(change.range.start.line) .. ")"
else
title = title .. ")"
end
end
end
end
title = string.format("[%d] %s", i, title)
table.insert(data, title)
table.insert(action_tuples, {client_id, action, title, i})
end
end
log(action_tuples)
log(data)
if #action_tuples == 0 then
vim.notify('No code actions available', vim.log.levels.INFO)
return
end
local width = 42
for _, str in ipairs(data) do
if #str > width then
width = #str
end
end
local divider = string.rep('', width + 2)
table.insert(data, 2, divider)
local listview = gui.new_list_view {
items = data,
width = width + 4,
loc = "top_center",
relative = "cursor",
rawdata = true,
data = data,
on_confirm = function(item)
trace(item)
local action_chosen = nil
for _, value in pairs(action_tuples) do
if value[3] == item then
action_chosen = value
return require('navigator.lspwrapper').on_user_choice(action_chosen, ctx)
end
end
end,
on_move = function(pos)
trace(pos)
return pos
end
}
log("new buffer", listview.bufnr)
vim.api.nvim_buf_add_highlight(listview.bufnr, -1, 'Title', 0, 0, -1)
end
local diagnostic = vim.diagnostic or vim.lsp.diagnostic local diagnostic = vim.diagnostic or vim.lsp.diagnostic
code_action.code_action_handler = util.mk_handler(function(err, results, ctx, cfg)
if err ~= nil then
log("code action err", err, results, ctx, cfg)
return
end
on_code_action_results(results, ctx)
end)
-- https://github.com/glepnir/lspsaga.nvim/blob/main/lua/lspsaga/codeaction.lua -- https://github.com/glepnir/lspsaga.nvim/blob/main/lua/lspsaga/codeaction.lua
-- lspsaga has a clever design to inject code action indicator -- lspsaga has a clever design to inject code action indicator
local sign_group = 'nvcodeaction' local sign_group = "nvcodeaction"
local get_namespace = function() local get_namespace = function()
return api.nvim_create_namespace(sign_group) return api.nvim_create_namespace(sign_group)
end end
@ -30,46 +147,38 @@ local function _update_virtual_text(line, actions)
if line then if line then
trace(line, actions) trace(line, actions)
local icon_with_indent = ' ' .. config.icons.code_action_icon local icon_with_indent = " " .. config.icons.code_action_icon
local title = actions[1].title local title = actions[1].title
pcall(api.nvim_buf_set_extmark, 0, namespace, line, -1, { pcall(api.nvim_buf_set_extmark, 0, namespace, line, -1, {
virt_text = { { icon_with_indent .. title, 'LspDiagnosticsSignHint' } }, virt_text = {{icon_with_indent .. title, "LspDiagnosticsSignHint"}},
virt_text_pos = 'overlay', virt_text_pos = "overlay",
hl_mode = 'combine', hl_mode = "combine"
}) })
end end
end end
local function _update_sign(line) local function _update_sign(line)
if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then
vim.fn.sign_define(sign_name, { vim.fn.sign_define(sign_name, {
text = config.icons.code_action_icon, text = config.icons.code_action_icon,
texthl = 'LspDiagnosticsSignHint', texthl = "LspDiagnosticsSignHint"
}) })
end end
local winid = get_current_winid() local winid = get_current_winid()
if code_action[winid] == nil then if code_action[winid] == nil then
code_action[winid] = {} code_action[winid] = {}
end end
-- only show code action on the current line, remove all others if code_action[winid].lightbulb_line ~= 0 then
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 = "%"})
vim.fn.sign_unplace(sign_group, { id = code_action[winid].lightbulb_line, buffer = '%' })
log('sign removed', line)
end end
if line then if line then
-- log("updatasign", line, sign_group, sign_name) -- log("updatasign", line, sign_group, sign_name)
local id = vim.fn.sign_place( vim.fn.sign_place(line, sign_group, sign_name, "%",
line, {lnum = line + 1, priority = config.lsp.code_action.sign_priority})
sign_group, code_action[winid].lightbulb_line = line
sign_name,
'%',
{ lnum = line + 1, priority = config.lsp.code_action.sign_priority }
)
code_action[winid].lightbulb_line = id
log('sign updated', id)
end end
end end
@ -78,15 +187,7 @@ local need_check_diagnostic = { ['python'] = true }
function code_action:render_action_virtual_text(line, diagnostics) function code_action:render_action_virtual_text(line, diagnostics)
return function(err, actions, context) return function(err, actions, context)
trace(actions, context) if actions == nil or type(actions) ~= "table" or vim.tbl_isempty(actions) then
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 -- no actions cleanup
if config.lsp.code_action.virtual_text then if config.lsp.code_action.virtual_text then
_update_virtual_text(nil) _update_virtual_text(nil)
@ -96,13 +197,12 @@ function code_action:render_action_virtual_text(line, diagnostics)
end end
else else
trace(err, line, diagnostics, actions, context) trace(err, line, diagnostics, actions, context)
if config.lsp.code_action.sign then if config.lsp.code_action.sign then
if need_check_diagnostic[vim.bo.filetype] then if need_check_diagnostic[vim.bo.filetype] then
if next(diagnostics) == nil then if next(diagnostics) == nil then
-- no diagnostic, no code action sign..
_update_sign(nil) _update_sign(nil)
else else
-- no diagnostic, no code action sign..
_update_sign(line) _update_sign(line)
end end
else else
@ -126,13 +226,13 @@ function code_action:render_action_virtual_text(line, diagnostics)
end end
local special_buffers = { local special_buffers = {
['lspsagafinder'] = true, ["lspsagafinder"] = true,
['NvimTree'] = true, ["NvimTree"] = true,
['vista'] = true, ["vista"] = true,
['guihua'] = true, ["guihua"] = true,
['lspinfo'] = true, ["lspinfo"] = true,
['markdown'] = true, ["markdown"] = true,
['text'] = true, ["text"] = true
} }
-- local action_call_back = function (_,_) -- local action_call_back = function (_,_)
-- return Action:action_callback() -- return Action:action_callback()
@ -148,59 +248,35 @@ local code_action_req = function(_call_back_fn, diagnostics)
params.context = context params.context = context
local line = params.range.start.line local line = params.range.start.line
local callback = _call_back_fn(line, diagnostics) 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 end
local function sort_select(action_tuples, opts, on_user_choice) local function code_action_request(params)
if action_tuples ~= nil and action_tuples[1][2] ~= nil and action_tuples[1][2].command then local bufnr = vim.api.nvim_get_current_buf()
table.sort(action_tuples, function(a, b) local method = 'textDocument/codeAction'
return a[1] > b[1] vim.lsp.buf_request_all(bufnr, method, params, function(results)
on_code_action_results(results, {bufnr = bufnr, method = method, params = params})
end) end)
end end
trace(action_tuples)
require('guihua.gui').select(action_tuples, opts, on_user_choice)
end
code_action.code_action = function() code_action.code_action = function()
local original_select = vim.ui.select local diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
vim.ui.select = sort_select local context = {diagnostics = diagnostics}
local params = vim.lsp.util.make_range_params()
vim.lsp.buf.code_action() params.context = context
vim.defer_fn(function() -- vim.lsp.buf_request(0, "textDocument/codeAction", params, code_action.code_action_handler)
vim.ui.select = original_select code_action_request(params)
end, 1000)
end end
code_action.range_code_action = function(startpos, endpos) code_action.range_code_action = function(startpos, endpos)
local context = {} local context = {}
context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
local params = util.make_given_range_params(startpos, endpos)
local bufnr = vim.api.nvim_get_current_buf()
startpos = startpos or api.nvim_buf_get_mark(bufnr, '<')
endpos = endpos or api.nvim_buf_get_mark(bufnr, '>')
log(startpos, endpos)
local params = vim.lsp.util.make_given_range_params(startpos, endpos)
params.context = context params.context = context
code_action_request(params)
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 end
code_action.code_action_prompt = function(bufnr) code_action.code_action_prompt = function()
if special_buffers[vim.bo.filetype] then if special_buffers[vim.bo.filetype] then
log('skip buffer', vim.bo.filetype) log('skip buffer', vim.bo.filetype)
return return

@ -3,111 +3,182 @@
-- https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/codelens.lua -- https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/codelens.lua
local codelens = require('vim.lsp.codelens') local codelens = require('vim.lsp.codelens')
local log = require('navigator.util').log local log = require"navigator.util".log
local trace = require('navigator.util').trace local mk_handler = require"navigator.util".mk_handler
-- trace = log local nvim_0_6 = require"navigator.util".nvim_0_6
local lsphelper = require('navigator.lspwrapper') local trace = require"navigator.util".trace
local lsphelper = require "navigator.lspwrapper"
local api = vim.api local api = vim.api
local gui = require "navigator.gui"
local M = {} local M = {}
local config = require('navigator').config_values() local config = require("navigator").config_values()
local sign_name = 'NavigatorCodeLensLightBulb' local sign_name = "NavigatorCodeLensLightBulb"
if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then 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' }) vim.fn.sign_define(sign_name,
{text = config.icons.code_lens_action_icon, texthl = "LspDiagnosticsSignHint"})
end end
local sign_group = 'nvcodelensaction' local sign_group = "nvcodelensaction"
local get_current_winid = require('navigator.util').get_current_winid local get_current_winid = require('navigator.util').get_current_winid
local is_enabled = true
local code_lens_action = {} local code_lens_action = {}
local function _update_sign(line) local function _update_sign(line)
trace('update sign at line ', line) trace("update sign at line ", line)
local winid = get_current_winid() local winid = get_current_winid()
if code_lens_action[winid] == nil then if code_lens_action[winid] == nil then
code_lens_action[winid] = {} code_lens_action[winid] = {}
end end
if code_lens_action[winid].lightbulb_line ~= 0 then if code_lens_action[winid].lightbulb_line ~= 0 then
vim.fn.sign_unplace(sign_group, { id = code_lens_action[winid].lightbulb_line, buffer = '%' }) vim.fn.sign_unplace(sign_group, {id = code_lens_action[winid].lightbulb_line, buffer = "%"})
end end
if line then if line then
-- log("updatasign", line, sign_group, sign_name) -- log("updatasign", line, sign_group, sign_name)
vim.fn.sign_place( vim.fn.sign_place(line, sign_group, sign_name, "%",
line, {lnum = line + 1, priority = config.lsp.code_lens_action.sign_priority})
sign_group,
sign_name,
'%',
{ lnum = line + 1, priority = config.lsp.code_lens_action.sign_priority }
)
code_lens_action[winid].lightbulb_line = line code_lens_action[winid].lightbulb_line = line
end end
end end
local codelens_hdlr = function(err, result, ctx, cfg) local codelens_hdlr = mk_handler(function(err, result, ctx, cfg)
trace(ctx, result)
M.codelens_ctx = ctx
if err or result == nil then if err or result == nil then
if err then if err then
log('lsp code lens', vim.inspect(err), ctx, cfg) log("lsp code lens", vim.inspect(err), ctx, cfg)
end end
return return
end end
trace('codelenes result', result) trace("codelenes result", result)
for _, v in pairs(result) do for _, v in pairs(result) do
_update_sign(v.range.start.line) _update_sign(v.range.start.line)
end end
end end)
function M.setup(bufnr) function M.setup()
log('setup for ****** ', bufnr) vim.cmd('highlight! link LspCodeLens LspDiagnosticsHint')
vim.api.nvim_set_hl(0, 'LspCodeLens', { link = 'DiagnosticsHint', default = true }) vim.cmd('highlight! link LspCodeLensText LspDiagnosticsInformation')
vim.api.nvim_set_hl(0, 'LspCodeLensText', { link = 'DiagnosticsInformation', default = true }) vim.cmd('highlight! link LspCodeLensTextSign LspDiagnosticsSignInformation')
vim.api.nvim_set_hl(0, 'LspCodeLensSign', { link = 'DiagnosticsInformation', default = true }) vim.cmd('highlight! link LspCodeLensTextSeparator Boolean')
vim.api.nvim_set_hl(0, 'LspCodeLensSeparator', { link = 'Boolean', default = true })
vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI', 'InsertLeave' }, { vim.cmd('augroup navigator.codelenses')
group = vim.api.nvim_create_augroup('nv__codelenses', {}), vim.cmd(' autocmd!')
buffer = bufnr or vim.api.nvim_win_get_buf(), vim.cmd(
callback = function() "autocmd BufEnter,CursorHold,InsertLeave <buffer> lua require('navigator.codelens').refresh()")
require('navigator.codelens').refresh() vim.cmd('augroup end')
end, local on_codelens = vim.lsp.handlers["textDocument/codeLens"]
}) vim.lsp.handlers["textDocument/codeLens"] = mk_handler(
function(err, result, ctx, cfg)
-- trace(err, result, ctx.client_id, ctx.bufnr, cfg or {})
cfg = cfg or {}
ctx = ctx or {bufnr = vim.api.nvim_get_current_buf()}
if nvim_0_6() then
on_codelens(err, result, ctx, cfg)
codelens_hdlr(err, result, ctx, cfg)
else
on_codelens(err, ctx.method, result, ctx.client_id, ctx.bufnr)
codelens_hdlr(err, _, result, ctx.client_id or 0, ctx.bufnr or 0)
end
end)
end end
M.lsp_clients = {} M.lsp_clients = {}
function M.refresh() function M.refresh()
if next(vim.lsp.buf_get_clients(0)) == nil then if #vim.lsp.buf_get_clients() < 1 then
log('Must have a client running to use lsp code action') log("Must have a client running to use lsp code action")
return return
end end
if not lsphelper.check_capabilities('codeLensProvider') then if not lsphelper.check_capabilities("code_lens") then
return return
end end
M.inline() vim.lsp.codelens.refresh()
end end
local virtual_types_ns = api.nvim_create_namespace('ng_virtual_types') function M.run_action()
log("run code len action")
function M.disable() assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp code action")
local bufnr = vim.api.nvim_get_current_buf() if not lsphelper.check_capabilities("code_lens") then
vim.api.nvim_buf_clear_namespace(bufnr, virtual_types_ns, 0, -1) return
is_enabled = false
end end
function M.run_action() local line = api.nvim_win_get_cursor(0)[1]
local original_select = vim.ui.select local bufnr = api.nvim_get_current_buf()
vim.ui.select = require('guihua.gui').select
log('codelens action') local lenses = codelens.get(bufnr)
log(lenses)
if lenses == nil or #lenses == 0 then
return
end
local width = 40
local data = {
" " .. _NgConfigValues.icons.code_lens_action_icon .. " CodeLens Action <C-o> Apply <C-e> Exit"
}
local idx = 1
for i, lens in pairs(lenses) do
if lens.range.start.line == (line - 1) then
local title = lens.command.title:gsub("\r\n", "\\r\\n")
title = title:gsub("\n", "\\n")
title = string.format("[%d] %s", idx, title)
table.insert(data, title)
lenses[i].display_title = title
width = math.max(width, #lens.command.title)
idx = idx + 1
end
end
local apply = require('navigator.lspwrapper').apply_action
local function apply_action(action)
local action_chosen = nil
for key, value in pairs(lenses) do
if value.display_title == action then
action_chosen = value
end
end
if action_chosen == nil then
log("no match for ", action, lenses)
return
end
apply(action_chosen)
end
codelens.run() local divider = string.rep('', width + 2)
vim.defer_fn(function()
vim.ui.select = original_select
end, 1000)
table.insert(data, 2, divider)
if #data > 2 then
local lv = gui.new_list_view {
items = data,
width = width + 4,
loc = "top_center",
relative = "cursor",
rawdata = true,
data = data,
on_confirm = function(pos)
log(pos)
apply_action(pos)
end,
on_move = function(pos)
log(pos)
return pos
end
}
vim.api.nvim_buf_add_highlight(lv.bufnr, -1, 'Title', 0, 0, -1)
else
print('no codelense in current line')
end
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 end
M.inline = function() M.inline = function()
@ -118,27 +189,49 @@ M.inline = function()
if vim.fn.getcmdwintype() == ':' then if vim.fn.getcmdwintype() == ':' then
return return
end end
if #vim.lsp.buf_get_clients() == 0 then
if next(vim.lsp.buf_get_clients(0)) == nil then
return return
end end
local bufnr = api.nvim_get_current_buf() local bufnr = api.nvim_get_current_buf()
local parameter = lsp.util.make_position_params() local parameter = lsp.util.make_position_params()
local response = lsp.buf_request_sync(bufnr, "textDocument/codeLens", parameter)
local on_codelens = vim.lsp.handlers['textDocument/codeLens']
lsp.buf_request(bufnr, 'textDocument/codeLens', parameter, function(err, response, ctx, _)
-- Clear previous highlighting -- Clear previous highlighting
api.nvim_buf_clear_namespace(bufnr, virtual_types_ns, 0, -1) api.nvim_buf_clear_namespace(bufnr, virtual_types_ns, 0, -1)
if response then if response then
trace(response) log(response)
for _, v in ipairs(response) do
on_codelens(err, response, ctx, _) if v == nil or v.result == nil then
return
codelens_hdlr (err, response, ctx, _) end -- no response
for _, vv in pairs(v.result) do
local start_line = -1
for _, vvv in pairs(vv.range) do
start_line = tonumber(vvv.line)
end
local cmd = vv.command
local msg = _NgConfigValues.icons.code_action_icon .. ' '
if cmd then
local txt = cmd.title or ''
txt = txt .. ' ' .. (cmd.command or '') .. ' '
msg = msg .. txt .. ' '
end
log(msg)
api.nvim_buf_set_extmark(bufnr, virtual_types_ns, start_line, -1, {
virt_text = {{msg, "LspCodeLensText"}},
virt_text_pos = 'overlay',
hl_mode = 'combine'
})
end end
end) end
-- else
-- api.nvim_command("echohl WarningMsg | echo 'VirtualTypes: No response' | echohl None")
end
end end
return M return M

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

@ -1,42 +1,35 @@
local util = require('navigator.util') local util = require "navigator.util"
local lsphelper = require('navigator.lspwrapper') local lsphelper = require "navigator.lspwrapper"
local locations_to_items = lsphelper.locations_to_items local locations_to_items = lsphelper.locations_to_items
local gui = require('navigator.gui') local gui = require "navigator.gui"
local log = util.log local log = util.log
local trace = util.trace local TextView = require("guihua.textview")
local TextView = require('guihua.textview')
-- callback for lsp definition, implementation and declaration handler -- callback for lsp definition, implementation and declaration handler
local definition_hdlr = function(err, locations, ctx, _) local definition_hdlr = util.mk_handler(function(err, locations, ctx, _)
-- log(locations) -- log(locations)
if err ~= nil then if err ~= nil then
vim.notify('Defination: ' .. tostring(err) .. vim.inspect(ctx), vim.lsp.log_levels.WARN) print(err, ctx)
return return
end end
if type(locations) == 'number' then if type(locations) == "number" then
log(locations) log(locations)
log('unable to handle request') log("unable to handle request")
end end
if locations == nil or vim.tbl_isempty(locations) then if locations == nil or vim.tbl_isempty(locations) then
vim.notify('Definition not found') print "Definition not found"
return return
end 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 vim.tbl_islist(locations) then
if #locations > 1 then if #locations > 1 then
local items = locations_to_items(locations) local items = locations_to_items(locations)
gui.new_list_view({items = items, api = 'Definition'}) gui.new_list_view({items = items, api = 'Definition'})
else else
vim.lsp.util.jump_to_location(locations[1], oe) vim.lsp.util.jump_to_location(locations[1])
end end
else else
vim.lsp.util.jump_to_location(locations, oe) vim.lsp.util.jump_to_location(locations)
end
end end
end)
local function get_symbol() local function get_symbol()
local currentWord = vim.fn.expand('<cword>') local currentWord = vim.fn.expand('<cword>')
@ -44,13 +37,13 @@ local function get_symbol()
end end
local function def_preview(timeout_ms) local function def_preview(timeout_ms)
assert(next(vim.lsp.buf_get_clients(0)), 'Must have a client running') assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running")
local method = 'textDocument/definition' local method = "textDocument/definition"
local params = vim.lsp.util.make_position_params() local params = vim.lsp.util.make_position_params()
local result = vim.lsp.buf_request_sync(0, method, params, timeout_ms or 1000) local result = vim.lsp.buf_request_sync(0, method, params, timeout_ms or 1000)
if result == nil or vim.tbl_isempty(result) then if result == nil or vim.tbl_isempty(result) then
vim.notify('No result found: ' .. method, vim.lsp.log_levels.WARN) print("No result found: " .. method)
return nil return nil
end end
@ -65,7 +58,7 @@ local function def_preview(timeout_ms)
end end
if vim.tbl_isempty(data) then if vim.tbl_isempty(data) then
vim.notify('No result found: ' .. method, vim.lsp.log_levels.WARN) print("No result found: " .. method)
return nil return nil
end end
@ -74,7 +67,7 @@ local function def_preview(timeout_ms)
local row = range.start.line local row = range.start.line
-- in case there are comments -- in case there are comments
row = math.max(row - 3, 1) row = math.max(row - 3, 1)
local delta = range.start.line - row + 3 local delta = range.start.line - row + 1
local uri = data[1].uri or data[1].targetUri local uri = data[1].uri or data[1].targetUri
if not uri then if not uri then
return return
@ -84,29 +77,21 @@ local function def_preview(timeout_ms)
vim.fn.bufload(bufnr) vim.fn.bufload(bufnr)
end end
local ok, parsers = pcall(require, 'nvim-treesitter.parsers') local ok, parsers = pcall(require, "nvim-treesitter.parsers")
local lines_num = 12
-- 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 if ok then
local ts = require('navigator.treesitter') local ts = require 'navigator.treesitter'
local root = parsers.get_parser(bufnr) local root = parsers.get_parser(bufnr)
log(range) log(range)
if ts == nil then
return
end
local def_node = ts.get_node_at_pos({range['start'].line, range['start'].character}, root) local def_node = ts.get_node_at_pos({range['start'].line, range['start'].character}, root)
local sr, _, er, _ = ts.get_node_scope(def_node) local sr, _, er, _ = ts.get_node_scope(def_node)
log(sr, er) log(sr, er)
lines_num = math.max(lines_num, er - sr + 5) -- comments etc lines_num = math.max(lines_num, er - sr + 1)
end end
-- TODO: 32 should be an option -- TODO: 12 should be an option
local definition = vim.api.nvim_buf_get_lines(bufnr, row, range['end'].line + lines_num, false) 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) 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 for _ = 1, math.min(3, #definition), 1 do
if #definition[1] < 2 then if #definition[1] < 2 then
@ -117,37 +102,35 @@ local function def_preview(timeout_ms)
end end
end end
local width = 40 local width = 40
for key, value in pairs(definition) do
local maxwidth = math.floor( vim.api.nvim_get_option('columns') * 0.8)
for _, value in pairs(definition) do
-- log(key, value, width) -- log(key, value, width)
width = math.max(width, #value + 4) width = math.max(width, #value + 4)
width = math.min(maxwidth, width) width = math.min(120, width)
end end
definition = vim.list_extend({ '  [' .. get_symbol() .. '] Definition: ' }, definition) definition = vim.list_extend({"  [" .. get_symbol() .. "] Definition: "}, definition)
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
-- TODO multiple resuts? -- TODO multiple resuts?
local opts = { local opts = {
relative = 'cursor', relative = "cursor",
style = 'minimal', style = "minimal",
ft = filetype, ft = filetype,
rect = { width = width, height = math.min(#definition + 3, 16), pos_y = 2 }, -- TODO: 16 hardcoded rect = {width = width, height = #definition + 2},
data = definition, data = definition,
enter = true, enter = true,
border = _NgConfigValues.border or 'shadow', border = _NgConfigValues.border or "shadow"
} }
TextView:new(opts) TextView:new(opts)
delta = delta + 1 -- header delta = delta + 1 -- header
local cmd = 'normal! ' .. tostring(delta) .. 'G' local cmd = "normal! " .. tostring(delta) .. "G"
vim.cmd(cmd) vim.cmd(cmd)
vim.cmd('set cursorline') vim.cmd('set cursorline')
if #def_line > 0 then if #def_line > 0 then
local niddle = require('guihua.util').add_escape(def_line[1]) local niddle = require('guihua.util').add_escape(def_line[1])
-- log(def_line[1], niddle) -- log(def_line[1], niddle)
vim.fn.matchadd('Search', niddle) vim.fn.matchadd("Search", niddle)
end end
-- TODO: -- TODO:
-- https://github.com/oblitum/goyo.vim/blob/master/autoload/goyo.vim#L108-L135 -- https://github.com/oblitum/goyo.vim/blob/master/autoload/goyo.vim#L108-L135
@ -157,19 +140,19 @@ local def = function()
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local ref_params = vim.lsp.util.make_position_params() local ref_params = vim.lsp.util.make_position_params()
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr)
-- if client.resolved_capabilities.goto_definition then if client.resolved_capabilities.goto_definition then
if client.server_capabilities.definitionProvider then client.request("textDocument/definition", ref_params, definition_hdlr, _bufnr)
client.request('textDocument/definition', ref_params, definition_hdlr, _bufnr)
end end
end) end)
end end
vim.lsp.handlers['textDocument/definition'] = definition_hdlr vim.lsp.handlers["textDocument/definition"] = definition_hdlr
return { return {
definition = def, definition = def,
definition_handler = definition_hdlr, definition_handler = definition_hdlr,
definition_preview = def_preview, definition_preview = def_preview,
declaration_handler = definition_hdlr, declaration_handler = definition_hdlr,
typeDefinition_handler = definition_hdlr, typeDefinition_handler = definition_hdlr
} }

@ -1,22 +1,27 @@
local gui = require('navigator.gui') local gui = require "navigator.gui"
local diagnostic_list = {} local diagnostic_list = {}
local diagnostic = vim.diagnostic or vim.lsp.diagnostic local diagnostic = vim.diagnostic or vim.lsp.diagnostic
-- local hide = diagnostic.hide or diagnostic.clear -- local hide = diagnostic.hide or diagnostic.clear
local util = require('navigator.util') _NG_VT_DIAG_NS = vim.api.nvim_create_namespace("navigator_lua_diag")
local util = require "navigator.util"
local log = util.log local log = util.log
local trace = require('guihua.log').trace local trace = require"guihua.log".trace
-- trace = log -- trace = log
local error = util.error local error = util.error
local path_sep = require('navigator.util').path_sep() local path_sep = require"navigator.util".path_sep()
local path_cur = require('navigator.util').path_cur() local mk_handler = require"navigator.util".mk_handler
local empty = util.empty local path_cur = require"navigator.util".path_cur()
local api = vim.api diagnostic_list[vim.bo.filetype] = {}
_NG_VT_DIAG_NS = api.nvim_create_namespace('navigator_lua_diag')
if not util.nvim_0_6_1() then local function clear_diag_VT(bufnr) -- important for clearing out when no more errors
util.warn('Navigator 0.4+ only support nvim-0.6+, please use Navigator 0.3.x or a newer version of neovim') log(bufnr, _NG_VT_DIAG_NS)
if bufnr == nil or _NG_VT_DIAG_NS == nil then
return
end
vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
_NG_VT_DIAG_NS = nil
end end
diagnostic_list[vim.bo.filetype] = {}
local diag_map = {} local diag_map = {}
if vim.diagnostic then if vim.diagnostic then
@ -24,12 +29,10 @@ if vim.diagnostic then
Error = vim.diagnostic.severity.ERROR, Error = vim.diagnostic.severity.ERROR,
Warning = vim.diagnostic.severity.WARN, Warning = vim.diagnostic.severity.WARN,
Info = vim.diagnostic.severity.Info, Info = vim.diagnostic.severity.Info,
Hint = vim.diagnostic.severity.Hint, Hint = vim.diagnostic.severity.Hint
} }
end end
local diagnostic_cfg
local function get_count(bufnr, level) local function get_count(bufnr, level)
if vim.diagnostic ~= nil then if vim.diagnostic ~= nil then
return #diagnostic.get(bufnr, {severity = diag_map[level]}) return #diagnostic.get(bufnr, {severity = diag_map[level]})
@ -39,18 +42,13 @@ local function get_count(bufnr, level)
end end
local function error_marker(result, ctx, config) 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() vim.defer_fn(function()
if vim.tbl_isempty(result.diagnostics) then if vim.tbl_isempty(result.diagnostics) then
return return
end end
if _NgConfigValues.lsp.diagnostic_scrollbar_sign == nil then -- not enabled or already shown
return
end
local first_line = vim.fn.line('w0') local first_line = vim.fn.line('w0')
-- local rootfolder = vim.fn.expand('%:h:t') -- get the current file root folder -- local rootfolder = vim.fn.expand('%:h:t') -- get the current file root folder
@ -58,19 +56,17 @@ local function error_marker(result, ctx, config)
if bufnr == nil then if bufnr == nil then
bufnr = vim.uri_to_bufnr(result.uri) bufnr = vim.uri_to_bufnr(result.uri)
end end
local success, fname = pcall(api.nvim_buf_get_name, bufnr) local fname = vim.api.nvim_buf_get_name(bufnr)
if not success then
return
end
local uri = vim.uri_from_fname(fname) local uri = vim.uri_from_fname(fname)
if uri ~= result.uri then if uri ~= result.uri then
log('not same buf', ctx, result.uri, bufnr, vim.fn.bufnr()) log("not same buf", ctx, result.uri, bufnr, vim.fn.bufnr())
return return
end end
if not api.nvim_buf_is_loaded(bufnr) then if not vim.api.nvim_buf_is_loaded(bufnr) then
trace('buf not loaded', bufnr) log("buf not loaded", bufnr)
return return
end end
trace('schedule callback', result, ctx, config) trace('schedule callback', result, ctx, config)
@ -79,8 +75,8 @@ local function error_marker(result, ctx, config)
if result == nil or result.diagnostics == nil or #result.diagnostics == 0 then if result == nil or result.diagnostics == nil or #result.diagnostics == 0 then
local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]])
if diag_cnt == 0 and _NG_VT_DIAG_NS ~= nil then if diag_cnt == 0 and _NG_VT_DIAG_NS ~= nil then
log('great no errors') log("great no errors")
api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
end end
return return
end end
@ -88,20 +84,20 @@ local function error_marker(result, ctx, config)
-- total line num of current buffer -- total line num of current buffer
-- local winid = vim.fn.win_getid(vim.fn.winnr()) -- local winid = vim.fn.win_getid(vim.fn.winnr())
-- local winid = api.nvim_get_current_win() -- local winid = vim.api.nvim_get_current_win()
local total_num = api.nvim_buf_line_count(bufnr) local total_num = vim.api.nvim_buf_line_count(bufnr)
-- local total_num = vim.fn.getbufinfo(vim.fn.winbufnr(winid))[1].linecount -- local total_num = vim.fn.getbufinfo(vim.fn.winbufnr(winid))[1].linecount
-- window size of current buffer -- window size of current buffer
local stats = api.nvim_list_uis()[1] local stats = vim.api.nvim_list_uis()[1]
-- local wwidth = stats.width; -- local wwidth = stats.width;
local wheight = stats.height local wheight = stats.height;
if total_num <= wheight then if total_num <= wheight then
return return
end end
if _NG_VT_DIAG_NS == nil then if _NG_VT_DIAG_NS == nil then
_NG_VT_DIAG_NS = api.nvim_create_namespace('navigator_lua_diag') _NG_VT_DIAG_NS = vim.api.nvim_create_namespace("navigator_lua_diag")
end end
local pos = {} local pos = {}
@ -132,65 +128,62 @@ local function error_marker(result, ctx, config)
end end
pos[#pos] = {line = p, sign = bar, severity = math.min(diag.severity, pos[#pos].severity)} pos[#pos] = {line = p, sign = bar, severity = math.min(diag.severity, pos[#pos].severity)}
else else
table.insert(pos, { table.insert(pos,
{
line = p, line = p,
sign = _NgConfigValues.lsp.diagnostic_scrollbar_sign[1], sign = _NgConfigValues.lsp.diagnostic_scrollbar_sign[1],
severity = diag.severity, severity = diag.severity
}) })
end end
end end
trace('pos, line:', p, diag.severity, diag.range) trace("pos, line:", p, diag.severity, diag.range)
end end
if not vim.tbl_isempty(pos) then if not vim.tbl_isempty(pos) then
api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
end end
for _, s in pairs(pos) do for i, s in pairs(pos) do
local hl = 'ErrorMsg' local hl = 'ErrorMsg'
if type(s.severity) == 'number' then if type(s.severity) == "number" then
if s.severity == 2 then if s.severity == 2 then
hl = 'WarningMsg' hl = 'WarningMsg'
elseif s.severity >= 3 then elseif s.severity >= 3 then
hl = 'DiagnosticInfo' hl = 'DiagnosticInfo'
end end
elseif type(s.severity) == 'string' then elseif type(s.severity) == "string" then
if s.severity:lower() == 'warn' then if s.severity:lower() == "warn" then
hl = 'WarningMsg' hl = "WarningMsg"
end end
end end
local l = s.line + first_line local l = s.line + first_line
if l > total_num then if l > total_num then
l = total_num l = total_num
end end
trace('add pos', s, bufnr) trace("add pos", s, bufnr)
api.nvim_buf_set_extmark( vim.api.nvim_buf_set_extmark(bufnr, _NG_VT_DIAG_NS, l, -1,
bufnr, {virt_text = {{s.sign, hl}}, virt_text_pos = 'right_align'})
_NG_VT_DIAG_NS,
l,
-1,
{ virt_text = { { s.sign, hl } }, virt_text_pos = 'right_align' }
)
end end
end, 10) -- defer in 10ms end, 10) -- defer in 10ms
end end
local update_err_marker_async = function() local update_err_marker_async = function()
local debounce = require('navigator.debounce').debounce_trailing local debounce = require'navigator.debounce'.debounce_trailing
return debounce(400, error_marker) return debounce(20, error_marker)
end end
local diag_hdlr = function(err, result, ctx, config) local diag_hdlr = mk_handler(function(err, result, ctx, config)
require('navigator.lspclient.highlight').diagnositc_config_sign()
config = config or diagnostic_cfg require"navigator.lspclient.highlight".diagnositc_config_sign()
if err ~= nil then if err ~= nil then
log(err, config, result) log(err, config, result)
return return
end end
local mode = api.nvim_get_mode().mode local mode = vim.api.nvim_get_mode().mode
if mode ~= 'n' and config.update_in_insert == false then if mode ~= 'n' and config.update_in_insert == false then
trace('skip sign update in insert mode') log("skip sign update in insert mode")
end end
local cwd = vim.loop.cwd() local cwd = vim.loop.cwd()
local ft = vim.bo.filetype local ft = vim.bo.filetype
@ -199,21 +192,18 @@ local diag_hdlr = function(err, result, ctx, config)
end end
local client_id = ctx.client_id local client_id = ctx.client_id
local bufnr = ctx.bufnr or 0
if result.diagnostics ~= nil and result.diagnostics ~= {} then if result.diagnostics ~= nil and result.diagnostics ~= {} then
trace('diagnostic', result.diagnostics, ctx, config) trace('diagnostic', result.diagnostics, ctx, config)
end end
if util.nvim_0_6() then
trace(err, result, ctx, config) trace(err, result, ctx, config)
vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config) vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config)
local uri = result.uri else
log("old version of lsp nvim 050")
local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) vim.lsp.diagnostic.on_publish_diagnostics(err, _, result, ctx.client_id, _, config)
if empty(result.diagnostics) and diag_cnt > 0 then
trace('no result? ', diag_cnt)
return
end end
local uri = result.uri
-- trace("diag: ", mode, result, ctx, config) -- trace("diag: ", mode, result, ctx, config)
if result and result.diagnostics then if result and result.diagnostics then
local item_list = {} local item_list = {}
@ -224,9 +214,7 @@ local diag_hdlr = function(err, result, ctx, config)
item.lnum = v.range.start.line + 1 item.lnum = v.range.start.line + 1
item.col = v.range.start.character + 1 item.col = v.range.start.character + 1
item.uri = uri item.uri = uri
-- trace(item)
local head = _NgConfigValues.icons.diagnostic_head local head = _NgConfigValues.icons.diagnostic_head
if v.severity then
if v.severity == 1 then if v.severity == 1 then
head = _NgConfigValues.icons.diagnostic_head_severity_1 head = _NgConfigValues.icons.diagnostic_head_severity_1
end end
@ -236,54 +224,28 @@ local diag_hdlr = function(err, result, ctx, config)
if v.severity > 2 then if v.severity > 2 then
head = _NgConfigValues.icons.diagnostic_head_severity_3 head = _NgConfigValues.icons.diagnostic_head_severity_3
end end
else
v.severity = 2
end
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
local bufnr1 = vim.uri_to_bufnr(uri) local bufnr1 = vim.uri_to_bufnr(uri)
local loaded = api.nvim_buf_is_loaded(bufnr1) if not vim.api.nvim_buf_is_loaded(bufnr1) then
if _NgConfigValues.diagnostic_load_files then if _NgConfigValues.diagnostic_load_files then
-- print('load buffers') -- print('load buffers')
if not loaded then
vim.fn.bufload(bufnr1) -- this may slow down the neovim vim.fn.bufload(bufnr1) -- this may slow down the neovim
end
local pos = v.range.start local pos = v.range.start
local row = pos.line local row = pos.line
local line = (api.nvim_buf_get_lines(bufnr1, row, row + 1, false) or { '' })[1] local line = (vim.api.nvim_buf_get_lines(bufnr1, row, row + 1, false) or {""})[1]
if line ~= nil then if line ~= nil then
item.text = head .. line .. _NgConfigValues.icons.diagnostic_head_description .. v.message item.text = head .. line .. _NgConfigValues.icons.diagnostic_head_description .. v.message
table.insert(item_list, item)
else else
error('diagnostic result empty line', v, row, bufnr1) error("diagnostic result empty line", v, row, bufnr1)
end end
else else
item.text = head .. _NgConfigValues.icons.diagnostic_head_description .. v.message item.text = head .. _NgConfigValues.icons.diagnostic_head_description .. v.message
table.insert(item_list, item)
end end
if v.releated_msg then
item.text = item.text .. '; ' .. item.releated_msg
end end
if v.releated_lnum then
item.text = item.text .. ':' .. tostring(item.releated_lnum)
end
table.insert(item_list, item)
end end
-- local old_items = vim.fn.getqflist() -- local old_items = vim.fn.getqflist()
if diagnostic_list[ft][uri] == nil then diagnostic_list[ft][uri] = item_list
diagnostic_list[ft][uri] = {}
end
diagnostic_list[ft][uri][tostring(client_id)] = item_list
trace(uri, ft, diagnostic_list)
if not result.uri then if not result.uri then
result.uri = uri result.uri = uri
end end
@ -291,74 +253,37 @@ local diag_hdlr = function(err, result, ctx, config)
local marker = update_err_marker_async() local marker = update_err_marker_async()
marker(result, ctx, config) marker(result, ctx, config)
else else
trace('great, no diag errors') trace("great, no diag errors")
api.nvim_buf_clear_namespace(0, _NG_VT_DIAG_NS, 0, -1) vim.api.nvim_buf_clear_namespace(0, _NG_VT_DIAG_NS, 0, -1)
_NG_VT_DIAG_NS = nil _NG_VT_DIAG_NS = nil
end end
end
-- local diag_hdlr_async = function() end)
-- local debounce = require('navigator.debounce').debounce_trailing
-- return debounce(100, diag_hdlr)
-- end
local M = {} local M = {}
function M.setup() local diagnostic_cfg = {
if diagnostic_cfg ~= nil and diagnostic_cfg.float ~= nil then
return
end
diagnostic_cfg = {
-- Enable underline, use default values -- Enable underline, use default values
underline = _NgConfigValues.lsp.diagnostic.underline, underline = true,
-- Enable virtual -- Enable virtual text, override spacing to 3 (prevent overlap)
virtual_text = {spacing = 3, prefix = _NgConfigValues.icons.diagnostic_virtual_text},
-- Use a function to dynamically turn signs off -- Use a function to dynamically turn signs off
-- and on, using buffer local variables -- and on, using buffer local variables
signs = true, signs = true,
update_in_insert = _NgConfigValues.lsp.diagnostic.update_in_insert or false, update_in_insert = _NgConfigValues.lsp.diagnostic_update_in_insert or false,
severity_sort = _NgConfigValues.lsp.diagnostic.severity_sort, severity_sort = {reverse = true}
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 if _NgConfigValues.lsp.diagnostic_virtual_text == false then
api.nvim_create_autocmd({ 'WinScrolled' }, { diagnostic_cfg.virtual_text = false
group = api.nvim_create_augroup('NGWinScrolledGroup', {}),
pattern = '*',
callback = function()
require('navigator.diagnostics').update_err_marker()
end,
})
end
end end
local function clear_diag_VT(bufnr) -- important for clearing out when no more errors -- vim.lsp.handlers["textDocument/publishDiagnostics"]
bufnr = bufnr or api.nvim_get_current_buf() M.diagnostic_handler = vim.lsp.with(diag_hdlr, diagnostic_cfg)
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() M.hide_diagnostic = function()
if _NG_VT_DIAG_NS then if _NG_VT_DIAG_NS then
clear_diag_VT() vim.api.nvim_buf_clear_namespace(0, _NG_VT_DIAG_NS, 0, -1)
_NG_VT_DIAG_NS = nil
end end
end end
@ -373,89 +298,83 @@ end
M.show_buf_diagnostics = function() M.show_buf_diagnostics = function()
if diagnostic_list[vim.bo.filetype] ~= nil then 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 results = diagnostic_list[vim.bo.filetype]
local display_items = {} local display_items = {}
for _, client_items in pairs(results) do for _, items in pairs(results) do
for _, items in pairs(client_items) do
for _, it in pairs(items) do for _, it in pairs(items) do
table.insert(display_items, it) table.insert(display_items, it)
end end
end end
end
-- log(display_items) -- log(display_items)
if #display_items > 0 then if #display_items > 0 then
local listview = gui.new_list_view({ local listview = gui.new_list_view({
items = display_items, items = display_items,
api = _NgConfigValues.icons.diagnostic_file .. _NgConfigValues.icons.diagnostic_head .. ' Diagnostic ', api = _NgConfigValues.icons.diagnostic_file .. _NgConfigValues.icons.diagnostic_head .. " Diagnostic ",
enable_preview_edit = true, enable_preview_edit = true
}) })
if listview == nil then trace("new buffer", listview.bufnr)
return log('nil listview') vim.api.nvim_buf_add_highlight(listview.bufnr, -1, 'Title', 0, 0, -1)
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 end
end end
-- set loc list win -- set loc list win
M.set_diag_loclist = function(bufnr) M.set_diag_loclist = function()
bufnr = bufnr or api.nvim_get_current_buf()
local bufnr = vim.api.nvim_get_current_buf()
local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]])
if diag_cnt == 0 then if diag_cnt == 0 then
log('great, no errors!') log("great, no errors!")
return return
end end
local clients = vim.lsp.buf_get_clients(0)
local clients = vim.lsp.buf_get_clients(bufnr)
local cfg = {open = diag_cnt > 0} local cfg = {open = diag_cnt > 0}
for _, client in pairs(clients) do for _, client in pairs(clients) do
cfg.client_id = client['id'] cfg.client_id = client['id']
break break
end end
if not vim.tbl_isempty(vim.lsp.buf_get_clients(bufnr)) then if not vim.tbl_isempty(vim.lsp.buf_get_clients(0)) then
local err_cnt = get_count(0, [[Error]]) local err_cnt = get_count(0, [[Error]])
if err_cnt > 0 and _NgConfigValues.lsp.disply_diagnostic_qf then if err_cnt > 0 and _NgConfigValues.lsp.disply_diagnostic_qf then
if diagnostic.set_loclist then if diagnostic.set_loclist then
diagnostic.set_loclist(cfg) diagnostic.set_loclist(cfg)
else else
cfg.namespaces = diagnostic.get_namespaces() cfg.namespaces = diagnostic.get_namespace(nil)
diagnostic.setloclist(cfg) diagnostic.setloclist(cfg)
end end
else else
vim.cmd('lclose') vim.cmd("lclose")
end end
end end
end end
-- TODO: callback when scroll -- TODO: callback when scroll
function M.update_err_marker() function M.update_err_marker()
trace('update err marker', _NG_VT_DIAG_NS) trace("update err marker", _NG_VT_DIAG_NS)
if _NG_VT_DIAG_NS == nil then if _NG_VT_DIAG_NS == nil then
-- nothing to update -- nothing to update
return return
end end
local bufnr = api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local diag_cnt = get_count(bufnr, [[Error]]) local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) + get_count(bufnr, [[Info]])
+ get_count(bufnr, [[Warning]])
+ get_count(bufnr, [[Info]])
+ get_count(bufnr, [[Hint]]) + get_count(bufnr, [[Hint]])
-- redraw -- redraw
if diag_cnt == 0 and _NG_VT_DIAG_NS ~= nil then 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') vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
trace("no errors")
return return
end end
api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
local errors = diagnostic.get(bufnr) local errors = diagnostic.get(bufnr)
if #errors == 0 then if #errors == 0 then
trace('no errors', errors) trace("no errors", errors)
return return
end end
local uri = vim.uri_from_bufnr(bufnr) local uri = vim.uri_from_bufnr(bufnr)
@ -466,92 +385,35 @@ function M.update_err_marker()
marker(result, {bufnr = bufnr, method = 'textDocument/publishDiagnostics'}) marker(result, {bufnr = bufnr, method = 'textDocument/publishDiagnostics'})
end end
function M.get_line_diagnostic() -- TODO: update the marker
local lnum = api.nvim_win_get_cursor(0)[1] - 1 if _NgConfigValues.diagnostic_scrollbar_sign then
local diags = diagnostic.get(api.nvim_get_current_buf(), { lnum = lnum }) print("config deprecated, set lsp.diagnostic_scrollbar_sign instead")
table.sort(diags, function(diag1, diag2)
return diag1.severity < diag2.severity
end)
return diags
end end
function M.show_diagnostics(pos) if _NgConfigValues.lsp.diagnostic_scrollbar_sign then
local bufnr = api.nvim_get_current_buf() vim.cmd [[autocmd WinScrolled * lua require'navigator.diagnostics'.update_err_marker()]]
end
local lnum, col = unpack(api.nvim_win_get_cursor(0)) function M.get_line_diagnostic()
lnum = lnum - 1 local lnum = vim.api.nvim_win_get_cursor(0)[1] - 1
local opt = { border = 'single', severity_sort = true } return diagnostic.get(vim.api.nvim_get_current_buf(), {lnum = lnum})
end
if pos ~= nil and type(pos) == 'number' then function M.show_diagnostics(pos)
opt.scope = 'buffer' local bufnr = vim.api.nvim_get_current_buf()
else local lnum = vim.api.nvim_win_get_cursor(0)[1] - 1
local opt = {border = 'single'}
if diagnostic.open_float and type(diagnostic.open_float) == "function" then
if pos == true then if pos == true then
opt.scope = 'cursor' opt.scope = "cursor"
else else
opt.scope = 'line' opt.scope = "line"
end
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) 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 else
return {} -- deprecated
end diagnostic.show_line_diagnostics(opt, bufnr, lnum)
end,
})
p:open(true)
end 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 end
return M return M

@ -1,15 +1,17 @@
local util = require('navigator.util') local util = require "navigator.util"
local log = util.log local log = util.log
local trace = util.trace local trace = util.trace
local mk_handler = util.mk_handler
local api = vim.api local api = vim.api
local references = {} local references = {}
_NG_hi_list = {} _NG_hi_list = {}
_NG_current_symbol = '' _NG_current_symbol = ""
_NG_ref_hi_idx = 1 _NG_ref_hi_idx = 1
-- extract symbol from cursor -- extract symbol from cursor
local function get_symbol() local function get_symbol()
return vim.fn.expand('<cword>') local currentWord = vim.fn.expand('<cword>')
return currentWord
end end
local function add_locs(bufnr, result) local function add_locs(bufnr, result)
@ -17,16 +19,16 @@ local function add_locs(bufnr, result)
if #result < 1 then if #result < 1 then
return return
end 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 if _NG_hi_list[symbol] == nil then
_NG_hi_list[symbol] = {range = {}} _NG_hi_list[symbol] = {range = {}}
end end
if _NG_hi_list[symbol] ~= nil then if _NG_hi_list[symbol] ~= nil then
trace('already added', symbol) trace("already added", symbol)
_NG_hi_list[symbol].range = {} _NG_hi_list[symbol].range = {}
-- vim.fn.matchdelete(hid) -- vim.fn.matchdelete(hid)
end end
trace('add ', symbol) trace("add ", symbol)
_NG_hi_list[symbol].range = result _NG_hi_list[symbol].range = result
_NG_current_symbol = symbol _NG_current_symbol = symbol
end end
@ -35,7 +37,7 @@ local function nohl()
for key, value in pairs(_NG_hi_list) do for key, value in pairs(_NG_hi_list) do
if value.hi_ids ~= nil then if value.hi_ids ~= nil then
for _, v in ipairs(value.hi_ids) do for _, v in ipairs(value.hi_ids) do
trace('delete', v) trace("delete", v)
vim.fn.matchdelete(v) vim.fn.matchdelete(v)
end end
_NG_hi_list[key].hi_ids = nil _NG_hi_list[key].hi_ids = nil
@ -50,8 +52,8 @@ local function hi_symbol()
vim.lsp.buf.document_highlight() vim.lsp.buf.document_highlight()
symbol = _NG_current_symbol symbol = _NG_current_symbol
end end
if symbol == nil or symbol == '' then if symbol == nil or symbol == "" then
log('nil symbol') log("nil symbol")
return return
end end
@ -63,7 +65,7 @@ local function hi_symbol()
local range = _NG_hi_list[symbol].range or {} local range = _NG_hi_list[symbol].range or {}
if _NG_hi_list[symbol].hi_ids ~= nil then if _NG_hi_list[symbol].hi_ids ~= nil then
for _, value in ipairs(_NG_hi_list[symbol].hi_ids) do for _, value in ipairs(_NG_hi_list[symbol].hi_ids) do
log('delete', value) log("delete", value)
vim.fn.matchdelete(value) vim.fn.matchdelete(value)
end end
_NG_hi_list[symbol].hi_ids = nil _NG_hi_list[symbol].hi_ids = nil
@ -72,23 +74,23 @@ local function hi_symbol()
local cur_pos = vim.fn.getpos('.') local cur_pos = vim.fn.getpos('.')
_NG_hi_list[symbol].hi_ids = {} _NG_hi_list[symbol].hi_ids = {}
local totalref = #range 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 total_match = 0
local match_result = vim.api.nvim_exec(cmd, true) local match_result = vim.api.nvim_exec(cmd, true)
local p = match_result:find(' match') local p = match_result:find(" match")
vim.cmd('nohl') vim.cmd("nohl")
vim.fn.setpos('.', cur_pos) vim.fn.setpos('.', cur_pos)
if p ~= nil then if p ~= nil then
p = match_result:sub(1, p) p = match_result:sub(1, p)
total_match = tonumber(p) total_match = tonumber(p)
end end
if total_match == totalref then -- same number as matchpos 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 k = range[1].kind
local hi_name = string.format('NGHiReference_%i_%i', _NG_ref_hi_idx, k) local hi_name = string.format("NGHiReference_%i_%i", _NG_ref_hi_idx, k)
local m = string.format('\\<%s\\>', symbol_wd) local m = string.format("\\<%s\\>", symbol_wd)
local r = vim.fn.matchadd(hi_name, m, 20) 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) table.insert(_NG_hi_list[symbol].hi_ids, r)
-- --
-- vim.fn.matchdelete(r) -- vim.fn.matchdelete(r)
@ -104,7 +106,7 @@ local function hi_symbol()
l = el l = el
end end
local w = value.range['end'].character - value.range.start.character local w = value.range['end'].character - value.range.start.character
local hi_name = string.format('NGHiReference_%i_%i', _NG_ref_hi_idx, k) local hi_name = string.format("NGHiReference_%i_%i", _NG_ref_hi_idx, k)
trace(hi_name, {l, cs, w}) trace(hi_name, {l, cs, w})
local m = vim.fn.matchaddpos(hi_name, {{l, cs, w}}, 10) local m = vim.fn.matchaddpos(hi_name, {{l, cs, w}}, 10)
table.insert(_NG_hi_list[symbol].hi_ids, m) table.insert(_NG_hi_list[symbol].hi_ids, m)
@ -123,9 +125,6 @@ end
-- returns r1 < r2 based on start of range -- returns r1 < r2 based on start of range
local function before(r1, r2) local function before(r1, r2)
if not r1 or not r2 then
return false
end
if r1.start.line < r2.start.line then if r1.start.line < r2.start.line then
return true return true
end end
@ -138,13 +137,14 @@ local function before(r1, r2)
return false return false
end end
local handle_document_highlight = function(_, result, ctx) local handle_document_highlight = mk_handler(function(_, result, ctx)
trace(result, ctx) trace(result)
if not ctx.bufnr then if not ctx.bufnr then
log('ducment highlight error', result, ctx) log("ducment highlight error", result, ctx)
return return
end end
if type(result) ~= 'table' or vim.fn.empty(result) == 1 then if type(result) ~= "table" then
log("clear up")
vim.lsp.util.buf_clear_references(ctx.bufnr) vim.lsp.util.buf_clear_references(ctx.bufnr)
return return
end end
@ -153,13 +153,12 @@ local handle_document_highlight = function(_, result, ctx)
return before(a.range, b.range) return before(a.range, b.range)
end) end)
references[ctx.bufnr] = result references[ctx.bufnr] = result
local client_id = ctx.client_id vim.lsp.util.buf_highlight_references(ctx.bufnr, result)
vim.lsp.util.buf_highlight_references(ctx.bufnr, result, util.encoding(client_id)) end)
end
-- modify from vim-illuminate -- modify from vim-illuminate
local function goto_adjent_reference(opt) local function goto_adjent_reference(opt)
trace(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 bufnr = vim.api.nvim_get_current_buf()
local refs = references[bufnr] local refs = references[bufnr]
@ -204,66 +203,54 @@ local function cmd_nohl()
end end
end end
local nav_doc_hl = function(bufnr) _G.nav_doc_hl = function()
trace('nav_doc_hl', bufnr) local bufnr = vim.api.nvim_get_current_buf()
bufnr = bufnr or vim.api.nvim_get_current_buf()
local ref_params = vim.lsp.util.make_position_params() local ref_params = vim.lsp.util.make_position_params()
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _) vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, bufnr)
if client.server_capabilities.documentHighlightProvider == true then if client.resolved_capabilities.document_highlight then
trace('sending doc highlight', client.name, bufnr) client.request("textDocument/documentHighlight", ref_params, handle_document_highlight, bufnr)
client.request('textDocument/documentHighlight', ref_params, handle_document_highlight, bufnr)
end end
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)
end,
})
end end
vim.lsp.handlers['textDocument/documentHighlight'] = function(err, result, ctx) local function documentHighlight()
local buffer = ctx.bufnr or api.nvim_get_current_buf() 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 nav_doc_hl()
autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
augroup END
]], false)
vim.lsp.handlers["textDocument/documentHighlight"] = mk_handler(function(err, result, ctx)
local bufnr = ctx.bufnr
if err then if err then
vim.notify(err, vim.lsp.log_levels.ERROR) print(err)
return return
end end
if not result or not result[1] or not result[1]['range'] then if not result then
return return
end end
trace('dochl', result) trace("dochl", result)
if type(result) ~= 'table' then bufnr = api.nvim_get_current_buf()
vim.lsp.util.buf_clear_references(buffer) 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 return
end 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) table.sort(result, function(a, b)
return before(a.range, b.range) return before(a.range, b.range)
end) end)
references[buffer] = result references[bufnr] = result
add_locs(buffer, result) add_locs(bufnr, result)
end end)
end end
return { return {
@ -272,6 +259,5 @@ return {
handle_document_highlight = handle_document_highlight, handle_document_highlight = handle_document_highlight,
hi_symbol = hi_symbol, hi_symbol = hi_symbol,
nohl = nohl, nohl = nohl,
nav_doc_hl = nav_doc_hl, cmd_nohl = cmd_nohl
cmd_nohl = cmd_nohl,
} }

@ -1,4 +1,5 @@
local log = require('navigator.util').log local log = require"navigator.util".log
local mk_handler = require"navigator.util".mk_handler
local lsp = vim.lsp local lsp = vim.lsp
local api = vim.api local api = vim.api
@ -17,7 +18,7 @@ M.servers_supporting_folding = {
texlab = true, texlab = true,
clangd = false, clangd = false,
gopls = true, gopls = true,
julials = false, julials = false
} }
M.active_folding_clients = {} M.active_folding_clients = {}
@ -28,16 +29,21 @@ function M.on_attach()
end end
function M.setup_plugin() function M.setup_plugin()
local cmd_group = api.nvim_create_augroup('NGFoldGroup', {}) api.nvim_command("augroup FoldingCommand")
api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost' }, { api.nvim_command("autocmd! * <buffer>")
group = cmd_group, api.nvim_command("autocmd BufEnter <buffer> lua require'navigator.foldlsp'.update_folds()")
pattern = '*', api.nvim_command("autocmd BufWritePost <buffer> lua require'navigator.foldlsp'.update_folds()")
callback = function() api.nvim_command("augroup end")
require('navigator.foldlsp').update_folds()
end, -- vim.cmd([[
}) --
-- function! folding_nvim#foldexpr()
local clients = vim.lsp.buf_get_clients(0) -- return luaeval(printf('require"navigator.foldlsp".get_fold_indic(%d)', v:lnum))
-- endfunction
--
-- ]])
local clients = vim.lsp.buf_get_clients()
for _, client in pairs(clients) do for _, client in pairs(clients) do
local client_id = client['id'] local client_id = client['id']
@ -51,6 +57,7 @@ function M.setup_plugin()
M.active_folding_clients[client_id] = server_supports_folding M.active_folding_clients[client_id] = server_supports_folding
end end
end end
-- print(vim.inspect(M.active_folding_clients))
end end
function M.update_folds() function M.update_folds()
@ -67,7 +74,8 @@ function M.update_folds()
-- client.config.callbacks['textDocument/foldingRange'] = M.fold_handler -- client.config.callbacks['textDocument/foldingRange'] = M.fold_handler
local current_bufnr = api.nvim_get_current_buf() local current_bufnr = api.nvim_get_current_buf()
local params = {uri = vim.uri_from_bufnr(current_bufnr)} local params = {uri = vim.uri_from_bufnr(current_bufnr)}
client.request('textDocument/foldingRange', { textDocument = params }, M.fold_handler, current_bufnr) client.request('textDocument/foldingRange', {textDocument = params}, M.fold_handler,
current_bufnr)
end end
end end
end end
@ -81,11 +89,11 @@ function M.debug_folds()
end end
end end
M.fold_handler = function(err, result, ctx, _) M.fold_handler = mk_handler(function(err, result, ctx, config)
-- params: err, method, result, client_id, bufnr -- params: err, method, result, client_id, bufnr
-- XXX: handle err? -- XXX: handle err?
if err or result == nil or #result == 0 then 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) print(err, ctx.method, ctx.client_id)
return return
end end
M.debug_folds() M.debug_folds()
@ -105,7 +113,7 @@ M.fold_handler = function(err, result, ctx, _)
api.nvim_win_set_option(current_window, 'foldmethod', 'expr') api.nvim_win_set_option(current_window, 'foldmethod', 'expr')
api.nvim_win_set_option(current_window, 'foldexpr', 'foldlsp#foldexpr()') api.nvim_win_set_option(current_window, 'foldexpr', 'foldlsp#foldexpr()')
end end
end end)
function M.adjust_foldstart(line_no) function M.adjust_foldstart(line_no)
return line_no + 1 return line_no + 1
@ -152,12 +160,13 @@ function M.get_fold_indic(lnum)
-- without any marker. -- without any marker.
return fold_level return fold_level
elseif is_foldstart then elseif is_foldstart then
return string.format('>%d', fold_level) return string.format(">%d", fold_level)
elseif is_foldend then elseif is_foldend then
return string.format('<%d', fold_level) return string.format("<%d", fold_level)
else else
return fold_level return fold_level
end end
end end
return M return M

@ -1,11 +1,11 @@
local log = require"navigator.util".log
-- NOTE: this file is a modified version of fold.lua from nvim-treesitter -- 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 api = vim.api
local tsutils = require('nvim-treesitter.ts_utils') local tsutils = require "nvim-treesitter.ts_utils"
local query = require('nvim-treesitter.query') local query = require "nvim-treesitter.query"
local parsers = require('nvim-treesitter.parsers') local parsers = require "nvim-treesitter.parsers"
local get_node_at_line = require('navigator.treesitter').get_node_at_line local get_node_at_line = require('navigator.treesitter').get_node_at_line
local M = {} local M = {}
@ -17,57 +17,41 @@ function M.on_attach()
-- M.update_folds() -- M.update_folds()
end end
function NG_custom_fold_text() function _G.custom_fold_text()
local line = vim.fn.getline(vim.v.foldstart) local line = vim.fn.getline(vim.v.foldstart)
local line_count = vim.v.foldend - vim.v.foldstart + 1 local line_count = vim.v.foldend - vim.v.foldstart + 1
-- log("" .. line .. " // " .. line_count .. " lines") -- log("" .. line .. " // " .. line_count .. " lines")
local ss, se = line:find('^%s*') return "" .. line .. ": " .. line_count .. " lines"
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 end
vim.opt.foldtext = NG_custom_fold_text() vim.opt.foldtext = custom_fold_text()
vim.opt.fillchars = { eob = '-', fold = ' ' }
vim.opt.viewoptions:remove('options') vim.opt.fillchars = {eob = "-", fold = " "}
function M.setup_fold() vim.opt.viewoptions:remove("options")
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() function M.setup_fold()
if not parsers.has_parser() then if not parsers.has_parser() then
api.nvim_win_set_option(current_window, 'foldmethod', 'indent') print("treesitter folding not enabled for current file")
log('fallback to indent folding')
return return
end end
log('setup treesitter folding') log("setup treesitter folding")
api.nvim_win_set_option(current_window, 'foldmethod', 'expr') api.nvim_command("augroup FoldingCommand")
api.nvim_win_set_option(current_window, 'foldexpr', 'folding#ngfoldexpr()') api.nvim_command("autocmd! * <buffer>")
end api.nvim_command("augroup end")
vim.opt.foldtext = 'v:lua.custom_fold_text()'
vim.opt.fillchars = {eob = "-", fold = " "}
vim.opt.viewoptions:remove("options")
local function get_fold_level(levels, lnum) local current_window = api.nvim_get_current_win()
local prev_l = levels[lnum] api.nvim_win_set_option(current_window, 'foldmethod', 'expr')
local prev_ln api.nvim_win_set_option(current_window, 'foldexpr', 'folding#foldexpr()')
if prev_l:find('>') then
prev_ln = tonumber(prev_l:sub(2))
else
prev_ln = tonumber(prev_l)
end
return prev_ln
end end
-- This is cached on buf tick to avoid computing that multiple times -- This is cached on buf tick to avoid computing that multiple times
-- Especially not for every line in the file when `zx` is hit -- Especially not for every line in the file when `zx` is hit
local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr) local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
local max_fold_level = api.nvim_win_get_option(0, 'foldnestmax') local max_fold_level = api.nvim_win_get_option(0, "foldnestmax")
local trim_level = function(level) local trim_level = function(level)
if level > max_fold_level then if level > max_fold_level then
return max_fold_level return max_fold_level
@ -78,15 +62,15 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
local parser = parsers.get_parser(bufnr) local parser = parsers.get_parser(bufnr)
if not parser then if not parser then
log('treesitter parser not loaded') warn("treesitter parser not loaded")
return {} return {}
end end
local matches = query.get_capture_matches_recursively(bufnr, function(lang) local matches = query.get_capture_matches_recursively(bufnr, function(lang)
if query.has_folds(lang) then if query.has_folds(lang) then
return '@fold', 'folds' return "@fold", "folds"
elseif query.has_locals(lang) then elseif query.has_locals(lang) then
return '@scope', 'locals' return "@scope", "locals"
end end
end) end)
@ -97,7 +81,7 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
local prev_start = -1 local prev_start = -1
local prev_stop = -1 local prev_stop = -1
local min_fold_lines = api.nvim_win_get_option(0, 'foldminlines') local min_fold_lines = api.nvim_win_get_option(0, "foldminlines")
for _, node in ipairs(matches) do for _, node in ipairs(matches) do
local start, _, stop, stop_col = node.node:range() local start, _, stop, stop_col = node.node:range()
@ -119,41 +103,21 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
prev_stop = stop prev_stop = stop
end end
end end
trace(start_counts)
trace(stop_counts)
local levels = {} local levels = {}
local current_level = 0 local current_level = 0
-- We now have the list of fold opening and closing, fill the gaps and mark where fold start -- 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 for lnum = 0, api.nvim_buf_line_count(bufnr) do
local node, _ = get_node_at_line(lnum + 1) local node, _ = get_node_at_line(lnum + 1)
-- log(lnum, node:type())
local comment = node:type() == 'comment' 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) local last_trimmed_level = trim_level(current_level)
current_level = current_level + (start_counts[lnum] or 0) current_level = current_level + (start_counts[lnum] or 0)
local trimmed_level = trim_level(current_level) local trimmed_level = trim_level(current_level)
local current_level2 = current_level - (stop_counts[lnum] or 0) current_level = current_level - (stop_counts[lnum] or 0)
local next_trimmed_level = trim_level(current_level2) local next_trimmed_level = trim_level(current_level)
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 -- Determine if it's the start/end of a fold
-- NB: vim's fold-expr interface does not have a mechanism to indicate that -- 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 -- two (or more) folds start at this line, so it cannot distinguish between
@ -162,82 +126,54 @@ local folds_levels = tsutils.memoize_by_buf_tick(function(bufnr)
-- ( \n ( \n ) \n ( \n ) \n ) -- ( \n ( \n ) \n ( \n ) \n )
-- If it did have such a mechansim, (trimmed_level - last_trimmed_level) -- If it did have such a mechansim, (trimmed_level - last_trimmed_level)
-- would be the correct number of starts to pass on. -- would be the correct number of starts to pass on.
if trimmed_level - last_trimmed_level > 0 then if comment then
if levels[lnum + 1] ~= '>' .. tostring(trimmed_level) then if lnum == 0 or levels[lnum] == tostring(trimmed_level) then
levels[lnum + 1] = tostring(trimmed_level) -- hack do not fold current line as it is first in fold range levels[lnum + 1] = ">" .. tostring(trimmed_level + 1) -- allow comment fold independtly
else
levels[lnum + 1] = tostring(trimmed_level + 1) -- allow comment fold independtly
end end
levels[lnum + 2] = '>' .. tostring(trimmed_level + 1) -- dirty hack fold start from next line else
trace('fold start') if trimmed_level - last_trimmed_level > 0 then
elseif trimmed_level - next_trimmed_level > 0 then -- last line in fold range levels[lnum + 1] = tostring(trimmed_level - 1) -- hack
levels[lnum + 2] = ">" .. tostring(trimmed_level) -- dirty hack
elseif trimmed_level - next_trimmed_level > 0 then
-- Ending marks tend to confuse vim more than it helps, particularly when -- 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 -- the fold level changes by at least 2; we can uncomment this if
-- vim's behavior gets fixed. -- vim's behavior gets fixed.
if lnum ~= 0 then
trace('fold end') levels[lnum] = "<" .. tostring(trimmed_level)
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 end
-- levels[lnum + 1] = tostring(trimmed_level + 1) 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 else
local prev_l = levels[lnum] if levels[lnum + 1] == nil then
if prev_l:find('>') then levels[lnum + 1] = tostring(trimmed_level)
levels[lnum + 1] = prev_l:sub(2)
else
levels[lnum + 1] = prev_l
end
end
else
levels[lnum + 1] = tostring(n)
end end
end end
end end
trace(levels)
end
pre_node = node
end end
trace(levels) -- log(levels)
return levels return levels
end) end)
function M.get_fold_indic(lnum) 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 buf = api.nvim_get_current_buf()
local shown = false local shown = false
for i = 1, vim.fn.tabpagenr('$') do for i = 1, vim.fn.tabpagenr('$') do
for _, value in pairs(vim.fn.tabpagebuflist(i)) do for key, value in pairs(vim.fn.tabpagebuflist(i)) do
if value == buf then if value == buf then
shown = true shown = true
end end
end end
end end
if not shown then if not shown then
return '0' return "0"
end end
local levels = folds_levels(buf) or {} local levels = folds_levels(buf) or {}
-- trace(lnum, levels[lnum]) -- TODO: comment it out in master -- log(lnum, levels[lnum]) -- TODO: comment it out in master
return levels[lnum] or '0' return levels[lnum] or "0"
end end
return M return M

@ -1,26 +1,26 @@
-- https://github.com/wention/dotfiles/blob/master/.config/nvim/lua/config/lsp.lua -- 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 -- https://github.com/lukas-reineke/dotfiles/blob/master/vim/lua/lsp/handlers.lua
local mk_handler = require"navigator.util".mk_handler
return { return {
format_hdl = function(err, result, ctx, _) -- FIXME: bufnr is nil format_hdl = mk_handler(function(err, result, ctx, cfg) -- FIXME: bufnr is nil
if err ~= nil or result == nil then if err ~= nil or result == nil then
return return
end end
local util = require('navigator.util') local util = require "navigator.util"
local log = util.log 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, -- If the buffer hasn't been modified before the formatting has finished,
-- update the buffer -- update the buffer
-- if not vim.api.nvim_buf_get_option(ctx.bufnr, 'modified') then -- if not vim.api.nvim_buf_get_option(ctx.bufnr, 'modified') then
vim.defer_fn(function() vim.defer_fn(function()
log('fmt callback') log('fmt callback')
if ctx.bufnr == vim.api.nvim_get_current_buf() or not vim.api.nvim_buf_get_option(ctx.bufnr, 'modified') then 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() local view = vim.fn.winsaveview()
vim.lsp.util.apply_text_edits(result, ctx.bufnr, offset_encoding) vim.lsp.util.apply_text_edits(result, ctx.bufnr)
vim.fn.winrestview(view) vim.fn.winrestview(view)
-- FIXME: commented out as a workaround -- FIXME: commented out as a workaround
-- if bufnr == vim.api.nvim_get_current_buf() then -- if bufnr == vim.api.nvim_get_current_buf() then
@ -30,20 +30,7 @@ return {
vim.api.nvim_command('silent doautocmd <nomodeline> User FormatterPost') vim.api.nvim_command('silent doautocmd <nomodeline> User FormatterPost')
-- end -- end
end end
end, 100) end, 100)
end, 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
vim.go.operatorfunc = 'v:lua.op_func_formatting'
vim.api.nvim_feedkeys('g@', 'n', false)
end,
} }

@ -1,68 +1,234 @@
local M = {} local M = {}
-- local ListView = require('guihua.listview') local ListView = require "guihua.listview"
-- local TextView = require('guihua.textview') local TextView = require "guihua.textview"
local util = require('navigator.util') local util = require "navigator.util"
local log = util.log local log = require"navigator.util".log
local trace = require('navigator.util').trace local trace = require"navigator.util".trace
local api = vim.api local api = vim.api
local active_list_view -- only one listview at a time
function M.new_list_view(opts) local top_center = require"guihua.location".top_center
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
--
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) -- log(opts)
local config = require('navigator').config_values() -- 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
if active_list_view ~= nil then -- trace(syntax, contents)
trace(active_list_view) local win_opts = {
local winnr = active_list_view.win syntax = syntax,
local bufnr = active_list_view.buf 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
}
if bufnr and api.nvim_buf_is_valid(bufnr) and winnr and api.nvim_win_is_valid(winnr) then if _NgConfigValues.external then
log('list view already present') win_opts.external = true
return active_list_view win_opts.relative = nil
end
-- 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 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,
external = win_opts.external
})
return w
end
function M.preview_uri(opts) -- uri, width, line, col, offset_x, offset_y
-- local handle = vim.loop.new_async(vim.schedule_wrap(function()
local line_beg = opts.lnum - 1
if line_beg >= _NgConfigValues.preview_lines_before then
line_beg = line_beg - _NgConfigValues.preview_lines_before
elseif line_beg >= 2 then
line_beg = line_beg - 2
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)
-- handle:close()
-- end))
-- handle:send()
end
function M.new_list_view(opts)
local config = require("navigator").config_values()
local items = opts.items local items = opts.items
local data = {}
local wwidth = api.nvim_get_option("columns")
opts.height_ratio = opts.height or config.height local loc = "top_center"
opts.width_ratio = opts.height or config.width
opts.preview_height_ratio = opts.preview_height or config.preview_height local mwidth = _NgConfigValues.width
opts.preview_lines = config.preview_lines 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, opts.width or 120)
local wheight = math.floor(
1 + api.nvim_get_option("lines") * (_NgConfigValues.height + _NgConfigValues.preview_height))
local pheight = math.max(_NgConfigValues.preview_lines,
math.floor(api.nvim_get_option("lines") * _NgConfigValues.preview_height))
local prompt = opts.prompt or false
if opts.rawdata then if opts.rawdata then
opts.data = items data = items
else else
opts.data = require('navigator.render').prepare_for_render(items, opts) trace(items)
end data = require"navigator.render".prepare_for_render(items, opts)
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 end
if not items or vim.tbl_isempty(items) then
log('empty data return') local border = _NgConfigValues.border or 'shadow'
if not data or vim.tbl_isempty(data) then
return return
end end
opts.transparency = config.transparency -- replace
if #items >= config.lines_show_prompt then -- TODO: 10 vimrc opt
opts.prompt = true if #data > 10 and opts.prompt == nil then
loc = "top_center"
prompt = true
end end
opts.external = config.external local lheight = math.min(#data, math.floor(wheight * _NgConfigValues.height))
opts.preview_lines_before = 4
if _NgConfigValues.debug then local r, _ = top_center(lheight, width)
local logopts = { items = {}, data = {} }
logopts = vim.tbl_deep_extend('keep', logopts, opts) local offset_y = r + lheight
log(logopts) -- 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
local transparency = _NgConfigValues.transparency
if transparency == 100 then
transparency = nil
end
local ext = _NgConfigValues.external
if ext then
opts.relative = nil
end
return ListView:new({
loc = loc,
prompt = prompt,
relative = opts.relative,
style = opts.style,
api = opts.api,
total = opts.total,
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,
external = ext,
on_confirm = opts.on_confirm or function(item, split_opts)
log(split_opts)
split_opts = split_opts or {}
if item.filename ~= nil then
log("openfile ", item.filename, item.lnum, item.col)
util.open_file_at(item.filename, item.lnum, item.col, split_opts.split)
end
end,
transparency = transparency,
on_move = opts.on_move or function(item)
trace("on move", item)
trace("on move", item.text or item, item.uri, item.filename)
-- todo fix
if item.uri == nil then
item.uri = "file:///" .. item.filename
end
return M.preview_uri({
uri = item.uri,
width = width,
height = lheight, -- this is to cal offset
preview_height = pheight,
lnum = item.lnum,
col = item.col,
range = item.range,
offset_x = 0,
offset_y = offset_y,
border = border,
external = ext,
enable_edit = opts.enable_preview_edit or false
})
end end
active_list_view = require('guihua.gui').new_list_view(opts) })
return active_list_view
end end
return M return M

@ -1,312 +1,102 @@
local gui = require('navigator.gui') local gui = require "navigator.gui"
local util = require('navigator.util') local util = require "navigator.util"
local log = util.log local log = util.log
local trace = util.trace local trace = util.trace
local partial = util.partial local partial = util.partial
local lsphelper = require('navigator.lspwrapper') local lsphelper = require "navigator.lspwrapper"
local path_sep = require('navigator.util').path_sep() local path_sep = require"navigator.util".path_sep()
local path_cur = require('navigator.util').path_cur() local path_cur = require"navigator.util".path_cur()
local cwd = vim.loop.cwd() local cwd = vim.loop.cwd()
local in_method = 'callHierarchy/incomingCalls'
local out_method = 'callHierarchy/outgoingCalls'
local lsp_method = { to = out_method, from = in_method }
local panel_method = { to = out_method, from = in_method }
local M = {} local M = {}
local outgoing_calls_handler local function call_hierarchy_handler(direction, err, result, ctx, cfg, error_message)
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 if not result then
vim.notify('No call hierarchy items found', vim.lsp.log_levels.WARN) print("No call hierarchy items found")
return return
end end
-- trace('call_hierarchy', result) trace('call_hierarchy', result)
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 if err ~= nil then
log('dir', direction, 'result', result, 'err', err, ctx) log("dir", direction, "result", result, "err", err, ctx)
vim.notify('ERROR: ' .. err, vim.lsp.log_levels.WARN) print("ERROR: " .. error_message)
return return
end end
local items = ctx.items or {} local items = {}
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 if call_hierarchy_item.kind then
kind = require('navigator.lspclient.lspkind').symbol_kind(call_hierarchy_item.kind) .. ' ' kind = require'navigator.lspclient.lspkind'.symbol_kind(call_hierarchy_item.kind) .. ' '
end end
-- for _, range in pairs(call_hierarchy_call.fromRanges) do
range = call_hierarchy_item.range or call_hierarchy_item.selectionRange
local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri)) local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri))
local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1) local display_filename = filename:gsub(cwd .. path_sep, path_cur, 1)
call_hierarchy_item.detail = call_hierarchy_item.detail or '' call_hierarchy_item.detail = call_hierarchy_item.detail or ""
call_hierarchy_item.detail = string.gsub(call_hierarchy_item.detail, '\n', '') call_hierarchy_item.detail = call_hierarchy_item.detail:gsub("\n", "")
trace(call_hierarchy_item) trace(range, call_hierarchy_item)
local disp_item = vim.tbl_deep_extend('force', {}, call_hierarchy_item) local disp_item = {
disp_item = vim.tbl_deep_extend('force', disp_item, { uri = call_hierarchy_item.uri,
filename = filename, filename = filename,
display_filename = display_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, text = kind .. call_hierarchy_item.name .. '' .. call_hierarchy_item.detail,
lnum = call_hierarchy_item.selectionRange.start.line + 1, range = range,
col = call_hierarchy_item.selectionRange.start.character, lnum = range.start.line + 1,
}) col = range.start.character
table.insert(items, disp_item)
end
trace(items)
return items
end
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 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 make_params = function(uri, pos) table.insert(items, disp_item)
return { -- end
textDocument = {
uri = uri,
},
position = pos,
}
end end
local function display_panel(args)
-- args = {items=results, ft=ft, api=api}
log(args)
local Panel = require('guihua.panel')
local bufnr = args.bufnr or vim.api.nvim_get_current_buf()
-- local ft = args.ft or vim.api.nvim_buf_get_option(bufnr, 'buftype')
local items = args.items
local p = Panel:new({
header = args.header or 'Call Hierarchy',
render = function(buf)
log(buf)
return items return items
end,
fold = function(panel, node)
if node.expanded ~= nil then
node.expanded = not node.expanded
vim.cmd('normal! za')
else
expand(panel, node)
node.expanded = true
end
log('fold')
return node
end,
})
p:open(true)
end
local function 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
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( local call_hierarchy_handler_from = partial(call_hierarchy_handler, "from")
hierarchy_handler, local call_hierarchy_handler_to = partial(call_hierarchy_handler, "to")
'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 function incoming_calls_handler(bang, err, result, ctx, cfg)
local outgoing_calls_expand = util.partial4(hierarchy_handler, 'to', call_hierarchy_handler_to, expand_item, '') assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp hierarchy")
local results = call_hierarchy_handler_from(err, result, ctx, cfg, "Incoming calls not found")
function expand(panel, node) local ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft")
trace(panel, node) gui.new_list_view({items = results, ft = ft, api = ''})
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 end
local bufnr = vim.uri_to_bufnr(node.uri) local function outgoing_calls_handler(bang, err, result, ctx, cfg)
call_hierarchy(node.method, { local results = call_hierarchy_handler_to(err, result, ctx, cfg, "Outgoing calls not found")
params = params,
panel = panel,
parent_node = node,
handler = handler,
bufnr = bufnr,
})
end
local request = vim.lsp.buf_request
-- call_hierarchy with floating window local ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft")
call_hierarchy = function(method, opts) gui.new_list_view({items = results, ft = ft, api = ''})
trace(method, opts) -- fzf_locations(bang, "", "Outgoing Calls", results, false)
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 end
-- log(opts, params)
return request( function M.incoming_calls(bang, opts)
bufnr, assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp hierarchy")
'textDocument/prepareCallHierarchy', if not lsphelper.check_capabilities("call_hierarchy") then
params,
vim.lsp.with(function(err, result, ctx)
if err then
vim.notify(err.message, vim.log.levels.WARN)
return return
end 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) local params = vim.lsp.util.make_position_params()
call_hierarchy(in_method, opts) lsphelper.call_sync("callHierarchy/incomingCalls", params, opts, partial(incoming_calls_handler, bang))
end end
function M.outgoing_calls(opts) function M.outgoing_calls(bang, opts)
call_hierarchy(out_method, opts) assert(#vim.lsp.buf_get_clients() > 0, "Must have a client running to use lsp_tags")
if not lsphelper.check_capabilities("call_hierarchy") then
return
end end
function M.incoming_calls_panel(opts) local params = vim.lsp.util.make_position_params()
opts = vim.tbl_extend('force', { handler = incoming_calls_panel }, opts or {}) lsphelper.call_sync("callHierarchy/outgoingCalls", params, opts, partial(outgoing_calls_handler, bang))
call_hierarchy(in_method, opts)
end end
function M.outgoing_calls_panel(opts) M.incoming_calls_call = partial(M.incoming_calls, 0)
opts = vim.tbl_extend('force', { handler = outgoing_calls_panel }, opts or {}) M.outgoing_calls_call = partial(M.outgoing_calls, 0)
call_hierarchy(out_method, opts)
end
M.incoming_calls_handler = incoming_calls_handler M.incoming_calls_handler = partial(incoming_calls_handler, 0)
M.outgoing_calls_handler = outgoing_calls_handler M.outgoing_calls_handler = partial(outgoing_calls_handler, 0)
-- 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 return M

@ -1,35 +1,37 @@
local util = require('navigator.util') local util = require "navigator.util"
local lsphelper = require('navigator.lspwrapper') local mk_handler = util.mk_handler
local gui = require('navigator.gui') local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui"
local M = {} local M = {}
-- local location = require('guihua.location') local location = require("guihua.location")
local partial = util.partial local partial = util.partial
local locations_to_items = lsphelper.locations_to_items local locations_to_items = lsphelper.locations_to_items
local log = util.log local log = util.log
-- dataformat should be same as reference -- dataformat should be same as reference
local function location_handler(err, locations, ctx, _, msg) local function location_handler(err, locations, ctx, cfg, msg)
if err ~= nil then if err ~= nil then
vim.notify('ERROR: ' .. tostring(err) .. ' ' .. msg, vim.lsp.log_levels.WARN) print("ERROR: " .. tostring(err) .. " " .. msg)
return return
end end
return locations_to_items(locations, ctx) return locations_to_items(locations)
end end
local function implementation_handler(_, err, result, ctx, cfg) local function implementation_handler(bang, err, result, ctx, cfg)
local results = location_handler(err, result, ctx, cfg, 'Implementation not found') local results = location_handler(err, result, ctx, cfg, "Implementation not found")
local ft = vim.api.nvim_buf_get_option(ctx.bufnr, 'ft') local ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft")
gui.new_list_view({items = results, ft = ft, api = 'Implementation'}) gui.new_list_view({items = results, ft = ft, api = 'Implementation'})
end end
function M.implementation(bang, opts) function M.implementation(bang, opts)
if not lsphelper.check_capabilities('implementationProvider') then if not lsphelper.check_capabilities("implementation") then
return return
end end
local params = vim.lsp.util.make_position_params() local params = vim.lsp.util.make_position_params()
log('impel params', params) log("impel params", params)
lsphelper.call_sync('textDocument/implementation', params, opts, partial(implementation_handler, bang)) lsphelper.call_sync("textDocument/implementation", params, opts,
partial(implementation_handler, bang))
end end
M.implementation_call = partial(M.implementation, 0) M.implementation_call = partial(M.implementation, 0)

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

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

File diff suppressed because it is too large Load Diff

@ -1,191 +0,0 @@
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,4 +1,6 @@
local M = {} local lsp = require("vim.lsp")
M = {}
local capabilities = vim.lsp.protocol.make_client_capabilities() local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true capabilities.textDocument.completion.completionItem.snippetSupport = true

@ -1,9 +1,8 @@
local M = {} local M = {}
-- local log = require('navigator.util').log local log = require"navigator.util".log
local api = vim.api local api = vim.api
local cmd_group = api.nvim_create_augroup('NGHiGroup', {})
-- lsp sign          ﮻         ﯭ        ﳀ   -- lsp sign          ﮻         ﯭ        ﳀ  
function M.diagnositc_config_sign() function M.diagnositc_config_sign()
if M.configed then if M.configed then
@ -11,62 +10,80 @@ function M.diagnositc_config_sign()
end end
local icons = _NgConfigValues.icons local icons = _NgConfigValues.icons
local sign_name = 'NavigatorLightBulb' local sign_name = "NavigatorLightBulb"
if vim.fn.sign_getdefined(sign_name).text == nil then 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
vim.fn.sign_define(sign_name, { text = icons.code_lens_action_icon, texthl = 'LspDiagnosticsSignHint' }) .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 end
local e, w, i, h = icons.diagnostic_err, icons.diagnostic_warn, icons.diagnostic_info, icons.diagnostic_hint local e, w, i, h = icons.diagnostic_err, icons.diagnostic_warn, icons.diagnostic_info,
icons.diagnostic_hint
if vim.diagnostic ~= nil then
local t = vim.fn.sign_getdefined('DiagnosticSignWarn') local t = vim.fn.sign_getdefined('DiagnosticSignWarn')
if vim.tbl_isempty(t) or t[1].text == 'W ' and icons.icons == true then 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('DiagnosticSignError',
vim.fn.sign_define('DiagnosticSignInfo', { text = i, texthl = 'DiagnosticInfo', linehl = '', numhl = '' }) {text = e, texthl = 'DiagnosticError', linehl = '', numhl = ''})
vim.fn.sign_define('DiagnosticSignHint', { text = h, texthl = 'DiagnosticHint', 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') t = vim.fn.sign_getdefined('DiagnosticSignWarn')
end end
else
local t = vim.fn.sign_getdefined('LspDiagnosticSignWarn')
if vim.tbl_isempty(t) or t[1].text == "W " and icons.icons == true then
vim.fn.sign_define('LspDiagnosticsSignError',
{text = e, texthl = 'LspDiagnosticsSignError', linehl = '', numhl = ''})
vim.fn.sign_define('LspDiagnosticsSignWarning',
{text = w, texthl = 'LspDiagnosticsSignWarning', linehl = '', numhl = ''})
vim.fn.sign_define('LspDiagnosticsSignInformation', {
text = i,
texthl = 'LspDiagnosticsSignInformation',
linehl = '',
numhl = ''
})
vim.fn.sign_define('LspDiagnosticsSignHint',
{text = h, texthl = 'LspDiagnosticsSignHint', linehl = '', numhl = ''})
end
end
M.configed = true M.configed = true
end end
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() function M.add_highlight()
-- lsp system default -- 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_set_hl(0, 'DiagnosticUnderlineError', { link = 'SpellBad', default = true }) api.nvim_command("hi! link DiagnosticUnderlineError SpellBad")
api.nvim_set_hl(0, 'DiagnosticUnderlineWarning', { link = 'SpellRare', default = true }) api.nvim_command("hi! link DiagnosticUnderlineWarning SpellRare")
api.nvim_set_hl(0, 'DiagnosticUnderlineInformation', { link = 'SpellRare', default = true }) api.nvim_command("hi! link DiagnosticUnderlineInformation SpellRare")
api.nvim_set_hl(0, 'DiagnosticUnderlineHint', { link = 'SpellRare', default = true }) api.nvim_command("hi! link DiagnosticUnderlineHint SpellRare")
api.nvim_set_hl(0, 'NGPreviewTitle', { link = 'Title', default = true }) api.nvim_command("hi def link NGPreviewTitle Title")
api.nvim_set_hl(0, 'LspReferenceRead', { default = true, link = 'IncSearch'}) local colors = {
api.nvim_set_hl(0, 'LspReferenceText', { default = true, link = 'Visual'}) {'#aefe00', '#aede00', '#aebe00', '#4e7efe'}, {'#ff00e0', '#df00e0', '#af00e0', '#fedefe'},
api.nvim_set_hl( 0, 'LspReferenceWrite', { default = true, link = 'Search'}) {'#1000ef', '#2000df', '#2000cf', '#f0f040'}, {'#d8a8a3', '#c8a8a3', '#b8a8a3', '#4e2c33'},
{'#ffa724', '#efa024', '#dfa724', '#0040ff'}, {'#afdc2b', '#09dc4b', '#08d04b', '#ef4f8f'}
}
for i = 1, #colors do for i = 1, #colors do
for j = 1, 3 do for j = 1, 3 do
local hlg = string.format('NGHiReference_%i_%i', i, j) -- , colors[i][j], colors[i][4] local cmd = string.format("hi! default NGHiReference_%i_%i guibg=%s guifg=%s ", i, j,
api.nvim_set_hl(0, hlg, { fg = colors[i][j], bg = colors[i][4], default = true }) colors[i][j], colors[i][4])
vim.cmd(cmd)
end end
end end
end end
api.nvim_create_autocmd('ColorScheme', {
group = cmd_group,
pattern = '*',
callback = function()
M.add_highlight()
end,
})
return M return M

@ -1,135 +1,78 @@
local kind_symbols = { local kind_symbols = {
Text = '', Text = "",
Method = 'ƒ', Method = "ƒ",
Function = '', Function = "",
Constructor = '', Constructor = "",
Field = '', Field = "",
Variable = '', Variable = "",
Class = '', Class = "",
Interface = '', Interface = "",
Module = '', Module = "",
Property = '', Property = "",
Unit = '', Unit = "",
Value = '', Value = "",
Enum = '', Enum = "",
Keyword = '', Keyword = "",
Snippet = '', Snippet = "",
Color = '', Color = "",
File = '', File = "",
Reference = '', Reference = "",
Folder = '', Folder = "",
EnumMember = '', EnumMember = "",
Constant = '', Constant = "",
Struct = '', Struct = "",
Event = '', Event = "",
Operator = '', Operator = "",
TypeParameter = '', TypeParameter = "",
Default = '', Default = ""
} }
local CompletionItemKind = { local CompletionItemKind = {
'', "", "𝔉 ", "", "", "", "", "", "", "", "", "", "", "𝕰 ", "",
'𝔉 ', "", "", "", "", "", "", "", "", "", "", "", ""
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'𝕰 ',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
} }
-- A symbol kind. -- A symbol kind.
-- local SymbolKind = { local SymbolKind = {
-- File = 1, File = 1,
-- Module = 2, Module = 2,
-- Namespace = 3, Namespace = 3,
-- Package = 4, Package = 4,
-- Class = 5, Class = 5,
-- Method = 6, Method = 6,
-- Property = 7, Property = 7,
-- Field = 8, Field = 8,
-- Constructor = 9, Constructor = 9,
-- Enum = 10, Enum = 10,
-- Interface = 11, Interface = 11,
-- Function = 12, Function = 12,
-- Variable = 13, Variable = 13,
-- Constant = 14, Constant = 14,
-- String = 15, String = 15,
-- Number = 16, Number = 16,
-- Boolean = 17, Boolean = 17,
-- Array = 18, Array = 18,
-- Object = 19, Object = 19,
-- Key = 20, Key = 20,
-- Null = 21, Null = 21,
-- EnumMember = 22, EnumMember = 22,
-- Struct = 23, Struct = 23,
-- Event = 24, Event = 24,
-- Operator = 25, Operator = 25,
-- TypeParameter = 26 TypeParameter = 26
-- } }
local SymbolItemKind = { local SymbolItemKind = {
'', "", "", "", "", "", "ƒ ", "", "", "", "𝕰 ", "", "", "", "", "",
'', "", "", "", "", "", "", "", "", "", "", "", ""
'',
'',
'',
'ƒ ',
'',
'',
'',
'𝕰 ',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
} }
local lspkind = {} local lspkind = {}
function lspkind.comp_kind(kind) function lspkind.comp_kind(kind) return CompletionItemKind[kind] or "" end
return CompletionItemKind[kind] or ''
end
function lspkind.symbol_kind(kind) function lspkind.symbol_kind(kind) return SymbolItemKind[kind] or "" end
return SymbolItemKind[kind] or ''
end
function lspkind.cmp_kind(kind) function lspkind.cmp_kind(kind) return kind_symbols[kind] or "" end
return kind_symbols[kind] or ''
end
function lspkind.init() function lspkind.init() require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind end
require('vim.lsp.protocol').CompletionItemKind = CompletionItemKind
end
return lspkind return lspkind

@ -1,112 +1,83 @@
local util = require('navigator.util') local log = require"navigator.util".log
local log = util.log local trace = require"navigator.util".trace
local trace = util.trace
local api = vim.api local event_hdlrs = {
{ev = "BufWritePre", func = [[require "navigator.diagnostics".set_diag_loclist()]]},
if vim.lsp.buf.format == nil then {ev = "CursorHold", func = "document_highlight()"},
vim.lsp.buf.format = vim.lsp.buf.formatting {ev = "CursorHoldI", func = "document_highlight()"},
end {ev = "CursorMoved", func = "clear_references()"}
}
if vim.diagnostic == nil then local double = {"", "", "", "", "", "", "", ""}
util.error('Please update nvim to 0.6.1+') local single = {"", "", "", "", "", "", "", ""}
end
local double = { '', '', '', '', '', '', '', '' }
local single = { '', '', '', '', '', '', '', '' }
-- TODO https://github.com/neovim/neovim/pull/16591 use vimkeymap.set/del
-- LuaFormatter off -- LuaFormatter off
local key_maps = { local key_maps = {
{ key = 'gr', func = require('navigator.reference').async_ref, desc = 'async_ref' }, {key = "gr", func = "require('navigator.reference').reference()"},
{ key = '<Leader>gr', func = require('navigator.reference').reference, desc = 'reference' }, -- reference deprecated {mode = "i", key = "<M-k>", func = "signature_help()"},
{ mode = 'i', key = '<M-k>', func = vim.lsp.signature_help, desc = 'signature_help' }, {key = "<c-k>", func = "signature_help()"},
{ key = '<c-k>', func = vim.lsp.buf.signature_help, desc = 'signature_help' }, {key = "g0", func = "require('navigator.symbols').document_symbols()"},
{ key = 'g0', func = require('navigator.symbols').document_symbols, desc = 'document_symbols' }, {key = "gW", func = "workspace_symbol()"},
{ key = 'gW', func = require('navigator.workspace').workspace_symbol_live, desc = 'workspace_symbol_live' }, {key = "<c-]>", func = "require('navigator.definition').definition()"},
{ key = '<c-]>', func = require('navigator.definition').definition, desc = 'definition' }, {key = "gD", func = "declaration({ border = 'rounded', max_width = 80 })"},
{ key = 'gd', func = require('navigator.definition').definition, desc = 'definition' }, {key = "gp", func = "require('navigator.definition').definition_preview()"},
{ key = 'gD', func = vim.lsp.buf.declaration, desc = 'declaration' }, {key = "gT", func = "require('navigator.treesitter').buf_ts()"},
{ key = 'gp', func = require('navigator.definition').definition_preview, desc = 'definition_preview' }, {key = "<Leader>gT", func = "require('navigator.treesitter').bufs_ts()"},
{ key = '<Leader>gt', func = require('navigator.treesitter').buf_ts, desc = 'buf_ts' }, {key = "K", func = "hover({ popup_opts = { border = single, max_width = 80 }})"},
{ key = '<Leader>gT', func = require('navigator.treesitter').bufs_ts, desc = 'bufs_ts' }, {key = "<Space>ca", mode = "n", func = "require('navigator.codeAction').code_action()"},
{ key = '<Leader>ct', func = require('navigator.ctags').ctags, desc = 'ctags' }, {key = "<Space>cA", mode = "v", func = "range_code_action()"},
{ key = 'K', func = vim.lsp.buf.hover, desc = 'hover' }, {key = "<Leader>re", func = "rename()"},
{ key = '<Space>ca', mode = 'n', func = require('navigator.codeAction').code_action, desc = 'code_action' }, {key = "<Space>rn", func = "require('navigator.rename').rename()"},
{ {key = "<Leader>gi", func = "incoming_calls()"},
key = '<Space>ca', {key = "<Leader>go", func = "outgoing_calls()"},
mode = 'v', {key = "gi", func = "implementation()"},
func = require('navigator.codeAction').range_code_action, {key = "<Space>D", func = "type_definition()"},
desc = 'range_code_action', {key = "gL", func = "require('navigator.diagnostics').show_diagnostics()"},
}, {key = "gG", func = "require('navigator.diagnostics').show_buf_diagnostics()"},
-- { key = '<Leader>re', func = 'rename()' }, {key = "<Leader>dt", func = "require('navigator.diagnostics').toggle_diagnostics()"},
{ key = '<Space>rn', func = require('navigator.rename').rename, desc = 'rename' }, {key = "]d", func = "diagnostic.goto_next({ border = 'rounded', max_width = 80})"},
{ key = '<Leader>gi', func = vim.lsp.buf.incoming_calls, desc = 'incoming_calls' }, {key = "[d", func = "diagnostic.goto_prev({ border = 'rounded', max_width = 80})"},
{ key = '<Leader>go', func = vim.lsp.buf.outgoing_calls, desc = 'outgoing_calls' }, {key = "]r", func = "require('navigator.treesitter').goto_next_usage()"},
{ key = 'gi', func = vim.lsp.buf.implementation, desc = 'implementation' }, {key = "[r", func = "require('navigator.treesitter').goto_previous_usage()"},
{ key = '<Space>D', func = vim.lsp.buf.type_definition, desc = 'type_definition' }, {key = "<C-LeftMouse>", func = "definition()"},
{ key = 'gL', func = require('navigator.diagnostics').show_diagnostics, desc = 'show_diagnostics' }, {key = "g<LeftMouse>", func = "implementation()"},
{ key = 'gG', func = require('navigator.diagnostics').show_buf_diagnostics, desc = 'show_buf_diagnostics' }, {key = "<Leader>k", func = "require('navigator.dochighlight').hi_symbol()"},
{ key = '<Leader>dt', func = require('navigator.diagnostics').toggle_diagnostics, desc = 'toggle_diagnostics' }, {key = '<Space>wa', func = 'add_workspace_folder()'},
{ key = ']d', func = vim.diagnostic.goto_next, desc = 'next diagnostics' }, {key = '<Space>wr', func = 'remove_workspace_folder()'},
{ key = '[d', func = vim.diagnostic.goto_prev, desc = 'prev diagnostics' }, {key = '<Space>ff', func = 'formatting()', mode='n'},
{ key = ']O', func = vim.diagnostic.set_loclist, desc = 'diagnostics set loclist' }, {key = '<Space>ff', func = 'range_formatting()', mode='v'},
{ key = ']r', func = require('navigator.treesitter').goto_next_usage, desc = 'goto_next_usage' }, {key = '<Space>wl', func = 'print(vim.inspect(vim.lsp.buf.list_workspace_folders()))'},
{ key = '[r', func = require('navigator.treesitter').goto_previous_usage, desc = 'goto_previous_usage' }, {key = "<Space>la", mode = "n", func = "require('navigator.codelens').run_action()"},
{ 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 -- LuaFormatter on
local M = {} local M = {}
local ccls_mappings = { local ccls_mappings = {
{ key = '<Leader>gi', func = require('navigator.cclshierarchy').incoming_calls, desc = 'incoming_calls' }, {key = "<Leader>gi", func = "require('navigator.cclshierarchy').incoming_calls()"},
{ key = '<Leader>go', func = require('navigator.cclshierarchy').outgoing_calls, desc = 'outgoing_calls' }, {key = "<Leader>go", func = "require('navigator.cclshierarchy').outgoing_calls()"}
} }
local check_cap = function(opts) local check_cap = function(cap)
-- log(vim.lsp.buf_get_clients(0)) -- log(vim.lsp.buf_get_clients(0))
local fmt, rfmt, ccls local fmt, rfmt, ccls
local cap = opts.cap if cap and cap.document_formatting then
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 fmt = true
end end
if cap and cap.documentRangeFormattingProvider then if cap and cap.document_range_formatting then
rfmt = true rfmt = true
end end
for _, value in pairs(vim.lsp.buf_get_clients(0)) do for _, value in pairs(vim.lsp.buf_get_clients(0)) do
trace(value) trace(value)
if value ~= nil and value.server_capabilities == nil then if value ~= nil and value.resolved_capabilities == nil then
if value.server_capabilities.documentFormattingProvider then if value.resolved_capabilities.document_formatting then
fmt = true fmt = true
end end
if value.server_capabilities.documentRangeFormattingProvider then if value.resolved_capabilities.document_range_formatting then
rfmt = true rfmt = true
end end
log('override ccls', value.config) log("override ccls", value.config)
if value.config.name == 'ccls' then if value.config.name == "ccls" then
ccls = true ccls = true
end end
end end
@ -114,52 +85,25 @@ local check_cap = function(opts)
return fmt, rfmt, ccls return fmt, rfmt, ccls
end end
local function set_cmds(_) local function set_mapping(user_opts)
local commands = { log('setup mapping')
[[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>",
}
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} local opts = {noremap = true, silent = true}
vim.validate({ user_opts = user_opts or {}
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 user_key = _NgConfigValues.keymaps or {}
local bufnr = lsp_attach_info.bufnr or 0 local bufnr = user_opts.bufnr or 0
local function del_keymap(mode, key, ...) local function del_keymap(...)
local ks = vim.api.nvim_buf_get_keymap(bufnr, mode) vim.api.nvim_buf_del_keymap(bufnr, ...)
if vim.tbl_contains(ks, key) then
vim.api.nvim_buf_del_keymap(bufnr, mode, key, ...)
end
end end
local function set_keymap(...) local function set_keymap(...)
vim.api.nvim_buf_set_keymap(bufnr, ...) vim.api.nvim_buf_set_keymap(bufnr, ...)
end end
-- local function buf_set_option(...) -- local function buf_set_option(...)
-- vim.api.nvim_buf_set_option(bufnr, ...) -- vim.api.nvim_buf_set_option(bufnr, ...)
-- end -- end
local doc_fmt, range_fmt, ccls = check_cap(lsp_attach_info) local doc_fmt, range_fmt, ccls = check_cap(user_opts.cap)
if ccls then if ccls then
vim.list_extend(key_maps, ccls_mappings) vim.list_extend(key_maps, ccls_mappings)
@ -167,10 +111,10 @@ local function set_mapping(lsp_attach_info)
if _NgConfigValues.default_mapping ~= false then if _NgConfigValues.default_mapping ~= false then
for _, v in pairs(user_key) do for _, v in pairs(user_key) do
trace('binding', v) trace("binding", v)
local exists = false local exists = false
for _, default in pairs(key_maps) do 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 if v.func == default.func and (not default.override) then
default.key, default.override, exists = v.key, true, true default.key, default.override, exists = v.key, true, true
break break
end end
@ -181,112 +125,84 @@ local function set_mapping(lsp_attach_info)
end end
else else
key_maps = _NgConfigValues.keymaps or {} key_maps = _NgConfigValues.keymaps or {}
log('setting maps to ', key_maps) log("setting maps to ", key_maps)
end end
local fmtkey, rfmtkey, nrfmtkey local fmtkey, rfmtkey
require('navigator.formatting')
for _, value in pairs(key_maps) do for _, value in pairs(key_maps) do
if value.doc then local f = "<Cmd>lua vim.lsp.buf." .. value.func .. "<CR>"
vim.notify('doc field no longer supported in navigator mapping, use desc instead') if string.find(value.func, "require") then
end f = "<Cmd>lua " .. value.func .. "<CR>"
if type(value.func) == 'string' then -- deprecated will remove when 0.8 is out elseif string.find(value.func, "diagnostic") then
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.' local diagnostic = '<Cmd>lua vim.'
diagnostic = '<Cmd>lua vim.' if vim.lsp.diagnostic ~= nil then
f = diagnostic .. value.func .. '<CR>' diagnostic = '<Cmd>lua vim.lsp.'
end
f = diagnostic .. value.func .. "<CR>"
elseif string.find(value.func, "vim.") then
f = "<Cmd>lua " .. value.func .. "<CR>"
end end
local k = value.key local k = value.key
local m = value.mode or 'n' local m = value.mode or "n"
if string.find(value.func, 'range_formatting') then if string.find(value.func, "range_formatting") then
rfmtkey = value.key rfmtkey = value.key
elseif string.find(value.func, 'format') then elseif string.find(value.func, "formatting") then
fmtkey = value.key fmtkey = value.key
end end
trace('binding', k, f) log("binding", k, f)
set_keymap(m, k, f, opts) set_keymap(m, k, f, opts)
end end
if type(value.func) == 'function' then -- new from 0.7.x
-- neovim 0.7.0
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
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
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
-- if user_opts.cap.document_formatting then -- if user_opts.cap.document_formatting then
if doc_fmt and _NgConfigValues.lsp.format_on_save then if doc_fmt and _NgConfigValues.lsp.format_on_save then
local gn = api.nvim_create_augroup('NavAuGroupFormat', {}) vim.cmd([[
aug NavigatorAuFormat
local fopts = _NgConfigValues.lsp.format_options au!
autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting()
if not fopts.async and vim.api.nvim_buf_line_count(0) > 4000 then aug END
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 elseif fmtkey then
del_keymap('n', fmtkey) del_keymap('n', fmtkey)
end end
if user_opts.cap.document_range_formatting then
if lsp_attach_info.cap and lsp_attach_info.cap.document_range_formatting then log("formatting enabled", user_opts.cap)
log('formatting enabled', lsp_attach_info.cap)
end end
if not range_fmt and rfmtkey then if not range_fmt and rfmtkey then
del_keymap('v', rfmtkey) del_keymap("v", rfmtkey)
end end
if not range_fmt and nrfmtkey then log("enable format ", doc_fmt, range_fmt)
del_keymap('n', nrfmtkey)
end end
log('enable format ', doc_fmt, range_fmt, _NgConfigValues.lsp.format_on_save)
local function autocmd(user_opts)
vim.api.nvim_exec([[
aug NavigatorDocHlAu
au!
au CmdlineLeave : lua require('navigator.dochighlight').cmd_nohl()
aug END
]], false)
end end
local function autocmd() local function set_event_handler(user_opts)
local gn = api.nvim_create_augroup('NavAuGroupDocHlAu', {}) 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,java"
-- 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!]]
api.nvim_create_autocmd({ 'BufWritePre' }, { for _, value in pairs(event_hdlrs) do
group = gn, local f = ""
callback = require('navigator.dochighlight').cmd_nohl, if string.find(value.func, "require") ~= nil then
}) f = "lua " .. value.func
else
f = "lua vim.lsp.buf." .. value.func
end
local cmd = "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " .. value.ev .. " <buffer> silent! " .. f
vim.api.nvim_command(cmd)
end
vim.api.nvim_command([[augroup END]])
end end
M.toggle_lspformat = function(on) M.toggle_lspformat = function(on)
@ -297,102 +213,68 @@ M.toggle_lspformat = function(on)
end end
if _NgConfigValues.lsp.format_on_save then if _NgConfigValues.lsp.format_on_save then
if on == nil then if on == nil then
vim.notify('format on save true', vim.lsp.log_levels.INFO) print("format on save true")
end end
vim.cmd([[set eventignore-=BufWritePre]]) vim.cmd([[set eventignore=""]])
else else
if on == nil then if on == nil then
vim.notify('format on save false', vim.lsp.log_levels.INFO) print("format on save false")
end
vim.cmd([[set eventignore+=BufWritePre]])
end end
vim.cmd([[set eventignore=BufWritePre]])
end end
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 end
attach_opts = attach_opts or { bufnr = 0, client = {}, cap = {} }
set_mapping(attach_opts)
set_cmds(attach_opts)
autocmd() function M.setup(user_opts)
user_opts = user_opts or _NgConfigValues
set_mapping(user_opts)
local client = attach_opts.client or {} autocmd(user_opts)
local cap = client.server_capabilities or vim.lsp.protocol.make_client_capabilities() set_event_handler(user_opts)
log('lsp cap:', cap.codeActionProvider) local cap = user_opts.cap or vim.lsp.protocol.make_client_capabilities()
log('lsp cap:', cap)
if cap.call_hierarchy or cap.callHierarchyProvider then if cap.call_hierarchy or cap.callHierarchy then
vim.lsp.handlers['callHierarchy/incomingCalls'] = require('navigator.hierarchy').incoming_calls_handler vim.lsp.handlers["callHierarchy/incomingCalls"] = require"navigator.hierarchy".incoming_calls_handler
vim.lsp.handlers['callHierarchy/outgoingCalls'] = require('navigator.hierarchy').outgoing_calls_handler vim.lsp.handlers["callHierarchy/outgoingCalls"] = require"navigator.hierarchy".outgoing_calls_handler
end end
vim.lsp.handlers['textDocument/references'] = require('navigator.reference').reference_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/codeAction"] = require"navigator.codeAction".code_action_handler
vim.lsp.handlers['textDocument/definition'] = require('navigator.definition').definition_handler vim.lsp.handlers["textDocument/definition"] = require"navigator.definition".definition_handler
if cap.declarationProvider then if cap.declaration then
vim.lsp.handlers['textDocument/declaration'] = require('navigator.definition').declaration_handler vim.lsp.handlers["textDocument/declaration"] = require"navigator.definition".declaration_handler
end end
vim.lsp.handlers['textDocument/typeDefinition'] = require('navigator.definition').typeDefinition_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/implementation"] = require"navigator.implementation".implementation_handler
-- vim.lsp.handlers['textDocument/documentSymbol'] = require('navigator.symbols').document_symbol_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["workspace/symbol"] = require"navigator.symbols".workspace_symbol_handler
vim.lsp.handlers['textDocument/publishDiagnostics'] = require('navigator.diagnostics').diagnostic_handler vim.lsp.handlers["textDocument/publishDiagnostics"] = require"navigator.diagnostics".diagnostic_handler
-- TODO: when active signature merge to neovim, remove this setup: -- TODO: when active signature merge to neovim, remove this setup:
if if _NgConfigValues.signature_help_cfg then
_NgConfigValues.signature_help_cfg and #_NgConfigValues.signature_help_cfg > 0 or _NgConfigValues.lsp_signature_help log("setup signature from navigator")
then local hassig, sig = pcall(require, "lsp_signature")
log('setup signature from navigator')
local hassig, sig = pcall(require, 'lsp_signature')
if hassig then if hassig then
sig.setup(_NgConfigValues.signature_help_cfg or {}) sig.setup(_NgConfigValues.signature_help_cfg)
end end
else else
vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(require('navigator.signature').signature_handler, { vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(require"navigator.signature".signature_handler, {
border = { '', '', '', '', '', '', '', '' }, border = {"", "", "", "", "", "", "", ""}
}) })
end end
api.nvim_create_autocmd({ 'BufWritePre' }, { vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {border = single})
group = api.nvim_create_augroup('nvim_nv_event_autos', {}), if cap.document_formatting then
buffer = attach_opts.bufnr, log("formatting enabled setup hdl")
callback = function() vim.lsp.handlers["textDocument/formatting"] = require"navigator.formatting".format_hdl
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
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 end
return M return M

@ -1,47 +0,0 @@
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',
}

@ -1,89 +0,0 @@
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,35 +1,36 @@
local M = {} local M = {}
local util = require('navigator.util') local util = require "navigator.util"
local nvim_0_6 = util.nvim_0_6()
local gutil = require('guihua.util') local gutil = require "guihua.util"
local lsp = require('vim.lsp') local lsp = require "vim.lsp"
local api = vim.api local api = vim.api
local log = require('navigator.util').log local log = require"navigator.util".log
local lerr = require('navigator.util').error local lerr = require"navigator.util".error
local trace = require('navigator.util').trace local trace = require"navigator.util".trace
local symbol_kind = require('navigator.lspclient.lspkind').symbol_kind local symbol_kind = require"navigator.lspclient.lspkind".symbol_kind
local cwd = vim.loop.cwd() local cwd = vim.loop.cwd()
local is_win = vim.loop.os_uname().sysname:find('Windows') local is_win = vim.loop.os_uname().sysname:find("Windows")
local path_sep = require('navigator.util').path_sep() local path_sep = require"navigator.util".path_sep()
local path_cur = require('navigator.util').path_cur() local path_cur = require"navigator.util".path_cur()
cwd = gutil.add_pec(cwd) cwd = gutil.add_pec(cwd)
local ts_nodes = require('navigator.lru').new(1000, 1024 * 1024) local ts_nodes = require('navigator.lru').new(1000, 1024 * 1024)
local ts_nodes_time = require('navigator.lru').new(1000) local ts_nodes_time = require('navigator.lru').new(1000)
local TS_analysis_enabled = require('navigator').config_values().treesitter_analysis local TS_analysis_enabled = require"navigator".config_values().treesitter_analysis
local nts = require('navigator.treesitter')
-- extract symbol from range -- extract symbol from range
function M.get_symbol(text, range) function M.get_symbol(text, range)
if range == nil then if range == nil then
return '' return ""
end end
return string.sub(text, range.start.character + 1, range['end'].character) return string.sub(text, range.start.character + 1, range['end'].character)
end end
local function check_lhs(text, symbol) 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 s = find(text, symbol)
local eq = string.find(text, '=') or 0 local eq = string.find(text, '=') or 0
local eq2 = string.find(text, '==') or 0 local eq2 = string.find(text, '==') or 0
@ -39,7 +40,7 @@ local function check_lhs(text, symbol)
return false return false
end end
if s < eq and eq ~= eq2 then if s < eq and eq ~= eq2 then
trace(symbol, 'modified') trace(symbol, "modified")
end end
if eq == eq3 + 1 then if eq == eq3 + 1 then
return false return false
@ -53,20 +54,18 @@ local function check_lhs(text, symbol)
end end
function M.lines_from_locations(locations, include_filename) function M.lines_from_locations(locations, include_filename)
local fnamemodify = function(filename) local fnamemodify = (function(filename)
if include_filename then if include_filename then
return vim.fn.fnamemodify(filename, ':~:.') .. ':' return vim.fn.fnamemodify(filename, ":~:.") .. ":"
else else
return '' return ""
end
end end
end)
local lines = {} local lines = {}
for _, loc in ipairs(locations) do for _, loc in ipairs(locations) do
table.insert( table.insert(lines,
lines, (fnamemodify(loc["filename"]) .. loc["lnum"] .. ":" .. loc["col"] .. ": " .. vim.trim(loc["text"])))
(fnamemodify(loc['filename']) .. loc['lnum'] .. ':' .. loc['col'] .. ': ' .. vim.trim(loc['text']))
)
end end
return lines return lines
@ -74,8 +73,7 @@ end
function M.symbols_to_items(result) function M.symbols_to_items(result)
local locations = {} local locations = {}
result = result or {} -- log(result)
log(#result)
for i = 1, #result do for i = 1, #result do
local item = result[i].location local item = result[i].location
if item ~= nil and item.range ~= nil then if item ~= nil and item.range ~= nil then
@ -85,23 +83,23 @@ function M.symbols_to_items(result)
item.name = result[i].name -- symbol name item.name = result[i].name -- symbol name
item.text = result[i].name item.text = result[i].name
if kind ~= nil then if kind ~= nil then
item.text = kind .. ': ' .. item.text item.text = kind .. ": " .. item.text
end end
if not item.filename then
item.filename = vim.uri_to_fname(item.uri) item.filename = vim.uri_to_fname(item.uri)
end
item.display_filename = item.filename:gsub(cwd .. path_sep, path_cur, 1) item.display_filename = item.filename:gsub(cwd .. path_sep, path_cur, 1)
if item.range == nil or item.range.start == nil then 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 end
item.lnum = item.range.start.line + 1 item.lnum = item.range.start.line + 1
if item.containerName ~= nil then if item.containerName ~= nil then
item.text = '' .. item.containerName .. item.text item.text = "" .. item.containerName .. item.text
end end
table.insert(locations, item) table.insert(locations, item)
end end
end end
-- local items = locations_to_items(locations)
-- log(locations[1]) -- log(locations[1])
return locations return locations
end end
@ -123,8 +121,7 @@ function M.check_capabilities(feature, client_id)
local supported_client = false local supported_client = false
for _, client in pairs(clients) do for _, client in pairs(clients) do
-- supported_client = client.resolved_capabilities[feature] supported_client = client.resolved_capabilities[feature]
supported_client = client.server_capabilities[feature]
if supported_client then if supported_client then
break break
end end
@ -134,9 +131,9 @@ function M.check_capabilities(feature, client_id)
return true return true
else else
if #clients == 0 then if #clients == 0 then
log('LSP: no client attached') log("LSP: no client attached")
else else
trace('LSP: server does not support ' .. feature) trace("LSP: server does not support " .. feature)
end end
return false return false
end end
@ -145,37 +142,39 @@ end
function M.call_sync(method, params, opts, handler) function M.call_sync(method, params, opts, handler)
params = params or {} params = params or {}
opts = opts or {} opts = opts or {}
log(method, params) local results_lsp, err = lsp.buf_request_sync(0, method, params, opts.timeout or vim.g.navtator_timeout or 1000)
local results_lsp, err = lsp.buf_request_sync(opts.bufnr or 0, method, params, opts.timeout or 1000)
return handler(err, extract_result(results_lsp), { method = method, no_show = opts.no_show }, nil) if nvim_0_6() then
handler(err, extract_result(results_lsp), {method = method}, nil)
else
handler(err, method, extract_result(results_lsp), nil, nil)
end
end end
function M.call_async(method, params, handler, bufnr) function M.call_async(method, params, handler)
params = params or {} params = params or {}
local callback = function(...) local callback = function(...)
util.show(...) util.show(...)
handler(...) handler(...)
end end
bufnr = bufnr or 0 return lsp.buf_request(0, method, params, callback)
return lsp.buf_request(bufnr, method, params, callback)
-- results_lsp, canceller -- results_lsp, canceller
end end
local function ts_functions(uri, optional) local function ts_functions(uri)
local unload_bufnr local unload_bufnr
local ts_enabled, _ = pcall(require, 'nvim-treesitter.locals') local ts_enabled, _ = pcall(require, "nvim-treesitter.locals")
if not ts_enabled or not TS_analysis_enabled then if not ts_enabled or not TS_analysis_enabled then
lerr('ts not enabled') lerr("ts not enabled")
return nil return nil
end end
local ts_func = nts.buf_func local ts_func = require"navigator.treesitter".buf_func
local bufnr = vim.uri_to_bufnr(uri) local bufnr = vim.uri_to_bufnr(uri)
local x = os.clock() local x = os.clock()
trace(ts_nodes) trace(ts_nodes)
local tsnodes = ts_nodes:get(uri) local tsnodes = ts_nodes:get(uri)
if tsnodes ~= nil then if tsnodes ~= nil then
trace('get data from cache') trace("get data from cache")
local t = ts_nodes_time:get(uri) or 0 local t = ts_nodes_time:get(uri) or 0
local fname = vim.uri_to_fname(uri) local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname) local modified = vim.fn.getftime(fname)
@ -187,12 +186,9 @@ local function ts_functions(uri, optional)
ts_nodes_time:delete(uri) ts_nodes_time:delete(uri)
end end
end end
if optional then
return
end
local unload = false local unload = false
if not api.nvim_buf_is_loaded(bufnr) then if not api.nvim_buf_is_loaded(bufnr) then
trace('! load buf !', uri, bufnr) trace("! load buf !", uri, bufnr)
vim.fn.bufload(bufnr) vim.fn.bufload(bufnr)
-- vim.api.nvim_buf_detach(bufnr) -- if user opens the buffer later, it prevents user attach event -- vim.api.nvim_buf_detach(bufnr) -- if user opens the buffer later, it prevents user attach event
unload = true unload = true
@ -205,38 +201,35 @@ local function ts_functions(uri, optional)
ts_nodes:set(uri, funcs) ts_nodes:set(uri, funcs)
ts_nodes_time:set(uri, os.time()) ts_nodes_time:set(uri, os.time())
trace(funcs, ts_nodes:get(uri)) trace(funcs, ts_nodes:get(uri))
trace(string.format('elapsed time: %.4f\n', os.clock() - x)) -- how long it tooks trace(string.format("elapsed time: %.4f\n", os.clock() - x)) -- how long it tooks
return funcs, unload_bufnr return funcs, unload_bufnr
end end
local function ts_definition(uri, range, optional) local function ts_definition(uri, range)
local unload_bufnr local unload_bufnr
local ts_enabled, _ = pcall(require, 'nvim-treesitter.locals') local ts_enabled, _ = pcall(require, "nvim-treesitter.locals")
if not ts_enabled or not TS_analysis_enabled then if not ts_enabled or not TS_analysis_enabled then
lerr('ts not enabled') lerr("ts not enabled")
return nil return nil
end end
local key = string.format('%s_%d_%d_%d', uri, range.start.line, range.start.character, range['end'].line) 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 tsnode = ts_nodes:get(key)
local ftime = ts_nodes_time:get(key) local ftime = ts_nodes_time:get(key)
local fname = vim.uri_to_fname(uri) local fname = vim.uri_to_fname(uri)
local modified = vim.fn.getftime(fname) local modified = vim.fn.getftime(fname)
if tsnodes and modified <= ftime then if tsnodes and modified <= ftime then
log('ts def from cache') log('ts def from cache')
return tsnodes return tsnode
end end
if optional then local ts_def = require"navigator.treesitter".find_definition
return
end
local ts_def = nts.find_definition
local bufnr = vim.uri_to_bufnr(uri) local bufnr = vim.uri_to_bufnr(uri)
local x = os.clock() local x = os.clock()
trace(ts_nodes) trace(ts_nodes)
local unload = false local unload = false
if not api.nvim_buf_is_loaded(bufnr) then if not api.nvim_buf_is_loaded(bufnr) then
log('! load buf !', uri, bufnr) log("! load buf !", uri, bufnr)
vim.fn.bufload(bufnr) vim.fn.bufload(bufnr)
unload = true unload = true
end end
@ -245,14 +238,13 @@ local function ts_definition(uri, range, optional)
if unload then if unload then
unload_bufnr = bufnr unload_bufnr = bufnr
end end
trace(string.format(' ts def elapsed time: %.4f\n', os.clock() - x), def_range) -- how long it takes 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:set(key, def_range)
ts_nodes_time:set(key, x) ts_nodes_time:set(key, x)
return def_range, unload_bufnr return def_range, unload_bufnr
end end
local function find_ts_func_by_range(funcs, range) local function find_ts_func_by_range(funcs, range)
log(funcs, range)
if funcs == nil or range == nil then if funcs == nil or range == nil then
return nil return nil
end end
@ -270,10 +262,6 @@ end
local function order_locations(locations) local function order_locations(locations)
table.sort(locations, function(i, j) 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.uri == j.uri then
if i.range and i.range.start then if i.range and i.range.start then
return i.range.start.line < j.range.start.line return i.range.start.line < j.range.start.line
@ -291,7 +279,7 @@ local function slice_locations(locations, max_items)
if #locations > max_items then if #locations > max_items then
local uri = locations[max_items] local uri = locations[max_items]
for i = max_items + 1, #locations do for i = max_items + 1, #locations do
if uri ~= locations[i] then if uri ~= locations[i] and not brk then
cut = i cut = i
break break
end end
@ -303,43 +291,31 @@ local function slice_locations(locations, max_items)
second_part = vim.list_slice(locations, cut + 1, #locations) second_part = vim.list_slice(locations, cut + 1, #locations)
end end
return first_part, second_part return first_part, second_part
end end
-- local function test_locations() local function test_locations()
-- local locations = { local locations = {
-- { uri = '1', range = { start = { line = 1 } } }, {uri = '1', range = {start = {line = 1}}}, {uri = '2', range = {start = {line = 2}}},
-- { uri = '2', range = { start = { line = 2 } } }, {uri = '2', range = {start = {line = 3}}}, {uri = '1', range = {start = {line = 3}}},
-- { uri = '2', range = { start = { line = 3 } } }, {uri = '1', range = {start = {line = 4}}}, {uri = '3', range = {start = {line = 4}}},
-- { uri = '1', range = { start = { line = 3 } } }, {uri = '3', range = {start = {line = 4}}}
-- { uri = '1', range = { start = { line = 4 } } }, }
-- { uri = '3', range = { start = { line = 4 } } }, local second_part
-- { uri = '3', range = { start = { line = 4 } } }, order_locations(locations)
-- } local locations, second_part = slice_locations(locations, 3)
-- local second_part log(locations, second_part)
-- order_locations(locations) end
-- local locations, second_part = slice_locations(locations, 3)
-- log(locations, second_part) function M.locations_to_items(locations, max_items)
-- end max_items = max_items or 100000 --
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 if not locations or vim.tbl_isempty(locations) then
vim.notify('list not avalible', vim.lsp.log_levels.WARN) print("list not avalible")
return return
end end
local width = 4 local width = 4
local items = {} local items = {} -- lsp.util.locations_to_items(locations)
-- items and locations may not matching -- items and locations may not matching
local uri_def = {} local uri_def = {}
@ -349,54 +325,41 @@ function M.locations_to_items(locations, ctx)
locations, second_part = slice_locations(locations, max_items) locations, second_part = slice_locations(locations, max_items)
trace(locations) trace(locations)
vim.cmd([[set eventignore+=FileType]]) local cut = -1
local unload_bufnrs = {} local unload_bufnrs = {}
for i, loc in ipairs(locations) do for i, loc in ipairs(locations) do
local item = lsp.util.locations_to_items({ loc }, enc)[1] local funcs = nil
local item = lsp.util.locations_to_items({loc})[1]
-- log(item)
item.range = locations[i].range or locations[i].targetRange item.range = locations[i].range or locations[i].targetRange
item.uri = locations[i].uri or locations[i].targetUri item.uri = locations[i].uri or locations[i].targetUri
item.definition = locations[i].definition
if is_win then if is_win then
log(item.uri) -- file:///C:/path/to/file log(item.uri) -- file:///C:/path/to/file
log(cwd) log(cwd)
end end
-- only load top 30 file. -- only load top 30 file.
local proj_file = item.uri:find(cwd) or is_win or i < _NgConfigValues.treesitter_analysis_max_num local proj_file = item.uri:find(cwd) or is_win or i < 30
local unload, def local unload, def
local context = '' if TS_analysis_enabled and proj_file then
if TS_analysis_enabled and proj_file and not ctx.no_show then funcs, unload = ts_functions(item.uri)
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)
-- TODO: unload buffers
if unload then if unload then
table.insert(unload_bufnrs, unload) table.insert(unload_bufnrs, unload)
unload = nil
end end
if not uri_def[item.uri] then if not uri_def[item.uri] then
-- find def in file -- find def in file
def, unload = ts_definition(item.uri, item.range, ts_optional(i, #unload_bufnrs)) def, unload = ts_definition(item.uri, item.range)
if def and def.start then if def and def.start then
uri_def[item.uri] = def uri_def[item.uri] = def
if def.start then -- find for the 1st time if def.start then -- find for the 1st time
for j = 1, #items do 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 if items[j].uri == item.uri and items[j].range.start.line == def.start.line then
items[j].definition = true items[j].definition = true
end end
end end
end end
end
else else
if uri_def[item.uri] == false then 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 uri_def[item.uri] = {} -- no def in file, TODO: it is tricky the definition is in another file and it is the
@ -410,25 +373,19 @@ function M.locations_to_items(locations, ctx)
end end
end end
trace(uri_def[item.uri], item.range) -- set to log if need to get all in rnge trace(uri_def[item.uri], item.range) -- set to log if need to get all in rnge
local def1 = uri_def[item.uri] local def = uri_def[item.uri]
if def1 and def1.start and item.range then if def and def.start and item.range then
if def1.start.line == item.range.start.line then if def.start.line == item.range.start.line then
log('ts def in current line') log("ts def in current line")
item.definition = true item.definition = true
end end
end end
end end
item.filename = assert(vim.uri_to_fname(item.uri)) item.filename = assert(vim.uri_to_fname(item.uri))
local filename = item.filename:gsub(cwd .. path_sep, path_cur, 1) 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.display_filename = filename or item.filename
item.call_by = context -- find_ts_func_by_range(funcs, item.range) item.call_by = find_ts_func_by_range(funcs, item.range)
item.rpath = util.get_relative_path(cwd, item.filename) item.rpath = util.get_relative_path(cwd, item.filename)
width = math.max(width, #item.text) width = math.max(width, #item.text)
item.symbol_name = M.get_symbol(item.text, item.range) item.symbol_name = M.get_symbol(item.text, item.range)
@ -449,27 +406,90 @@ function M.locations_to_items(locations, ctx)
end, 100) end, 100)
end end
vim.cmd([[set eventignore-=FileType]]) return items, width + 24, second_part -- TODO handle long line?
end
function M.apply_action(action, ctx, client)
assert(action ~= nil, "action must not be nil")
if action.edit then
vim.lsp.util.apply_workspace_edit(action.edit)
end
if action.command then
local command = type(action.command) == 'table' and action.command or action
local fn = client.commands[command.command] or (vim.lsp.commands and vim.lsp.commands[command.command])
if fn then
local enriched_ctx = vim.deepcopy(ctx)
enriched_ctx.client_id = client.id
fn(command, enriched_ctx)
else
M.execute_command(command)
end
end
log(action)
end
trace(items) local function apply_action(action, client, ctx)
return items, width + 30, second_part -- TODO handle long line? log(action, client)
if action.edit then
require('vim.lsp.util').apply_workspace_edit(action.edit)
end
if action.command then
local command = type(action.command) == 'table' and action.command or action
local fn = vim.lsp.commands and vim.lsp.commands[command.command]
if fn then
local enriched_ctx = vim.deepcopy(ctx)
enriched_ctx.client_id = client.id
fn(command, ctx)
else
require('vim.lsp.buf').execute_command(command)
end
end
end
function M.on_user_choice(action_tuple, ctx)
if not action_tuple then
return
end
log(action_tuple)
-- textDocument/codeAction can return either Command[] or CodeAction[]
--
-- CodeAction
-- ...
-- edit?: WorkspaceEdit -- <- must be applied before command
-- command?: Command
--
-- Command:
-- title: string
-- command: string
-- arguments?: any[]
--
local client = vim.lsp.get_client_by_id(action_tuple[1])
local action = action_tuple[2]
if not action.edit and client and type(client.resolved_capabilities.code_action) == 'table'
and client.resolved_capabilities.code_action.resolveProvider then
client.request('codeAction/resolve', action, function(err, resolved_action)
if err then
vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR)
return
end
apply_action(resolved_action, client, ctx)
end)
else
apply_action(action, client, ctx)
end
end end
function M.symbol_to_items(locations) function M.symbol_to_items(locations)
if not locations or vim.tbl_isempty(locations) then if not locations or vim.tbl_isempty(locations) then
vim.notify('list not avalible', vim.lsp.log_levels.WARN) print("list not avalible")
return return
end end
local items = {} local items = {} -- lsp.util.locations_to_items(locations)
-- items and locations may not matching -- items and locations may not matching
table.sort(locations, function(i, j) table.sort(locations, function(i, j)
if i.definition then
return true
end
if j.definition then
return false
end
if i.uri == j.uri then if i.uri == j.uri then
if i.range and i.range.start then if i.range and i.range.start then
return i.range.start.line < j.range.start.line return i.range.start.line < j.range.start.line
@ -480,7 +500,7 @@ function M.symbol_to_items(locations)
end end
end) end)
for i, _ in ipairs(locations) do for i, _ in ipairs(locations) do
local item = {} local item = {} -- lsp.util.locations_to_items({loc})[1]
item.uri = locations[i].uri item.uri = locations[i].uri
item.range = locations[i].range item.range = locations[i].range
item.filename = assert(vim.uri_to_fname(item.uri)) item.filename = assert(vim.uri_to_fname(item.uri))
@ -497,9 +517,10 @@ end
function M.request(method, hdlr) -- e.g textDocument/reference function M.request(method, hdlr) -- e.g textDocument/reference
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local ref_params = vim.lsp.util.make_position_params() local ref_params = vim.lsp.util.make_position_params()
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _) vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr)
client.request(method, ref_params, hdlr, bufnr) client.request(method, ref_params, hdlr, bufnr)
end) end)
end end
return M return M

@ -1,9 +1,11 @@
local util = require('navigator.util') local util = require "navigator.util"
local mk_handler = util.mk_handler
local log = util.log local log = util.log
local lsphelper = require('navigator.lspwrapper') local lsphelper = require "navigator.lspwrapper"
local gui = require('navigator.gui') local gui = require "navigator.gui"
local lsp = require('navigator.lspwrapper') local lsp = require "navigator.lspwrapper"
local trace = require('navigator.util').trace local trace = require"navigator.util".trace
ListViewCtrl = ListViewCtrl or require('guihua.listviewctrl').ListViewCtrl
-- local partial = util.partial -- local partial = util.partial
-- local cwd = vim.loop.cwd() -- local cwd = vim.loop.cwd()
-- local lsphelper = require "navigator.lspwrapper" -- local lsphelper = require "navigator.lspwrapper"
@ -11,125 +13,66 @@ local locations_to_items = lsphelper.locations_to_items
local M = {} local M = {}
local ref_view = function(err, locations, ctx, cfg) local ref_view = function(err, locations, ctx, cfg)
cfg = cfg or {}
local truncate = cfg and cfg.truncate or 20 local truncate = cfg and cfg.truncate or 20
local opts = {} local opts = {}
trace('arg1', err, ctx, 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) 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("num", num)
-- log("bfnr", bufnr) -- log("bfnr", bufnr)
if err ~= nil then if err ~= nil then
vim.notify( print('lsp ref callback error', err, ctx, vim.inspect(locations))
'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)) log('ref callback error, lsp may not ready', err, ctx, vim.inspect(locations))
return return
end end
if type(locations) ~= 'table' then if type(locations) ~= 'table' then
log(locations) log(locations)
log('ctx', ctx) log("ctx", ctx)
vim.notify('incorrect setup' .. vim.inspect(locations), vim.lsp.log_levels.WARN) print("incorrect setup", locations)
return return
end end
if locations == nil or vim.tbl_isempty(locations) then if locations == nil or vim.tbl_isempty(locations) then
vim.notify('References not found', vim.lsp.log_levels.INFO) print "References not found"
return return
end end
ctx.max_items = truncate local items, width, second_part = locations_to_items(locations, truncate)
local items, width, second_part = locations_to_items(locations, ctx)
local thread_items = vim.deepcopy(items) local thread_items = vim.deepcopy(items)
log('splits: ', #items, #second_part) log("splits: ", #items, #second_part)
local ft = vim.api.nvim_buf_get_option(ctx.bufnr or 0, 'ft') local ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft")
local wwidth = vim.api.nvim_get_option('columns') local wwidth = vim.api.nvim_get_option("columns")
local mwidth = _NgConfigValues.width local mwidth = _NgConfigValues.width
width = math.min(width + 30, math.floor(wwidth * mwidth)) width = math.min(width + 30, 120, math.floor(wwidth * mwidth))
-- log(items) -- log(items)
-- log(width) -- log(width)
opts = { opts = {
total = #locations, total = #locations,
items = items, items = items,
rawdata = false,
ft = ft, ft = ft,
width = width, width = width,
api = 'Reference', api = "Reference",
enable_preview_edit = true, enable_preview_edit = true
} }
local listview local listview = gui.new_list_view(opts)
if not ctx.no_show then
listview = gui.new_list_view(opts)
if listview == nil then trace("update items", listview.ctrl.class)
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 local nv_ref_async
nv_ref_async = vim.loop.new_async(vim.schedule_wrap(function() nv_ref_async = vim.loop.new_async(vim.schedule_wrap(function()
log('$$$$$$$$ seperate thread... $$$$$$$$') log('$$$$$$$$ seperate thread... $$$$$$$$')
if vim.tbl_isempty(second_part) then if vim.tbl_isempty(second_part) then
return return
end end
ctx.max_items = #second_part local items2 = locations_to_items(second_part)
local items2 = locations_to_items(second_part, ctx)
vim.list_extend(thread_items, items2) vim.list_extend(thread_items, items2)
local data = require('navigator.render').prepare_for_render(thread_items, opts) local data = require"navigator.render".prepare_for_render(thread_items, opts)
log('thread data size', #data)
listview.ctrl:on_data_update(data) listview.ctrl:on_data_update(data)
if nv_ref_async then if nv_ref_async then
vim.loop.close(nv_ref_async) vim.loop.close(nv_ref_async)
else else
log('invalid asy', nv_ref_async) log("invalid asy", nv_ref_async)
end end
end)) end))
@ -137,62 +80,28 @@ local ref_view = function(err, locations, ctx, cfg)
vim.loop.new_thread(function(asy) vim.loop.new_thread(function(asy)
asy:send() asy:send()
end, nv_ref_async) end, nv_ref_async)
end, 100) end, 100)
return listview, items, width return listview, items, width
end end
local ref_hdlr = function(err, locations, ctx, cfg) local ref_hdlr = mk_handler(function(err, locations, ctx, cfg)
_NgConfigValues.closer = nil _NgConfigValues.closer = nil
trace(err, locations, ctx, cfg) 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() M.async_hdlr = vim.loop.new_async(vim.schedule_wrap(function()
ref_view(err, locations, ctx, cfg) ref_view(err, locations, ctx, cfg)
if M.async_hdlr:is_active() then
M.async_hdlr:close() M.async_hdlr:close()
end
end)) end))
M.async_hdlr:send() M.async_hdlr:send()
end 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 } -- local async_reference_request = function()
lsp.call_async('textDocument/references', ref_params, function(err, result, ctx, config) -- local ref_params = vim.lsp.util.make_position_params()
if err ~= nil or result == nil then -- ref_params.context = {includeDeclaration = true}
log('failed to get ref', err, result, ctx, config) -- -- lsp.call_async("textDocument/references", ref_params, ref_hdlr) -- return asyncresult, canceller
result = {} -- lsp.call_async("textDocument/references", ref_params, ref_hdlr) -- return asyncresult, canceller
end -- 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() local ref_req = function()
if _NgConfigValues.closer ~= nil then if _NgConfigValues.closer ~= nil then
@ -203,8 +112,8 @@ local ref_req = function()
ref_params.context = {includeDeclaration = true} ref_params.context = {includeDeclaration = true}
-- lsp.call_async("textDocument/references", ref_params, ref_hdlr) -- return asyncresult, canceller -- lsp.call_async("textDocument/references", ref_params, ref_hdlr) -- return asyncresult, canceller
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
log('bufnr', bufnr) log(bufnr)
local ids, closer = vim.lsp.buf_request(bufnr, 'textDocument/references', ref_params, ref_hdlr) local ids, closer = vim.lsp.buf_request(bufnr, "textDocument/references", ref_params, ref_hdlr)
log(ids) log(ids)
_NgConfigValues.closer = closer _NgConfigValues.closer = closer
@ -215,46 +124,12 @@ local ref = function()
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
local ref_params = vim.lsp.util.make_position_params() local ref_params = vim.lsp.util.make_position_params()
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _) vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, bufnr)
if client.server_capabilities.referencesProvider then if client.resolved_capabilities.find_references then
client.request('textDocument/references', ref_params, ref_hdlr, bufnr) client.request("textDocument/references", ref_params, ref_hdlr, bufnr)
end end
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 end
return { return {reference_handler = ref_hdlr, reference = ref_req, ref_view = ref_view}
side_panel = side_panel,
reference_handler = ref_hdlr,
reference = ref_req,
ref_view = ref_view,
async_ref = async_ref,
all_ref = ref,
}

@ -1,16 +1,44 @@
-- https://github.com/lukas-reineke/dotfiles/blob/master/vim/lua/lsp/rename.lua -- https://github.com/lukas-reineke/dotfiles/blob/master/vim/lua/lsp/rename.lua
local M = {} local M = {}
local util = require('navigator.util') local util = require "navigator.util"
-- local rename_prompt = 'Rename -> ' local rename_prompt = "Rename -> "
M.rename = function() M.rename = function()
local input = vim.ui.input local current_name = vim.fn.expand("<cword>")
local bufnr = vim.api.nvim_create_buf(false, true)
vim.ui.input = require('guihua.floating').input vim.api.nvim_buf_set_option(bufnr, "buftype", "prompt")
vim.lsp.buf.rename() vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe")
vim.defer_fn(function() vim.api.nvim_buf_add_highlight(bufnr, -1, "NGPreviewTitle", 0, 0, #rename_prompt)
vim.ui.input = input vim.fn.prompt_setprompt(bufnr, rename_prompt)
end, 1000) 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")
vim.api.nvim_buf_set_option(bufnr, "filetype", "guihua")
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({"n", "i"}, "<BS>", [[<ESC>"_cl]], {silent = true, buffer = true})
vim.cmd(string.format("normal i%s", current_name))
end end
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)
end
-- M.callback()
return M return M

@ -1,18 +1,16 @@
local util = require('navigator.util') local log = require"guihua.log".info
local log = util.log local trace = require"guihua.log".trace
local trace = util.trace
local clone = require('guihua.util').clone
local M = {} local M = {}
local clone = require'guihua.util'.clone
local function filename(url) local function filename(url)
if url == nil then if url == nil then
return '' return ''
end end
return url:match('^.+/(.+)$') or url return url:match("^.+/(.+)$") or url
end end
local function extension(url) local function extension(url)
local ext = url:match('^.+(%..+)$') or 'txt' local ext = url:match("^.+(%..+)$") or "txt"
return string.sub(ext, 2) return string.sub(ext, 2)
end end
@ -40,6 +38,7 @@ local function get_pads(win_width, text, postfix)
i = i + rem * 10 i = i + rem * 10
-- log(i) -- log(i)
end end
end end
if i > 3 then if i > 3 then
@ -53,7 +52,7 @@ end
function M.prepare_for_render(items, opts) function M.prepare_for_render(items, opts)
opts = opts or {} opts = opts or {}
if items == nil or #items < 1 then if items == nil or #items < 1 then
vim.notify('no item found or empty fields', vim.lsp.log_levels.INFO) print("no item found or empty fields")
return return
end end
local item = clone(items[1]) local item = clone(items[1])
@ -61,24 +60,32 @@ function M.prepare_for_render(items, opts)
local last_summary_idx = 1 local last_summary_idx = 1
local total_ref_in_file = 1 local total_ref_in_file = 1
local total = opts.total local total = opts.total
local icon = '' local icon = ""
local lspapi = opts.api or '' local lspapi = opts.api or ""
local ok, devicons = pcall(require, 'nvim-web-devicons') local ok, devicons = pcall(require, "nvim-web-devicons")
if ok then if ok then
local fn = filename(items[1].filename) local fn = filename(items[1].filename)
local ext = extension(fn) local ext = extension(fn)
icon = devicons.get_icon(fn, ext) or icon icon = devicons.get_icon(fn, ext) or icon
end end
-- local call_by_presented = false local call_by_presented = false
opts.width = opts.width or math.floor(vim.api.nvim_get_option('columns') * 0.8) local width = 100
opts.width = opts.width or width
local win_width = opts.width -- buf local win_width = opts.width -- buf
for i = 1, #items do
if items[i].call_by and #items[i].call_by > 0 then
call_by_presented = true
end
end
-- log(items[1])
for i = 1, #items do for i = 1, #items do
local space local space
local trim local trim
local lspapi_display = lspapi 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 fn = display_items[last_summary_idx].filename
local dfn = items[i].display_filename local dfn = items[i].display_filename
@ -91,23 +98,18 @@ function M.prepare_for_render(items, opts)
display_items[last_summary_idx].filename_only = true display_items[last_summary_idx].filename_only = true
-- trace(items[i], items[i].filename, last_summary_idx, display_items[last_summary_idx].filename) -- trace(items[i], items[i].filename, last_summary_idx, display_items[last_summary_idx].filename)
-- TODO refact display_filename generate part -- TODO refact display_filename generate part
if items[i].filename == fn or opts.hide_filename then if items[i].filename == fn then
space, trim = get_pads(opts.width, icon .. ' ' .. dfn, lspapi_display .. ' 14 of 33 ') 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 if trim and opts.width > 50 and #dfn > opts.width - 20 then
local fn1 = string.sub(dfn, 1, opts.width - 50) local fn1 = string.sub(dfn, 1, opts.width - 50)
local fn2 = string.sub(dfn, #dfn - 10, #dfn) 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 = ' ' space = ' '
-- log("trim", fn1, fn2) -- log("trim", fn1, fn2)
end end
local api_disp = string.format( local api_disp = string.format("%s %s%s%s %i", icon,
'%s %s%s%s %i', display_items[last_summary_idx].display_filename, space,
icon, lspapi_display, total_ref_in_file)
display_items[last_summary_idx].display_filename,
space,
lspapi_display,
total_ref_in_file
)
if total then if total then
api_disp = api_disp .. ' of: ' .. tostring(total) api_disp = api_disp .. ' of: ' .. tostring(total)
@ -116,17 +118,20 @@ function M.prepare_for_render(items, opts)
display_items[last_summary_idx].text = api_disp display_items[last_summary_idx].text = api_disp
total_ref_in_file = total_ref_in_file + 1 total_ref_in_file = total_ref_in_file + 1
else else
lspapi_display = lspapi lspapi_display = lspapi
item = clone(items[i]) item = clone(items[i])
space, trim = get_pads(opts.width, icon .. ' ' .. item.display_filename, lspapi_display .. ' 12 of 33') space, trim = get_pads(opts.width, icon .. ' ' .. item.display_filename,
lspapi_display .. ' 12 of 33')
if trim and opts.width > 52 and #item.display_filename > opts.width - 20 then 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) item.display_filename = string.sub(item.display_filename, 1, opts.width - 52) .. ""
.. '' .. string.sub(item.display_filename,
.. string.sub(item.display_filename, #item.display_filename - 10, #item.display_filename) #item.display_filename - 10,
#item.display_filename)
space = ' ' space = ' '
end 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) trace(item.text)
table.insert(display_items, item) table.insert(display_items, item)
@ -135,48 +140,58 @@ function M.prepare_for_render(items, opts)
end end
-- content of code lines -- content of code lines
item = clone(items[i]) item = clone(items[i])
item.text = require('navigator.util').trim_and_pad(item.text) item.text = require'navigator.util'.trim_and_pad(item.text)
item.text = string.format('%4i: %s', item.lnum, item.text) item.text = string.format("%4i: %s", item.lnum, item.text)
local ts_report = '' local ts_report = ""
if item.lhs then if item.lhs then
ts_report = _NgConfigValues.icons.value_changed ts_report = _NgConfigValues.icons.value_changed
end end
-- log(item.text, item.symbol_name, item.uri)
-- log(item.text)
if item.definition then if item.definition then
log('definition', item)
ts_report = ts_report .. _NgConfigValues.icons.value_definition .. ' ' ts_report = ts_report .. _NgConfigValues.icons.value_definition .. ' '
end end
local header_len = #ts_report + 4 -- magic number 2 local header_len = #ts_report + 4 -- magic number 2
trace(ts_report, header_len) trace(ts_report, header_len)
item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '') item.text = item.text:gsub('%s*[%[%(%{]*%s*$', '')
if item.call_by ~= nil and item.call_by ~= '' then if item.call_by ~= nil and #item.call_by > 0 then
ts_report = ts_report .. '' .. item.call_by trace("call_by:", #item.call_by)
for _, value in pairs(item.call_by) do
if value.node_text then
local txt = value.node_text:gsub('%s*[%[%(%{]*%s*$', '')
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
end end
if #ts_report > 1 then if #ts_report > 1 then
space, trim = get_pads(win_width, item.text, ts_report) 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 if trim then
item.text = string.sub(item.text, 1, l) item.text = string.sub(item.text, 1, opts.width - 20) .. ""
item.text = util.sub_match(item.text)
-- let check if there are unmatched "/'
end end
if #space + #item.text + #ts_report >= win_width then if #space + #item.text + #ts_report >= win_width then
if #item.text + #ts_report >= win_width then if #item.text + #ts_report > win_width then
trace("exceeding", #item.text, #ts_report, win_width)
space = ' ' 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 else
local remain = win_width - #item.text - #ts_report local remain = win_width - #item.text - #ts_report
trace('remain', remain) trace("remain", remain)
space = string.rep(' ', remain) space = string.rep(' ', remain)
end end
end end

@ -1,5 +1,11 @@
local gui = require "navigator.gui"
local util = require "navigator.util" local util = require "navigator.util"
local mk_handler = util.mk_handler
local log = util.log local log = util.log
local partial = util.partial
local lsphelper = require "navigator.lspwrapper"
local cwd = vim.loop.cwd()
local M = {}
--- navigator signature --- navigator signature
local match_parameter = function(result) local match_parameter = function(result)
@ -34,19 +40,20 @@ local match_parameter = function(result)
if type(nextParameter.label) == "string" then -- label = 'par1 int' if type(nextParameter.label) == "string" then -- label = 'par1 int'
local i, j = label:find(nextParameter.label, 1, true) local i, j = label:find(nextParameter.label, 1, true)
if i ~= nil then 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 signature.label = label
end end
end end
end end
end end
local signature_handler = function(err, result, ctx, config) local signature_handler = mk_handler(function(err, result, ctx, config)
if config == nil then if config == nil then
log("config nil") log("config nil")
end end
if err then if err then
vim.notify("signature help error: ".. vim.inspect(err) .. vim.inspect(result), ctx, config, vim.lsp.log_levels.WARN) print("signature help error: ", err, vim.inspect(result), ctx, config)
end end
config = config or {} config = config or {}
if config.border == nil then if config.border == nil then
@ -65,5 +72,5 @@ local signature_handler = function(err, result, ctx, config)
local syntax = vim.lsp.util.try_trim_markdown_code_blocks(lines) local syntax = vim.lsp.util.try_trim_markdown_code_blocks(lines)
config.focus_id = ctx.bufnr .. "lsp_signature" config.focus_id = ctx.bufnr .. "lsp_signature"
vim.lsp.util.open_floating_preview(lines, syntax, config) vim.lsp.util.open_floating_preview(lines, syntax, config)
end end)
return {signature_handler = signature_handler} return {signature_handler = signature_handler}

@ -1,18 +1,55 @@
local gui = require('navigator.gui') local gui = require "navigator.gui"
local M = {} local M = {}
local log = require('navigator.util').log local log = require"navigator.util".log
local trace = require('navigator.util').trace local mk_handler = require"navigator.util".mk_handler
local lsphelper = require('navigator.lspwrapper') local lsphelper = require "navigator.lspwrapper"
local symbol_kind = require('navigator.lspclient.lspkind').symbol_kind local locations_to_items = lsphelper.locations_to_items
local clone = require"guihua.util".clone
local symbol_kind = require"navigator.lspclient.lspkind".symbol_kind
local symbols_to_items = lsphelper.symbols_to_items 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) function M.workspace_symbols(query)
query = query or pcall(vim.fn.input, 'Query: ') opts = opts or {}
local lspopts = {
loc = "top_center",
prompt = true,
-- rawdata = true,
api = ""
}
query = query or pcall(vim.fn.input, "Query: ")
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
vim.list_extend(lspopts, opts)
local params = {query=query} local params = {query=query}
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr)
if client.server_capabilities.workspaceSymbolProvider then if client.resolved_capabilities.workspace_symbol then
client.request('workspace/symbol', params, M.workspace_symbol_handler, _bufnr) client.request("workspace/symbol", params, M.workspace_symbol_handler, _bufnr)
end end
end) end)
end end
@ -20,40 +57,37 @@ end
function M.document_symbols(opts) function M.document_symbols(opts)
opts = opts or {} opts = opts or {}
local lspopts = { local lspopts = {
loc = 'top_center', loc = "top_center",
prompt = true, prompt = true,
-- rawdata = true, -- rawdata = true,
api = '', api = ""
} }
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
vim.list_extend(lspopts, opts) vim.list_extend(lspopts, opts)
local params = vim.lsp.util.make_position_params() local params = vim.lsp.util.make_position_params()
params.context = {includeDeclaration = true} params.context = {includeDeclaration = true}
params.query = opts.prompt or '' params.query = opts.prompt or ""
vim.lsp.for_each_buffer_client(bufnr, function(client, _, _bufnr) vim.lsp.for_each_buffer_client(bufnr, function(client, client_id, _bufnr)
if client.server_capabilities.documentSymbolProvider then if client.resolved_capabilities.document_symbol then
client.request('textDocument/documentSymbol', params, M.document_symbol_handler, _bufnr) client.request("textDocument/documentSymbol", params, M.document_symbol_handler, _bufnr)
end end
end) end)
end end
M.document_symbol_handler = function(err, result, ctx)
M.document_symbol_handler = mk_handler(function(err, result, ctx)
if err then if err then
vim.notify('failed to get document symbol' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) print("failed to get document symbol", ctx)
end
local bufnr = ctx.bufnr or 0
local query = ' '
if ctx.params and ctx.params.query then
query = query .. ctx.params.query .. ' '
end end
if not result or vim.tbl_isempty(result) then if not result or vim.tbl_isempty(result) then
vim.notify('symbol ' .. query .. ' not found for buf ' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) print("symbol not found for buf", ctx)
return return
end end
-- log(result)
local locations = {} local locations = {}
local fname = vim.fn.expand('%:p:f') local fname = vim.fn.expand("%:p:f")
local uri = vim.uri_from_fname(fname) local uri = vim.uri_from_fname(fname)
-- vim.list_extend(locations, vim.lsp.util.symbols_to_items(result) or {}) -- vim.list_extend(locations, vim.lsp.util.symbols_to_items(result) or {})
log(result[1]) log(result[1])
@ -62,25 +96,18 @@ M.document_symbol_handler = function(err, result, ctx)
item.kind = result[i].kind item.kind = result[i].kind
local kind = symbol_kind(item.kind) local kind = symbol_kind(item.kind)
item.name = result[i].name item.name = result[i].name
item.range = result[i].range or result[i].location.range item.range = result[i].range
if item.range == nil then
log('range missing in result', result[i])
end
item.uri = uri item.uri = uri
item.selectionRange = result[i].selectionRange item.selectionRange = result[i].selectionRange
item.detail = result[i].detail or '' item.detail = result[i].detail or ""
if item.detail == '()' then if item.detail == "()" then
item.detail = 'func' item.detail = "func"
end end
item.lnum = item.range.start.line + 1 item.lnum = result[i].range.start.line + 1
item.text = '[' .. kind .. ']' .. item.name .. ' ' .. item.detail item.text = "[" .. kind .. "]" .. item.name .. " " .. item.detail
item.filename = fname item.filename = fname
item.indent_level = 1
item.type = kind
item.node_text = item.name
table.insert(locations, item) table.insert(locations, item)
if result[i].children ~= nil then if result[i].children ~= nil then
@ -88,81 +115,93 @@ M.document_symbol_handler = function(err, result, ctx)
local child = {} local child = {}
child.kind = c.kind child.kind = c.kind
child.name = c.name child.name = c.name
child.range = c.range or c.location.range child.range = c.range
local ckind = symbol_kind(child.kind) local ckind = symbol_kind(child.kind)
child.node_text = child.name
child.type = ckind
child.selectionRange = c.selectionRange child.selectionRange = c.selectionRange
child.filename = fname child.filename = fname
child.uri = uri child.uri = uri
child.lnum = child.range.start.line + 1 child.lnum = c.range.start.line + 1
child.detail = c.detail or '' child.detail = c.detail or ""
child.indent_level = item.indent_level + 1 child.text = "  [" .. ckind .. "] " .. child.name .. " " .. child.detail
child.text = '' .. ckind .. '' .. child.name .. ' ' .. child.detail
table.insert(locations, child) table.insert(locations, child)
end end
end end
end end
if ctx.no_show then
return locations
end
local ft = vim.api.nvim_buf_get_option(bufnr, 'ft') local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
gui.new_list_view({ -- trace(locations)
items = locations, -- local items = locations_to_items(locations)
prompt = true, gui.new_list_view({items = locations, prompt = true, rawdata = true, ft = ft, api = ""})
rawdata = true,
height = 0.62,
preview_height = 0.1,
ft = ft,
api = _NgConfigValues.icons.doc_symbol,
})
end
M.workspace_symbol_handler = function(err, result, ctx, cfg) -- if locations == nil or vim.tbl_isempty(locations) then
trace(err, result, ctx, cfg) -- 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.loop.cwd() .. "/"
-- local add = util.get_relative_path(cwd, item.filename)
-- data[i] = data[i] .. " - " .. add
-- end
-- item.text = nil
-- end
-- opts.data = data
end)
M.workspace_symbol_handler = mk_handler(function(err, result, ctx, cfg)
if err then if err then
vim.notify('failed to get workspace symbol' .. vim.inspect(ctx), vim.lsp.log_levels.WARN) print("failed to get workspace symbol", ctx)
end
local query = ' '
if ctx.params and ctx.params.query then
query = query .. ctx.params.query .. ' '
end end
if not result or vim.tbl_isempty(result) then if not result or vim.tbl_isempty(result) then
log('symbol not found', ctx) print("symbol not found for buf", ctx)
vim.notify('symbol' .. query .. 'not found for buf ' .. tostring(ctx.bufnr), vim.lsp.log_levels.WARN)
return return
end end
log(result[1]) log(result[1])
local items = symbols_to_items(result) local items = symbols_to_items(result)
log(items[1]) 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(ctx.bufnr, 'ft') local ft = vim.api.nvim_buf_get_option(ctx.bufnr, "ft")
gui.new_list_view({ items = items, prompt = true, ft = ft, rowdata = true, api = '' }) gui.new_list_view({items = items, prompt = true, ft = ft, rowdata = true, api = ""})
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.loop.cwd() .. "/"
-- local add = util.get_relative_path(cwd, item.filename)
-- data[i] = data[i] .. " - " .. add
-- end
-- item.text = nil
-- end
-- opts.data = data
end)
function M.side_panel()
local Panel = require('guihua.panel')
local buf = vim.api.nvim_get_current_buf()
local p = Panel:new({
scope = 'range',
render = function(bufnr)
local ft = vim.api.nvim_buf_get_option(bufnr, 'buftype')
if ft == 'nofile' or ft == 'guihua' or ft == 'prompt' then
return
end
local params = vim.lsp.util.make_range_params()
local sync_req = require('navigator.lspwrapper').call_sync
return sync_req(
'textDocument/documentSymbol',
params,
{ timeout = 1000, bufnr = bufnr, no_show = true },
vim.lsp.with(M.document_symbol_handler, { no_show = true })
)
end,
})
p:open(true)
end
return M return M

@ -1,32 +1,28 @@
--- Note: some of the functions/code coped from treesitter/refactor/navigation.lua and may be modified --- Note: some of the functions/code coped from treesitter/refactor/navigation.lua and may be modified
-- to fit in navigator.lua -- to fit in navigator.lua
local gui = require('navigator.gui') local gui = require "navigator.gui"
local fn = vim.fn local fn = vim.fn
local lru = require('navigator.lru').new(500, 1024 * 1024) 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 if not ok then
error('treesitter not installed') error("treesitter not installed")
return nil return nil
end end
local parsers = require('nvim-treesitter.parsers') local parsers = require "nvim-treesitter.parsers"
local utils = require('nvim-treesitter.utils') local utils = require "nvim-treesitter.utils"
local locals = require('nvim-treesitter.locals') local locals = require 'nvim-treesitter.locals'
local ts_utils = require('nvim-treesitter.ts_utils') local ts_utils = require 'nvim-treesitter.ts_utils'
local api = vim.api local api = vim.api
local util = require('navigator.util') local util = require "navigator.util"
local M = {} local M = {}
local cwd = vim.loop.cwd() local cwd = vim.loop.cwd()
local log = require('navigator.util').log local log = require"navigator.util".log
local lerr = require('navigator.util').error local lerr = require"navigator.util".error
local trace = function(...) end local trace = require"navigator.util".trace
trace = log
if vim.fn.has('nvim-0.7') == 1 then
local trace = require('navigator.util').trace
end
local get_icon = function(kind) local get_icon = function(kind)
if kind == nil or _NgConfigValues.icons.match_kinds[kind] == nil then if kind == nil or _NgConfigValues.icons.match_kinds[kind] == nil then
@ -47,7 +43,7 @@ function M.goto_definition(bufnr)
local definition = locals.find_definition(node_at_point, bufnr) local definition = locals.find_definition(node_at_point, bufnr)
if definition ~= node_at_point then if definition ~= node_at_point then
log('def found:', definition:range()) log("def found:", definition:range())
ts_utils.goto_node(definition) ts_utils.goto_node(definition)
end end
end end
@ -73,12 +69,13 @@ local function node_is_definination(node)
end end
return false return false
end end
-- use lsp range to find def -- use lsp range to find def
function M.find_definition(range, bufnr) function M.find_definition(range, bufnr)
if not range or not range.start then if not range or not range.start then
lerr('find_def incorrect range', range) lerr("find_def incorrect range", range)
return return
end end
bufnr = bufnr or api.nvim_get_current_buf() bufnr = bufnr or api.nvim_get_current_buf()
@ -90,7 +87,7 @@ function M.find_definition(range, bufnr)
end 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 if not node_at_point then
lerr('no node at cursor') lerr("no node at cursor")
return return
end end
@ -98,191 +95,32 @@ function M.find_definition(range, bufnr)
if definition ~= node_at_point then -- NOTE: it may not worksfor some of languages. if def not found, ts 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 -- returns current node. if your node is def, then it also return self... then I have no idea weather it is
-- def or not -- def or not
trace('info: def found:', definition:range(), definition:type()) trace("info: def found:", definition:range(), definition:type())
local r, c = definition:range() local r, c = definition:range()
return {start = {line = r, character = c}} return {start = {line = r, character = c}}
elseif node_is_definination(node_at_point) then elseif node_is_definination(node_at_point) then
trace('declaraction here ', definition:type()) trace("declaraction here ", definition:type())
local r, c = definition:range() local r, c = definition:range()
return {start = {line = r, character = c}} return {start = {line = r, character = c}}
else else
trace('error: def not found in ', bufnr, definition:range(), definition:type(), definition:parent():type()) trace("error: def not found in ", bufnr, definition:range(), definition:type(), definition:parent():type())
end
end
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 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 end
--- Get definitions of bufnr (unique and sorted by order of appearance). --- Get definitions of bufnr (unique and sorted by order of appearance).
--- This function copy from treesitter/refactor/navigation.lua --- This function copy from treesitter/refactor/navigation.lua
local function get_definitions(bufnr) local function get_definitions(bufnr)
local local_nodes = ts_locals.get_locals(bufnr) local local_nodes = ts_locals.get_locals(bufnr)
-- Make sure the nodes are unique. -- Make sure the nodes are unique.
local nodes_set = {} local nodes_set = {}
for _, loc in ipairs(local_nodes) do for _, loc in ipairs(local_nodes) do
trace(loc)
if loc.definition then if loc.definition then
ts_locals.recurse_local_nodes(loc.definition, function(_, node, _, match) ts_locals.recurse_local_nodes(loc.definition, function(_, node, _, match)
-- lua doesn't compare tables by value, -- lua doesn't compare tables by value,
-- use the value from byte count instead. -- use the value from byte count instead.
local row, col, offset = node:start() local _, _, start = node:start()
local erow, ecol, end_ = node:end_() nodes_set[start] = {node = node, type = match or ""}
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 end
end end
@ -312,12 +150,14 @@ local function prepare_node(node, kind)
end end
local function get_scope(type, source) local function get_scope(type, source)
local sbl, sbc, sel, sec = source:range()
local current = source local current = source
local result = current
local next = ts_utils.get_next_node(source) local next = ts_utils.get_next_node(source)
local parent = current:parent() local parent = current:parent()
trace(source:type(), source:range(), parent) trace(source:type(), source:range(), parent)
if type == 'method' or type:find('function') and parent ~= nil then if type == 'method' or type == 'function' and parent ~= nil then
trace(parent:type(), parent:range()) trace(parent:type(), parent:range())
-- a function name -- a function name
if parent:type() == 'function_name' then if parent:type() == 'function_name' then
@ -330,7 +170,7 @@ local function get_scope(type, source)
-- for C++ -- for C++
local n = source local n = source
for _ = 1, 4, 1 do for i = 1, 4, 1 do
if n == nil or n:parent() == nil then if n == nil or n:parent() == nil then
break break
end end
@ -342,8 +182,8 @@ local function get_scope(type, source)
return parent, true return parent, true
end end
if type == 'var' and next ~= nil then if type == "var" and next ~= nil then
if next:type() == 'function' or next:type() == 'arrow_function' or next:type() == 'function_definition' then if next:type() == "function" or next:type() == "arrow_function" or next:type() == "function_definition" then
trace(current:type(), current:range()) trace(current:type(), current:range())
return next, true return next, true
elseif parent:type() == 'function_declaration' then elseif parent:type() == 'function_declaration' then
@ -356,7 +196,7 @@ local function get_scope(type, source)
-- M.fun1 = function() end -- M.fun1 = function() end
-- lets work up and see next node, lua -- lets work up and see next node, lua
local n = source local n = source
for _ = 1, 4, 1 do for i = 1, 4, 1 do
if n == nil or n:parent() == nil then if n == nil or n:parent() == nil then
break break
end end
@ -368,9 +208,10 @@ local function get_scope(type, source)
end end
end end
if source:type() == 'type_identifier' then if source:type() == "type_identifier" then
return source:parent(), true return source:parent(), true
end end
end end
local function get_smallest_context(source) local function get_smallest_context(source)
@ -388,7 +229,7 @@ local function get_smallest_context(source)
-- if source:type() == "identifier" then return get_var_context(source) end -- if source:type() == "identifier" then return get_var_context(source) end
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) function M.goto_adjacent_usage(bufnr, delta)
local opt = {forward = true} local opt = {forward = true}
@ -428,7 +269,7 @@ local function key(fname, filter)
end end
local function get_all_nodes(bufnr, filter, summary) local function get_all_nodes(bufnr, filter, summary)
local fname = vim.fn.expand('%:p:f') local fname = vim.fn.expand("%:p:f")
local uri = vim.uri_from_fname(fname) local uri = vim.uri_from_fname(fname)
if bufnr ~= 0 then if bufnr ~= 0 then
uri = vim.uri_from_bufnr(bufnr) uri = vim.uri_from_bufnr(bufnr)
@ -441,8 +282,7 @@ local function get_all_nodes(bufnr, filter, summary)
local result = lru:get(hash) local result = lru:get(hash)
if result ~= nil and result.ftime == ftime then if result ~= nil and result.ftime == ftime then
trace('get data from cache', ftime, result) log("get data from cache")
return result.nodes, result.length return result.nodes, result.length
end end
@ -452,33 +292,31 @@ local function get_all_nodes(bufnr, filter, summary)
trace(bufnr, filter, summary) trace(bufnr, filter, summary)
if not bufnr then if not bufnr then
vim.notify('get_all_node invalid bufnr', vim.lsp.log_levels.WARN) print("get_all_node invalide bufnr")
end end
summary = summary or false summary = summary or false
local ft = vim.api.nvim_buf_get_option(bufnr, 'filetype')
if not parsers.has_parser() then if not parsers.has_parser() then
if not require('navigator.lspclient.clients').ft_disabled(ft) then print("ts not loaded")
-- vim.notify('ts not loaded ' .. ft, vim.lsp.log_levels.Debug)
log('ts not loaded ' .. ft)
end
return {}
end end
local path_sep = require('navigator.util').path_sep() local path_sep = require"navigator.util".path_sep()
local path_cur = require('navigator.util').path_cur() local path_cur = require"navigator.util".path_cur()
local display_filename = fname:gsub(cwd .. path_sep, path_cur, 1) local display_filename = fname:gsub(cwd .. path_sep, path_cur, 1)
local all_nodes = {} local all_nodes = {}
local containers = filter or { -- Support completion-nvim customized label map
['function'] = true, local customized_labels = vim.g.completion_customize_lsp_label or {}
['local_function'] = true,
['arrow_function'] = true, -- Force some types to act like they are parents
['type'] = true, -- instead of neighbors of the next nodes.
['class'] = true, local containers = {
['call_expression'] = true, ["function"] = true,
-- ['var'] = true, ["local_function"] = true,
['struct'] = true, ["arrow_function"] = true,
['method'] = true, ["type"] = true,
["class"] = true,
["struct"] = true,
["method"] = true
} }
-- check and load buff -- check and load buff
@ -492,31 +330,15 @@ local function get_all_nodes(bufnr, filter, summary)
-- Step 2 find correct completions -- Step 2 find correct completions
local length = 10 local length = 10
local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos local parents = {} -- stack of nodes a clever algorithm from treesiter refactor @Santos Gallegos
local loaded_symbol = {}
for _, def in ipairs(get_definitions(bufnr)) do for _, def in ipairs(get_definitions(bufnr)) do
local n = #parents local n = #parents
for i = 1, n do for i = 1, n do
local index = n + 1 - i local index = n + 1 - i
local parent_def = parents[index] local parent_def = parents[index]
-- trace(parent_def.type, parent_def.node:type(), vim.treesitter.get_node_text(parent_def.node, bufnr)) if ts_utils.is_parent(parent_def.node, def.node)
-- trace(def.node:type(), vim.treesitter.get_node_text(def.node, bufnr)) or (containers[parent_def.type] and ts_utils.is_parent(parent_def.node:parent(), def.node)) then
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 break
else else
-- trace('leave node', i, index)
parents[index] = nil parents[index] = nil
end end
end end
@ -529,24 +351,15 @@ local function get_all_nodes(bufnr, filter, summary)
item.type = node.type item.type = node.type
if filter ~= nil and not filter[item.type] then if filter ~= nil and not filter[item.type] then
trace('skipped', item.type, item.kind) trace(item.type, item.kind)
goto continue
end
if item.type == 'associated' then
trace('skipped', item.type, item.kind)
goto continue goto continue
end end
local tsdata = node.def local tsdata = node.def
if node.def == nil then if node.def == nil then
trace('skipped', item.type, item.kind)
goto continue goto continue
end end
local text = vim.treesitter.get_node_text(tsdata, bufnr) or '' item.node_text = ts_utils.get_node_text(tsdata, bufnr)[1]
text = vim.split(text, '\n')[1] or ''
item.node_text = text
log(item.node_text)
local scope, is_func local scope, is_func
if summary then if summary then
@ -554,68 +367,41 @@ local function get_all_nodes(bufnr, filter, summary)
else else
scope, is_func = get_smallest_context(tsdata) scope, is_func = get_smallest_context(tsdata)
end end
log(item, scope, is_func)
if is_func then if is_func then
-- hack for lua and maybe other language aswell -- hack for lua and maybe other language aswell
local parent = tsdata:parent() local parent = tsdata:parent()
if parent ~= nil and _NgConfigValues.debug == 'trace' then -- for github action failure if parent ~= nil and parent:type() == 'function_name' or parent:type() == 'function_name_field' then
trace(parent:type(), vim.treesitter.get_node_text(parent, bufnr):sub(1, 30), item.node_text, item.type) item.node_text = ts_utils.get_node_text(parent, bufnr)[1]
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) log(parent:type(), item.node_text)
end end
end end
trace(item.node_text, item.kind, item.type) trace(item.node_text, item.kind, item.type)
if scope ~= nil then if scope ~= nil then
-- it is strange..
if not is_func and summary then if not is_func and summary then
trace('skipped', item.node_text, item.type)
goto continue goto continue
end end
item.node_scope = ts_utils.node_to_lsp_range(scope) item.node_scope = ts_utils.node_to_lsp_range(scope)
end end
if item.node_text and vim.trim(item.node_text) == '_' then
goto continue
end
if summary then if summary then
if item.node_scope ~= nil then if item.node_scope ~= nil then
table.insert(all_nodes, item) table.insert(all_nodes, item)
end end
if item.node_scope then if item.node_scope then
trace( trace(item.type, tsdata:type(), item.node_text, item.kind, item.node_text, "range",
item.type, item.node_scope.start.line, item.node_scope['end'].line) -- set to log if need to trace result
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 end
goto continue goto continue
end end
item.range = ts_utils.node_to_lsp_range(tsdata) item.range = ts_utils.node_to_lsp_range(tsdata)
local start_line_node, _, _ = tsdata:start() local start_line_node, _, _ = tsdata:start()
if item.node_text == "_" then
local line_text = api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or '' goto continue
item.full_text = vim.trim(line_text) end
item.full_text = vim.trim(api.nvim_buf_get_lines(bufnr, start_line_node, start_line_node + 1, false)[1] or "")
item.full_text = item.full_text:gsub('%s*[%[%(%{]*%s*$', '') item.full_text = item.full_text:gsub('%s*[%[%(%{]*%s*$', '')
item.uri = uri item.uri = uri
@ -625,43 +411,16 @@ local function get_all_nodes(bufnr, filter, summary)
item.lnum, item.col, _ = def.node:start() item.lnum, item.col, _ = def.node:start()
item.lnum = item.lnum + 1 item.lnum = item.lnum + 1
item.col = item.col + 1 item.col = item.col + 1
local indent = '' local indent = ""
if #parents > 1 then 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 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 if #item.text > length then
length = #item.text length = #item.text
end end
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) table.insert(all_nodes, item)
loaded_symbol[item.node_text .. item.kind] = item.node_scope
end
::continue:: ::continue::
end end
end end
@ -675,27 +434,22 @@ local function get_all_nodes(bufnr, filter, summary)
end end
function M.buf_func(bufnr) 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 if not ok or ts_locals == nil then
error('treesitter not loaded: ' .. ft) error("treesitter not loaded")
return return
end end
bufnr = bufnr or api.nvim_get_current_buf() bufnr = bufnr or api.nvim_get_current_buf()
local all_nodes, width = get_all_nodes(bufnr, { local all_nodes, width = get_all_nodes(bufnr, {
['function'] = true, ["function"] = true,
['arrow_function'] = true, ["var"] = true,
['var'] = true, ["method"] = true,
['method'] = true, ["class"] = true,
['class'] = true, ["type"] = true
['type'] = true,
}, true) }, true)
if #all_nodes < 1 then if #all_nodes < 1 then
trace('no node found for ', bufnr) -- set to log trace("no node found for ", bufnr) -- set to log
return return
end end
@ -722,62 +476,36 @@ function M.buf_func(bufnr)
return false return false
end) end)
end end
log(all_nodes)
return all_nodes, width return all_nodes, width
end end
function M.all_ts_nodes(bufnr) function M.buf_ts()
if ts_locals == nil then if ts_locals == nil then
error('treesitter not loaded') error("treesitter not loaded")
return return
end end
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 bufnr = api.nvim_get_current_buf()
local panel = Panel:new({ local all_nodes, width = get_all_nodes(bufnr)
header = 'treesitter',
render = function(b)
local ft = vim.api.nvim_buf_get_option(b, 'buftype')
log('render for ', bufnr, b)
if ft == 'nofile' or ft == 'guihua' then
b = bufnr
end
return require('navigator.treesitter').all_ts_nodes(b)
end,
scope = 'node_scope'
})
panel:open(true)
end
function M.buf_ts() local ft = vim.api.nvim_buf_get_option(bufnr, "ft")
local all_nodes, width = M.all_ts_nodes() gui.new_list_view({
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, items = all_nodes,
prompt = true, prompt = true,
ft = ft, ft = ft,
rawdata = true, rawdata = true,
height = 0.62,
preview_height = 0.12,
width = width + 10, width = width + 10,
api = _NgConfigValues.icons.treesitter_defult, api = _NgConfigValues.icons.treesitter_defult
}) })
return listview, all_nodes, width
end end
M.get_all_nodes = get_all_nodes M.get_all_nodes = get_all_nodes
function M.bufs_ts() function M.bufs_ts()
if ts_locals == nil then if ts_locals == nil then
error('treesitter not loaded') error("treesitter not loaded")
return return
end end
local bufs = vim.api.nvim_list_bufs() local bufs = vim.api.nvim_list_bufs()
@ -800,15 +528,13 @@ function M.bufs_ts()
if #ts_opened > 1 then if #ts_opened > 1 then
trace(ts_opened) trace(ts_opened)
local ft = vim.api.nvim_buf_get_option(0, 'ft') local ft = vim.api.nvim_buf_get_option(0, "ft")
gui.new_list_view({ gui.new_list_view({
items = ts_opened, items = ts_opened,
prompt = true, prompt = true,
ft = ft, ft = ft,
height = 0.62,
preview_height = 0.12,
width = max_length + 10, width = max_length + 10,
api = _NgConfigValues.icons.treesitter_defult, api = _NgConfigValues.icons.treesitter_defult
}) })
end end
end end
@ -852,7 +578,7 @@ function M.get_node_at_line(lnum)
return node return node
end end
local usage_namespace = vim.api.nvim_create_namespace('nvim-treesitter-usages') local usage_namespace = vim.api.nvim_create_namespace("nvim-treesitter-usages")
function M.highlight_usages(bufnr) function M.highlight_usages(bufnr)
M.clear_usage_highlights(bufnr) M.clear_usage_highlights(bufnr)
@ -869,12 +595,12 @@ function M.highlight_usages(bufnr)
for _, usage_node in ipairs(usages) do for _, usage_node in ipairs(usages) do
if usage_node ~= node_at_point then if usage_node ~= node_at_point then
ts_utils.highlight_node(usage_node, bufnr, usage_namespace, 'TSDefinitionUsage') ts_utils.highlight_node(usage_node, bufnr, usage_namespace, "TSDefinitionUsage")
end end
end end
if def_node ~= node_at_point then if def_node ~= node_at_point then
ts_utils.highlight_node(def_node, bufnr, usage_namespace, 'TSDefinition') ts_utils.highlight_node(def_node, bufnr, usage_namespace, "TSDefinition")
end end
end end
@ -925,6 +651,7 @@ function M.get_node_scope(node)
end end
return sr, sc, er, ec return sr, sc, er, ec
end end
return M return M

@ -3,29 +3,26 @@
-- Some of function copied from https://github.com/RishabhRD/nvim-lsputils -- 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 is_windows = uv.os_uname().version:match("Windows")
local guihua = require('guihua.util')
local nvim_0_6_1 local nvim_0_6
local nvim_0_8
local vfn = vim.fn
local api = vim.api
M.path_sep = function() 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 if is_win then
return '\\' return "\\"
else else
return '/' return "/"
end end
end end
local path_sep = M.path_sep() local path_sep = M.path_sep()
M.path_cur = function() 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 if is_win then
return '.\\' return ".\\"
else else
return './' return "./"
end end
end end
@ -42,48 +39,25 @@ function M.get_data_from_file(filename, startLine)
startLine = startLine - 2 startLine = startLine - 2
displayLine = 2 displayLine = 2
end end
local uri = 'file:///' .. filename local uri = "file:///" .. filename
local bufnr = vim.uri_to_bufnr(uri) local bufnr = vim.uri_to_bufnr(uri)
if not api.nvim_buf_is_loaded(bufnr) then if not vim.api.nvim_buf_is_loaded(bufnr) then
vfn.bufload(bufnr) vim.fn.bufload(bufnr)
end end
local data = api.nvim_buf_get_lines(bufnr, startLine, startLine + 8, false) local data = vim.api.nvim_buf_get_lines(bufnr, startLine, startLine + 8, false)
if data == nil or vim.tbl_isempty(data) then if data == nil or vim.tbl_isempty(data) then
startLine = nil startLine = nil
else else
local len = #data local len = #data
startLine = startLine + 1 startLine = startLine + 1
for i = 1, len, 1 do for i = 1, len, 1 do
data[i] = startLine .. ' ' .. data[i] data[i] = startLine .. " " .. data[i]
startLine = startLine + 1 startLine = startLine + 1
end end
end end
return {data = data, line = displayLine} return {data = data, line = displayLine}
end 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) M.merge = function(t1, t2)
for k, v in pairs(t2) do for k, v in pairs(t2) do
t1[k] = v t1[k] = v
@ -96,15 +70,15 @@ M.map = function(modes, key, result, options)
local buffer = options.buffer local buffer = options.buffer
options.buffer = nil options.buffer = nil
if type(modes) ~= 'table' then if type(modes) ~= "table" then
modes = {modes} modes = {modes}
end end
for i = 1, #modes do for i = 1, #modes do
if buffer then if buffer then
api.nvim_buf_set_keymap(0, modes[i], key, result, options) vim.api.nvim_buf_set_keymap(0, modes[i], key, result, options)
else else
api.nvim_set_keymap(modes[i], key, result, options) vim.api.nvim_set_keymap(modes[i], key, result, options)
end end
end end
end end
@ -139,13 +113,7 @@ end
function M.get_relative_path(base_path, my_path) function M.get_relative_path(base_path, my_path)
local base_data = getDir(base_path) local base_data = getDir(base_path)
if base_data == nil then
return
end
local my_data = getDir(my_path) local my_data = getDir(my_path)
if my_data == nil then
return
end
local base_len = #base_data local base_len = #base_data
local my_len = #my_data local my_len = #my_data
@ -164,7 +132,7 @@ function M.get_relative_path(base_path, my_path)
end end
cur = i cur = i
end end
local data = '' local data = ""
for i = cur + 1, my_len do for i = cur + 1, my_len do
data = data .. my_data[i] .. path_sep data = data .. my_data[i] .. path_sep
end end
@ -172,46 +140,24 @@ function M.get_relative_path(base_path, my_path)
return data return data
end end
M.log = function(...) local level = "error"
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'
function M.setup()
if _NgConfigValues.debug == true then if _NgConfigValues.debug == true then
level = 'info' level = "info"
elseif _NgConfigValues.debug == 'trace' then elseif _NgConfigValues.debug == "trace" then
level = 'trace' level = "trace"
end end
local default_config = {use_console = false, use_file = true, level = level} local default_config = {use_console = false, use_file = true, level = level}
if _NgConfigValues.debug_console_output then if _NgConfigValues.debug_console_output then
default_config.use_console = true default_config.use_console = true
default_config.use_file = false default_config.use_file = false
end end
M._log = require("guihua.log").new(default_config, true)
M._log = require('guihua.log').new(default_config, true)
if _NgConfigValues.debug then
-- add log to you lsp.log -- add log to you lsp.log
M.trace = M._log.trace M.log = M._log.info
M.info = M._log.info M.info = M._log.info
M.warn = M._log.warn M.trace = M._log.trace
M.error = M._log.error M.error = M._log.error
M.log = M.info
end
end
function M.fmt(...) function M.fmt(...)
M._log.fmt_info(...) M._log.fmt_info(...)
@ -219,10 +165,10 @@ end
function M.split(inputstr, sep) function M.split(inputstr, sep)
if sep == nil then if sep == nil then
sep = '%s' sep = "%s"
end end
local t = {} local t = {}
for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
table.insert(t, str) table.insert(t, str)
end end
return t return t
@ -232,29 +178,29 @@ function M.quickfix_extract(line)
-- check if it is a line of file pos been selected -- check if it is a line of file pos been selected
local split = M.split local split = M.split
line = vim.trim(line) line = vim.trim(line)
local sep = split(line, ' ') local sep = split(line, " ")
if #sep < 2 then if #sep < 2 then
M.log(line) M.log(line)
return nil return nil
end end
sep = split(sep[1], ':') sep = split(sep[1], ":")
if #sep < 3 then if #sep < 3 then
M.log(line) M.log(line)
return nil return nil
end end
local location = { local location = {
uri = 'file:///' .. sep[1], uri = "file:///" .. sep[1],
range = { start = { line = sep[2] - 3 > 0 and sep[2] - 3 or 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 return location
end end
function M.getArgs(inputstr) function M.getArgs(inputstr)
local sep = '%s' local sep = "%s"
local t = {} local t = {}
local cmd local cmd
for str in string.gmatch(inputstr, '([^' .. sep .. ']+)') do for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
if not cmd then if not cmd then
cmd = str cmd = str
else else
@ -265,47 +211,47 @@ function M.getArgs(inputstr)
end end
function M.p(t) function M.p(t)
vim.notify(vim.inspect(t), vim.log.levels.INFO) print(vim.inspect(t))
end end
function M.printError(msg) function M.printError(msg)
vim.cmd('echohl ErrorMsg') vim.cmd("echohl ErrorMsg")
vim.cmd(string.format([[echomsg '%s']], msg)) vim.cmd(string.format([[echomsg '%s']], msg))
vim.cmd('echohl None') vim.cmd("echohl None")
end end
function M.reload() function M.reload()
vim.lsp.stop_client(vim.lsp.get_active_clients()) vim.lsp.stop_client(vim.lsp.get_active_clients())
vim.cmd([[edit]]) vim.cmd [[edit]]
end end
function M.open_log() function M.open_log()
local path = vim.lsp.get_log_path() local path = vim.lsp.get_log_path()
vim.cmd('edit ' .. path) vim.cmd("edit " .. path)
end end
function table.pack(...) function table.pack(...)
return { n = select('#', ...), ... } return {n = select("#", ...), ...}
end end
function M.show(...) function M.show(...)
local string = '' local string = ""
local args = table.pack(...) local args = table.pack(...)
for i = 1, args.n do for i = 1, args.n do
string = string .. tostring(args[i]) .. '\t' string = string .. tostring(args[i]) .. "\t"
end end
return string .. '\n' return string .. "\n"
end end
function M.split2(s, sep) function M.split2(s, sep)
local fields = {} local fields = {}
sep = sep or ' ' sep = sep or " "
local pattern = string.format('([^%s]+)', sep) local pattern = string.format("([^%s]+)", sep)
_ = string.gsub(s, pattern, function(c) string.gsub(s, pattern, function(c)
fields[#fields + 1] = c fields[#fields + 1] = c
end) end)
@ -332,20 +278,32 @@ function M.trim_and_pad(txt)
end end
M.open_file = function(filename) M.open_file = function(filename)
api.nvim_command(string.format('e! %s', filename)) vim.api.nvim_command(string.format("e! %s", filename))
end end
M.open_file_at = guihua.open_file_at M.open_file_at = function(filename, line, col, split)
if split == nil then
-- code
vim.api.nvim_command(string.format("e! +%s %s", line, filename))
elseif split == 'v' then
vim.api.nvim_command(string.format("vsp! +%s %s", line, filename))
elseif split == 's' then
vim.api.nvim_command(string.format("sp! +%s %s", line, filename))
end
-- vim.api.nvim_command(string.format("e! %s", filename))
col = col or 1
vim.fn.cursor(line, col)
end
-- function M.exists(var) function M.exists(var)
-- for k, _ in pairs(_G) do for k, _ in pairs(_G) do
-- if k == var then if k == var then
-- return true return true
-- end end
-- end end
-- end end
local exclude_ft = { 'scrollbar', 'help', 'NvimTree' } local exclude_ft = {"scrollbar", "help", "NvimTree"}
function M.exclude(fname) function M.exclude(fname)
for i = 1, #exclude_ft do for i = 1, #exclude_ft do
if string.find(fname, exclude_ft[i]) then if string.find(fname, exclude_ft[i]) then
@ -359,16 +317,18 @@ end
-- name space search -- name space search
local nss local nss
local api = vim.api
local bufs local bufs
function M.set_virt_eol(bufnr, lnum, chunks, priority, id) function M.set_virt_eol(bufnr, lnum, chunks, priority, id)
if nss == nil then if nss == nil then
nss = api.nvim_create_namespace('navigator_search') nss = api.nvim_create_namespace("navigator_search")
end end
bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr bufnr = bufnr == 0 and api.nvim_get_current_buf() or bufnr
bufs[bufnr] = true bufs[bufnr] = true
-- id may be nil -- id may be nil
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 end
function M.clear_buf(bufnr) function M.clear_buf(bufnr)
@ -396,177 +356,54 @@ function M.get_current_winid()
return api.nvim_get_current_win() return api.nvim_get_current_win()
end end
function M.nvim_0_6_1() function M.nvim_0_6()
if nvim_0_6_1 ~= nil then if nvim_0_6 ~= nil then
return nvim_0_6_1 return nvim_0_6
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 end
nvim_0_8 = vfn.has('nvim-0.8') == 1 if debug.getinfo(vim.lsp.handlers.signature_help).nparams == 4 then
if nvim_0_8 == false then nvim_0_6 = true
M.log('Please use navigator 0.4 version for neovim version < 0.8') else
nvim_0_6 = false
end end
return nvim_0_8 return nvim_0_6
end end
function M.mk_handler(fn) function M.mk_handler(fn)
return function(...) return function(...)
local config_or_client_id = select(4, ...)
local is_new = M.nvim_0_6()
if is_new then
return fn(...) return fn(...)
else
local err = select(1, ...)
local method = select(2, ...)
local result = select(3, ...)
local client_id = select(4, ...)
local bufnr = select(5, ...)
local config = select(6, ...)
return fn(err, result, {method = method, client_id = client_id, bufnr = bufnr}, config)
end
end end
end end
function M.partial(func, arg) function M.partial(func, arg)
return function(...) return (M.mk_handler(function(...)
return func(arg, ...) return func(arg, ...)
end end))
end
function M.partial2(func, arg1, arg2)
return function(...)
return func(arg1, arg2, ...)
end
end
function M.partial3(func, arg1, arg2, arg3)
return function(...)
return func(arg1, arg2, arg3, ...)
end
end
function M.partial4(func, arg1, arg2, arg3, arg4)
return function(...)
return func(arg1, arg2, arg3, arg4, ...)
end
end
function M.empty(t)
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 end
-- alternatively: use vim.notify("namespace does not exist or is anonymous", vim.log.levels.ERROR) -- alternatively: use vim.notify("namespace does not exist or is anonymous", vim.log.levels.ERROR)
function M.warn(msg) function M.warn(msg)
vim.notify('WRN: ' .. msg, vim.lsp.log_levels.WARN) vim.api.nvim_echo({{"WRN: " .. msg, "WarningMsg"}}, true, {})
end end
function M.error(msg) function M.error(msg)
vim.notify('ERR: ' .. msg, vim.lsp.log_levels.EROR) vim.api.nvim_echo({{"ERR: " .. msg, "ErrorMsg"}}, true, {})
end end
function M.info(msg) function M.info(msg)
vim.notify('INF: ' .. msg, vim.lsp.log_levels.INFO) vim.api.nvim_echo({{"Info: " .. msg}}, true, {})
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 end
return M return M

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

@ -1,38 +0,0 @@
# 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`
- ...

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

@ -1,17 +0,0 @@
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)
}
}

@ -1,49 +0,0 @@
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{})
}

@ -1,25 +0,0 @@
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)
}
}

@ -1,26 +0,0 @@
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)
}
}

@ -1,67 +0,0 @@
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)
}

@ -1,31 +0,0 @@
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")
}

@ -1,39 +0,0 @@
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)
}

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

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

@ -1,22 +0,0 @@
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)
}
}

@ -1,70 +0,0 @@
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")
// }
// }

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

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

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

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

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

@ -1,11 +0,0 @@
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()

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

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

@ -1,14 +0,0 @@
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 ])

@ -1,79 +0,0 @@
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")

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

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

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

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

@ -39,9 +39,7 @@ require'lspconfig'.gopls.setup{}
require'navigator'.setup({ require'navigator'.setup({
debug = false, -- log output, set to true and log path: ~/.local/share/nvim/gh.log debug = false, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
code_action_icon = " ",
icons={code_action_icon = " "},
width = 0.75, -- max width ratio (number of cols for the floating window) / (window width) 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 height = 0.3, -- max list window height, 0.3 by default
preview_height = 0.35, -- max height of preview windows preview_height = 0.35, -- max height of preview windows

@ -1,13 +1,13 @@
local helpers = {} local helpers = {}
local busted = require('plenary/busted') local busted = require("plenary/busted")
local eq = assert.are.same local eq = assert.are.same
local cur_dir = vim.fn.expand('%:p:h') local cur_dir = vim.fn.expand("%:p:h")
-- local status = require("plenary.reload").reload_module("go.nvim") -- local status = require("plenary.reload").reload_module("go.nvim")
-- status = require("plenary.reload").reload_module("nvim-treesitter") -- status = require("plenary.reload").reload_module("nvim-treesitter")
-- local ulog = require('go.utils').log -- local ulog = require('go.utils').log
describe('should run lsp reference', function() describe("should run lsp reference", function()
-- vim.fn.readfile('minimal.vim') -- vim.fn.readfile('minimal.vim')
local nvim_6 = true local nvim_6 = true
if debug.getinfo(vim.lsp.handlers.signature_help).nparams > 4 then if debug.getinfo(vim.lsp.handlers.signature_help).nparams > 4 then
@ -16,89 +16,112 @@ describe('should run lsp reference', function()
local result = { local result = {
{ {
range = {['end'] = {character = 6, line = 14}, start = {character = 1, line = 14}}, range = {['end'] = {character = 6, line = 14}, start = {character = 1, line = 14}},
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, }, {
{
range = {['end'] = {character = 15, line = 24}, start = {character = 10, line = 24}}, range = {['end'] = {character = 15, line = 24}, start = {character = 10, line = 24}},
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, }, {
{
range = {['end'] = {character = 17, line = 28}, start = {character = 12, line = 28}}, range = {['end'] = {character = 17, line = 28}, start = {character = 12, line = 28}},
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, }, {
{
range = {['end'] = {character = 19, line = 51}, start = {character = 14, line = 51}}, range = {['end'] = {character = 19, line = 51}, start = {character = 14, line = 51}},
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, }, {
{
range = {['end'] = {character = 19, line = 55}, start = {character = 14, line = 55}}, range = {['end'] = {character = 19, line = 55}, start = {character = 14, line = 55}},
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, }, {
{
range = {['end'] = {character = 16, line = 59}, start = {character = 11, line = 59}}, range = {['end'] = {character = 16, line = 59}, start = {character = 11, line = 59}},
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface.go', uri = "file://" .. cur_dir .. "/tests/fixtures/interface.go"
}, }, {
{
range = {['end'] = {character = 16, line = 5}, start = {character = 11, line = 5}}, range = {['end'] = {character = 16, line = 5}, start = {character = 11, line = 5}},
uri = 'file://' .. cur_dir .. '/tests/fixtures/interface_test.go', 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') it("should show references", function()
local status = require("plenary.reload").reload_module("navigator")
local status = require("plenary.reload").reload_module("guihua")
local status = require("plenary.reload").reload_module("lspconfig")
vim.cmd([[packadd navigator.lua]]) vim.cmd([[packadd navigator.lua]])
vim.cmd([[packadd guihua.lua]]) vim.cmd([[packadd guihua.lua]])
local path = cur_dir .. '/tests/fixtures/interface.go' -- %:p:h ? %:p local path = cur_dir .. "/tests/fixtures/interface.go" -- %:p:h ? %:p
local cmd = " silent exe 'e " .. path .. "'" local cmd = " silent exe 'e " .. path .. "'"
vim.cmd(cmd) vim.cmd(cmd)
vim.cmd([[cd %:p:h]]) vim.cmd([[cd %:p:h]])
local bufn = vim.fn.bufnr('') local bufn = vim.fn.bufnr("")
-- require'lspconfig'.gopls.setup {} -- require'lspconfig'.gopls.setup {}
require('navigator').setup({ require'navigator'.setup({
debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
icons = { code_action_icon = 'A ' }, code_action_icon = "A ",
width = 0.75, -- max width ratio (number of cols for the floating window) / (window width) 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 height = 0.3, -- max list window height, 0.3 by default
preview_height = 0.35, -- max height of preview windows preview_height = 0.35, -- max height of preview windows
border = 'none', border = 'none'
}) })
if vim.fn.has('nvim-0.7') then
_NgConfigValues.treesitter_analysis = true
else
_NgConfigValues.treesitter_analysis = false
end
-- allow gopls start -- allow gopls start
for i = 1, 10 do
for _ = 1, 20 do vim.wait(400, function()
vim.wait(400, function() end) end)
local found = false local clients = vim.lsp.get_active_clients()
for _, client in ipairs(vim.lsp.get_active_clients()) do print("lsp clients: ", #clients)
if client.name == 'gopls' then if #clients > 0 then
found = true
break
end
end
if found then
break break
end end
end end
it('should show references', function()
vim.fn.setpos('.', { bufn, 15, 4, 0 }) -- width
vim.bo.filetype = 'go' vim.fn.setpos(".", {bufn, 15, 4, 0}) -- width
vim.lsp.buf.references()
vim.bo.filetype = "go"
-- vim.lsp.buf.references()
eq(1, 1) eq(1, 1)
end) end)
it('reference handler should return items', function() it("reference handler should return items", function()
vim.fn.setpos('.', { bufn, 15, 4, 0 }) -- width
local status = require("plenary.reload").reload_module("navigator")
local status = require("plenary.reload").reload_module("guihua")
vim.cmd([[packadd navigator.lua]])
vim.cmd([[packadd guihua.lua]])
local path = cur_dir .. "/tests/fixtures/interface.go" -- %:p:h ? %:p
print(path)
local cmd = " silent exe 'e " .. path .. "'"
vim.cmd(cmd)
-- vim.cmd([[cd %:p:h]])
local bufn = vim.fn.bufnr("")
vim.fn.setpos(".", {bufn, 15, 4, 0}) -- width
vim.bo.filetype = "go"
require'navigator'.setup({
debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
code_action_icon = "A ",
width = 0.75, -- max width ratio (number of cols for the floating window) / (window width)
height = 0.3, -- max list window height, 0.3 by default
preview_height = 0.35, -- max height of preview windows
debug_console_output = true,
border = 'none'
})
_NgConfigValues.debug_console_output = true
vim.bo.filetype = "go"
-- allow gopls start
for i = 1, 10 do
vim.wait(400, function()
end)
local clients = vim.lsp.get_active_clients()
print("clients ", #clients)
if #clients > 0 then
break
end
end
vim.bo.filetype = 'go'
-- allow gopls start -- allow gopls start
vim.wait(200, function() end) vim.wait(200, function()
end)
local win, items, width local win, items, width
@ -106,22 +129,67 @@ describe('should run lsp reference', function()
win, items, width = require('navigator.reference').ref_view(nil, result, { win, items, width = require('navigator.reference').ref_view(nil, result, {
method = 'textDocument/references', method = 'textDocument/references',
bufnr = 1, bufnr = 1,
client_id = 1, client_id = 1
}, {}) }, {})
else else
win, items, width = require('navigator.reference').reference_handler(nil, 'textDocument/references', result, 1, 1) win, items, width = require('navigator.reference').reference_handler(nil,
"textDocument/references",
result, 1, 1)
end end
-- print('win', vim.inspect(win)) print("win", vim.inspect(win))
print('items', vim.inspect(items)) print("items", vim.inspect(items))
eq(win.ctrl.data[1].display_filename, './tests/fixtures/interface.go') eq(win.ctrl.data[1].display_filename, "./interface.go")
eq(win.ctrl.data[2].range.start.line, 14) eq(win.ctrl.data[2].range.start.line, 14)
eq(items[1].display_filename, './tests/fixtures/interface.go') eq(items[1].display_filename, "./interface.go")
-- eq(width, 60) -- eq(width, 60)
end) end)
it('reference handler should return items with thread', function() it("reference handler should return items with thread", function()
vim.wait(200, function() end)
local status = require("plenary.reload").reload_module("navigator")
local status = require("plenary.reload").reload_module("guihua")
vim.cmd([[packadd navigator.lua]])
vim.cmd([[packadd guihua.lua]])
local path = cur_dir .. "/tests/fixtures/interface.go" -- %:p:h ? %:p
print(path)
local cmd = " silent exe 'e " .. path .. "'"
vim.cmd(cmd)
vim.cmd([[cd %:p:h]])
local bufn = vim.fn.bufnr("")
vim.fn.setpos(".", {bufn, 15, 4, 0}) -- width
vim.bo.filetype = "go"
require'navigator'.setup({
debug = true, -- log output, set to true and log path: ~/.local/share/nvim/gh.log
code_action_icon = "A ",
width = 0.75, -- max width ratio (number of cols for the floating window) / (window width)
height = 0.3, -- max list window height, 0.3 by default
preview_height = 0.35, -- max height of preview windows
debug_console_output = true,
border = 'none'
})
_NgConfigValues.debug_console_output = true
vim.bo.filetype = "go"
-- allow gopls start
for i = 1, 10 do
vim.wait(400, function()
end)
local clients = vim.lsp.get_active_clients()
print("clients ", #clients)
if #clients > 0 then
break
end
end
-- allow gopls start
vim.wait(200, function()
end)
local win, items, width local win, items, width
@ -129,15 +197,20 @@ describe('should run lsp reference', function()
win, items, width = require('navigator.reference').ref_view(nil, result, { win, items, width = require('navigator.reference').ref_view(nil, result, {
method = 'textDocument/references', method = 'textDocument/references',
bufnr = 1, bufnr = 1,
client_id = 1, client_id = 1
}, {truncate = 2}) }, {truncate = 2})
else else
win, items, width = require('navigator.reference').reference_handler(nil, 'textDocument/references', result, 1, 1) win, items, width = require('navigator.reference').reference_handler(nil,
"textDocument/references",
result, 1, 1)
end end
-- print('win', vim.inspect(win))
print('items', vim.inspect(items)) print("win", vim.inspect(win))
print("items", vim.inspect(items))
-- eq(win.ctrl.data, "./interface.go") -- eq(win.ctrl.data, "./interface.go")
eq(win.ctrl.data[1].display_filename, './tests/fixtures/interface.go') eq(win.ctrl.data[1].display_filename, "./interface.go")
eq(win.ctrl.data[2].range.start.line, 14) eq(win.ctrl.data[2].range.start.line, 14)
-- eq(items[1].display_filename, "./interface.go") -- eq(items[1].display_filename, "./interface.go")

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

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

@ -1,58 +0,0 @@
[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"

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