diff --git a/modules/home/programs/terminal/nvim/config/lua/git-config.lua b/modules/home/programs/terminal/nvim/config/lua/git-config.lua index cfce6a8..43c21bd 100644 --- a/modules/home/programs/terminal/nvim/config/lua/git-config.lua +++ b/modules/home/programs/terminal/nvim/config/lua/git-config.lua @@ -18,50 +18,107 @@ require("gitsigns").setup({ }) +local function git_cmd(dir, cmd) + return vim.fn.system("git -C " .. vim.fn.escape(dir, " ") .. " " .. cmd) +end + +local function git_cmd_list(dir, cmd) + return vim.fn.systemlist("git -C " .. vim.fn.escape(dir, " ") .. " " .. cmd) +end + +local function yank(message) + vim.fn.setreg("+", message) + vim.notify("Copied:\n\t" .. message, vim.log.levels.INFO) +end + local function get_git_info() local abs_path = vim.fn.expand("%:p") - local git_root = vim.fn.systemlist( - "git -C " .. vim.fn.escape(vim.fn.fnamemodify(abs_path, ":h"), " ") .. " rev-parse --show-toplevel" - )[1] + local file_dir = vim.fn.fnamemodify(abs_path, ":h") + local git_root = git_cmd_list(file_dir, "rev-parse --show-toplevel")[1] if vim.v.shell_error ~= 0 then + vim.notify("Failed to get git info", vim.log.levels.ERROR) return end - local git_repo = vim.fn.system("git remote get-url origin"):match("([^/:]+/[^/.]+)%.?[^/]*$"):gsub("\n", "") - local git_branch = vim.fn.system("git rev-parse --abbrev-ref HEAD"):gsub("\n", "") + local git_repo = git_cmd(file_dir, "remote get-url origin"):match("([^/:]+/[^/.]+)%.?[^/]*$"):gsub("\n", "") + local git_branch = git_cmd(file_dir, "rev-parse --abbrev-ref HEAD"):gsub("\n", "") return { + abs_path = abs_path, + file_dir = file_dir, file = vim.fn.fnamemodify(abs_path, ":s?" .. git_root .. "/??"), branch = git_branch, repo = git_repo, } end -local function copy_git_link() - local git_info = get_git_info() - if git_info == nil then - vim.notify("Failed to get git info", vim.log.levels.ERROR) +local function get_blame_info(file_dir, abs_path, line_num) + local blame_output = git_cmd_list( + file_dir, + "blame -L " .. line_num .. "," .. line_num .. " --porcelain " .. vim.fn.escape(abs_path, " ") + ) + + if vim.v.shell_error ~= 0 then + vim.notify("Failed to get git blame", vim.log.levels.ERROR) return end + local commit_hash = blame_output[1]:match("^(%S+)") + local original_line = blame_output[1]:match("^%S+ (%d+)") + local commit_file = nil + + for _, line in ipairs(blame_output) do + local f = line:match("^filename (.+)$") + if f then + commit_file = f + break + end + end + + if not commit_hash or not commit_file then + vim.notify("Failed to extract commit info", vim.log.levels.ERROR) + return + end + + return { + hash = commit_hash, + line = tonumber(original_line), + file = commit_file, + file_hash = vim.fn.system("echo -n " .. vim.fn.shellescape(commit_file) .. " | sha256sum | cut -d' ' -f1"):gsub("\n", + ""), + } +end + +local function copy_git_link() + local git_info = get_git_info() + if not git_info then return end + local start_line = vim.fn.line("v") local end_line = vim.fn.line(".") - local message = string.format( + yank(string.format( "https://github.com/%s/blob/%s/%s#L%d-L%d", - git_info.repo, - git_info.branch, - git_info.file, - start_line, - end_line - ) - vim.fn.setreg("+", message) - vim.notify("Copied:\n\t" .. message, vim.log.levels.INFO) + git_info.repo, git_info.branch, git_info.file, start_line, end_line + )) end --- Create KeyMaps -vim.keymap.set("v", "gy", function() copy_git_link() end, { desc = "Copy GitHub Link" }) +local function copy_commit_url() + local git_info = get_git_info() + if not git_info then return end + + local blame = get_blame_info(git_info.file_dir, git_info.abs_path, vim.fn.line(".")) + if not blame then return end + + yank(string.format( + "https://github.com/%s/commit/%s#diff-%sR%d", + git_info.repo, blame.hash, blame.file_hash, blame.line + )) +end + +-- Keymaps +vim.keymap.set("v", "gy", copy_git_link, { desc = "Copy GitHub Link" }) +vim.keymap.set("n", "gC", copy_commit_url, { desc = "Copy Commit URL" }) vim.keymap.set('n', 'go', 'DiffviewOpen', { desc = "Open Diff - Current" }) vim.keymap.set('n', 'gO', 'DiffviewOpen origin/main...HEAD', { desc = "Open Diff - Main" }) vim.keymap.set('n', 'gh', 'DiffviewFileHistory', { desc = "Diff History" })