Skip to content

Commit ccf43b0

Browse files
committed
extract DirectoryLinkNode and FileLinkNode, move Node methods to children
1 parent 010ae03 commit ccf43b0

File tree

8 files changed

+311
-223
lines changed

8 files changed

+311
-223
lines changed

lua/nvim-tree/log.lua

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,25 @@ function M.raw(typ, fmt, ...)
2121
end
2222
end
2323

24+
--- Write to a new file
25+
---@param typ string as per log.types config
26+
---@param path string absolute path
27+
---@param fmt string for string.format
28+
---@param ... any arguments for string.format
29+
function M.file(typ, path, fmt, ...)
30+
if not M.enabled(typ) then
31+
return
32+
end
33+
34+
local line = string.format(fmt, ...)
35+
local file = io.open(path, "w")
36+
if file then
37+
io.output(file)
38+
io.write(line)
39+
io.close(file)
40+
end
41+
end
42+
2443
---@class Profile
2544
---@field start number nanos
2645
---@field tag string
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
local git = require("nvim-tree.git")
2+
3+
local DirectoryNode = require("nvim-tree.node.directory")
4+
5+
---@class (exact) DirectoryLinkNode: DirectoryNode
6+
---@field link_to string absolute path
7+
---@field fs_stat_target uv.fs_stat.result
8+
local DirectoryLinkNode = DirectoryNode:new()
9+
10+
---Static factory method
11+
---@param explorer Explorer
12+
---@param parent Node
13+
---@param absolute_path string
14+
---@param link_to string
15+
---@param name string
16+
---@param fs_stat uv.fs_stat.result?
17+
---@param fs_stat_target uv.fs_stat.result
18+
---@return DirectoryLinkNode? nil on vim.loop.fs_realpath failure
19+
function DirectoryLinkNode:create(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target)
20+
-- create DirectoryNode with the target path for the watcher
21+
local o = DirectoryNode:create(explorer, parent, link_to, name, fs_stat)
22+
23+
o = self:new(o) --[[@as DirectoryLinkNode]]
24+
25+
-- reset absolute path to the link itself
26+
o.absolute_path = absolute_path
27+
28+
o.type = "link"
29+
o.link_to = link_to
30+
o.fs_stat_target = fs_stat_target
31+
32+
return o
33+
end
34+
35+
-----Update the GitStatus of link target
36+
-----@param parent_ignored boolean
37+
-----@param status table|nil
38+
function DirectoryLinkNode:update_git_status(parent_ignored, status)
39+
self.git_status = git.git_status_dir(parent_ignored, status, self.link_to)
40+
end
41+
42+
---Create a sanitized partial copy of a node, populating children recursively.
43+
---@return DirectoryLinkNode cloned
44+
function DirectoryLinkNode:clone()
45+
local clone = DirectoryNode.clone(self) --[[@as DirectoryLinkNode]]
46+
47+
clone.type = self.type
48+
clone.link_to = self.link_to
49+
clone.fs_stat_target = self.fs_stat_target
50+
51+
return clone
52+
end
53+
54+
return DirectoryLinkNode

lua/nvim-tree/node/directory.lua

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local git = require("nvim-tree.git")
12
local watch = require("nvim-tree.explorer.watch")
23

34
local BaseNode = require("nvim-tree.node")
@@ -58,6 +59,118 @@ function DirectoryNode:destroy()
5859
end
5960
end
6061

