-
-
Notifications
You must be signed in to change notification settings - Fork 628
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
Changes from 8 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
7bc3616
fix(#2519): diagnostics overhaul
iusmac 2baf3da
fix: Properly filter diagnostics from coc
iusmac f30a8f6
Assign diagnostic version per node to reduce overhead
iusmac 7413041
Require renderer once
iusmac e3c90fd
Revert "Require renderer once"
iusmac b8079fb
Rename `buffer_severity_dict` to `BUFFER_SEVERITY`
iusmac e6f343a
Log diagnostics update properly
iusmac a15c8a9
Implement error handling for coc.nvim
iusmac b05f7e3
CI style fixes
iusmac d7bd359
Capture `Keyboard interrupt` when handling coc exceptions
iusmac c37c895
add more doc
alex-courtis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = {} | ||
|
@@ -12,6 +11,24 @@ local severity_levels = { | |
Hint = 4, | ||
} | ||
|
||
---@class DiagStatus | ||
---@field value integer|nil | ||
---@field cache_version integer | ||
|
||
--- The buffer-severity mappings derived during the last diagnostic list update. | ||
---@type table | ||
local BUFFER_SEVERITY = {} | ||
|
||
--- The cache version number of the buffer-severity mappings. | ||
---@type integer | ||
local BUFFER_SEVERITY_VERSION = 0 | ||
|
||
---@param path string | ||
---@return string | ||
local function uniformize_path(path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 = {} | ||
|
@@ -25,11 +42,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 | ||
|
@@ -44,30 +60,41 @@ local function is_severity_in_range(severity, config) | |
return config.max <= severity and severity <= config.min | ||
end | ||
|
||
---@param err string | ||
local function handle_coc_exception(err) | ||
log.line("diagnostics", "handle_coc_exception: %s", vim.inspect(err)) | ||
local notify = true | ||
|
||
-- avoid distractions on interrupts (CTRL-C) | ||
if err:find("Vim:Interrupt") then | ||
notify = false | ||
end | ||
|
||
if notify then | ||
require("nvim-tree.notify").error("Diagnostics update from coc.nvim failed. " .. vim.inspect(err)) | ||
end | ||
end | ||
|
||
---@return table | ||
local function from_coc() | ||
if vim.g.coc_service_initialized ~= 1 then | ||
return {} | ||
end | ||
|
||
local diagnostic_list = vim.fn.CocAction "diagnosticList" | ||
if type(diagnostic_list) ~= "table" or vim.tbl_isempty(diagnostic_list) then | ||
local ok, diagnostic_list = xpcall(function() | ||
return vim.fn.CocAction("diagnosticList") | ||
end, handle_coc_exception) | ||
if not ok or type(diagnostic_list) ~= "table" or vim.tbl_isempty(diagnostic_list) then | ||
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 | ||
|
||
|
@@ -78,50 +105,86 @@ local function is_using_coc() | |
return vim.g.coc_service_initialized == 1 | ||
end | ||
|
||
---@param node Node | ||
---@return DiagStatus | ||
local function from_cache(node) | ||
local nodepath = uniformize_path(node.absolute_path) | ||
local max_severity = nil | ||
if not node.nodes then | ||
-- direct cache hit for files | ||
max_severity = BUFFER_SEVERITY[nodepath] | ||
else | ||
-- dirs should be searched in the list of cached buffer names by prefix | ||
for bufname, severity in pairs(BUFFER_SEVERITY) 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 | ||
return { value = max_severity, cache_version = BUFFER_SEVERITY_VERSION } | ||
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 = from_coc() | ||
else | ||
buffer_severity = 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 | ||
BUFFER_SEVERITY = from_nvim_lsp() | ||
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 | ||
end | ||
end | ||
BUFFER_SEVERITY_VERSION = BUFFER_SEVERITY_VERSION + 1 | ||
if log.enabled("diagnostics") then | ||
for bufname, severity in pairs(BUFFER_SEVERITY) do | ||
log.line("diagnostics", "Indexing bufname '%s' with severity %d", bufname, severity) | ||
end | ||
end | ||
log.profile_end(profile) | ||
require("nvim-tree.renderer").draw() | ||
if view.is_buf_valid(view.get_bufnr()) then | ||
require("nvim-tree.renderer").draw() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
---@return DiagStatus|nil | ||
function M.get_diag_status(node) | ||
if not M.enable then | ||
return nil | ||
end | ||
|
||
-- dir but we shouldn't show on dirs at all | ||
if node.nodes ~= nil and not M.show_on_dirs then | ||
return nil | ||
end | ||
|
||
-- here, we do a lazy update of the diagnostic status carried by the node. | ||
-- This is by design, as diagnostics and nodes live in completely separate | ||
-- worlds, and this module is the link between the two | ||
if not node.diag_status or node.diag_status.cache_version < BUFFER_SEVERITY_VERSION then | ||
node.diag_status = from_cache(node) | ||
end | ||
|
||
-- file | ||
if not node.nodes then | ||
return node.diag_status | ||
end | ||
|
||
-- dir is closed or we should show on open_dirs | ||
if not node.open or M.show_on_open_dirs then | ||
return node.diag_status | ||
end | ||
return nil | ||
end | ||
|
||
function M.setup(opts) | ||
M.enable = opts.diagnostics.enable | ||
M.debounce_delay = opts.diagnostics.debounce_delay | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good; we have a cache similar to git's.
It'll take me a while to properly review and test this one.