Skip to content

Commit

Permalink
fix(#2954): more efficient LSP updates, increase diagnostics.debounce…
Browse files Browse the repository at this point in the history
…_delay from 50ms to 500ms (#3007)

* fix(#2954): use LSP diagnostic data deltas from events instead of a full query

* fix(#2954): use LSP diagnostic data deltas from events instead of a full query
  • Loading branch information
alex-courtis authored Nov 21, 2024
1 parent f7c65e1 commit 1f3ffd6
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 48 deletions.
4 changes: 2 additions & 2 deletions doc/nvim-tree-lua.txt
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ Following is the default configuration. See |nvim-tree-opts| for details. >lua
enable = false,
show_on_dirs = false,
show_on_open_dirs = true,
debounce_delay = 50,
debounce_delay = 500,
severity = {
min = vim.diagnostic.severity.HINT,
max = vim.diagnostic.severity.ERROR,
Expand Down Expand Up @@ -1272,7 +1272,7 @@ Enable/disable the feature.

*nvim-tree.diagnostics.debounce_delay*
Idle milliseconds between diagnostic event and update.
Type: `number`, Default: `50` (ms)
Type: `number`, Default: `500` (ms)

*nvim-tree.diagnostics.show_on_dirs*
Show diagnostic icons on parent directories.
Expand Down
8 changes: 4 additions & 4 deletions lua/nvim-tree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,16 @@ local function setup_autocommands(opts)

if opts.diagnostics.enable then
create_nvim_tree_autocmd("DiagnosticChanged", {
callback = function()
callback = function(ev)
log.line("diagnostics", "DiagnosticChanged")
require("nvim-tree.diagnostics").update()
require("nvim-tree.diagnostics").update_lsp(ev)
end,
})
create_nvim_tree_autocmd("User", {
pattern = "CocDiagnosticChange",
callback = function()
log.line("diagnostics", "CocDiagnosticChange")
require("nvim-tree.diagnostics").update()
require("nvim-tree.diagnostics").update_coc()
end,
})
end
Expand Down Expand Up @@ -386,7 +386,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
enable = false,
show_on_dirs = false,
show_on_open_dirs = true,
debounce_delay = 50,
debounce_delay = 500,
severity = {
min = vim.diagnostic.severity.HINT,
max = vim.diagnostic.severity.ERROR,
Expand Down
99 changes: 57 additions & 42 deletions lua/nvim-tree/diagnostics.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ local COC_SEVERITY_LEVELS = {
}

---Absolute Node path to LSP severity level
---@alias NodeSeverities table<string, lsp.DiagnosticSeverity>
---@alias NodeSeverities table<string, vim.diagnostic.Severity>

---@class DiagStatus
---@field value lsp.DiagnosticSeverity|nil
Expand All @@ -37,33 +37,6 @@ local function uniformize_path(path)
return utils.canonical_path(path:gsub("\\", "/"))
end

---Marshal severities from LSP. Does nothing when LSP disabled.
---@return NodeSeverities
local function from_nvim_lsp()
local buffer_severity = {}

-- is_enabled is not present in all 0.10 builds/releases, see #2781
local is_enabled = false
if vim.fn.has("nvim-0.10") == 1 and type(vim.diagnostic.is_enabled) == "function" then
is_enabled = vim.diagnostic.is_enabled()
elseif type(vim.diagnostic.is_disabled) == "function" then ---@diagnostic disable-line: deprecated
is_enabled = not vim.diagnostic.is_disabled() ---@diagnostic disable-line: deprecated
end

if is_enabled then
for _, diagnostic in ipairs(vim.diagnostic.get(nil, { severity = M.severity })) do
if diagnostic.severity and diagnostic.bufnr and vim.api.nvim_buf_is_valid(diagnostic.bufnr) then
local bufname = uniformize_path(vim.api.nvim_buf_get_name(diagnostic.bufnr))
if not buffer_severity[bufname] or diagnostic.severity < buffer_severity[bufname] then
buffer_severity[bufname] = diagnostic.severity
end
end
end
end

return buffer_severity
end

---Severity is within diagnostics.severity.min, diagnostics.severity.max
---@param severity lsp.DiagnosticSeverity
---@param config table
Expand Down Expand Up @@ -135,35 +108,77 @@ local function from_cache(node)
for bufname, severity in pairs(NODE_SEVERITIES) do
local node_contains_buf = vim.startswith(bufname, nodepath .. "/")
if node_contains_buf then
if severity == M.severity.max then
if not max_severity or severity < max_severity then
max_severity = severity
break
else
max_severity = math.min(max_severity or severity, severity)
end
end
end
end
return { value = max_severity, cache_version = NODE_SEVERITIES_VERSION }
end

---Fired on DiagnosticChanged and CocDiagnosticChanged events:
---Fired on DiagnosticChanged for a single buffer.
---This will be called on set and reset of diagnostics.
---On disabling LSP, a reset event will be sent for all buffers.
---@param ev table standard event with data.diagnostics populated
function M.update_lsp(ev)
if not M.enable or not ev or not ev.data or not ev.data.diagnostics then
return
end

local profile_event = log.profile_start("DiagnosticChanged event")

---@type vim.Diagnostic[]
local diagnostics = ev.data.diagnostics

-- use the buffer from the event, as ev.data.diagnostics will be empty on resolved diagnostics
local bufname = uniformize_path(vim.api.nvim_buf_get_name(ev.buf))

---@type vim.diagnostic.Severity?
local new_severity = nil

-- most severe (lowest) severity in user range
for _, diagnostic in ipairs(diagnostics) do
if diagnostic.severity >= M.severity.max and diagnostic.severity <= M.severity.min then
if not new_severity or diagnostic.severity < new_severity then
new_severity = diagnostic.severity
end
end
end

-- record delta and schedule a redraw
if new_severity ~= NODE_SEVERITIES[bufname] then
NODE_SEVERITIES[bufname] = new_severity
NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1

utils.debounce("DiagnosticChanged redraw", M.debounce_delay, function()
local profile_redraw = log.profile_start("DiagnosticChanged redraw")

local explorer = core.get_explorer()
if explorer then
explorer.renderer:draw()
end

log.profile_end(profile_redraw)
end)
end

log.profile_end(profile_event)
end

---Fired on CocDiagnosticChanged events:
---debounced retrieval, cache update, version increment and draw
function M.update()
function M.update_coc()
if not M.enable then
return
end
utils.debounce("diagnostics", M.debounce_delay, function()
local profile = log.profile_start("diagnostics update")
if is_using_coc() then
NODE_SEVERITIES = from_coc()
else
NODE_SEVERITIES = from_nvim_lsp()
end
utils.debounce("CocDiagnosticChanged update", M.debounce_delay, function()
local profile = log.profile_start("CocDiagnosticChanged update")
NODE_SEVERITIES = from_coc()
NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1
if log.enabled("diagnostics") then
for bufname, severity in pairs(NODE_SEVERITIES) do
log.line("diagnostics", "Indexing bufname '%s' with severity %d", bufname, severity)
log.line("diagnostics", "COC Indexing bufname '%s' with severity %d", bufname, severity)
end
end
log.profile_end(profile)
Expand Down

0 comments on commit 1f3ffd6

Please sign in to comment.