Skip to content

Commit 2278895

Browse files
committed
refactor(#2837): multi instance reload
1 parent c7bb2bc commit 2278895

File tree

5 files changed

+262
-272
lines changed

5 files changed

+262
-272
lines changed

lua/nvim-tree/actions/finders/find-file.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ local log = require "nvim-tree.log"
22
local view = require "nvim-tree.view"
33
local utils = require "nvim-tree.utils"
44
local renderer = require "nvim-tree.renderer"
5-
local reload = require "nvim-tree.explorer.reload"
65
local core = require "nvim-tree.core"
76
local Iterator = require "nvim-tree.iterators.node-iterator"
87

@@ -13,7 +12,8 @@ local running = {}
1312
---Find a path in the tree, expand it and focus it
1413
---@param path string relative or absolute
1514
function M.fn(path)
16-
if not core.get_explorer() or not view.is_visible() then
15+
local explorer = core.get_explorer()
16+
if not explorer or not view.is_visible() then
1717
return
1818
end
1919

@@ -32,7 +32,7 @@ function M.fn(path)
3232

3333
-- refresh the contents of all parents, expanding groups as needed
3434
if utils.get_node_from_path(path_real) == nil then
35-
reload.refresh_parent_nodes_for_path(vim.fn.fnamemodify(path_real, ":h"))
35+
explorer:refresh_parent_nodes_for_path(vim.fn.fnamemodify(path_real, ":h"))
3636
end
3737

3838
local line = core.get_nodes_starting_line()

lua/nvim-tree/actions/reloaders.lua

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
local git = require "nvim-tree.git"
22
local view = require "nvim-tree.view"
33
local renderer = require "nvim-tree.renderer"
4-
local explorer_module = require "nvim-tree.explorer"
54
local core = require "nvim-tree.core"
65
local explorer_node = require "nvim-tree.explorer.node"
76
local Iterator = require "nvim-tree.iterators.node-iterator"
87

98
local M = {}
109

11-
---@param node Explorer|nil
10+
---@param explorer Explorer|nil
1211
---@param projects table
13-
local function refresh_nodes(node, projects)
14-
Iterator.builder({ node })
12+
local function refresh_nodes(explorer, projects)
13+
Iterator.builder({ explorer })
1514
:applier(function(n)
1615
if n.nodes then
1716
local toplevel = git.get_toplevel(n.cwd or n.link_to or n.absolute_path)
18-
explorer_module.reload(n, projects[toplevel] or {})
17+
if explorer then
18+
explorer:reload(n, projects[toplevel] or {})
19+
end
1920
end
2021
end)
2122
:recursor(function(n)

lua/nvim-tree/explorer/init.lua

Lines changed: 247 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
1+
local builders = require "nvim-tree.explorer.node-builders"
12
local git = require "nvim-tree.git"
3+
local log = require "nvim-tree.log"
24
local notify = require "nvim-tree.notify"
5+
local utils = require "nvim-tree.utils"
36
local watch = require "nvim-tree.explorer.watch"
47
local explorer_node = require "nvim-tree.explorer.node"
58

9+
local NodeIterator = require "nvim-tree.iterators.node-iterator"
10+
local Watcher = require "nvim-tree.watcher"
11+
612
local Filters = require "nvim-tree.explorer.filters"
713
local Marks = {} -- circular dependencies
814
local LiveFilter = require "nvim-tree.explorer.live-filter"
915
local Sorters = require "nvim-tree.explorer.sorters"
1016
local Clipboard = {} -- circular dependencies
1117

18+
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
19+
1220
local config
1321

1422
---@class Explorer
@@ -23,7 +31,6 @@ local config
2331
local Explorer = {}
2432

2533
Explorer.explore = require("nvim-tree.explorer.explore").explore
26-
Explorer.reload = require("nvim-tree.explorer.reload").reload
2734

2835
---@param path string|nil
2936
---@return Explorer|nil
@@ -61,14 +68,6 @@ function Explorer:new(path)
6168
return o
6269
end
6370

64-
---@private
65-
---@param node Node
66-
function Explorer:_load(node)
67-
local cwd = node.link_to or node.absolute_path
68-
local git_status = git.load_project_status(cwd)
69-
Explorer.explore(node, git_status, self)
70-
end
71-
7271
---@param node Node
7372
function Explorer:expand(node)
7473
self:_load(node)
@@ -86,15 +85,253 @@ function Explorer:destroy()
8685
iterate(self)
8786
end
8887

88+
---@param node Node
89+
---@param git_status table|nil
90+
function Explorer:reload(node, git_status)
91+
local cwd = node.link_to or node.absolute_path
92+
local handle = vim.loop.fs_scandir(cwd)
93+
if not handle then
94+
return
95+
end
96+
97+
local profile = log.profile_start("reload %s", node.absolute_path)
98+
99+
local filter_status = self.filters:prepare(git_status)
100+
101+
if node.group_next then
102+
node.nodes = { node.group_next }
103+
node.group_next = nil
104+
end
105+
106+
local remain_childs = {}
107+
108+
local node_ignored = explorer_node.is_git_ignored(node)
109+
---@type table<string, Node>
110+
local nodes_by_path = utils.key_by(node.nodes, "absolute_path")
111+
112+
-- To reset we must 'zero' everything that we use
113+
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
114+
git = 0,
115+
buf = 0,
116+
dotfile = 0,
117+
custom = 0,
118+
bookmark = 0,
119+
})
120+
121+
while true do
122+
local name, t = vim.loop.fs_scandir_next(handle)
123+
if not name then
124+
break
125+
end
126+
127+
local abs = utils.path_join { cwd, name }
128+
---@type uv.fs_stat.result|nil
129+
local stat = vim.loop.fs_stat(abs)
130+
131+
local filter_reason = self.filters:should_filter_as_reason(abs, stat, filter_status)
132+
if filter_reason == FILTER_REASON.none then
133+
remain_childs[abs] = true
134+
135+
-- Recreate node if type changes.
136+
if nodes_by_path[abs] then
137+
local n = nodes_by_path[abs]
138+
139+
if n.type ~= t then
140+
utils.array_remove(node.nodes, n)
141+
explorer_node.node_destroy(n)
142+
nodes_by_path[abs] = nil
143+
end
144+
end
145+
146+
if not nodes_by_path[abs] then
147+
local new_child = nil
148+
if t == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
149+
new_child = builders.folder(node, abs, name, stat)
150+
elseif t == "file" then
151+
new_child = builders.file(node, abs, name, stat)
152+
elseif t == "link" then
153+
local link = builders.link(node, abs, name, stat)
154+
if link.link_to ~= nil then
155+
new_child = link
156+
end
157+
end
158+
if new_child then
159+
table.insert(node.nodes, new_child)
160+
nodes_by_path[abs] = new_child
161+
end
162+
else
163+
local n = nodes_by_path[abs]
164+
if n then
165+
n.executable = builders.is_executable(abs) or false
166+
n.fs_stat = stat
167+
end
168+
end
169+
else
170+
for reason, value in pairs(FILTER_REASON) do
171+
if filter_reason == value then
172+
node.hidden_stats[reason] = node.hidden_stats[reason] + 1
173+
end
174+
end
175+
end
176+
end
177+
178+
node.nodes = vim.tbl_map(
179+
self:update_status(nodes_by_path, node_ignored, git_status),
180+
vim.tbl_filter(function(n)
181+
if remain_childs[n.absolute_path] then
182+
return remain_childs[n.absolute_path]
183+
else
184+
explorer_node.node_destroy(n)
185+
return false
186+
end
187+
end, node.nodes)
188+
)
189+
190+
local is_root = not node.parent
191+
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
192+
if config.group_empty and not is_root and child_folder_only then
193+
node.group_next = child_folder_only
194+
local ns = self:reload(child_folder_only, git_status)
195+
node.nodes = ns or {}
196+
log.profile_end(profile)
197+
return ns
198+
end
199+
200+
self.sorters:sort(node.nodes)
201+
self.live_filter:apply_filter(node)
202+
log.profile_end(profile)
203+
return node.nodes
204+
end
205+
206+
---TODO #2837 #2871 move this and similar to node
207+
---Refresh contents and git status for a single node
208+
---@param node Node
209+
---@param callback function
210+
function Explorer:refresh_node(node, callback)
211+
if type(node) ~= "table" then
212+
callback()
213+
end
214+
215+
local parent_node = utils.get_parent_of_group(node)
216+
217+
self:reload_and_get_git_project(node.absolute_path, function(toplevel, project)
218+
self:reload(parent_node, project)
219+
220+
self:update_parent_statuses(parent_node, project, toplevel)
221+
222+
callback()
223+
end)
224+
end
225+
226+
---Refresh contents of all nodes to a path: actual directory and links.
227+
---Groups will be expanded if needed.
228+
---@param path string absolute path
229+
function Explorer:refresh_parent_nodes_for_path(path)
230+
local profile = log.profile_start("refresh_parent_nodes_for_path %s", path)
231+
232+
-- collect parent nodes from the top down
233+
local parent_nodes = {}
234+
NodeIterator.builder({ self })
235+
:recursor(function(node)
236+
return node.nodes
237+
end)
238+
:applier(function(node)
239+
local abs_contains = node.absolute_path and path:find(node.absolute_path, 1, true) == 1
240+
local link_contains = node.link_to and path:find(node.link_to, 1, true) == 1
241+
if abs_contains or link_contains then
242+
table.insert(parent_nodes, node)
243+
end
244+
end)
245+
:iterate()
246+
247+
-- refresh in order; this will expand groups as needed
248+
for _, node in ipairs(parent_nodes) do
249+
local toplevel = git.get_toplevel(node.absolute_path)
250+
local project = git.get_project(toplevel) or {}
251+
252+
self:reload(node, project)
253+
self:update_parent_statuses(node, project, toplevel)
254+
end
255+
256+
log.profile_end(profile)
257+
end
258+
259+
---@private
260+
---@param node Node
261+
function Explorer:_load(node)
262+
local cwd = node.link_to or node.absolute_path
263+
local git_status = git.load_project_status(cwd)
264+
Explorer.explore(node, git_status, self)
265+
end
266+
89267
function Explorer.setup(opts)
90268
config = opts
91269
require("nvim-tree.explorer.node").setup(opts)
92270
require("nvim-tree.explorer.explore").setup(opts)
93-
require("nvim-tree.explorer.reload").setup(opts)
94271
require("nvim-tree.explorer.watch").setup(opts)
95272

