From af7e187aa9416785c75cf9c905e98af88081d638 Mon Sep 17 00:00:00 2001 From: Oscar Wallberg Date: Thu, 30 Apr 2026 18:13:49 +0200 Subject: [PATCH] feat(git): :Gedit, path/stage completion, status.statusline() --- lua/core/options.lua | 2 +- lua/git/init.lua | 14 +++++-- lua/git/repo.lua | 89 ++++++++++++++++++++++++++++++++++++++++++-- lua/git/status.lua | 5 +++ 4 files changed, 102 insertions(+), 8 deletions(-) diff --git a/lua/core/options.lua b/lua/core/options.lua index f40e3ca..196993c 100644 --- a/lua/core/options.lua +++ b/lua/core/options.lua @@ -77,7 +77,7 @@ vim.opt.inccommand = "split" vim.opt.winborder = "rounded" vim.opt.confirm = true -vim.opt.statusline = "%{expand('%:.')} %{%v:lua.require('git').status()%} %3(%m%)" +vim.opt.statusline = "%{expand('%:.')} %{%v:lua.require('git.status').statusline()%} %3(%m%)" .. " %=" .. " %{%v:lua.vim.diagnostic.status()%}" .. " %{&filetype} %{&fileencoding} %{&fileformat}" diff --git a/lua/git/init.lua b/lua/git/init.lua index 84a844e..5b96cf5 100644 --- a/lua/git/init.lua +++ b/lua/git/init.lua @@ -13,10 +13,6 @@ local HIGHLIGHTS = { local M = {} -function M.status() - return vim.b.git_status or "" -end - function M.init() for name, link in pairs(HIGHLIGHTS) do vim.api.nvim_set_hl(0, name, { link = link, default = true }) @@ -103,6 +99,16 @@ function M.init() complete = complete_rev, desc = "Diff against (horizontal split)", }) + vim.api.nvim_create_user_command("Gedit", function(opts) + vim.cmd.edit({ + args = { "git://" .. opts.args }, + magic = { file = false }, + }) + end, { + nargs = 1, + complete = complete_rev, + desc = "Edit a git object ()", + }) vim.api.nvim_create_user_command("G", function(opts) require("git.cmd").run(opts.fargs) diff --git a/lua/git/repo.lua b/lua/git/repo.lua index 25abd07..49edb0b 100644 --- a/lua/git/repo.lua +++ b/lua/git/repo.lua @@ -98,10 +98,93 @@ function M.complete_rev(arg_lead) if not worktree then return {} end + + local stage, stage_path_lead = arg_lead:match("^:([0-3]):(.*)$") + if stage then + local out = util.exec( + { "git", "ls-files", "--stage" }, + { cwd = worktree, silent = true } + ) + if not out then + return {} + end + local matches = {} + for _, line in ipairs(util.split_lines(out)) do + local row_stage, row_path = line:match("^%S+ %S+ (%d)\t(.*)$") + if + row_stage == stage + and row_path + and row_path:sub(1, #stage_path_lead) == stage_path_lead + then + table.insert(matches, ":" .. stage .. ":" .. row_path) + end + end + return matches + end + + local colon = arg_lead:find(":", 1, true) + if not colon then + local matches = {} + for _, ref in ipairs(M.list_refs(worktree)) do + if ref:sub(1, #arg_lead) == arg_lead then + table.insert(matches, ref) + end + end + return matches + end + + local rev = arg_lead:sub(1, colon - 1) + local path_lead = arg_lead:sub(colon + 1) + local dir, name_lead = path_lead:match("^(.*/)([^/]*)$") + dir = dir or "" + name_lead = name_lead or path_lead + + if rev ~= "" then + local cmd = { "git", "ls-tree", rev } + if dir ~= "" then + table.insert(cmd, dir) + end + local out = util.exec(cmd, { cwd = worktree, silent = true }) + if not out then + return {} + end + local matches = {} + for _, line in ipairs(util.split_lines(out)) do + local typ, full_path = line:match("^%S+ (%S+) %S+\t(.*)$") + if typ and full_path then + local basename = dir == "" and full_path + or full_path:sub(#dir + 1) + if typ == "tree" then + basename = basename .. "/" + end + if basename:sub(1, #name_lead) == name_lead then + table.insert(matches, rev .. ":" .. dir .. basename) + end + end + end + return matches + end + + local cmd = { "git", "ls-files" } + if dir ~= "" then + table.insert(cmd, dir) + end + local out = util.exec(cmd, { cwd = worktree, silent = true }) + if not out then + return {} + end local matches = {} - for _, ref in ipairs(M.list_refs(worktree)) do - if ref:sub(1, #arg_lead) == arg_lead then - table.insert(matches, ref) + local seen = {} + for _, full_path in ipairs(util.split_lines(out)) do + local rel = dir == "" and full_path or full_path:sub(#dir + 1) + local slash = rel:find("/", 1, true) + local segment = slash and rel:sub(1, slash) or rel + if + not seen[segment] + and segment:sub(1, #name_lead) == name_lead + then + seen[segment] = true + table.insert(matches, ":" .. dir .. segment) end end return matches diff --git a/lua/git/status.lua b/lua/git/status.lua index 9826624..7573874 100644 --- a/lua/git/status.lua +++ b/lua/git/status.lua @@ -49,4 +49,9 @@ function M.format(code) return string.format("%%#%s#%s%%*", hl, char) end +---@return string +function M.statusline() + return vim.b.git_status or "" +end + return M