mirror of
https://github.com/neovim/neovim.git
synced 2025-09-16 00:08:19 +00:00
fix(terminal): crash if TermClose deletes own buffer #19222
- Partially fixes #10386 except for the case where the alternate buffer is the default, empty, first buffer created on startup. #vimlife - TODO: port patches related to `can_unload_buffer`, maybe that fully fixes #10386? vim-patch:8.0.1732: crash when terminal API call deletes the buffer
This commit is contained in:
@@ -89,6 +89,7 @@
|
|||||||
static char *msg_loclist = N_("[Location List]");
|
static char *msg_loclist = N_("[Location List]");
|
||||||
static char *msg_qflist = N_("[Quickfix List]");
|
static char *msg_qflist = N_("[Quickfix List]");
|
||||||
static char *e_auabort = N_("E855: Autocommands caused command to abort");
|
static char *e_auabort = N_("E855: Autocommands caused command to abort");
|
||||||
|
static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
|
||||||
|
|
||||||
// Number of times free_buffer() was called.
|
// Number of times free_buffer() was called.
|
||||||
static int buf_free_count = 0;
|
static int buf_free_count = 0;
|
||||||
@@ -438,7 +439,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
|||||||
// Disallow deleting the buffer when it is locked (already being closed or
|
// Disallow deleting the buffer when it is locked (already being closed or
|
||||||
// halfway a command that relies on it). Unloading is allowed.
|
// halfway a command that relies on it). Unloading is allowed.
|
||||||
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
|
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
|
||||||
emsg(_("E937: Attempt to delete a buffer that is in use"));
|
emsg(_(e_buflocked));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +530,9 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool i
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buf->terminal) {
|
if (buf->terminal) {
|
||||||
|
buf->b_locked++;
|
||||||
terminal_close(&buf->terminal, -1);
|
terminal_close(&buf->terminal, -1);
|
||||||
|
buf->b_locked--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always remove the buffer when there is no file name.
|
// Always remove the buffer when there is no file name.
|
||||||
@@ -1182,6 +1185,10 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
|
|||||||
if (unload) {
|
if (unload) {
|
||||||
int forward;
|
int forward;
|
||||||
bufref_T bufref;
|
bufref_T bufref;
|
||||||
|
if (buf->b_locked) {
|
||||||
|
emsg(_(e_buflocked));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
set_bufref(&bufref, buf);
|
set_bufref(&bufref, buf);
|
||||||
|
|
||||||
// When unloading or deleting a buffer that's already unloaded and
|
// When unloading or deleting a buffer that's already unloaded and
|
||||||
|
@@ -169,7 +169,9 @@ func Test_autocmd_bufunload_avoiding_SEGV_01()
|
|||||||
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
|
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
|
||||||
augroup END
|
augroup END
|
||||||
|
|
||||||
call assert_fails('edit bb.txt', 'E937:')
|
" Todo: check for E937 generated first
|
||||||
|
" call assert_fails('edit bb.txt', 'E937:')
|
||||||
|
call assert_fails('edit bb.txt', 'E517:')
|
||||||
|
|
||||||
autocmd! test_autocmd_bufunload
|
autocmd! test_autocmd_bufunload
|
||||||
augroup! test_autocmd_bufunload
|
augroup! test_autocmd_bufunload
|
||||||
@@ -540,7 +542,7 @@ func Test_three_windows()
|
|||||||
e Xtestje2
|
e Xtestje2
|
||||||
sp Xtestje1
|
sp Xtestje1
|
||||||
call assert_fails('e', 'E937:')
|
call assert_fails('e', 'E937:')
|
||||||
call assert_equal('Xtestje2', expand('%'))
|
call assert_equal('Xtestje1', expand('%'))
|
||||||
|
|
||||||
" Test changing buffers in a BufWipeout autocommand. If this goes wrong
|
" Test changing buffers in a BufWipeout autocommand. If this goes wrong
|
||||||
" there are ml_line errors and/or a Crash.
|
" there are ml_line errors and/or a Crash.
|
||||||
@@ -563,7 +565,6 @@ func Test_three_windows()
|
|||||||
|
|
||||||
au!
|
au!
|
||||||
enew
|
enew
|
||||||
bwipe! Xtestje1
|
|
||||||
call delete('Xtestje1')
|
call delete('Xtestje1')
|
||||||
call delete('Xtestje2')
|
call delete('Xtestje2')
|
||||||
call delete('Xtestje3')
|
call delete('Xtestje3')
|
||||||
|
@@ -7,6 +7,8 @@ local eval, eq, neq, retry =
|
|||||||
helpers.eval, helpers.eq, helpers.neq, helpers.retry
|
helpers.eval, helpers.eq, helpers.neq, helpers.retry
|
||||||
local ok = helpers.ok
|
local ok = helpers.ok
|
||||||
local feed = helpers.feed
|
local feed = helpers.feed
|
||||||
|
local pcall_err = helpers.pcall_err
|
||||||
|
local assert_alive = helpers.assert_alive
|
||||||
local iswin = helpers.iswin
|
local iswin = helpers.iswin
|
||||||
|
|
||||||
describe('autocmd TermClose', function()
|
describe('autocmd TermClose', function()
|
||||||
@@ -16,6 +18,24 @@ describe('autocmd TermClose', function()
|
|||||||
command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=')
|
command('set shellcmdflag=EXE shellredir= shellpipe= shellquote= shellxquote=')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
local function test_termclose_delete_own_buf()
|
||||||
|
command('autocmd TermClose * bdelete!')
|
||||||
|
command('terminal')
|
||||||
|
eq('Vim(bdelete):E937: Attempt to delete a buffer that is in use', pcall_err(command, 'bdelete!'))
|
||||||
|
assert_alive()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: fixed after merging patches for `can_unload_buffer`?
|
||||||
|
pending('TermClose deleting its own buffer, altbuf = buffer 1 #10386', function()
|
||||||
|
test_termclose_delete_own_buf()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('TermClose deleting its own buffer, altbuf NOT buffer 1 #10386', function()
|
||||||
|
command('edit foo1')
|
||||||
|
test_termclose_delete_own_buf()
|
||||||
|
end)
|
||||||
|
|
||||||
it('triggers when fast-exiting terminal job stops', function()
|
it('triggers when fast-exiting terminal job stops', function()
|
||||||
command('autocmd TermClose * let g:test_termclose = 23')
|
command('autocmd TermClose * let g:test_termclose = 23')
|
||||||
command('terminal')
|
command('terminal')
|
||||||
|
Reference in New Issue
Block a user