From 7bc3616ed836abbdfd98dde9163b76248571ff80 Mon Sep 17 00:00:00 2001 From: iusmac Date: Fri, 22 Dec 2023 19:58:20 +0100 Subject: [PATCH 01/11] fix(#2519): diagnostics overhaul Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 76 ++++++++++++++++++------------ lua/nvim-tree/node.lua | 1 + lua/nvim-tree/renderer/builder.lua | 2 + 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 93c689634a9..b6d511bf771 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -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,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) + return utils.canonical_path(path:gsub("\\", "/")) +end + ---@return table local function from_nvim_lsp() local buffer_severity = {} @@ -25,7 +34,7 @@ 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 bufname = uniformize_path(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 @@ -67,7 +76,7 @@ local function from_coc() local buffer_severity = {} for bufname, severity in pairs(diagnostics) do if is_severity_in_range(severity, M.severity) then - buffer_severity[bufname] = severity + buffer_severity[uniformize_path(bufname)] = severity end end @@ -79,47 +88,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() 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() 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) diff --git a/lua/nvim-tree/node.lua b/lua/nvim-tree/node.lua index 55ee1942dc4..19bf4449eb3 100644 --- a/lua/nvim-tree/node.lua +++ b/lua/nvim-tree/node.lua @@ -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 diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index bfd72f7aac1..be60872688a 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -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) + -- various components local indent_markers = pad.get_indent_markers(self.depth, idx, num_children, node, self.markers) local arrows = pad.get_arrows(node) From 2baf3dada46e90a712ae295879c1f109c6aae5a6 Mon Sep 17 00:00:00 2001 From: iusmac Date: Sat, 23 Dec 2023 01:59:50 +0100 Subject: [PATCH 02/11] fix: Properly filter diagnostics from coc Also, while we're at it, refactor the lsp function for consistency. There should be no functional change, just cosmetic. Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index b6d511bf771..0cf15b54253 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -35,10 +35,9 @@ local function from_nvim_lsp() local buf = diagnostic.bufnr if vim.api.nvim_buf_is_valid(buf) then local bufname = uniformize_path(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 severity = diagnostic.severity + local highest_severity = buffer_severity[bufname] or severity + buffer_severity[bufname] = math.min(highest_severity, severity) end end end @@ -64,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[uniformize_path(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 From f30a8f6862ed9ebdccb549328fa6baaa2c6428b1 Mon Sep 17 00:00:00 2001 From: iusmac Date: Sun, 24 Dec 2023 15:17:40 +0100 Subject: [PATCH 03/11] Assign diagnostic version per node to reduce overhead Signed-off-by: iusmac --- lua/nvim-tree/actions/moves/item.lua | 4 +- lua/nvim-tree/diagnostics.lua | 79 +++++++++++++------ lua/nvim-tree/node.lua | 2 +- lua/nvim-tree/renderer/builder.lua | 2 - .../renderer/components/diagnostics.lua | 9 ++- 5 files changed, 67 insertions(+), 29 deletions(-) diff --git a/lua/nvim-tree/actions/moves/item.lua b/lua/nvim-tree/actions/moves/item.lua index 9375ef5f4dc..bf72c0ea2df 100644 --- a/lua/nvim-tree/actions/moves/item.lua +++ b/lua/nvim-tree/actions/moves/item.lua @@ -3,6 +3,7 @@ local view = require "nvim-tree.view" local core = require "nvim-tree.core" local lib = require "nvim-tree.lib" local explorer_node = require "nvim-tree.explorer.node" +local diagnostics = require "nvim-tree.diagnostics" local M = {} @@ -33,7 +34,8 @@ function M.fn(opts) local git_status = explorer_node.get_git_status(node) valid = git_status ~= nil and (not opts.skip_gitignored or git_status[1] ~= "!!") elseif opts.what == "diag" then - valid = node.diag_status ~= nil + local diag_status = diagnostics.get_diag_status(node) + valid = diag_status ~= nil and diag_status.value ~= nil elseif opts.what == "opened" then valid = vim.fn.bufloaded(node.absolute_path) ~= 0 end diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 0cf15b54253..8028fe76d0b 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -11,10 +11,18 @@ local severity_levels = { Hint = 4, } +---@class DiagStatus +---@field value integer|nil +---@field cache_version integer + --- A dictionary tree containing buffer-severity mappings. ---@type table local buffer_severity_dict = {} +--- 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) @@ -80,6 +88,31 @@ 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_dict[nodepath] + else + -- dirs should be searched in the list of cached buffer names by prefix + 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 + return { value = max_severity, cache_version = BUFFER_SEVERITY_VERSION } +end + function M.update() if not M.enable then return @@ -91,6 +124,7 @@ function M.update() else buffer_severity_dict = from_nvim_lsp() end + BUFFER_SEVERITY_VERSION = BUFFER_SEVERITY_VERSION + 1 log.node("diagnostics", buffer_severity_dict, "update") log.profile_end(profile) if view.is_buf_valid(view.get_bufnr()) then @@ -100,33 +134,34 @@ function M.update() end ---@param node Node -function M.update_node_severity_level(node) +---@return DiagStatus|nil +function M.get_diag_status(node) if not M.enable then - return + return nil end - local is_folder = node.nodes ~= nil - local nodepath = uniformize_path(node.absolute_path) + -- dir but we shouldn't show on dirs at all + if node.nodes ~= nil and not M.show_on_dirs then + return nil + end - 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 - node.diag_status = max_severity - else - node.diag_status = buffer_severity_dict[nodepath] + -- 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) diff --git a/lua/nvim-tree/node.lua b/lua/nvim-tree/node.lua index 19bf4449eb3..323fc53c4c4 100644 --- a/lua/nvim-tree/node.lua +++ b/lua/nvim-tree/node.lua @@ -17,7 +17,7 @@ ---@field parent DirNode ---@field type string ---@field watcher function|nil ----@field diag_status integer|nil +---@field diag_status DiagStatus|nil ---@class DirNode: BaseNode ---@field has_children boolean diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index be60872688a..bfd72f7aac1 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -413,8 +413,6 @@ 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) - -- various components local indent_markers = pad.get_indent_markers(self.depth, idx, num_children, node, self.markers) local arrows = pad.get_arrows(node) diff --git a/lua/nvim-tree/renderer/components/diagnostics.lua b/lua/nvim-tree/renderer/components/diagnostics.lua index 3a145406e70..1fa864deac9 100644 --- a/lua/nvim-tree/renderer/components/diagnostics.lua +++ b/lua/nvim-tree/renderer/components/diagnostics.lua @@ -1,4 +1,5 @@ local HL_POSITION = require("nvim-tree.enum").HL_POSITION +local diagnostics = require "nvim-tree.diagnostics" local M = { HS_FILE = {}, @@ -17,10 +18,11 @@ function M.get_highlight(node) end local group + local diag_status = diagnostics.get_diag_status(node) if node.nodes then - group = M.HS_FOLDER[node.diag_status] + group = M.HS_FOLDER[diag_status and diag_status.value] else - group = M.HS_FILE[node.diag_status] + group = M.HS_FILE[diag_status and diag_status.value] end if group then @@ -35,7 +37,8 @@ end ---@return HighlightedString|nil modified icon function M.get_icon(node) if node and M.config.diagnostics.enable and M.config.renderer.icons.show.diagnostics then - return M.ICON[node.diag_status] + local diag_status = diagnostics.get_diag_status(node) + return M.ICON[diag_status and diag_status.value] end end From 74130416300b1e33569c9bf908f3a8e2912ec8e6 Mon Sep 17 00:00:00 2001 From: iusmac Date: Sun, 24 Dec 2023 16:04:36 +0100 Subject: [PATCH 04/11] Require renderer once Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 8028fe76d0b..b56cb260470 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -1,6 +1,7 @@ local utils = require "nvim-tree.utils" local view = require "nvim-tree.view" local log = require "nvim-tree.log" +local renderer = require "nvim-tree.renderer" local M = {} @@ -128,7 +129,7 @@ function M.update() 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() + renderer.draw() end end) end From e3c90fd16ca553218715f02441bda4ce0b8a0460 Mon Sep 17 00:00:00 2001 From: iusmac Date: Mon, 25 Dec 2023 11:25:01 +0100 Subject: [PATCH 05/11] Revert "Require renderer once" Causes circular requires after the previous commit. This reverts commit 74130416300b1e33569c9bf908f3a8e2912ec8e6. --- lua/nvim-tree/diagnostics.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index b56cb260470..8028fe76d0b 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -1,7 +1,6 @@ local utils = require "nvim-tree.utils" local view = require "nvim-tree.view" local log = require "nvim-tree.log" -local renderer = require "nvim-tree.renderer" local M = {} @@ -129,7 +128,7 @@ function M.update() log.node("diagnostics", buffer_severity_dict, "update") log.profile_end(profile) if view.is_buf_valid(view.get_bufnr()) then - renderer.draw() + require("nvim-tree.renderer").draw() end end) end From b8079fbf7bbd499085f82f1c6156b0618697fd88 Mon Sep 17 00:00:00 2001 From: iusmac Date: Sun, 24 Dec 2023 15:55:46 +0100 Subject: [PATCH 06/11] Rename `buffer_severity_dict` to `BUFFER_SEVERITY` Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 8028fe76d0b..461cb8ade90 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -15,9 +15,9 @@ local severity_levels = { ---@field value integer|nil ---@field cache_version integer ---- A dictionary tree containing buffer-severity mappings. +--- The buffer-severity mappings derived during the last diagnostic list update. ---@type table -local buffer_severity_dict = {} +local BUFFER_SEVERITY = {} --- The cache version number of the buffer-severity mappings. ---@type integer @@ -95,10 +95,10 @@ local function from_cache(node) local max_severity = nil if not node.nodes then -- direct cache hit for files - max_severity = buffer_severity_dict[nodepath] + 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_dict) do + 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 @@ -120,12 +120,12 @@ function M.update() utils.debounce("diagnostics", M.debounce_delay, function() local profile = log.profile_start "diagnostics update" if is_using_coc() then - buffer_severity_dict = from_coc() + BUFFER_SEVERITY = from_coc() else - buffer_severity_dict = from_nvim_lsp() + BUFFER_SEVERITY = from_nvim_lsp() end BUFFER_SEVERITY_VERSION = BUFFER_SEVERITY_VERSION + 1 - log.node("diagnostics", buffer_severity_dict, "update") + log.node("diagnostics", BUFFER_SEVERITY, "update") log.profile_end(profile) if view.is_buf_valid(view.get_bufnr()) then require("nvim-tree.renderer").draw() From e6f343a6d44d27e24ecf203f165c75902c39f30a Mon Sep 17 00:00:00 2001 From: iusmac Date: Mon, 25 Dec 2023 18:10:23 +0100 Subject: [PATCH 07/11] Log diagnostics update properly Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 461cb8ade90..13cd1b00389 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -125,7 +125,11 @@ function M.update() BUFFER_SEVERITY = from_nvim_lsp() end BUFFER_SEVERITY_VERSION = BUFFER_SEVERITY_VERSION + 1 - log.node("diagnostics", BUFFER_SEVERITY, "update") + 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) if view.is_buf_valid(view.get_bufnr()) then require("nvim-tree.renderer").draw() From a15c8a90d895f5e32e80146ac842a03b486a40f4 Mon Sep 17 00:00:00 2001 From: iusmac Date: Mon, 25 Dec 2023 19:32:07 +0100 Subject: [PATCH 08/11] Implement error handling for coc.nvim Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 13cd1b00389..983289388eb 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -60,14 +60,31 @@ 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 From b05f7e3d1125b234525449205d2e7c9047558cea Mon Sep 17 00:00:00 2001 From: iusmac Date: Tue, 26 Dec 2023 09:18:54 +0100 Subject: [PATCH 09/11] CI style fixes Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 983289388eb..6051ad0128e 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -66,7 +66,7 @@ local function handle_coc_exception(err) local notify = true -- avoid distractions on interrupts (CTRL-C) - if err:find("Vim:Interrupt") then + if err:find "Vim:Interrupt" then notify = false end @@ -82,7 +82,7 @@ local function from_coc() end local ok, diagnostic_list = xpcall(function() - return vim.fn.CocAction("diagnosticList") + 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 {} @@ -142,7 +142,7 @@ function M.update() BUFFER_SEVERITY = from_nvim_lsp() end BUFFER_SEVERITY_VERSION = BUFFER_SEVERITY_VERSION + 1 - if log.enabled("diagnostics") then + 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 From d7bd3598fc04ad9a2c2d280e5ef2901506f8f691 Mon Sep 17 00:00:00 2001 From: iusmac Date: Thu, 28 Dec 2023 17:31:44 +0100 Subject: [PATCH 10/11] Capture `Keyboard interrupt` when handling coc exceptions Signed-off-by: iusmac --- lua/nvim-tree/diagnostics.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 6051ad0128e..a4286b714de 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -66,7 +66,7 @@ local function handle_coc_exception(err) local notify = true -- avoid distractions on interrupts (CTRL-C) - if err:find "Vim:Interrupt" then + if err:find "Vim:Interrupt" or err:find "Keyboard interrupt" then notify = false end From c37c895a42e4a3995829480729a6a544a5b2e3fb Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 30 Dec 2023 14:04:45 +1100 Subject: [PATCH 11/11] add more doc --- lua/nvim-tree/diagnostics.lua | 65 ++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index a4286b714de..6ca13c9c067 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -4,24 +4,32 @@ local log = require "nvim-tree.log" local M = {} -local severity_levels = { +---TODO add "$VIMRUNTIME" to "workspace.library" and use the @enum instead of this integer +---@alias lsp.DiagnosticSeverity integer + +---COC severity level strings to LSP severity levels +---@enum COC_SEVERITY_LEVELS +local COC_SEVERITY_LEVELS = { Error = 1, Warning = 2, Information = 3, Hint = 4, } +---Absolute Node path to LSP severity level +---@alias NodeSeverities table + ---@class DiagStatus ----@field value integer|nil +---@field value lsp.DiagnosticSeverity|nil ---@field cache_version integer --- The buffer-severity mappings derived during the last diagnostic list update. ----@type table -local BUFFER_SEVERITY = {} +---@type NodeSeverities +local NODE_SEVERITIES = {} ---- The cache version number of the buffer-severity mappings. +---The cache version number of the buffer-severity mappings. ---@type integer -local BUFFER_SEVERITY_VERSION = 0 +local NODE_SEVERITIES_VERSION = 0 ---@param path string ---@return string @@ -29,7 +37,8 @@ local function uniformize_path(path) return utils.canonical_path(path:gsub("\\", "/")) end ----@return table +---Marshal severities from LSP. Does nothing when LSP disabled. +---@return NodeSeverities local function from_nvim_lsp() local buffer_severity = {} @@ -53,13 +62,15 @@ local function from_nvim_lsp() return buffer_severity end ----@param severity integer +---Severity is within diagnostics.severity.min, diagnostics.severity.max +---@param severity lsp.DiagnosticSeverity ---@param config table ---@return boolean local function is_severity_in_range(severity, config) return config.max <= severity and severity <= config.min end +---Handle any COC exceptions, preventing any propagation ---@param err string local function handle_coc_exception(err) log.line("diagnostics", "handle_coc_exception: %s", vim.inspect(err)) @@ -75,9 +86,16 @@ local function handle_coc_exception(err) end end ----@return table +---COC service initialized +---@return boolean +local function is_using_coc() + return vim.g.coc_service_initialized == 1 +end + +---Marshal severities from COC. Does nothing when COC service not started. +---@return NodeSeverities local function from_coc() - if vim.g.coc_service_initialized ~= 1 then + if not is_using_coc() then return {} end @@ -91,7 +109,7 @@ local function from_coc() local buffer_severity = {} for _, diagnostic in ipairs(diagnostic_list) do local bufname = uniformize_path(diagnostic.file) - local coc_severity = severity_levels[diagnostic.severity] + local coc_severity = COC_SEVERITY_LEVELS[diagnostic.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) @@ -101,10 +119,7 @@ local function from_coc() return buffer_severity end -local function is_using_coc() - return vim.g.coc_service_initialized == 1 -end - +---Maybe retrieve severity level from the cache ---@param node Node ---@return DiagStatus local function from_cache(node) @@ -112,10 +127,10 @@ local function from_cache(node) local max_severity = nil if not node.nodes then -- direct cache hit for files - max_severity = BUFFER_SEVERITY[nodepath] + max_severity = NODE_SEVERITIES[nodepath] else -- dirs should be searched in the list of cached buffer names by prefix - for bufname, severity in pairs(BUFFER_SEVERITY) do + 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 @@ -127,9 +142,11 @@ local function from_cache(node) end end end - return { value = max_severity, cache_version = BUFFER_SEVERITY_VERSION } + return { value = max_severity, cache_version = NODE_SEVERITIES_VERSION } end +---Fired on DiagnosticChanged and CocDiagnosticChanged events: +---debounced retrieval, cache update, version increment and draw function M.update() if not M.enable then return @@ -137,13 +154,13 @@ function M.update() utils.debounce("diagnostics", M.debounce_delay, function() local profile = log.profile_start "diagnostics update" if is_using_coc() then - BUFFER_SEVERITY = from_coc() + NODE_SEVERITIES = from_coc() else - BUFFER_SEVERITY = from_nvim_lsp() + NODE_SEVERITIES = from_nvim_lsp() end - BUFFER_SEVERITY_VERSION = BUFFER_SEVERITY_VERSION + 1 + NODE_SEVERITIES_VERSION = NODE_SEVERITIES_VERSION + 1 if log.enabled "diagnostics" then - for bufname, severity in pairs(BUFFER_SEVERITY) do + for bufname, severity in pairs(NODE_SEVERITIES) do log.line("diagnostics", "Indexing bufname '%s' with severity %d", bufname, severity) end end @@ -154,6 +171,8 @@ function M.update() end) end +---Maybe retrieve diagnostic status for a node. +---Returns cached value when node's version matches. ---@param node Node ---@return DiagStatus|nil function M.get_diag_status(node) @@ -169,7 +188,7 @@ function M.get_diag_status(node) -- 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 + if not node.diag_status or node.diag_status.cache_version < NODE_SEVERITIES_VERSION then node.diag_status = from_cache(node) end