From 35e74c3b4730129806b6edd85ea90991a2ddbca6 Mon Sep 17 00:00:00 2001 From: Bastian Ahrens Date: Thu, 20 Apr 2023 12:05:40 +0200 Subject: [PATCH 1/6] feat: Allow for placement of diagnostic icons Similar to the other icons there's now an option `diagnostic_placement` that can either be "signcolumn" (default), "before" or "after". --- lua/nvim-tree.lua | 4 +- lua/nvim-tree/diagnostics.lua | 78 +++++++++++------------------- lua/nvim-tree/renderer/builder.lua | 57 +++++++++++++++++----- lua/nvim-tree/renderer/init.lua | 2 +- 4 files changed, 78 insertions(+), 63 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index d97f3c71d1d..ecec543da99 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -304,14 +304,14 @@ local function setup_autocommands(opts) create_nvim_tree_autocmd("DiagnosticChanged", { callback = function() log.line("diagnostics", "DiagnosticChanged") - require("nvim-tree.diagnostics").update() + reloaders.reload_explorer() end, }) create_nvim_tree_autocmd("User", { pattern = "CocDiagnosticChange", callback = function() log.line("diagnostics", "CocDiagnosticChange") - require("nvim-tree.diagnostics").update() + reloaders.reload_explorer() end, }) end diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 95f76031472..ef47d468fc8 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -15,7 +15,15 @@ local sign_names = { { "NvimTreeSignHint", "NvimTreeLspDiagnosticsHint" }, } -local function add_sign(linenr, severity) +function M.get_sign(severity) + if not severity then + return nil + end + + return M.diagnostic_icons[severity] +end + +function M.add_sign(linenr, severity) local buf = view.get_bufnr() if not vim.api.nvim_buf_is_valid(buf) or not vim.api.nvim_buf_is_loaded(buf) then return @@ -86,53 +94,16 @@ function M.clear() vim.fn.sign_unplace(GROUP) end -function M.update() +function M.get_diagnostics() if not M.enable or not core.get_explorer() or not view.is_buf_valid(view.get_bufnr()) then - return + 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() - else - buffer_severity = from_nvim_lsp() - end - M.clear() - - 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 - 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) - if - M.show_on_dirs - and vim.startswith(bufpath:gsub("\\", "/"), nodepath:gsub("\\", "/") .. "/") - 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 - add_sign(line, severity) - elseif nodepath == bufpath then - log.line("diagnostics", " matched file node '%s'", node.absolute_path) - node.diag_status = severity - add_sign(line, severity) - end - end - end - end - log.profile_end(profile) - end) + if is_using_coc() then + return from_coc() + else + return from_nvim_lsp() + end end local links = { @@ -146,6 +117,19 @@ function M.setup(opts) M.enable = opts.diagnostics.enable M.debounce_delay = opts.diagnostics.debounce_delay M.severity = opts.diagnostics.severity + M.diagnostic_icons = { + { str = opts.diagnostics.icons.error, hl = sign_names[1][2] }, + { str = opts.diagnostics.icons.warning, hl = sign_names[2][2] }, + { str = opts.diagnostics.icons.info, hl = sign_names[3][2] }, + { str = opts.diagnostics.icons.hint, hl = sign_names[4][2] }, + } + + if opts.renderer.icons.diagnostic_placement == "signcolumn" then + vim.fn.sign_define(sign_names[1][1], { text = opts.diagnostics.icons.error, texthl = sign_names[1][2] }) + vim.fn.sign_define(sign_names[2][1], { text = opts.diagnostics.icons.warning, texthl = sign_names[2][2] }) + vim.fn.sign_define(sign_names[3][1], { text = opts.diagnostics.icons.info, texthl = sign_names[3][2] }) + vim.fn.sign_define(sign_names[4][1], { text = opts.diagnostics.icons.hint, texthl = sign_names[4][2] }) + end if M.enable then log.line("diagnostics", "setup") @@ -153,10 +137,6 @@ function M.setup(opts) M.show_on_dirs = opts.diagnostics.show_on_dirs M.show_on_open_dirs = opts.diagnostics.show_on_open_dirs - vim.fn.sign_define(sign_names[1][1], { text = opts.diagnostics.icons.error, texthl = sign_names[1][2] }) - vim.fn.sign_define(sign_names[2][1], { text = opts.diagnostics.icons.warning, texthl = sign_names[2][2] }) - vim.fn.sign_define(sign_names[3][1], { text = opts.diagnostics.icons.info, texthl = sign_names[3][2] }) - vim.fn.sign_define(sign_names[4][1], { text = opts.diagnostics.icons.hint, texthl = sign_names[4][2] }) for lhs, rhs in pairs(links) do vim.cmd("hi def link " .. lhs .. " " .. rhs) diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index 04e0ec48f62..f95298aba81 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -1,6 +1,7 @@ local utils = require "nvim-tree.utils" local core = require "nvim-tree.core" +local diagnostics = require "nvim-tree.diagnostics" local git = require "nvim-tree.renderer.components.git" local pad = require "nvim-tree.renderer.components.padding" local icons = require "nvim-tree.renderer.components.icons" @@ -72,6 +73,14 @@ function Builder:configure_git_icons_placement(where) return self end +function Builder:configure_diagnostic_icons_placement(where) + if where ~= "after" and where ~= "before" and where ~= "signcolumn" then + where = "signcolumn" -- default before + end + self.diagnostic_placement = where + return self +end + function Builder:configure_modified_placement(where) if where ~= "after" and where ~= "before" and where ~= "signcolumn" then where = "after" -- default after @@ -225,6 +234,20 @@ function Builder:_get_modified_icon(node) return modified_icon end +---@param node table +---@return HighlightedString|nil icon +function Builder:_get_diagnostic_icon(node, diagnostic_severity) + local diagnostic_icon = diagnostics.get_sign(diagnostic_severity) + + if self.diagnostic_placement == "signcolumn" and diagnostic_severity then + node.diag_status = diagnostic_severity + diagnostics.add_sign(self.index + 1, diagnostic_severity) + diagnostic_icon = nil + end + + return diagnostic_icon +end + ---@param node table ---@return string icon_highlight, string name_highlight function Builder:_get_highlight_override(node, unloaded_bufnr) @@ -272,9 +295,9 @@ end ---@param git_icons HighlightedString[]|nil ---@param modified_icon HighlightedString|nil ---@return HighlightedString[] -function Builder:_format_line(padding, icon, name, git_icons, modified_icon) +function Builder:_format_line(padding, icon, name, git_icons, modified_icon, diagnostic_icon) local added_len = 0 - local function add_to_end(t1, t2) + local function append_to_line(t1, t2) for _, v in ipairs(t2) do if added_len > 0 then table.insert(t1, { str = self.icon_padding }) @@ -291,29 +314,39 @@ function Builder:_format_line(padding, icon, name, git_icons, modified_icon) end local line = { padding } - add_to_end(line, { icon }) + append_to_line(line, { icon }) + if git_icons and self.git_placement == "before" then - add_to_end(line, git_icons) + append_to_line(line, git_icons) end if modified_icon and self.modified_placement == "before" then - add_to_end(line, { modified_icon }) + append_to_line(line, { modified_icon }) end - add_to_end(line, { name }) + if diagnostic_icon and self.diagnostic_placement == "before" then + append_to_line(line, { diagnostic_icon }) + end + + append_to_line(line, { name }) + if git_icons and self.git_placement == "after" then - add_to_end(line, git_icons) + append_to_line(line, git_icons) end if modified_icon and self.modified_placement == "after" then - add_to_end(line, { modified_icon }) + append_to_line(line, { modified_icon }) + end + if diagnostic_icon and self.diagnostic_placement == "after" then + append_to_line(line, { diagnostic_icon }) end return line end -function Builder:_build_line(node, idx, num_children, unloaded_bufnr) +function Builder:_build_line(node, idx, num_children, unloaded_bufnr, diagnostic_severity) -- various components local padding = pad.get_padding(self.depth, idx, num_children, node, self.markers) local git_icons = self:_get_git_icons(node) local modified_icon = self:_get_modified_icon(node) + local diagnostic_icon = self:_get_diagnostic_icon(node, diagnostic_severity) -- main components local is_folder = node.nodes ~= nil @@ -336,7 +369,7 @@ function Builder:_build_line(node, idx, num_children, unloaded_bufnr) name.hl = name_hl end - local line = self:_format_line(padding, icon, name, git_icons, modified_icon) + local line = self:_format_line(padding, icon, name, git_icons, modified_icon, diagnostic_icon) self:_insert_line(self:_unwrap_highlighted_strings(line)) self.index = self.index + 1 @@ -364,10 +397,12 @@ end function Builder:build(tree, unloaded_bufnr) local num_children = self:_get_nodes_number(tree.nodes) + local _diagnostics = diagnostics.get_diagnostics() local idx = 1 for _, node in ipairs(tree.nodes) do if not node.hidden then - self:_build_line(node, idx, num_children, unloaded_bufnr) + local nodepath = utils.canonical_path(node.absolute_path) + self:_build_line(node, idx, num_children, unloaded_bufnr, _diagnostics[nodepath]) idx = idx + 1 end end diff --git a/lua/nvim-tree/renderer/init.lua b/lua/nvim-tree/renderer/init.lua index 3a61adcf240..7d4d1f443b4 100644 --- a/lua/nvim-tree/renderer/init.lua +++ b/lua/nvim-tree/renderer/init.lua @@ -68,6 +68,7 @@ function M.draw(unloaded_bufnr) :configure_modified_highlighting(M.config.highlight_modified) :configure_icon_padding(M.config.icons.padding) :configure_git_icons_placement(M.config.icons.git_placement) + :configure_diagnostic_icons_placement(M.config.icons.diagnostic_placement) :configure_modified_placement(M.config.icons.modified_placement) :configure_symlink_destination(M.config.symlink_destination) :configure_filter(live_filter.filter, live_filter.prefix) @@ -83,7 +84,6 @@ function M.draw(unloaded_bufnr) vim.api.nvim_win_set_cursor(view.get_winnr(), cursor) end - diagnostics.update() marks.draw() view.grow_from_content() From 016640f78996d975409985db727bafecca0c52f4 Mon Sep 17 00:00:00 2001 From: Bastian Ahrens Date: Thu, 20 Apr 2023 12:34:27 +0200 Subject: [PATCH 2/6] fix: Add default diagnostic_placement option --- lua/nvim-tree.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index ecec543da99..9f8b5c53320 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -406,6 +406,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS }, icons = { webdev_colors = true, + diagnostic_placement = "signcolumn", git_placement = "before", modified_placement = "after", padding = " ", From 6f309c44e5765c0785aed7130f357524de1b798a Mon Sep 17 00:00:00 2001 From: Bastian Ahrens Date: Thu, 20 Apr 2023 12:34:43 +0200 Subject: [PATCH 3/6] fix: Linting --- lua/nvim-tree/diagnostics.lua | 1 - lua/nvim-tree/renderer/builder.lua | 2 +- lua/nvim-tree/renderer/init.lua | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index ef47d468fc8..e375d2686c5 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -1,4 +1,3 @@ -local utils = require "nvim-tree.utils" local view = require "nvim-tree.view" local core = require "nvim-tree.core" local log = require "nvim-tree.log" diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index f95298aba81..a8c8e4aa66e 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -75,7 +75,7 @@ end function Builder:configure_diagnostic_icons_placement(where) if where ~= "after" and where ~= "before" and where ~= "signcolumn" then - where = "signcolumn" -- default before + where = "signcolumn" end self.diagnostic_placement = where return self diff --git a/lua/nvim-tree/renderer/init.lua b/lua/nvim-tree/renderer/init.lua index 7d4d1f443b4..9363e6bfce0 100644 --- a/lua/nvim-tree/renderer/init.lua +++ b/lua/nvim-tree/renderer/init.lua @@ -1,5 +1,4 @@ local core = require "nvim-tree.core" -local diagnostics = require "nvim-tree.diagnostics" local log = require "nvim-tree.log" local view = require "nvim-tree.view" local modified = require "nvim-tree.renderer.components.modified" From 52ea93eab9d3ed8e564fe32a4f765715277b28d5 Mon Sep 17 00:00:00 2001 From: Bastian Ahrens Date: Mon, 24 Apr 2023 21:03:24 +0200 Subject: [PATCH 4/6] fix: Redraw the tree on diagnostic change Also make sure the diagnostics are cleared correctly --- lua/nvim-tree.lua | 4 ++-- lua/nvim-tree/renderer/builder.lua | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 9f8b5c53320..911a84a4e6d 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -304,14 +304,14 @@ local function setup_autocommands(opts) create_nvim_tree_autocmd("DiagnosticChanged", { callback = function() log.line("diagnostics", "DiagnosticChanged") - reloaders.reload_explorer() + require("nvim-tree.renderer").draw() end, }) create_nvim_tree_autocmd("User", { pattern = "CocDiagnosticChange", callback = function() log.line("diagnostics", "CocDiagnosticChange") - reloaders.reload_explorer() + require("nvim-tree.renderer").draw() end, }) end diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index a8c8e4aa66e..8e8146174c7 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -397,12 +397,17 @@ end function Builder:build(tree, unloaded_bufnr) local num_children = self:_get_nodes_number(tree.nodes) - local _diagnostics = diagnostics.get_diagnostics() + local all_diagnostics = diagnostics.get_diagnostics() + + if self.diagnostic_placement == "signcolumn" then + diagnostics.clear() + end + local idx = 1 for _, node in ipairs(tree.nodes) do if not node.hidden then local nodepath = utils.canonical_path(node.absolute_path) - self:_build_line(node, idx, num_children, unloaded_bufnr, _diagnostics[nodepath]) + self:_build_line(node, idx, num_children, unloaded_bufnr, all_diagnostics[nodepath]) idx = idx + 1 end end From 29ff2c302b17be25992ce86bde1b5fef87b9b6bd Mon Sep 17 00:00:00 2001 From: Bastian Ahrens Date: Mon, 24 Apr 2023 22:25:45 +0200 Subject: [PATCH 5/6] feat: Add diagnostics signs for folders --- lua/nvim-tree/diagnostics.lua | 3 --- lua/nvim-tree/renderer/builder.lua | 38 +++++++++++++++++++++++++----- lua/nvim-tree/renderer/init.lua | 3 +++ 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index e375d2686c5..cca11378247 100644 --- a/lua/nvim-tree/diagnostics.lua +++ b/lua/nvim-tree/diagnostics.lua @@ -134,9 +134,6 @@ function M.setup(opts) log.line("diagnostics", "setup") end - M.show_on_dirs = opts.diagnostics.show_on_dirs - M.show_on_open_dirs = opts.diagnostics.show_on_open_dirs - for lhs, rhs in pairs(links) do vim.cmd("hi def link " .. lhs .. " " .. rhs) end diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index 8e8146174c7..848130caba1 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -24,6 +24,16 @@ function Builder.new(root_cwd) }, Builder) end +function Builder:configure_show_on_open_dirs(show_on_open_dirs) + self.show_on_open_dirs = show_on_open_dirs + return self +end + +function Builder:configure_show_on_dirs(show_on_dirs) + self.show_on_dirs = show_on_dirs + return self +end + function Builder:configure_root_label(root_folder_label) self.root_folder_label = root_folder_label or DEFAULT_ROOT_FOLDER_LABEL return self @@ -237,15 +247,13 @@ end ---@param node table ---@return HighlightedString|nil icon function Builder:_get_diagnostic_icon(node, diagnostic_severity) - local diagnostic_icon = diagnostics.get_sign(diagnostic_severity) - if self.diagnostic_placement == "signcolumn" and diagnostic_severity then node.diag_status = diagnostic_severity diagnostics.add_sign(self.index + 1, diagnostic_severity) - diagnostic_icon = nil + return nil end - return diagnostic_icon + return diagnostics.get_sign(diagnostic_severity) end ---@param node table @@ -397,17 +405,35 @@ end function Builder:build(tree, unloaded_bufnr) local num_children = self:_get_nodes_number(tree.nodes) - local all_diagnostics = diagnostics.get_diagnostics() + local diagnostic_severities_by_path = diagnostics.get_diagnostics() if self.diagnostic_placement == "signcolumn" then diagnostics.clear() end local idx = 1 + for _, node in ipairs(tree.nodes) do if not node.hidden then + local is_folder = node.nodes ~= nil local nodepath = utils.canonical_path(node.absolute_path) - self:_build_line(node, idx, num_children, unloaded_bufnr, all_diagnostics[nodepath]) + + local severity = nil + if is_folder then + for bufname, buf_severity in pairs(diagnostic_severities_by_path) do + local bufpath = utils.canonical_path(bufname) + if + self.show_on_dirs + and vim.startswith(bufpath:gsub("\\", "/"), nodepath:gsub("\\", "/") .. "/") + and (not node.open or self.show_on_open_dirs) + then + severity = buf_severity + end + end + elseif diagnostic_severities_by_path[nodepath] then + severity = diagnostic_severities_by_path[nodepath] + end + self:_build_line(node, idx, num_children, unloaded_bufnr, severity) idx = idx + 1 end end diff --git a/lua/nvim-tree/renderer/init.lua b/lua/nvim-tree/renderer/init.lua index 9363e6bfce0..ca4a7a65905 100644 --- a/lua/nvim-tree/renderer/init.lua +++ b/lua/nvim-tree/renderer/init.lua @@ -68,6 +68,8 @@ function M.draw(unloaded_bufnr) :configure_icon_padding(M.config.icons.padding) :configure_git_icons_placement(M.config.icons.git_placement) :configure_diagnostic_icons_placement(M.config.icons.diagnostic_placement) + :configure_show_on_dirs(M.config.diagnostics.show_on_dirs) + :configure_show_on_open_dirs(M.config.diagnostics.show_on_open_dirs) :configure_modified_placement(M.config.icons.modified_placement) :configure_symlink_destination(M.config.symlink_destination) :configure_filter(live_filter.filter, live_filter.prefix) @@ -92,6 +94,7 @@ end function M.setup(opts) M.config = opts.renderer + M.config.diagnostics = opts.diagnostics M.config.modified = opts.modified _padding.setup(opts) From aa597b4ec97af1f71ee44df05d38046c0f461f15 Mon Sep 17 00:00:00 2001 From: Bastian Ahrens Date: Mon, 24 Apr 2023 22:36:04 +0200 Subject: [PATCH 6/6] feat: Add profiling to diagnostic update --- lua/nvim-tree/renderer/builder.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index 848130caba1..7b24130f869 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -1,6 +1,7 @@ local utils = require "nvim-tree.utils" local core = require "nvim-tree.core" +local log = require "nvim-tree.log" local diagnostics = require "nvim-tree.diagnostics" local git = require "nvim-tree.renderer.components.git" local pad = require "nvim-tree.renderer.components.padding" @@ -413,6 +414,8 @@ function Builder:build(tree, unloaded_bufnr) local idx = 1 + local profile = log.profile_start "diagnostic update" + log.line("diagnostics", "update") for _, node in ipairs(tree.nodes) do if not node.hidden then local is_folder = node.nodes ~= nil @@ -422,21 +425,25 @@ function Builder:build(tree, unloaded_bufnr) if is_folder then for bufname, buf_severity in pairs(diagnostic_severities_by_path) do local bufpath = utils.canonical_path(bufname) + log.line("diagnostics", " %d checking nodepath '%s'", idx, nodepath) if self.show_on_dirs and vim.startswith(bufpath:gsub("\\", "/"), nodepath:gsub("\\", "/") .. "/") and (not node.open or self.show_on_open_dirs) then + log.line("diagnostics", " matched folder node '%s'", nodepath) severity = buf_severity end end elseif diagnostic_severities_by_path[nodepath] then + log.line("diagnostics", " matched file node '%s'", nodepath) severity = diagnostic_severities_by_path[nodepath] end self:_build_line(node, idx, num_children, unloaded_bufnr, severity) idx = idx + 1 end end + log.profile_end(profile) return self end