feat(git): add in-house hunks module, replace gitsigns.nvim
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
local Revision = require("git.core.revision")
|
||||
local object = require("git.object")
|
||||
local repo = require("git.core.repo")
|
||||
local util = require("git.core.util")
|
||||
|
||||
local M = {}
|
||||
|
||||
---@class ow.Git.Diffsplit.OpenOpts
|
||||
---@field target string?
|
||||
---@field mods vim.api.keyset.cmd.mods?
|
||||
|
||||
---@param cur_buf integer
|
||||
---@return string? target
|
||||
---@return string? err
|
||||
local function infer_target(cur_buf)
|
||||
local cur_name = vim.api.nvim_buf_get_name(cur_buf)
|
||||
local cur_rev = object.parse_uri(cur_name)
|
||||
if cur_rev then
|
||||
local r = repo.resolve(cur_buf)
|
||||
if not r then
|
||||
return nil, "git URI buffer has no worktree"
|
||||
end
|
||||
if not cur_rev.path then
|
||||
return nil, "git URI has no path, cannot diff against worktree"
|
||||
end
|
||||
local worktree_path = vim.fs.joinpath(r.worktree, cur_rev.path)
|
||||
if not vim.uv.fs_stat(worktree_path) then
|
||||
return nil, "worktree file does not exist: " .. cur_rev.path
|
||||
end
|
||||
return worktree_path, nil
|
||||
end
|
||||
|
||||
if cur_name == "" then
|
||||
return nil, "no file in current buffer"
|
||||
end
|
||||
if vim.bo[cur_buf].buftype ~= "" then
|
||||
return nil, "cannot diff this buffer (not a worktree file)"
|
||||
end
|
||||
local resolved = vim.fn.resolve(cur_name)
|
||||
local r = repo.resolve(resolved)
|
||||
if not r then
|
||||
return nil, "not in a git repository"
|
||||
end
|
||||
local rel = vim.fs.relpath(r.worktree, resolved)
|
||||
if not rel then
|
||||
return nil, "current buffer is outside the worktree"
|
||||
end
|
||||
return object.format_uri(Revision.new({ stage = 0, path = rel })), nil
|
||||
end
|
||||
|
||||
---@param target string
|
||||
---@param cur_buf integer
|
||||
---@return string? resolved
|
||||
---@return string? err
|
||||
local function resolve_target(target, cur_buf)
|
||||
if vim.startswith(target, object.URI_PREFIX) then
|
||||
return target, nil
|
||||
end
|
||||
if vim.fn.filereadable(target) == 1 then
|
||||
return target, nil
|
||||
end
|
||||
local cur_name = vim.api.nvim_buf_get_name(cur_buf)
|
||||
local cur_rev = object.parse_uri(cur_name)
|
||||
local r, rel
|
||||
if cur_rev and cur_rev.path then
|
||||
r = repo.resolve(cur_buf)
|
||||
rel = cur_rev.path
|
||||
elseif cur_name ~= "" then
|
||||
local resolved = vim.fn.resolve(cur_name)
|
||||
r = repo.resolve(resolved)
|
||||
if r then
|
||||
rel = vim.fs.relpath(r.worktree, resolved)
|
||||
end
|
||||
end
|
||||
if not r then
|
||||
return nil, "not in a git repository"
|
||||
end
|
||||
if not rel then
|
||||
return nil, "current buffer has no path"
|
||||
end
|
||||
if not r:rev_parse(target, true) then
|
||||
return nil, "invalid rev: " .. target
|
||||
end
|
||||
return object.format_uri(Revision.new({ base = target, path = rel })), nil
|
||||
end
|
||||
|
||||
---@param cur_buf integer
|
||||
---@param target string
|
||||
---@return 'aboveleft'|'belowright'|nil
|
||||
local function default_split(cur_buf, target)
|
||||
local cur_rev = object.parse_uri(vim.api.nvim_buf_get_name(cur_buf))
|
||||
local target_rev = object.parse_uri(target)
|
||||
if not cur_rev and target_rev then
|
||||
return "aboveleft"
|
||||
end
|
||||
if cur_rev and not target_rev then
|
||||
return "belowright"
|
||||
end
|
||||
if cur_rev and target_rev then
|
||||
if cur_rev.stage == 0 and target_rev.base then
|
||||
return "aboveleft"
|
||||
end
|
||||
if cur_rev.base and target_rev.stage == 0 then
|
||||
return "belowright"
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
---@param opts? ow.Git.Diffsplit.OpenOpts
|
||||
function M.open(opts)
|
||||
opts = opts or {}
|
||||
local cur_buf = vim.api.nvim_get_current_buf()
|
||||
local target, err
|
||||
if opts.target then
|
||||
target, err = resolve_target(opts.target, cur_buf)
|
||||
else
|
||||
target, err = infer_target(cur_buf)
|
||||
end
|
||||
if not target then
|
||||
util.error("%s", err or "no diff target")
|
||||
return
|
||||
end
|
||||
local mods = opts.mods
|
||||
if not mods or mods.split == nil then
|
||||
local placement = default_split(cur_buf, target)
|
||||
if placement then
|
||||
mods = vim.tbl_extend("force", mods or {}, { split = placement })
|
||||
end
|
||||
end
|
||||
vim.cmd.diffsplit({ args = { target }, mods = mods })
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user