refactor(#2882, #2883): multi instance explore, reloaders (#2897)

* refactor(#2883): multi instance explore

* refactor(#2882): multi instance reloaders

* style
This commit is contained in:
Alexander Courtis
2024-09-14 15:35:31 +10:00
committed by GitHub
parent 03f737e574
commit cd9c6db77f
12 changed files with 247 additions and 115 deletions

View File

@@ -201,7 +201,10 @@ local function setup_autocommands(opts)
create_nvim_tree_autocmd("BufWritePost", { create_nvim_tree_autocmd("BufWritePost", {
callback = function() callback = function()
if opts.auto_reload_on_write and not opts.filesystem_watchers.enable then if opts.auto_reload_on_write and not opts.filesystem_watchers.enable then
actions.reloaders.reload_explorer() local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end end
end, end,
}) })
@@ -217,7 +220,7 @@ local function setup_autocommands(opts)
(explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" (explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function() utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
actions.reloaders.reload_explorer() explorer:reload_explorer()
end) end)
end end
end, end,
@@ -234,7 +237,7 @@ local function setup_autocommands(opts)
(explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" (explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function() utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
actions.reloaders.reload_explorer() explorer:reload_explorer()
end) end)
end end
end, end,
@@ -244,7 +247,10 @@ local function setup_autocommands(opts)
pattern = { "FugitiveChanged", "NeogitStatusRefreshed" }, pattern = { "FugitiveChanged", "NeogitStatusRefreshed" },
callback = function() callback = function()
if not opts.filesystem_watchers.enable and opts.git.enable then if not opts.filesystem_watchers.enable and opts.git.enable then
actions.reloaders.reload_git() local explorer = core.get_explorer()
if explorer then
explorer:reload_git()
end
end end
end, end,
}) })
@@ -292,7 +298,10 @@ local function setup_autocommands(opts)
callback = function() callback = function()
if utils.is_nvim_tree_buf(0) then if utils.is_nvim_tree_buf(0) then
if vim.fn.getcwd() ~= core.get_cwd() or (opts.reload_on_bufenter and not opts.filesystem_watchers.enable) then if vim.fn.getcwd() ~= core.get_cwd() or (opts.reload_on_bufenter and not opts.filesystem_watchers.enable) then
actions.reloaders.reload_explorer() local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end end
end end
end, end,
@@ -343,7 +352,10 @@ local function setup_autocommands(opts)
callback = function() callback = function()
utils.debounce("Buf:modified", opts.view.debounce_delay, function() utils.debounce("Buf:modified", opts.view.debounce_delay, function()
buffers.reload_modified() buffers.reload_modified()
actions.reloaders.reload_explorer() local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end) end)
end, end,
}) })

View File

@@ -5,7 +5,6 @@ local core = require "nvim-tree.core"
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
local renderer = require "nvim-tree.renderer" local renderer = require "nvim-tree.renderer"
local reloaders = require "nvim-tree.actions.reloaders"
local find_file = require("nvim-tree.actions.finders.find-file").fn local find_file = require("nvim-tree.actions.finders.find-file").fn
@@ -248,7 +247,7 @@ function Clipboard:do_paste(node, action, action_fn)
self.data[action] = {} self.data[action] = {}
if not self.config.filesystem_watchers.enable then if not self.config.filesystem_watchers.enable then
reloaders.reload_explorer() self.explorer:reload_explorer()
end end
end end

View File

@@ -1,3 +1,4 @@
local core = require "nvim-tree.core"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
local view = require "nvim-tree.view" local view = require "nvim-tree.view"
@@ -116,8 +117,9 @@ function M.fn(node)
local function do_remove() local function do_remove()
M.remove(node) M.remove(node)
if not M.config.filesystem_watchers.enable then local explorer = core.get_explorer()
require("nvim-tree.actions.reloaders").reload_explorer() if not M.config.filesystem_watchers.enable and explorer then
explorer:reload_explorer()
end end
end end

View File

