mirror of
https://github.com/neovim/neovim.git
synced 2026-02-21 09:50:19 +10:00
fix(buffer): don't reuse 1-line terminal buffer (#37261)
Problem: :edit and :enew may reuse a 1-line terminal buffer, causing
the new buffer to still be a terminal buffer.
Solution: Don't reuse a terminal buffer, as it's not reused when it has
more than 1 line.
After this change close_buffer() is the only place where buf_freeall()
can be called on a terminal buffer, so move the buf_close_terminal()
call into buf_freeall() to save some code. Furthermore, closing the
terminal in buf_freeall() is probably more correct anyway, as it is
"things allocated for a buffer that are related to the file".
Also, remove the useless check for on_detach callbacks deleting buffer.
Even if b_locked fails to prevent that, the crash will happen at the end
of buf_updates_unload() first. On the other hand, many other call sites
of buf_updates_unload() and other buffer_updates_* functions don't set
b_locked, which may be a problem as well...
(cherry picked from commit 23aa4853b3)
This commit is contained in:
committed by
github-actions[bot]
parent
ea871923eb
commit
cae3c838a7
@@ -659,18 +659,6 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
||||
|
||||
buf->b_nwindows = nwindows;
|
||||
|
||||
if (buf->terminal) {
|
||||
buf->b_locked_split++;
|
||||
buf_close_terminal(buf);
|
||||
buf->b_locked_split--;
|
||||
|
||||
// Must check this before calling buf_freeall(), otherwise is_curbuf will be true
|
||||
// in buf_freeall() but still false here, leading to a 0-line buffer.
|
||||
if (buf == curbuf && !is_curbuf) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
buf_freeall(buf, ((del_buf ? BFA_DEL : 0)
|
||||
+ (wipe_buf ? BFA_WIPE : 0)
|
||||
+ (ignore_abort ? BFA_IGNORE_ABORT : 0)));
|
||||
@@ -809,11 +797,12 @@ void buf_freeall(buf_T *buf, int flags)
|
||||
bufref_T bufref;
|
||||
set_bufref(&bufref, buf);
|
||||
|
||||
buf_updates_unload(buf, false);
|
||||
if (!bufref_valid(&bufref)) {
|
||||
// on_detach callback deleted the buffer.
|
||||
return;
|
||||
if (buf->terminal) {
|
||||
buf_close_terminal(buf);
|
||||
}
|
||||
|
||||
buf_updates_unload(buf, false);
|
||||
|
||||
if ((buf->b_ml.ml_mfp != NULL)
|
||||
&& apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf)
|
||||
&& !bufref_valid(&bufref)) {
|
||||
@@ -2064,6 +2053,7 @@ bool curbuf_reusable(void)
|
||||
return (curbuf != NULL
|
||||
&& curbuf->b_ffname == NULL
|
||||
&& curbuf->b_nwindows <= 1
|
||||
&& !curbuf->terminal
|
||||
&& (curbuf->b_ml.ml_mfp == NULL || buf_is_empty(curbuf))
|
||||
&& !bt_quickfix(curbuf)
|
||||
&& !curbufIsChanged());
|
||||
|
||||
@@ -609,7 +609,7 @@ void terminal_close(Terminal **termpp, int status)
|
||||
|
||||
if (status == -1 || exiting) {
|
||||
// If this was called by buf_close_terminal() (status is -1), or if exiting, we
|
||||
// must inform the buffer the terminal no longer exists so that close_buffer()
|
||||
// must inform the buffer the terminal no longer exists so that buf_freeall()
|
||||
// won't call buf_close_terminal() again.
|
||||
// If inside Terminal mode event handling, setting buf_handle to 0 also
|
||||
// informs terminal_enter() to call the close callback before returning.
|
||||
@@ -2110,7 +2110,7 @@ static void refresh_terminal(Terminal *term)
|
||||
{
|
||||
buf_T *buf = handle_get_buffer(term->buf_handle);
|
||||
if (!buf) {
|
||||
// Destroyed by `close_buffer`. Do not do anything else.
|
||||
// Destroyed by `buf_freeall()`. Do not do anything else.
|
||||
return;
|
||||
}
|
||||
linenr_T ml_before = buf->b_ml.ml_line_count;
|
||||
|
||||
@@ -1024,6 +1024,33 @@ describe(':terminal buffer', function()
|
||||
|
||||
test_open_term_in_buf_with_closed_term(env)
|
||||
end)
|
||||
|
||||
it('with nvim_open_term() channel and only 1 line is not reused by :enew', function()
|
||||
command('1new')
|
||||
local oldbuf = api.nvim_get_current_buf()
|
||||
api.nvim_open_term(oldbuf, {})
|
||||
eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
|
||||
feed('i')
|
||||
eq({ mode = 't', blocking = false }, api.nvim_get_mode())
|
||||
feed([[<C-\><C-N>]])
|
||||
eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
|
||||
|
||||
command('enew')
|
||||
neq(oldbuf, api.nvim_get_current_buf())
|
||||
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
||||
feed('i')
|
||||
eq({ mode = 'i', blocking = false }, api.nvim_get_mode())
|
||||
feed('<Esc>')
|
||||
eq({ mode = 'n', blocking = false }, api.nvim_get_mode())
|
||||
|
||||
command('buffer #')
|
||||
eq(oldbuf, api.nvim_get_current_buf())
|
||||
eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
|
||||
feed('i')
|
||||
eq({ mode = 't', blocking = false }, api.nvim_get_mode())
|
||||
feed([[<C-\><C-N>]])
|
||||
eq({ mode = 'nt', blocking = false }, api.nvim_get_mode())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('on_lines does not emit out-of-bounds line indexes when', function()
|
||||
|
||||
Reference in New Issue
Block a user