62+
---@return boolean
63+
function DirectoryNode:has_one_child_folder()
64+
return #self.nodes == 1 and self.nodes[1].nodes and vim.loop.fs_access(self.nodes[1].absolute_path, "R") or false
65+
end
66+
67+
-- If node is grouped, return the last node in the group. Otherwise, return the given node.
68+
---@return Node
69+
function DirectoryNode:last_group_node()
70+
local node = self --[[@as BaseNode]]
71+
72+
while node.group_next do
73+
node = node.group_next
74+
end
75+
76+
return node
77+
end
78+
79+
---Group empty folders
80+
-- Recursively group nodes
81+
---@return Node[]
82+
function DirectoryNode:group_empty_folders()
83+
local is_root = not self.parent
84+
local child_folder_only = self:has_one_child_folder() and self.nodes[1]
85+
if self.explorer.opts.renderer.group_empty and not is_root and child_folder_only then
86+
self.group_next = child_folder_only
87+
local ns = child_folder_only:group_empty_folders()
88+
self.nodes = ns or {}
89+
return ns
90+
end
91+
return self.nodes
92+
end
93+
94+
---Ungroup empty folders
95+
-- If a node is grouped, ungroup it: put node.group_next to the node.nodes and set node.group_next to nil
96+
function DirectoryNode:ungroup_empty_folders()
97+
local cur = self
98+
while cur and cur.group_next do
99+
cur.nodes = { cur.group_next }
100+
cur.group_next = nil
101+
cur = cur.nodes[1]
102+
end
103+
end
104+
105+
---Update the GitStatus of absolute path of the directory
106+
---@param parent_ignored boolean
107+
---@param status table|nil
108+
function DirectoryNode:update_git_status(parent_ignored, status)
109+
self.git_status = git.git_status_dir(parent_ignored, status, self.absolute_path)
110+
end
111+
112+
---@return GitStatus|nil
113+
function BaseNode:get_git_status()
114+
if not self.git_status or not self.explorer.opts.git.show_on_dirs then
115+
return nil
116+
end
117+
118+
local status = {}
119+
if not self:last_group_node().open or self.explorer.opts.git.show_on_open_dirs then
120+
-- dir is closed or we should show on open_dirs
121+
if self.git_status.file ~= nil then
122+
table.insert(status, self.git_status.file)
123+
end
124+
if self.git_status.dir ~= nil then
125+
if self.git_status.dir.direct ~= nil then
126+
for _, s in pairs(self.git_status.dir.direct) do
127+
table.insert(status, s)
128+
end
129+
end
130+
if self.git_status.dir.indirect ~= nil then
131+
for _, s in pairs(self.git_status.dir.indirect) do
132+
table.insert(status, s)
133+
end
134+
end
135+
end
136+
else
137+
-- dir is open and we shouldn't show on open_dirs
138+
if self.git_status.file ~= nil then
139+
table.insert(status, self.git_status.file)
140+
end
141+
if self.git_status.dir ~= nil and self.git_status.dir.direct ~= nil then
142+
local deleted = {
143+
[" D"] = true,
144+
["D "] = true,
145+
["RD"] = true,
146+
["DD"] = true,
147+
}
148+
for _, s in pairs(self.git_status.dir.direct) do
149+
if deleted[s] then
150+
table.insert(status, s)
151+
end
152+
end
153+
end
154+
end
155+
if #status == 0 then
156+
return nil
157+
else
158+
return status
159+
end
160+
end
161+
162+
---@param projects table
163+
function DirectoryNode:reload_node_status(projects)
164+
local toplevel = git.get_toplevel(self.absolute_path)
165+
local status = projects[toplevel] or {}
166+
for _, node in ipairs(self.nodes) do
167+
node:update_git_status(self:is_git_ignored(), status)
168+
if node.nodes and #node.nodes > 0 then
169+
self:reload_node_status(projects)
170+
end
171+
end
172+
end
173+
61174
---Create a sanitized partial copy of a node, populating children recursively.
62175
---@return DirectoryNode cloned
63176
function DirectoryNode:clone()

lua/nvim-tree/node/factory.lua

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
local DirectoryLinkNode = require("nvim-tree.node.directory-link")
12
local DirectoryNode = require("nvim-tree.node.directory")
2-
local LinkNode = require("nvim-tree.node.link")
3+
local FileLinkNode = require("nvim-tree.node.file-link")
34
local FileNode = require("nvim-tree.node.file")
45
local Watcher = require("nvim-tree.watcher")
56

@@ -8,21 +9,37 @@ local M = {}
89
---Factory function to create the appropriate Node
910
---@param explorer Explorer
1011
---@param parent Node
11-
---@param abs string
12+
---@param absolute_path string
1213
---@param stat uv.fs_stat.result? -- on nil stat return nil Node
1314
---@param name string
1415
---@return Node?
15-
function M.create_node(explorer, parent, abs, stat, name)
16+
function M.create_node(explorer, parent, absolute_path, stat, name)
1617
if not stat then
1718
return nil
1819
end
1920

