Closes #5246
This commit is contained in:
Justin M. Keyes 2016-10-26 14:01:49 +02:00
commit 90bf31c742
9 changed files with 202 additions and 78 deletions

View File

@ -83,14 +83,21 @@ In the GUI tab pages line you can use the right mouse button to open menu.
Execute {cmd} and when it opens a new window open a new tab Execute {cmd} and when it opens a new window open a new tab
page instead. Doesn't work for |:diffsplit|, |:diffpatch|, page instead. Doesn't work for |:diffsplit|, |:diffpatch|,
|:execute| and |:normal|. |:execute| and |:normal|.
When [count] is omitted the tab page appears after the current If [count] is given the new tab page appears after the tab
one. page [count] otherwise the new tab page will appear after the
When [count] is specified the new tab page comes after tab current one.
page [count]. Use ":0tab cmd" to get the new tab page as the
first one.
Examples: > Examples: >
:tab split " opens current buffer in new tab page :tab split " opens current buffer in new tab page
:tab help gt " opens tab page with help for "gt" :tab help gt " opens tab page with help for "gt"
:.tab help gt " as above
:+tab help " opens tab page with help after the next
" tab page
:-tab help " opens tab page with help before the
" current one
:0tab help " opens tab page with help before the
" first one
:$tab help " opens tab page with help after the last
" one
CTRL-W gf Open a new tab page and edit the file name under the cursor. CTRL-W gf Open a new tab page and edit the file name under the cursor.
See |CTRL-W_gf|. See |CTRL-W_gf|.
@ -140,7 +147,7 @@ something else.
:{count}tabo[nly][!] :{count}tabo[nly][!]
Close all tab pages except the {count}th one. > Close all tab pages except the {count}th one. >
:.tabonly " one :.tabonly " as above
:-tabonly " close all tab pages except the previous one :-tabonly " close all tab pages except the previous one
:+tabonly " close all tab pages except the next one :+tabonly " close all tab pages except the next one
:1tabonly " close all tab pages except the first one :1tabonly " close all tab pages except the first one

View File

