Skip to content
Open
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
23 changes: 13 additions & 10 deletions lua/codediff/ui/view/compact.lua
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,6 @@ function M.compute_visible_lines(changes, side, line_count, context_lines)
local range_start = range.start_line
local range_end = range.end_line -- exclusive

-- For zero-width ranges (pure insertion/deletion), use start_line as anchor
if range_start == range_end then
range_end = range_start + 1
end

local ctx_start = math.max(1, range_start - context_lines)
local ctx_end = math.min(line_count, range_end - 1 + context_lines)
for l = ctx_start, ctx_end do
Expand All @@ -98,7 +93,17 @@ end
-- closes folds — navigation keys like zj/zk/]z/[z are excluded since they
-- don't change fold state).
local FOLD_KEYS = {
"zo", "zO", "zc", "zC", "za", "zA", "zv", "zx", "zX", "zM", "zR",
"zo",
"zO",
"zc",
"zC",
"za",
"zA",
"zv",
"zx",
"zX",
"zM",
"zR",
}

--- Install buffer-local keymap wraps that propagate fold-open/close actions
Expand Down Expand Up @@ -126,8 +131,7 @@ local function setup_fold_sync(session, tabpage)
lifecycle.begin_keymap_scope(tabpage, "compact")

for _, pane in ipairs(panes) do
if pane.win and vim.api.nvim_win_is_valid(pane.win)
and pane.buf and vim.api.nvim_buf_is_valid(pane.buf) then
if pane.win and vim.api.nvim_win_is_valid(pane.win) and pane.buf and vim.api.nvim_buf_is_valid(pane.buf) then
for _, key in ipairs(FOLD_KEYS) do
lifecycle.set_buf_keymap(tabpage, pane.buf, "n", key, function()
local count = vim.v.count > 0 and tostring(vim.v.count) or ""
Expand All @@ -153,8 +157,7 @@ local function setup_fold_sync(session, tabpage)
_syncing = true
local ok, err = pcall(function()
for _, other in ipairs(panes) do
if other.win ~= pane.win
and other.win and vim.api.nvim_win_is_valid(other.win) then
if other.win ~= pane.win and other.win and vim.api.nvim_win_is_valid(other.win) then
local target = M.compute_corresponding_lnum(changes, pane.side, other.side, src_lnum)
target = math.max(1, math.min(target, vim.api.nvim_buf_line_count(other.buf)))
-- Save & restore cursor so the partner pane doesn't visibly jump.
Expand Down
26 changes: 21 additions & 5 deletions tests/ui/view/compact_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,16 @@ describe("compact mode (#344)", function()
assert.is_nil(visible[11], "buffer only has 10 lines")
end)

it("treats zero-width ranges (pure insertion/deletion) as a one-line anchor", function()
-- Zero-width range at line 5 — gets treated as the one-line slice [5,6).
-- With context=2: visible = max(1, 5-2)..min(N, 6-1+2) = 3..7.
it("shows context on both sides of zero-width ranges without an extra anchor line", function()
-- A zero-width range is a boundary between lines, not a changed line.
-- With context=2, show two real lines on either side: 3..6.
local changes = { make_change(5, 5, 5, 8) }
local visible = compact.compute_visible_lines(changes, "original", 100, 2)
for l = 3, 7 do
for l = 3, 6 do
assert.is_true(visible[l], "line " .. l .. " should be visible")
end
assert.is_nil(visible[2])
assert.is_nil(visible[8])
assert.is_nil(visible[7])
end)

it("merges overlapping hunks' visible ranges via set union", function()
Expand Down Expand Up @@ -162,6 +162,8 @@ describe("compact mode (#344)", function()
-- Original side: anchor at 5, ±2 context → 3..6.
assert.is_true(orig_visible[5])
assert.is_true(orig_visible[3])
assert.is_true(orig_visible[6])
assert.is_nil(orig_visible[7])
assert.is_nil(orig_visible[10])

-- Modified side: hunk 10..12, ±2 context → 8..14.
Expand All @@ -170,6 +172,20 @@ describe("compact mode (#344)", function()
assert.is_nil(mod_visible[5])
end)

it("aligns the first folded line after a pure insertion", function()
local changes = { make_change(50, 50, 60, 61) }
local original = compact.compute_visible_lines(changes, "original", 100, 3)
local modified = compact.compute_visible_lines(changes, "modified", 110, 3)

-- Context ends at 52/63, so the next folds begin at corresponding
-- unchanged lines 53/64. Before the fix, the original fold began at 54.
assert.is_true(original[52])
assert.is_true(modified[63])
assert.is_nil(original[53])
assert.is_nil(modified[64])
assert.equal(64, compact.compute_corresponding_lnum(changes, "original", "modified", 53))
end)

it("supports zero context (only the hunk lines visible)", function()
local changes = { make_change(10, 13, 10, 13) }
local visible = compact.compute_visible_lines(changes, "original", 100, 0)
Expand Down