mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:7.4.2324
Problem: Crash when editing a new buffer and BufUnload autocommand wipes
out the new buffer. (Norio Takagi)
Solution: Don't allow wiping out this buffer. (partly by Hirohito Higashi)
Move old style test13 into test_autocmd. Avoid ml_get error when
editing a file.
e0ab94e712
This commit is contained in:
parent
ca853edb6f
commit
165ba3e636
@ -360,6 +360,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
|
||||
wipe_buf = true;
|
||||
}
|
||||
|
||||
// Disallow deleting the buffer when it is locked (already being closed or
|
||||
// halfway a command that relies on it). Unloading is allowed.
|
||||
if (buf->b_locked > 0 && (del_buf || wipe_buf)) {
|
||||
EMSG(_("E937: Attempt to delete a buffer that is in use"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (win_valid_any_tab(win)) {
|
||||
// Set b_last_cursor when closing the last window for the buffer.
|
||||
// Remember the last cursor position and window options of the buffer.
|
||||
@ -378,14 +385,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
|
||||
|
||||
/* When the buffer is no longer in a window, trigger BufWinLeave */
|
||||
if (buf->b_nwindows == 1) {
|
||||
buf->b_closing = true;
|
||||
buf->b_locked++;
|
||||
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false,
|
||||
buf) && !bufref_valid(&bufref)) {
|
||||
// Autocommands deleted the buffer.
|
||||
EMSG(_(e_auabort));
|
||||
return;
|
||||
}
|
||||
buf->b_closing = false;
|
||||
buf->b_locked--;
|
||||
if (abort_if_last && one_window()) {
|
||||
/* Autocommands made this the only window. */
|
||||
EMSG(_(e_auabort));
|
||||
@ -395,14 +402,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
|
||||
/* When the buffer becomes hidden, but is not unloaded, trigger
|
||||
* BufHidden */
|
||||
if (!unload_buf) {
|
||||
buf->b_closing = true;
|
||||
buf->b_locked++;
|
||||
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false,
|
||||
buf) && !bufref_valid(&bufref)) {
|
||||
// Autocommands deleted the buffer.
|
||||
EMSG(_(e_auabort));
|
||||
return;
|
||||
}
|
||||
buf->b_closing = false;
|
||||
buf->b_locked--;
|
||||
if (abort_if_last && one_window()) {
|
||||
/* Autocommands made this the only window. */
|
||||
EMSG(_(e_auabort));
|
||||
@ -559,7 +566,7 @@ void buf_freeall(buf_T *buf, int flags)
|
||||
tabpage_T *the_curtab = curtab;
|
||||
|
||||
// Make sure the buffer isn't closed by autocommands.
|
||||
buf->b_closing = true;
|
||||
buf->b_locked++;
|
||||
|
||||
bufref_T bufref;
|
||||
set_bufref(&bufref, buf);
|
||||
@ -584,7 +591,7 @@ void buf_freeall(buf_T *buf, int flags)
|
||||
// Autocommands may delete the buffer.
|
||||
return;
|
||||
}
|
||||
buf->b_closing = false;
|
||||
buf->b_locked--;
|
||||
|
||||
// If the buffer was in curwin and the window has changed, go back to that
|
||||
// window, if it still exists. This avoids that ":edit x" triggering a
|
||||
@ -1111,7 +1118,7 @@ do_buffer (
|
||||
* a window with this buffer.
|
||||
*/
|
||||
while (buf == curbuf
|
||||
&& !(curwin->w_closing || curwin->w_buffer->b_closing)
|
||||
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
|
||||
&& (firstwin != lastwin || first_tabpage->tp_next != NULL)) {
|
||||
if (win_close(curwin, FALSE) == FAIL)
|
||||
break;
|
||||
@ -4499,7 +4506,7 @@ void ex_buffer_all(exarg_T *eap)
|
||||
: wp->w_width != Columns)
|
||||
|| (had_tab > 0 && wp != firstwin)
|
||||
) && firstwin != lastwin
|
||||
&& !(wp->w_closing || wp->w_buffer->b_closing)
|
||||
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)
|
||||
) {
|
||||
win_close(wp, FALSE);
|
||||
wpnext = firstwin; /* just in case an autocommand does
|
||||
|
@ -470,9 +470,9 @@ struct file_buffer {
|
||||
|
||||
int b_nwindows; /* nr of windows open on this buffer */
|
||||
|
||||
int b_flags; /* various BF_ flags */
|
||||
bool b_closing; /* buffer is being closed, don't let
|
||||
autocommands close it too. */
|
||||
int b_flags; // various BF_ flags
|
||||
int b_locked; // Buffer is being closed or referenced, don't
|
||||
// let autocommands wipe it out.
|
||||
|
||||
/*
|
||||
* b_ffname has the full path of the file (NULL for no name).
|
||||
|
@ -2284,8 +2284,11 @@ int do_ecmd(
|
||||
} else {
|
||||
win_T *the_curwin = curwin;
|
||||
|
||||
// Set the w_closing flag to avoid that autocommands close the window.
|
||||
// Set w_closing to avoid that autocommands close the window.
|
||||
// Set b_locked for the same reason.
|
||||
the_curwin->w_closing = true;
|
||||
buf->b_locked++;
|
||||
|
||||
if (curbuf == old_curbuf.br_buf) {
|
||||
buf_copy_options(buf, BCO_ENTER);
|
||||
}
|
||||
@ -2298,6 +2301,7 @@ int do_ecmd(
|
||||
false);
|
||||
|
||||
the_curwin->w_closing = false;
|
||||
buf->b_locked--;
|
||||
|
||||
// autocmds may abort script processing
|
||||
if (aborting() && curwin->w_buffer != NULL) {
|
||||
@ -2444,11 +2448,6 @@ int do_ecmd(
|
||||
/* Assume success now */
|
||||
retval = OK;
|
||||
|
||||
/*
|
||||
* Reset cursor position, could be used by autocommands.
|
||||
*/
|
||||
check_cursor();
|
||||
|
||||
/*
|
||||
* Check if we are editing the w_arg_idx file in the argument list.
|
||||
*/
|
||||
|
@ -5775,7 +5775,7 @@ static void ex_quit(exarg_T *eap)
|
||||
// Refuse to quit when locked or when the buffer in the last window is
|
||||
// being closed (can only happen in autocommands).
|
||||
if (curbuf_locked()
|
||||
|| (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) {
|
||||
|| (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5836,7 +5836,7 @@ static void ex_quit_all(exarg_T *eap)
|
||||
apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
|
||||
/* Refuse to quit when locked or when the buffer in the last window is
|
||||
* being closed (can only happen in autocommands). */
|
||||
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
|
||||
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
|
||||
return;
|
||||
|
||||
exiting = true;
|
||||
@ -6131,7 +6131,7 @@ static void ex_exit(exarg_T *eap)
|
||||
apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, curbuf);
|
||||
/* Refuse to quit when locked or when the buffer in the last window is
|
||||
* being closed (can only happen in autocommands). */
|
||||
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_closing))
|
||||
if (curbuf_locked() || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
|
||||
return;
|
||||
|
||||
// if more files or windows we won't exit
|
||||
|
@ -1,63 +0,0 @@
|
||||
Tests for autocommands on :close command
|
||||
|
||||
Write three files and open them, each in a window.
|
||||
Then go to next window, with autocommand that deletes the previous one.
|
||||
Do this twice, writing the file.
|
||||
|
||||
Also test deleting the buffer on a Unload event. If this goes wrong there
|
||||
will be the ATTENTION prompt.
|
||||
|
||||
Also test changing buffers in a BufDel autocommand. If this goes wrong there
|
||||
are ml_line errors and/or a Crash.
|
||||
|
||||
STARTTEST
|
||||
:/^start of testfile/,/^end of testfile/w! Xtestje1
|
||||
:/^start of testfile/,/^end of testfile/w! Xtestje2
|
||||
:/^start of testfile/,/^end of testfile/w! Xtestje3
|
||||
:e Xtestje1
|
||||
otestje1
|
||||
:w
|
||||
:sp Xtestje2
|
||||
otestje2
|
||||
:w
|
||||
:sp Xtestje3
|
||||
otestje3
|
||||
:w
|
||||
|
||||
:au WinLeave Xtestje2 bwipe
|
||||
|
||||
:w! test.out
|
||||
:au WinLeave Xtestje1 bwipe Xtestje3
|
||||
:close
|
||||
:w >>test.out
|
||||
:e Xtestje1
|
||||
:bwipe Xtestje2 Xtestje3 test.out
|
||||
:au!
|
||||
:au! BufUnload Xtestje1 bwipe
|
||||
:e Xtestje3
|
||||
:w >>test.out
|
||||
:e Xtestje2
|
||||
:sp Xtestje1
|
||||
:e
|
||||
:w >>test.out
|
||||
:au!
|
||||
:only
|
||||
:e Xtestje1
|
||||
:bwipe Xtestje2 Xtestje3 test.out test13.in
|
||||
:au BufWipeout Xtestje1 buf Xtestje1
|
||||
:bwipe
|
||||
:w >>test.out
|
||||
:only
|
||||
:new|set buftype=help
|
||||
:wincmd w
|
||||
:1quit
|
||||
:$put ='Final line'
|
||||
:$w >>test.out
|
||||
:qa!
|
||||
ENDTEST
|
||||
|
||||
start of testfile
|
||||
contents
|
||||
contents
|
||||
contents
|
||||
end of testfile
|
@ -1,31 +0,0 @@
|
||||
start of testfile
|
||||
testje1
|
||||
contents
|
||||
contents
|
||||
contents
|
||||
end of testfile
|
||||
start of testfile
|
||||
testje1
|
||||
contents
|
||||
contents
|
||||
contents
|
||||
end of testfile
|
||||
start of testfile
|
||||
testje3
|
||||
contents
|
||||
contents
|
||||
contents
|
||||
end of testfile
|
||||
start of testfile
|
||||
testje2
|
||||
contents
|
||||
contents
|
||||
contents
|
||||
end of testfile
|
||||
start of testfile
|
||||
testje1
|
||||
contents
|
||||
contents
|
||||
contents
|
||||
end of testfile
|
||||
Final line
|
@ -77,11 +77,49 @@ function Test_autocmd_bufunload_with_tabnext()
|
||||
quit
|
||||
call assert_equal(2, tabpagenr('$'))
|
||||
|
||||
autocmd! test_autocmd_bufunload_with_tabnext_group
|
||||
augroup! test_autocmd_bufunload_with_tabnext_group
|
||||
tablast
|
||||
quit
|
||||
endfunc
|
||||
|
||||
" SEGV occurs in older versions. (At least 7.4.2321 or older)
|
||||
function Test_autocmd_bufunload_avoiding_SEGV_01()
|
||||
split aa.txt
|
||||
let lastbuf = bufnr('$')
|
||||
|
||||
augroup test_autocmd_bufunload
|
||||
autocmd!
|
||||
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
|
||||
augroup END
|
||||
|
||||
call assert_fails('edit bb.txt', 'E937:')
|
||||
|
||||
autocmd! test_autocmd_bufunload
|
||||
augroup! test_autocmd_bufunload
|
||||
bwipe! aa.txt
|
||||
bwipe! bb.txt
|
||||
endfunc
|
||||
|
||||
" SEGV occurs in older versions. (At least 7.4.2321 or older)
|
||||
function Test_autocmd_bufunload_avoiding_SEGV_02()
|
||||
setlocal buftype=nowrite
|
||||
let lastbuf = bufnr('$')
|
||||
|
||||
augroup test_autocmd_bufunload
|
||||
autocmd!
|
||||
exe 'autocmd BufUnload <buffer> ' . (lastbuf + 1) . 'bwipeout!'
|
||||
augroup END
|
||||
|
||||
normal! i1
|
||||
call assert_fails('edit a.txt', 'E517:')
|
||||
call feedkeys("\<CR>")
|
||||
|
||||
autocmd! test_autocmd_bufunload
|
||||
augroup! test_autocmd_bufunload
|
||||
bwipe! a.txt
|
||||
endfunc
|
||||
|
||||
func Test_win_tab_autocmd()
|
||||
let g:record = []
|
||||
|
||||
@ -196,6 +234,67 @@ func Test_augroup_deleted()
|
||||
au! VimEnter
|
||||
endfunc
|
||||
|
||||
" Tests for autocommands on :close command.
|
||||
" This used to be in test13.
|
||||
func Test_three_windows()
|
||||
" Write three files and open them, each in a window.
|
||||
" Then go to next window, with autocommand that deletes the previous one.
|
||||
" Do this twice, writing the file.
|
||||
e! Xtestje1
|
||||
call setline(1, 'testje1')
|
||||
w
|
||||
sp Xtestje2
|
||||
call setline(1, 'testje2')
|
||||
w
|
||||
sp Xtestje3
|
||||
call setline(1, 'testje3')
|
||||
w
|
||||
wincmd w
|
||||
au WinLeave Xtestje2 bwipe
|
||||
wincmd w
|
||||
call assert_equal('Xtestje1', expand('%'))
|
||||
|
||||
au WinLeave Xtestje1 bwipe Xtestje3
|
||||
close
|
||||
call assert_equal('Xtestje1', expand('%'))
|
||||
|
||||
" Test deleting the buffer on a Unload event. If this goes wrong there
|
||||
" will be the ATTENTION prompt.
|
||||
e Xtestje1
|
||||
au!
|
||||
au! BufUnload Xtestje1 bwipe
|
||||
call assert_fails('e Xtestje3', 'E937:')
|
||||
call assert_equal('Xtestje3', expand('%'))
|
||||
|
||||
e Xtestje2
|
||||
sp Xtestje1
|
||||
call assert_fails('e', 'E937:')
|
||||
call assert_equal('Xtestje2', expand('%'))
|
||||
|
||||
" Test changing buffers in a BufWipeout autocommand. If this goes wrong
|
||||
" there are ml_line errors and/or a Crash.
|
||||
au!
|
||||
only
|
||||
e Xanother
|
||||
e Xtestje1
|
||||
bwipe Xtestje2
|
||||
bwipe Xtestje3
|
||||
au BufWipeout Xtestje1 buf Xtestje1
|
||||
bwipe
|
||||
call assert_equal('Xanother', expand('%'))
|
||||
|
||||
only
|
||||
help
|
||||
wincmd w
|
||||
1quit
|
||||
call assert_equal('Xanother', expand('%'))
|
||||
|
||||
au!
|
||||
call delete('Xtestje1')
|
||||
call delete('Xtestje2')
|
||||
call delete('Xtestje3')
|
||||
endfunc
|
||||
|
||||
func Test_BufEnter()
|
||||
au! BufEnter
|
||||
au Bufenter * let val = val . '+'
|
||||
|
@ -117,7 +117,7 @@ static int included_patches[] = {
|
||||
// 2327 NA
|
||||
2326,
|
||||
// 2325 NA
|
||||
// 2324,
|
||||
2324,
|
||||
2323,
|
||||
2322,
|
||||
2321,
|
||||
|
@ -1727,7 +1727,7 @@ void close_windows(buf_T *buf, int keep_curwin)
|
||||
|
||||
for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) {
|
||||
if (wp->w_buffer == buf && (!keep_curwin || wp != curwin)
|
||||
&& !(wp->w_closing || wp->w_buffer->b_closing)) {
|
||||
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
|
||||
if (win_close(wp, false) == FAIL) {
|
||||
// If closing the window fails give up, to avoid looping forever.
|
||||
break;
|
||||
@ -1745,8 +1745,7 @@ void close_windows(buf_T *buf, int keep_curwin)
|
||||
if (tp != curtab) {
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
|
||||
if (wp->w_buffer == buf
|
||||
&& !(wp->w_closing || wp->w_buffer->b_closing)
|
||||
) {
|
||||
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)) {
|
||||
win_close_othertab(wp, FALSE, tp);
|
||||
|
||||
/* Start all over, the tab page may be closed and
|
||||
@ -1882,8 +1881,9 @@ int win_close(win_T *win, int free_buf)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing))
|
||||
return FAIL; /* window is already being closed */
|
||||
if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) {
|
||||
return FAIL; // window is already being closed
|
||||
}
|
||||
if (win == aucmd_win) {
|
||||
EMSG(_("E813: Cannot close autocmd window"));
|
||||
return FAIL;
|
||||
@ -2064,7 +2064,8 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
|
||||
|
||||
// Get here with win->w_buffer == NULL when win_close() detects the tab page
|
||||
// changed.
|
||||
if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) {
|
||||
if (win->w_closing
|
||||
|| (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) {
|
||||
return; // window is already being closed
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user