@ -323,13 +323,14 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
wipe_buf = true; wipe_buf = true;
} }
if (win_valid(win)) { if (win_valid_any_tab(win)) {
/* Set b_last_cursor when closing the last window for the buffer. // Set b_last_cursor when closing the last window for the buffer.
* Remember the last cursor position and window options of the buffer. // Remember the last cursor position and window options of the buffer.
* This used to be only for the current window, but then options like // This used to be only for the current window, but then options like
* 'foldmethod' may be lost with a ":only" command. */ // 'foldmethod' may be lost with a ":only" command.
if (buf->b_nwindows == 1) if (buf->b_nwindows == 1) {
set_last_cursor(win); set_last_cursor(win);
}
buflist_setfpos(buf, win, buflist_setfpos(buf, win,
win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum, win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
win->w_cursor.col, TRUE); win->w_cursor.col, TRUE);
@ -402,7 +403,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
buf->b_nwindows = nwindows; buf->b_nwindows = nwindows;
buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
if (win_valid(win) && win->w_buffer == buf) { if (win_valid_any_tab(win) && win->w_buffer == buf) {
win->w_buffer = NULL; // make sure we don't use the buffer now win->w_buffer = NULL; // make sure we don't use the buffer now
} }
@ -478,17 +479,20 @@ void buf_clear_file(buf_T *buf)
buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */ buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */
} }
/* /// buf_freeall() - free all things allocated for a buffer that are related to
* buf_freeall() - free all things allocated for a buffer that are related to /// the file. Careful: get here with "curwin" NULL when exiting.
* the file. flags: ///
* BFA_DEL buffer is going to be deleted /// @param flags BFA_DEL buffer is going to be deleted
* BFA_WIPE buffer is going to be wiped out /// BFA_WIPE buffer is going to be wiped out
* BFA_KEEP_UNDO do not free undo information /// BFA_KEEP_UNDO do not free undo information
*/
void buf_freeall(buf_T *buf, int flags) void buf_freeall(buf_T *buf, int flags)
{ {
bool is_curbuf = (buf == curbuf); bool is_curbuf = (buf == curbuf);
int is_curwin = (curwin != NULL && curwin->w_buffer == buf);
win_T *the_curwin = curwin;
tabpage_T *the_curtab = curtab;
// Make sure the buffer isn't closed by autocommands.
buf->b_closing = true; buf->b_closing = true;
apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf); apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf);
if (!buf_valid(buf)) /* autocommands may delete the buffer */ if (!buf_valid(buf)) /* autocommands may delete the buffer */
@ -505,8 +509,18 @@ void buf_freeall(buf_T *buf, int flags)
return; return;
} }
buf->b_closing = false; buf->b_closing = false;
if (aborting()) /* autocmds may abort script processing */
// 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
// "tabnext" BufUnload autocmd leaves a window behind without a buffer.
if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin)) {
block_autocmds();
goto_tabpage_win(the_curtab, the_curwin);
unblock_autocmds();
}
if (aborting()) { // autocmds may abort script processing
return; return;
}
/* /*
* It's possible that autocommands change curbuf to the one being deleted. * It's possible that autocommands change curbuf to the one being deleted.

View File

@ -2250,28 +2250,28 @@ do_ecmd (
xfree(new_name); xfree(new_name);
goto theend; goto theend;
} }
if (buf == curbuf) /* already in new buffer */ if (buf == curbuf) { // already in new buffer
auto_buf = TRUE; auto_buf = true;
else { } else {
if (curbuf == old_curbuf) win_T *the_curwin = curwin;
// Set the w_closing flag to avoid that autocommands close the window.
the_curwin->w_closing = true;
if (curbuf == old_curbuf) {
buf_copy_options(buf, BCO_ENTER); buf_copy_options(buf, BCO_ENTER);
/* close the link to the current buffer */
u_sync(FALSE);
close_buffer(oldwin, curbuf,
(flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, FALSE);
/* Autocommands may open a new window and leave oldwin open
* which leads to crashes since the above call sets
* oldwin->w_buffer to NULL. */
if (curwin != oldwin && oldwin != aucmd_win && win_valid(oldwin)) {
assert(oldwin);
if (oldwin->w_buffer == NULL) {
win_close(oldwin, FALSE);
}
} }
if (aborting()) { /* autocmds may abort script processing */ // Close the link to the current buffer. This will set
// curwin->w_buffer to NULL.
u_sync(false);
close_buffer(oldwin, curbuf,
(flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
false);
the_curwin->w_closing = false;
// autocmds may abort script processing
if (aborting() && curwin->w_buffer != NULL) {
xfree(new_name); xfree(new_name);
goto theend; goto theend;
} }

View File

@ -1302,8 +1302,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
* 2. Handle command modifiers. * 2. Handle command modifiers.
*/ */
p = ea.cmd; p = ea.cmd;
if (ascii_isdigit(*ea.cmd)) p = skip_range(ea.cmd, NULL);
p = skipwhite(skipdigits(ea.cmd));
switch (*p) { switch (*p) {
/* When adding an entry, also modify cmd_exists(). */ /* When adding an entry, also modify cmd_exists(). */
case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3)) case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3))
@ -1406,12 +1405,18 @@ static char_u * do_one_cmd(char_u **cmdlinep,
continue; continue;
case 't': if (checkforcmd(&p, "tab", 3)) { case 't': if (checkforcmd(&p, "tab", 3)) {
if (ascii_isdigit(*ea.cmd)) long tabnr = get_address(&ea, &ea.cmd, ADDR_TABS, ea.skip, false);
cmdmod.tab = atoi((char *)ea.cmd) + 1; if (tabnr == MAXLNUM) {
else cmdmod.tab = tabpage_index(curtab) + 1;
cmdmod.tab = tabpage_index(curtab) + 1; } else {
ea.cmd = p; if (tabnr < 0 || tabnr > LAST_TAB_NR) {
continue; errormsg = (char_u *)_(e_invrange);
goto doend;
}
cmdmod.tab = tabnr + 1;
}
ea.cmd = p;
continue;
} }
if (!checkforcmd(&ea.cmd, "topleft", 2)) if (!checkforcmd(&ea.cmd, "topleft", 2))
break; break;
@ -1766,11 +1771,8 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (text_locked() && !(ea.argt & CMDWIN) if (text_locked() && !(ea.argt & CMDWIN)
&& !IS_USER_CMDIDX(ea.cmdidx)) { && !IS_USER_CMDIDX(ea.cmdidx)) {
/* Command not allowed when editing the command line. */ // Command not allowed when editing the command line.
if (cmdwin_type != 0) errormsg = get_text_locked_msg();
errormsg = (char_u *)_(e_cmdwin);
else
errormsg = (char_u *)_(e_secure);
goto doend; goto doend;
} }
/* Disallow editing another buffer when "curbuf_lock" is set. /* Disallow editing another buffer when "curbuf_lock" is set.

View File

@ -1688,10 +1688,15 @@ int text_locked(void) {
*/ */
void text_locked_msg(void) void text_locked_msg(void)
{ {
if (cmdwin_type != 0) EMSG(_(get_text_locked_msg()));
EMSG(_(e_cmdwin)); }
else
EMSG(_(e_secure)); char_u * get_text_locked_msg(void) {
if (cmdwin_type != 0) {
return e_cmdwin;
} else {
return e_secure;
}
} }
/* /*

View File

@ -186,4 +186,62 @@ function Test_tabpage_with_autocmd()
bw! bw!
endfunction endfunction
function Test_tabpage_with_tab_modifier()
for n in range(4)
tabedit
endfor
function s:check_tab(pre_nr, cmd, post_nr)
exec 'tabnext ' . a:pre_nr
exec a:cmd
call assert_equal(a:post_nr, tabpagenr())
call assert_equal('help', &filetype)
helpclose
endfunc
call s:check_tab(1, 'tab help', 2)
call s:check_tab(1, '3tab help', 4)
call s:check_tab(1, '.tab help', 2)
call s:check_tab(1, '.+1tab help', 3)
call s:check_tab(1, '0tab help', 1)
call s:check_tab(2, '+tab help', 4)
call s:check_tab(2, '+2tab help', 5)
call s:check_tab(4, '-tab help', 4)
call s:check_tab(4, '-2tab help', 3)
call s:check_tab(3, '$tab help', 6)
call assert_fails('99tab help', 'E16:')
call assert_fails('+99tab help', 'E16:')
call assert_fails('-99tab help', 'E16:')
delfunction s:check_tab
tabonly!
bw!
endfunction
func Test_tabnext_on_buf_unload1()
" This once caused a crash
new
tabedit
tabfirst
au BufUnload <buffer> tabnext
q
while tabpagenr('$') > 1
bwipe!
endwhile
endfunc
func Test_tabnext_on_buf_unload2()
" This once caused a crash
tabedit
autocmd BufUnload <buffer> tabnext
file x
edit y
while tabpagenr('$') > 1
bwipe!
endwhile
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -129,10 +129,10 @@ static int included_patches[] = {
// 2315, // 2315,
// 2314, // 2314,
// 2313, // 2313,
// 2312, 2312,
// 2311, // 2311,
// 2310 NA // 2310 NA
// 2309, 2309,
// 2308 NA // 2308 NA
// 2307, // 2307,
// 2306, // 2306,
@ -204,7 +204,7 @@ static int included_patches[] = {
// 2240, // 2240,
// 2239, // 2239,
// 2238 NA // 2238 NA
// 2237, 2237,
// 2236, // 2236,
// 2235, // 2235,
// 2234 NA // 2234 NA
@ -229,7 +229,7 @@ static int included_patches[] = {
// 2215, // 2215,
// 2214 NA // 2214 NA
2213, 2213,
// 2212, 2212,
// 2211 NA // 2211 NA
// 2210 NA // 2210 NA
// 2209, // 2209,

View File

@ -1065,6 +1065,23 @@ bool win_valid(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
return false; return false;
} }
/// Check if "win" is a pointer to an existing window in any tabpage.
///
/// @param win window to check
bool win_valid_any_tab(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
if (win == NULL) {
return false;
}
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp == win) {
return true;
}
}
return false;
}
/* /*
* Return the number of windows. * Return the number of windows.
*/ */
@ -1918,7 +1935,7 @@ int win_close(win_T *win, int free_buf)
if (win->w_buffer != NULL) { if (win->w_buffer != NULL) {
win->w_closing = true; win->w_closing = true;
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true); close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true);
if (win_valid(win)) { if (win_valid_any_tab(win)) {
win->w_closing = false; win->w_closing = false;
} }
@ -1938,11 +1955,19 @@ int win_close(win_T *win, int free_buf)
curwin->w_buffer = curbuf; curwin->w_buffer = curbuf;
getout(0); getout(0);
} }
/* Autocommands may have closed the window already, or closed the only // Autocommands may have moved to another tab page.
* other window or moved to another tab page. */ if (curtab != prev_curtab && win_valid_any_tab(win)
else if (!win_valid(win) || last_window() || curtab != prev_curtab && win->w_buffer == NULL) {
|| close_last_window_tabpage(win, free_buf, prev_curtab)) // Need to close the window anyway, since the buffer is NULL.
win_close_othertab(win, false, prev_curtab);
return FAIL; return FAIL;
}
// Autocommands may have closed the window already, or closed the only
// other window or moved to another tab page.
if (!win_valid(win) || last_window()
|| close_last_window_tabpage(win, free_buf, prev_curtab)) {
return FAIL;
}
// let terminal buffers know that this window dimensions may be ignored // let terminal buffers know that this window dimensions may be ignored
win->w_closing = true; win->w_closing = true;
@ -2019,12 +2044,16 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
tabpage_T *ptp = NULL; tabpage_T *ptp = NULL;
int free_tp = FALSE; int free_tp = FALSE;
assert(win->w_buffer); // to avoid np dereference warning in next line // Get here with win->w_buffer == NULL when win_close() detects the tab page
if (win->w_closing || win->w_buffer->b_closing) // changed.
return; /* window is already being closed */ if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) {
return; // window is already being closed
}
/* Close the link to the buffer. */ if (win->w_buffer != NULL) {
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE); // Close the link to the buffer.
close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
}
/* Careful: Autocommands may have closed the tab page or made it the /* Careful: Autocommands may have closed the tab page or made it the
* current tab page. */ * current tab page. */
@ -3213,11 +3242,8 @@ void goto_tabpage(int n)
int i; int i;
if (text_locked()) { if (text_locked()) {
/* Not allowed when editing the command line. */ // Not allowed when editing the command line.
if (cmdwin_type != 0) text_locked_msg();
EMSG(_(e_cmdwin));
else
EMSG(_(e_secure));
return; return;
} }

View File

@ -102,6 +102,18 @@ describe('ShaDa support code', function()
eq(2, nvim_current_line()) eq(2, nvim_current_line())
end) end)
it('is able to dump and read back mark " from a closed tab', function()
nvim_command('edit ' .. testfilename)
nvim_command('edit ' .. testfilename_2)
nvim_command('2')
nvim_command('q!')
nvim_command('qall')
reset()
nvim_command('edit ' .. testfilename_2)
nvim_command('normal! `"')
eq(2, nvim_current_line())
end)
it('is able to populate v:oldfiles', function() it('is able to populate v:oldfiles', function()
nvim_command('edit ' .. testfilename) nvim_command('edit ' .. testfilename)
local tf_full = curbufmeths.get_name() local tf_full = curbufmeths.get_name()