chore: resolve undefined-field

This commit is contained in:
Alexander Courtis
2024-10-27 17:22:04 +11:00
parent a16e67f3f4
commit 2fba44f8c3
7 changed files with 101 additions and 74 deletions

View File

@@ -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 }

View File

@@ -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

View File

@@ -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<string, string>
-- Git xy short-format statuses
---@alias GitPathXYs table<string, string[]>
---@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<string, GitProject>
_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<string, string|false>
_toplevels_by_path = {},
-- git dirs by toplevel
---@type table<string, string>
_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

View File

@@ -4,19 +4,17 @@ local notify = require("nvim-tree.notify")
local Class = require("nvim-tree.class")
---@alias GitXYByPath table<string, string> -- 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 = {

View File

@@ -82,8 +82,8 @@ function M.should_show_untracked(cwd)
return untracked[cwd]
end
---@param t table|nil
---@param k string
---@param t table<string|integer, boolean>?
---@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

View File

@@ -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

View File

@@ -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