Fix inline diff visualization for line replacements
- Detect when a deletion is immediately followed by an addition (replacement) - Show replacements differently: highlight line as DiffChange and show old text at EOL - Prevents the 'extra red line' issue where replacements showed deletion as separate line - Pure deletions still shown as virtual lines above their position - Improves visual clarity when reviewing simple line changes
This commit is contained in:
parent
e809333fe3
commit
581a69b2dd
|
@ -122,33 +122,50 @@ function M.apply_diff_visualization(bufnr)
|
||||||
-- Track which lines in the current buffer correspond to additions/deletions
|
-- Track which lines in the current buffer correspond to additions/deletions
|
||||||
local additions = {}
|
local additions = {}
|
||||||
local deletions = {}
|
local deletions = {}
|
||||||
|
local replacements = {} -- Track lines that are replacements (have both deletion and addition)
|
||||||
|
|
||||||
-- Start from the beginning of the hunk and track line numbers
|
-- Start from the beginning of the hunk and track line numbers
|
||||||
local new_line_num = hunk.new_start -- 1-indexed line number in new file
|
local new_line_num = hunk.new_start -- 1-indexed line number in new file
|
||||||
local old_line_num = hunk.old_start -- 1-indexed line number in old file
|
local old_line_num = hunk.old_start -- 1-indexed line number in old file
|
||||||
|
|
||||||
for _, diff_line in ipairs(hunk.lines) do
|
-- First pass: identify replacements by looking for adjacent -/+ pairs
|
||||||
if diff_line:match('^%+') then
|
local j = 1
|
||||||
-- This is an added line - it exists in the current buffer at new_line_num
|
while j <= #hunk.lines do
|
||||||
|
local line = hunk.lines[j]
|
||||||
|
if line:match('^%-') and j < #hunk.lines and hunk.lines[j + 1]:match('^%+') then
|
||||||
|
-- This is a replacement: deletion followed by addition
|
||||||
|
table.insert(replacements, {
|
||||||
|
old_text = line:sub(2),
|
||||||
|
new_text = hunk.lines[j + 1]:sub(2),
|
||||||
|
line = new_line_num - 1 -- 0-indexed line in buffer
|
||||||
|
})
|
||||||
|
j = j + 2 -- Skip both lines
|
||||||
|
new_line_num = new_line_num + 1
|
||||||
|
old_line_num = old_line_num + 1
|
||||||
|
elseif line:match('^%+') then
|
||||||
|
-- Pure addition
|
||||||
table.insert(additions, new_line_num - 1) -- Convert to 0-indexed for extmarks
|
table.insert(additions, new_line_num - 1) -- Convert to 0-indexed for extmarks
|
||||||
new_line_num = new_line_num + 1
|
new_line_num = new_line_num + 1
|
||||||
-- Don't advance old_line_num for additions
|
j = j + 1
|
||||||
elseif diff_line:match('^%-') then
|
elseif line:match('^%-') then
|
||||||
-- This is a deleted line - show as virtual text above current position
|
-- Pure deletion
|
||||||
table.insert(deletions, {
|
table.insert(deletions, {
|
||||||
line = new_line_num - 1, -- 0-indexed, show above current position
|
line = new_line_num - 1, -- 0-indexed, show above current position
|
||||||
text = diff_line:sub(2),
|
text = line:sub(2),
|
||||||
})
|
})
|
||||||
old_line_num = old_line_num + 1
|
old_line_num = old_line_num + 1
|
||||||
-- Don't advance new_line_num for deletions
|
j = j + 1
|
||||||
elseif diff_line:match('^%s') then
|
elseif line:match('^%s') then
|
||||||
-- Context line - advance both
|
-- Context line - advance both
|
||||||
new_line_num = new_line_num + 1
|
new_line_num = new_line_num + 1
|
||||||
old_line_num = old_line_num + 1
|
old_line_num = old_line_num + 1
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
j = j + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply highlighting for additions
|
-- Apply highlighting for pure additions
|
||||||
for _, line_idx in ipairs(additions) do
|
for _, line_idx in ipairs(additions) do
|
||||||
if line_idx >= 0 and line_idx < #buf_lines then
|
if line_idx >= 0 and line_idx < #buf_lines then
|
||||||
vim.api.nvim_buf_set_extmark(bufnr, ns_id, line_idx, 0, {
|
vim.api.nvim_buf_set_extmark(bufnr, ns_id, line_idx, 0, {
|
||||||
|
@ -160,17 +177,31 @@ function M.apply_diff_visualization(bufnr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Show deletions as virtual text above their position with full-width background
|
-- Apply highlighting for replacements (show old text inline with new text)
|
||||||
|
for j, repl in ipairs(replacements) do
|
||||||
|
if repl.line >= 0 and repl.line < #buf_lines then
|
||||||
|
-- Highlight the line as changed
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns_id, repl.line, 0, {
|
||||||
|
line_hl_group = 'DiffChange',
|
||||||
|
id = 5000 + i * 100 + j
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Show the old text as virtual text at end of line
|
||||||
|
vim.api.nvim_buf_set_extmark(bufnr, ns_id, repl.line, 0, {
|
||||||
|
virt_text = {{' ← was: ' .. repl.old_text, 'DiffDelete'}},
|
||||||
|
virt_text_pos = 'eol',
|
||||||
|
id = 6000 + i * 100 + j
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Show pure deletions as virtual text above their position
|
||||||
for j, del in ipairs(deletions) do
|
for j, del in ipairs(deletions) do
|
||||||
if del.line >= 0 and del.line <= #buf_lines then
|
if del.line >= 0 and del.line <= #buf_lines then
|
||||||
-- Calculate full width for the deletion line
|
-- Show deletion with a more subtle approach to avoid visual artifacts
|
||||||
local text = '- ' .. del.text
|
|
||||||
local win_width = vim.api.nvim_win_get_width(0)
|
|
||||||
local padding = string.rep(' ', math.max(0, win_width - vim.fn.strdisplaywidth(text)))
|
|
||||||
|
|
||||||
vim.api.nvim_buf_set_extmark(bufnr, ns_id, del.line, 0, {
|
vim.api.nvim_buf_set_extmark(bufnr, ns_id, del.line, 0, {
|
||||||
virt_lines = {{
|
virt_lines = {{
|
||||||
{'- ' .. del.text .. padding, 'DiffDelete'}
|
{'- ' .. del.text, 'DiffDelete'}
|
||||||
}},
|
}},
|
||||||
virt_lines_above = true,
|
virt_lines_above = true,
|
||||||
id = 3000 + i * 100 + j
|
id = 3000 + i * 100 + j
|
||||||
|
@ -178,10 +209,12 @@ function M.apply_diff_visualization(bufnr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add sign in gutter for hunk (use first addition or deletion line)
|
-- Add sign in gutter for hunk (use first addition, replacement, or deletion line)
|
||||||
local sign_line = nil
|
local sign_line = nil
|
||||||
if #additions > 0 then
|
if #additions > 0 then
|
||||||
sign_line = additions[1]
|
sign_line = additions[1]
|
||||||
|
elseif #replacements > 0 then
|
||||||
|
sign_line = replacements[1].line
|
||||||
elseif #deletions > 0 then
|
elseif #deletions > 0 then
|
||||||
sign_line = deletions[1].line
|
sign_line = deletions[1].line
|
||||||
else
|
else
|
||||||
|
@ -191,10 +224,13 @@ function M.apply_diff_visualization(bufnr)
|
||||||
local sign_text = '>'
|
local sign_text = '>'
|
||||||
local sign_hl = 'DiffAdd'
|
local sign_hl = 'DiffAdd'
|
||||||
|
|
||||||
-- If hunk has deletions, use different sign
|
-- Choose appropriate sign based on hunk content
|
||||||
if #deletions > 0 then
|
if #replacements > 0 then
|
||||||
sign_text = '~'
|
sign_text = '~'
|
||||||
sign_hl = 'DiffChange'
|
sign_hl = 'DiffChange'
|
||||||
|
elseif #deletions > 0 then
|
||||||
|
sign_text = '-'
|
||||||
|
sign_hl = 'DiffDelete'
|
||||||
end
|
end
|
||||||
|
|
||||||
if sign_line and sign_line >= 0 and sign_line < #buf_lines then
|
if sign_line and sign_line >= 0 and sign_line < #buf_lines then
|
||||||
|
|
Loading…
Reference in New Issue