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
neovim_0.6
rayx 3 years ago committed by GitHub
parent 90039247b4
commit 79fee5dda8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -13,7 +13,8 @@ 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 <span style="color:red"> *browser* </span> in closure.js
- The first line of floating windows shows there are 3 references for the symbol <span style="color:red"> _browser_ </span> in closure.js
- The first reference of browser is an assignment, an emoji 📝 indicates the value is changed in this line. In many
cases, we search for references to find out when the value changed.
- The second reference of `browser` is inside function `displayName` and `displayName` sit inside `makeFunc`, So you
@ -27,9 +28,10 @@ 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.
``<- 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
@ -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,16 +284,15 @@ 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)
@ -301,6 +301,7 @@ servers. (Prevent loading multiple LSP for same source code.) e.g. I saw stran
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
@ -321,7 +322,7 @@ require'navigator'.setup({
### Default keymaps
| mode | key | function |
|--- |--- |--- |
| ---- | --------------- | ---------------------------------------------------------- |
| n | gr | show reference and context |
| i | \<m-k\> | signature help |
| n | \<c-k\> | signature help |
@ -369,8 +370,6 @@ require'navigator'.setup({
| i/n | \<C-f\> | next page in listview |
| i/n | \<C-s\> | save the modification to preview window to file |
### Colors/Highlight:
You can override default highlight GHListDark (listview) and GHTextViewDark (code view)
@ -385,8 +384,6 @@ 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,
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,23 +429,22 @@ require'navigator'.setup({on_attach = function(client, bufnr) require 'illuminat
Highlight I am using:
* LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()`
- LspReferenceRead, LspReferenceText and LspReferenceWrite are used for `autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()`
That is where you saw the current symbol been highlighted.
* GHListDark and GHTextViewDark is used for floating listvew and TextView. They are be based on current background
- 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 |
## Screenshots
colorscheme: [aurora](https://github.com/ray-x/aurora)
@ -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

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

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

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

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

@ -1,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,27 +13,61 @@ 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)
local function clear_diag_VT(bufnr) -- important for clearing out when no more errors
log(bufnr, _NG_VT_DIAG_NS)
if bufnr == nil or _NG_VT_DIAG_NS == nil then
return
end
vim.api.nvim_buf_clear_namespace(bufnr, _NG_VT_DIAG_NS, 0, -1)
_NG_VT_DIAG_NS = nil
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
end
local function error_marker(result, ctx, config)
vim.defer_fn(function()
if vim.tbl_isempty(result.diagnostics) then
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
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())
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
trace(result, bufnr)
if not vim.api.nvim_buf_is_loaded(bufnr) then
log("buf not loaded")
return
end
trace('schedule callback', result, ctx, config)
-- trace(result, bufnr)
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 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
@ -42,40 +76,48 @@ local function error_marker(result, client_id)
-- 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.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 wwidth = stats.width;
local wheight = stats.height;
-- 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")
if _NG_VT_DIAG_NS == nil then
_NG_VT_DIAG_NS = vim.api.nvim_create_namespace("navigator_lua_diag")
end
vim.api.nvim_buf_clear_namespace(0, _NG_VT_NS, 0, -1)
if total_num <= wheight then
first_line = 0
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
local p = diag.range.start.line
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
}
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,
@ -84,30 +126,47 @@ local function error_marker(result, client_id)
})
end
end
trace("pos", pos, diag.range.start)
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 > 1 then
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
vim.api.nvim_buf_set_extmark(bufnr, _NG_VT_NS, l, -1,
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
vim.cmd("lclose")
cfg.namespaces = diagnostic.get_namespace(nil)
diagnostic.setloclist(cfg)
end
else
vim.cmd("lclose")
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

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

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

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

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

@ -1,20 +1,62 @@
local M = {}
local log = require"navigator.util".log
local api = vim.api
-- lsp sign          ﮻         ﯭ        ﳀ  
function M.diagnositc_config_sign()
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 = '', texthl = 'LspDiagnosticsSignError', linehl = '', numhl = ''})
{text = e, texthl = 'LspDiagnosticsSignError', linehl = '', numhl = ''})
vim.fn.sign_define('LspDiagnosticsSignWarning',
{text = '', texthl = 'LspDiagnosticsSignWarning', linehl = '', numhl = ''})
{text = w, texthl = 'LspDiagnosticsSignWarning', linehl = '', numhl = ''})
vim.fn.sign_define('LspDiagnosticsSignInformation', {
text = '',
text = i,
texthl = 'LspDiagnosticsSignInformation',
linehl = '',
numhl = ''
})
vim.fn.sign_define('LspDiagnosticsSignHint',
{text = '💡', texthl = 'LspDiagnosticsSignHint', linehl = '', numhl = ''})
{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'},

@ -31,7 +31,7 @@ local key_maps = {
{key = "<Leader>go", func = "outgoing_calls()"},
{key = "gi", func = "implementation()"},
{key = "<Space>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 = "<Cmd>lua " .. value.func .. "<CR>"
elseif string.find(value.func, "diagnostic") then
f = "<Cmd>lua vim.lsp." .. value.func .. "<CR>"
local diagnostic = '<Cmd>lua vim.'
if vim.lsp.diagnostic ~= nil then
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
@ -241,9 +245,11 @@ function M.setup(user_opts)
require"navigator.diagnostics".diagnostic_handler
-- TODO: when active signature merge to neovim, remove this setup:
if _NgConfigValues.signature_help_cfg then
log("setup signature from navigator")
local hassig, sig = pcall(require, "lsp_signature")
if hassig then
if _NgConfigValues.signature_help_cfg then
sig.setup(_NgConfigValues.signature_help_cfg)
end
else

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

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

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

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

Loading…
Cancel
Save