refactor(git): drive :G dispatch from buffer content
This commit is contained in:
+9
-113
@@ -7,90 +7,13 @@ local M = {}
|
|||||||
|
|
||||||
---@class ow.Git.Cmd.SplitHandler
|
---@class ow.Git.Cmd.SplitHandler
|
||||||
---@field ft string
|
---@field ft string
|
||||||
---@field needs_rev boolean?
|
|
||||||
---@field on_state? fun(state: ow.Git.Repo.BufState, r: ow.Git.Repo, args: string[])
|
|
||||||
|
|
||||||
---@param r ow.Git.Repo
|
|
||||||
---@param args string[] -- diff args including leading "diff"
|
|
||||||
---@return string left_ref
|
|
||||||
---@return string? right_ref -- nil means worktree
|
|
||||||
local function compute_diff_refs(r, args)
|
|
||||||
local cached = false
|
|
||||||
local positional = {} ---@type string[]
|
|
||||||
local saw_separator = false
|
|
||||||
for i = 2, #args do
|
|
||||||
local a = args[i]
|
|
||||||
if saw_separator then
|
|
||||||
break
|
|
||||||
elseif a == "--" then
|
|
||||||
saw_separator = true
|
|
||||||
elseif a == "--cached" or a == "--staged" then
|
|
||||||
cached = true
|
|
||||||
elseif a:sub(1, 1) ~= "-" then
|
|
||||||
table.insert(positional, a)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function defaults()
|
|
||||||
if cached then
|
|
||||||
return "HEAD", ":"
|
|
||||||
end
|
|
||||||
return ":", nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if #positional == 0 then
|
|
||||||
return defaults()
|
|
||||||
end
|
|
||||||
|
|
||||||
local first = positional[1] --[[@as string]]
|
|
||||||
if #positional == 1 then
|
|
||||||
local lhs, rhs = first:match("^(.-)%.%.%.(.+)$")
|
|
||||||
if lhs then
|
|
||||||
return (lhs ~= "" and lhs or "HEAD"), rhs
|
|
||||||
end
|
|
||||||
lhs, rhs = first:match("^(.-)%.%.(.+)$")
|
|
||||||
if lhs then
|
|
||||||
return (lhs ~= "" and lhs or "HEAD"),
|
|
||||||
(rhs ~= "" and rhs or "HEAD")
|
|
||||||
end
|
|
||||||
if r:rev_parse(first, true) then
|
|
||||||
if cached then
|
|
||||||
return first, ":"
|
|
||||||
end
|
|
||||||
return first, nil
|
|
||||||
end
|
|
||||||
return defaults()
|
|
||||||
end
|
|
||||||
|
|
||||||
local second = positional[2] --[[@as string]]
|
|
||||||
local first_ok = r:rev_parse(first, true) ~= nil
|
|
||||||
if first_ok and r:rev_parse(second, true) then
|
|
||||||
return first, second
|
|
||||||
end
|
|
||||||
if first_ok then
|
|
||||||
if cached then
|
|
||||||
return first, ":"
|
|
||||||
end
|
|
||||||
return first, nil
|
|
||||||
end
|
|
||||||
return defaults()
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type table<string, ow.Git.Cmd.SplitHandler>
|
---@type table<string, ow.Git.Cmd.SplitHandler>
|
||||||
local SPLIT_HANDLERS = {
|
local SPLIT_HANDLERS = {
|
||||||
log = { ft = "git" },
|
log = { ft = "git" },
|
||||||
diff = {
|
diff = { ft = "git" },
|
||||||
ft = "gitdiff",
|
|
||||||
on_state = function(state, r, args)
|
|
||||||
local left, right = compute_diff_refs(r, args)
|
|
||||||
state.left_ref = left
|
|
||||||
state.right_ref = right
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
M._compute_diff_refs = compute_diff_refs
|
|
||||||
|
|
||||||
---@type string[]?
|
---@type string[]?
|
||||||
local cached_cmds
|
local cached_cmds
|
||||||
|
|
||||||
@@ -207,28 +130,16 @@ function M.parse_args(line)
|
|||||||
return args
|
return args
|
||||||
end
|
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 name string
|
---@param name string
|
||||||
---@return integer buf
|
---@return integer buf
|
||||||
local function place_split(name)
|
local function place_split(name)
|
||||||
local buf = vim.fn.bufnr("\\V" .. name)
|
-- bufadd resolves the name the same way nvim_buf_set_name does
|
||||||
if buf == -1 then
|
-- (cwd-prefixing for non-absolute names), so calling it twice with
|
||||||
buf = util.new_scratch({ name = name, bufhidden = "hide" })
|
-- the same name returns the same buffer.
|
||||||
return buf
|
local buf = vim.fn.bufadd(name)
|
||||||
end
|
|
||||||
if not vim.api.nvim_buf_is_loaded(buf) then
|
if not vim.api.nvim_buf_is_loaded(buf) then
|
||||||
vim.fn.bufload(buf)
|
vim.fn.bufload(buf)
|
||||||
|
util.setup_scratch(buf, { bufhidden = "hide" })
|
||||||
end
|
end
|
||||||
local win = vim.fn.bufwinid(buf)
|
local win = vim.fn.bufwinid(buf)
|
||||||
if win ~= -1 then
|
if win ~= -1 then
|
||||||
@@ -278,9 +189,7 @@ end
|
|||||||
---@param args string[]
|
---@param args string[]
|
||||||
---@param conf ow.Git.Cmd.SplitHandler
|
---@param conf ow.Git.Cmd.SplitHandler
|
||||||
local function run_in_split(r, args, conf)
|
local function run_in_split(r, args, conf)
|
||||||
local cmd = { "git" }
|
util.git(args, {
|
||||||
vim.list_extend(cmd, args)
|
|
||||||
util.exec(cmd, {
|
|
||||||
cwd = r.worktree,
|
cwd = r.worktree,
|
||||||
on_done = function(stdout)
|
on_done = function(stdout)
|
||||||
if not stdout then
|
if not stdout then
|
||||||
@@ -291,19 +200,6 @@ local function run_in_split(r, args, conf)
|
|||||||
object.attach_dispatch(buf)
|
object.attach_dispatch(buf)
|
||||||
attach_history_keys(buf)
|
attach_history_keys(buf)
|
||||||
local state = r:state(buf) --[[@as -nil]]
|
local state = r:state(buf) --[[@as -nil]]
|
||||||
state.sha = nil
|
|
||||||
state.parent_sha = nil
|
|
||||||
if conf.needs_rev then
|
|
||||||
local user_rev = first_positional(args, 2) or "HEAD"
|
|
||||||
local sha = r:rev_parse(user_rev, true)
|
|
||||||
if sha then
|
|
||||||
state.sha = sha
|
|
||||||
state.parent_sha = r:rev_parse(user_rev .. "^", true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if conf.on_state then
|
|
||||||
conf.on_state(state, r, args)
|
|
||||||
end
|
|
||||||
vim.bo[buf].filetype = conf.ft
|
vim.bo[buf].filetype = conf.ft
|
||||||
-- Force a new undo block so each rerun is its own undo step.
|
-- Force a new undo block so each rerun is its own undo step.
|
||||||
vim.bo[buf].undolevels = vim.bo[buf].undolevels
|
vim.bo[buf].undolevels = vim.bo[buf].undolevels
|
||||||
@@ -386,7 +282,7 @@ function M.run(args)
|
|||||||
object.open(r, args[2])
|
object.open(r, args[2])
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
run_in_split(r, args, { ft = "git", needs_rev = true })
|
run_in_split(r, args, { ft = "git" })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -395,7 +291,7 @@ function M.run(args)
|
|||||||
object.open(r, args[3])
|
object.open(r, args[3])
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
run_in_split(r, args, { ft = "git", needs_rev = true })
|
run_in_split(r, args, { ft = "git" })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -28,11 +28,13 @@ function M.commit(opts)
|
|||||||
f:close()
|
f:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local buf, win = util.new_scratch({ name = file_path })
|
local buf, win = util.new_scratch({
|
||||||
|
name = file_path,
|
||||||
|
buftype = "acwrite",
|
||||||
|
modifiable = true,
|
||||||
|
})
|
||||||
proxy_buf = buf
|
proxy_buf = buf
|
||||||
proxy_win = win
|
proxy_win = win
|
||||||
vim.bo[buf].buftype = "acwrite"
|
|
||||||
vim.bo[buf].modifiable = true
|
|
||||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||||
vim.bo[buf].modified = false
|
vim.bo[buf].modified = false
|
||||||
vim.bo[buf].filetype = "gitcommit"
|
vim.bo[buf].filetype = "gitcommit"
|
||||||
|
|||||||
@@ -97,10 +97,7 @@ function M.read_uri(buf)
|
|||||||
end
|
end
|
||||||
repo.bind(buf, r)
|
repo.bind(buf, r)
|
||||||
|
|
||||||
vim.bo[buf].swapfile = false
|
util.setup_scratch(buf, { bufhidden = "delete" })
|
||||||
vim.bo[buf].bufhidden = "delete"
|
|
||||||
vim.bo[buf].buftype = "nofile"
|
|
||||||
vim.bo[buf].modifiable = false
|
|
||||||
if vim.bo[buf].filetype ~= "gitlog" then
|
if vim.bo[buf].filetype ~= "gitlog" then
|
||||||
vim.bo[buf].filetype = "gitlog"
|
vim.bo[buf].filetype = "gitlog"
|
||||||
end
|
end
|
||||||
|
|||||||
+63
-69
@@ -182,8 +182,7 @@ local function populate(buf, r, rev, state, rev_sha)
|
|||||||
if rev.path == nil then
|
if rev.path == nil then
|
||||||
local commit_sha = r:rev_parse(rev_str .. "^{commit}", true)
|
local commit_sha = r:rev_parse(rev_str .. "^{commit}", true)
|
||||||
if commit_sha then
|
if commit_sha then
|
||||||
local patch = util.exec({
|
local patch = util.git({
|
||||||
"git",
|
|
||||||
"diff-tree",
|
"diff-tree",
|
||||||
"-p",
|
"-p",
|
||||||
"-m",
|
"-m",
|
||||||
@@ -195,7 +194,6 @@ local function populate(buf, r, rev, state, rev_sha)
|
|||||||
if patch then
|
if patch then
|
||||||
stdout = (stdout:gsub("\n*$", "\n\n")) .. patch
|
stdout = (stdout:gsub("\n*$", "\n\n")) .. patch
|
||||||
end
|
end
|
||||||
state.parent_sha = r:rev_parse(commit_sha .. "^", true)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -220,8 +218,12 @@ function M.read_uri(buf)
|
|||||||
repo.bind(buf, r)
|
repo.bind(buf, r)
|
||||||
local state = r:state(buf) --[[@as -nil]]
|
local state = r:state(buf) --[[@as -nil]]
|
||||||
|
|
||||||
vim.bo[buf].swapfile = false
|
local writable = rev.stage == 0 and rev.path ~= nil
|
||||||
vim.bo[buf].bufhidden = "delete"
|
util.setup_scratch(buf, {
|
||||||
|
bufhidden = "delete",
|
||||||
|
buftype = writable and "acwrite" or "nofile",
|
||||||
|
modifiable = writable,
|
||||||
|
})
|
||||||
|
|
||||||
local rev_sha = r:rev_parse(rev:format(), true)
|
local rev_sha = r:rev_parse(rev:format(), true)
|
||||||
if not rev_sha then
|
if not rev_sha then
|
||||||
@@ -234,15 +236,9 @@ function M.read_uri(buf)
|
|||||||
|
|
||||||
state.immutable = is_immutable_rev(rev)
|
state.immutable = is_immutable_rev(rev)
|
||||||
|
|
||||||
if rev.stage == 0 and rev.path then
|
if writable and not state.index_writer then
|
||||||
vim.bo[buf].buftype = "acwrite"
|
attach_index_writer(buf, r, rev.path --[[@as string]])
|
||||||
if not state.index_writer then
|
state.index_writer = true
|
||||||
attach_index_writer(buf, r, rev.path)
|
|
||||||
state.index_writer = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
vim.bo[buf].buftype = "nofile"
|
|
||||||
vim.bo[buf].modifiable = false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if rev.path then
|
if rev.path then
|
||||||
@@ -283,35 +279,47 @@ local function refresh(buf, r)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param r ow.Git.Repo
|
---@param buf integer
|
||||||
---@param ref string? -- nil = worktree, ":" = index, else commit/sha
|
---@param path string
|
||||||
|
local function set_ft_from_path(buf, path)
|
||||||
|
local ft = vim.filetype.match({ filename = path, buf = buf })
|
||||||
|
if ft then
|
||||||
|
vim.bo[buf].filetype = ft
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param r ow.Git.Repo
|
||||||
|
---@param blob string?
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param blob string? -- diff section blob hash; if zero, side has no content
|
|
||||||
---@return integer?
|
---@return integer?
|
||||||
local function side_buf(r, ref, path, blob)
|
local function side_buf(r, blob, path)
|
||||||
if blob and is_zero(blob) then
|
if not blob or is_zero(blob) then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if ref == nil then
|
local full, status = r:resolve_sha(blob)
|
||||||
local p = vim.fs.joinpath(r.worktree, path)
|
if status == "ambiguous" then
|
||||||
if not vim.uv.fs_stat(p) then
|
util.error("ambiguous blob abbreviation: %s", blob)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
if full then
|
||||||
|
local buf = M.buf_for(r, Revision.new({ base = full }))
|
||||||
|
set_ft_from_path(buf, path)
|
||||||
|
return buf
|
||||||
|
end
|
||||||
|
local p = vim.fs.joinpath(r.worktree, path)
|
||||||
|
if vim.uv.fs_stat(p) then
|
||||||
local buf = vim.fn.bufadd(p)
|
local buf = vim.fn.bufadd(p)
|
||||||
vim.fn.bufload(buf)
|
vim.fn.bufload(buf)
|
||||||
return buf
|
return buf
|
||||||
end
|
end
|
||||||
local rev = ref == ":" and Revision.new({ stage = 0, path = path })
|
return nil
|
||||||
or Revision.new({ base = ref, path = path })
|
|
||||||
return M.buf_for(r, rev)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param r ow.Git.Repo
|
---@param r ow.Git.Repo
|
||||||
---@param ref string?
|
|
||||||
---@param path string
|
|
||||||
---@param blob string?
|
---@param blob string?
|
||||||
local function load_side(r, ref, path, blob)
|
---@param path string
|
||||||
local buf = side_buf(r, ref, path, blob)
|
local function load_side(r, blob, path)
|
||||||
|
local buf = side_buf(r, blob, path)
|
||||||
if not buf then
|
if not buf then
|
||||||
util.error("no content for %s", path)
|
util.error("no content for %s", path)
|
||||||
return
|
return
|
||||||
@@ -321,16 +329,14 @@ local function load_side(r, ref, path, blob)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---@param r ow.Git.Repo
|
---@param r ow.Git.Repo
|
||||||
---@param left_ref string?
|
|
||||||
---@param right_ref string?
|
|
||||||
---@param section ow.Git.DiffSection
|
---@param section ow.Git.DiffSection
|
||||||
local function open_section(r, left_ref, right_ref, section)
|
local function open_section(r, section)
|
||||||
if not section.blob_a or not section.blob_b then
|
if not section.blob_a or not section.blob_b then
|
||||||
util.error("no index line, cannot determine blob SHAs")
|
util.error("no index line, cannot determine blob SHAs")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local left = side_buf(r, left_ref, section.path_a, section.blob_a)
|
local left = side_buf(r, section.blob_a, section.path_a)
|
||||||
local right = side_buf(r, right_ref, section.path_b, section.blob_b)
|
local right = side_buf(r, section.blob_b, section.path_b)
|
||||||
if left and right then
|
if left and right then
|
||||||
require("git.diff").open(left, right, true)
|
require("git.diff").open(left, right, true)
|
||||||
return
|
return
|
||||||
@@ -377,36 +383,24 @@ function M.open_under_cursor()
|
|||||||
local line = vim.api.nvim_get_current_line()
|
local line = vim.api.nvim_get_current_line()
|
||||||
local r = s.repo
|
local r = s.repo
|
||||||
|
|
||||||
if s.sha and not s.left_ref then
|
local sha = line:match("^commit (%x+)$")
|
||||||
local sha = line:match("^commit (%x+)$")
|
or line:match("^parent (%x+)$")
|
||||||
or line:match("^parent (%x+)$")
|
or line:match("^tree (%x+)$")
|
||||||
or line:match("^tree (%x+)$")
|
or line:match("^object (%x+)$")
|
||||||
or line:match("^object (%x+)$")
|
if sha then
|
||||||
if sha then
|
M.open(r, sha, { split = false })
|
||||||
M.open(r, sha, { split = false })
|
return true
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local entry_type, entry_sha, entry_name =
|
|
||||||
line:match("^%d+ (%w+) (%x+)\t(.+)$")
|
|
||||||
if entry_sha then
|
|
||||||
local nav_rev = entry_type == "blob"
|
|
||||||
and Revision.new({ base = s.sha, path = entry_name }):format()
|
|
||||||
or entry_sha
|
|
||||||
M.open(r, nav_rev, { split = false })
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local left_ref, right_ref
|
local entry_type, entry_sha, entry_name =
|
||||||
if s.left_ref then
|
line:match("^%d+ (%w+) (%x+)\t(.+)$")
|
||||||
left_ref = s.left_ref
|
if entry_sha then
|
||||||
right_ref = s.right_ref
|
if entry_type == "blob" then
|
||||||
elseif s.sha then
|
load_side(r, entry_sha, entry_name --[[@as string]])
|
||||||
left_ref = s.parent_sha or "0"
|
else
|
||||||
right_ref = s.sha
|
M.open(r, entry_sha, { split = false })
|
||||||
else
|
end
|
||||||
return false
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local section = diff_section()
|
local section = diff_section()
|
||||||
@@ -415,23 +409,23 @@ function M.open_under_cursor()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if line:match("^diff %-%-git ") then
|
if line:match("^diff %-%-git ") then
|
||||||
open_section(r, left_ref, right_ref, section)
|
open_section(r, section)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if line:match("^%-%-%- ") then
|
if line:match("^%-%-%- ") then
|
||||||
load_side(r, left_ref, section.path_a, section.blob_a)
|
load_side(r, section.blob_a, section.path_a)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if line:match("^%+%+%+ ") then
|
if line:match("^%+%+%+ ") then
|
||||||
load_side(r, right_ref, section.path_b, section.blob_b)
|
load_side(r, section.blob_b, section.path_b)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
local prefix = line:sub(1, 1)
|
local prefix = line:sub(1, 1)
|
||||||
if prefix == "+" then
|
if prefix == "+" then
|
||||||
load_side(r, right_ref, section.path_b, section.blob_b)
|
load_side(r, section.blob_b, section.path_b)
|
||||||
return true
|
return true
|
||||||
elseif prefix == "-" then
|
elseif prefix == "-" then
|
||||||
load_side(r, left_ref, section.path_a, section.blob_a)
|
load_side(r, section.blob_a, section.path_a)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
|
|||||||
+24
-3
@@ -15,9 +15,6 @@ end
|
|||||||
---@class ow.Git.Repo.BufState
|
---@class ow.Git.Repo.BufState
|
||||||
---@field repo ow.Git.Repo
|
---@field repo ow.Git.Repo
|
||||||
---@field sha string?
|
---@field sha string?
|
||||||
---@field parent_sha string?
|
|
||||||
---@field left_ref string?
|
|
||||||
---@field right_ref string?
|
|
||||||
---@field initialized boolean?
|
---@field initialized boolean?
|
||||||
---@field immutable boolean?
|
---@field immutable boolean?
|
||||||
---@field index_writer boolean?
|
---@field index_writer boolean?
|
||||||
@@ -318,6 +315,30 @@ function Repo:rev_parse(rev, short)
|
|||||||
return trimmed ~= "" and trimmed or nil
|
return trimmed ~= "" and trimmed or nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@alias ow.Git.Repo.ResolveStatus "ok"|"ambiguous"|"missing"
|
||||||
|
|
||||||
|
---@param prefix string
|
||||||
|
---@return string? full_sha
|
||||||
|
---@return ow.Git.Repo.ResolveStatus
|
||||||
|
function Repo:resolve_sha(prefix)
|
||||||
|
local result = self:get_cached("resolve:" .. prefix, function(self)
|
||||||
|
local out = util.exec(
|
||||||
|
{ "git", "rev-parse", "--disambiguate=" .. prefix },
|
||||||
|
{ cwd = self.worktree, silent = true }
|
||||||
|
)
|
||||||
|
local trimmed = out and vim.trim(out) or ""
|
||||||
|
if trimmed == "" then
|
||||||
|
return { nil, "missing" }
|
||||||
|
end
|
||||||
|
local lines = util.split_lines(trimmed)
|
||||||
|
if #lines == 1 then
|
||||||
|
return { lines[1], "ok" }
|
||||||
|
end
|
||||||
|
return { nil, "ambiguous" }
|
||||||
|
end)
|
||||||
|
return result[1], result[2]
|
||||||
|
end
|
||||||
|
|
||||||
---@type table<string, ow.Git.Repo> keyed by worktree
|
---@type table<string, ow.Git.Repo> keyed by worktree
|
||||||
local repos = {}
|
local repos = {}
|
||||||
|
|
||||||
|
|||||||
@@ -754,10 +754,7 @@ function M.read_uri(buf)
|
|||||||
end
|
end
|
||||||
repo.bind(buf, r)
|
repo.bind(buf, r)
|
||||||
|
|
||||||
vim.bo[buf].buftype = "nofile"
|
util.setup_scratch(buf, { bufhidden = "hide" })
|
||||||
vim.bo[buf].swapfile = false
|
|
||||||
vim.bo[buf].modifiable = false
|
|
||||||
vim.bo[buf].bufhidden = "hide"
|
|
||||||
vim.bo[buf].filetype = "gitstatus"
|
vim.bo[buf].filetype = "gitstatus"
|
||||||
|
|
||||||
---@type integer?
|
---@type integer?
|
||||||
|
|||||||
+17
-5
@@ -2,16 +2,19 @@ local M = {}
|
|||||||
|
|
||||||
---@class ow.Git.Util.ScratchOpts
|
---@class ow.Git.Util.ScratchOpts
|
||||||
---@field name string?
|
---@field name string?
|
||||||
---@field bufhidden ("hide"|"wipe")?
|
---@field bufhidden ("hide"|"wipe"|"delete")?
|
||||||
|
---@field buftype ("nofile"|"acwrite"|"nowrite")?
|
||||||
|
---@field modifiable boolean?
|
||||||
|
|
||||||
---@param buf integer
|
---@param buf integer
|
||||||
---@param opts ow.Git.Util.ScratchOpts
|
---@param opts ow.Git.Util.ScratchOpts
|
||||||
local function setup_scratch(buf, opts)
|
function M.setup_scratch(buf, opts)
|
||||||
vim.bo[buf].buftype = "nofile"
|
vim.bo[buf].buftype = opts.buftype or "nofile"
|
||||||
vim.bo[buf].bufhidden = opts.bufhidden or "wipe"
|
vim.bo[buf].bufhidden = opts.bufhidden or "wipe"
|
||||||
vim.bo[buf].swapfile = false
|
vim.bo[buf].swapfile = false
|
||||||
vim.bo[buf].modifiable = false
|
vim.bo[buf].modifiable = opts.modifiable == true
|
||||||
vim.bo[buf].modified = false
|
vim.bo[buf].modified = false
|
||||||
|
vim.bo[buf].buflisted = false
|
||||||
if opts.name then
|
if opts.name then
|
||||||
pcall(vim.api.nvim_buf_set_name, buf, opts.name)
|
pcall(vim.api.nvim_buf_set_name, buf, opts.name)
|
||||||
end
|
end
|
||||||
@@ -52,7 +55,7 @@ end
|
|||||||
function M.new_scratch(opts)
|
function M.new_scratch(opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local buf = vim.api.nvim_create_buf(false, true)
|
local buf = vim.api.nvim_create_buf(false, true)
|
||||||
setup_scratch(buf, opts)
|
M.setup_scratch(buf, opts)
|
||||||
return buf, M.place_buf(buf, opts.split)
|
return buf, M.place_buf(buf, opts.split)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -188,6 +191,15 @@ end
|
|||||||
---@field silent boolean?
|
---@field silent boolean?
|
||||||
---@field on_done fun(stdout: string?)?
|
---@field on_done fun(stdout: string?)?
|
||||||
|
|
||||||
|
---@param args string[]
|
||||||
|
---@param opts ow.Git.Util.ExecOpts?
|
||||||
|
---@return string?
|
||||||
|
function M.git(args, opts)
|
||||||
|
local cmd = { "git" }
|
||||||
|
vim.list_extend(cmd, args)
|
||||||
|
return M.exec(cmd, opts)
|
||||||
|
end
|
||||||
|
|
||||||
---@param cmd string[]
|
---@param cmd string[]
|
||||||
---@param opts ow.Git.Util.ExecOpts?
|
---@param opts ow.Git.Util.ExecOpts?
|
||||||
---@return string?
|
---@return string?
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
if exists("b:current_syntax")
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
runtime! syntax/git.vim
|
|
||||||
|
|
||||||
let b:current_syntax = "gitdiff"
|
|
||||||
@@ -234,90 +234,3 @@ t.test("complete unknown subcommand falls back to tracked paths", function()
|
|||||||
local matches = cmd.complete("", "G nonexistent ", 14)
|
local matches = cmd.complete("", "G nonexistent ", 14)
|
||||||
eq_sorted(matches, { "a", "b" })
|
eq_sorted(matches, { "a", "b" })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
t.test("compute_diff_refs default is index vs worktree", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right = cmd._compute_diff_refs(r, { "diff" })
|
|
||||||
t.eq(left, ":")
|
|
||||||
t.eq(right, nil)
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs --cached is HEAD vs index", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right = cmd._compute_diff_refs(r, { "diff", "--cached" })
|
|
||||||
t.eq(left, "HEAD")
|
|
||||||
t.eq(right, ":")
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs --staged is HEAD vs index", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right = cmd._compute_diff_refs(r, { "diff", "--staged" })
|
|
||||||
t.eq(left, "HEAD")
|
|
||||||
t.eq(right, ":")
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs single rev is rev vs worktree", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right = cmd._compute_diff_refs(r, { "diff", "HEAD" })
|
|
||||||
t.eq(left, "HEAD")
|
|
||||||
t.eq(right, nil)
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs single rev with --cached is rev vs index", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right =
|
|
||||||
cmd._compute_diff_refs(r, { "diff", "--cached", "HEAD" })
|
|
||||||
t.eq(left, "HEAD")
|
|
||||||
t.eq(right, ":")
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs two revs", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
git(dir, "commit", "--allow-empty", "-m", "second")
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right =
|
|
||||||
cmd._compute_diff_refs(r, { "diff", "HEAD~1", "HEAD" })
|
|
||||||
t.eq(left, "HEAD~1")
|
|
||||||
t.eq(right, "HEAD")
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs double-dot range", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
git(dir, "commit", "--allow-empty", "-m", "second")
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right = cmd._compute_diff_refs(r, { "diff", "HEAD~1..HEAD" })
|
|
||||||
t.eq(left, "HEAD~1")
|
|
||||||
t.eq(right, "HEAD")
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs triple-dot range", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
git(dir, "commit", "--allow-empty", "-m", "second")
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right =
|
|
||||||
cmd._compute_diff_refs(r, { "diff", "HEAD~1...HEAD" })
|
|
||||||
t.eq(left, "HEAD~1")
|
|
||||||
t.eq(right, "HEAD")
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs path-only falls back to defaults", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right = cmd._compute_diff_refs(r, { "diff", "a" })
|
|
||||||
t.eq(left, ":")
|
|
||||||
t.eq(right, nil)
|
|
||||||
end)
|
|
||||||
|
|
||||||
t.test("compute_diff_refs ignores args after --", function()
|
|
||||||
local dir = make_repo({ a = "x" })
|
|
||||||
local r = assert(require("git.repo").resolve(dir))
|
|
||||||
local left, right =
|
|
||||||
cmd._compute_diff_refs(r, { "diff", "--", "HEAD" })
|
|
||||||
t.eq(left, ":")
|
|
||||||
t.eq(right, nil)
|
|
||||||
end)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user