@@ -1,3 +1,4 @@
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local events = require "nvim-tree.events" local events = require "nvim-tree.events"
@@ -155,7 +156,10 @@ function M.fn(default_modifier)
M.rename(node, prepend .. new_file_path .. append) M.rename(node, prepend .. new_file_path .. append)
if not M.config.filesystem_watchers.enable then if not M.config.filesystem_watchers.enable then
require("nvim-tree.actions.reloaders").reload_explorer() local explorer = core.get_explorer()
if explorer then
explorer:reload_explorer()
end
end end
find_file(utils.path_remove_trailing(new_file_path)) find_file(utils.path_remove_trailing(new_file_path))

View File

@@ -1,6 +1,6 @@
local core = require "nvim-tree.core"
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
local reloaders = require "nvim-tree.actions.reloaders"
local M = { local M = {
config = {}, config = {},
@@ -52,6 +52,8 @@ function M.remove(node)
end end
end end
local explorer = core.get_explorer()
if node.nodes ~= nil and not node.link_to then if node.nodes ~= nil and not node.link_to then
trash_path(function(_, rc) trash_path(function(_, rc)
if rc ~= 0 then if rc ~= 0 then
@@ -59,8 +61,8 @@ function M.remove(node)
return return
end end
events._dispatch_folder_removed(node.absolute_path) events._dispatch_folder_removed(node.absolute_path)
if not M.config.filesystem_watchers.enable then if not M.config.filesystem_watchers.enable and explorer then
reloaders.reload_explorer() explorer:reload_explorer()
end end
end) end)
else else
@@ -72,8 +74,8 @@ function M.remove(node)
end end
events._dispatch_file_removed(node.absolute_path) events._dispatch_file_removed(node.absolute_path)
clear_buffer(node.absolute_path) clear_buffer(node.absolute_path)
if not M.config.filesystem_watchers.enable then if not M.config.filesystem_watchers.enable and explorer then
reloaders.reload_explorer() explorer:reload_explorer()
end end
end) end)
end end

View File

@@ -4,7 +4,6 @@ M.finders = require "nvim-tree.actions.finders"
M.fs = require "nvim-tree.actions.fs" M.fs = require "nvim-tree.actions.fs"
M.moves = require "nvim-tree.actions.moves" M.moves = require "nvim-tree.actions.moves"
M.node = require "nvim-tree.actions.node" M.node = require "nvim-tree.actions.node"
M.reloaders = require "nvim-tree.actions.reloaders"
M.root = require "nvim-tree.actions.root" M.root = require "nvim-tree.actions.root"
M.tree = require "nvim-tree.actions.tree" M.tree = require "nvim-tree.actions.tree"

View File

@@ -1,72 +0,0 @@
local git = require "nvim-tree.git"
local view = require "nvim-tree.view"
local renderer = require "nvim-tree.renderer"
local core = require "nvim-tree.core"
local explorer_node = require "nvim-tree.explorer.node"
local Iterator = require "nvim-tree.iterators.node-iterator"
local M = {}
---@param explorer Explorer|nil
---@param projects table
local function refresh_nodes(explorer, projects)
Iterator.builder({ explorer })
:applier(function(n)
if n.nodes then
local toplevel = git.get_toplevel(n.cwd or n.link_to or n.absolute_path)
if explorer then
explorer:reload(n, projects[toplevel] or {})
end
end
end)
:recursor(function(n)
return n.group_next and { n.group_next } or (n.open and n.nodes)
end)
:iterate()
end
---@param parent_node Node|nil
---@param projects table
function M.reload_node_status(parent_node, projects)
if parent_node == nil then
return
end
local toplevel = git.get_toplevel(parent_node.absolute_path)
local status = projects[toplevel] or {}
for _, node in ipairs(parent_node.nodes) do
explorer_node.update_git_status(node, explorer_node.is_git_ignored(parent_node), status)
if node.nodes and #node.nodes > 0 then
M.reload_node_status(node, projects)
end
end
end
local event_running = false
function M.reload_explorer()
if event_running or not core.get_explorer() or vim.v.exiting ~= vim.NIL then
return
end
event_running = true
local projects = git.reload()
refresh_nodes(core.get_explorer(), projects)
if view.is_visible() then
renderer.draw()
end
event_running = false
end
function M.reload_git()
if not core.get_explorer() or not git.config.git.enable or event_running then
return
end
event_running = true
local projects = git.reload()
M.reload_node_status(core.get_explorer(), projects)
renderer.draw()
event_running = false
end
return M

