Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions lua/diffview/scene/layouts/diff_1_inline.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,28 @@ local M = {}
---`'diffopt'` forces `vim.diff` to disable the heuristic instead of falling
---back to whatever default the vim.diff implementation currently uses.
---
---`linematch` is intentionally not forwarded. In `result_type = "indices"`
---mode it splits a single modify-hunk into smaller hunks that pair lines by
---similarity rather than by position — e.g. an old `-- foo = X, -- comment`
---line gets paired with a new `-- bar = Y, -- comment` line further down
---because both share the `-- ` prefix and a `, -- ` separator, even though
---the natural (positional) pair is the new uncommented `foo = X` line. The
---inline renderer pairs lines positionally inside each hunk, so a non-zero
---linematch causes deletions to render anchored against the wrong new line.
---`linematch` defaults to 60 (git's default) so a single modify-hunk whose
---old and new sides differ in length is split into properly-aligned
---sub-hunks. Without it, the renderer pairs lines positionally inside the
---one large hunk, so e.g., commenting-out a block (8 old lines vs 9 new
---lines: a new TEMP header plus 8 lines each gaining a `-- ` prefix) pairs
---every `vim.api.nvim_set_hl(...)` line with the wrong commented copy,
---producing > `INTRALINE_MAX_HUNKS` per pair and falling back to whole-line
---add+delete instead of showing only the `-- ` insertion. The user's
---`'diffopt'` `linematch:N` entry overrides this default (including `0` to
---opt back into positional pairing).
---@return InlineDiffOpts
local function effective_diffopt()
local out = { indent_heuristic = false }
local out = { indent_heuristic = false, linematch = 60 }
local diffopt = vim.opt.diffopt --[[@as vim.Option]]
for _, v in
ipairs(diffopt:get() --[[@as string[] ]])
do
local key, val = v:match("^([%w_-]+):(.+)$")
if key == "algorithm" then
out.algorithm = val
elseif key == "linematch" then
out.linematch = tonumber(val)
elseif v == "indent-heuristic" then
out.indent_heuristic = true
elseif v == "iwhite" then
Expand Down
50 changes: 50 additions & 0 deletions lua/diffview/tests/functional/inline_diff_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,56 @@ describe("diffview.scene.inline_diff", function()
assert.are.equal("DiffviewDiffDeleteInline", h)
end
end)

-- Commenting-out a block prepends `-- ` to N lines and inserts a header
-- line above them, producing an N:N+1 modify hunk. With positional
-- pairing inside one hunk the new header pairs with the first old line
-- and every subsequent pair becomes a shifted, dissimilar pair — so the
-- char-level diff fragments, returns `"skipped"`, and the block renders
-- as a full add+delete instead of inline `-- ` insertions. Linematch
-- splits the hunk into a pure-add for the header plus an aligned N:N
-- modify, which the renderer can pair positionally without skipping.
it("renders a commented-out block as inline `-- ` insertions with linematch", function()
local bufnr = fresh_buf({
"-- TEMP: disable overrides.",
"-- vim.api.nvim_set_hl(0, 'A')",
"-- vim.api.nvim_set_hl(0, 'B')",
})
inline_diff.render(bufnr, { "vim.api.nvim_set_hl(0, 'A')", "vim.api.nvim_set_hl(0, 'B')" }, {
"-- TEMP: disable overrides.",
"-- vim.api.nvim_set_hl(0, 'A')",
"-- vim.api.nvim_set_hl(0, 'B')",
}, { style = "overleaf", linematch = 60 })

local marks = extmarks(bufnr)
-- The header is a pure addition and should be the only row carrying a
-- full `DiffviewDiffAdd` line highlight; the commented lines below
-- must NOT pick up that backdrop (which is the symptom of the
-- positional-pairing fallback).
local add_rows = {}
for _, h in ipairs(line_hls(marks)) do
if h.hl == "DiffviewDiffAdd" then
add_rows[#add_rows + 1] = h.row
end
end
assert.are.same({ 0 }, add_rows)

-- Each commented line carries a `DiffviewDiffAddInline` extmark over
-- the inserted `-- ` prefix.
local adds = char_ranges(marks, "DiffviewDiffAddInline")
local rows_with_add = {}
for _, a in ipairs(adds) do
rows_with_add[a.row] = true
end
assert.is_true(rows_with_add[1], "expected inline add on row 1")
assert.is_true(rows_with_add[2], "expected inline add on row 2")

-- No paired old lines are echoed as deletion virt_lines (the
-- skipped-fallback symptom): only the inline strikethrough virt_text
-- representing the empty old prefix would appear, and even that is
-- empty for a pure prefix insertion.
assert.are.same({}, virt_line_counts(marks))
end)
end)

describe("deletion_highlight", function()
Expand Down
16 changes: 13 additions & 3 deletions lua/diffview/tests/functional/layouts_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1769,14 +1769,24 @@ describe("diffview.scene.layouts.diff_1_inline diffopt forwarding", function()
eq(true, effective_diffopt().indent_heuristic)
end)

it("never forwards linematch even when set in 'diffopt'", function()
set_diffopt({ "linematch:60", "iblank" })
it("defaults linematch to 60 when absent from 'diffopt'", function()
set_diffopt({ "internal" })
eq(60, effective_diffopt().linematch)
end)

it("forwards linematch:N from 'diffopt'", function()
set_diffopt({ "linematch:30", "iblank" })
local opts = effective_diffopt()
assert.is_nil(opts.linematch)
eq(30, opts.linematch)
-- Sanity-check that other entries still parse (confirms the loop ran).
eq(true, opts.ignore_blank_lines)
end)

it("honours an explicit linematch:0 to opt out of line-matching", function()
set_diffopt({ "linematch:0" })
eq(0, effective_diffopt().linematch)
end)

it("leaves ignore flags nil when 'diffopt' has no corresponding entry", function()
set_diffopt({ "internal" })
local opts = effective_diffopt()
Expand Down
Loading