96273
Marks = require "nvim-tree.marks"
97274
Clipboard = require "nvim-tree.actions.fs.clipboard"
98275
end
99276

277+
---@private
278+
---@param nodes_by_path table
279+
---@param node_ignored boolean
280+
---@param status table|nil
281+
---@return fun(node: Node): table
282+
function Explorer:update_status(nodes_by_path, node_ignored, status)
283+
return function(node)
284+
if nodes_by_path[node.absolute_path] then
285+
explorer_node.update_git_status(node, node_ignored, status)
286+
end
287+
return node
288+
end
289+
end
290+
291+
---TODO #2837 #2871 move this and similar to node
292+
---@private
293+
---@param path string
294+
---@param callback fun(toplevel: string|nil, project: table|nil)
295+
function Explorer:reload_and_get_git_project(path, callback)
296+
local toplevel = git.get_toplevel(path)
297+
298+
git.reload_project(toplevel, path, function()
299+
callback(toplevel, git.get_project(toplevel) or {})
300+
end)
301+
end
302+
303+
---TODO #2837 #2871 move this and similar to node
304+
---@private
305+
---@param node Node
306+
---@param project table|nil
307+
---@param root string|nil
308+
function Explorer:update_parent_statuses(node, project, root)
309+
while project and node do
310+
-- step up to the containing project
311+
if node.absolute_path == root then
312+
-- stop at the top of the tree
313+
if not node.parent then
314+
break
315+
end
316+
317+
root = git.get_toplevel(node.parent.absolute_path)
318+
319+
-- stop when no more projects
320+
if not root then
321+
break
322+
end
323+
324+
-- update the containing project
325+
project = git.get_project(root)
326+
git.reload_project(root, node.absolute_path, nil)
327+
end
328+
329+
-- update status
330+
explorer_node.update_git_status(node, explorer_node.is_git_ignored(node.parent), project)
331+
332+
-- maybe parent
333+
node = node.parent
334+
end
335+
end
336+
100337
return Explorer

0 commit comments

Comments
 (0)