From 2fba44f8c3ab8a140decd07c6e8970db8eb5d9f8 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sun, 27 Oct 2024 17:22:04 +1100 Subject: [PATCH] chore: resolve undefined-field --- lua/nvim-tree/explorer/filters.lua | 2 +- lua/nvim-tree/explorer/init.lua | 10 +-- lua/nvim-tree/git/init.lua | 109 ++++++++++++++++---------- lua/nvim-tree/git/runner.lua | 11 ++- lua/nvim-tree/git/utils.lua | 31 ++++---- lua/nvim-tree/node/directory-link.lua | 6 +- lua/nvim-tree/node/file-link.lua | 6 +- 7 files changed, 101 insertions(+), 74 deletions(-) diff --git a/lua/nvim-tree/explorer/filters.lua b/lua/nvim-tree/explorer/filters.lua index 6cf21d4a..87f7f95e 100644 --- a/lua/nvim-tree/explorer/filters.lua +++ b/lua/nvim-tree/explorer/filters.lua @@ -178,7 +178,7 @@ local function custom(self, path) end ---Prepare arguments for should_filter. This is done prior to should_filter for efficiency reasons. ----@param git_status table|nil optional results of git.load_project_status(...) +---@param git_status table|nil optional results of git.load_project(...) ---@return table --- git_status: reference --- bufinfo: empty unless no_buffer set: vim.fn.getbufinfo { buflisted = 1 } diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 9c7ae241..b787fffc 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -321,7 +321,7 @@ function Explorer:refresh_parent_nodes_for_path(path) local project = git.get_project(toplevel) or {} self:reload(node, project) - git.update_parent_statuses(node, project, toplevel) + git.update_parent_projects(node, project, toplevel) end log.profile_end(profile) @@ -331,7 +331,7 @@ end ---@param node DirectoryNode function Explorer:_load(node) local cwd = node.link_to or node.absolute_path - local git_status = git.load_project_status(cwd) + local git_status = git.load_project(cwd) self:explore(node, git_status, self) end @@ -423,7 +423,7 @@ function Explorer:explore(node, status, parent) local single_child = node:single_child_directory() if config.renderer.group_empty and not is_root and single_child then local child_cwd = single_child.link_to or single_child.absolute_path - local child_status = git.load_project_status(child_cwd) + local child_status = git.load_project(child_cwd) node.group_next = single_child local ns = self:explore(single_child, child_status, parent) node.nodes = ns or {} @@ -463,7 +463,7 @@ function Explorer:reload_explorer() end event_running = true - local projects = git.reload() + local projects = git.reload_all_projects() self:refresh_nodes(projects) if view.is_visible() then self.renderer:draw() @@ -477,7 +477,7 @@ function Explorer:reload_git() end event_running = true - local projects = git.reload() + local projects = git.reload_all_projects() git.reload_node_status(self, projects) self.renderer:draw() event_running = false diff --git a/lua/nvim-tree/git/init.lua b/lua/nvim-tree/git/init.lua index a97bd5df..da373609 100644 --- a/lua/nvim-tree/git/init.lua +++ b/lua/nvim-tree/git/init.lua @@ -7,20 +7,39 @@ local Watcher = require("nvim-tree.watcher").Watcher local Iterator = require("nvim-tree.iterators.node-iterator") local DirectoryNode = require("nvim-tree.node.directory") ----@class (exact) GitStatus -- xy short-format statuses +---Git xy short-format statuses for a single node +---@class (exact) GitStatus ---@field file string? ---@field dir table<"direct" | "indirect", string[]>? +-- Git xy short-format status +---@alias GitPathXY table + +-- Git xy short-format statuses +---@alias GitPathXYs table + +---@alias GitProjectFiles GitPathXY +---@alias GitProjectDirs table<"direct" | "indirect", GitPathXYs> + +---Git state for an entire repo +---@class (exact) GitProject +---@field files GitProjectFiles? +---@field dirs GitProjectDirs? +---@field watcher Watcher? + local M = { config = {}, - -- all projects keyed by toplevel + ---all projects keyed by toplevel + ---@type table _projects_by_toplevel = {}, - -- index of paths inside toplevels, false when not inside a project + ---index of paths inside toplevels, false when not inside a project + ---@type table _toplevels_by_path = {}, -- git dirs by toplevel + ---@type table _git_dirs_by_toplevel = {}, } @@ -36,33 +55,33 @@ local WATCHED_FILES = { ---@param toplevel string|nil ---@param path string|nil ----@param project table ----@param statuses GitXYByPath? -local function reload_git_statuses(toplevel, path, project, statuses) +---@param project GitProject +---@param project_files GitProjectFiles? +local function reload_git_project(toplevel, path, project, project_files) if path then for p in pairs(project.files) do if p:find(path, 1, true) == 1 then project.files[p] = nil end end - project.files = vim.tbl_deep_extend("force", project.files, statuses) + project.files = vim.tbl_deep_extend("force", project.files, project_files) else - project.files = statuses + project.files = project_files or {} end - project.dirs = git_utils.file_status_to_dir_status(project.files, toplevel) + project.dirs = git_utils.project_files_to_project_dirs(project.files, toplevel) end --- Is this path in a known ignored directory? ---@param path string ----@param project table git status +---@param project GitProject ---@return boolean local function path_ignored_in_project(path, project) if not path or not project then return false end - if project and project.files then + if project.files then for file, status in pairs(project.files) do if status == "!!" and vim.startswith(path, file) then return true @@ -72,9 +91,8 @@ local function path_ignored_in_project(path, project) return false end ---- Reload all projects ----@return table projects maybe empty -function M.reload() +---@return GitProject[] maybe empty +function M.reload_all_projects() if not M.config.git.enable then return {} end @@ -87,11 +105,12 @@ function M.reload() end --- Reload one project. Does nothing when no project or path is ignored ----@param toplevel string|nil ----@param path string|nil optional path to update only ----@param callback function|nil +---@param toplevel string? +---@param path string? optional path to update only +---@param callback function? function M.reload_project(toplevel, path, callback) - local project = M._projects_by_toplevel[toplevel] + local project = M._projects_by_toplevel[toplevel] --[[@as GitProject]] + if not toplevel or not project or not M.config.git.enable then if callback then callback() @@ -116,21 +135,21 @@ function M.reload_project(toplevel, path, callback) } if callback then - ---@param statuses GitXYByPath + ---@param statuses GitPathXY runner_opts.callback = function(statuses) - reload_git_statuses(toplevel, path, project, statuses) + reload_git_project(toplevel, path, project, statuses) callback() end GitRunner:run(runner_opts) else -- TODO #1974 use callback once async/await is available - reload_git_statuses(toplevel, path, project, GitRunner:run(runner_opts)) + reload_git_project(toplevel, path, project, GitRunner:run(runner_opts)) end end --- Retrieve a known project ----@param toplevel string|nil ----@return table|nil project +---@param toplevel string? +---@return GitProject? project function M.get_project(toplevel) return M._projects_by_toplevel[toplevel] end @@ -151,11 +170,10 @@ function M.get_toplevel(path) return nil end - if M._toplevels_by_path[path] then - return M._toplevels_by_path[path] - end - - if M._toplevels_by_path[path] == false then + local tl = M._toplevels_by_path[path] + if tl then + return tl + elseif tl == false then return nil end @@ -194,8 +212,15 @@ function M.get_toplevel(path) end M._toplevels_by_path[path] = toplevel + M._git_dirs_by_toplevel[toplevel] = git_dir - return M._toplevels_by_path[path] + + toplevel = M._toplevels_by_path[path] + if toplevel == false then + return nil + else + return toplevel + end end local function reload_tree_at(toplevel) @@ -230,8 +255,8 @@ end --- Load the project status for a path. Does nothing when no toplevel for path. --- Only fetches project status when unknown, otherwise returns existing. ---@param path string absolute ----@return table project maybe empty -function M.load_project_status(path) +---@return GitProject maybe empty +function M.load_project(path) if not M.config.git.enable then return {} end @@ -242,12 +267,12 @@ function M.load_project_status(path) return {} end - local status = M._projects_by_toplevel[toplevel] - if status then - return status + local project = M._projects_by_toplevel[toplevel] + if project then + return project end - local statuses = GitRunner:run({ + local path_xys = GitRunner:run({ toplevel = toplevel, list_untracked = git_utils.should_show_untracked(toplevel), list_ignored = true, @@ -275,10 +300,10 @@ function M.load_project_status(path) }) end - if statuses then + if path_xys then M._projects_by_toplevel[toplevel] = { - files = statuses, - dirs = git_utils.file_status_to_dir_status(statuses, toplevel), + files = path_xys, + dirs = git_utils.project_files_to_project_dirs(path_xys, toplevel), watcher = watcher, } return M._projects_by_toplevel[toplevel] @@ -289,9 +314,9 @@ function M.load_project_status(path) end ---@param dir DirectoryNode ----@param project table? +---@param project GitProject? ---@param root string? -function M.update_parent_statuses(dir, project, root) +function M.update_parent_projects(dir, project, root) while project and dir do -- step up to the containing project if dir.absolute_path == root then @@ -331,14 +356,14 @@ function M.refresh_dir(dir) dir.explorer:reload(node, project) - M.update_parent_statuses(dir, project, toplevel) + M.update_parent_projects(dir, project, toplevel) dir.explorer.renderer:draw() end) end ---@param dir DirectoryNode? ----@param projects table +---@param projects GitProject[] function M.reload_node_status(dir, projects) dir = dir and dir:as(DirectoryNode) if not dir or #dir.nodes == 0 then diff --git a/lua/nvim-tree/git/runner.lua b/lua/nvim-tree/git/runner.lua index d4163905..d8e8f64c 100644 --- a/lua/nvim-tree/git/runner.lua +++ b/lua/nvim-tree/git/runner.lua @@ -4,19 +4,17 @@ local notify = require("nvim-tree.notify") local Class = require("nvim-tree.class") ----@alias GitXYByPath table -- short-format statuses - ---@class (exact) GitRunnerOpts ---@field toplevel string absolute path ---@field path string? absolute path ---@field list_untracked boolean ---@field list_ignored boolean ---@field timeout integer ----@field callback fun(statuses: GitXYByPath)? +---@field callback fun(statuses: GitPathXY)? ---@class (exact) GitRunner: Class ---@field private opts GitRunnerOpts ----@field private statuses GitXYByPath +---@field private statuses GitPathXY ---@field private rc integer? -- -1 indicates timeout local GitRunner = Class:new() @@ -207,7 +205,8 @@ function GitRunner:finalise() end end ----@return GitXYByPath? statuses nil if callback present +---@private +---@return GitPathXY? statuses nil if callback present function GitRunner:execute() local async = self.opts.callback ~= nil local profile = log.profile_start("git %s job %s %s", async and "async" or "sync", self.opts.toplevel, self.opts.path) @@ -240,7 +239,7 @@ end ---Static method to run a git process, which will be killed if it takes more than timeout ---@param opts GitRunnerOpts ----@return GitXYByPath? statuses nil if callback present +---@return GitPathXY? statuses nil if callback present function GitRunner:run(opts) ---@type GitRunner local runner = { diff --git a/lua/nvim-tree/git/utils.lua b/lua/nvim-tree/git/utils.lua index 2faf0642..d1cb056b 100644 --- a/lua/nvim-tree/git/utils.lua +++ b/lua/nvim-tree/git/utils.lua @@ -82,8 +82,8 @@ function M.should_show_untracked(cwd) return untracked[cwd] end ----@param t table|nil ----@param k string +---@param t table? +---@param k string|integer ---@return table local function nil_insert(t, k) t = t or {} @@ -91,31 +91,33 @@ local function nil_insert(t, k) return t end ----@param status table +---@param project_files GitProjectFiles ---@param cwd string|nil ----@return table -function M.file_status_to_dir_status(status, cwd) - local direct = {} - for p, s in pairs(status) do +---@return GitProjectDirs +function M.project_files_to_project_dirs(project_files, cwd) + ---@type GitProjectDirs + local project_dirs = {} + + project_dirs.direct = {} + for p, s in pairs(project_files) do if s ~= "!!" then local modified = vim.fn.fnamemodify(p, ":h") - direct[modified] = nil_insert(direct[modified], s) + project_dirs.direct[modified] = nil_insert(project_dirs.direct[modified], s) end end - local indirect = {} - for dirname, statuses in pairs(direct) do + project_dirs.indirect = {} + for dirname, statuses in pairs(project_dirs.direct) do for s, _ in pairs(statuses) do local modified = dirname while modified ~= cwd and modified ~= "/" do modified = vim.fn.fnamemodify(modified, ":h") - indirect[modified] = nil_insert(indirect[modified], s) + project_dirs.indirect[modified] = nil_insert(project_dirs.indirect[modified], s) end end end - local r = { indirect = indirect, direct = direct } - for _, d in pairs(r) do + for _, d in pairs(project_dirs) do for dirname, statuses in pairs(d) do local new_statuses = {} for s, _ in pairs(statuses) do @@ -124,7 +126,8 @@ function M.file_status_to_dir_status(status, cwd) d[dirname] = new_statuses end end - return r + + return project_dirs end ---Git file status for an absolute path with optional fallback diff --git a/lua/nvim-tree/node/directory-link.lua b/lua/nvim-tree/node/directory-link.lua index ecf7e3f1..b5dfb47e 100644 --- a/lua/nvim-tree/node/directory-link.lua +++ b/lua/nvim-tree/node/directory-link.lua @@ -36,9 +36,9 @@ function DirectoryLinkNode:destroy() DirectoryNode.destroy(self) end ------Update the directory GitStatus of link target and the file status of the link itself ------@param parent_ignored boolean ------@param status table|nil +---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_utils.git_status_dir(parent_ignored, status, self.link_to, self.absolute_path) end diff --git a/lua/nvim-tree/node/file-link.lua b/lua/nvim-tree/node/file-link.lua index 5f9c6920..ca7cb3a9 100644 --- a/lua/nvim-tree/node/file-link.lua +++ b/lua/nvim-tree/node/file-link.lua @@ -32,9 +32,9 @@ function FileLinkNode:destroy() FileNode.destroy(self) end ------Update the GitStatus of the target otherwise the link itself ------@param parent_ignored boolean ------@param status table|nil +---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_utils.git_status_file(parent_ignored, status, self.link_to, self.absolute_path) end