1
+ local builders = require " nvim-tree.explorer.node-builders"
1
2
local git = require " nvim-tree.git"
3
+ local log = require " nvim-tree.log"
2
4
local notify = require " nvim-tree.notify"
5
+ local utils = require " nvim-tree.utils"
3
6
local watch = require " nvim-tree.explorer.watch"
4
7
local explorer_node = require " nvim-tree.explorer.node"
5
8
9
+ local NodeIterator = require " nvim-tree.iterators.node-iterator"
10
+ local Watcher = require " nvim-tree.watcher"
11
+
6
12
local Filters = require " nvim-tree.explorer.filters"
7
13
local Marks = {} -- circular dependencies
8
14
local LiveFilter = require " nvim-tree.explorer.live-filter"
9
15
local Sorters = require " nvim-tree.explorer.sorters"
10
16
local Clipboard = {} -- circular dependencies
11
17
18
+ local FILTER_REASON = require (" nvim-tree.enum" ).FILTER_REASON
19
+
12
20
local config
13
21
14
22
--- @class Explorer
@@ -23,7 +31,6 @@ local config
23
31
local Explorer = {}
24
32
25
33
Explorer .explore = require (" nvim-tree.explorer.explore" ).explore
26
- Explorer .reload = require (" nvim-tree.explorer.reload" ).reload
27
34
28
35
--- @param path string | nil
29
36
--- @return Explorer | nil
@@ -61,14 +68,6 @@ function Explorer:new(path)
61
68
return o
62
69
end
63
70
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
-
72
71
--- @param node Node
73
72
function Explorer :expand (node )
74
73
self :_load (node )
@@ -86,15 +85,253 @@ function Explorer:destroy()
86
85
iterate (self )
87
86
end
88
87
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
+
89
267
function Explorer .setup (opts )
90
268
config = opts
91
269
require (" nvim-tree.explorer.node" ).setup (opts )
92
270
require (" nvim-tree.explorer.explore" ).setup (opts )
93
- require (" nvim-tree.explorer.reload" ).setup (opts )
94
271
require (" nvim-tree.explorer.watch" ).setup (opts )
95
272
96
273
Marks = require " nvim-tree.marks"
97
274
Clipboard = require " nvim-tree.actions.fs.clipboard"
98
275
end
99
276
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
+
100
337
return Explorer
0 commit comments