View File

@@ -1,12 +1,12 @@
local lib = require "nvim-tree.lib" local lib = require "nvim-tree.lib"
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local reloaders = require "nvim-tree.actions.reloaders"
local core = require "nvim-tree.core" local core = require "nvim-tree.core"
local M = {} local M = {}
local function reload() ---@param explorer Explorer
local function reload(explorer)
local node = lib.get_node_at_cursor() local node = lib.get_node_at_cursor()
reloaders.reload_explorer() explorer:reload_explorer()
utils.focus_node_or_parent(node) utils.focus_node_or_parent(node)
end end
@@ -19,39 +19,46 @@ local function wrap_explorer(fn)
end end
end end
---@param explorer Explorer
local function custom(explorer) local function custom(explorer)
explorer.filters.config.filter_custom = not explorer.filters.config.filter_custom explorer.filters.config.filter_custom = not explorer.filters.config.filter_custom
reload() reload(explorer)
end end
---@param explorer Explorer
local function git_ignored(explorer) local function git_ignored(explorer)
explorer.filters.config.filter_git_ignored = not explorer.filters.config.filter_git_ignored explorer.filters.config.filter_git_ignored = not explorer.filters.config.filter_git_ignored
reload() reload(explorer)
end end
---@param explorer Explorer
local function git_clean(explorer) local function git_clean(explorer)
explorer.filters.config.filter_git_clean = not explorer.filters.config.filter_git_clean explorer.filters.config.filter_git_clean = not explorer.filters.config.filter_git_clean
reload() reload(explorer)
end end
---@param explorer Explorer
local function no_buffer(explorer) local function no_buffer(explorer)
explorer.filters.config.filter_no_buffer = not explorer.filters.config.filter_no_buffer explorer.filters.config.filter_no_buffer = not explorer.filters.config.filter_no_buffer
reload() reload(explorer)
end end
---@param explorer Explorer
local function no_bookmark(explorer) local function no_bookmark(explorer)
explorer.filters.config.filter_no_bookmark = not explorer.filters.config.filter_no_bookmark explorer.filters.config.filter_no_bookmark = not explorer.filters.config.filter_no_bookmark
reload() reload(explorer)
end end
---@param explorer Explorer
local function dotfiles(explorer) local function dotfiles(explorer)
explorer.filters.config.filter_dotfiles = not explorer.filters.config.filter_dotfiles explorer.filters.config.filter_dotfiles = not explorer.filters.config.filter_dotfiles
reload() reload(explorer)
end end
---@param explorer Explorer
local function enable(explorer) local function enable(explorer)
explorer.filters.config.enable = not explorer.filters.config.enable explorer.filters.config.enable = not explorer.filters.config.enable
reload() reload(explorer)
end end
M.custom = wrap_explorer(custom) M.custom = wrap_explorer(custom)

View File

