refactor(git): rework module around clearer Status and Repo split
This commit is contained in:
+157
-31
@@ -1,6 +1,6 @@
|
||||
local M = {}
|
||||
|
||||
M.UNMERGED = {
|
||||
local UNMERGED = {
|
||||
DD = true,
|
||||
AU = true,
|
||||
UD = true,
|
||||
@@ -10,48 +10,174 @@ M.UNMERGED = {
|
||||
UU = true,
|
||||
}
|
||||
|
||||
---@param code string
|
||||
---@return string? char
|
||||
---@return string? hl_group
|
||||
function M.indicator(code)
|
||||
if code == "" then
|
||||
return nil
|
||||
---@alias ow.Git.Status.EntryKind "untracked"|"unstaged"|"staged"|"unmerged"|"ignored"
|
||||
|
||||
---@class ow.Git.Status.Entry
|
||||
---@field path string
|
||||
---@field kind ow.Git.Status.EntryKind
|
||||
---@field char string
|
||||
---@field hl string
|
||||
---@field orig string?
|
||||
|
||||
---@class ow.Git.Status.BranchInfo
|
||||
---@field head string?
|
||||
---@field upstream string?
|
||||
---@field ahead integer
|
||||
---@field behind integer
|
||||
|
||||
---@class ow.Git.Status
|
||||
---@field branch ow.Git.Status.BranchInfo
|
||||
---@field entries table<string, ow.Git.Status.Entry[]>
|
||||
local Status = {}
|
||||
Status.__index = Status
|
||||
|
||||
---@param kind ow.Git.Status.EntryKind
|
||||
---@return ow.Git.Status.Entry[]
|
||||
function Status:by_kind(kind)
|
||||
local out = {}
|
||||
for _, list in pairs(self.entries) do
|
||||
for _, e in ipairs(list) do
|
||||
if e.kind == kind then
|
||||
table.insert(out, e)
|
||||
end
|
||||
end
|
||||
end
|
||||
if code == "??" then
|
||||
return "?", "GitUntracked"
|
||||
end
|
||||
if code == "!!" then
|
||||
return "!", "GitIgnored"
|
||||
end
|
||||
if M.UNMERGED[code] then
|
||||
return "U", "GitUnmerged"
|
||||
end
|
||||
local x, y = code:sub(1, 1), code:sub(2, 2)
|
||||
if x == "R" or y == "R" then
|
||||
return out
|
||||
end
|
||||
|
||||
---@param x string
|
||||
---@return string char, string hl
|
||||
local function staged_attrs(x)
|
||||
if x == "R" then
|
||||
return "R", "GitRenamed"
|
||||
end
|
||||
if y == " " and x ~= " " then
|
||||
return x, "GitStaged"
|
||||
return x, "GitStaged"
|
||||
end
|
||||
|
||||
---@param y string
|
||||
---@return string char, string hl
|
||||
local function unstaged_attrs(y)
|
||||
if y == "R" then
|
||||
return "R", "GitRenamed"
|
||||
end
|
||||
if y == "D" then
|
||||
return "D", "GitDeleted"
|
||||
end
|
||||
return "M", "GitUnstaged"
|
||||
return y, "GitUnstaged"
|
||||
end
|
||||
|
||||
---@param code string
|
||||
---@return string?
|
||||
function M.format(code)
|
||||
local char, hl = M.indicator(code)
|
||||
if not char then
|
||||
return nil
|
||||
---@param line string
|
||||
---@return ow.Git.Status.BranchInfo
|
||||
local function parse_branch_line(line)
|
||||
local info = { ahead = 0, behind = 0 }
|
||||
local content = line:sub(4)
|
||||
local arrow = content:find("...", 1, true)
|
||||
if not arrow then
|
||||
info.head = content
|
||||
return info
|
||||
end
|
||||
return string.format("%%#%s#%s%%*", hl, char)
|
||||
info.head = content:sub(1, arrow - 1)
|
||||
local rest = content:sub(arrow + 3)
|
||||
local bracket = rest:find(" %[")
|
||||
if not bracket then
|
||||
info.upstream = rest
|
||||
return info
|
||||
end
|
||||
info.upstream = rest:sub(1, bracket - 1)
|
||||
local inside = rest:match("%[([^%]]+)%]")
|
||||
if inside then
|
||||
info.ahead = (tonumber(inside:match("ahead (%d+)")) or 0) --[[@as integer]]
|
||||
info.behind = (tonumber(inside:match("behind (%d+)")) or 0) --[[@as integer]]
|
||||
end
|
||||
return info
|
||||
end
|
||||
|
||||
---@return string
|
||||
function M.statusline()
|
||||
return vim.b.git_status or ""
|
||||
---@param line string
|
||||
---@return string x, string y, string path, string? orig
|
||||
local function parse_status_line(line)
|
||||
local x = line:sub(1, 1)
|
||||
local y = line:sub(2, 2)
|
||||
local rest = line:sub(4)
|
||||
local orig
|
||||
if x == "R" or x == "C" or y == "R" or y == "C" then
|
||||
local arrow = rest:find(" -> ", 1, true)
|
||||
if arrow then
|
||||
orig = rest:sub(1, arrow - 1)
|
||||
rest = rest:sub(arrow + 4)
|
||||
end
|
||||
end
|
||||
return x, y, rest, orig
|
||||
end
|
||||
|
||||
---@param entries table<string, ow.Git.Status.Entry[]>
|
||||
---@param entry ow.Git.Status.Entry
|
||||
local function add(entries, entry)
|
||||
local key = entry.path
|
||||
if key:sub(-1) == "/" then
|
||||
key = key:sub(1, -2)
|
||||
end
|
||||
local list = entries[key] or {}
|
||||
table.insert(list, entry)
|
||||
entries[key] = list
|
||||
end
|
||||
|
||||
---@param stdout string
|
||||
---@return ow.Git.Status
|
||||
function M.parse(stdout)
|
||||
local branch = { ahead = 0, behind = 0 }
|
||||
---@type table<string, ow.Git.Status.Entry[]>
|
||||
local entries = {}
|
||||
for line in stdout:gmatch("[^\r\n]+") do
|
||||
if line:sub(1, 2) == "##" then
|
||||
branch = parse_branch_line(line)
|
||||
else
|
||||
local x, y, path, orig = parse_status_line(line)
|
||||
if x == "?" and y == "?" then
|
||||
add(entries, {
|
||||
path = path,
|
||||
kind = "untracked",
|
||||
char = "?",
|
||||
hl = "GitUntracked",
|
||||
})
|
||||
elseif x == "!" and y == "!" then
|
||||
add(entries, {
|
||||
path = path,
|
||||
kind = "ignored",
|
||||
char = "i",
|
||||
hl = "GitIgnored",
|
||||
})
|
||||
elseif UNMERGED[x .. y] then
|
||||
add(entries, {
|
||||
path = path,
|
||||
kind = "unmerged",
|
||||
char = "!",
|
||||
hl = "GitUnmerged",
|
||||
})
|
||||
else
|
||||
if x ~= " " then
|
||||
local char, hl = staged_attrs(x)
|
||||
add(entries, {
|
||||
path = path,
|
||||
kind = "staged",
|
||||
char = char,
|
||||
hl = hl,
|
||||
orig = orig,
|
||||
})
|
||||
end
|
||||
if y ~= " " then
|
||||
local char, hl = unstaged_attrs(y)
|
||||
add(entries, {
|
||||
path = path,
|
||||
kind = "unstaged",
|
||||
char = char,
|
||||
hl = hl,
|
||||
orig = orig,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return setmetatable({ branch = branch, entries = entries }, Status)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user