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/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/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 f23f3c12086..00000000000 --- a/lua/nvim-tree/actions/root/change-dir.lua +++ /dev/null @@ -1,105 +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(), -} - ----@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 - -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/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/api.lua b/lua/nvim-tree/api.lua index e5a109142f1..10c34249147 100644 --- a/lua/nvim-tree/api.lua +++ b/lua/nvim-tree/api.lua @@ -9,7 +9,6 @@ local keymap = require("nvim-tree.keymap") local notify = require("nvim-tree.notify") local DirectoryNode = require("nvim-tree.node.directory") -local FileNode = require("nvim-tree.node.file") local FileLinkNode = require("nvim-tree.node.file-link") local RootNode = require("nvim-tree.node.root") local UserDecorator = require("nvim-tree.renderer.decorator.user") @@ -158,23 +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 - actions.root.change_dir.fn("..") - return - end - - if node:is(FileNode) and node.parent ~= nil then - actions.root.change_dir.fn(node.parent:last_group_node().absolute_path) - return - end - - if node:is(DirectoryNode) then - actions.root.change_dir.fn(node:last_group_node().absolute_path) - return - 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") @@ -281,7 +264,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 013d2c2efa4..4859b6530ee 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -20,9 +20,9 @@ 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 change_dir = require("nvim-tree.actions.root.change-dir") local find_file = require("nvim-tree.actions.finders.find-file") local config @@ -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() @@ -148,7 +152,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 @@ -191,7 +195,7 @@ function Explorer:create_autocmds() end ---@param node DirectoryNode -function Explorer:expand(node) +function Explorer:expand_dir_node(node) self:_load(node) end @@ -525,7 +529,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 @@ -664,15 +668,15 @@ 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() + local cwd = self.absolute_path if cwd == nil then return 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 @@ -683,6 +687,106 @@ function Explorer:get_nodes() return self:clone() end +---@private +---@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 == self.current_tab +end + +---@private +---@param name string +---@return string|nil +function Explorer:clean_input_cwd(name) + name = vim.fn.fnameescape(name) + local cwd = self.absolute_path + 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 + +---@private +---@param foldername string +---@return boolean +function Explorer:prevent_cwd_change(foldername) + local is_same_cwd = foldername == self.absolute_path + 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 + +---@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 + +---@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 + self.renderer:draw() + end + + log.profile_end(profile) +end + +---@param input_cwd string +---@param with_open boolean|nil +function Explorer:change_dir(input_cwd, with_open) + 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 + 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 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 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