From 6b1f5b88f51c342d6be21141ebb814c065792d94 Mon Sep 17 00:00:00 2001 From: uanela Date: Tue, 9 Dec 2025 16:43:37 +0200 Subject: [PATCH 01/17] chore: moving root action dir-up to explorer class --- lua/nvim-tree/actions/root/dir-up.lua | 22 ---------------------- lua/nvim-tree/actions/root/init.lua | 1 - lua/nvim-tree/api.lua | 2 +- lua/nvim-tree/explorer/init.lua | 16 ++++++++++++++++ 4 files changed, 17 insertions(+), 24 deletions(-) delete mode 100644 lua/nvim-tree/actions/root/dir-up.lua diff --git a/lua/nvim-tree/actions/root/dir-up.lua b/lua/nvim-tree/actions/root/dir-up.lua deleted file mode 100644 index a8c41c8c6f0..00000000000 --- a/lua/nvim-tree/actions/root/dir-up.lua +++ /dev/null @@ -1,22 +0,0 @@ -local utils = require("nvim-tree.utils") -local core = require("nvim-tree.core") - -local M = {} - ----@param node Node -function M.fn(node) - if not node or node.name == ".." then - require("nvim-tree.actions.root.change-dir").fn("..") - else - local cwd = core.get_cwd() - if cwd == nil then - return - end - - local newdir = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h") - require("nvim-tree.actions.root.change-dir").fn(newdir) - require("nvim-tree.actions.finders.find-file").fn(node.absolute_path) - end -end - -return M diff --git a/lua/nvim-tree/actions/root/init.lua b/lua/nvim-tree/actions/root/init.lua index 1177e2050e1..cb64420d6d4 100644 --- a/lua/nvim-tree/actions/root/init.lua +++ b/lua/nvim-tree/actions/root/init.lua @@ -1,7 +1,6 @@ local M = {} M.change_dir = require("nvim-tree.actions.root.change-dir") -M.dir_up = require("nvim-tree.actions.root.dir-up") function M.setup(opts) M.change_dir.setup(opts) diff --git a/lua/nvim-tree/api.lua b/lua/nvim-tree/api.lua index a1ee507f8af..cd2b33b953f 100644 --- a/lua/nvim-tree/api.lua +++ b/lua/nvim-tree/api.lua @@ -168,7 +168,7 @@ Api.tree.change_root_to_node = wrap_node(function(node) end end) -Api.tree.change_root_to_parent = wrap_node(actions.root.dir_up.fn) +Api.tree.change_root_to_parent = wrap_node(wrap_explorer("dir_up")) Api.tree.get_node_under_cursor = wrap_explorer("get_node_at_cursor") Api.tree.get_nodes = wrap_explorer("get_nodes") diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index afd1b182cee..3e8df0ebd5c 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -659,6 +659,22 @@ function Explorer:get_nodes_by_line(line_start) return nodes_by_line end +---@param node Node +function Explorer:dir_up(node) + if not node or node.name == ".." then + require("nvim-tree.actions.root.change-dir").fn("..") + else + local cwd = core.get_cwd() + if cwd == nil then + return + end + + local newdir = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h") + require("nvim-tree.actions.root.change-dir").fn(newdir) + require("nvim-tree.actions.finders.find-file").fn(node.absolute_path) + end +end + ---Api.tree.get_nodes ---@return nvim_tree.api.Node function Explorer:get_nodes() From 41585a8ef86aba52b7793daa41c727b50a15fa29 Mon Sep 17 00:00:00 2001 From: uanela Date: Fri, 19 Dec 2025 18:15:30 +0200 Subject: [PATCH 02/17] chore(explorer): moving requires to top level --- lua/nvim-tree/explorer/init.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 3e8df0ebd5c..013d2c2efa4 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -22,6 +22,8 @@ local Clipboard = require("nvim-tree.actions.fs.clipboard") local Renderer = require("nvim-tree.renderer") local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON +local change_dir = require("nvim-tree.actions.root.change-dir") +local find_file = require("nvim-tree.actions.finders.find-file") local config @@ -662,7 +664,7 @@ end ---@param node Node function Explorer:dir_up(node) if not node or node.name == ".." then - require("nvim-tree.actions.root.change-dir").fn("..") + change_dir.fn("..") else local cwd = core.get_cwd() if cwd == nil then @@ -670,8 +672,8 @@ function Explorer:dir_up(node) end local newdir = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h") - require("nvim-tree.actions.root.change-dir").fn(newdir) - require("nvim-tree.actions.finders.find-file").fn(node.absolute_path) + change_dir.fn(newdir) + find_file.fn(node.absolute_path) end end From a12890c4b153213f71905bb9a6362b690fc58b7c Mon Sep 17 00:00:00 2001 From: uanela Date: Sat, 27 Dec 2025 12:44:47 +0200 Subject: [PATCH 03/17] wip(explorer): moved all change-dir functions to explorer class --- lua/nvim-tree/actions/root/change-dir.lua | 92 -------------------- lua/nvim-tree/explorer/init.lua | 101 ++++++++++++++++++++-- 2 files changed, 95 insertions(+), 98 deletions(-) diff --git a/lua/nvim-tree/actions/root/change-dir.lua b/lua/nvim-tree/actions/root/change-dir.lua index f23f3c12086..7a04939c62d 100644 --- a/lua/nvim-tree/actions/root/change-dir.lua +++ b/lua/nvim-tree/actions/root/change-dir.lua @@ -6,98 +6,6 @@ local M = { current_tab = vim.api.nvim_get_current_tabpage(), } ----@param name string ----@return string|nil -local function clean_input_cwd(name) - name = vim.fn.fnameescape(name) - local cwd = core.get_cwd() - if cwd == nil then - return - end - local root_parent_cwd = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h") - if name == ".." and root_parent_cwd then - return vim.fn.expand(root_parent_cwd) - else - return vim.fn.expand(name) - end -end - ----@param new_tabpage integer ----@return boolean -local function is_window_event(new_tabpage) - local is_event_scope_window = vim.v.event.scope == "window" or vim.v.event.changed_window or false - return is_event_scope_window and new_tabpage == M.current_tab -end - ----@param foldername string ----@return boolean -local function prevent_cwd_change(foldername) - local is_same_cwd = foldername == core.get_cwd() - local is_restricted_above = M.options.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1) - return is_same_cwd or is_restricted_above -end - ----@param input_cwd string ----@param with_open boolean|nil -function M.fn(input_cwd, with_open) - if not core.get_explorer() then - return - end - - local new_tabpage = vim.api.nvim_get_current_tabpage() - if is_window_event(new_tabpage) then - return - end - - local foldername = clean_input_cwd(input_cwd) - if foldername == nil or prevent_cwd_change(foldername) then - return - end - - M.current_tab = new_tabpage - M.force_dirchange(foldername, with_open) -end - ----@param global boolean ----@param path string -local function cd(global, path) - vim.cmd((global and "cd " or "lcd ") .. vim.fn.fnameescape(path)) -end - ----@return boolean -local function should_change_dir() - return M.options.enable and vim.tbl_isempty(vim.v.event) -end - ----@param f function ----@return fun(foldername: string, should_open_view: boolean|nil) -local function add_profiling_to(f) - return function(foldername, should_open_view) - local profile = log.profile_start("change dir %s", foldername) - f(foldername, should_open_view) - log.profile_end(profile) - end -end - -M.force_dirchange = add_profiling_to(function(foldername, should_open_view) - local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs - if valid_dir then - if should_change_dir() then - cd(M.options.global, foldername) - end - core.init(foldername) - end - - if should_open_view then - require("nvim-tree.lib").open() - else - local explorer = core.get_explorer() - if explorer then - explorer.renderer:draw() - end - end -end) - function M.setup(options) M.options = options.actions.change_dir end diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 013d2c2efa4..81140a273c1 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -31,6 +31,7 @@ local config ---@field uid_explorer number vim.loop.hrtime() at construction time ---@field opts table user options ---@field augroup_id integer +---@field current_tab integer ---@field renderer Renderer ---@field filters Filters ---@field live_filter LiveFilter @@ -60,12 +61,15 @@ function Explorer:new(args) self.open = true self.opts = config - self.sorters = Sorter({ explorer = self }) - self.renderer = Renderer({ explorer = self }) - self.filters = Filters({ explorer = self }) - self.live_filter = LiveFilter({ explorer = self }) - self.marks = Marks({ explorer = self }) - self.clipboard = Clipboard({ explorer = self }) + + self.sorters = Sorter({ explorer = self }) + self.renderer = Renderer({ explorer = self }) + self.filters = Filters({ explorer = self }) + self.live_filter = LiveFilter({ explorer = self }) + self.marks = Marks({ explorer = self }) + self.clipboard = Clipboard({ explorer = self }) + + self.current_tab = vim.api.nvim_get_current_tabpage() self:create_autocmds() @@ -683,6 +687,91 @@ function Explorer:get_nodes() return self:clone() end +---@param new_tabpage integer +---@return boolean +function Explorer:is_window_event(new_tabpage) + local is_event_scope_window = vim.v.event.scope == "window" or vim.v.event.changed_window or false + return is_event_scope_window and new_tabpage == M.current_tab +end + +---@param name string +---@return string|nil +function Explorer:clean_input_cwd(name) + name = vim.fn.fnameescape(name) + local cwd = core.get_cwd() + if cwd == nil then + return + end + local root_parent_cwd = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h") + if name == ".." and root_parent_cwd then + return vim.fn.expand(root_parent_cwd) + else + return vim.fn.expand(name) + end +end + +---@param foldername string +---@return boolean +function Explorer:prevent_cwd_change(foldername) + local is_same_cwd = foldername == core.get_cwd() + local is_restricted_above = M.options.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1) + return is_same_cwd or is_restricted_above +end + +function Explorer:force_dirchange(foldername, with_open) + local fn = add_profiling_to(function(foldername, should_open_view) + local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs + if valid_dir then + if should_change_dir() then + cd(M.options.global, foldername) + end + core.init(foldername) + end + + if should_open_view then + require("nvim-tree.lib").open() + else + local explorer = core.get_explorer() + if explorer then + explorer.renderer:draw() + end + end + end) + + fn(foldername, with_open) +end + +---@param f function +---@return fun(foldername: string, should_open_view: boolean|nil) +function Explorer:add_profiling_to(f) + return function(foldername, should_open_view) + local profile = log.profile_start("change dir %s", foldername) + f(foldername, should_open_view) + log.profile_end(profile) + end +end + +---@param input_cwd string +---@param with_open boolean|nil +function Explorer:change_dir(input_cwd, with_open) + if not core.get_explorer() then + return + end + + local new_tabpage = vim.api.nvim_get_current_tabpage() + if self:is_window_event(new_tabpage) then + return + end + + local foldername = self:clean_input_cwd(input_cwd) + if foldername == nil or self:prevent_cwd_change(foldername) then + return + end + + self.current_tab = new_tabpage + M.force_dirchange(foldername, with_open) +end + function Explorer:setup(opts) config = opts end From fd5ee1daded26097d6e35a2140415de73a33d81e Mon Sep 17 00:00:00 2001 From: uanela Date: Sat, 27 Dec 2025 12:48:58 +0200 Subject: [PATCH 04/17] wip(explorer): rename M.current_tab to self.current_tab --- lua/nvim-tree/explorer/init.lua | 45 ++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 81140a273c1..b7c572005ca 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -691,7 +691,7 @@ end ---@return boolean function Explorer:is_window_event(new_tabpage) local is_event_scope_window = vim.v.event.scope == "window" or vim.v.event.changed_window or false - return is_event_scope_window and new_tabpage == M.current_tab + return is_event_scope_window and new_tabpage == self.current_tab end ---@param name string @@ -718,12 +718,33 @@ function Explorer:prevent_cwd_change(foldername) return is_same_cwd or is_restricted_above end -function Explorer:force_dirchange(foldername, with_open) - local fn = add_profiling_to(function(foldername, should_open_view) +---@param f function +---@return fun(foldername: string, should_open_view: boolean|nil) +function Explorer:add_profiling_to(f) + return function(foldername, should_open_view) + local profile = log.profile_start("change dir %s", foldername) + f(foldername, should_open_view) + log.profile_end(profile) + end +end + +---@return boolean +function Explorer:should_change_dir() + return M.options.enable and vim.tbl_isempty(vim.v.event) +end + +---@param global boolean +---@param path string +function Explorer:cd(global, path) + vim.cmd((global and "cd " or "lcd ") .. vim.fn.fnameescape(path)) +end + +function Explorer:force_dirchange(_foldername, with_open) + local fn = self:add_profiling_to(function(foldername, should_open_view) local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs if valid_dir then - if should_change_dir() then - cd(M.options.global, foldername) + if self:should_change_dir() then + self:cd(M.options.global, foldername) end core.init(foldername) end @@ -738,17 +759,7 @@ function Explorer:force_dirchange(foldername, with_open) end end) - fn(foldername, with_open) -end - ----@param f function ----@return fun(foldername: string, should_open_view: boolean|nil) -function Explorer:add_profiling_to(f) - return function(foldername, should_open_view) - local profile = log.profile_start("change dir %s", foldername) - f(foldername, should_open_view) - log.profile_end(profile) - end + fn(_foldername, with_open) end ---@param input_cwd string @@ -769,7 +780,7 @@ function Explorer:change_dir(input_cwd, with_open) end self.current_tab = new_tabpage - M.force_dirchange(foldername, with_open) + self:force_dirchange(foldername, with_open) end function Explorer:setup(opts) From 85b621bc81a8fc2a3d50f6fbb71f909fead50a8f Mon Sep 17 00:00:00 2001 From: uanela Date: Sat, 27 Dec 2025 13:30:57 +0200 Subject: [PATCH 05/17] chore(explorer): move change_dir root action to explorer and replace all places using change_dir to use it from explorer --- lua/nvim-tree.lua | 22 ++++++++++++++++------ lua/nvim-tree/actions/init.lua | 2 -- lua/nvim-tree/actions/root/change-dir.lua | 13 ------------- lua/nvim-tree/actions/root/init.lua | 9 --------- lua/nvim-tree/api.lua | 6 +++--- lua/nvim-tree/explorer/init.lua | 11 +++++------ lua/nvim-tree/lib.lua | 5 +++-- 7 files changed, 27 insertions(+), 41 deletions(-) delete mode 100644 lua/nvim-tree/actions/root/change-dir.lua delete mode 100644 lua/nvim-tree/actions/root/init.lua diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index dc0178b4a18..f0d3450d302 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -11,6 +11,16 @@ local M = { init_root = "", } +--- Helper function to execute some explorer method safely +---@param fn string # key of explorer +---@return function|nil +local function explorer_fn(fn) + local explorer = core.get_explorer() + if explorer then + return explorer[fn] + end +end + --- Update the tree root to a directory or the directory containing ---@param path string relative or absolute ---@param bufnr number|nil @@ -47,7 +57,7 @@ function M.change_root(path, bufnr) -- test if in vim_cwd if utils.path_relative(path, vim_cwd) ~= path then if vim_cwd ~= cwd then - actions.root.change_dir.fn(vim_cwd) + explorer_fn("change_dir")(vim_cwd) end return end @@ -58,19 +68,19 @@ function M.change_root(path, bufnr) -- otherwise test M.init_root if _config.prefer_startup_root and utils.path_relative(path, M.init_root) ~= path then - actions.root.change_dir.fn(M.init_root) + explorer_fn("change_dir")(M.init_root) return end -- otherwise root_dirs for _, dir in pairs(_config.root_dirs) do dir = vim.fn.fnamemodify(dir, ":p") if utils.path_relative(path, dir) ~= path then - actions.root.change_dir.fn(dir) + explorer_fn("change_dir")(dir) return end end -- finally fall back to the folder containing the file - actions.root.change_dir.fn(vim.fn.fnamemodify(path, ":p:h")) + explorer_fn("change_dir")(vim.fn.fnamemodify(path, ":p:h")) end function M.tab_enter() @@ -110,7 +120,7 @@ function M.open_on_directory() return end - actions.root.change_dir.force_dirchange(bufname, true) + explorer_fn("force_dirchange")(bufname, true) end ---@return table @@ -134,7 +144,7 @@ end ---@param name string|nil function M.change_dir(name) if name then - actions.root.change_dir.fn(name) + explorer_fn("change_dir")(name) end if _config.update_focused_file.update_root.enable then diff --git a/lua/nvim-tree/actions/init.lua b/lua/nvim-tree/actions/init.lua index 0a85a26832c..d117a24119a 100644 --- a/lua/nvim-tree/actions/init.lua +++ b/lua/nvim-tree/actions/init.lua @@ -4,13 +4,11 @@ M.finders = require("nvim-tree.actions.finders") M.fs = require("nvim-tree.actions.fs") M.moves = require("nvim-tree.actions.moves") M.node = require("nvim-tree.actions.node") -M.root = require("nvim-tree.actions.root") M.tree = require("nvim-tree.actions.tree") function M.setup(opts) M.fs.setup(opts) M.node.setup(opts) - M.root.setup(opts) M.tree.setup(opts) end diff --git a/lua/nvim-tree/actions/root/change-dir.lua b/lua/nvim-tree/actions/root/change-dir.lua deleted file mode 100644 index 7a04939c62d..00000000000 --- a/lua/nvim-tree/actions/root/change-dir.lua +++ /dev/null @@ -1,13 +0,0 @@ -local log = require("nvim-tree.log") -local utils = require("nvim-tree.utils") -local core = require("nvim-tree.core") - -local M = { - current_tab = vim.api.nvim_get_current_tabpage(), -} - -function M.setup(options) - M.options = options.actions.change_dir -end - -return M diff --git a/lua/nvim-tree/actions/root/init.lua b/lua/nvim-tree/actions/root/init.lua deleted file mode 100644 index cb64420d6d4..00000000000 --- a/lua/nvim-tree/actions/root/init.lua +++ /dev/null @@ -1,9 +0,0 @@ -local M = {} - -M.change_dir = require("nvim-tree.actions.root.change-dir") - -function M.setup(opts) - M.change_dir.setup(opts) -end - -return M diff --git a/lua/nvim-tree/api.lua b/lua/nvim-tree/api.lua index cd2b33b953f..5d0885d979e 100644 --- a/lua/nvim-tree/api.lua +++ b/lua/nvim-tree/api.lua @@ -159,11 +159,11 @@ end) Api.tree.change_root_to_node = wrap_node(function(node) if node.name == ".." or node:is(RootNode) then - actions.root.change_dir.fn("..") + wrap_explorer("change_dir")("..") else node = node:as(DirectoryNode) if node then - actions.root.change_dir.fn(node:last_group_node().absolute_path) + wrap_explorer("change_dir")(node:last_group_node().absolute_path) end end end) @@ -274,7 +274,7 @@ local function open_or_expand_or_dir_up(mode, toggle_group) local dir = node:as(DirectoryNode) if root or node.name == ".." then - actions.root.change_dir.fn("..") + wrap_explorer("change_dir")("..") elseif dir then dir:expand_or_collapse(toggle_group) elseif not toggle_group then diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index b7c572005ca..1100d049d4f 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -22,7 +22,6 @@ local Clipboard = require("nvim-tree.actions.fs.clipboard") local Renderer = require("nvim-tree.renderer") local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON -local change_dir = require("nvim-tree.actions.root.change-dir") local find_file = require("nvim-tree.actions.finders.find-file") local config @@ -668,7 +667,7 @@ end ---@param node Node function Explorer:dir_up(node) if not node or node.name == ".." then - change_dir.fn("..") + self:change_dir("..") else local cwd = core.get_cwd() if cwd == nil then @@ -676,7 +675,7 @@ function Explorer:dir_up(node) end local newdir = vim.fn.fnamemodify(utils.path_remove_trailing(cwd), ":h") - change_dir.fn(newdir) + self:change_dir(newdir) find_file.fn(node.absolute_path) end end @@ -714,7 +713,7 @@ end ---@return boolean function Explorer:prevent_cwd_change(foldername) local is_same_cwd = foldername == core.get_cwd() - local is_restricted_above = M.options.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1) + local is_restricted_above = config.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1) return is_same_cwd or is_restricted_above end @@ -730,7 +729,7 @@ end ---@return boolean function Explorer:should_change_dir() - return M.options.enable and vim.tbl_isempty(vim.v.event) + return config.enable and vim.tbl_isempty(vim.v.event) end ---@param global boolean @@ -744,7 +743,7 @@ function Explorer:force_dirchange(_foldername, with_open) local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs if valid_dir then if self:should_change_dir() then - self:cd(M.options.global, foldername) + self:cd(config.global, foldername) end core.init(foldername) end diff --git a/lua/nvim-tree/lib.lua b/lua/nvim-tree/lib.lua index a464edf39ba..788af98d5bd 100644 --- a/lua/nvim-tree/lib.lua +++ b/lua/nvim-tree/lib.lua @@ -24,8 +24,9 @@ end ---@param cwd string local function handle_buf_cwd(cwd) - if M.respect_buf_cwd and cwd ~= core.get_cwd() then - require("nvim-tree.actions.root.change-dir").fn(cwd) + local explorer = core.get_explorer() + if M.respect_buf_cwd and cwd ~= core.get_cwd() and explorer then + explorer:change_dir(cwd) end end From 896e629056a12f9de04e87be9d8479cc36810fda Mon Sep 17 00:00:00 2001 From: uanela Date: Sat, 27 Dec 2025 13:33:33 +0200 Subject: [PATCH 06/17] refactor(explorer): changing _foldername to folder_name --- lua/nvim-tree/explorer/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 1100d049d4f..74abda7d0de 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -738,7 +738,7 @@ function Explorer:cd(global, path) vim.cmd((global and "cd " or "lcd ") .. vim.fn.fnameescape(path)) end -function Explorer:force_dirchange(_foldername, with_open) +function Explorer:force_dirchange(folder_name, with_open) local fn = self:add_profiling_to(function(foldername, should_open_view) local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs if valid_dir then @@ -758,7 +758,7 @@ function Explorer:force_dirchange(_foldername, with_open) end end) - fn(_foldername, with_open) + fn(folder_name, with_open) end ---@param input_cwd string From 5084dd43afe76203fb400eb1a77cf911dae72409 Mon Sep 17 00:00:00 2001 From: uanela Date: Sat, 27 Dec 2025 14:24:57 +0200 Subject: [PATCH 07/17] wip(explorer): rename expand method to expand_dir_node and all places using it --- lua/nvim-tree/actions/finders/find-file.lua | 2 +- lua/nvim-tree/actions/tree/modifiers/expand.lua | 4 ++-- lua/nvim-tree/explorer/init.lua | 2 +- lua/nvim-tree/node/directory.lua | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/nvim-tree/actions/finders/find-file.lua b/lua/nvim-tree/actions/finders/find-file.lua index 4f5d8089a85..fd66c646a29 100644 --- a/lua/nvim-tree/actions/finders/find-file.lua +++ b/lua/nvim-tree/actions/finders/find-file.lua @@ -66,7 +66,7 @@ function M.fn(path) dir.open = true end if #dir.nodes == 0 then - core.get_explorer():expand(dir) + core.get_explorer():expand_dir_node(dir) if dir.group_next and incremented_line then line = line - 1 end diff --git a/lua/nvim-tree/actions/tree/modifiers/expand.lua b/lua/nvim-tree/actions/tree/modifiers/expand.lua index 44e3fa67baa..e2ba684a2eb 100644 --- a/lua/nvim-tree/actions/tree/modifiers/expand.lua +++ b/lua/nvim-tree/actions/tree/modifiers/expand.lua @@ -23,7 +23,7 @@ local function expand(node) node = node:last_group_node() node.open = true if #node.nodes == 0 then - core.get_explorer():expand(node) + core.get_explorer():expand_dir_node(node) end end @@ -66,7 +66,7 @@ local function should_expand(expansion_count, node, should_descend) if not dir.open and should_descend(expansion_count, node) then if #node.nodes == 0 then - core.get_explorer():expand(dir) -- populate node.group_next + core.get_explorer():expand_dir_node(dir) -- populate node.group_next end if dir.group_next then diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 74abda7d0de..18d38223478 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -194,7 +194,7 @@ function Explorer:create_autocmds() end ---@param node DirectoryNode -function Explorer:expand(node) +function Explorer:expand_dir_node(node) self:_load(node) end diff --git a/lua/nvim-tree/node/directory.lua b/lua/nvim-tree/node/directory.lua index 0965e4ab9fc..da1599cbdc5 100644 --- a/lua/nvim-tree/node/directory.lua +++ b/lua/nvim-tree/node/directory.lua @@ -167,7 +167,7 @@ function DirectoryNode:expand_or_collapse(toggle_group) end if #self.nodes == 0 then - self.explorer:expand(self) + self.explorer:expand_dir_node(self) end local head_node = self:get_parent_of_group() or self From d5b8ead8882903da5084325d559c9516c2d9826e Mon Sep 17 00:00:00 2001 From: uanela Date: Sun, 28 Dec 2025 11:46:34 +0200 Subject: [PATCH 08/17] wip(explorer): mark private methods and remove add_profiling_to method --- lua/nvim-tree/explorer/init.lua | 65 ++++++++++++++------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 18d38223478..4a1ed911689 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -151,7 +151,7 @@ function Explorer:create_autocmds() pattern = "NvimTree_*", callback = function() if utils.is_nvim_tree_buf(0) then - if vim.fn.getcwd() ~= core.get_cwd() or (self.opts.reload_on_bufenter and not self.opts.filesystem_watchers.enable) then + if vim.fn.getcwd() ~= self.absolute_path or (self.opts.reload_on_bufenter and not self.opts.filesystem_watchers.enable) then self:reload_explorer() end end @@ -528,7 +528,7 @@ function Explorer:get_node_at_cursor() return end - if cursor[1] == 1 and view.is_root_folder_visible(core.get_cwd()) then + if cursor[1] == 1 and view.is_root_folder_visible(self.absolute_path) then return self end @@ -669,7 +669,7 @@ function Explorer:dir_up(node) if not node or node.name == ".." then self:change_dir("..") else - local cwd = core.get_cwd() + local cwd = self.absolute_path if cwd == nil then return end @@ -686,6 +686,7 @@ function Explorer:get_nodes() return self:clone() end +---@private ---@param new_tabpage integer ---@return boolean function Explorer:is_window_event(new_tabpage) @@ -693,11 +694,12 @@ function Explorer:is_window_event(new_tabpage) return is_event_scope_window and new_tabpage == self.current_tab end +---@private ---@param name string ---@return string|nil function Explorer:clean_input_cwd(name) name = vim.fn.fnameescape(name) - local cwd = core.get_cwd() + local cwd = self.absolute_path if cwd == nil then return end @@ -709,65 +711,54 @@ function Explorer:clean_input_cwd(name) end end +---@private ---@param foldername string ---@return boolean function Explorer:prevent_cwd_change(foldername) - local is_same_cwd = foldername == core.get_cwd() + local is_same_cwd = foldername == self.absolute_path local is_restricted_above = config.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1) return is_same_cwd or is_restricted_above end ----@param f function ----@return fun(foldername: string, should_open_view: boolean|nil) -function Explorer:add_profiling_to(f) - return function(foldername, should_open_view) - local profile = log.profile_start("change dir %s", foldername) - f(foldername, should_open_view) - log.profile_end(profile) - end -end - +---@private ---@return boolean function Explorer:should_change_dir() return config.enable and vim.tbl_isempty(vim.v.event) end +---@private ---@param global boolean ---@param path string function Explorer:cd(global, path) vim.cmd((global and "cd " or "lcd ") .. vim.fn.fnameescape(path)) end -function Explorer:force_dirchange(folder_name, with_open) - local fn = self:add_profiling_to(function(foldername, should_open_view) - local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs - if valid_dir then - if self:should_change_dir() then - self:cd(config.global, foldername) - end - core.init(foldername) +---@private +---@param foldername string +---@param should_open_view boolean|nil +function Explorer:force_dirchange(foldername, should_open_view) + local profile = log.profile_start("change dir %s", foldername) + + local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs + if valid_dir then + if self:should_change_dir() then + self:cd(config.global, foldername) end + core.init(foldername) + end - if should_open_view then - require("nvim-tree.lib").open() - else - local explorer = core.get_explorer() - if explorer then - explorer.renderer:draw() - end - end - end) + if should_open_view then + require("nvim-tree.lib").open() + else + self.renderer:draw() + end - fn(folder_name, with_open) + log.profile_end(profile) end ---@param input_cwd string ---@param with_open boolean|nil function Explorer:change_dir(input_cwd, with_open) - if not core.get_explorer() then - return - end - local new_tabpage = vim.api.nvim_get_current_tabpage() if self:is_window_event(new_tabpage) then return From 0d6eefd155b6f7d42e32a87e8cd4b56c14c0ea87 Mon Sep 17 00:00:00 2001 From: uanela Date: Sun, 28 Dec 2025 12:10:11 +0200 Subject: [PATCH 09/17] fix: allow changing root to file`s parent directory and fix restrict_above_cwd config path --- lua/nvim-tree/api.lua | 3 +++ lua/nvim-tree/explorer/init.lua | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/nvim-tree/api.lua b/lua/nvim-tree/api.lua index 5d0885d979e..e6bd9c53511 100644 --- a/lua/nvim-tree/api.lua +++ b/lua/nvim-tree/api.lua @@ -10,6 +10,7 @@ local notify = require("nvim-tree.notify") local DirectoryNode = require("nvim-tree.node.directory") local FileLinkNode = require("nvim-tree.node.file-link") +local FileNode = require("nvim-tree.node.file") local RootNode = require("nvim-tree.node.root") local UserDecorator = require("nvim-tree.renderer.decorator.user") @@ -160,6 +161,8 @@ end) Api.tree.change_root_to_node = wrap_node(function(node) if node.name == ".." or node:is(RootNode) then wrap_explorer("change_dir")("..") + elseif node:is(FileNode) and node.parent ~= nil then + wrap_explorer("change_dir")(node.parent:last_group_node().absolute_path) else node = node:as(DirectoryNode) if node then diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 4a1ed911689..60de65202f8 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -716,7 +716,7 @@ end ---@return boolean function Explorer:prevent_cwd_change(foldername) local is_same_cwd = foldername == self.absolute_path - local is_restricted_above = config.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1) + local is_restricted_above = config.actions.change_dir.restrict_above_cwd and foldername < vim.fn.getcwd(-1, -1) return is_same_cwd or is_restricted_above end From db36b04752e992d999f13d11b1bdc7da378097df Mon Sep 17 00:00:00 2001 From: uanela Date: Sun, 28 Dec 2025 12:23:51 +0200 Subject: [PATCH 10/17] refactor(api): moving change_root_to_node to Explorer:change_dir_to_node --- lua/nvim-tree/api.lua | 15 +-------------- lua/nvim-tree/explorer/init.lua | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lua/nvim-tree/api.lua b/lua/nvim-tree/api.lua index e6bd9c53511..10c34249147 100644 --- a/lua/nvim-tree/api.lua +++ b/lua/nvim-tree/api.lua @@ -10,7 +10,6 @@ local notify = require("nvim-tree.notify") local DirectoryNode = require("nvim-tree.node.directory") local FileLinkNode = require("nvim-tree.node.file-link") -local FileNode = require("nvim-tree.node.file") local RootNode = require("nvim-tree.node.root") local UserDecorator = require("nvim-tree.renderer.decorator.user") @@ -158,19 +157,7 @@ Api.tree.change_root = wrap(function(...) require("nvim-tree").change_dir(...) end) -Api.tree.change_root_to_node = wrap_node(function(node) - if node.name == ".." or node:is(RootNode) then - wrap_explorer("change_dir")("..") - elseif node:is(FileNode) and node.parent ~= nil then - wrap_explorer("change_dir")(node.parent:last_group_node().absolute_path) - else - node = node:as(DirectoryNode) - if node then - wrap_explorer("change_dir")(node:last_group_node().absolute_path) - end - end -end) - +Api.tree.change_root_to_node = wrap_node(wrap_explorer("change_dir_to_node")) Api.tree.change_root_to_parent = wrap_node(wrap_explorer("dir_up")) Api.tree.get_node_under_cursor = wrap_explorer("get_node_at_cursor") Api.tree.get_nodes = wrap_explorer("get_nodes") diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 60de65202f8..4859b6530ee 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -20,6 +20,7 @@ local LiveFilter = require("nvim-tree.explorer.live-filter") local Sorter = require("nvim-tree.explorer.sorter") local Clipboard = require("nvim-tree.actions.fs.clipboard") local Renderer = require("nvim-tree.renderer") +local FileNode = require("nvim-tree.node.file") local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON local find_file = require("nvim-tree.actions.finders.find-file") @@ -773,6 +774,19 @@ function Explorer:change_dir(input_cwd, with_open) self:force_dirchange(foldername, with_open) end +function Explorer:change_dir_to_node(node) + if node.name == ".." or node:is(RootNode) then + self:change_dir("..") + elseif node:is(FileNode) and node.parent ~= nil then + self:change_dir(node.parent:last_group_node().absolute_path) + else + node = node:as(DirectoryNode) + if node then + self:change_dir(node:last_group_node().absolute_path) + end + end +end + function Explorer:setup(opts) config = opts end From 195f9242d4b8d35935696aca98263f581f9ace9f Mon Sep 17 00:00:00 2001 From: uanela Date: Thu, 22 Jan 2026 11:44:10 +0200 Subject: [PATCH 11/17] fix(explorer): get new explorer before redrawing on --- lua/nvim-tree/explorer/init.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 4859b6530ee..b8ea5876145 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -751,7 +751,12 @@ function Explorer:force_dirchange(foldername, should_open_view) if should_open_view then require("nvim-tree.lib").open() else - self.renderer:draw() + -- TODO #2255 + -- The call to core.init destroyed this Explorer instance hence we need to fetch the new instance. + local explorer = core.get_explorer() + if explorer then + explorer.renderer:draw() + end end log.profile_end(profile) From fd300ed3243c520e6e189bd61d70bfd83cb7e7d7 Mon Sep 17 00:00:00 2001 From: uanela Date: Thu, 22 Jan 2026 11:49:44 +0200 Subject: [PATCH 12/17] docs(explorer): adding link to PR discussion for more context --- lua/nvim-tree/explorer/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index b8ea5876145..fc873c8e0c8 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -753,6 +753,7 @@ function Explorer:force_dirchange(foldername, should_open_view) else -- TODO #2255 -- The call to core.init destroyed this Explorer instance hence we need to fetch the new instance. + -- Problem is described at https://github.com/nvim-tree/nvim-tree.lua/pull/3233#issuecomment-3704402527 local explorer = core.get_explorer() if explorer then explorer.renderer:draw() From 4995699a7039d5c8d131ee935e75ec6f45006c56 Mon Sep 17 00:00:00 2001 From: uanela Date: Thu, 22 Jan 2026 12:21:13 +0200 Subject: [PATCH 13/17] fix(explorer): correctly check config.actions.change_dir for change_dir related methods --- lua/nvim-tree/explorer/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index fc873c8e0c8..21c3a07b92e 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -724,7 +724,7 @@ end ---@private ---@return boolean function Explorer:should_change_dir() - return config.enable and vim.tbl_isempty(vim.v.event) + return config.actions.change_dir.enable and vim.tbl_isempty(vim.v.event) end ---@private @@ -743,7 +743,7 @@ function Explorer:force_dirchange(foldername, should_open_view) local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs if valid_dir then if self:should_change_dir() then - self:cd(config.global, foldername) + self:cd(config.actions.change_dir.global, foldername) end core.init(foldername) end From 986c79a2e70f97146bc6c9d5af00dfb4e201c23e Mon Sep 17 00:00:00 2001 From: uanela Date: Thu, 22 Jan 2026 13:57:43 +0200 Subject: [PATCH 14/17] fix: correctly pass explorer as first arg when calling explorer methods in lua/nvim-tree --- lua/nvim-tree.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 3c0000ef854..4390884c27a 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -13,11 +13,12 @@ local M = { --- Helper function to execute some explorer method safely ---@param fn string # key of explorer +---@param ... any|nil ---@return function|nil -local function explorer_fn(fn) +local function explorer_fn(fn, ...) local explorer = core.get_explorer() if explorer then - return explorer[fn] + return explorer[fn](explorer, ...) end end @@ -57,7 +58,7 @@ function M.change_root(path, bufnr) -- test if in vim_cwd if utils.path_relative(path, vim_cwd) ~= path then if vim_cwd ~= cwd then - explorer_fn("change_dir")(vim_cwd) + explorer_fn("change_dir", vim_cwd) end return end @@ -68,19 +69,19 @@ function M.change_root(path, bufnr) -- otherwise test M.init_root if _config.prefer_startup_root and utils.path_relative(path, M.init_root) ~= path then - explorer_fn("change_dir")(M.init_root) + explorer_fn("change_dir", M.init_root) return end -- otherwise root_dirs for _, dir in pairs(_config.root_dirs) do dir = vim.fn.fnamemodify(dir, ":p") if utils.path_relative(path, dir) ~= path then - explorer_fn("change_dir")(dir) + explorer_fn("change_dir", dir) return end end -- finally fall back to the folder containing the file - explorer_fn("change_dir")(vim.fn.fnamemodify(path, ":p:h")) + explorer_fn("change_dir", vim.fn.fnamemodify, path, ":p:h") end function M.tab_enter() @@ -120,7 +121,7 @@ function M.open_on_directory() return end - explorer_fn("force_dirchange")(bufname, true) + explorer_fn("force_dirchange", bufname, true) end ---@return table @@ -144,7 +145,7 @@ end ---@param name string|nil function M.change_dir(name) if name then - explorer_fn("change_dir")(name) + explorer_fn("change_dir", name) end if _config.update_focused_file.update_root.enable then From e4f7cc5077f517417debd1386d3493d2a6d98bf8 Mon Sep 17 00:00:00 2001 From: uanela Date: Thu, 22 Jan 2026 14:09:54 +0200 Subject: [PATCH 15/17] fix: correctly pass args to change_dir --- lua/nvim-tree.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 4390884c27a..f62eff1e811 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -81,7 +81,7 @@ function M.change_root(path, bufnr) end end -- finally fall back to the folder containing the file - explorer_fn("change_dir", vim.fn.fnamemodify, path, ":p:h") + explorer_fn("change_dir", vim.fn.fnamemodify(path, ":p:h")) end function M.tab_enter() From 0933b4649874a6f4b78919753e511fa064f1710b Mon Sep 17 00:00:00 2001 From: uanela Date: Fri, 23 Jan 2026 05:31:54 +0200 Subject: [PATCH 16/17] fix: check for explorer before force_dirchange in open_on_directory --- lua/nvim-tree.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index f62eff1e811..08969fdcc3a 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -121,6 +121,12 @@ function M.open_on_directory() return end + + local explorer = core.get_explorer() + if not explorer then + core.init(bufname) + end + explorer_fn("force_dirchange", bufname, true) end From b97299e94d5cf303c7af746c77cd59119a7ce3f8 Mon Sep 17 00:00:00 2001 From: uanela Date: Fri, 23 Jan 2026 10:05:28 +0200 Subject: [PATCH 17/17] fix: check if force_dirchange should call core.init --- lua/nvim-tree.lua | 2 +- lua/nvim-tree/explorer/init.lua | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index 08969fdcc3a..783e77ae948 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -127,7 +127,7 @@ function M.open_on_directory() core.init(bufname) end - explorer_fn("force_dirchange", bufname, true) + explorer_fn("force_dirchange", bufname, true, false) end ---@return table diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 21c3a07b92e..cee3b4f0ca4 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -737,7 +737,8 @@ end ---@private ---@param foldername string ---@param should_open_view boolean|nil -function Explorer:force_dirchange(foldername, should_open_view) +---@param should_init boolean|nil +function Explorer:force_dirchange(foldername, should_open_view, should_init) local profile = log.profile_start("change dir %s", foldername) local valid_dir = vim.fn.isdirectory(foldername) == 1 -- prevent problems on non existing dirs @@ -745,7 +746,10 @@ function Explorer:force_dirchange(foldername, should_open_view) if self:should_change_dir() then self:cd(config.actions.change_dir.global, foldername) end - core.init(foldername) + + if should_init ~= false then + core.init(foldername) + end end if should_open_view then