fix(highlight): support combined diff format for unmerged files

Problem: fugitive shows combined diffs (@@@ headers, 2-char prefixes)
for unmerged (UU) files. The parser and highlight pipeline assumed
unified diff format (@@, 1-char prefix), causing broken prefix
concealment, missing background colors on ` +`/`+ ` lines, and no
treesitter highlights due to garbage prefix chars in code arrays.

Solution: detect prefix width from the number of leading @ signs in
hunk headers. Propagate prefix_width through the parser (new field on
diffs.Hunk) and highlight pipeline (prefix stripping, col_offset,
concealment, line classification). Add U to filename pattern for
unmerged file detection. Skip intra-line diffing for combined diffs
since the 2-char prefix semantics don't produce meaningful change
groups.
This commit is contained in:
Barrett Ruth 2026-02-09 17:03:17 -05:00
parent 59fcf14817
commit a6dd0503b3
4 changed files with 232 additions and 29 deletions

View file

@ -830,6 +830,114 @@ describe('highlight', function()
delete_buffer(bufnr)
end)
it('applies DiffsAdd background to combined diff lines with + in prefix', function()
local bufnr = create_buffer({
'@@@ -1,3 -1,5 +1,9 @@@',
' local M = {}',
'++<<<<<<< HEAD',
' + return 1',
'++=======',
'+ return 2',
'++>>>>>>> feature',
})
local hunk = {
filename = 'test.lua',
lang = 'lua',
start_line = 1,
prefix_width = 2,
lines = {
' local M = {}',
'++<<<<<<< HEAD',
' + return 1',
'++=======',
'+ return 2',
'++>>>>>>> feature',
},
}
highlight.highlight_hunk(
bufnr,
ns,
hunk,
default_opts({ highlights = { background = true } })
)
local extmarks = get_extmarks(bufnr)
local add_lines = {}
for _, mark in ipairs(extmarks) do
if mark[4] and mark[4].hl_group == 'DiffsAdd' then
add_lines[mark[2]] = true
end
end
assert.is_nil(add_lines[0])
assert.is_nil(add_lines[1])
assert.is_true(add_lines[2] ~= nil)
assert.is_true(add_lines[3] ~= nil)
assert.is_true(add_lines[4] ~= nil)
assert.is_true(add_lines[5] ~= nil)
assert.is_true(add_lines[6] ~= nil)
delete_buffer(bufnr)
end)
it('conceals 2-char prefix for combined diffs', function()
local bufnr = create_buffer({
'@@@ -1,2 -1,2 +1,3 @@@',
' local M = {}',
'++<<<<<<< HEAD',
})
local hunk = {
filename = 'test.lua',
lang = 'lua',
start_line = 1,
prefix_width = 2,
lines = { ' local M = {}', '++<<<<<<< HEAD' },
}
highlight.highlight_hunk(bufnr, ns, hunk, default_opts({ hide_prefix = true }))
local extmarks = get_extmarks(bufnr)
local overlay_count = 0
for _, mark in ipairs(extmarks) do
if mark[4] and mark[4].virt_text_pos == 'overlay' then
overlay_count = overlay_count + 1
assert.are.equal(' ', mark[4].virt_text[1][1])
end
end
assert.are.equal(2, overlay_count)
delete_buffer(bufnr)
end)
it('produces treesitter captures on combined diff content lines', function()
local bufnr = create_buffer({
'@@@ -1,2 -1,2 +1,3 @@@',
' local M = {}',
' +local x = 1',
})
local hunk = {
filename = 'test.lua',
lang = 'lua',
start_line = 1,
prefix_width = 2,
lines = { ' local M = {}', ' +local x = 1' },
}
highlight.highlight_hunk(bufnr, ns, hunk, default_opts())
local extmarks = get_extmarks(bufnr)
local has_ts = false
for _, mark in ipairs(extmarks) do
if mark[4] and mark[4].hl_group and mark[4].hl_group:match('^@.*%.lua$') then
has_ts = true
break
end
end
assert.is_true(has_ts)
delete_buffer(bufnr)
end)
it('filters @spell and @nospell captures from injections', function()
local bufnr = create_buffer({
'@@ -1,1 +1,2 @@',