diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 1bf8b95edd0..591a670f428 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() + require("nvim-tree.renderer").draw() end, }) create_nvim_tree_autocmd("User", { pattern = "CocDiagnosticChange", callback = function() log.line("diagnostics", "CocDiagnosticChange") - require("nvim-tree.diagnostics").update() + require("nvim-tree.renderer").draw() end, }) end @@ -406,6 +406,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS }, icons = { webdev_colors = true, + diagnostic_placement = "signcolumn", git_placement = "before", modified_placement = "after", padding = " ", diff --git a/lua/nvim-tree/diagnostics.lua b/lua/nvim-tree/diagnostics.lua index 95f76031472..cca11378247 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" @@ -15,7 +14,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 +93,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,18 +116,24 @@ 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") end - 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) end diff --git a/lua/nvim-tree/renderer/builder.lua b/lua/nvim-tree/renderer/builder.lua index 4ffded19015..a71e0c9978e 100644 --- a/lua/nvim-tree/renderer/builder.lua +++ b/lua/nvim-tree/renderer/builder.lua @@ -1,6 +1,8 @@ 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" local icons = require "nvim-tree.renderer.components.icons" @@ -23,6 +25,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 @@ -72,6 +84,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" + 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 +245,18 @@ 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) + if self.diagnostic_placement == "signcolumn" and diagnostic_severity then + node.diag_status = diagnostic_severity + diagnostics.add_sign(self.index + 1, diagnostic_severity) + return nil + end + + return diagnostics.get_sign(diagnostic_severity) +end + ---@param node table ---@return string icon_highlight, string name_highlight function Builder:_get_highlight_override(node, unloaded_bufnr) @@ -272,9 +304,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 +323,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 +378,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 @@ -366,13 +408,44 @@ end function Builder:build(tree, unloaded_bufnr) local num_children = self:_get_nodes_number(tree.nodes) + local diagnostic_severities_by_path = diagnostics.get_diagnostics() + + if self.diagnostic_placement == "signcolumn" then + diagnostics.clear() + end + 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 - self:_build_line(node, idx, num_children, unloaded_bufnr) + local is_folder = node.nodes ~= nil + local nodepath = utils.canonical_path(node.absolute_path) + + local severity = nil + 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 diff --git a/lua/nvim-tree/renderer/init.lua b/lua/nvim-tree/renderer/init.lua index 3a61adcf240..ca4a7a65905 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" @@ -68,6 +67,9 @@ 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_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) @@ -83,7 +85,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() @@ -93,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)