Skip to content

refactor(#2886): multi instance: node class refactoring: extract links, *_git_status #2944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 11, 2024
31 changes: 22 additions & 9 deletions lua/nvim-tree/git/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -283,33 +283,46 @@ function M.load_project_status(path)
end
end

---Git file and directory status for an absolute path with optional file fallback
---@param parent_ignored boolean
---@param status table|nil
---@param absolute_path string
---@param path string
---@param path_file string? alternative file path when no other file status
---@return GitStatus|nil
function M.git_status_dir(parent_ignored, status, absolute_path)
function M.git_status_dir(parent_ignored, status, path, path_file)
if parent_ignored then
return { file = "!!" }
end

if status then
return {
file = status.files and status.files[absolute_path],
file = status.files and status.files[path] or path_file and status.files[path_file],
dir = status.dirs and {
direct = status.dirs.direct[absolute_path],
indirect = status.dirs.indirect[absolute_path],
direct = status.dirs.direct[path],
indirect = status.dirs.indirect[path],
},
}
end
end

---Git file status for an absolute path with optional fallback
---@param parent_ignored boolean
---@param status table|nil
---@param absolute_path string
---@param path string
---@param path_fallback string?
---@return GitStatus
function M.git_status_file(parent_ignored, status, absolute_path)
local file_status = parent_ignored and "!!" or (status and status.files and status.files[absolute_path])
return { file = file_status }
function M.git_status_file(parent_ignored, status, path, path_fallback)
if parent_ignored then
return { file = "!!" }
end

if not status or not status.files then
return {}
end

return {
file = status.files[path] or status.files[path_fallback]
}
end

function M.purge_state()
Expand Down
19 changes: 19 additions & 0 deletions lua/nvim-tree/log.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ function M.raw(typ, fmt, ...)
end
end

--- Write to a new file
---@param typ string as per log.types config
---@param path string absolute path
---@param fmt string for string.format
---@param ... any arguments for string.format
function M.file(typ, path, fmt, ...)
if not M.enabled(typ) then
return
end

local line = string.format(fmt, ...)
local file = io.open(path, "w")
if file then
io.output(file)
io.write(line)
io.close(file)
end
end

---@class Profile
---@field start number nanos
---@field tag string
Expand Down
54 changes: 54 additions & 0 deletions lua/nvim-tree/node/directory-link.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
local git = require("nvim-tree.git")

local DirectoryNode = require("nvim-tree.node.directory")

---@class (exact) DirectoryLinkNode: DirectoryNode
---@field link_to string absolute path
---@field fs_stat_target uv.fs_stat.result
local DirectoryLinkNode = DirectoryNode:new()

---Static factory method
---@param explorer Explorer
---@param parent Node
---@param absolute_path string
---@param link_to string
---@param name string
---@param fs_stat uv.fs_stat.result?
---@param fs_stat_target uv.fs_stat.result
---@return DirectoryLinkNode? nil on vim.loop.fs_realpath failure
function DirectoryLinkNode:create(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target)
-- create DirectoryNode with the target path for the watcher
local o = DirectoryNode:create(explorer, parent, link_to, name, fs_stat)

o = self:new(o) --[[@as DirectoryLinkNode]]

-- reset absolute path to the link itself
o.absolute_path = absolute_path

o.type = "link"
o.link_to = link_to
o.fs_stat_target = fs_stat_target

return o
end

-----Update the directory GitStatus of link target and the file status of the link itself
-----@param parent_ignored boolean
-----@param status table|nil
function DirectoryLinkNode:update_git_status(parent_ignored, status)
self.git_status = git.git_status_dir(parent_ignored, status, self.link_to, self.absolute_path)
end

---Create a sanitized partial copy of a node, populating children recursively.
---@return DirectoryLinkNode cloned
function DirectoryLinkNode:clone()
local clone = DirectoryNode.clone(self) --[[@as DirectoryLinkNode]]

clone.type = self.type
clone.link_to = self.link_to
clone.fs_stat_target = self.fs_stat_target

return clone
end

return DirectoryLinkNode
58 changes: 58 additions & 0 deletions lua/nvim-tree/node/directory.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
local git = require("nvim-tree.git")
local watch = require("nvim-tree.explorer.watch")

local BaseNode = require("nvim-tree.node")
Expand Down Expand Up @@ -58,6 +59,63 @@ function DirectoryNode:destroy()
end
end

---Update the GitStatus of the directory
---@param parent_ignored boolean
---@param status table|nil
function DirectoryNode:update_git_status(parent_ignored, status)
self.git_status = git.git_status_dir(parent_ignored, status, self.absolute_path, nil)
end

---@return GitStatus|nil
function DirectoryNode:get_git_status()
if not self.git_status or not self.explorer.opts.git.show_on_dirs then
return nil
end

local status = {}
if not self:last_group_node().open or self.explorer.opts.git.show_on_open_dirs then
-- dir is closed or we should show on open_dirs
if self.git_status.file ~= nil then
table.insert(status, self.git_status.file)
end
if self.git_status.dir ~= nil then
if self.git_status.dir.direct ~= nil then
for _, s in pairs(self.git_status.dir.direct) do
table.insert(status, s)
end
end
if self.git_status.dir.indirect ~= nil then
for _, s in pairs(self.git_status.dir.indirect) do
table.insert(status, s)
end
end
end
else
-- dir is open and we shouldn't show on open_dirs
if self.git_status.file ~= nil then
table.insert(status, self.git_status.file)
end
if self.git_status.dir ~= nil and self.git_status.dir.direct ~= nil then
local deleted = {
[" D"] = true,
["D "] = true,
["RD"] = true,
["DD"] = true,
}
for _, s in pairs(self.git_status.dir.direct) do
if deleted[s] then
table.insert(status, s)
end
end
end
end
if #status == 0 then
return nil
else
return status
end
end

---Create a sanitized partial copy of a node, populating children recursively.
---@return DirectoryNode cloned
function DirectoryNode:clone()
Expand Down
31 changes: 24 additions & 7 deletions lua/nvim-tree/node/factory.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local DirectoryLinkNode = require("nvim-tree.node.directory-link")
local DirectoryNode = require("nvim-tree.node.directory")
local LinkNode = require("nvim-tree.node.link")
local FileLinkNode = require("nvim-tree.node.file-link")
local FileNode = require("nvim-tree.node.file")
local Watcher = require("nvim-tree.watcher")

Expand All @@ -8,21 +9,37 @@ local M = {}
---Factory function to create the appropriate Node
---@param explorer Explorer
---@param parent Node
---@param abs string
---@param absolute_path string
---@param stat uv.fs_stat.result? -- on nil stat return nil Node
---@param name string
---@return Node?
function M.create_node(explorer, parent, abs, stat, name)
function M.create_node(explorer, parent, absolute_path, stat, name)
if not stat then
return nil
end

if stat.type == "directory" and vim.loop.fs_access(abs, "R") and Watcher.is_fs_event_capable(abs) then
return DirectoryNode:create(explorer, parent, abs, name, stat)
if stat.type == "directory" then
-- directory must be readable and enumerable
if vim.loop.fs_access(absolute_path, "R") and Watcher.is_fs_event_capable(absolute_path) then
return DirectoryNode:create(explorer, parent, absolute_path, name, stat)
end
elseif stat.type == "file" then
return FileNode:create(explorer, parent, abs, name, stat)
-- any file
return FileNode:create(explorer, parent, absolute_path, name, stat)
elseif stat.type == "link" then
return LinkNode:create(explorer, parent, abs, name, stat)
-- link target path and stat must resolve
local link_to = vim.loop.fs_realpath(absolute_path)
local link_to_stat = link_to and vim.loop.fs_stat(link_to)
if not link_to or not link_to_stat then
return
end

-- choose directory or file
if link_to_stat.type == "directory" then
return DirectoryLinkNode:create(explorer, parent, absolute_path, link_to, name, stat, link_to_stat)
else
return FileLinkNode:create(explorer, parent, absolute_path, link_to, name, stat, link_to_stat)
end
end

return nil
Expand Down
50 changes: 50 additions & 0 deletions lua/nvim-tree/node/file-link.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
local git = require("nvim-tree.git")

local FileNode = require("nvim-tree.node.file")

---@class (exact) FileLinkNode: FileNode
---@field link_to string absolute path
---@field fs_stat_target uv.fs_stat.result
local FileLinkNode = FileNode:new()

---Static factory method
---@param explorer Explorer
---@param parent Node
---@param absolute_path string
---@param link_to string
---@param name string
---@param fs_stat uv.fs_stat.result?
---@param fs_stat_target uv.fs_stat.result
---@return FileLinkNode? nil on vim.loop.fs_realpath failure
function FileLinkNode:create(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target)
local o = FileNode:create(explorer, parent, absolute_path, name, fs_stat)

o = self:new(o) --[[@as FileLinkNode]]

o.type = "link"
o.link_to = link_to
o.fs_stat_target = fs_stat_target

return o
end

-----Update the GitStatus of the target otherwise the link itself
-----@param parent_ignored boolean
-----@param status table|nil
function FileLinkNode:update_git_status(parent_ignored, status)
self.git_status = git.git_status_file(parent_ignored, status, self.link_to, self.absolute_path)
end

---Create a sanitized partial copy of a node
---@return FileLinkNode cloned
function FileLinkNode:clone()
local clone = FileNode.clone(self) --[[@as FileLinkNode]]

clone.type = self.type
clone.link_to = self.link_to
clone.fs_stat_target = self.fs_stat_target

return clone
end

return FileLinkNode
19 changes: 18 additions & 1 deletion lua/nvim-tree/node/file.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
local git = require("nvim-tree.git")
local utils = require("nvim-tree.utils")

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

---Create a sanitized partial copy of a node, populating children recursively.
---Update the GitStatus of the file
---@param parent_ignored boolean
---@param status table|nil
function FileNode:update_git_status(parent_ignored, status)
self.git_status = git.git_status_file(parent_ignored, status, self.absolute_path, nil)
end

---@return GitStatus|nil
function FileNode:get_git_status()
if not self.git_status then
return nil
end

return self.git_status.file and { self.git_status.file }
end

---Create a sanitized partial copy of a node
---@return FileNode cloned
function FileNode:clone()
local clone = BaseNode.clone(self) --[[@as FileNode]]
Expand Down
Loading
Loading