fix(hunks): remove hunk overlay
This commit is contained in:
@@ -7,7 +7,7 @@ Features:
|
||||
- Status sidebar with stage/unstage/discard actions
|
||||
- Log viewer
|
||||
- Diff splits against any revision, the index, or the worktree
|
||||
- Gutter signs + optional hunk overlay
|
||||
- Gutter signs
|
||||
- Per-hunk stage / reset / preview / select
|
||||
- Blame: cursor-line popup, inline annotation, full-file gutter
|
||||
- Commit proxy (compose in a Neovim buffer)
|
||||
@@ -36,7 +36,6 @@ vim.pack.add({ "https://git.owall.se/oscar/git.nvim" })
|
||||
| `:Gdiffsplit [vertical\|horizontal] [<rev>]` | Open a diff split (default vertical, vs index) |
|
||||
| `:Gedit <rev>[:path]` | Open an object as a buffer (`git://` URI) |
|
||||
| `:Gstatus [sidebar\|split\|current]` | Open the status view (default `split`) |
|
||||
| `:GitHunkOverlay` | Toggle the in-buffer hunk overlay |
|
||||
|
||||
## Mappings
|
||||
|
||||
@@ -55,7 +54,6 @@ vim.pack.add({ "https://git.owall.se/oscar/git.nvim" })
|
||||
| `<Plug>(git-hunk-select)` | Visually select the hunk under cursor |
|
||||
| `<Plug>(git-hunk-stage-toggle)` | Stage / unstage hunk or visual hunks |
|
||||
| `<Plug>(git-hunk-reset)` | Reset hunk |
|
||||
| `<Plug>(git-hunk-overlay)` | Toggle the in-buffer hunk overlay |
|
||||
| `<Plug>(git-hunk-next)` | Jump to next hunk |
|
||||
| `<Plug>(git-hunk-prev)` | Jump to previous hunk |
|
||||
|
||||
|
||||
+1
-398
@@ -5,7 +5,6 @@ local util = require("git.core.util")
|
||||
local M = {}
|
||||
|
||||
local NS_SIGNS = vim.api.nvim_create_namespace("ow.git.hunks")
|
||||
local NS_OVERLAY = vim.api.nvim_create_namespace("ow.git.hunks.overlay")
|
||||
local NS_PREVIEW = vim.api.nvim_create_namespace("ow.git.hunks.preview")
|
||||
local NS_PREVIEW_OVERFLOW =
|
||||
vim.api.nvim_create_namespace("ow.git.hunks.preview.overflow")
|
||||
@@ -28,10 +27,8 @@ local NS_PREVIEW_OVERFLOW =
|
||||
---@field index_sha string?
|
||||
---@field head string[]?
|
||||
---@field head_sha string?
|
||||
---@field index_hl { src: string[], lines: table[][]? }?
|
||||
---@field hunks ow.Git.Hunks.Hunk[]
|
||||
---@field staged ow.Git.Hunks.Hunk[]
|
||||
---@field overlay boolean
|
||||
---@field autocmds integer[]
|
||||
|
||||
---@type table<integer, ow.Git.Hunks.BufState>
|
||||
@@ -71,16 +68,6 @@ local function diff_opts()
|
||||
return opts
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
local function inline_diff_enabled()
|
||||
for _, item in ipairs(vim.split(vim.o.diffopt, ",", { plain = true })) do
|
||||
if item == "inline:none" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
---@param old_lines string[]
|
||||
---@param new_lines string[]
|
||||
---@return ow.Git.Hunks.Hunk[]
|
||||
@@ -241,7 +228,7 @@ local function render_signs(buf)
|
||||
end
|
||||
vim.api.nvim_buf_clear_namespace(buf, NS_SIGNS, 0, -1)
|
||||
local state = states[buf]
|
||||
if not state or state.overlay then
|
||||
if not state then
|
||||
return
|
||||
end
|
||||
local signs = resolve_signs()
|
||||
@@ -268,379 +255,9 @@ local function render_signs(buf)
|
||||
end
|
||||
end
|
||||
|
||||
local SKIP_CAPTURES = { spell = true, nospell = true, conceal = true }
|
||||
|
||||
---@alias ow.Git.Hunks.InlineSpan { start_col: integer, end_col: integer }
|
||||
|
||||
---@param line string
|
||||
---@return { text: string, start_col: integer, end_col: integer }[]
|
||||
local function char_pieces(line)
|
||||
local out = {}
|
||||
local start = 1
|
||||
for c in line:gmatch("[%z\1-\127\194-\244][\128-\191]*") do
|
||||
local stop = start + #c - 1
|
||||
table.insert(out, {
|
||||
text = c,
|
||||
start_col = start - 1,
|
||||
end_col = stop,
|
||||
})
|
||||
start = stop + 1
|
||||
end
|
||||
if start <= #line then
|
||||
table.insert(out, {
|
||||
text = line:sub(start),
|
||||
start_col = start - 1,
|
||||
end_col = #line,
|
||||
})
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
---@param pieces { text: string, start_col: integer, end_col: integer }[]
|
||||
---@param start_idx integer
|
||||
---@param count integer
|
||||
---@return ow.Git.Hunks.InlineSpan?
|
||||
local function char_span(pieces, start_idx, count)
|
||||
if count <= 0 then
|
||||
return nil
|
||||
end
|
||||
local first = pieces[start_idx]
|
||||
local last = pieces[start_idx + count - 1]
|
||||
if not first or not last then
|
||||
return nil
|
||||
end
|
||||
return { start_col = first.start_col, end_col = last.end_col }
|
||||
end
|
||||
|
||||
---@param old_line string
|
||||
---@param new_line string
|
||||
---@return ow.Git.Hunks.InlineSpan[] old_spans
|
||||
---@return ow.Git.Hunks.InlineSpan[] new_spans
|
||||
local function inline_spans(old_line, new_line)
|
||||
if old_line == new_line or not inline_diff_enabled() then
|
||||
return {}, {}
|
||||
end
|
||||
local old_pieces = char_pieces(old_line)
|
||||
local new_pieces = char_pieces(new_line)
|
||||
if #old_pieces == 0 then
|
||||
return {},
|
||||
#new_pieces > 0 and { { start_col = 0, end_col = #new_line } } or {}
|
||||
end
|
||||
if #new_pieces == 0 then
|
||||
return { { start_col = 0, end_col = #old_line } }, {}
|
||||
end
|
||||
local old_text = {}
|
||||
for _, c in ipairs(old_pieces) do
|
||||
table.insert(old_text, c.text)
|
||||
end
|
||||
local new_text = {}
|
||||
for _, c in ipairs(new_pieces) do
|
||||
table.insert(new_text, c.text)
|
||||
end
|
||||
local raw = vim.text.diff(
|
||||
table.concat(old_text, "\n"),
|
||||
table.concat(new_text, "\n"),
|
||||
diff_opts()
|
||||
)
|
||||
if type(raw) ~= "table" then
|
||||
return { { start_col = 0, end_col = #old_line } }, {
|
||||
{ start_col = 0, end_col = #new_line },
|
||||
}
|
||||
end
|
||||
local old_spans = {}
|
||||
local new_spans = {}
|
||||
for _, h in ipairs(raw) do
|
||||
local os_ = h[1] --[[@as integer]]
|
||||
local oc = h[2] --[[@as integer]]
|
||||
local ns_ = h[3] --[[@as integer]]
|
||||
local nc = h[4] --[[@as integer]]
|
||||
local old_span = char_span(old_pieces, os_, oc)
|
||||
if old_span then
|
||||
table.insert(old_spans, old_span)
|
||||
end
|
||||
local new_span = char_span(new_pieces, ns_, nc)
|
||||
if new_span then
|
||||
table.insert(new_spans, new_span)
|
||||
end
|
||||
end
|
||||
return old_spans, new_spans
|
||||
end
|
||||
|
||||
---@param hl string|string[]
|
||||
---@return string[]
|
||||
local function hl_list(hl)
|
||||
if type(hl) == "table" then
|
||||
return vim.list_extend({}, hl)
|
||||
end
|
||||
return { hl }
|
||||
end
|
||||
|
||||
---@param col integer
|
||||
---@param spans ow.Git.Hunks.InlineSpan[]
|
||||
---@return boolean
|
||||
local function in_inline_span(col, spans)
|
||||
for _, span in ipairs(spans) do
|
||||
if col >= span.start_col and col < span.end_col then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@param line string
|
||||
---@param chunks table[]
|
||||
---@param spans ow.Git.Hunks.InlineSpan[]?
|
||||
---@return table[]
|
||||
local function apply_inline_to_chunks(line, chunks, spans)
|
||||
if not spans or #spans == 0 then
|
||||
return chunks
|
||||
end
|
||||
local out = {}
|
||||
local col = 0
|
||||
for _, chunk in ipairs(chunks) do
|
||||
local text = chunk[1]
|
||||
local hl = chunk[2]
|
||||
local pos = 1
|
||||
while pos <= #text do
|
||||
local inline = in_inline_span(col, spans)
|
||||
local stop = pos + 1
|
||||
while
|
||||
stop <= #text
|
||||
and in_inline_span(col + stop - pos, spans) == inline
|
||||
do
|
||||
stop = stop + 1
|
||||
end
|
||||
local piece = text:sub(pos, stop - 1)
|
||||
local piece_hl = hl
|
||||
if inline then
|
||||
piece_hl = hl_list(hl)
|
||||
table.insert(piece_hl, "DiffText")
|
||||
end
|
||||
table.insert(out, { piece, piece_hl })
|
||||
col = col + #piece
|
||||
pos = stop
|
||||
end
|
||||
end
|
||||
if col < #line then
|
||||
return chunks
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
---@param buf integer
|
||||
---@param lines string[]
|
||||
---@return table[][]?
|
||||
local function highlight_index(buf, lines)
|
||||
if not vim.treesitter.highlighter.active[buf] then
|
||||
return nil
|
||||
end
|
||||
local got, parser = pcall(vim.treesitter.get_parser, buf)
|
||||
if not got or not parser then
|
||||
return nil
|
||||
end
|
||||
local lang = parser:lang()
|
||||
local query = vim.treesitter.query.get(lang, "highlights")
|
||||
if not query then
|
||||
return nil
|
||||
end
|
||||
local source = table.concat(lines, "\n")
|
||||
local got_root, root = pcall(function()
|
||||
local trees = vim.treesitter.get_string_parser(source, lang):parse()
|
||||
local tree = trees and trees[1]
|
||||
return tree and tree:root()
|
||||
end)
|
||||
if not got_root or not root then
|
||||
return nil
|
||||
end
|
||||
---@type table<integer, table<integer, string>>
|
||||
local groups = {}
|
||||
for id, node in query:iter_captures(root, source) do
|
||||
local name = query.captures[id]
|
||||
if name and name:sub(1, 1) ~= "_" and not SKIP_CAPTURES[name] then
|
||||
local sr, sc, er, ec = node:range()
|
||||
for row = sr, math.min(er, #lines - 1) do
|
||||
local row_groups = groups[row] or {}
|
||||
groups[row] = row_groups
|
||||
local from = row == sr and sc or 0
|
||||
local to = row == er and ec or #(lines[row + 1] or "")
|
||||
for col = from, to - 1 do
|
||||
row_groups[col] = name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local out = {}
|
||||
for row = 0, #lines - 1 do
|
||||
local line = lines[row + 1] or ""
|
||||
local row_groups = groups[row] or {}
|
||||
local chunks = {}
|
||||
local col = 0
|
||||
while col < #line do
|
||||
local name = row_groups[col]
|
||||
local stop = col + 1
|
||||
while stop < #line and row_groups[stop] == name do
|
||||
stop = stop + 1
|
||||
end
|
||||
local hl ---@type string|string[]
|
||||
if name then
|
||||
hl = { "GitHunkDeleteLine", "@" .. name }
|
||||
else
|
||||
hl = "GitHunkDeleteLine"
|
||||
end
|
||||
table.insert(chunks, { line:sub(col + 1, stop), hl })
|
||||
col = stop
|
||||
end
|
||||
out[row + 1] = chunks
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
---@param h ow.Git.Hunks.Hunk
|
||||
---@param hl_lines table[][]? per-index-line syntax chunks, or nil
|
||||
---@param inline_by_line table<integer, ow.Git.Hunks.InlineSpan[]>?
|
||||
---@return table[]
|
||||
local function delete_virt_lines(h, hl_lines, inline_by_line)
|
||||
local width = vim.o.columns
|
||||
local virt = {}
|
||||
for i, line in ipairs(h.old_lines) do
|
||||
local pad = math.max(width - vim.api.nvim_strwidth(line), 0)
|
||||
local cached = hl_lines and hl_lines[h.old_start + i - 1]
|
||||
local inline = inline_by_line and inline_by_line[i]
|
||||
if cached then
|
||||
local chunks = apply_inline_to_chunks(
|
||||
line,
|
||||
vim.list_extend({}, cached),
|
||||
inline
|
||||
)
|
||||
table.insert(chunks, {
|
||||
string.rep(" ", pad),
|
||||
"GitHunkDeleteLine",
|
||||
})
|
||||
table.insert(virt, chunks)
|
||||
else
|
||||
local chunks = apply_inline_to_chunks(
|
||||
line,
|
||||
{ { line, "GitHunkDeleteLine" } },
|
||||
inline
|
||||
)
|
||||
table.insert(chunks, {
|
||||
string.rep(" ", pad),
|
||||
"GitHunkDeleteLine",
|
||||
})
|
||||
table.insert(virt, chunks)
|
||||
end
|
||||
end
|
||||
return virt
|
||||
end
|
||||
|
||||
---@param h ow.Git.Hunks.Hunk
|
||||
---@return table<integer, ow.Git.Hunks.InlineSpan[]> old_by_line
|
||||
---@return table<integer, ow.Git.Hunks.InlineSpan[]> new_by_line
|
||||
local function hunk_inline_spans(h)
|
||||
if h.type ~= "change" or #h.old_lines ~= #h.new_lines then
|
||||
return {}, {}
|
||||
end
|
||||
local old_by_line = {}
|
||||
local new_by_line = {}
|
||||
for i = 1, math.min(#h.old_lines, #h.new_lines) do
|
||||
local old_line = h.old_lines[i] or ""
|
||||
local new_line = h.new_lines[i] or ""
|
||||
local old_spans, new_spans = inline_spans(old_line, new_line)
|
||||
old_by_line[i] = old_spans
|
||||
new_by_line[i] = new_spans
|
||||
end
|
||||
return old_by_line, new_by_line
|
||||
end
|
||||
|
||||
---@param state ow.Git.Hunks.BufState
|
||||
---@param buf integer
|
||||
---@return table[][]?
|
||||
local function index_spans(state, buf)
|
||||
if not state.index then
|
||||
return nil
|
||||
end
|
||||
local cache = state.index_hl
|
||||
if cache and cache.src == state.index then
|
||||
return cache.lines
|
||||
end
|
||||
local lines = highlight_index(buf, state.index)
|
||||
state.index_hl = { src = state.index, lines = lines }
|
||||
return lines
|
||||
end
|
||||
|
||||
---@param buf integer
|
||||
local function render_overlay(buf)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
vim.api.nvim_buf_clear_namespace(buf, NS_OVERLAY, 0, -1)
|
||||
local state = states[buf]
|
||||
if not state or not state.overlay then
|
||||
return
|
||||
end
|
||||
local line_count = vim.api.nvim_buf_line_count(buf)
|
||||
local hl_lines = index_spans(state, buf)
|
||||
for _, h in ipairs(state.hunks) do
|
||||
local old_inline, new_inline = hunk_inline_spans(h)
|
||||
if h.type ~= "delete" then
|
||||
for r = h.new_start, h.new_start + h.new_count - 1 do
|
||||
local row = r - 1
|
||||
if row >= 0 and row < line_count then
|
||||
pcall(
|
||||
vim.api.nvim_buf_set_extmark,
|
||||
buf,
|
||||
NS_OVERLAY,
|
||||
row,
|
||||
0,
|
||||
{
|
||||
line_hl_group = "GitHunkAddLine",
|
||||
priority = 100,
|
||||
}
|
||||
)
|
||||
for _, span in ipairs(new_inline[r - h.new_start + 1] or {}) do
|
||||
pcall(
|
||||
vim.api.nvim_buf_set_extmark,
|
||||
buf,
|
||||
NS_OVERLAY,
|
||||
row,
|
||||
span.start_col,
|
||||
{
|
||||
end_col = span.end_col,
|
||||
hl_group = "DiffText",
|
||||
priority = 101,
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if h.type ~= "add" then
|
||||
local row, above
|
||||
if h.type == "delete" then
|
||||
if h.new_start <= 0 then
|
||||
row, above = 0, true
|
||||
elseif h.new_start >= line_count then
|
||||
row, above = math.max(line_count - 1, 0), false
|
||||
else
|
||||
row, above = h.new_start, true
|
||||
end
|
||||
else
|
||||
row, above = math.max(h.new_start - 1, 0), true
|
||||
end
|
||||
pcall(vim.api.nvim_buf_set_extmark, buf, NS_OVERLAY, row, 0, {
|
||||
virt_lines = delete_virt_lines(h, hl_lines, old_inline),
|
||||
virt_lines_above = above,
|
||||
right_gravity = false,
|
||||
invalidate = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param buf integer
|
||||
local function render(buf)
|
||||
render_signs(buf)
|
||||
render_overlay(buf)
|
||||
end
|
||||
|
||||
---@param state ow.Git.Hunks.BufState
|
||||
@@ -758,7 +375,6 @@ function M.attach(buf)
|
||||
head_sha = nil,
|
||||
hunks = {},
|
||||
staged = {},
|
||||
overlay = false,
|
||||
autocmds = {},
|
||||
}
|
||||
states[buf] = state
|
||||
@@ -797,7 +413,6 @@ function M.detach(buf)
|
||||
end
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
vim.api.nvim_buf_clear_namespace(buf, NS_SIGNS, 0, -1)
|
||||
vim.api.nvim_buf_clear_namespace(buf, NS_OVERLAY, 0, -1)
|
||||
end
|
||||
for _, id in ipairs(state.autocmds) do
|
||||
pcall(vim.api.nvim_del_autocmd, id)
|
||||
@@ -807,18 +422,6 @@ function M.detach(buf)
|
||||
states[buf] = nil
|
||||
end
|
||||
|
||||
---@param buf integer?
|
||||
function M.toggle_overlay(buf)
|
||||
buf = resolve_buf(buf)
|
||||
local state = states[buf]
|
||||
if not state then
|
||||
util.warning("git hunks: buffer not attached")
|
||||
return
|
||||
end
|
||||
state.overlay = not state.overlay
|
||||
render(buf)
|
||||
end
|
||||
|
||||
---@param hunks ow.Git.Hunks.Hunk[]
|
||||
---@param row integer 1-indexed cursor line
|
||||
---@return ow.Git.Hunks.Hunk?
|
||||
|
||||
@@ -42,8 +42,6 @@ local DEFAULT_HIGHLIGHTS = {
|
||||
GitHunkRemoved = "Removed",
|
||||
GitHunkHeader = "Statement",
|
||||
GitHunkAnnotation = "Title",
|
||||
GitHunkAddLine = "DiffAdd",
|
||||
GitHunkDeleteLine = "DiffDelete",
|
||||
|
||||
GitBlameAuthor = "GitAuthor",
|
||||
GitBlameDate = "GitDate",
|
||||
@@ -241,12 +239,6 @@ end, {
|
||||
desc = "Open git status view",
|
||||
})
|
||||
|
||||
vim.api.nvim_create_user_command("GitHunkOverlay", function()
|
||||
require("git.hunks").toggle_overlay()
|
||||
end, {
|
||||
desc = "Toggle the git hunk overlay in the current buffer",
|
||||
})
|
||||
|
||||
vim.keymap.set("n", "<Plug>(git-edit)", function()
|
||||
local rev = vim.fn.input("Edit git object: ")
|
||||
if rev == "" then
|
||||
@@ -326,9 +318,6 @@ end, { silent = true, desc = "Preview hunk under cursor" })
|
||||
vim.keymap.set("n", "<Plug>(git-hunk-select)", function()
|
||||
require("git.hunks").select_hunk()
|
||||
end, { silent = true, desc = "Select hunk under cursor" })
|
||||
vim.keymap.set("n", "<Plug>(git-hunk-overlay)", function()
|
||||
require("git.hunks").toggle_overlay()
|
||||
end, { silent = true, desc = "Toggle the git hunk overlay" })
|
||||
vim.keymap.set("n", "<Plug>(git-blame-popup)", function()
|
||||
require("git.blame").line_popup()
|
||||
end, { silent = true, desc = "Show git blame for the current line" })
|
||||
|
||||
@@ -47,14 +47,6 @@ local function sign_marks(buf)
|
||||
return out
|
||||
end
|
||||
|
||||
---@param buf integer
|
||||
---@param ns_name string
|
||||
---@return vim.api.keyset.get_extmark_item[]
|
||||
local function detailed_marks(buf, ns_name)
|
||||
local ns = vim.api.nvim_get_namespaces()[ns_name]
|
||||
return vim.api.nvim_buf_get_extmarks(buf, ns, 0, -1, { details = true })
|
||||
end
|
||||
|
||||
---@return integer?
|
||||
local function find_float()
|
||||
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(0)) do
|
||||
@@ -64,19 +56,6 @@ local function find_float()
|
||||
end
|
||||
end
|
||||
|
||||
---@param hl string|string[]?
|
||||
---@param group string
|
||||
---@return boolean
|
||||
local function has_hl(hl, group)
|
||||
if hl == group then
|
||||
return true
|
||||
end
|
||||
if type(hl) == "table" then
|
||||
return vim.tbl_contains(hl, group)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
t.test("pure add: hunk shape and add signs", function()
|
||||
local _, buf, state = setup("a\nd\n", "a\nb\nc\nd\n")
|
||||
t.eq(#state.hunks, 1, "one hunk for a pure addition")
|
||||
@@ -160,134 +139,6 @@ t.test("editing the buffer refreshes signs", function()
|
||||
t.eq(hk.type, "change")
|
||||
end)
|
||||
|
||||
t.test("overlay: change hunk shows deletion and addition", function()
|
||||
local _, buf = setup("a\nb\nc\n", "a\nB\nc\n")
|
||||
hunks.toggle_overlay(buf)
|
||||
---@type integer?
|
||||
local add_row
|
||||
---@type vim.api.keyset.extmark_details?
|
||||
local add_d
|
||||
---@type vim.api.keyset.extmark_details?
|
||||
local virt_d
|
||||
for _, m in ipairs(detailed_marks(buf, "ow.git.hunks.overlay")) do
|
||||
local d = assert(m[4])
|
||||
if d.line_hl_group then
|
||||
add_row, add_d = m[2], d
|
||||
elseif d.virt_lines then
|
||||
virt_d = d
|
||||
end
|
||||
end
|
||||
add_d = assert(add_d, "the added line should get a line highlight")
|
||||
t.eq(add_row, 1, "addition highlighted on the changed line")
|
||||
t.eq(add_d.line_hl_group, "GitHunkAddLine")
|
||||
virt_d = assert(virt_d, "the deletion should render as virtual lines")
|
||||
local piece = assert(assert(assert(virt_d.virt_lines)[1])[1])
|
||||
t.truthy(
|
||||
vim.startswith(piece[1], "b"),
|
||||
"deleted line shows the old content"
|
||||
)
|
||||
t.truthy(has_hl(piece[2], "GitHunkDeleteLine"))
|
||||
t.truthy(has_hl(piece[2], "DiffText"))
|
||||
local seen_inline = false
|
||||
for _, m in ipairs(detailed_marks(buf, "ow.git.hunks.overlay")) do
|
||||
local d = assert(m[4])
|
||||
if d.hl_group == "DiffText" then
|
||||
seen_inline = true
|
||||
t.eq(m[2], 1, "inline addition highlight is on the changed line")
|
||||
t.eq(m[3], 0, "inline addition starts at the changed byte")
|
||||
t.eq(d.end_col, 1, "inline addition ends after the changed byte")
|
||||
end
|
||||
end
|
||||
t.truthy(seen_inline, "the added side gets an inline highlight")
|
||||
end)
|
||||
|
||||
t.test("overlay: delete hunk shows only deletion lines", function()
|
||||
local _, buf = setup("a\nb\nc\n", "a\nc\n")
|
||||
hunks.toggle_overlay(buf)
|
||||
local marks = detailed_marks(buf, "ow.git.hunks.overlay")
|
||||
t.eq(#marks, 1, "a pure delete has no addition highlight")
|
||||
local d = assert(assert(marks[1])[4])
|
||||
local piece = assert(assert(assert(d.virt_lines)[1])[1])
|
||||
t.truthy(vim.startswith(piece[1], "b"))
|
||||
t.eq(piece[2], "GitHunkDeleteLine")
|
||||
end)
|
||||
|
||||
t.test("overlay: add hunk highlights the added lines", function()
|
||||
local _, buf = setup("a\nd\n", "a\nb\nc\nd\n")
|
||||
hunks.toggle_overlay(buf)
|
||||
local rows = {}
|
||||
for _, m in ipairs(detailed_marks(buf, "ow.git.hunks.overlay")) do
|
||||
local d = assert(m[4])
|
||||
t.falsy(d.virt_lines, "a pure add has no deletion virtual lines")
|
||||
t.eq(d.line_hl_group, "GitHunkAddLine")
|
||||
table.insert(rows, m[2])
|
||||
end
|
||||
table.sort(rows)
|
||||
t.eq(rows, { 1, 2 }, "both added lines highlighted")
|
||||
end)
|
||||
|
||||
t.test("overlay: deleted lines are treesitter-highlighted", function()
|
||||
local _, buf =
|
||||
setup("-- a note\nlocal x = 1\nlocal y = 2\n", "local y = 2\n", "a.lua")
|
||||
t.truthy(
|
||||
pcall(vim.treesitter.start, buf, "lua"),
|
||||
"the lua parser should be available"
|
||||
)
|
||||
hunks.toggle_overlay(buf)
|
||||
---@type table[]?
|
||||
local virt
|
||||
for _, m in ipairs(detailed_marks(buf, "ow.git.hunks.overlay")) do
|
||||
local d = assert(m[4])
|
||||
if d.virt_lines then
|
||||
virt = d.virt_lines
|
||||
end
|
||||
end
|
||||
virt = assert(virt, "a deletion virtual line should render")
|
||||
---@type table<string, boolean>
|
||||
local seen = {}
|
||||
for _, line in ipairs(virt) do
|
||||
for _, c in ipairs(line) do
|
||||
local hl = c[2]
|
||||
if
|
||||
type(hl) == "table"
|
||||
and hl[1] == "GitHunkDeleteLine"
|
||||
and hl[2]
|
||||
then
|
||||
seen[hl[2]] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
t.truthy(seen["@comment"], "the deleted comment keeps its @comment group")
|
||||
t.truthy(seen["@keyword"], "deleted code keeps its syntax groups")
|
||||
end)
|
||||
|
||||
t.test("overlay: toggling swaps gutter signs for the overlay", function()
|
||||
local _, buf = setup("a\nb\nc\n", "a\nB\nc\n")
|
||||
t.truthy(
|
||||
#detailed_marks(buf, "ow.git.hunks") > 0,
|
||||
"gutter signs present while the overlay is off"
|
||||
)
|
||||
t.eq(#detailed_marks(buf, "ow.git.hunks.overlay"), 0)
|
||||
|
||||
hunks.toggle_overlay(buf)
|
||||
t.truthy(
|
||||
#detailed_marks(buf, "ow.git.hunks.overlay") > 0,
|
||||
"overlay present once it is on"
|
||||
)
|
||||
t.eq(
|
||||
#detailed_marks(buf, "ow.git.hunks"),
|
||||
0,
|
||||
"gutter signs replaced while the overlay is on"
|
||||
)
|
||||
|
||||
hunks.toggle_overlay(buf)
|
||||
t.eq(#detailed_marks(buf, "ow.git.hunks.overlay"), 0)
|
||||
t.truthy(
|
||||
#detailed_marks(buf, "ow.git.hunks") > 0,
|
||||
"gutter signs restored after toggling the overlay off"
|
||||
)
|
||||
end)
|
||||
|
||||
t.test("toggle_stage stages the change into the index", function()
|
||||
local dir, buf = setup("a\nb\nc\n", "a\nB\nc\n")
|
||||
vim.api.nvim_win_set_cursor(0, { 2, 0 })
|
||||
|
||||
Reference in New Issue
Block a user