chore: resolve undefined-field
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user