fix(scrollbind): window jumps back and forth with tall virt_lines block

Problem:
Scrolling a 'scrollbind' window whose peer has a virt_lines block
taller than the window makes the peer jump back and forth.

Solution:
After scrollup()/scrolldown(), sync w_scbind_pos to the actual
get_vtopline() when the target was not clamped to buffer bounds.

Ref: esmuellert/codediff.nvim#519
AI-assisted
This commit is contained in:
Yanuo Ma
2026-08-27 10:04:33 -04:00
committed by Lewis Russell
parent 8d5ebdf986
commit b781e95a24
2 changed files with 90 additions and 0 deletions

View File

@@ -2185,6 +2185,12 @@ void check_scrollbind(linenr_T vtopline_diff, int leftcol_diff)
} else {
scrolldown(curwin, -y, false);
}
// The scroll may not reach new_vtopline (tall virt_lines block).
// Sync w_scbind_pos to the actual position, else the miss repeats.
if (curwin->w_scbind_pos == new_vtopline) {
curwin->w_scbind_pos = get_vtopline(curwin);
}
}
redraw_later(curwin, UPD_VALID);

View File

@@ -439,4 +439,88 @@ describe('Scrollbind', function()
]],
})
end)
it('does not jump back and forth with a virt_lines block taller than the window', function()
n.exec_lua(function()
local lines = {} --- @type string[]
for i = 1, 20 do
lines[i] = tostring(i)
end
-- Bound window: one virt_lines block of 15 rows, window is 10 rows.
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
vim.bo.buftype = 'nofile'
local virt_lines = {} --- @type table[]
for i = 1, 15 do
virt_lines[i] = { { 'v' .. i } }
end
vim.api.nvim_buf_set_extmark(0, vim.api.nvim_create_namespace('test'), 4, 0, {
virt_lines = virt_lines,
})
vim.wo.scrollbind = true
-- Scrolled window: plain buffer.
vim.cmd.vnew()
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
vim.bo.buftype = 'nofile'
vim.wo.scrollbind = true
end)
n.feed('5<C-e>')
screen:expect({
grid = [[
^6 │v7 |
7 │v8 |
8 │v9 |
9 │v10 |
10 │v11 |
11 │v12 |
12 │v13 |
13 │v14 |
14 │v15 |
15 │6 |
{3:[Scratch] }{2:[Scratch] }|
|
]],
})
n.feed('<C-e>')
screen:expect({
grid = [[
^7 │v8 |
8 │v9 |
9 │v10 |
10 │v11 |
11 │v12 |
12 │v13 |
13 │v14 |
14 │v15 |
15 │6 |
16 │7 |
{3:[Scratch] }{2:[Scratch] }|
|
]],
})
n.feed('<C-e>')
screen:expect({
grid = [[
^8 │v9 |
9 │v10 |
10 │v11 |
11 │v12 |
12 │v13 |
13 │v14 |
14 │v15 |
15 │6 |
16 │7 |
17 │8 |
{3:[Scratch] }{2:[Scratch] }|
|
]],
})
end)
end)