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 0000000..73e2df0 --- /dev/null +++ b/.config/yazi/plugins/yatline-githead.yazi/main.lua @@ -0,0 +1,314 @@ +---@diagnostic disable: undefined-global + +local save = ya.sync(function(this, cwd, output) + if cx.active.current.cwd == Url(cwd) then + this.output = output + ya.render() + end +end) + +return { + setup = function(this, options) + options = options or {} + + local config = { + show_branch = options.show_branch == nil and true or options.show_branch, + branch_prefix = options.branch_prefix or "on", + branch_symbol = options.branch_symbol or "", + branch_borders = options.branch_borders or "()", + + commit_symbol = options.commit_symbol or "@", + + show_behind_ahead = options.behind_ahead == nil and true or options.behind_ahead, + behind_symbol = options.behind_symbol or "⇣", + ahead_symbol = options.ahead_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 = { + prefix_color = options.prefix_color or "white", + branch_color = options.branch_color or "blue", + commit_color = options.commit_color or "bright magenta", + behind_color = options.behind_color or "bright magenta", + ahead_color = options.ahead_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 get_branch(status) + local branch = status:match("On branch (%S+)") + + if branch == nil then + local commit = status:match("onto (%S+)") or status:match("detached at (%S+)") + + if commit == nil then + return "" + else + local branch_prefix = config.branch_prefix == "" and " " or " " .. config.branch_prefix .. " " + local commit_prefix = config.commit_symbol == "" and "" or config.commit_symbol + + return { "commit", branch_prefix .. commit_prefix, commit } + end + else + 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", branch_prefix, branch_string } + end + end + + local function get_behind_ahead(status) + local diverged_ahead, diverged_behind = status:match("have (%d+) and (%d+) different") + if diverged_ahead and diverged_behind then + return { " " .. config.behind_symbol .. diverged_behind, config.ahead_symbol .. diverged_ahead } + else + local behind = status:match("behind %S+ by (%d+) commit") + local ahead = status:match("ahead of %S+ by (%d+) commit") + if ahead then + return { "", " " .. config.ahead_symbol .. ahead } + elseif behind then + return { " " .. config.behind_symbol .. behind, "" } + else + return "" + end + end + end + + local function get_stashes(status) + local stashes = tonumber(status:match("Your stash currently has (%S+)")) + + return stashes ~= nil and " " .. config.stashes_symbol .. stashes or "" + end + + local function get_state(status) + local result = status:match("Unmerged paths:%s*(.-)%s*\n\n") + if result then + local filtered_result = result:gsub("^[%s]*%b()[%s]*", ""):gsub("^[%s]*%b()[%s]*", "") + + local unmerged = 0 + for line in filtered_result:gmatch("[^\r\n]+") do + if line:match("%S") then + unmerged = unmerged + 1 + end + end + + local state_name = "" + + if config.show_state_prefix then + if status:find("git merge") then + state_name = "merge " + elseif status:find("git cherry%-pick") then + state_name = "cherry " + elseif status:find("git rebase") then + state_name = "rebase " + + if status:find("done") then + local done = status:match("%((%d+) com.- done%)") or "" + state_name = state_name .. done .. "/" .. unmerged .. " " + end + else + state_name = "" + end + end + + return " " .. state_name .. config.state_symbol .. unmerged + else + return "" + end + end + + local function get_staged(status) + local result = status:match("Changes to be committed:%s*(.-)%s*\n\n") + if result then + local filtered_result = result:gsub("^[%s]*%b()[%s]*", "") + + local staged = 0 + for line in filtered_result:gmatch("[^\r\n]+") do + if line:match("%S") then + staged = staged + 1 + end + end + + return " " .. config.staged_symbol .. staged + else + return "" + end + end + + local function get_unstaged(status) + local result = status:match("Changes not staged for commit:%s*(.-)%s*\n\n") + if result then + local filtered_result = result:gsub("^[%s]*%b()[\r\n]*", ""):gsub("^[%s]*%b()[\r\n]*", "") + + local unstaged = 0 + for line in filtered_result:gmatch("[^\r\n]+") do + if line:match("%S") then + unstaged = unstaged + 1 + end + end + + return " " .. config.unstaged_symbol .. unstaged + else + return "" + end + end + + local function get_untracked(status) + local result = status:match("Untracked files:%s*(.-)%s*\n\n") + if result then + local filtered_result = result:gsub("^[%s]*%b()[\r\n]*", "") + + local untracked = 0 + for line in filtered_result:gmatch("[^\r\n]+") do + if line:match("%S") then + untracked = untracked + 1 + end + end + + return " " .. config.untracked_symbol .. untracked + else + return "" + end + end + + function Header:githead() + local status = this.output + + local branch_array = get_branch(status) + local prefix = ui.Span(config.show_branch and branch_array[2] or ""):fg(theme.prefix_color) + local branch = ui.Span(config.show_branch and branch_array[3] or "") + :fg(branch_array[1] == "commit" and theme.commit_color or theme.branch_color) + local behind_ahead = get_behind_ahead(status) + local behind = ui.Span(config.show_behind_ahead and behind_ahead[1] or ""):fg(theme.behind_color) + local ahead = ui.Span(config.show_behind_ahead and behind_ahead[2] or ""):fg(theme.ahead_color) + local stashes = ui.Span(config.show_stashes and get_stashes(status) or ""):fg(theme.stashes_color) + local state = ui.Span(config.show_state and get_state(status) or ""):fg(theme.state_color) + local staged = ui.Span(config.show_staged and get_staged(status) or ""):fg(theme.staged_color) + local unstaged = ui.Span(config.show_unstaged and get_unstaged(status) or ""):fg(theme.unstaged_color) + local untracked = ui.Span(config.show_untracked and get_untracked(status) or ""):fg(theme.untracked_color) + + return ui.Line({ prefix, branch, behind, ahead, stashes, state, staged, unstaged, untracked }) + end + + Header:children_add(Header.githead, 2000, Header.LEFT) + + local callback = function() + local cwd = cx.active.current.cwd + + if this.cwd ~= cwd then + this.cwd = cwd + ya.manager_emit("plugin", { + this._id, + ya.quote(tostring(cwd), true), + }) + end + end + + ps.sub("cd", callback) + ps.sub("tab", callback) + + if Yatline ~= nil then + function Yatline.coloreds.get:githead() + local status = this.output + local githead = {} + + local branch = config.show_branch and get_branch(status) or "" + if branch ~= nil and branch ~= "" then + table.insert(githead, { branch[2], theme.prefix_color }) + if branch[1] == "commit" then + table.insert(githead, { branch[3], theme.commit_color }) + else + table.insert(githead, { branch[3], theme.branch_color }) + end + end + + local behind_ahead = config.show_behind_ahead and get_behind_ahead(status) or "" + if behind_ahead ~= nil and behind_ahead ~= "" then + if behind_ahead[1] ~= nil and behind_ahead[1] ~= "" then + table.insert(githead, { behind_ahead[1], theme.behind_color }) + elseif behind_ahead[2] ~= nil and behind_ahead[2] ~= "" then + table.insert(githead, { behind_ahead[2], theme.ahead_color }) + end + end + + local stashes = config.show_stashes and get_stashes(status) or "" + if stashes ~= nil and stashes ~= "" then + table.insert(githead, { stashes, theme.stashes_color }) + end + + local state = config.show_state and get_state(status) or "" + if state ~= nil and state ~= "" then + table.insert(githead, { state, theme.state_color }) + end + + local staged = config.show_staged and get_staged(status) or "" + if staged ~= nil and staged ~= "" then + table.insert(githead, { staged, theme.staged_color }) + end + + local unstaged = config.show_unstaged and get_unstaged(status) or "" + if unstaged ~= nil and unstaged ~= "" then + table.insert(githead, { unstaged, theme.unstaged_color }) + end + + local untracked = config.show_untracked and get_untracked(status) or "" + if untracked ~= nil and untracked ~= "" then + table.insert(githead, { untracked, theme.untracked_color }) + end + + if #githead == 0 then + return "" + else + table.insert(githead, { " ", theme.prefix_color }) + return githead + end + end + end + end, + + entry = function(_, job) + local args = job.args or job + local command = Command("git") + :args({ "status", "--ignore-submodules=dirty", "--branch", "--show-stash", "--ahead-behind" }) + :cwd(args[1]) + :env("LANGUAGE", "en_US.UTF-8") + :stdout(Command.PIPED) + local output = command:output() + + if output then + save(args[1], output.stdout) + end + end, +}