diff --git a/lua/nvim-tree/classic.lua b/lua/nvim-tree/classic.lua index 25350e4d..1702db35 100644 --- a/lua/nvim-tree/classic.lua +++ b/lua/nvim-tree/classic.lua @@ -19,11 +19,7 @@ Object.__index = Object ---@diagnostic disable-line: inject-field function Object:new(...) end ----Extend a class T ----super will be set to T ----@generic T ----@param self T ----@return T +---Extend a class, setting .super function Object:extend() local cls = {} for k, v in pairs(self) do @@ -38,10 +34,11 @@ function Object:extend() end ---Implement the functions of a mixin ----Add the mixin to the implements table +---Add the mixin to .implements ---@param class Object function Object:implement(class) if not rawget(self, "implements") then + -- set on the class itself instead of parents rawset(self, "implements", {}) end self.implements[class] = true @@ -78,11 +75,7 @@ function Object:as(class) return self:is(class) and self or nil end ----Constructor that invokes :new on a new instance ----@generic T ----@param self T ----@param ... any ----@return T +---Constructor to create instance, call :new and return function Object:__call(...) local obj = setmetatable({}, self) obj:new(...) diff --git a/lua/nvim-tree/core.lua b/lua/nvim-tree/core.lua index 4ff9cc78..60d4a0a6 100644 --- a/lua/nvim-tree/core.lua +++ b/lua/nvim-tree/core.lua @@ -25,7 +25,7 @@ function M.init(foldername) path, err = vim.loop.cwd() end if path then - TreeExplorer = require("nvim-tree.explorer")(path) + TreeExplorer = require("nvim-tree.explorer")({ path = path }) else notify.error(err) TreeExplorer = nil diff --git a/lua/nvim-tree/enum.lua b/lua/nvim-tree/enum.lua index a680c2b3..9c50bc27 100644 --- a/lua/nvim-tree/enum.lua +++ b/lua/nvim-tree/enum.lua @@ -1,13 +1,5 @@ local M = {} ----Must be synced with uv.fs_stat.result as it is compared with it ----@enum (key) NODE_TYPE -M.NODE_TYPE = { - directory = 1, - file = 2, - link = 4, -} - ---Setup options for "highlight_*" ---@enum HL_POSITION M.HL_POSITION = { diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index d32d0ecb..b10de76e 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -37,9 +37,19 @@ local config ---@field clipboard Clipboard local Explorer = RootNode:extend() ----@param path string -function Explorer:new(path) - Explorer.super.new(self, self, path, "..", nil) +---@class Explorer +---@overload fun(opts: ExplorerArgs): Explorer + +---@class (exact) ExplorerArgs +---@field path string + +---@param args ExplorerArgs +function Explorer:new(args) + Explorer.super.new(self, { + explorer = self, + absolute_path = args.path, + name = "..", + }) self.uid_explorer = vim.loop.hrtime() self.augroup_id = vim.api.nvim_create_augroup("NvimTree_Explorer_" .. self.uid_explorer, {}) @@ -222,7 +232,13 @@ function Explorer:reload(node, project) end if not nodes_by_path[abs] then - local new_child = node_factory.create_node(self, node, abs, stat, name) + local new_child = node_factory.create({ + explorer = self, + parent = node, + absolute_path = abs, + name = name, + fs_stat = stat + }) if new_child then table.insert(node.nodes, new_child) nodes_by_path[abs] = new_child @@ -360,7 +376,13 @@ function Explorer:populate_children(handle, cwd, node, project, parent) local stat = vim.loop.fs_lstat(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 = node_factory.create_node(self, node, abs, stat, name) + local child = node_factory.create({ + explorer = self, + parent = node, + absolute_path = abs, + name = name, + fs_stat = stat + }) if child then table.insert(node.nodes, child) nodes_by_path[child.absolute_path] = true diff --git a/lua/nvim-tree/node/directory-link.lua b/lua/nvim-tree/node/directory-link.lua index 4cdc337c..bde98102 100644 --- a/lua/nvim-tree/node/directory-link.lua +++ b/lua/nvim-tree/node/directory-link.lua @@ -8,23 +8,23 @@ local LinkNode = require("nvim-tree.node.link") local DirectoryLinkNode = DirectoryNode:extend() DirectoryLinkNode:implement(LinkNode) ----@param explorer Explorer ----@param parent DirectoryNode ----@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 -function DirectoryLinkNode:new(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target) - -- create DirectoryNode with the target path for the watcher - DirectoryLinkNode.super.new(self, explorer, parent, link_to, name, fs_stat) +---@class DirectoryLinkNode +---@overload fun(opts: LinkNodeArgs): DirectoryLinkNode + +---@protected +---@param args LinkNodeArgs +function DirectoryLinkNode:new(args) + LinkNode.new(self, args) + + -- create DirectoryNode with watcher on link_to + local absolute_path = args.absolute_path + args.absolute_path = args.link_to + DirectoryLinkNode.super.new(self, args) + + self.type = "link" -- reset absolute path to the link itself self.absolute_path = absolute_path - - self.type = "link" - self.link_to = link_to - self.fs_stat_target = fs_stat_target end function DirectoryLinkNode:destroy() @@ -76,7 +76,6 @@ end 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 diff --git a/lua/nvim-tree/node/directory.lua b/lua/nvim-tree/node/directory.lua index e7db8a1e..02de70cb 100644 --- a/lua/nvim-tree/node/directory.lua +++ b/lua/nvim-tree/node/directory.lua @@ -12,36 +12,26 @@ local Node = require("nvim-tree.node") ---@field private watcher Watcher? local DirectoryNode = Node:extend() ----@param explorer Explorer ----@param parent DirectoryNode? ----@param absolute_path string ----@param name string ----@param fs_stat uv.fs_stat.result|nil -function DirectoryNode:new(explorer, parent, absolute_path, name, fs_stat) - DirectoryNode.super.new(self) +---@class DirectoryNode +---@overload fun(opts: NodeArgs): DirectoryNode - local handle = vim.loop.fs_scandir(absolute_path) +---@protected +---@param args NodeArgs +function DirectoryNode:new(args) + DirectoryNode.super.new(self, args) + + local handle = vim.loop.fs_scandir(args.absolute_path) local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil or false - self.type = "directory" - self.explorer = explorer - self.absolute_path = absolute_path - self.executable = false - self.fs_stat = fs_stat - self.git_status = nil - self.hidden = false - self.name = name - self.parent = parent - self.watcher = nil - self.diag_status = nil + self.type = "directory" - self.has_children = has_children - self.group_next = nil - self.nodes = {} - self.open = false - self.hidden_stats = nil + self.has_children = has_children + self.group_next = nil + self.nodes = {} + self.open = false + self.hidden_stats = nil - self.watcher = require("nvim-tree.explorer.watch").create_watcher(self) + self.watcher = require("nvim-tree.explorer.watch").create_watcher(self) end function DirectoryNode:destroy() diff --git a/lua/nvim-tree/node/factory.lua b/lua/nvim-tree/node/factory.lua index 94cd5d48..adaaa5a7 100644 --- a/lua/nvim-tree/node/factory.lua +++ b/lua/nvim-tree/node/factory.lua @@ -7,38 +7,38 @@ local Watcher = require("nvim-tree.watcher") local M = {} ---Factory function to create the appropriate Node ----@param explorer Explorer ----@param parent DirectoryNode ----@param absolute_path string ----@param stat uv.fs_stat.result? -- on nil stat return nil Node ----@param name string +---nil on invalid stat or invalid link target stat +---@param args NodeArgs ---@return Node? -function M.create_node(explorer, parent, absolute_path, stat, name) - if not stat then +function M.create(args) + if not args.fs_stat then return nil end - if stat.type == "directory" then + if args.fs_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(explorer, parent, absolute_path, name, stat) + if vim.loop.fs_access(args.absolute_path, "R") and Watcher.is_fs_event_capable(args.absolute_path) then + return DirectoryNode(args) end - elseif stat.type == "file" then - -- any file - return FileNode(explorer, parent, absolute_path, name, stat) - elseif stat.type == "link" then + elseif args.fs_stat.type == "file" then + return FileNode(args) + elseif args.fs_stat.type == "link" then -- link target path and stat must resolve - local link_to = vim.loop.fs_realpath(absolute_path) + local link_to = vim.loop.fs_realpath(args.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 + ---@cast args LinkNodeArgs + args.link_to = link_to + args.fs_stat_target = link_to_stat + -- choose directory or file if link_to_stat.type == "directory" then - return DirectoryLinkNode(explorer, parent, absolute_path, link_to, name, stat, link_to_stat) + return DirectoryLinkNode(args) else - return FileLinkNode(explorer, parent, absolute_path, link_to, name, stat, link_to_stat) + return FileLinkNode(args) end end diff --git a/lua/nvim-tree/node/file-link.lua b/lua/nvim-tree/node/file-link.lua index b892c2a0..14ff2407 100644 --- a/lua/nvim-tree/node/file-link.lua +++ b/lua/nvim-tree/node/file-link.lua @@ -8,19 +8,16 @@ local LinkNode = require("nvim-tree.node.link") local FileLinkNode = FileNode:extend() FileLinkNode:implement(LinkNode) ----@param explorer Explorer ----@param parent DirectoryNode ----@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 -function FileLinkNode:new(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target) - FileLinkNode.super.new(self, explorer, parent, absolute_path, name, fs_stat) +---@class FileLinkNode +---@overload fun(opts: LinkNodeArgs): FileLinkNode + +---@protected +---@param args LinkNodeArgs +function FileLinkNode:new(args) + LinkNode.new(self, args) + FileLinkNode.super.new(self, args) self.type = "link" - self.link_to = link_to - self.fs_stat_target = fs_stat_target end function FileLinkNode:destroy() @@ -65,7 +62,6 @@ end 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 diff --git a/lua/nvim-tree/node/file.lua b/lua/nvim-tree/node/file.lua index b281633e..6a4fc10a 100644 --- a/lua/nvim-tree/node/file.lua +++ b/lua/nvim-tree/node/file.lua @@ -17,26 +17,17 @@ local PICTURE_MAP = { ---@field extension string local FileNode = Node:extend() ----@param explorer Explorer ----@param parent DirectoryNode ----@param absolute_path string ----@param name string ----@param fs_stat uv.fs_stat.result? -function FileNode:new(explorer, parent, absolute_path, name, fs_stat) - FileNode.super.new(self) +---@class FileNode +---@overload fun(opts: NodeArgs): FileNode - self.type = "file" - self.explorer = explorer - self.absolute_path = absolute_path - self.executable = utils.is_executable(absolute_path) - self.fs_stat = fs_stat - self.git_status = nil - self.hidden = false - self.name = name - self.parent = parent - self.diag_status = nil +---@protected +---@param args NodeArgs +function FileNode:new(args) + FileNode.super.new(self, args) - self.extension = string.match(name, ".?[^.]+%.(.*)") or "" + self.type = "file" + self.extension = string.match(args.name, ".?[^.]+%.(.*)") or "" + self.executable = utils.is_executable(args.absolute_path) end function FileNode:destroy() diff --git a/lua/nvim-tree/node/init.lua b/lua/nvim-tree/node/init.lua index e2189140..51fa7c8d 100644 --- a/lua/nvim-tree/node/init.lua +++ b/lua/nvim-tree/node/init.lua @@ -2,7 +2,7 @@ local Object = require("nvim-tree.classic") ---Abstract Node class. ---@class (exact) Node: Object ----@field type NODE_TYPE +---@field type "file" | "directory" | "link" uv.fs_stat.result.type ---@field explorer Explorer ---@field absolute_path string ---@field executable boolean @@ -15,8 +15,26 @@ local Object = require("nvim-tree.classic") ---@field private is_dot boolean cached is_dotfile local Node = Object:extend() -function Node:new() - self.is_dot = false +---@class (exact) NodeArgs +---@field explorer Explorer +---@field parent DirectoryNode? +---@field absolute_path string +---@field name string +---@field fs_stat uv.fs_stat.result? + +---@protected +---@param args NodeArgs +function Node:new(args) + self.explorer = args.explorer + self.absolute_path = args.absolute_path + self.executable = false + self.fs_stat = args.fs_stat + self.git_status = nil + self.hidden = false + self.name = args.name + self.parent = args.parent + self.diag_status = nil + self.is_dot = false end function Node:destroy() diff --git a/lua/nvim-tree/node/link.lua b/lua/nvim-tree/node/link.lua index 9193e1c8..c76bd17f 100644 --- a/lua/nvim-tree/node/link.lua +++ b/lua/nvim-tree/node/link.lua @@ -5,4 +5,17 @@ local Object = require("nvim-tree.classic") ---@field protected fs_stat_target uv.fs_stat.result local LinkNode = Object:extend() +---@class (exact) LinkNodeArgs: NodeArgs +---@field link_to string +---@field fs_stat_target uv.fs_stat.result +--- +---@protected +---@param args LinkNodeArgs +function LinkNode:new(args) + LinkNode.super.new(self, args) + + self.link_to = args.link_to + self.fs_stat_target = args.fs_stat_target +end + return LinkNode diff --git a/lua/nvim-tree/node/root.lua b/lua/nvim-tree/node/root.lua index 4a33e33c..dea9372a 100644 --- a/lua/nvim-tree/node/root.lua +++ b/lua/nvim-tree/node/root.lua @@ -3,12 +3,13 @@ local DirectoryNode = require("nvim-tree.node.directory") ---@class (exact) RootNode: DirectoryNode local RootNode = DirectoryNode:extend() ----@param explorer Explorer ----@param absolute_path string ----@param name string ----@param fs_stat uv.fs_stat.result|nil -function RootNode:new(explorer, absolute_path, name, fs_stat) - RootNode.super.new(self, explorer, nil, absolute_path, name, fs_stat) +---@class RootNode +---@overload fun(opts: NodeArgs): RootNode + +---@protected +---@param args NodeArgs +function RootNode:new(args) + RootNode.super.new(self, args) end ---Root is never a dotfile