Skip to content

Commit

Permalink
fix(terminal): fix resize crash with pending scrollback (#16665)
Browse files Browse the repository at this point in the history
refresh_scrollback assumes pending scrollback rows exist only if the
terminal window height decreased (or the screen was full).

However, after accumulating scrollback, it's possible in some cases for
the terminal height to increase before refresh_scrollback is called via
invalidation (especially when the terminal buffer isn't initially
displayed in a window before nvim_open_term), which may crash.

As we'll have enough room for some scrollback rows, just append them to
the top of the buffer until it fills the window, then continue with the
previous logic for any remaining scrollback rows if necessary.
  • Loading branch information
github-actions[bot] committed Dec 15, 2021
1 parent 785bace commit ae249d8
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 1 deletion.
13 changes: 12 additions & 1 deletion src/nvim/terminal.c
Expand Up @@ -1466,6 +1466,17 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
int width, height;
vterm_get_size(term->vt, &height, &width);

// May still have pending scrollback after increase in terminal height if the
// scrollback wasn't refreshed in time; append these to the top of the buffer.
int row_offset = term->sb_pending;
while (term->sb_pending > 0 && buf->b_ml.ml_line_count < height) {
fetch_row(term, term->sb_pending - row_offset - 1, width);
ml_append(0, (uint8_t *)term->textbuf, 0, false);
appended_lines(0, 1);
term->sb_pending--;
}

row_offset -= term->sb_pending;
while (term->sb_pending > 0) {
// This means that either the window height has decreased or the screen
// became full and libvterm had to push all rows up. Convert the first
Expand All @@ -1476,7 +1487,7 @@ static void refresh_scrollback(Terminal *term, buf_T *buf)
ml_delete(1, false);
deleted_lines(1, 1);
}
fetch_row(term, -term->sb_pending, width);
fetch_row(term, -term->sb_pending - row_offset, width);
int buf_index = (int)buf->b_ml.ml_line_count - height;
ml_append(buf_index, (uint8_t *)term->textbuf, 0, false);
appended_lines(buf_index, 1);
Expand Down
70 changes: 70 additions & 0 deletions test/functional/terminal/scrollback_spec.lua
Expand Up @@ -12,6 +12,8 @@ local curbufmeths = helpers.curbufmeths
local nvim = helpers.nvim
local feed_data = thelpers.feed_data
local pcall_err = helpers.pcall_err
local exec_lua = helpers.exec_lua
local assert_alive = helpers.assert_alive

describe(':terminal scrollback', function()
local screen
Expand Down Expand Up @@ -527,3 +529,71 @@ describe("'scrollback' option", function()
end)

end)

describe("pending scrollback line handling", function()
local screen

before_each(function()
clear()
screen = Screen.new(30, 7)
screen:attach()
screen:set_default_attr_ids {
[1] = {foreground = Screen.colors.Brown},
[2] = {reverse = true},
[3] = {bold = true},
}
end)

it("does not crash after setting 'number' #14891", function()
exec_lua [[
local a = vim.api
local buf = a.nvim_create_buf(true, true)
local chan = a.nvim_open_term(buf, {})
a.nvim_win_set_option(0, "number", true)
a.nvim_chan_send(chan, ("a\n"):rep(11) .. "a")
a.nvim_win_set_buf(0, buf)
]]
screen:expect [[
{1: 1 }^a |
{1: 2 } a |
{1: 3 } a |
{1: 4 } a |
{1: 5 } a |
{1: 6 } a |
|
]]
feed('G')
screen:expect [[
{1: 7 } a |
{1: 8 } a |
{1: 9 } a |
{1: 10 } a |
{1: 11 } a |
{1: 12 } ^a |
|
]]
assert_alive()
end)

it("does not crash after nvim_buf_call #14891", function()
exec_lua [[
local a = vim.api
local bufnr = a.nvim_create_buf(false, true)
a.nvim_buf_call(bufnr, function()
vim.fn.termopen({"echo", ("hi\n"):rep(11)})
end)
a.nvim_win_set_buf(0, bufnr)
vim.cmd("startinsert")
]]
screen:expect [[
hi |
hi |
hi |
|
|
[Process exited 0]{2: } |
{3:-- TERMINAL --} |
]]
assert_alive()
end)
end)

0 comments on commit ae249d8

Please sign in to comment.