Compare commits
7 Commits
2064c629ed
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b869334d6 | |||
| 01ca0025dd | |||
| b52f34ce9a | |||
| e050896dc0 | |||
| 4c8b3f0d3e | |||
| 72ab9059fa | |||
| 7c8975af10 |
+37
-7
@@ -42,15 +42,34 @@ local function resolve_buf(buf)
|
|||||||
return buf and buf ~= 0 and buf or vim.api.nvim_get_current_buf()
|
return buf and buf ~= 0 and buf or vim.api.nvim_get_current_buf()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Mirror the hunk-affecting parts of the user's 'diffopt' so the gutter
|
||||||
|
---lines up with what `:diffsplit` shows.
|
||||||
|
---@return table
|
||||||
|
local function diff_opts()
|
||||||
|
local opts = { result_type = "indices", algorithm = "myers" }
|
||||||
|
for _, item in ipairs(vim.split(vim.o.diffopt, ",", { plain = true })) do
|
||||||
|
if item == "indent-heuristic" then
|
||||||
|
opts.indent_heuristic = true
|
||||||
|
else
|
||||||
|
local algorithm = item:match("^algorithm:(.+)$")
|
||||||
|
if algorithm then
|
||||||
|
opts.algorithm = algorithm
|
||||||
|
end
|
||||||
|
local linematch = item:match("^linematch:(%d+)$")
|
||||||
|
if linematch then
|
||||||
|
opts.linematch = tonumber(linematch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return opts
|
||||||
|
end
|
||||||
|
|
||||||
---@param state ow.Git.Hunks.BufState
|
---@param state ow.Git.Hunks.BufState
|
||||||
---@param new_lines string[]
|
---@param new_lines string[]
|
||||||
local function compute_hunks(state, new_lines)
|
local function compute_hunks(state, new_lines)
|
||||||
local old = table.concat(state.index or {}, "\n")
|
local old = table.concat(state.index or {}, "\n")
|
||||||
local new = table.concat(new_lines, "\n")
|
local new = table.concat(new_lines, "\n")
|
||||||
local raw = vim.text.diff(old, new, {
|
local raw = vim.text.diff(old, new, diff_opts())
|
||||||
result_type = "indices",
|
|
||||||
algorithm = "histogram",
|
|
||||||
})
|
|
||||||
---@type ow.Git.Hunks.Hunk[]
|
---@type ow.Git.Hunks.Hunk[]
|
||||||
local hunks = {}
|
local hunks = {}
|
||||||
if type(raw) ~= "table" then
|
if type(raw) ~= "table" then
|
||||||
@@ -127,11 +146,11 @@ local function render_signs(buf)
|
|||||||
end
|
end
|
||||||
pcall(vim.api.nvim_buf_set_extmark, buf, NS_SIGNS, row, 0, {
|
pcall(vim.api.nvim_buf_set_extmark, buf, NS_SIGNS, row, 0, {
|
||||||
sign_text = signs.delete,
|
sign_text = signs.delete,
|
||||||
sign_hl_group = "GitHunkDelete",
|
sign_hl_group = "GitHunkRemoved",
|
||||||
priority = 100,
|
priority = 100,
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
local hl = h.type == "add" and "GitHunkAdd" or "GitHunkChange"
|
local hl = h.type == "add" and "GitHunkAdded" or "GitHunkChanged"
|
||||||
local sign = h.type == "add" and signs.add or signs.change
|
local sign = h.type == "add" and signs.add or signs.change
|
||||||
for r = h.new_start, h.new_start + h.new_count - 1 do
|
for r = h.new_start, h.new_start + h.new_count - 1 do
|
||||||
local row = r - 1
|
local row = r - 1
|
||||||
@@ -380,6 +399,11 @@ end
|
|||||||
|
|
||||||
local schedule, sched_handle = util.keyed_debounce(recompute, 100)
|
local schedule, sched_handle = util.keyed_debounce(recompute, 100)
|
||||||
|
|
||||||
|
---@param buf integer
|
||||||
|
function M._flush(buf)
|
||||||
|
sched_handle.flush(buf)
|
||||||
|
end
|
||||||
|
|
||||||
---@param buf integer
|
---@param buf integer
|
||||||
function M.attach(buf)
|
function M.attach(buf)
|
||||||
if states[buf] then
|
if states[buf] then
|
||||||
@@ -575,7 +599,7 @@ end
|
|||||||
|
|
||||||
---@param buf? integer
|
---@param buf? integer
|
||||||
function M.stage_hunk(buf)
|
function M.stage_hunk(buf)
|
||||||
local _, state, h = cursor_hunk(buf)
|
local target, state, h = cursor_hunk(buf)
|
||||||
if not state then
|
if not state then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -592,6 +616,12 @@ function M.stage_hunk(buf)
|
|||||||
"git apply failed: %s",
|
"git apply failed: %s",
|
||||||
vim.trim(res.stderr or "")
|
vim.trim(res.stderr or "")
|
||||||
)
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local s = states[target]
|
||||||
|
if s then
|
||||||
|
s.index_sha = nil
|
||||||
|
schedule(target)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-2
@@ -184,8 +184,7 @@ local function populate(buf, r, rev, state, rev_sha)
|
|||||||
local patch = util.git({
|
local patch = util.git({
|
||||||
"diff-tree",
|
"diff-tree",
|
||||||
"-p",
|
"-p",
|
||||||
"-m",
|
"--diff-merges=first-parent",
|
||||||
"--first-parent",
|
|
||||||
"--root",
|
"--root",
|
||||||
"--no-commit-id",
|
"--no-commit-id",
|
||||||
commit_sha,
|
commit_sha,
|
||||||
|
|||||||
+3
-3
@@ -35,9 +35,9 @@ local DEFAULT_HIGHLIGHTS = {
|
|||||||
GitUnmergedDeletedByThem = "GitUnmerged",
|
GitUnmergedDeletedByThem = "GitUnmerged",
|
||||||
GitUnmergedDeletedByUs = "GitUnmerged",
|
GitUnmergedDeletedByUs = "GitUnmerged",
|
||||||
|
|
||||||
GitHunkAdd = "Added",
|
GitHunkAdded = "Added",
|
||||||
GitHunkChange = "Changed",
|
GitHunkChanged = "Changed",
|
||||||
GitHunkDelete = "Removed",
|
GitHunkRemoved = "Removed",
|
||||||
GitHunkAddLine = "DiffAdd",
|
GitHunkAddLine = "DiffAdd",
|
||||||
GitHunkDeleteLine = "DiffDelete",
|
GitHunkDeleteLine = "DiffDelete",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ local highlights = {
|
|||||||
TabLineFill = { bg = c.bg1 },
|
TabLineFill = { bg = c.bg1 },
|
||||||
EndOfBuffer = { fg = "NONE", bg = "NONE" },
|
EndOfBuffer = { fg = "NONE", bg = "NONE" },
|
||||||
DiffAdd = { bg = "#1a2f22" },
|
DiffAdd = { bg = "#1a2f22" },
|
||||||
DiffChange = { bg = "#15304a" },
|
|
||||||
DiffDelete = { bg = "#311c1e" },
|
|
||||||
Changed = { fg = c.yellow },
|
|
||||||
NvimTreeIndentMarker = { fg = c.bg3 },
|
NvimTreeIndentMarker = { fg = c.bg3 },
|
||||||
}
|
}
|
||||||
for kind, color in pairs(completion_kind_colors) do
|
for kind, color in pairs(completion_kind_colors) do
|
||||||
|
|||||||
+29
-10
@@ -15,6 +15,7 @@ local function setup(committed, worktree, file)
|
|||||||
vim.cmd.edit(dir .. "/" .. file)
|
vim.cmd.edit(dir .. "/" .. file)
|
||||||
local buf = vim.api.nvim_get_current_buf()
|
local buf = vim.api.nvim_get_current_buf()
|
||||||
hunks.attach(buf)
|
hunks.attach(buf)
|
||||||
|
hunks._flush(buf)
|
||||||
t.wait_for(function()
|
t.wait_for(function()
|
||||||
local s = hunks.state(buf)
|
local s = hunks.state(buf)
|
||||||
return s ~= nil and s.index ~= nil
|
return s ~= nil and s.index ~= nil
|
||||||
@@ -69,8 +70,8 @@ t.test("pure add: hunk shape and add signs", function()
|
|||||||
t.eq(hk.new_start, 2)
|
t.eq(hk.new_start, 2)
|
||||||
t.eq(hk.new_count, 2)
|
t.eq(hk.new_count, 2)
|
||||||
t.eq(sign_marks(buf), {
|
t.eq(sign_marks(buf), {
|
||||||
{ row = 1, sign = "┃", hl = "GitHunkAdd" },
|
{ row = 1, sign = "┃", hl = "GitHunkAdded" },
|
||||||
{ row = 2, sign = "┃", hl = "GitHunkAdd" },
|
{ row = 2, sign = "┃", hl = "GitHunkAdded" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -82,7 +83,7 @@ t.test("pure delete (middle): hunk shape and delete sign", function()
|
|||||||
t.eq(hk.new_count, 0)
|
t.eq(hk.new_count, 0)
|
||||||
t.eq(hk.old_lines, { "b" })
|
t.eq(hk.old_lines, { "b" })
|
||||||
t.eq(sign_marks(buf), {
|
t.eq(sign_marks(buf), {
|
||||||
{ row = 0, sign = "▁", hl = "GitHunkDelete" },
|
{ row = 0, sign = "▁", hl = "GitHunkRemoved" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ t.test("top-of-file delete: sign anchors on line 1", function()
|
|||||||
t.eq(hk.new_start, 0)
|
t.eq(hk.new_start, 0)
|
||||||
t.eq(hk.old_lines, { "a" })
|
t.eq(hk.old_lines, { "a" })
|
||||||
t.eq(sign_marks(buf), {
|
t.eq(sign_marks(buf), {
|
||||||
{ row = 0, sign = "▁", hl = "GitHunkDelete" },
|
{ row = 0, sign = "▁", hl = "GitHunkRemoved" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -110,8 +111,8 @@ t.test("change of N lines: hunk shape and change signs", function()
|
|||||||
t.eq(hk.old_lines, { "b", "c" })
|
t.eq(hk.old_lines, { "b", "c" })
|
||||||
t.eq(hk.new_lines, { "B", "C" })
|
t.eq(hk.new_lines, { "B", "C" })
|
||||||
t.eq(sign_marks(buf), {
|
t.eq(sign_marks(buf), {
|
||||||
{ row = 1, sign = "┃", hl = "GitHunkChange" },
|
{ row = 1, sign = "┃", hl = "GitHunkChanged" },
|
||||||
{ row = 2, sign = "┃", hl = "GitHunkChange" },
|
{ row = 2, sign = "┃", hl = "GitHunkChanged" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -119,8 +120,8 @@ t.test("multi-hunk file: two separate change hunks", function()
|
|||||||
local _, buf, state = setup("a\nb\nc\nd\ne\n", "A\nb\nc\nd\nE\n")
|
local _, buf, state = setup("a\nb\nc\nd\ne\n", "A\nb\nc\nd\nE\n")
|
||||||
t.eq(#state.hunks, 2, "two hunks for two disjoint changes")
|
t.eq(#state.hunks, 2, "two hunks for two disjoint changes")
|
||||||
t.eq(sign_marks(buf), {
|
t.eq(sign_marks(buf), {
|
||||||
{ row = 0, sign = "┃", hl = "GitHunkChange" },
|
{ row = 0, sign = "┃", hl = "GitHunkChanged" },
|
||||||
{ row = 4, sign = "┃", hl = "GitHunkChange" },
|
{ row = 4, sign = "┃", hl = "GitHunkChanged" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -135,6 +136,7 @@ t.test("editing the buffer refreshes signs", function()
|
|||||||
t.eq(#state.hunks, 0)
|
t.eq(#state.hunks, 0)
|
||||||
vim.api.nvim_buf_set_lines(buf, 1, 2, false, { "CHANGED" })
|
vim.api.nvim_buf_set_lines(buf, 1, 2, false, { "CHANGED" })
|
||||||
vim.api.nvim_exec_autocmds("TextChanged", { buffer = buf })
|
vim.api.nvim_exec_autocmds("TextChanged", { buffer = buf })
|
||||||
|
hunks._flush(buf)
|
||||||
t.wait_for(function()
|
t.wait_for(function()
|
||||||
local s = assert(hunks.state(buf))
|
local s = assert(hunks.state(buf))
|
||||||
return #s.hunks == 1
|
return #s.hunks == 1
|
||||||
@@ -294,6 +296,23 @@ t.test("stage_hunk stages a deletion", function()
|
|||||||
t.eq(h.git(dir, "show", ":0:a.txt").stdout, "a\nc")
|
t.eq(h.git(dir, "show", ":0:a.txt").stdout, "a\nc")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
t.test("stage_hunk refreshes the gutter when status stays modified", function()
|
||||||
|
local _, buf = setup("a\nb\nc\nd\ne\n", "A\nb\nC\nd\nE\n")
|
||||||
|
t.eq(#assert(hunks.state(buf)).hunks, 3)
|
||||||
|
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 1, 0 })
|
||||||
|
hunks.stage_hunk(buf)
|
||||||
|
t.wait_for(function()
|
||||||
|
return #assert(hunks.state(buf)).hunks == 2
|
||||||
|
end, "gutter to drop the first staged hunk")
|
||||||
|
|
||||||
|
vim.api.nvim_win_set_cursor(0, { 3, 0 })
|
||||||
|
hunks.stage_hunk(buf)
|
||||||
|
t.wait_for(function()
|
||||||
|
return #assert(hunks.state(buf)).hunks == 1
|
||||||
|
end, "gutter to drop the middle staged hunk")
|
||||||
|
end)
|
||||||
|
|
||||||
t.test("reset_hunk restores the index content for a change", function()
|
t.test("reset_hunk restores the index content for a change", function()
|
||||||
local _, buf, state = setup("a\nb\nc\n", "a\nB\nc\n")
|
local _, buf, state = setup("a\nb\nc\n", "a\nB\nc\n")
|
||||||
vim.api.nvim_win_set_cursor(0, { 2, 0 })
|
vim.api.nvim_win_set_cursor(0, { 2, 0 })
|
||||||
@@ -327,7 +346,7 @@ t.test("git_hunk_signs overrides the sign character per kind", function()
|
|||||||
end)
|
end)
|
||||||
local _, buf = setup("a\nb\nc\n", "a\nB\nc\n")
|
local _, buf = setup("a\nb\nc\n", "a\nB\nc\n")
|
||||||
t.eq(sign_marks(buf), {
|
t.eq(sign_marks(buf), {
|
||||||
{ row = 1, sign = "C", hl = "GitHunkChange" },
|
{ row = 1, sign = "C", hl = "GitHunkChanged" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -339,7 +358,7 @@ t.test("git_hunk_signs falls back to the default for unset kinds", function()
|
|||||||
end)
|
end)
|
||||||
local _, buf = setup("a\nb\nc\n", "a\nB\nc\n")
|
local _, buf = setup("a\nb\nc\n", "a\nB\nc\n")
|
||||||
t.eq(sign_marks(buf), {
|
t.eq(sign_marks(buf), {
|
||||||
{ row = 1, sign = "┃", hl = "GitHunkChange" },
|
{ row = 1, sign = "┃", hl = "GitHunkChanged" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,23 @@ t.test("M.open(HEAD:<path>) loads file content at HEAD", function()
|
|||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
t.test("M.open on a merge commit diffs against the first parent only", function()
|
||||||
|
local dir = h.make_repo({ ["a.txt"] = "one\n" })
|
||||||
|
t.write(dir, "a.txt", "two\n")
|
||||||
|
h.git(dir, "stash")
|
||||||
|
local stash = h.git(dir, "rev-parse", "stash@{0}").stdout
|
||||||
|
local r = assert(require("git.core.repo").resolve(dir))
|
||||||
|
|
||||||
|
object.open(r, stash, { split = false })
|
||||||
|
local count = 0
|
||||||
|
for _, l in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, false)) do
|
||||||
|
if l:match("^diff %-%-git ") then
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
t.eq(count, 1, "the stashed file's diff appears once, not per-parent")
|
||||||
|
end)
|
||||||
|
|
||||||
t.test("M.open errors on a bogus base, no buffer is opened", function()
|
t.test("M.open errors on a bogus base, no buffer is opened", function()
|
||||||
local dir = h.make_repo({ a = "first\n" })
|
local dir = h.make_repo({ a = "first\n" })
|
||||||
local r = assert(require("git.core.repo").resolve(dir))
|
local r = assert(require("git.core.repo").resolve(dir))
|
||||||
|
|||||||
Reference in New Issue
Block a user