feat(git): replace vim-fugitive with custom git module
This commit is contained in:
+216
@@ -0,0 +1,216 @@
|
||||
local log = require("log")
|
||||
local repo = require("git.repo")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@class ow.Git.SplitHandler
|
||||
---@field ft string
|
||||
---@field needs_ref boolean?
|
||||
|
||||
---@type table<string, ow.Git.SplitHandler>
|
||||
local SPLIT_HANDLERS = {
|
||||
log = { ft = "gitlog" },
|
||||
show = { ft = "git", needs_ref = true },
|
||||
["cat-file"] = { ft = "git", needs_ref = true },
|
||||
diff = { ft = "diff" },
|
||||
}
|
||||
|
||||
---@type string[]?
|
||||
local cached_cmds
|
||||
|
||||
---@return string[]
|
||||
local function git_cmds()
|
||||
if cached_cmds then
|
||||
return cached_cmds
|
||||
end
|
||||
local result = vim.system(
|
||||
{ "git", "--list-cmds=main,others,alias" },
|
||||
{ text = true }
|
||||
)
|
||||
:wait()
|
||||
cached_cmds = {}
|
||||
if result.code == 0 then
|
||||
for line in (result.stdout or ""):gmatch("[^\r\n]+") do
|
||||
if line ~= "" then
|
||||
table.insert(cached_cmds, line)
|
||||
end
|
||||
end
|
||||
table.sort(cached_cmds)
|
||||
end
|
||||
return cached_cmds
|
||||
end
|
||||
|
||||
---@param content string
|
||||
---@return string[]
|
||||
local function split_lines(content)
|
||||
local lines = vim.split(content, "\n", { plain = true, trimempty = false })
|
||||
if #lines > 0 and lines[#lines] == "" then
|
||||
table.remove(lines)
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
---@param args string[]
|
||||
---@param start integer
|
||||
---@return string?
|
||||
local function first_positional(args, start)
|
||||
for i = start, #args do
|
||||
local a = args[i]
|
||||
if a:sub(1, 1) ~= "-" then
|
||||
return a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param worktree string
|
||||
---@param args string[]
|
||||
---@param conf ow.Git.SplitHandler
|
||||
local function run_in_split(worktree, args, conf)
|
||||
vim.cmd("new")
|
||||
local buf = vim.api.nvim_get_current_buf()
|
||||
vim.bo[buf].buftype = "nofile"
|
||||
vim.bo[buf].bufhidden = "hide"
|
||||
vim.bo[buf].swapfile = false
|
||||
vim.bo[buf].modifiable = false
|
||||
vim.b[buf].git_worktree = worktree
|
||||
if conf.needs_ref then
|
||||
local user_ref = first_positional(args, 2) or "HEAD"
|
||||
local sha = repo.rev_parse(worktree, user_ref, true) or user_ref
|
||||
vim.b[buf].git_ref = sha
|
||||
vim.b[buf].git_parent_ref =
|
||||
repo.rev_parse(worktree, user_ref .. "^", true)
|
||||
pcall(vim.api.nvim_buf_set_name, buf, "git://" .. sha .. "/")
|
||||
end
|
||||
vim.bo[buf].filetype = conf.ft
|
||||
|
||||
local cmd = { "git" }
|
||||
vim.list_extend(cmd, args)
|
||||
vim.system(cmd, { cwd = worktree, text = true }, function(obj)
|
||||
vim.schedule(function()
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
local content = (obj.stdout or "") .. (obj.stderr or "")
|
||||
vim.bo[buf].modifiable = true
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, split_lines(content))
|
||||
vim.bo[buf].modifiable = false
|
||||
vim.bo[buf].modified = false
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param worktree string
|
||||
---@param args string[]
|
||||
local function run_to_messages(worktree, args)
|
||||
local cmd = { "git" }
|
||||
vim.list_extend(cmd, args)
|
||||
vim.system(cmd, { cwd = worktree, text = true }, function(obj)
|
||||
vim.schedule(function()
|
||||
local out = vim.trim(obj.stdout or "")
|
||||
local err = vim.trim(obj.stderr or "")
|
||||
local chunks = {}
|
||||
if out ~= "" then
|
||||
table.insert(chunks, { out })
|
||||
end
|
||||
if err ~= "" then
|
||||
if #chunks > 0 then
|
||||
table.insert(chunks, { "\n" })
|
||||
end
|
||||
table.insert(chunks, { err, "ErrorMsg" })
|
||||
end
|
||||
if #chunks == 0 and obj.code ~= 0 then
|
||||
table.insert(
|
||||
chunks,
|
||||
{ "git exited " .. tostring(obj.code), "ErrorMsg" }
|
||||
)
|
||||
end
|
||||
if #chunks > 0 then
|
||||
vim.api.nvim_echo(chunks, true, {})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param args string[]
|
||||
---@param flag string
|
||||
---@return boolean
|
||||
local function has_flag(args, flag)
|
||||
for _, a in ipairs(args) do
|
||||
if a == flag then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param args string[]
|
||||
---@return boolean
|
||||
local function has_message(args)
|
||||
for _, a in ipairs(args) do
|
||||
if
|
||||
a == "-m"
|
||||
or a == "--message"
|
||||
or a:match("^%-%-message=")
|
||||
or a:match("^%-m")
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param args string[]
|
||||
function M.run(args)
|
||||
local path = vim.api.nvim_buf_get_name(0)
|
||||
if path == "" then
|
||||
path = vim.fn.getcwd()
|
||||
end
|
||||
local _, worktree = repo.resolve(path)
|
||||
if not worktree then
|
||||
log.warning("not in a git repository")
|
||||
return
|
||||
end
|
||||
|
||||
local sub = args[1]
|
||||
if sub == "commit" and not has_message(args) then
|
||||
require("git.commit").commit({ amend = has_flag(args, "--amend") })
|
||||
return
|
||||
end
|
||||
|
||||
local conf = sub and SPLIT_HANDLERS[sub]
|
||||
if conf then
|
||||
run_in_split(worktree, args, conf)
|
||||
else
|
||||
run_to_messages(worktree, args)
|
||||
end
|
||||
end
|
||||
|
||||
---@param arg_lead string
|
||||
---@param cmd_line string
|
||||
---@return string[]
|
||||
function M.complete(arg_lead, cmd_line, _)
|
||||
local rest = cmd_line:gsub("^%s*%S+%s*", "", 1)
|
||||
local words = vim.split(rest, "%s+", { trimempty = false })
|
||||
if #words > 1 then
|
||||
return {}
|
||||
end
|
||||
local matches = {}
|
||||
for _, c in ipairs(git_cmds()) do
|
||||
if c:sub(1, #arg_lead) == arg_lead then
|
||||
table.insert(matches, c)
|
||||
end
|
||||
end
|
||||
return matches
|
||||
end
|
||||
|
||||
function M.setup()
|
||||
vim.api.nvim_create_user_command("G", function(opts)
|
||||
M.run(opts.fargs)
|
||||
end, {
|
||||
nargs = "*",
|
||||
complete = M.complete,
|
||||
desc = "Run git",
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user