Skip to content

fix(#2519): Diagnostics Not Updated When Tree Not Visible #2597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 30, 2023
97 changes: 52 additions & 45 deletions lua/nvim-tree/diagnostics.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local core = require "nvim-tree.core"
local log = require "nvim-tree.log"

local M = {}
Expand All @@ -12,6 +11,16 @@ local severity_levels = {
Hint = 4,
}

--- A dictionary tree containing buffer-severity mappings.
---@type table
local buffer_severity_dict = {}

---@param path string
---@return string
local function uniformize_path(path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice name!

return utils.canonical_path(path:gsub("\\", "/"))
end

---@return table
local function from_nvim_lsp()
local buffer_severity = {}
Expand All @@ -25,11 +34,10 @@ local function from_nvim_lsp()
for _, diagnostic in ipairs(vim.diagnostic.get(nil, { severity = M.severity })) do
local buf = diagnostic.bufnr
if vim.api.nvim_buf_is_valid(buf) then
local bufname = vim.api.nvim_buf_get_name(buf)
local lowest_severity = buffer_severity[bufname]
if not lowest_severity or diagnostic.severity < lowest_severity then
buffer_severity[bufname] = diagnostic.severity
end
local bufname = uniformize_path(vim.api.nvim_buf_get_name(buf))
local severity = diagnostic.severity
local highest_severity = buffer_severity[bufname] or severity
buffer_severity[bufname] = math.min(highest_severity, severity)
end
end
end
Expand All @@ -55,19 +63,13 @@ local function from_coc()
return {}
end

local diagnostics = {}
local buffer_severity = {}
for _, diagnostic in ipairs(diagnostic_list) do
local bufname = diagnostic.file
local bufname = uniformize_path(diagnostic.file)
local coc_severity = severity_levels[diagnostic.severity]

local serverity = diagnostics[bufname] or vim.diagnostic.severity.HINT
diagnostics[bufname] = math.min(coc_severity, serverity)
end

local buffer_severity = {}
for bufname, severity in pairs(diagnostics) do
if is_severity_in_range(severity, M.severity) then
buffer_severity[bufname] = severity
local highest_severity = buffer_severity[bufname] or coc_severity
if is_severity_in_range(highest_severity, M.severity) then
buffer_severity[bufname] = math.min(highest_severity, coc_severity)
end
end

Expand All @@ -79,47 +81,52 @@ local function is_using_coc()
end

function M.update()
if not M.enable or not core.get_explorer() or not view.is_buf_valid(view.get_bufnr()) then
if not M.enable then
return
end
utils.debounce("diagnostics", M.debounce_delay, function()
local profile = log.profile_start "diagnostics update"
log.line("diagnostics", "update")

local buffer_severity
if is_using_coc() then
buffer_severity = from_coc()
buffer_severity_dict = from_coc()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/ nit no dict please - we're not writing python here ;)

else
buffer_severity = from_nvim_lsp()
buffer_severity_dict = from_nvim_lsp()
end

local nodes_by_line = utils.get_nodes_by_line(core.get_explorer().nodes, core.get_nodes_starting_line())
for _, node in pairs(nodes_by_line) do
node.diag_status = nil
log.node("diagnostics", buffer_severity_dict, "update")
log.profile_end(profile)
if view.is_buf_valid(view.get_bufnr()) then
require("nvim-tree.renderer").draw()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/ nit please require once at the start of the file

end
end)
end

---@param node Node
function M.update_node_severity_level(node)
if not M.enable then
return
end

for bufname, severity in pairs(buffer_severity) do
local bufpath = utils.canonical_path(bufname)
log.line("diagnostics", " bufpath '%s' severity %d", bufpath, severity)
if 0 < severity and severity < 5 then
for line, node in pairs(nodes_by_line) do
local nodepath = utils.canonical_path(node.absolute_path)
log.line("diagnostics", " %d checking nodepath '%s'", line, nodepath)

local node_contains_buf = vim.startswith(bufpath:gsub("\\", "/"), nodepath:gsub("\\", "/") .. "/")
if M.show_on_dirs and node_contains_buf and (not node.open or M.show_on_open_dirs) then
log.line("diagnostics", " matched fold node '%s'", node.absolute_path)
node.diag_status = severity
elseif nodepath == bufpath then
log.line("diagnostics", " matched file node '%s'", node.absolute_path)
node.diag_status = severity
local is_folder = node.nodes ~= nil
local nodepath = uniformize_path(node.absolute_path)

if is_folder then
local max_severity = nil
if M.show_on_dirs and (not node.open or M.show_on_open_dirs) then
for bufname, severity in pairs(buffer_severity_dict) do
local node_contains_buf = vim.startswith(bufname, nodepath .. "/")
if node_contains_buf then
if severity == M.severity.max then
max_severity = severity
break
else
max_severity = math.min(max_severity or severity, severity)
end
end
end
end
log.profile_end(profile)
require("nvim-tree.renderer").draw()
end)
node.diag_status = max_severity
else
node.diag_status = buffer_severity_dict[nodepath]
end
end

function M.setup(opts)
Expand Down
1 change: 1 addition & 0 deletions lua/nvim-tree/node.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
---@field parent DirNode
---@field type string
---@field watcher function|nil
---@field diag_status integer|nil

---@class DirNode: BaseNode
---@field has_children boolean
Expand Down
2 changes: 2 additions & 0 deletions lua/nvim-tree/renderer/builder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ end
function Builder:_build_line(node, idx, num_children, unloaded_bufnr)
local copy_paste = require "nvim-tree.actions.fs.copy-paste"

require("nvim-tree.diagnostics").update_node_severity_level(node)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is very concerning from a performance perspective; we go out of our way to avoid such calls on drawing each line.

Could we improve via one of:

  • do this once for all nodes prior to rendering
  • short-circuit this so that it's only called when there is no diagnostic info


-- various components
local indent_markers = pad.get_indent_markers(self.depth, idx, num_children, node, self.markers)
local arrows = pad.get_arrows(node)
Expand Down