20-
if stat.type == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
21-
return DirectoryNode:create(explorer, parent, abs, name, stat)
21+
if stat.type == "directory" then
22+
-- directory must be readable and enumerable
23+
if vim.loop.fs_access(absolute_path, "R") and Watcher.is_fs_event_capable(absolute_path) then
24+
return DirectoryNode:create(explorer, parent, absolute_path, name, stat)
25+
end
2226
elseif stat.type == "file" then
23-
return FileNode:create(explorer, parent, abs, name, stat)
27+
-- any file
28+
return FileNode:create(explorer, parent, absolute_path, name, stat)
2429
elseif stat.type == "link" then
25-
return LinkNode:create(explorer, parent, abs, name, stat)
30+
-- link target path and stat must resolve
31+
local link_to = vim.loop.fs_realpath(absolute_path)
32+
local link_to_stat = link_to and vim.loop.fs_stat(link_to)
33+
if not link_to or not link_to_stat then
34+
return
35+
end
36+
37+
-- choose directory or file
38+
if link_to_stat.type == "directory" then
39+
return DirectoryLinkNode:create(explorer, parent, absolute_path, link_to, name, stat, link_to_stat)
40+
else
41+
return FileLinkNode:create(explorer, parent, absolute_path, link_to, name, stat, link_to_stat)
42+
end
2643
end
2744

2845
return nil

lua/nvim-tree/node/file-link.lua

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
local git = require("nvim-tree.git")
2+
3+
local FileNode = require("nvim-tree.node.file")
4+
5+
---@class (exact) FileLinkNode: FileNode
6+
---@field link_to string absolute path
7+
---@field fs_stat_target uv.fs_stat.result
8+
local FileLinkNode = FileNode:new()
9+
10+
---Static factory method
11+
---@param explorer Explorer
12+
---@param parent Node
13+
---@param absolute_path string
14+
---@param link_to string
15+
---@param name string
16+
---@param fs_stat uv.fs_stat.result?
17+
---@param fs_stat_target uv.fs_stat.result
18+
---@return FileLinkNode? nil on vim.loop.fs_realpath failure
19+
function FileLinkNode:create(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target)
20+
local o = FileNode:create(explorer, parent, absolute_path, name, fs_stat)
21+
22+
o = self:new(o) --[[@as FileLinkNode]]
23+
24+
o.type = "link"
25+
o.link_to = link_to
26+
o.fs_stat_target = fs_stat_target
27+
28+
return o
29+
end
30+
31+
-----Update the GitStatus of link target
32+
-----@param parent_ignored boolean
33+
-----@param status table|nil
34+
function FileLinkNode:update_git_status(parent_ignored, status)
35+
self.git_status = git.git_status_file(parent_ignored, status, self.link_to)
36+
end
37+
38+
---Create a sanitized partial copy of a node
39+
---@return FileLinkNode cloned
40+
function FileLinkNode:clone()
41+
local clone = FileNode.clone(self) --[[@as FileLinkNode]]
42+
43+
clone.type = self.type
44+
clone.link_to = self.link_to
45+
clone.fs_stat_target = self.fs_stat_target
46+
47+
return clone
48+
end
49+
50+
return FileLinkNode

lua/nvim-tree/node/file.lua

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local git = require("nvim-tree.git")
12
local utils = require("nvim-tree.utils")
23

34
local BaseNode = require("nvim-tree.node")
@@ -36,7 +37,23 @@ function FileNode:create(explorer, parent, absolute_path, name, fs_stat)
3637
return o
3738
end
3839

39-
---Create a sanitized partial copy of a node, populating children recursively.
40+
---Update the GitStatus of absolute path of the file
41+
---@param parent_ignored boolean
42+
---@param status table|nil
43+
function FileNode:update_git_status(parent_ignored, status)
44+
self.git_status = git.git_status_file(parent_ignored, status, self.absolute_path)
45+
end
46+
47+
---@return GitStatus|nil
48+
function FileNode:get_git_status()
49+
if not self.git_status then
50+
return nil
51+
end
52+
53+
return self.git_status.file and { self.git_status.file }
54+
end
55+
56+
---Create a sanitized partial copy of a node
4057
---@return FileNode cloned
4158
function FileNode:clone()
4259
local clone = BaseNode.clone(self) --[[@as FileNode]]

0 commit comments

Comments
 (0)