diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2021-12-15 15:18:06 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-15 08:18:06 -0700 |
commit | 0a1391fdd742608de4efb6e6f2ca0f57590a0869 (patch) | |
tree | 39d1c8530f2a210893845fe3150f57ee1f27f41f | |
parent | cf65071e2ef6defcb7125daddf09fc4a52cc85cd (diff) | |
download | rneovim-0a1391fdd742608de4efb6e6f2ca0f57590a0869.tar.gz rneovim-0a1391fdd742608de4efb6e6f2ca0f57590a0869.tar.bz2 rneovim-0a1391fdd742608de4efb6e6f2ca0f57590a0869.zip |
fix(terminal): fix resize crash with pending scrollback (#14891)
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.
-rw-r--r-- | src/nvim/terminal.c | 13 | ||||
-rw-r--r-- | test/functional/terminal/scrollback_spec.lua | 70 |
2 files changed, 82 insertions, 1 deletions
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 04068a3cb8..d97c24dcf7 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -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 @@ -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); diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index b932c58430..11bdc73a47 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -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 @@ -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) |