@@ -72,6 +72,19 @@ local function wrap_node_or_nil(fn)
end end
end end
---Invoke a method on the singleton explorer.
---Print error when setup not called.
---@param explorer_method string explorer method name
---@return fun(...) : any
local function wrap_explorer(explorer_method)
return wrap(function(...)
local explorer = core.get_explorer()
if explorer then
return explorer[explorer_method](explorer, ...)
end
end)
end
---Invoke a member's method on the singleton explorer. ---Invoke a member's method on the singleton explorer.
---Print error when setup not called. ---Print error when setup not called.
---@param explorer_member string explorer member name ---@param explorer_member string explorer member name
@@ -108,7 +121,7 @@ Api.tree.toggle = wrap(actions.tree.toggle.fn)
Api.tree.close = wrap(view.close) Api.tree.close = wrap(view.close)
Api.tree.close_in_this_tab = wrap(view.close_this_tab_only) Api.tree.close_in_this_tab = wrap(view.close_this_tab_only)
Api.tree.close_in_all_tabs = wrap(view.close_all_tabs) Api.tree.close_in_all_tabs = wrap(view.close_all_tabs)
Api.tree.reload = wrap(actions.reloaders.reload_explorer) Api.tree.reload = wrap_explorer "reload_explorer"
---@class ApiTreeResizeOpts ---@class ApiTreeResizeOpts
---@field width string|function|number|table|nil ---@field width string|function|number|table|nil
@@ -243,7 +256,7 @@ Api.node.navigate.diagnostics.prev_recursive = wrap_node(actions.moves.item.fn {
Api.node.navigate.opened.next = wrap_node(actions.moves.item.fn { where = "next", what = "opened" }) Api.node.navigate.opened.next = wrap_node(actions.moves.item.fn { where = "next", what = "opened" })
Api.node.navigate.opened.prev = wrap_node(actions.moves.item.fn { where = "prev", what = "opened" }) Api.node.navigate.opened.prev = wrap_node(actions.moves.item.fn { where = "prev", what = "opened" })
Api.git.reload = wrap(actions.reloaders.reload_git) Api.git.reload = wrap_explorer "reload_git"
Api.events.subscribe = events.subscribe Api.events.subscribe = events.subscribe
Api.events.Event = events.Event Api.events.Event = events.Event

View File

@@ -2,10 +2,13 @@ local builders = require "nvim-tree.explorer.node-builders"
local git = require "nvim-tree.git" local git = require "nvim-tree.git"
local log = require "nvim-tree.log" local log = require "nvim-tree.log"
local notify = require "nvim-tree.notify" local notify = require "nvim-tree.notify"
local renderer = {} -- circular dependency, will become a member
local utils = require "nvim-tree.utils" local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local watch = require "nvim-tree.explorer.watch" local watch = require "nvim-tree.explorer.watch"
local explorer_node = require "nvim-tree.explorer.node" local explorer_node = require "nvim-tree.explorer.node"
local Iterator = require "nvim-tree.iterators.node-iterator"
local NodeIterator = require "nvim-tree.iterators.node-iterator" local NodeIterator = require "nvim-tree.iterators.node-iterator"
local Watcher = require "nvim-tree.watcher" local Watcher = require "nvim-tree.watcher"
@@ -30,8 +33,6 @@ local config
---@field clipboard Clipboard ---@field clipboard Clipboard
local Explorer = {} local Explorer = {}
Explorer.explore = require("nvim-tree.explorer.explore").explore
---@param path string|nil ---@param path string|nil
---@return Explorer|nil ---@return Explorer|nil
function Explorer:new(path) function Explorer:new(path)
@@ -264,17 +265,7 @@ end
function Explorer:_load(node) function Explorer:_load(node)
local cwd = node.link_to or node.absolute_path local cwd = node.link_to or node.absolute_path
local git_status = git.load_project_status(cwd) local git_status = git.load_project_status(cwd)
Explorer.explore(node, git_status, self) self:explore(node, git_status, self)
end
function Explorer.setup(opts)
config = opts
require("nvim-tree.explorer.node").setup(opts)
require("nvim-tree.explorer.explore").setup(opts)
require("nvim-tree.explorer.watch").setup(opts)
Marks = require "nvim-tree.marks"
Clipboard = require "nvim-tree.actions.fs.clipboard"
end end
---@private ---@private
@@ -337,4 +328,158 @@ function Explorer:update_parent_statuses(node, project, root)
end end
end end
---@private
---@param handle uv.uv_fs_t
---@param cwd string
---@param node Node
---@param git_status table
---@param parent Explorer
function Explorer:populate_children(handle, cwd, node, git_status, parent)
local node_ignored = explorer_node.is_git_ignored(node)
local nodes_by_path = utils.bool_record(node.nodes, "absolute_path")
local filter_status = parent.filters:prepare(git_status)
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
bookmark = 0,
})
while true do
local name, t = vim.loop.fs_scandir_next(handle)
if not name then
break
end
local abs = utils.path_join { cwd, name }
if Watcher.is_fs_event_capable(abs) then
local profile = log.profile_start("populate_children %s", abs)
---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_stat(abs)
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then
local child = nil
if t == "directory" and vim.loop.fs_access(abs, "R") then
child = builders.folder(node, abs, name, stat)
elseif t == "file" then
child = builders.file(node, abs, name, stat)
elseif t == "link" then
local link = builders.link(node, abs, name, stat)
if link.link_to ~= nil then
child = link
end
end
if child then
table.insert(node.nodes, child)
nodes_by_path[child.absolute_path] = true
explorer_node.update_git_status(child, node_ignored, git_status)
end
else
for reason, value in pairs(FILTER_REASON) do
if filter_reason == value then
node.hidden_stats[reason] = node.hidden_stats[reason] + 1
end
end
end
log.profile_end(profile)
end
end
end
---@private
---@param node Node
---@param status table
---@param parent Explorer
---@return Node[]|nil
function Explorer:explore(node, status, parent)
local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd)
if not handle then
return
end
local profile = log.profile_start("explore %s", node.absolute_path)
self:populate_children(handle, cwd, node, status, parent)
local is_root = not node.parent
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
if config.renderer.group_empty and not is_root and child_folder_only then
local child_cwd = child_folder_only.link_to or child_folder_only.absolute_path
local child_status = git.load_project_status(child_cwd)
node.group_next = child_folder_only
local ns = self:explore(child_folder_only, child_status, parent)
node.nodes = ns or {}
log.profile_end(profile)
return ns
end
parent.sorters:sort(node.nodes)
parent.live_filter:apply_filter(node)
log.profile_end(profile)
return node.nodes
end
---@private
---@param projects table
function Explorer:refresh_nodes(projects)
Iterator.builder({ self })
:applier(function(n)
if n.nodes then
local toplevel = git.get_toplevel(n.cwd or n.link_to or n.absolute_path)
self:reload(n, projects[toplevel] or {})
end
end)
:recursor(function(n)
return n.group_next and { n.group_next } or (n.open and n.nodes)
end)
:iterate()
end
local event_running = false
function Explorer:reload_explorer()
if event_running or vim.v.exiting ~= vim.NIL then
return
end
event_running = true
local projects = git.reload()
self:refresh_nodes(projects)
if view.is_visible() then
renderer.draw()
end
event_running = false
end
function Explorer:reload_git()
if not git.config.git.enable or event_running then
return
end
event_running = true
local projects = git.reload()
explorer_node.reload_node_status(self, projects)
renderer.draw()
event_running = false
end
function Explorer.setup(opts)
config = opts
require("nvim-tree.explorer.node").setup(opts)
require("nvim-tree.explorer.watch").setup(opts)
renderer = require "nvim-tree.renderer"
Marks = require "nvim-tree.marks"
Clipboard = require "nvim-tree.actions.fs.clipboard"
end
return Explorer return Explorer

