aboutsummaryrefslogtreecommitdiffstats
path: root/config/yazi/plugins/yatline-githead.yazi/main.lua
diff options
context:
space:
mode:
Diffstat (limited to 'config/yazi/plugins/yatline-githead.yazi/main.lua')
-rw-r--r--config/yazi/plugins/yatline-githead.yazi/main.lua569
1 files changed, 569 insertions, 0 deletions
diff --git a/config/yazi/plugins/yatline-githead.yazi/main.lua b/config/yazi/plugins/yatline-githead.yazi/main.lua
new file mode 100644
index 00000000..ab49987f
--- /dev/null
+++ b/config/yazi/plugins/yatline-githead.yazi/main.lua
@@ -0,0 +1,569 @@
+---@diagnostic disable: undefined-global
+
+local save = ya.sync(function(this, cwd, output)
+ if cwd:match("%.git[/\\]") or cwd:match("%.git$") then
+ this.output = nil
+ return
+ end
+ if cx.active.current.cwd == Url(cwd) then
+ ---@type GitStatusData
+ this.output = output
+ ui.render()
+ end
+end)
+
+return {
+ setup = function(this, options)
+ options = options or {}
+
+ local config = {
+ order = options.order or {
+ "branch",
+ "remote_branch",
+ "tag",
+ "commit",
+ "behind_ahead_remote",
+ "stashes",
+ "state",
+ "staged",
+ "unstaged",
+ "untracked",
+ },
+
+ show_numbers = options.show_numbers == nil and true or options.show_numbers,
+
+ show_branch = options.show_branch == nil and true or options.show_branch,
+ branch_prefix = options.branch_prefix or "",
+ branch_symbol = options.branch_symbol or "",
+ branch_borders = options.branch_borders or "",
+
+ show_remote_branch = options.show_remote_branch == nil and true or options.show_remote_branch,
+ always_show_remote_branch = options.always_show_remote_branch == nil and false
+ or options.always_show_remote_branch,
+ always_show_remote_repo = options.always_show_remote_repo == nil and false
+ or options.always_show_remote_repo,
+ remote_branch_prefix = options.remote_branch_prefix or ":",
+
+ show_tag = options.show_tag == nil and true or options.show_tag,
+ always_show_tag = options.always_show_tag == nil and false or options.always_show_tag,
+ tag_symbol = options.tag_symbol == nil and "#" or options.tag_symbol,
+
+ show_commit = options.show_commit == nil and true or options.show_commit,
+ always_show_commit = options.always_show_commit == nil and false or options.always_show_commit,
+ commit_symbol = options.commit_symbol == nil and "@" or options.commit_symbol,
+
+ show_behind_ahead_remote = options.show_behind_ahead_remote == nil and true
+ or options.show_behind_ahead_remote,
+ behind_remote_symbol = options.behind_remote_symbol or "⇣",
+ ahead_remote_symbol = options.ahead_remote_symbol or "⇡",
+
+ show_stashes = options.show_stashes == nil and true or options.show_stashes,
+ stashes_symbol = options.stashes_symbol or "$",
+
+ show_state = options.show_state == nil and true or options.show_state,
+ show_state_prefix = options.show_state_prefix == nil and true or options.show_state_prefix,
+ state_symbol = options.state_symbol or "~",
+
+ show_staged = options.show_staged == nil and true or options.show_staged,
+ staged_symbol = options.staged_symbol or "+",
+
+ show_unstaged = options.show_unstaged == nil and true or options.show_unstaged,
+ unstaged_symbol = options.unstaged_symbol or "!",
+
+ show_untracked = options.show_untracked == nil and true or options.show_untracked,
+ untracked_symbol = options.untracked_symbol or "?",
+ }
+
+ if options.theme then
+ options = options.theme
+ end
+
+ local theme = {
+ branch_color = options.branch_color or "blue",
+ remote_branch_color = options.remote_branch_color or "bright magenta",
+ tag_color = options.tag_color or "magenta",
+ commit_color = options.commit_color or "bright magenta",
+ behind_remote_color = options.behind_remote_color or "bright magenta",
+ ahead_remote_color = options.ahead_remote_color or "bright magenta",
+ stashes_color = options.stashes_color or "bright magenta",
+ state_color = options.state_color or "red",
+ staged_color = options.staged_color or "bright yellow",
+ unstaged_color = options.unstaged_color or "bright yellow",
+ untracked_color = options.untracked_color or "bright blue",
+ }
+
+ local function render_branch()
+ local branch = this.output.branch
+
+ if not branch then
+ return nil
+ end
+
+ local left_border = config.branch_borders:sub(1, 1)
+ local right_border = config.branch_borders:sub(2, 2)
+
+ local branch_string = ""
+
+ if config.branch_symbol == "" then
+ branch_string = left_border .. branch .. right_border
+ else
+ branch_string = left_border .. config.branch_symbol .. branch .. right_border
+ end
+
+ local branch_prefix = config.branch_prefix == "" and "" or config.branch_prefix .. " "
+
+ return {
+ { branch_prefix, nil },
+ { branch_string, theme.branch_color },
+ }
+ end
+
+ local function render_remote_branch()
+ local branch = this.output.branch
+ local remote_branch = this.output.remote_branch
+ local remote_repo = this.output.remote_repo
+
+ if not remote_branch then
+ return nil
+ end
+
+ local show_remote = config.always_show_remote_branch or branch ~= remote_branch
+ if not show_remote then
+ return nil
+ end
+
+ local remote_branch_label = config.always_show_remote_repo and (remote_repo .. "/" .. remote_branch)
+ or remote_branch
+
+ return {
+ { config.remote_branch_prefix, nil },
+ { remote_branch_label, theme.remote_branch_color },
+ }
+ end
+
+ local function render_tag()
+ local branch = this.output.branch
+ local tag = this.output.tag
+
+ if not tag then
+ return nil
+ end
+
+ if not branch or config.always_show_tag then
+ return {
+ { config.tag_symbol, nil },
+ { tag, theme.tag_color },
+ }
+ end
+ end
+
+ local function render_commit()
+ local branch = this.output.branch
+ local tag = this.output.tag
+ local commit = this.output.commit
+
+ if not commit then
+ return nil
+ end
+
+ if (not branch and not tag) or config.always_show_commit then
+ return {
+ { config.commit_symbol, nil },
+ { commit, theme.commit_color },
+ }
+ end
+ end
+
+ local function render_behind_ahead_remote()
+ local behind = this.output.behind_remote
+ local ahead = this.output.ahead_remote
+
+ local behind_label = behind
+ and behind > 0
+ and {
+ config.behind_remote_symbol .. (config.show_numbers and behind or ""),
+ theme.behind_remote_color,
+ }
+
+ local ahead_label = ahead
+ and ahead > 0
+ and {
+ config.ahead_remote_symbol .. (config.show_numbers and ahead or ""),
+ theme.ahead_remote_color,
+ }
+
+ if ahead_label and behind_label then
+ return {
+ behind_label,
+ ahead_label,
+ }
+ elseif ahead_label then
+ return ahead_label
+ elseif behind_label then
+ return behind_label
+ else
+ return nil
+ end
+ end
+
+ local function render_stashes()
+ local stashes_count = this.output.stashes
+
+ if not stashes_count then
+ return nil
+ end
+
+ local stashes_label = config.stashes_symbol
+
+ if config.show_numbers then
+ stashes_label = stashes_label .. stashes_count
+ end
+
+ return { stashes_label, theme.stashes_color }
+ end
+
+ local function render_state()
+ local state_prefix = this.output.state_prefix
+ local unmerged_count = this.output.unmerged_count
+ local rebase_done = this.output.rebase_done
+ local rebase_total = this.output.rebase_total
+
+ if state_prefix then
+ local state_label = ""
+ if config.show_state_prefix then
+ state_label = state_prefix
+ if state_prefix == "rebase-i" and config.show_numbers and rebase_done and rebase_total then
+ state_label = state_label .. " " .. rebase_done .. "/" .. rebase_total
+ end
+ end
+
+ local unmerged_label = ""
+ local symbol = ""
+
+ if unmerged_count and unmerged_count > 0 then
+ symbol = " " .. config.state_symbol
+ if config.show_numbers then
+ unmerged_label = tostring(unmerged_count)
+ end
+ end
+
+ return { state_label .. symbol .. unmerged_label, theme.state_color }
+ end
+ return nil
+ end
+
+ local function render_staged()
+ local staged = this.output.staged
+
+ if staged then
+ local staged_label = config.staged_symbol
+
+ if config.show_numbers then
+ local filtered_staged = staged:gsub("^[%s]*%b()[%s]*", "")
+
+ local staged_count = 0
+ for line in filtered_staged:gmatch("[^\r\n]+") do
+ if line:match("%S") then
+ staged_count = staged_count + 1
+ end
+ end
+
+ staged_label = staged_label .. staged_count
+ end
+
+ return { staged_label, theme.staged_color }
+ end
+
+ return nil
+ end
+
+ local function render_unstaged()
+ local unstaged = this.output.unstaged
+
+ if unstaged then
+ local unstaged_label = config.unstaged_symbol
+
+ if config.show_numbers then
+ local filtered_unstaged = unstaged:gsub("^[%s]*%b()[\r\n]*", ""):gsub("^[%s]*%b()[\r\n]*", "")
+
+ local unstaged_count = 0
+ for line in filtered_unstaged:gmatch("[^\r\n]+") do
+ if line:match("%S") then
+ unstaged_count = unstaged_count + 1
+ end
+ end
+
+ unstaged_label = unstaged_label .. unstaged_count
+ end
+
+ return { unstaged_label, theme.unstaged_color }
+ end
+
+ return nil
+ end
+
+ local function render_untracked()
+ local untracked = this.output.untracked
+
+ if untracked then
+ local untracked_label = config.untracked_symbol
+
+ if config.show_numbers then
+ local filtered_untracked = untracked:gsub("^[%s]*%b()[\r\n]*", "")
+ local untracked_count = 0
+
+ for line in filtered_untracked:gmatch("[^\r\n]+") do
+ if line:match("%S") then
+ untracked_count = untracked_count + 1
+ end
+ end
+
+ untracked_label = untracked_label .. untracked_count
+ end
+
+ return { untracked_label, theme.untracked_color }
+ end
+
+ return nil
+ end
+
+ function Yatline.coloreds.get:githead()
+ if not this.output then
+ return nil
+ end
+
+ local fn_table = {
+ branch = render_branch,
+ remote_branch = render_remote_branch,
+ tag = render_tag,
+ commit = render_commit,
+ behind_ahead_remote = render_behind_ahead_remote,
+ stashes = render_stashes,
+ state = render_state,
+ staged = render_staged,
+ unstaged = render_unstaged,
+ untracked = render_untracked,
+ }
+
+ local head = {}
+
+ for _, key in ipairs(config.order) do
+ local fn = fn_table[key]
+ local is_shown = config["show_" .. key]
+
+ if fn and is_shown then
+ local value = fn()
+ if value then
+ if #head ~= 0 and head[#head][1] ~= " " and head[#head][2] ~= "black" then
+ table.insert(head, { " ", "black" })
+ end
+ if type(value[1]) == "table" then
+ table.move(value, 1, #value, #head + 1, head)
+ else
+ table.insert(head, value)
+ end
+ end
+ end
+ end
+
+ if #head ~= 0 then
+ return head
+ else
+ return nil
+ end
+ end
+
+ local callback = function()
+ local cwd = cx.active.current.cwd
+ ya.emit("plugin", {
+ this._id,
+ ya.quote(tostring(cwd), true),
+ })
+ end
+
+ ps.sub("cd", callback)
+ ps.sub("rename", callback)
+ ps.sub("bulk", callback)
+ ps.sub("move", callback)
+ ps.sub("trash", callback)
+ ps.sub("delete", callback)
+ ps.sub("tab", callback)
+ end,
+
+ entry = function(_, job)
+ local args = job.args or job
+
+ ---@class GitStatusData
+ ---@field ahead_remote? integer
+ ---@field behind_remote? integer
+ ---@field branch? string
+ ---@field commit? string
+ ---@field rebase_done? integer
+ ---@field remote_branch? string
+ ---@field remote_repo? string
+ ---@field staged? string
+ ---@field stashes? integer
+ ---@field state_prefix? "bisect"|"cherry"|"merge"|"rebase-i"|"revert"
+ ---@field tag? string
+ ---@field unmerged_count? integer
+ ---@field unstaged? string
+ ---@field untracked? string
+ local data = {}
+
+ --- @param status string
+ local get_behind_ahead_remote = function(status)
+ local diverged_ahead, diverged_behind = status:match("have (%d+) and (%d+) different")
+ if diverged_ahead and diverged_behind then
+ data.behind_remote = tonumber(diverged_behind)
+ data.ahead_remote = tonumber(diverged_ahead)
+ else
+ local behind_remote = status:match("behind %S+ by (%d+) commits?")
+ local ahead_remote = status:match("ahead of %S+ by (%d+) commits?")
+
+ data.behind_remote = tonumber(behind_remote)
+ data.ahead_remote = tonumber(ahead_remote)
+ end
+ end
+
+ --- @param status string
+ local get_branch = function(status)
+ data.branch = status:match("On branch (%S+)")
+ end
+
+ --- @param status string
+ local get_stashes = function(status)
+ data.stashes = tonumber(status:match("Your stash currently has (%d+)"))
+ end
+
+ --- @param status string
+ local get_state = function(status)
+ local unmerged = status:match("Unmerged paths:%s*(.-)%s*\n\n")
+ if unmerged then
+ local filtered_unmerged = unmerged:gsub("^[%s]*%b()[%s]*", ""):gsub("^[%s]*%b()[%s]*", "")
+ local count = 0
+ for line in filtered_unmerged:gmatch("[^\r\n]+") do
+ if line:match("%S") then
+ count = count + 1
+ end
+ end
+ data.unmerged_count = count
+ end
+
+ if status:find("git merge") then
+ data.state_prefix = "merge"
+ elseif status:find("git cherry%-pick") then
+ data.state_prefix = "cherry"
+ elseif status:find("git rebase") then
+ data.state_prefix = "rebase-i"
+ local commands_done = status:match("%((%d+) commands? done%)")
+ if commands_done then
+ data.rebase_done = commands_done
+ if not data.unmerged_count then
+ data.rebase_total = commands_done
+ else
+ data.rebase_total = data.unmerged_count
+ end
+ end
+ elseif status:find("git revert") then
+ data.state_prefix = "revert"
+ elseif status:find("git bisect") then
+ data.state_prefix = "bisect"
+ end
+ end
+
+ --- @param status string
+ local get_staged = function(status)
+ data.staged = status:match("Changes to be committed:%s*(.-)%s*\n\n")
+ end
+
+ --- @param status string
+ local get_unstaged = function(status)
+ data.unstaged = status:match("Changes not staged for commit:%s*(.-)%s*\n\n")
+ end
+
+ --- @param status string
+ local get_untracked = function(status)
+ data.untracked = status:match("Untracked files:%s*(.-)%s*\n\n")
+ end
+
+ local get_status = function()
+ local cmd = Command("git")
+ :arg({
+ "status",
+ "--ignore-submodules=dirty",
+ "--branch",
+ "--show-stash",
+ "--ahead-behind",
+ })
+ :cwd(args[1])
+ :env("LANGUAGE", "en_US.UTF-8")
+ :stdout(Command.PIPED)
+ local cmd_output = cmd:output()
+
+ if cmd_output then
+ local status = cmd_output.stdout
+
+ get_branch(status)
+ get_behind_ahead_remote(status)
+ get_stashes(status)
+ get_state(status)
+ get_staged(status)
+ get_unstaged(status)
+ get_untracked(status)
+ end
+ end
+
+ local get_remote_branch = function()
+ local cmd = Command("git")
+ :arg({
+ "rev-parse",
+ "--abbrev-ref",
+ "--symbolic-full-name",
+ "@{upstream}",
+ })
+ :cwd(args[1])
+ :env("LANGUAGE", "en_US.UTF-8")
+ :stdout(Command.PIPED)
+ local cmd_output = cmd:output()
+
+ if cmd_output then
+ local remote_branch = cmd_output.stdout
+
+ data.remote_branch = remote_branch:gsub("[\r\n]", ""):match("^[^/]+/(.+)")
+ data.remote_repo = remote_branch:gsub("[\r\n]", ""):match("^([^/]+)/")
+ end
+ end
+
+ --- @param log string
+ local get_tag = function(log)
+ data.tag = log:match("tag: ([^, )]+)")
+ end
+
+ --- @param log string
+ local get_commit = function(log)
+ data.commit = log:match("^commit%s+([a-f0-9]+)")
+ end
+
+ local get_git_log = function()
+ local cmd = Command("git")
+ :arg({ "log", "--format=commit %h%d", "-n", "1" })
+ :cwd(args[1])
+ :env("LANGUAGE", "en_US.UTF-8")
+ :stdout(Command.PIPED)
+ local cmd_output = cmd:output()
+
+ if cmd_output then
+ local log = cmd_output.stdout
+
+ get_tag(log)
+ get_commit(log)
+ end
+ end
+
+ get_status()
+ get_remote_branch()
+ get_git_log()
+
+ save(args[1], data)
+ end,
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage