local repo = require("git.core.repo") local util = require("git.core.util") local M = {} local LOG_FORMAT = "%h %ad {%an}%d %s" local cr = vim.api.nvim_replace_termcodes("", true, false, true) ---@param buf integer ---@return boolean opened local function open_under_cursor(buf) local r = repo.resolve(buf) -- Anchor past the leading graph chars (matches the leading sha column, -- not any hex word that happens to appear later in the subject). local sha = r and vim.api.nvim_get_current_line():match("^[*|/\\_ ]*(%x%x%x%x%x%x%x+)") if not sha then return false end ---@cast r -nil require("git.object").open(r, sha, { split = false }) return true end ---@param buf integer local function attach_dispatch(buf) vim.keymap.set("n", "", function() if not open_under_cursor(buf) then vim.api.nvim_feedkeys(cr, "n", false) end end, { buffer = buf, silent = true, desc = "Open commit" }) vim.keymap.set("n", "gd", function() open_under_cursor(buf) end, { buffer = buf, silent = true, desc = "Open commit" }) end ---@param worktree string ---@param max_count integer? ---@return string? local function fetch(worktree, max_count) local args = { "log", "--graph", "--all", "--decorate", "--date=short", "--format=format:" .. LOG_FORMAT, } if max_count then table.insert(args, "--max-count=" .. max_count) end return util.git(args, { cwd = worktree }) end ---@type table -- worktree -> max_count local max_counts = {} ---@param buf integer ---@param r ow.Git.Repo local function populate(buf, r) local stdout = fetch(r.worktree, max_counts[r.worktree]) if not stdout then return end util.set_buf_lines(buf, 0, -1, util.split_lines(stdout)) end ---@class ow.Git.Log.OpenOpts ---@field max_count integer? ---@type table M.opt_parsers = { max_count = tonumber, } ---@param opts ow.Git.Log.OpenOpts? function M.open(opts) opts = opts or {} local r = repo.resolve() if not r then util.error("not in a git repository") return end max_counts[r.worktree] = opts.max_count local buf = vim.fn.bufadd(r.worktree .. "/GitLog") local visible = vim.fn.bufwinid(buf) if visible ~= -1 then vim.api.nvim_set_current_win(visible) populate(buf, r) vim.api.nvim_win_set_cursor(visible, { 1, 0 }) return end vim.fn.bufload(buf) repo.bind(buf, r) util.setup_scratch(buf, { bufhidden = "hide" }) vim.bo[buf].filetype = "gitlog" attach_dispatch(buf) local win = util.place_buf(buf, nil) vim.api.nvim_win_set_cursor(win, { 1, 0 }) populate(buf, r) end ---@param cmd_opts table function M.run_glog(cmd_opts) local parsed = { max_count = 1000 } for _, a in ipairs(cmd_opts.fargs) do local k, v = a:match("^([%w_]+)=(.*)$") if not k then util.error("invalid argument: %s", a) return end ---@cast v -nil local parser = M.opt_parsers[k] if parser then local value = parser(v) if value ~= nil then parsed[k] = value end end end M.open(parsed) end ---@param arg_lead string ---@return string[] function M.complete_glog(arg_lead) local matches = {} for k in pairs(M.opt_parsers) do local prefix = k .. "=" if prefix:sub(1, #arg_lead) == arg_lead then table.insert(matches, prefix) end end table.sort(matches) return matches end return M