From 79fee5dda81bef29fac7604d7f61e8e529418015 Mon Sep 17 00:00:00 2001 From: rayx Date: Sun, 19 Sep 2021 12:42:34 +1000 Subject: [PATCH] Vim diagnostic refactor (#63) * multigrid support * Tuning diagnostic performance, add codelens inline hint function * add ctx to error marker handler * setup lsp_signature from navigator * diagnostic refact PR https://github.com/neovim/neovim/pull/15585 * diagnostic api changes * allow disable emoji/nerdfont icons setup * improve diagnostic/codeaction/codelens preview popup; add seperate line * severity_sort set to reverse * prettier for markdown. code action virtual text show title --- README.md | 184 +++++----- lua/navigator.lua | 21 +- lua/navigator/codeAction.lua | 56 +++- lua/navigator/codelens.lua | 73 +++- lua/navigator/debounce.lua | 29 ++ lua/navigator/diagnostics.lua | 335 ++++++++++++------- lua/navigator/gui.lua | 16 +- lua/navigator/lazyloader.lua | 2 +- lua/navigator/lspclient/attach.lua | 3 +- lua/navigator/lspclient/clients.lua | 5 + lua/navigator/lspclient/highlight.lua | 72 +++- lua/navigator/lspclient/mapping.lua | 16 +- lua/navigator/lspwrapper.lua | 2 +- lua/navigator/{protocal.lua => protocal.txt} | 181 +++++++++- lua/navigator/render.lua | 3 + lua/navigator/util.lua | 14 + 16 files changed, 735 insertions(+), 277 deletions(-) create mode 100644 lua/navigator/debounce.lua rename lua/navigator/{protocal.lua => protocal.txt} (84%) diff --git a/README.md b/README.md index d06604c..84459e7 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,25 @@ The following screenshot shows javascript call tree 🌲 of variable `browser` i ![navigator](https://user-images.githubusercontent.com/1681295/126022829-291a7a2e-4d24-4fde-8293-5ae61562e67d.jpg) Explanation: -- The first line of floating windows shows there are 3 references for the symbol *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 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{}` + will see ` displayName{} <- makeFunc{}` - The third similar to the second, as var browser is on the right side of '=', the value not changed in this line -and emoji is not shown. + and emoji is not shown. #### Example: C++ definition C++ example: search reference and definition ![cpp_ref](https://user-images.githubusercontent.com/1681295/119215215-8bd7a080-bb0f-11eb-82fc-8cdf1955e6e7.jpg) -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. +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(). +`<- f main()` means the definition is inside function main(). #### Golang struct type + Struct type references in multiple Go ﳑ files ![go_reference](https://user-images.githubusercontent.com/1681295/119123823-54b3b180-ba73-11eb-8790-097601e10f6a.gif) @@ -37,6 +39,7 @@ 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 @@ -45,14 +48,14 @@ variable is: # Features: - LSP easy setup. Support the most commonly used lsp clients setup. Dynamic lsp activation based on buffer type. This -also enables you to handle workspace with mixed types of codes (e.g. Go + javascript + yml). A better default setup is -included for LSP clients. + also enables you to handle workspace with mixed types of codes (e.g. Go + javascript + yml). A better default setup is + included for LSP clients. - Out of box experience. 10 lines of minimum vimrc can turn your neovim into a full-featured LSP & Treesitter powered IDE - Unorthodox UI with floating windows, navigator provides a visual way to manage and navigate through symbols, diagnostic errors, reference etc. It covers -all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface -implementation. + all features(handler) provided by LSP from commonly used search reference, to less commonly used search for interface + implementation. - Luv async thread and tasks @@ -71,14 +74,14 @@ implementation. - Grouping references/implementation/incoming/outgoing based on file names. - Treesitter based variable/function context analysis. It is 10x times faster compared to purely rely on LSP. In most -of the case, it takes treesitter less than 4 ms to read and render all nodes for a file of 1,000 LOC. + of the case, it takes treesitter less than 4 ms to read and render all nodes for a file of 1,000 LOC. - The first plugin, IMO, allows you to search in all treesitter symbols in the workspace. - Nerdfont, emoji for LSP and treesitter kind - Optimize display (remove trailing bracket/space), display the caller of reference, de-duplicate lsp results (e.g reference -in the same line). Using treesitter for file preview highlighter etc + in the same line). Using treesitter for file preview highlighter etc - ccls call hierarchy (Non-standard `ccls/call` API) supports @@ -186,9 +189,6 @@ EOF ``` - - - Nondefault configuration example: ```lua @@ -219,7 +219,7 @@ require'navigator'.setup({ code_action_prompt = {enable = true, sign = true, sign_priority = 40, virtual_text = true}, icons = { -- Code action - code_action_icon = " ", + code_action_icon = "🏏", -- Diagnostics diagnostic_head = '🐛', diagnostic_head_severity_1 = "🈲", @@ -268,6 +268,7 @@ require'navigator'.setup({ ### LSP clients Built clients: + ```lua local servers = { "angularls", "gopls", "tsserver", "flow", "bashls", "dockerls", "julials", "pylsp", "pyright", @@ -283,24 +284,24 @@ Navigator will try to load avalible lsp server/client based on filetype. The cli 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 +- 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 pyls+pyright+pyls_ms +The plugin can work with multiple LSP, e.g sqls+gopls+efm. But there are cases you may need to disable some of the +servers. (Prevent loading multiple LSP for same source code.) e.g. I saw strange behaviours when I use pyls+pyright+pyls_ms together. If you have multiple similar LSP installed and have trouble with the plugin, please enable only one at a time. ### Disable a lsp client loading from navigator + To disable a specific LSP, set `filetypes` to {} e.g. ```lua @@ -320,56 +321,54 @@ require'navigator'.setup({ ### Default keymaps -| mode | key | function | -|--- |--- |--- | -| n | gr | show reference and context | -| i | \ | signature help | -| n | \ | signature help | -| n | gW | workspace symbol | -| n | gD | declaration | -| n | g0 | document symbol | -| n | \ | go to definition (if multiple show listview) | -| n | gp | definition | -| n | \ | definition| -| n | g\ | implementation| -| n | gT | treesitter document symbol | -| n | \gT | treesitter symbol for all open buffers | -| n | K | hover doc | -| n | \ca | code action (when you see 💡 ) | -| n | \la | code lens action (when you see a codelens indicator) | -| v | \cA | range code action (when you see 💡 ) | -| n | \rn | rename with floating window| -| n | \re | rename (lsp default)| -| n | \gi | incoming calls| -| n | \go | outgoing calls| -| n | gi | implementation | -| n | \ D | type definition | -| n | gL | show line diagnostic | -| n | gG | show diagnostic for all buffers | -| n | ]d | next diagnostic| -| n | [d | previous diagnostic| -| n | ]r | next treesitter reference/usage| -| n | [r | previous treesitter reference/usage| -| n | \ wa | add workspace folder| -| n | \ wr | remove workspace folder| -| n | \ wl | print workspace folder| -| n | \k | toggle reference highlight | -| i/n | \ | previous item in list| -| i/n | \ | next item in list| -| i/n | number 1~9 | move to ith row/item in the list| -| i/n | \ | previous item in list| -| i/n | \ | next item in list| -| n | \j | move cursor to preview (windows move to bottom view point)| -| n | \k | move cursor to list (windows move to up view point)| -| i/n | \ | open preview file in nvim/Apply action| -| n | \ | open preview file in nvim with vsplit| -| n | \ | open preview file in nvim with split| -| n | \ | open preview file in nvim/Apply action| -| i/n | \ | previous page in listview| -| i/n | \ | next page in listview| -| i/n | \ | save the modification to preview window to file| - - +| mode | key | function | +| ---- | --------------- | ---------------------------------------------------------- | +| n | gr | show reference and context | +| i | \ | signature help | +| n | \ | signature help | +| n | gW | workspace symbol | +| n | gD | declaration | +| n | g0 | document symbol | +| n | \ | go to definition (if multiple show listview) | +| n | gp | definition | +| n | \ | definition | +| n | g\ | implementation | +| n | gT | treesitter document symbol | +| n | \gT | treesitter symbol for all open buffers | +| n | K | hover doc | +| n | \ca | code action (when you see 💡 ) | +| n | \la | code lens action (when you see a codelens indicator) | +| v | \cA | range code action (when you see 💡 ) | +| n | \rn | rename with floating window | +| n | \re | rename (lsp default) | +| n | \gi | incoming calls | +| n | \go | outgoing calls | +| n | gi | implementation | +| n | \ D | type definition | +| n | gL | show line diagnostic | +| n | gG | show diagnostic for all buffers | +| n | ]d | next diagnostic | +| n | [d | previous diagnostic | +| n | ]r | next treesitter reference/usage | +| n | [r | previous treesitter reference/usage | +| n | \ wa | add workspace folder | +| n | \ wr | remove workspace folder | +| n | \ wl | print workspace folder | +| n | \k | toggle reference highlight | +| i/n | \ | previous item in list | +| i/n | \ | next item in list | +| i/n | number 1~9 | move to ith row/item in the list | +| i/n | \ | previous item in list | +| i/n | \ | next item in list | +| n | \j | move cursor to preview (windows move to bottom view point) | +| n | \k | move cursor to list (windows move to up view point) | +| i/n | \ | open preview file in nvim/Apply action | +| n | \ | open preview file in nvim with vsplit | +| n | \ | open preview file in nvim with split | +| n | \ | open preview file in nvim/Apply action | +| i/n | \ | previous page in listview | +| i/n | \ | next page in listview | +| i/n | \ | save the modification to preview window to file | ### Colors/Highlight: @@ -382,11 +381,9 @@ hi default GHTextViewDark guifg=#e0d8f4 guibg=#332e55 hi default GHListDark guifg=#e0d8f4 guibg=#103234 ``` -There are other Lsp highlight been used in this plugin, e.g LspReferenceRead/Text/Write are used for document highlight, +There are other Lsp highlight been used in this plugin, e.g LspReferenceRead/Text/Write are used for document highlight, LspDiagnosticsXXX are used for diagnostic. Please check highlight.lua and dochighlight.lua for more info. - - ## Dependency - lspconfig @@ -399,13 +396,14 @@ The plugin can be loaded lazily (packer `opt = true` ), And it will check if opt The terminal will need to be able to output nerdfont and emoji correctly. I am using Kitty with nerdfont (Victor Mono). - ## Integration with lspinstall + If you'd like to only use the lsp servers installed by lspinstall. Please set ```lua lspinstall = false ``` + In the config ## Usage @@ -431,22 +429,21 @@ require'navigator'.setup({on_attach = function(client, bufnr) require 'illuminat Highlight I am using: -* LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold lua vim.lsp.buf.document_highlight()` -That is where you saw the current symbol been highlighted. +- LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold lua vim.lsp.buf.document_highlight()` + That is where you saw the current symbol been highlighted. -* GHListDark and GHTextViewDark is used for floating listvew and TextView. They are be based on current background -(Normal) and PmenuSel +- GHListDark and GHTextViewDark is used for floating listvew and TextView. They are be based on current background + (Normal) and PmenuSel -* In future, I will use NormalFloat for floating view. But ATM, most of colorscheme does not define NormalFloat +- In future, I will use NormalFloat for floating view. But ATM, most of colorscheme does not define NormalFloat You can override the above highlight to fit your current colorscheme - ## commands -| command | function | -|--- |--- | -| LspToggleFmt | toggle lsp auto format | +| command | function | +| ------------ | ---------------------- | +| LspToggleFmt | toggle lsp auto format | ## Screenshots @@ -465,6 +462,7 @@ Pls check the first part of README ![workspace symbol](https://github.com/ray-x/files/blob/master/img/navigator/workspace_symbol.gif?raw=true) ### highlight document symbol and jump between reference + ![multiple_symbol_hi3](https://user-images.githubusercontent.com/1681295/120067627-f9f80680-c0bf-11eb-9216-18e5c8547f59.gif) # Current symbol highlight and jump backward/forward between symbols @@ -480,7 +478,6 @@ Visual studio code style show errors minimap in scroll bar area ![diagnostic_scroll_bar](https://user-images.githubusercontent.com/1681295/128736430-e365523d-810c-4c16-a3b4-c74969f45f0b.jpg) - Diagnostic in single bufer ![diagnostic](https://github.com/ray-x/files/blob/master/img/navigator/diag.jpg?raw=true) @@ -495,7 +492,6 @@ You can in place edit your code in floating window https://user-images.githubusercontent.com/1681295/121832919-89cbc080-cd0e-11eb-9778-11d0f356b38d.mov - (Note: This feature only avalible in `find reference` and `find diagnostic`, You can not add/remove lines in floating window) ### Implementation @@ -549,14 +545,12 @@ Codelens for C++/ccls. Symbol reference ![codelens_cpp_ccls](https://user-images.githubusercontent.com/1681295/132429134-abc6547e-79cc-44a4-b7a9-23550b895e51.jpg) - - - ### Predefined LSP symbol nerdfont/emoji ![nerdfont](https://github.com/ray-x/files/blob/master/img/navigator/icon_nerd.jpg?raw=true) # Break changes and known issues + [known issues I am working on](https://github.com/ray-x/navigator.lua/issues/1) # Todo diff --git a/lua/navigator.lua b/lua/navigator.lua index 4923317..a97215d 100644 --- a/lua/navigator.lua +++ b/lua/navigator.lua @@ -8,6 +8,7 @@ _NgConfigValues = { preview_lines_before = 5, -- lines before the highlight line default_mapping = true, keymaps = {}, -- e.g keymaps={{key = "GR", func = "references()"}, } this replace gr default mapping + external = nil, -- true: enable for goneovim multigrid otherwise false border = "single", -- border style, can be one of 'none', 'single', 'double', "shadow" combined_attach = "both", -- both: use both customized attach and navigator default attach, mine: only use my attach defined in vimrc @@ -33,7 +34,7 @@ _NgConfigValues = { -- to load those files diagnostic_virtual_text = true, -- show virtual for diagnostic message diagnostic_update_in_insert = false, -- update diagnostic message in insert mode - diagnostic_scrollbar_sign = {'▃', '█'}, -- set to nil to disable, set to {'╍', 'ﮆ'} to enable diagnostic status in scroll bar area + diagnostic_scrollbar_sign = {'▃', '▆', '█'}, -- set to nil to disable, set to {'╍', 'ﮆ'} to enable diagnostic status in scroll bar area tsserver = { -- filetypes = {'typescript'} -- disable javascript etc, -- set to {} to disable the lspclient for all filetype @@ -46,16 +47,22 @@ _NgConfigValues = { }, lspinstall = false, -- set to true if you would like use the lsp installed by lspinstall icons = { + icons = true, -- set to false to use system default ( if you using a terminal does not have nerd/icon) -- Code action - code_action_icon = " ", + code_action_icon = "🏏", -- "", -- code lens code_lens_action_icon = " ", -- Diagnostics diagnostic_head = '🐛', + diagnostic_err = "📛", + diagnostic_warn = "👎", + diagnostic_info = [[👩]], + diagnostic_hint = [[💁]], + diagnostic_head_severity_1 = "🈲", diagnostic_head_severity_2 = "☣️", diagnostic_head_severity_3 = "👎", - diagnostic_head_description = "📛", + diagnostic_head_description = "👹", diagnostic_virtual_text = "🦊", diagnostic_file = "🚑", -- Values @@ -131,7 +138,7 @@ M.setup = function(cfg) -- log("navigator loader") if _NgConfigValues.code_action_prompt.enable then - vim.cmd [[autocmd CursorHold * lua require'navigator.codeAction'.code_action_prompt()]] + vim.cmd [[autocmd CursorHold,CursorHoldI * lua require'navigator.codeAction'.code_action_prompt()]] end -- vim.cmd("autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4") if not _NgConfigValues.loaded then @@ -141,12 +148,6 @@ M.setup = function(cfg) if _NgConfigValues.ts_fold == true then require('navigator.foldts').on_attach() end - - --- if code line enabled - if _NgConfigValues.lsp.code_lens then - require("navigator.codelens").setup() - end - end return M diff --git a/lua/navigator/codeAction.lua b/lua/navigator/codeAction.lua index 6906cf3..b71c461 100644 --- a/lua/navigator/codeAction.lua +++ b/lua/navigator/codeAction.lua @@ -5,9 +5,13 @@ local code_action = {} local gui = require "navigator.gui" local config = require("navigator").config_values() local api = vim.api + +local sign_name = "NavigatorLightBulb" + +local diagnostic = vim.diagnostic or vim.lsp.diagnostic code_action.code_action_handler = util.mk_handler(function(err, actions, ctx, cfg) log(actions, ctx) - if actions == nil or vim.tbl_isempty(actions) then + if actions == nil or vim.tbl_isempty(actions) or err then print("No code actions available") return end @@ -19,13 +23,16 @@ code_action.code_action_handler = util.mk_handler(function(err, actions, ctx, cf table.insert(data, title) actions[i].display_title = title end - local width = 0 + 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 apply = require('navigator.lspwrapper').apply_action local function apply_action(action) local action_chosen = nil @@ -42,7 +49,7 @@ code_action.code_action_handler = util.mk_handler(function(err, actions, ctx, cf apply(action_chosen) end - gui.new_list_view { + local listview = gui.new_list_view { items = data, width = width + 4, loc = "top_center", @@ -58,6 +65,9 @@ code_action.code_action_handler = util.mk_handler(function(err, actions, ctx, cf return pos end } + + log("new buffer", listview.bufnr) + vim.api.nvim_buf_add_highlight(listview.bufnr, -1, 'Title', 0, 0, -1) end) -- https://github.com/glepnir/lspsaga.nvim/blob/main/lua/lspsaga/codeaction.lua @@ -71,22 +81,17 @@ local get_current_winid = function() return api.nvim_get_current_win() end -local sign_name = "NavigatorLightBulb" - -if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then - vim.fn.sign_define(sign_name, - {text = config.icons.code_action_icon, texthl = "LspDiagnosticsSignHint"}) -end - -local function _update_virtual_text(line) +local function _update_virtual_text(line, actions) local namespace = get_namespace() pcall(api.nvim_buf_clear_namespace, 0, namespace, 0, -1) if line then + log(line, actions) local icon_with_indent = " " .. config.icons.code_action_icon + local title = actions[1].title pcall(api.nvim_buf_set_extmark, 0, namespace, line, -1, { - virt_text = {{icon_with_indent, "LspDiagnosticsSignHint"}}, + virt_text = {{icon_with_indent .. title, "LspDiagnosticsSignHint"}}, virt_text_pos = "overlay", hl_mode = "combine" }) @@ -94,6 +99,11 @@ local function _update_virtual_text(line) end local function _update_sign(line) + + if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then + vim.fn.sign_define(sign_name, + {text = config.icons.code_action_icon, texthl = "LspDiagnosticsSignHint"}) + end local winid = get_current_winid() if code_action[winid] == nil then code_action[winid] = {} @@ -110,11 +120,14 @@ local function _update_sign(line) end end -local need_check_diagnostic = {["go"] = true, ["python"] = true} +-- local need_check_diagnostic = {["go"] = true, ["python"] = true} +local need_check_diagnostic = {['python'] = true} function code_action:render_action_virtual_text(line, diagnostics) - return function(_, _, actions) + return function(err, actions, context) + log(err, line, diagnostics, actions, context) if actions == nil or type(actions) ~= "table" or vim.tbl_isempty(actions) then + -- no actions cleanup if config.code_action_prompt.virtual_text then _update_virtual_text(nil) end @@ -127,6 +140,7 @@ function code_action:render_action_virtual_text(line, diagnostics) if next(diagnostics) == nil then _update_sign(nil) else + -- no diagnostic, no code action sign.. _update_sign(line) end else @@ -139,10 +153,10 @@ function code_action:render_action_virtual_text(line, diagnostics) if next(diagnostics) == nil then _update_virtual_text(nil) else - _update_virtual_text(line) + _update_virtual_text(line, actions) end else - _update_virtual_text(line) + _update_virtual_text(line, actions) end end end @@ -186,7 +200,15 @@ code_action.code_action_prompt = function() return end - local diagnostics = vim.lsp.diagnostic.get_line_diagnostics() + local diagnostics + if diagnostic.get_line_diagnostics then + -- old version + diagnostics = diagnostic.get_line_diagnostics() + else + local lnum = vim.api.nvim_win_get_cursor(0)[1] - 1 + diagnostics = diagnostic.get(vim.api.nvim_get_current_buf(), {lnum = lnum}) + end + local winid = get_current_winid() code_action[winid] = code_action[winid] or {} code_action[winid].lightbulb_line = code_action[winid].lightbulb_line or 0 diff --git a/lua/navigator/codelens.lua b/lua/navigator/codelens.lua index 8a364f3..27a7a80 100644 --- a/lua/navigator/codelens.lua +++ b/lua/navigator/codelens.lua @@ -69,7 +69,9 @@ function M.setup() 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) + -- 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) @@ -139,8 +141,12 @@ function M.run_action() end apply(action_chosen) end + + local divider = string.rep('─', width + 2) + + table.insert(data, 2, divider) if #data > 0 then - gui.new_list_view { + local lv = gui.new_list_view { items = data, width = width + 4, loc = "top_center", @@ -156,7 +162,70 @@ function M.run_action() return pos end } + + vim.api.nvim_buf_add_highlight(lv.bufnr, -1, 'Title', 0, 0, -1) + 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 + +M.inline = function() + local lsp = vim.lsp + if is_enabled == false then + return + end + if vim.fn.getcmdwintype() == ':' then + return end + if #vim.lsp.buf_get_clients() == 0 then + return + end + + local bufnr = api.nvim_get_current_buf() + local parameter = lsp.util.make_position_params() + local response = lsp.buf_request_sync(bufnr, "textDocument/codeLens", parameter) + + -- Clear previous highlighting + api.nvim_buf_clear_namespace(bufnr, virtual_types_ns, 0, -1) + + if response then + log(response) + for _, v in ipairs(response) do + if v == nil or v.result == nil then + return + 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 + -- else + -- api.nvim_command("echohl WarningMsg | echo 'VirtualTypes: No response' | echohl None") + end + end return M diff --git a/lua/navigator/debounce.lua b/lua/navigator/debounce.lua new file mode 100644 index 0000000..b9d9f76 --- /dev/null +++ b/lua/navigator/debounce.lua @@ -0,0 +1,29 @@ +local M = {} + +function M.debounce_trailing(ms, fn) + local timer = vim.loop.new_timer() + return function(...) + local argv = {...} + timer:start(ms, 0, function() + timer:stop() + fn(unpack(argv)) + end) + end +end + +function M.throttle_leading(ms, fn) + local timer = vim.loop.new_timer() + local running = false + return function(...) + if not running then + timer:start(ms, 0, function() + running = false + timer:stop() + end) + running = true + fn(...) + end + end +end + +return M diff --git a/lua/navigator/diagnostics.lua b/lua/navigator/diagnostics.lua index be657f9..c3abb88 100644 --- a/lua/navigator/diagnostics.lua +++ b/lua/navigator/diagnostics.lua @@ -1,11 +1,11 @@ local gui = require "navigator.gui" local diagnostic_list = {} - -_NG_VT_NS = vim.api.nvim_create_namespace("navigator_lua") +local diagnostic = vim.diagnostic or vim.lsp.diagnostic +-- local hide = diagnostic.hide or diagnostic.clear +_NG_VT_DIAG_NS = vim.api.nvim_create_namespace("navigator_lua_diag") local util = require "navigator.util" local log = util.log local trace = require"guihua.log".trace --- trace = log local error = util.error local path_sep = require"navigator.util".path_sep() @@ -13,101 +13,160 @@ local mk_handler = require"navigator.util".mk_handler local path_cur = require"navigator.util".path_cur() diagnostic_list[vim.bo.filetype] = {} -local function error_marker(result, client_id) - if _NgConfigValues.lsp.diagnostic_scrollbar_sign == nil then -- not enabled or already shown +local function clear_diag_VT(bufnr) -- important for clearing out when no more errors + log(bufnr, _NG_VT_DIAG_NS) + if bufnr == nil or _NG_VT_DIAG_NS == nil then return end - local first_line = vim.fn.line('w0') - -- local rootfolder = vim.fn.expand('%:h:t') -- get the current file root folder - trace(result) - local bufnr = vim.uri_to_bufnr(result.uri) - if bufnr ~= vim.api.nvim_get_current_buf() then - -- log("not same buf", client_id, result.uri, bufnr, vim.fn.bufnr()) - return + vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) + _NG_VT_DIAG_NS = nil +end + +local function get_count(bufnr, level) + if vim.diagnostic ~= nil then + return #diagnostic.get(bufnr, {severity = level}) + else + return diagnostic.get_count(bufnr, level) end - trace(result, bufnr) +end - if result == nil or result.diagnostics == nil or #result.diagnostics == 0 then - local diag_cnt = vim.lsp.diagnostic.get_count(bufnr, [[Error]]) - + vim.lsp.diagnostic.get_count(bufnr, [[Warning]]) - if diag_cnt == 0 and _NG_VT_NS ~= nil then - vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_NS, 0, -1) +local function error_marker(result, ctx, config) + vim.defer_fn(function() + if vim.tbl_isempty(result.diagnostics) then + return end - return - end + if _NgConfigValues.lsp.diagnostic_scrollbar_sign == nil then -- not enabled or already shown + return + end + local first_line = vim.fn.line('w0') + -- local rootfolder = vim.fn.expand('%:h:t') -- get the current file root folder - -- total line num of current buffer + local bufnr = ctx.bufnr + if bufnr == nil then + bufnr = vim.uri_to_bufnr(result.uri) + end + local fname = vim.api.nvim_buf_get_name(bufnr) + local uri = vim.uri_from_fname(fname) + if uri ~= result.uri then + log("not same buf", ctx, result.uri, bufnr, vim.fn.bufnr()) + return + end - -- local winid = vim.fn.win_getid(vim.fn.winnr()) - -- local winid = vim.api.nvim_get_current_win() - bufnr = vim.api.nvim_get_current_buf() - local total_num = vim.fn.getbufinfo(bufnr)[1].linecount - -- local total_num = vim.fn.getbufinfo(vim.fn.winbufnr(winid))[1].linecount - -- window size of current buffer + if not vim.api.nvim_buf_is_loaded(bufnr) then + log("buf not loaded") + return - local stats = vim.api.nvim_list_uis()[1] - local wwidth = stats.width; - local wheight = stats.height; + end - -- local wwidth = vim.fn.winwidth(winid) - -- local wheight = vim.fn.winheight(winid) - if total_num <= wheight then - return - end - if _NG_VT_NS == nil then - _NG_VT_NS = vim.api.nvim_create_namespace("navigator_lua") - end + trace('schedule callback', result, ctx, config) + -- trace(result, bufnr) - vim.api.nvim_buf_clear_namespace(0, _NG_VT_NS, 0, -1) - if total_num <= wheight then - first_line = 0 - end - local pos = {} - -- pos of virtual text - for _, diag in pairs(result.diagnostics) do - if diag.range and diag.range.start and diag.range.start.line then - local p = diag.range.start.line - p = util.round(p * wheight / math.max(wheight, total_num)) - if pos[#pos] and pos[#pos].line == p then - pos[#pos] = { - line = p, - sign = _NgConfigValues.lsp.diagnostic_scrollbar_sign[2], - severity = diag.severity - } - else - table.insert(pos, { - line = p, - sign = _NgConfigValues.lsp.diagnostic_scrollbar_sign[1], - severity = diag.severity - }) + if result == nil or result.diagnostics == nil or #result.diagnostics == 0 then + local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) + if diag_cnt == 0 and _NG_VT_DIAG_NS ~= nil then + log("great no errors") + vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) end + return end - trace("pos", pos, diag.range.start) - end - for i, s in pairs(pos) do - local hl = 'ErrorMsg' - if s.severity > 1 then - hl = 'WarningMsg' + -- total line num of current buffer + + -- local winid = vim.fn.win_getid(vim.fn.winnr()) + -- local winid = vim.api.nvim_get_current_win() + local total_num = vim.api.nvim_buf_line_count(bufnr) + -- local total_num = vim.fn.getbufinfo(vim.fn.winbufnr(winid))[1].linecount + -- window size of current buffer + + local stats = vim.api.nvim_list_uis()[1] + -- local wwidth = stats.width; + local wheight = stats.height; + + if total_num <= wheight then + return end - local l = s.line + first_line - if l > total_num then - l = total_num + if _NG_VT_DIAG_NS == nil then + _NG_VT_DIAG_NS = vim.api.nvim_create_namespace("navigator_lua_diag") end - vim.api.nvim_buf_set_extmark(bufnr, _NG_VT_NS, l, -1, - {virt_text = {{s.sign, hl}}, virt_text_pos = 'right_align'}) - end + + local pos = {} + local diags = result.diagnostics + + for i, _ in ipairs(diags) do + if not diags[i].range then + diags[i].range = {start = {line = diags[i].lnum}} + end + end + + table.sort(diags, function(a, b) + return a.range.start.line < b.range.start.line + end) + -- pos of virtual text + for _, diag in pairs(result.diagnostics) do + local p + if not diag.range then + diag.range = {start = {line = diag.lnum}} + end + if diag.range and diag.range.start and diag.range.start.line then + p = diag.range.start.line + p = util.round(p * wheight / math.max(wheight, total_num)) + if pos[#pos] and pos[#pos].line == p then + local bar = '▆' + if pos[#pos] == _NgConfigValues.lsp.diagnostic_scrollbar_sign[2] then + bar = '█' + end + pos[#pos] = {line = p, sign = bar, severity = math.min(diag.severity, pos[#pos].severity)} + else + table.insert(pos, { + line = p, + sign = _NgConfigValues.lsp.diagnostic_scrollbar_sign[1], + severity = diag.severity + }) + end + end + trace("pos, line:", p, diag.severity, diag.range) + end + + if not vim.tbl_isempty(pos) then + vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) + end + for i, s in pairs(pos) do + local hl = 'ErrorMsg' + if s.severity == 2 then + hl = 'WarningMsg' + else + hl = 'DiagnosticInfo' + end + local l = s.line + first_line + if l > total_num then + l = total_num + end + trace("add pos", s, bufnr) + + vim.api.nvim_buf_set_extmark(bufnr, _NG_VT_DIAG_NS, l, -1, + {virt_text = {{s.sign, hl}}, virt_text_pos = 'right_align'}) + end + + end, 10) -- defer in 10ms +end + +local update_err_marker_async = function() + local debounce = require'navigator.debounce'.debounce_trailing + return debounce(20, error_marker) end local diag_hdlr = mk_handler(function(err, result, ctx, config) - trace(result) + + require"navigator.lspclient.highlight".diagnositc_config_sign() if err ~= nil then - log(err, config) + log(err, config, result) return end - if vim.fn.mode() ~= 'n' and config.update_in_insert == false then + + local mode = vim.api.nvim_get_mode().mode + if mode ~= 'n' and config.update_in_insert == false then log("skip in insert mode") return end @@ -116,23 +175,26 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config) if diagnostic_list[ft] == nil then diagnostic_list[vim.bo.filetype] = {} end - -- vim.lsp.diagnostic.clear(vim.fn.bufnr(), client.id, nil, nil) + + local client_id = ctx.client_id + -- not sure if I should do this hack + if vim.tbl_isempty(result.diagnostics) then + if vim.api.nvim_buf_is_loaded(ctx.bufnr) then + -- diagnostic.reset(ctx.client_id) + -- clear_diag_VT(ctx.bufnr) + end + return + end + if util.nvim_0_6() then + trace(err, result, ctx, config) vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config) else log("old version of lsp nvim 050") vim.lsp.diagnostic.on_publish_diagnostics(err, _, result, ctx.client_id, _, config) end local uri = result.uri - if err then - log("diag", err, result) - return - end - if vim.fn.mode() ~= 'n' and config.update_in_insert == false then - log("skip in insert mode") - return - end - trace("diag: ", vim.fn.mode(), result, ctx, config) + -- trace("diag: ", mode, result, ctx, config) if result and result.diagnostics then local item_list = {} for _, v in ipairs(result.diagnostics) do @@ -155,6 +217,7 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config) local bufnr1 = vim.uri_to_bufnr(uri) if not vim.api.nvim_buf_is_loaded(bufnr1) then if _NgConfigValues.diagnostic_load_files then + -- print('load buffers') vim.fn.bufload(bufnr1) -- this may slow down the neovim local pos = v.range.start local row = pos.line @@ -178,10 +241,12 @@ local diag_hdlr = mk_handler(function(err, result, ctx, config) result.uri = uri end - error_marker(result, ctx.client_id) + local marker = update_err_marker_async() + marker(result, ctx, config) else - vim.api.nvim_buf_clear_namespace(0, _NG_VT_NS, 0, -1) - _NG_VT_NS = nil + trace("great, no diag errors") + vim.api.nvim_buf_clear_namespace(0, _NG_VT_DIAG_NS, 0, -1) + _NG_VT_DIAG_NS = nil end end) @@ -196,9 +261,7 @@ local diagnostic_cfg = { -- and on, using buffer local variables signs = true, update_in_insert = _NgConfigValues.lsp.diagnostic_update_in_insert or false, - severity_sort = function(a, b) - return a.severity < b.severity - end + severity_sort = {reverse = true} } if _NgConfigValues.lsp.diagnostic_virtual_text == false then @@ -209,24 +272,13 @@ end M.diagnostic_handler = vim.lsp.with(diag_hdlr, diagnostic_cfg) M.hide_diagnostic = function() - if _NG_VT_NS then - vim.api.nvim_buf_clear_namespace(0, _NG_VT_NS, 0, -1) - _NG_VT_NS = nil + if _NG_VT_DIAG_NS then + vim.api.nvim_buf_clear_namespace(0, _NG_VT_DIAG_NS, 0, -1) + _NG_VT_DIAG_NS = nil end end M.show_diagnostic = function() - vim.lsp.diagnostic.get_all() - - local bufs = vim.api.nvim_list_bufs() - for _, buf in ipairs(bufs) do - local bname = vim.fn.bufname(buf) - if #bname > 0 and not util.exclude(bname) then - if vim.api.nvim_buf_is_loaded(buf) then - vim.lsp.diagnostic.get(buf, nil) - end - end - end 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]}) @@ -239,56 +291,81 @@ M.show_diagnostic = function() end -- log(display_items) if #display_items > 0 then - gui.new_list_view({ + local listview = gui.new_list_view({ items = display_items, api = _NgConfigValues.icons.diagnostic_file .. _NgConfigValues.icons.diagnostic_head .. " Diagnostic ", enable_preview_edit = true }) + trace("new buffer", listview.bufnr) + vim.api.nvim_buf_add_highlight(listview.bufnr, -1, 'Title', 0, 0, -1) end end end --- set quickfix win +-- set loc list win M.set_diag_loclist = function() + + local bufnr = vim.api.nvim_get_current_buf() + local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) + if diag_cnt == 0 then + log("great, no errors!") + return + end + local clients = vim.lsp.buf_get_clients(0) + local cfg = {open = diag_cnt > 0} + for _, client in pairs(clients) do + cfg.client_id = client['id'] + break + end + if not vim.tbl_isempty(vim.lsp.buf_get_clients(0)) then - local err_cnt = vim.lsp.diagnostic.get_count(0, [[Error]]) + local err_cnt = get_count(0, [[Error]]) if err_cnt > 0 and _NgConfigValues.lsp.disply_diagnostic_qf then - vim.lsp.diagnostic.set_loclist() + if diagnostic.set_loclist then + diagnostic.set_loclist(cfg) + else + cfg.namespaces = diagnostic.get_namespace(nil) + diagnostic.setloclist(cfg) + end else vim.cmd("lclose") end end end -local function clear_diag_VT() -- important for clearing out when no more errors - vim.api.nvim_buf_clear_namespace(0, _NG_VT_NS, 0, -1) - _NG_VT_NS = nil -end - -- TODO: callback when scroll function M.update_err_marker() - if _NG_VT_NS == nil then + trace("update err marker", _NG_VT_DIAG_NS) + if _NG_VT_DIAG_NS == nil then -- nothing to update return end local bufnr = vim.api.nvim_get_current_buf() - local diag_cnt = vim.lsp.diagnostic.get_count(bufnr, [[Error]]) - + vim.lsp.diagnostic.get_count(bufnr, [[Warning]]) - if diag_cnt == 0 and _NG_VT_NS ~= nil then - vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_NS, 0, -1) + local diag_cnt = get_count(bufnr, [[Error]]) + get_count(bufnr, [[Warning]]) + + get_count(bufnr, [[Info]]) + get_count(bufnr, [[Hint]]) + + -- redraw + if diag_cnt == 0 and _NG_VT_DIAG_NS ~= nil then + + vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) + trace("no errors") return end - -- redraw - vim.api.nvim_buf_clear_namespace(0, _NG_VT_NS, 0, -1) - local errors = vim.lsp.diagnostic.get(bufnr) + vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1) + local errors = diagnostic.get(bufnr) if #errors == 0 then + trace("errors", errors) return end - local result = {diagnostics = errors, uri = errors[1].uri} - error_marker(result) + local uri = vim.uri_from_bufnr(bufnr) + local result = {diagnostics = errors, uri = errors[1].uri or uri} + + trace(result) + local marker = update_err_marker_async() + marker(result, {bufnr = bufnr, method = 'textDocument/publishDiagnostics'}) end -- TODO: update the marker @@ -300,4 +377,14 @@ if _NgConfigValues.lsp.diagnostic_scrollbar_sign then vim.cmd [[autocmd WinScrolled * lua require'navigator.diagnostics'.update_err_marker()]] end +function M.get_line_diagnostic() + local lnum = vim.api.nvim_win_get_cursor(0)[1] - 1 + return diagnostic.get(vim.api.nvim_get_current_buf(), {lnum = lnum}) +end + +function M.show_line_diagnostics() + local lnum = vim.api.nvim_win_get_cursor(0)[1] - 1 + diagnostic.show_line_diagnostics({border = 'single'}, vim.api.nvim_get_current_buf(), lnum) +end + return M diff --git a/lua/navigator/gui.lua b/lua/navigator/gui.lua index 9cd3972..7a47f41 100644 --- a/lua/navigator/gui.lua +++ b/lua/navigator/gui.lua @@ -58,6 +58,11 @@ function M._preview_location(opts) -- location, width, pos_x, pos_y uri = uri, allow_edit = opts.enable_edit } + + if _NgConfigValues.external then + win_opts.external = true + 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 @@ -84,7 +89,8 @@ function M._preview_location(opts) -- location, width, pos_x, pos_y border = opts.border, display_range = win_opts.display_range, hl_line = win_opts.hl_line, - allow_edit = win_opts.allow_edit + allow_edit = win_opts.allow_edit, + external = win_opts.external }) return w end @@ -125,7 +131,7 @@ function M.new_list_view(opts) 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) + 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( @@ -173,6 +179,10 @@ function M.new_list_view(opts) 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, @@ -186,6 +196,7 @@ function M.new_list_view(opts) -- 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 {} @@ -213,6 +224,7 @@ function M.new_list_view(opts) offset_x = 0, offset_y = offset_y, border = border, + external = ext, enable_edit = opts.enable_preview_edit or false }) end diff --git a/lua/navigator/lazyloader.lua b/lua/navigator/lazyloader.lua index bc4cd45..fe7fd34 100644 --- a/lua/navigator/lazyloader.lua +++ b/lua/navigator/lazyloader.lua @@ -1,7 +1,7 @@ local log = require"navigator.util".log _LoadedClients = {} local loader = nil -packer_plugins = packer_plugins or nil -- suppress warnings +local packer_plugins = packer_plugins or nil -- suppress warnings -- packer only if packer_plugins ~= nil then -- packer install diff --git a/lua/navigator/lspclient/attach.lua b/lua/navigator/lspclient/attach.lua index 39bd38a..743f072 100644 --- a/lua/navigator/lspclient/attach.lua +++ b/lua/navigator/lspclient/attach.lua @@ -14,6 +14,7 @@ local M = {} M.on_attach = function(client, bufnr) local uri = vim.uri_from_bufnr(bufnr) + if uri == "file://" or uri == "file:///" or #uri < 11 then log("skip for float buffer", uri) return {error = "invalid file", result = nil} @@ -24,7 +25,7 @@ M.on_attach = function(client, bufnr) diagnostic_map(bufnr) -- add highlight for Lspxxx require"navigator.lspclient.highlight".add_highlight() - + require"navigator.lspclient.highlight".diagnositc_config_sign() api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc") require("navigator.lspclient.mapping").setup({ diff --git a/lua/navigator/lspclient/clients.lua b/lua/navigator/lspclient/clients.lua index db06b0b..3c9e572 100644 --- a/lua/navigator/lspclient/clients.lua +++ b/lua/navigator/lspclient/clients.lua @@ -518,6 +518,11 @@ local function setup(user_opts) wait_lsp_startup(ft, retry, lsp_opts) + --- if code line enabled + if _NgConfigValues.lsp.code_lens then + require("navigator.codelens").setup() + end + _LoadedClients[ft] = true -- _LoadedClients[ft] = vim.tbl_extend("keep", _LoadedClients[ft] or {}, {ft}) diff --git a/lua/navigator/lspclient/highlight.lua b/lua/navigator/lspclient/highlight.lua index d209cfc..f14cdd0 100644 --- a/lua/navigator/lspclient/highlight.lua +++ b/lua/navigator/lspclient/highlight.lua @@ -1,20 +1,62 @@ local M = {} + +local log = require"navigator.util".log local api = vim.api -- lsp sign          ﮻         ﯭ        ﳀ   function M.diagnositc_config_sign() - vim.fn.sign_define('LspDiagnosticsSignError', - {text = '', texthl = 'LspDiagnosticsSignError', linehl = '', numhl = ''}) - vim.fn.sign_define('LspDiagnosticsSignWarning', - {text = '', texthl = 'LspDiagnosticsSignWarning', linehl = '', numhl = ''}) - vim.fn.sign_define('LspDiagnosticsSignInformation', { - text = '', - texthl = 'LspDiagnosticsSignInformation', - linehl = '', - numhl = '' - }) - vim.fn.sign_define('LspDiagnosticsSignHint', - {text = '💡', texthl = 'LspDiagnosticsSignHint', linehl = '', numhl = ''}) + if M.configed then + return + end + local icons = _NgConfigValues.icons + + local sign_name = "NavigatorLightBulb" + if vim.fn.sign_getdefined(sign_name).text == nil then + + vim.fn + .sign_define(sign_name, {text = icons.code_action_icon, texthl = "LspDiagnosticsSignHint"}) + + sign_name = "NavigatorCodeLensLightBulb" + vim.fn.sign_define(sign_name, + {text = icons.code_lens_action_icon, texthl = "LspDiagnosticsSignHint"}) + end + + local e, w, i, h = icons.diagnostic_err, icons.diagnostic_warn, icons.diagnostic_info, + icons.diagnostic_hint + if vim.diagnostic ~= nil then + local t = vim.fn.sign_getdefined('DiagnosticSignWarn') + if vim.tbl_isempty(t) or t[1].text == "W " and icons.icons == true then + + vim.fn.sign_define('DiagnosticSignError', + {text = e, texthl = 'DiagnosticError', linehl = '', numhl = ''}) + vim.fn.sign_define('DiagnosticSignWarn', + {text = w, texthl = 'DiagnosticWarn', linehl = '', numhl = ''}) + vim.fn.sign_define('DiagnosticSignInfo', + {text = i, texthl = 'DiagnosticInfo', linehl = '', numhl = ''}) + vim.fn.sign_define('DiagnosticSignHint', + {text = h, texthl = 'DiagnosticHint', linehl = '', numhl = ''}) + + t = vim.fn.sign_getdefined('DiagnosticSignWarn') + log('*** t ', t, "diagnostic add sign") + 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 end function M.add_highlight() @@ -24,8 +66,12 @@ function M.add_highlight() api.nvim_command("hi! link LspDiagnosticsUnderlineWarning SpellRare") api.nvim_command("hi! link LspDiagnosticsUnderlineInformation SpellRare") api.nvim_command("hi! link LspDiagnosticsUnderlineHint SpellRare") - api.nvim_command("hi def link NGPreviewTitle Title") + api.nvim_command("hi! link DiagnosticUnderlineError SpellBad") + api.nvim_command("hi! link DiagnosticUnderlineWarning SpellRare") + api.nvim_command("hi! link DiagnosticUnderlineInformation SpellRare") + api.nvim_command("hi! link DiagnosticUnderlineHint SpellRare") + api.nvim_command("hi def link NGPreviewTitle Title") local colors = { {'#aefe00', '#aede00', '#aebe00', '#4e7efe'}, {'#ff00e0', '#df00e0', '#af00e0', '#fedefe'}, {'#1000ef', '#2000df', '#2000cf', '#f0f040'}, {'#d8a8a3', '#c8a8a3', '#b8a8a3', '#4e2c33'}, diff --git a/lua/navigator/lspclient/mapping.lua b/lua/navigator/lspclient/mapping.lua index bc1050a..ede1920 100644 --- a/lua/navigator/lspclient/mapping.lua +++ b/lua/navigator/lspclient/mapping.lua @@ -31,7 +31,7 @@ local key_maps = { {key = "go", func = "outgoing_calls()"}, {key = "gi", func = "implementation()"}, {key = "D", func = "type_definition()"}, - {key = "gL", func = "diagnostic.show_line_diagnostics( { border = 'single' })"}, + {key = "gL", func = "require('navigator.diagnostics').show_line_diagnostics()"}, {key = "gG", func = "require('navigator.diagnostics').show_diagnostic()"}, {key = "]d", func = "diagnostic.goto_next({ border = 'single' })"}, {key = "[d", func = "diagnostic.goto_prev({ border = 'single' })"}, @@ -119,7 +119,11 @@ local function set_mapping(user_opts) if string.find(value.func, "require") then f = "lua " .. value.func .. "" elseif string.find(value.func, "diagnostic") then - f = "lua vim.lsp." .. value.func .. "" + local diagnostic = 'lua vim.' + if vim.lsp.diagnostic ~= nil then + diagnostic = 'lua vim.lsp.' + end + f = diagnostic .. value.func .. "" elseif string.find(value.func, "vim.") then f = "lua " .. value.func .. "" end @@ -241,9 +245,11 @@ function M.setup(user_opts) require"navigator.diagnostics".diagnostic_handler -- TODO: when active signature merge to neovim, remove this setup: - local hassig, sig = pcall(require, "lsp_signature") - if hassig then - if _NgConfigValues.signature_help_cfg then + + if _NgConfigValues.signature_help_cfg then + log("setup signature from navigator") + local hassig, sig = pcall(require, "lsp_signature") + if hassig then sig.setup(_NgConfigValues.signature_help_cfg) end else diff --git a/lua/navigator/lspwrapper.lua b/lua/navigator/lspwrapper.lua index 7f73ce4..32d70ea 100644 --- a/lua/navigator/lspwrapper.lua +++ b/lua/navigator/lspwrapper.lua @@ -40,7 +40,7 @@ local function check_lhs(text, symbol) return false end if s < eq and eq ~= eq2 then - log(symbol, "modified") + trace(symbol, "modified") end if eq == eq3 + 1 then return false diff --git a/lua/navigator/protocal.lua b/lua/navigator/protocal.txt similarity index 84% rename from lua/navigator/protocal.lua rename to lua/navigator/protocal.txt index 0c4d546..e952e94 100644 --- a/lua/navigator/protocal.lua +++ b/lua/navigator/protocal.txt @@ -1,4 +1,4 @@ -parameter +-- [[ -- parameter { position = { character = 6, @@ -7,8 +7,7 @@ parameter textDocument = { uri = "file:///Users/username/lsp_test/go/interface.go" } -} - +} ]] --[[ -- incomming/outgoing @@ -103,8 +102,8 @@ dir from result { { - --- [[ locations/reference from lsp +-- locations/reference from lsp +-- [[ { { range = { ["end"] = { @@ -130,7 +129,7 @@ dir from result { { }, uri = "file:///Users/username/lsp-test/go/interface.go" } } ---]] +]] -- -- definition @@ -1124,3 +1123,173 @@ definition.lua:9: { { } } } } + + +-- rust code lens + + +{ { + result = { { + command = { + arguments = { { + args = { + cargoArgs = { "run", "--package", "hello", "--bin", "hello" }, + cargoExtraArgs = {}, + executableArgs = {}, + workspaceRoot = "/Users/ray.xu/lsp_test/rust" + }, + kind = "cargo", + label = "run hello", + location = { + targetRange = { + end = { + character = 1, + line = 68 + }, + start = { + character = 0, + line = 45 + } + }, + targetSelectionRange = { + end = { + character = 7, + line = 45 + }, + start = { + character = 3, + line = 45 + } + }, + targetUri = "file:///Users/ray.xu/lsp_test/rust/src/main.rs" + } + } }, + command = "rust-analyzer.runSingle", + title = "▶︎ Run " + }, + range = { + end = { + character = 7, + line = 45 + }, + start = { + character = 3, + line = 45 + } + } + }, { + command = { + arguments = { { + args = { + cargoArgs = { "run", "--package", "hello", "--bin", "hello" }, + cargoExtraArgs = {}, + executableArgs = {}, + workspaceRoot = "/Users/ray.xu/lsp_test/rust" + }, + kind = "cargo", + label = "run hello", + location = { + targetRange = { + end = { + character = 1, + line = 68 + }, + start = { + character = 0, + line = 45 + } + }, + targetSelectionRange = { + end = { + character = 7, + line = 45 + }, + start = { + character = 3, + line = 45 + } + }, + targetUri = "file:///Users/ray.xu/lsp_test/rust/src/main.rs" + } + } }, + command = "rust-analyzer.debugSingle", + title = "Debug" + }, + range = { + end = { + character = 7, + line = 45 + }, + start = { + character = 3, + line = 45 + } + } + }, { + data = { + impls = { + position = { + character = 6, + line = 2 + }, + textDocument = { + uri = "file:///Users/ray.xu/lsp_test/rust/src/main.rs" + } + } + }, + range = { + end = { + character = 10, + line = 2 + }, + start = { + character = 6, + line = 2 + } + } + }, { + data = { + impls = { + position = { + character = 7, + line = 28 + }, + textDocument = { + uri = "file:///Users/ray.xu/lsp_test/rust/src/main.rs" + } + } + }, + range = { + end = { + character = 10, + line = 28 + }, + start = { + character = 7, + line = 28 + } + } + }, { + data = { + impls = { + position = { + character = 7, + line = 31 + }, + textDocument = { + uri = "file:///Users/ray.xu/lsp_test/rust/src/main.rs" + } + } + }, + range = { + end = { + character = 10, + line = 31 + }, + start = { + character = 7, + line = 31 + } + } + } } + } } diff --git a/lua/navigator/render.lua b/lua/navigator/render.lua index ae8276e..1f096b3 100644 --- a/lua/navigator/render.lua +++ b/lua/navigator/render.lua @@ -3,6 +3,9 @@ local trace = require"guihua.log".trace local M = {} local clone = require'guihua.util'.clone local function filename(url) + if url == nil then + return '' + end return url:match("^.+/(.+)$") or url end diff --git a/lua/navigator/util.lua b/lua/navigator/util.lua index 04472df..6584c37 100644 --- a/lua/navigator/util.lua +++ b/lua/navigator/util.lua @@ -393,4 +393,18 @@ function M.partial(func, arg) end)) end +-- alternatively: use vim.notify("namespace does not exist or is anonymous", vim.log.levels.ERROR) + +function M.warn(msg) + vim.api.nvim_echo({{"WRN: " .. msg, "WarningMsg"}}, true, {}) +end + +function M.error(msg) + vim.api.nvim_echo({{"ERR: " .. msg, "ErrorMsg"}}, true, {}) +end + +function M.info(msg) + vim.api.nvim_echo({{"Info: " .. msg}}, true, {}) +end + return M