fix(terminal): wrong scrollback with nvim_open_term() on buffer (#37791)

Problem:  Wrong scrollback when passing a buffer with many lines to
          nvim_open_term().
Solution: Delete all buffer lines before opening the terminal.

I tried to use buf_clear(), but it crashes inside deleted_lines_mark(),
so I'll just use deleted_lines_buf() for now. The behavior of marks can
be decided later.
This commit is contained in:
zeertzjq
2026-02-10 06:30:02 +08:00
committed by GitHub
parent 4b2cbc2154
commit 54468c1c3c
2 changed files with 101 additions and 2 deletions

View File

@@ -544,8 +544,8 @@ Terminal *terminal_alloc(buf_T *buf, TerminalOptions opts)
}
vterm_state_set_termprop(state, VTERM_PROP_CURSORBLINK, &cursor_blink);
// force a initial refresh of the screen to ensure the buffer will always
// have as many lines as screen rows when refresh_scrollback is called
// Force a initial refresh of the screen to ensure the buffer will always
// have as many lines as screen rows when refresh_scrollback() is called.
term->invalid_start = 0;
term->invalid_end = opts.height;
@@ -556,6 +556,12 @@ Terminal *terminal_alloc(buf_T *buf, TerminalOptions opts)
// events from this queue are copied back onto the main event queue.
term->pending.events = multiqueue_new(NULL, NULL);
linenr_T line_count = buf->b_ml.ml_line_count;
while (!(buf->b_ml.ml_flags & ML_EMPTY)) {
ml_delete_buf(buf, 1, false);
}
deleted_lines_buf(buf, 1, line_count);
return term;
}

View File

@@ -1102,3 +1102,96 @@ describe('pending scrollback line handling', function()
assert_alive()
end)
end)
describe('nvim_open_term()', function()
local screen --- @type test.functional.ui.screen
local buf --- @type integer
local win --- @type integer
before_each(function()
clear()
screen = Screen.new(30, 7)
screen:add_extra_attr_ids({
[100] = { foreground = tonumber('0xe00000'), fg_indexed = true },
[101] = { foreground = Screen.colors.White, background = Screen.colors.DarkGreen },
[102] = {
bold = true,
foreground = Screen.colors.White,
background = Screen.colors.DarkGreen,
},
})
api.nvim_buf_set_lines(0, 0, -1, true, { '\027[31mTEST\027[0m 0' })
feed('yy99pG$<C-V>98kg<C-A>')
screen:expect([[
{18:^[}[31mTEST{18:^[}[0m 0 |
{18:^[}[31mTEST{18:^[}[0m ^1 |
{18:^[}[31mTEST{18:^[}[0m 2 |
{18:^[}[31mTEST{18:^[}[0m 3 |
{18:^[}[31mTEST{18:^[}[0m 4 |
{18:^[}[31mTEST{18:^[}[0m 5 |
99 lines changed |
]])
buf = api.nvim_get_current_buf()
win = api.nvim_get_current_win()
command('set winwidth=10 | 10vnew')
end)
local function check_buffer_lines(start, stop)
local lines = api.nvim_buf_get_lines(buf, 0, -1, true)
for i = start, stop do
eq(('TEST %d'):format(i), lines[i - start + 1])
end
end
local function check_common()
feed('<C-W>lG')
screen:expect([[
│{100:TEST} 96 |
{1:~ }│{100:TEST} 97 |
{1:~ }│{100:TEST} 98 |
{1:~ }│{100:TEST} 99 |
{1:~ }│^ |
{2:[No Name] }{102:[Scratch] [-] }|
99 lines changed |
]])
end
it('on buffer with fewer lines than scrollback', function()
exec_lua(function()
vim.api.nvim_open_term(buf, {})
vim.api.nvim_win_set_cursor(win, { 3, 0 })
end)
screen:expect([[
^ │{100:TEST} 0 |
{1:~ }│{100:TEST} 1 |
{1:~ }│{100:TEST} 2 |
{1:~ }│{100:TEST} 3 |
{1:~ }│{100:TEST} 4 |
{3:[No Name] }{101:[Scratch] [-] }|
99 lines changed |
]])
eq({ 3, 0 }, api.nvim_win_get_cursor(win))
check_buffer_lines(0, 99)
check_common()
end)
it('on buffer with more lines than scrollback', function()
api.nvim_set_option_value('scrollback', 10, { buf = buf })
exec_lua(function()
vim.api.nvim_open_term(buf, {})
vim.api.nvim_win_set_cursor(win, { 3, 3 })
end)
screen:expect([[
^ │{100:TEST} 86 |
{1:~ }│{100:TEST} 87 |
{1:~ }│{100:TEST} 88 |
{1:~ }│{100:TEST} 89 |
{1:~ }│{100:TEST} 90 |
{3:[No Name] }{101:[Scratch] [-] }|
99 lines changed |
]])
eq({ 1, 0 }, api.nvim_win_get_cursor(win))
check_buffer_lines(86, 99)
check_common()
end)
end)