View File

@@ -1,3 +1,5 @@
local git = {} -- circular dependencies
local M = {} local M = {}
---@class GitStatus ---@class GitStatus
@@ -122,6 +124,23 @@ function M.get_git_status(node)
end end
end end
---@param parent_node Node|nil
---@param projects table
function M.reload_node_status(parent_node, projects)
if parent_node == nil then
return
end
local toplevel = git.get_toplevel(parent_node.absolute_path)
local status = projects[toplevel] or {}
for _, node in ipairs(parent_node.nodes) do
M.update_git_status(node, M.is_git_ignored(parent_node), status)
if node.nodes and #node.nodes > 0 then
M.reload_node_status(node, projects)
end
end
end
---@param node Node ---@param node Node
---@return boolean ---@return boolean
function M.is_git_ignored(node) function M.is_git_ignored(node)
@@ -157,6 +176,8 @@ function M.setup(opts)
M.config = { M.config = {
git = opts.git, git = opts.git,
} }
git = require "nvim-tree.git"
end end
return M return M

View File

@@ -38,7 +38,7 @@ end
function Marks:clear_reload() function Marks:clear_reload()
self:clear() self:clear()
if not self.config.filesystem_watchers.enable then if not self.config.filesystem_watchers.enable then
require("nvim-tree.actions.reloaders").reload_explorer() self.explorer:reload_explorer()
end end
end end