From 31481abe3e6aecc7a8aac17424cc7ef4aa328d2f Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 11:09:06 +1100 Subject: [PATCH 1/9] perf(#3257): replace view.initial_width with a function --- lua/nvim-tree/view.lua | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lua/nvim-tree/view.lua b/lua/nvim-tree/view.lua index 6e36c8e50da..9b84feff2ef 100644 --- a/lua/nvim-tree/view.lua +++ b/lua/nvim-tree/view.lua @@ -116,7 +116,7 @@ local function create_buffer(bufnr) events._dispatch_tree_attached_post(M.get_bufnr()) end ----@param size (fun():integer)|integer|string +---@param size nvim_tree.config.view.width.spec ---@return integer local function get_size(size) if type(size) == "number" then @@ -129,6 +129,16 @@ local function get_size(size) return math.floor(vim.o.columns * percent_as_decimal) end +---Return the width as per config +---@return integer +local function initial_width() + if type(config.g.view.width) == "table" then + return get_size(config.g.view.width.min or DEFAULT_MIN_WIDTH) + else + return get_size(config.g.view.width --[[@as nvim_tree.config.view.width.spec]]) + end +end + ---@param size (fun():integer)|integer|nil ---@return integer local function get_width(size) @@ -317,7 +327,7 @@ local function grow() padding = padding + wininfo[1].textoff end - local final_width = M.View.initial_width + local final_width = initial_width() local max_width = get_width(M.View.max_width) if max_width == -1 then max_width = math.huge @@ -606,7 +616,7 @@ function M.configure_width(width) M.configure_width(config.g.view.width) else -- otherwise - restore initial width - M.View.width = M.View.initial_width + M.View.width = initial_width() end else M.View.adaptive_size = false @@ -624,8 +634,6 @@ function M.setup(opts) M.View.winopts.signcolumn = options.signcolumn M.configure_width(options.width) - - M.View.initial_width = get_width() end return M From ae58f0ef797ba0ff2dd903ef537abc3a1e9abd47 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 11:33:24 +1100 Subject: [PATCH 2/9] perf(#3257): remove winopts from view setup --- lua/nvim-tree/view.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/nvim-tree/view.lua b/lua/nvim-tree/view.lua index 9b84feff2ef..04289dafa63 100644 --- a/lua/nvim-tree/view.lua +++ b/lua/nvim-tree/view.lua @@ -164,6 +164,12 @@ end local function set_window_options_and_buffer() pcall(vim.api.nvim_command, "buffer " .. M.get_bufnr()) + M.View.winopts.cursorline = config.g.view.cursorline + M.View.winopts.cursorlineopt = config.g.view.cursorlineopt + M.View.winopts.number = config.g.view.number + M.View.winopts.relativenumber = config.g.view.relativenumber + M.View.winopts.signcolumn = config.g.view.signcolumn + if vim.fn.has("nvim-0.10") == 1 then local eventignore = vim.api.nvim_get_option_value("eventignore", {}) vim.api.nvim_set_option_value("eventignore", "all", {}) @@ -627,12 +633,6 @@ end ---@param opts nvim_tree.config function M.setup(opts) local options = opts.view or {} - M.View.winopts.cursorline = options.cursorline - M.View.winopts.cursorlineopt = options.cursorlineopt - M.View.winopts.number = options.number - M.View.winopts.relativenumber = options.relativenumber - M.View.winopts.signcolumn = options.signcolumn - M.configure_width(options.width) end From d361be831ce6e9de9a36d08e3b4b1be7f740a9f6 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 11:51:18 +1100 Subject: [PATCH 3/9] perf(#3257): remove view setup --- lua/nvim-tree.lua | 1 - lua/nvim-tree/view.lua | 66 ++++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index f29005e5961..aa9b08a2c0b 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -289,7 +289,6 @@ function M.setup(config_user) end require("nvim-tree.appearance").setup() - require("nvim-tree.view").setup(config.g) require("nvim-tree.renderer.components").setup(config.g) setup_autocommands() diff --git a/lua/nvim-tree/view.lua b/lua/nvim-tree/view.lua index 04289dafa63..68be68a419e 100644 --- a/lua/nvim-tree/view.lua +++ b/lua/nvim-tree/view.lua @@ -139,6 +139,31 @@ local function initial_width() end end +---Configure width-related config +---@param width string|function|number|table|nil +function M.configure_width(width) + log.line("dev", "configure_width") + if type(width) == "table" then + M.View.adaptive_size = true + M.View.width = width.min or DEFAULT_MIN_WIDTH + M.View.max_width = width.max or DEFAULT_MAX_WIDTH + local lines_excluded = width.lines_excluded or DEFAULT_LINES_EXCLUDED + M.View.root_excluded = vim.tbl_contains(lines_excluded, "root") + M.View.padding = width.padding or DEFAULT_PADDING + elseif width == nil then + if config.g.view.width ~= nil then + -- if we had input config - fallback to it + M.configure_width(config.g.view.width) + else + -- otherwise - restore initial width + M.View.width = initial_width() + end + else + M.View.adaptive_size = false + M.View.width = width + end +end + ---@param size (fun():integer)|integer|nil ---@return integer local function get_width(size) @@ -198,7 +223,7 @@ local function open_win_config() if type(config.g.view.float.open_win_config) == "function" then return config.g.view.float.open_win_config() else - return config.g.view.float.open_win_config --[[ @as vim.api.keyset.win_config ]] + return config.g.view.float.open_win_config --[[@as vim.api.keyset.win_config]] end end @@ -305,6 +330,10 @@ function M.open(options) return end + if not M.View.width then + M.configure_width(config.g.view.width) + end + local profile = log.profile_start("view open") events._dispatch_on_tree_pre_open() @@ -420,6 +449,11 @@ end ---@param opts OpenInWinOpts|nil function M.open_in_win(opts) opts = opts or { hijack_current_buf = true, resize = true } + + if not M.View.width then + M.configure_width(config.g.view.width) + end + events._dispatch_on_tree_pre_open() if opts.winid and vim.api.nvim_win_is_valid(opts.winid) then vim.api.nvim_set_current_win(opts.winid) @@ -606,34 +640,4 @@ function M.is_width_determined() return type(M.View.width) ~= "function" end ----Configure width-related config ----@param width string|function|number|table|nil -function M.configure_width(width) - if type(width) == "table" then - M.View.adaptive_size = true - M.View.width = width.min or DEFAULT_MIN_WIDTH - M.View.max_width = width.max or DEFAULT_MAX_WIDTH - local lines_excluded = width.lines_excluded or DEFAULT_LINES_EXCLUDED - M.View.root_excluded = vim.tbl_contains(lines_excluded, "root") - M.View.padding = width.padding or DEFAULT_PADDING - elseif width == nil then - if config.g.view.width ~= nil then - -- if we had input config - fallback to it - M.configure_width(config.g.view.width) - else - -- otherwise - restore initial width - M.View.width = initial_width() - end - else - M.View.adaptive_size = false - M.View.width = width - end -end - ----@param opts nvim_tree.config -function M.setup(opts) - local options = opts.view or {} - M.configure_width(options.width) -end - return M From 39e446aa4d01073978b9f6cfbee334fa1a2bbdd5 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 12:29:22 +1100 Subject: [PATCH 4/9] perf(#3257): remove view setup --- lua/nvim-tree/view.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/nvim-tree/view.lua b/lua/nvim-tree/view.lua index 68be68a419e..f0fc8a21faa 100644 --- a/lua/nvim-tree/view.lua +++ b/lua/nvim-tree/view.lua @@ -142,7 +142,6 @@ end ---Configure width-related config ---@param width string|function|number|table|nil function M.configure_width(width) - log.line("dev", "configure_width") if type(width) == "table" then M.View.adaptive_size = true M.View.width = width.min or DEFAULT_MIN_WIDTH From 05a49e284c468ea6207b2c71e01db15041809f79 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 12:14:50 +1100 Subject: [PATCH 5/9] perf(#3257): remove devicons setup --- .../renderer/components/devicons.lua | 35 ++++++++++++------- lua/nvim-tree/renderer/components/init.lua | 2 -- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/lua/nvim-tree/renderer/components/devicons.lua b/lua/nvim-tree/renderer/components/devicons.lua index ad91d058c15..d5fd307c2cd 100644 --- a/lua/nvim-tree/renderer/components/devicons.lua +++ b/lua/nvim-tree/renderer/components/devicons.lua @@ -1,3 +1,5 @@ +local config = require("nvim-tree.config") + ---@alias devicons_get_icon fun(name: string, ext: string?, opts: table?): string?, string? ---@alias devicons_setup fun(opts: table?) @@ -6,22 +8,14 @@ ---@field get_icon devicons_get_icon local devicons -local M = {} +--One shot lazy discovery and setup done +local initialized = false ----Wrapper around nvim-web-devicons, nils if devicons not available ----@type devicons_get_icon -function M.get_icon(name, ext, opts) - if devicons then - return devicons.get_icon(name, ext, opts) - else - return nil, nil - end -end +local M = {} ---Attempt to use nvim-web-devicons if present and enabled for file or folder ----@param opts table -function M.setup(opts) - if opts.renderer.icons.show.file or opts.renderer.icons.show.folder then +local function initialize() + if config.g.renderer.icons.show.file or config.g.renderer.icons.show.folder then local ok, di = pcall(require, "nvim-web-devicons") if ok then devicons = di --[[@as DevIcons]] @@ -30,6 +24,21 @@ function M.setup(opts) devicons.setup() end end + initialized = true +end + +---Wrapper around nvim-web-devicons, nils if devicons not available +---@type devicons_get_icon +function M.get_icon(name, ext, opts) + if not initialized then + initialize() + end + + if devicons then + return devicons.get_icon(name, ext, opts) + else + return nil, nil + end end return M diff --git a/lua/nvim-tree/renderer/components/init.lua b/lua/nvim-tree/renderer/components/init.lua index a06827422dd..5ef3bbd6d8e 100644 --- a/lua/nvim-tree/renderer/components/init.lua +++ b/lua/nvim-tree/renderer/components/init.lua @@ -1,10 +1,8 @@ local M = {} -M.devicons = require("nvim-tree.renderer.components.devicons") M.padding = require("nvim-tree.renderer.components.padding") function M.setup(opts) - M.devicons.setup(opts) M.padding.setup(opts) end From 874c971e463ef68b9bd20920fe6fe8c0e6fd6f9a Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 12:49:51 +1100 Subject: [PATCH 6/9] perf(#3257): remove padding setup --- lua/nvim-tree.lua | 1 - lua/nvim-tree/config.lua | 15 +++++ lua/nvim-tree/renderer/components/init.lua | 9 --- lua/nvim-tree/renderer/components/padding.lua | 55 ++++++------------- 4 files changed, 33 insertions(+), 47 deletions(-) delete mode 100644 lua/nvim-tree/renderer/components/init.lua diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index aa9b08a2c0b..e86510920e1 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -289,7 +289,6 @@ function M.setup(config_user) end require("nvim-tree.appearance").setup() - require("nvim-tree.renderer.components").setup(config.g) setup_autocommands() diff --git a/lua/nvim-tree/config.lua b/lua/nvim-tree/config.lua index 8e819961a6d..5c3403d36d7 100644 --- a/lua/nvim-tree/config.lua +++ b/lua/nvim-tree/config.lua @@ -511,6 +511,21 @@ local function process_config(g) -- Open -- g.actions.open_file.window_picker.chars = tostring(g.actions.open_file.window_picker.chars):upper() + + -- + -- Padding + -- + if g.renderer.indent_width < 1 then + g.renderer.indent_width = 1 + end + for k, v in pairs(g.renderer.indent_markers.icons) do + if #v == 0 then + g.renderer.indent_markers.icons[k] = " " + else + -- return the first character from the UTF-8 encoded string; we may use utf8.codes from Lua 5.3 when available + g.renderer.indent_markers.icons[k] = v:match("[%z\1-\127\194-\244][\128-\191]*") + end + end end ---Validate user config and migrate legacy. diff --git a/lua/nvim-tree/renderer/components/init.lua b/lua/nvim-tree/renderer/components/init.lua deleted file mode 100644 index 5ef3bbd6d8e..00000000000 --- a/lua/nvim-tree/renderer/components/init.lua +++ /dev/null @@ -1,9 +0,0 @@ -local M = {} - -M.padding = require("nvim-tree.renderer.components.padding") - -function M.setup(opts) - M.padding.setup(opts) -end - -return M diff --git a/lua/nvim-tree/renderer/components/padding.lua b/lua/nvim-tree/renderer/components/padding.lua index be84ae0b9de..9e04addfdaf 100644 --- a/lua/nvim-tree/renderer/components/padding.lua +++ b/lua/nvim-tree/renderer/components/padding.lua @@ -1,3 +1,4 @@ +local config = require("nvim-tree.config") local DirectoryNode = require("nvim-tree.node.directory") local M = {} @@ -27,21 +28,21 @@ local function get_padding_indent_markers(depth, idx, nodes_number, markers, wit if depth > 0 then local has_folder_sibling = check_siblings_for_folder(node, with_arrows) - local indent = string.rep(" ", M.config.indent_width - 1) + local indent = string.rep(" ", config.g.renderer.indent_width - 1) markers[depth] = idx ~= nodes_number for i = 1, depth - early_stop do local glyph if idx == nodes_number and i == depth then - local bottom_width = M.config.indent_width - 2 + (with_arrows and not inline_arrows and has_folder_sibling and 2 or 0) - glyph = M.config.indent_markers.icons.corner - .. string.rep(M.config.indent_markers.icons.bottom, bottom_width) - .. (M.config.indent_width > 1 and " " or "") + local bottom_width = config.g.renderer.indent_width - 2 + (with_arrows and not inline_arrows and has_folder_sibling and 2 or 0) + glyph = config.g.renderer.indent_markers.icons.corner + .. string.rep(config.g.renderer.indent_markers.icons.bottom, bottom_width) + .. (config.g.renderer.indent_width > 1 and " " or "") elseif markers[i] and i == depth then - glyph = M.config.indent_markers.icons.item .. indent + glyph = config.g.renderer.indent_markers.icons.item .. indent elseif markers[i] then - glyph = M.config.indent_markers.icons.edge .. indent + glyph = config.g.renderer.indent_markers.icons.edge .. indent else - glyph = M.config.indent_markers.icons.none .. indent + glyph = config.g.renderer.indent_markers.icons.none .. indent end if not with_arrows or (inline_arrows and (depth ~= i or not node.nodes)) then @@ -68,10 +69,10 @@ end function M.get_indent_markers(depth, idx, nodes_number, node, markers, early_stop) local str = "" - local show_arrows = M.config.icons.show.folder_arrow - local show_markers = M.config.indent_markers.enable - local inline_arrows = M.config.indent_markers.inline_arrows - local indent_width = M.config.indent_width + local show_arrows = config.g.renderer.icons.show.folder_arrow + local show_markers = config.g.renderer.indent_markers.enable + local inline_arrows = config.g.renderer.indent_markers.inline_arrows + local indent_width = config.g.renderer.indent_width if show_markers then str = str .. get_padding_indent_markers(depth, idx, nodes_number, markers, show_arrows, inline_arrows, node, early_stop or 0) @@ -85,7 +86,7 @@ end ---@param node Node ---@return nvim_tree.api.highlighted_string[]? function M.get_arrows(node) - if not M.config.icons.show.folder_arrow then + if not config.g.renderer.icons.show.folder_arrow then return end @@ -95,38 +96,18 @@ function M.get_arrows(node) local dir = node:as(DirectoryNode) if dir then if dir.open then - str = M.config.icons.glyphs.folder["arrow_open"] .. M.config.icons.padding.folder_arrow + str = config.g.renderer.icons.glyphs.folder["arrow_open"] .. config.g.renderer.icons.padding.folder_arrow hl = "NvimTreeFolderArrowOpen" else - str = M.config.icons.glyphs.folder["arrow_closed"] .. M.config.icons.padding.folder_arrow + str = config.g.renderer.icons.glyphs.folder["arrow_closed"] .. config.g.renderer.icons.padding.folder_arrow end - elseif M.config.indent_markers.enable then + elseif config.g.renderer.indent_markers.enable then str = "" else - str = " " .. string.rep(" ", #M.config.icons.padding.folder_arrow) + str = " " .. string.rep(" ", #config.g.renderer.icons.padding.folder_arrow) end return { str = str, hl = { hl } } end -function M.setup(opts) - M.config = opts.renderer - - if M.config.indent_width < 1 then - M.config.indent_width = 1 - end - - local function check_marker(symbol) - if #symbol == 0 then - return " " - end - -- return the first character from the UTF-8 encoded string; we may use utf8.codes from Lua 5.3 when available - return symbol:match("[%z\1-\127\194-\244][\128-\191]*") - end - - for k, v in pairs(M.config.indent_markers.icons) do - M.config.indent_markers.icons[k] = check_marker(v) - end -end - return M From d5d51b91aade861a19512cfbeb7253c576a3da24 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 13:24:43 +1100 Subject: [PATCH 7/9] perf(#3257): remove appearance and log setup --- lua/nvim-tree.lua | 4 ++-- lua/nvim-tree/appearance/init.lua | 3 ++- lua/nvim-tree/explorer/init.lua | 2 +- lua/nvim-tree/log.lua | 11 +++++++---- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index e86510920e1..d560ac741cf 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -281,14 +281,14 @@ function M.setup(config_user) manage_netrw() - require("nvim-tree.log").setup(config.g) + require("nvim-tree.log").start() if log.enabled("config") then log.line("config", "default config + user") log.raw("config", "%s\n", vim.inspect(config.g)) end - require("nvim-tree.appearance").setup() + require("nvim-tree.appearance").define_hi() setup_autocommands() diff --git a/lua/nvim-tree/appearance/init.lua b/lua/nvim-tree/appearance/init.lua index 61714af644e..1b8d27cedc1 100644 --- a/lua/nvim-tree/appearance/init.lua +++ b/lua/nvim-tree/appearance/init.lua @@ -182,7 +182,8 @@ M.LEGACY_LINKS = { NvimTreeDiagnosticHintFolderHL = "NvimTreeLspDiagnosticsHintFolderText", } -function M.setup() +---Create all highlight groups and links. Idempotent. +function M.define_hi() -- non-linked for _, g in ipairs(M.HIGHLIGHT_GROUPS) do if g.def then diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index eb9b5f5d097..3559a74cf5c 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -88,7 +88,7 @@ function Explorer:create_autocmds() vim.api.nvim_create_autocmd("ColorScheme", { group = self.augroup_id, callback = function() - appearance.setup() + appearance.define_hi() view.reset_winhl() self.renderer:draw() end, diff --git a/lua/nvim-tree/log.lua b/lua/nvim-tree/log.lua index 9665c1335e6..ff5686061fd 100644 --- a/lua/nvim-tree/log.lua +++ b/lua/nvim-tree/log.lua @@ -1,3 +1,5 @@ +local config = require("nvim-tree.config") + ---@alias LogTypes "all" | "config" | "copy_paste" | "dev" | "diagnostics" | "git" | "profile" | "watcher" ---@type table @@ -110,11 +112,12 @@ function M.enabled(typ) return file_path ~= nil and (types[typ] or types.all) end -function M.setup(opts) - if opts.log and opts.log.enable and opts.log.types then - types = opts.log.types +--- Create the log file and enable logging, if globally configured +function M.start() + if config.g.log and config.g.log.enable and config.g.log.types then + types = config.g.log.types file_path = string.format("%s/nvim-tree.log", vim.fn.stdpath("log"), os.date("%H:%M:%S"), vim.env.USER) - if opts.log.truncate then + if config.g.log.truncate then os.remove(file_path) end require("nvim-tree.notify").debug("nvim-tree.lua logging to " .. file_path) From 3a247fbf2a032b59f72a3b7fca733bcc44d4f6e5 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 13:28:52 +1100 Subject: [PATCH 8/9] perf(#3257): remove appearance setup --- lua/nvim-tree.lua | 2 +- lua/nvim-tree/appearance/init.lua | 2 +- lua/nvim-tree/explorer/init.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index d560ac741cf..4d05899bf2a 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -288,7 +288,7 @@ function M.setup(config_user) log.raw("config", "%s\n", vim.inspect(config.g)) end - require("nvim-tree.appearance").define_hi() + require("nvim-tree.appearance").set_hl() setup_autocommands() diff --git a/lua/nvim-tree/appearance/init.lua b/lua/nvim-tree/appearance/init.lua index 1b8d27cedc1..9190ad07d49 100644 --- a/lua/nvim-tree/appearance/init.lua +++ b/lua/nvim-tree/appearance/init.lua @@ -183,7 +183,7 @@ M.LEGACY_LINKS = { } ---Create all highlight groups and links. Idempotent. -function M.define_hi() +function M.set_hl() -- non-linked for _, g in ipairs(M.HIGHLIGHT_GROUPS) do if g.def then diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 3559a74cf5c..d33be5f10bf 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -88,7 +88,7 @@ function Explorer:create_autocmds() vim.api.nvim_create_autocmd("ColorScheme", { group = self.augroup_id, callback = function() - appearance.define_hi() + appearance.set_hl() view.reset_winhl() self.renderer:draw() end, From 1d8038af8bbe0725bce04d32479b5a90da013798 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 28 Mar 2026 13:31:14 +1100 Subject: [PATCH 9/9] perf(#3257): remove rename-file setup --- lua/nvim-tree/actions/fs/rename-file.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lua/nvim-tree/actions/fs/rename-file.lua b/lua/nvim-tree/actions/fs/rename-file.lua index 0b4f22ac1d7..0740b813841 100644 --- a/lua/nvim-tree/actions/fs/rename-file.lua +++ b/lua/nvim-tree/actions/fs/rename-file.lua @@ -192,8 +192,4 @@ function M.rename_full(node) prompt_to_rename(node, ":p") end -function M.setup(opts) - config.g.filesystem_watchers = opts.filesystem_watchers -end - return M