diff --git a/lua/nvim-tree/actions/finders/find-file.lua b/lua/nvim-tree/actions/finders/find-file.lua index 8fec0cb5ea6..725b77b037b 100644 --- a/lua/nvim-tree/actions/finders/find-file.lua +++ b/lua/nvim-tree/actions/finders/find-file.lua @@ -3,6 +3,7 @@ local view = require "nvim-tree.view" local utils = require "nvim-tree.utils" local renderer = require "nvim-tree.renderer" local core = require "nvim-tree.core" +local reload = require "nvim-tree.explorer.reload" local Iterator = require "nvim-tree.iterators.node-iterator" local M = {} @@ -12,18 +13,26 @@ local running = {} ---Find a path in the tree, expand it and focus it ---@param fname string full path function M.fn(fname) - if running[fname] or not core.get_explorer() then + if not core.get_explorer() then return end - running[fname] = true - local ps = log.profile_start("find file %s", fname) -- always match against the real path local fname_real = vim.loop.fs_realpath(fname) if not fname_real then return end + if running[fname_real] then + return + end + running[fname_real] = true + + local ps = log.profile_start("find file %s", fname_real) + + -- we cannot wait for watchers + reload.refresh_nodes_for_path(vim.fn.fnamemodify(fname_real, ":h")) + local line = core.get_nodes_starting_line() local absolute_paths_searched = {} @@ -60,9 +69,9 @@ function M.fn(fname) view.set_cursor { line, 0 } end - running[fname] = false + running[fname_real] = false - log.profile_end(ps, "find file %s", fname) + log.profile_end(ps, "find file %s", fname_real) end return M diff --git a/lua/nvim-tree/actions/fs/create-file.lua b/lua/nvim-tree/actions/fs/create-file.lua index f19e58041ef..35ec267d77d 100644 --- a/lua/nvim-tree/actions/fs/create-file.lua +++ b/lua/nvim-tree/actions/fs/create-file.lua @@ -2,9 +2,10 @@ local utils = require "nvim-tree.utils" local events = require "nvim-tree.events" local lib = require "nvim-tree.lib" local core = require "nvim-tree.core" -local watch = require "nvim-tree.explorer.watch" local notify = require "nvim-tree.notify" +local find_file = require("nvim-tree.actions.finders.find-file").fn + local M = {} local function create_and_notify(file) @@ -99,22 +100,15 @@ function M.fn(node) is_error = true break end + events._dispatch_folder_created(new_file_path) end end if not is_error then notify.info(new_file_path .. " was properly created") end - events._dispatch_folder_created(new_file_path) - if M.enable_reload then - require("nvim-tree.actions.reloaders.reloaders").reload_explorer() - else - -- synchronous call required so that we may focus the file now - node = node.nodes ~= nil and node or node.parent - if node then - watch.refresh_path(node.absolute_path) - end - end - utils.focus_file(utils.path_remove_trailing(new_file_path)) + + -- synchronously refreshes as we can't wait for the watchers + find_file(utils.path_remove_trailing(new_file_path)) end) end diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index a1559299e5c..8f34f9d7b38 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -15,9 +15,9 @@ function Explorer.new(cwd) local explorer = setmetatable({ absolute_path = cwd, nodes = {}, - watcher = watch.create_watcher(cwd), open = true, }, Explorer) + explorer.watcher = watch.create_watcher(explorer) explorer:_load(explorer) return explorer end diff --git a/lua/nvim-tree/explorer/node-builders.lua b/lua/nvim-tree/explorer/node-builders.lua index 1cc04f9a15e..86027cdcd81 100644 --- a/lua/nvim-tree/explorer/node-builders.lua +++ b/lua/nvim-tree/explorer/node-builders.lua @@ -10,7 +10,7 @@ function M.folder(parent, absolute_path, name) local handle = vim.loop.fs_scandir(absolute_path) local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil - return { + local node = { type = "directory", absolute_path = absolute_path, fs_stat = vim.loop.fs_stat(absolute_path), @@ -20,8 +20,11 @@ function M.folder(parent, absolute_path, name) nodes = {}, open = false, parent = parent, - watcher = watch.create_watcher(absolute_path), } + + node.watcher = watch.create_watcher(node) + + return node end function M.is_executable(parent, absolute_path, ext) @@ -63,16 +66,18 @@ end function M.link(parent, absolute_path, name) --- I dont know if this is needed, because in my understanding, there isn't hard links in windows, but just to be sure i changed it. local link_to = vim.loop.fs_realpath(absolute_path) - local open, nodes, has_children, watcher - if (link_to ~= nil) and vim.loop.fs_stat(link_to).type == "directory" then + local open, nodes, has_children + + local is_dir_link = (link_to ~= nil) and vim.loop.fs_stat(link_to).type == "directory" + + if is_dir_link then local handle = vim.loop.fs_scandir(link_to) has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil open = false nodes = {} - watcher = watch.create_watcher(link_to) end - return { + local node = { type = "link", absolute_path = absolute_path, fs_stat = vim.loop.fs_stat(absolute_path), @@ -83,8 +88,13 @@ function M.link(parent, absolute_path, name) nodes = nodes, open = open, parent = parent, - watcher = watcher, } + + if is_dir_link then + node.watcher = watch.create_watcher(node) + end + + return node end return M diff --git a/lua/nvim-tree/explorer/reload.lua b/lua/nvim-tree/explorer/reload.lua index a7317461540..315bf34b61c 100644 --- a/lua/nvim-tree/explorer/reload.lua +++ b/lua/nvim-tree/explorer/reload.lua @@ -5,6 +5,10 @@ local filters = require "nvim-tree.explorer.filters" local sorters = require "nvim-tree.explorer.sorters" local live_filter = require "nvim-tree.live-filter" local notify = require "nvim-tree.notify" +local git = require "nvim-tree.git" +local log = require "nvim-tree.log" + +local NodeIterator = require "nvim-tree.iterators.node-iterator" local M = {} @@ -17,6 +21,19 @@ local function update_status(nodes_by_path, node_ignored, status) end end +local function reload_and_get_git_project(path) + local project_root = git.get_project_root(path) + git.reload_project(project_root, path) + return project_root, git.get_project(project_root) or {} +end + +local function update_parent_statuses(node, project, root) + while project and node and node.absolute_path ~= root do + common.update_git_status(node, false, project) + node = node.parent + end +end + function M.reload(node, status) local cwd = node.link_to or node.absolute_path local handle = vim.loop.fs_scandir(cwd) @@ -25,6 +42,8 @@ function M.reload(node, status) return end + local ps = log.profile_start("reload %s", node.absolute_path) + if node.group_next then node.nodes = { node.group_next } node.group_next = nil @@ -110,14 +129,65 @@ function M.reload(node, status) node.group_next = child_folder_only local ns = M.reload(child_folder_only, status) node.nodes = ns or {} + log.profile_end(ps, "reload %s", node.absolute_path) return ns end sorters.merge_sort(node.nodes, sorters.node_comparator) live_filter.apply_filter(node) + log.profile_end(ps, "reload %s", node.absolute_path) return node.nodes end +---Refresh contents and git status for a single node +---@param node table +function M.refresh_node(node) + if type(node) ~= "table" then + return + end + + local parent_node = utils.get_parent_of_group(node) + + local project_root, project = reload_and_get_git_project(node.absolute_path) + + require("nvim-tree.explorer.reload").reload(parent_node, project) + + update_parent_statuses(parent_node, project, project_root) +end + +---Refresh contents and git status for all nodes to a path: actual directory and links +---@param path string absolute path +function M.refresh_nodes_for_path(path) + local explorer = require("nvim-tree.core").get_explorer() + if not explorer then + return + end + + local pn = string.format("refresh_nodes_for_path %s", path) + local ps = log.profile_start(pn) + + NodeIterator.builder({ explorer }) + :hidden() + :recursor(function(node) + if node.group_next then + return { node.group_next } + end + if node.nodes then + return node.nodes + end + end) + :applier(function(node) + local abs_contains = node.absolute_path and path:match("^" .. node.absolute_path) + local link_contains = node.link_to and path:match("^" .. node.link_to) + if abs_contains or link_contains then + M.refresh_node(node) + end + end) + :iterate() + + log.profile_end(ps, pn) +end + function M.setup(opts) M.config = opts.renderer end diff --git a/lua/nvim-tree/explorer/watch.lua b/lua/nvim-tree/explorer/watch.lua index d42a47d5cf2..a24b0871d5d 100644 --- a/lua/nvim-tree/explorer/watch.lua +++ b/lua/nvim-tree/explorer/watch.lua @@ -1,23 +1,9 @@ local log = require "nvim-tree.log" local utils = require "nvim-tree.utils" -local git = require "nvim-tree.git" local Watcher = require("nvim-tree.watcher").Watcher local M = {} -local function reload_and_get_git_project(path) - local project_root = git.get_project_root(path) - git.reload_project(project_root, path) - return project_root, git.get_project(project_root) or {} -end - -local function update_parent_statuses(node, project, root) - while project and node and node.absolute_path ~= root do - require("nvim-tree.explorer.common").update_git_status(node, false, project) - node = node.parent - end -end - local function is_git(path) return vim.fn.fnamemodify(path, ":t") == ".git" end @@ -46,39 +32,38 @@ local function is_folder_ignored(path) return false end -function M.refresh_path(path) - log.line("watcher", "node event executing '%s'", path) - local n = utils.get_node_from_path(path) - if not n then - return +function M.create_watcher(node) + if not M.enabled or type(node) ~= "table" then + return nil end - local node = utils.get_parent_of_group(n) - local project_root, project = reload_and_get_git_project(path) - require("nvim-tree.explorer.reload").reload(node, project) - update_parent_statuses(node, project, project_root) - - require("nvim-tree.renderer").draw() -end - -function M.create_watcher(absolute_path) - if not M.enabled then - return nil + local path + if node.type == "link" then + path = node.link_to + else + path = node.absolute_path end - if is_git(absolute_path) or is_folder_ignored(absolute_path) then + + if is_git(path) or is_folder_ignored(path) then return nil end local function callback(watcher) - log.line("watcher", "node event scheduled %s", watcher.context) + log.line("watcher", "node event scheduled refresh %s", watcher.context) utils.debounce(watcher.context, M.debounce_delay, function() - M.refresh_path(watcher._path) + if node.link_to then + log.line("watcher", "node event executing refresh '%s' -> '%s'", node.link_to, node.absolute_path) + else + log.line("watcher", "node event executing refresh '%s'", node.absolute_path) + end + require("nvim-tree.explorer.reload").refresh_node(node) + require("nvim-tree.renderer").draw() end) end M.uid = M.uid + 1 - return Watcher:new(absolute_path, nil, callback, { - context = "explorer:watch:" .. absolute_path .. ":" .. M.uid, + return Watcher:new(path, nil, callback, { + context = "explorer:watch:" .. path .. ":" .. M.uid, }) end