changeset 10739:380e706814da v8.0.0259

patch 8.0.0259: tab commands do not handle count correctly commit https://github.com/vim/vim/commit/2f72c70657129c16e6b0e413752a775c804f02f8 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 29 14:48:10 2017 +0100 patch 8.0.0259: tab commands do not handle count correctly Problem: Tab commands do not handle count correctly. (Ken Hamada) Solution: Add ADDR_TABS_RELATIVE. (Hirohito Higashi)
author Christian Brabandt <cb@256bit.org>
date Sun, 29 Jan 2017 15:00:05 +0100
parents e0a99b268f88
children d3f75123cbc5
files src/ex_cmds.h src/ex_docmd.c src/testdir/test_tabpage.vim src/version.c
diffstat 4 files changed, 453 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -65,7 +65,8 @@
 #define ADDR_LOADED_BUFFERS	3
 #define ADDR_BUFFERS		4
 #define ADDR_TABS		5
-#define ADDR_QUICKFIX		6
+#define ADDR_TABS_RELATIVE	6   /* Tab page that only relative */
+#define ADDR_QUICKFIX		7
 #define ADDR_OTHER		99
 
 #ifndef DO_DECLARE_EXCMD
@@ -1425,9 +1426,9 @@ EX(CMD_tags,		"tags",		do_tags,
 			ADDR_LINES),
 EX(CMD_tab,		"tab",		ex_wrongmodifier,
 			NEEDARG|EXTRA|NOTRLCOM,
-			ADDR_LINES),
+			ADDR_TABS),
 EX(CMD_tabclose,	"tabclose",	ex_tabclose,
-			RANGE|NOTADR|COUNT|BANG|TRLBAR|CMDWIN,
+			BANG|RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR|CMDWIN,
 			ADDR_TABS),
 EX(CMD_tabdo,		"tabdo",	ex_listdo,
 			NEEDARG|EXTRA|NOTRLCOM|RANGE|NOTADR|DFLALL,
@@ -1440,34 +1441,34 @@ EX(CMD_tabfind,		"tabfind",	ex_splitview
 			ADDR_TABS),
 EX(CMD_tabfirst,	"tabfirst",	ex_tabnext,
 			TRLBAR,
-			ADDR_LINES),
+			ADDR_TABS),
 EX(CMD_tabmove,		"tabmove",	ex_tabmove,
 			RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR,
 			ADDR_TABS),
 EX(CMD_tablast,		"tablast",	ex_tabnext,
 			TRLBAR,
-			ADDR_LINES),
+			ADDR_TABS),
 EX(CMD_tabnext,		"tabnext",	ex_tabnext,
-			RANGE|NOTADR|COUNT|TRLBAR,
-			ADDR_LINES),
+			RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR,
+			ADDR_TABS),
 EX(CMD_tabnew,		"tabnew",	ex_splitview,
 			BANG|FILE1|RANGE|NOTADR|ZEROR|EDITCMD|ARGOPT|TRLBAR,
 			ADDR_TABS),
 EX(CMD_tabonly,		"tabonly",	ex_tabonly,
-			BANG|RANGE|NOTADR|TRLBAR|CMDWIN,
+			BANG|RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR|CMDWIN,
 			ADDR_TABS),
 EX(CMD_tabprevious,	"tabprevious",	ex_tabnext,
-			RANGE|NOTADR|COUNT|TRLBAR,
-			ADDR_LINES),
+			RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR,
+			ADDR_TABS_RELATIVE),
 EX(CMD_tabNext,		"tabNext",	ex_tabnext,
-			RANGE|NOTADR|COUNT|TRLBAR,
-			ADDR_LINES),
+			RANGE|NOTADR|ZEROR|EXTRA|NOSPC|TRLBAR,
+			ADDR_TABS_RELATIVE),
 EX(CMD_tabrewind,	"tabrewind",	ex_tabnext,
 			TRLBAR,
-			ADDR_LINES),
+			ADDR_TABS),
 EX(CMD_tabs,		"tabs",		ex_tabs,
 			TRLBAR|CMDWIN,
-			ADDR_LINES),
+			ADDR_TABS),
 EX(CMD_tcl,		"tcl",		ex_tcl,
 			RANGE|EXTRA|NEEDARG|CMDWIN,
 			ADDR_LINES),
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -2162,8 +2162,7 @@ do_one_cmd(
 		ea.line2 = curwin->w_cursor.lnum;
 		break;
 	    case ADDR_WINDOWS:
-		lnum = CURRENT_WIN_NR;
-		ea.line2 = lnum;
+		ea.line2 = CURRENT_WIN_NR;
 		break;
 	    case ADDR_ARGUMENTS:
 		ea.line2 = curwin->w_arg_idx + 1;
@@ -2175,8 +2174,10 @@ do_one_cmd(
 		ea.line2 = curbuf->b_fnum;
 		break;
 	    case ADDR_TABS:
-		lnum = CURRENT_TAB_NR;
-		ea.line2 = lnum;
+		ea.line2 = CURRENT_TAB_NR;
+		break;
+	    case ADDR_TABS_RELATIVE:
+		ea.line2 = 1;
 		break;
 #ifdef FEAT_QUICKFIX
 	    case ADDR_QUICKFIX:
@@ -2235,6 +2236,10 @@ do_one_cmd(
 			    goto doend;
 			}
 			break;
+		    case ADDR_TABS_RELATIVE:
+			errormsg = (char_u *)_(e_invrange);
+			goto doend;
+			break;
 		    case ADDR_ARGUMENTS:
 			if (ARGCOUNT == 0)
 			    ea.line1 = ea.line2 = 0;
@@ -2710,6 +2715,9 @@ do_one_cmd(
 	    case ADDR_TABS:
 		ea.line2 = LAST_TAB_NR;
 		break;
+	    case ADDR_TABS_RELATIVE:
+		ea.line2 = 1;
+		break;
 	    case ADDR_ARGUMENTS:
 		if (ARGCOUNT == 0)
 		    ea.line1 = ea.line2 = 0;
@@ -2786,7 +2794,8 @@ do_one_cmd(
 	    /*
 	     * Be vi compatible: no error message for out of range.
 	     */
-	    if (ea.line2 > curbuf->b_ml.ml_line_count)
+	    if (ea.addr_type == ADDR_LINES
+		    && ea.line2 > curbuf->b_ml.ml_line_count)
 		ea.line2 = curbuf->b_ml.ml_line_count;
 	}
     }
@@ -4427,6 +4436,11 @@ get_address(
 		    case ADDR_TABS:
 			lnum = CURRENT_TAB_NR;
 			break;
+		    case ADDR_TABS_RELATIVE:
+			EMSG(_(e_invrange));
+			cmd = NULL;
+			goto error;
+			break;
 #ifdef FEAT_QUICKFIX
 		    case ADDR_QUICKFIX:
 			lnum = qf_get_cur_valid_idx(eap);
@@ -4464,6 +4478,11 @@ get_address(
 		    case ADDR_TABS:
 			lnum = LAST_TAB_NR;
 			break;
+		    case ADDR_TABS_RELATIVE:
+			EMSG(_(e_invrange));
+			cmd = NULL;
+			goto error;
+			break;
 #ifdef FEAT_QUICKFIX
 		    case ADDR_QUICKFIX:
 			lnum = qf_get_size(eap);
@@ -4646,6 +4665,9 @@ get_address(
 		    case ADDR_TABS:
 			lnum = CURRENT_TAB_NR;
 			break;
+		    case ADDR_TABS_RELATIVE:
+			lnum = 1;
+			break;
 #ifdef FEAT_QUICKFIX
 		    case ADDR_QUICKFIX:
 			lnum = qf_get_cur_valid_idx(eap);
@@ -4662,7 +4684,14 @@ get_address(
 		n = 1;
 	    else
 		n = getdigits(&cmd);
-	    if (addr_type == ADDR_LOADED_BUFFERS
+
+	    if (addr_type == ADDR_TABS_RELATIVE)
+	    {
+		EMSG(_(e_invrange));
+		cmd = NULL;
+		goto error;
+	    }
+	    else if (addr_type == ADDR_LOADED_BUFFERS
 		    || addr_type == ADDR_BUFFERS)
 		lnum = compute_buffer_local_count(
 				    addr_type, lnum, (i == '-') ? -1 * n : n);
@@ -4795,6 +4824,9 @@ invalid_range(exarg_T *eap)
 		if (eap->line2 > LAST_TAB_NR)
 		    return (char_u *)_(e_invrange);
 		break;
+	    case ADDR_TABS_RELATIVE:
+		/* Do nothing */
+		break;
 #ifdef FEAT_QUICKFIX
 	    case ADDR_QUICKFIX:
 		if (eap->line2 != 1 && eap->line2 > qf_get_size(eap))
@@ -5453,6 +5485,104 @@ getargopt(exarg_T *eap)
 }
 
 /*
+ * Handle the argument for a tabpage related ex command.
+ * Returns a tabpage number.
+ * When an error is encountered then eap->errmsg is set.
+ */
+    static int
+get_tabpage_arg(exarg_T *eap)
+{
+    int tab_number;
+    int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1;
+
+    if (eap->arg && *eap->arg != NUL)
+    {
+	char_u *p = eap->arg;
+	char_u *p_save;
+	int    relative = 0; /* argument +N/-N means: go to N places to the
+			      * right/left relative to the current position. */
+
+	if (*p == '-')
+	{
+	    relative = -1;
+	    p++;
+	}
+	else if (*p == '+')
+	{
+	    relative = 1;
+	    p++;
+	}
+
+	p_save = p;
+	tab_number = getdigits(&p);
+
+	if (relative == 0)
+	{
+	    if (STRCMP(p, "$") == 0)
+		tab_number = LAST_TAB_NR;
+	    else if (p == p_save || *p_save == '-' || *p != NUL
+		    || tab_number > LAST_TAB_NR)
+	    {
+		/* No numbers as argument. */
+		eap->errmsg = e_invarg;
+		goto theend;
+	    }
+	}
+	else
+	{
+	    if (*p_save == NUL)
+		tab_number = 1;
+	    else if (p == p_save || *p_save == '-' || *p != NUL
+		    || tab_number == 0)
+	    {
+		/* No numbers as argument. */
+		eap->errmsg = e_invarg;
+		goto theend;
+	    }
+	    tab_number = tab_number * relative + tabpage_index(curtab);
+	    if (!unaccept_arg0 && relative == -1)
+		--tab_number;
+	}
+	if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR)
+	    eap->errmsg = e_invarg;
+    }
+    else if (eap->addr_count > 0)
+    {
+	if (unaccept_arg0 && eap->line2 == 0)
+	    eap->errmsg = e_invrange;
+	else
+	{
+	    tab_number = eap->line2;
+	    if (!unaccept_arg0 && **eap->cmdlinep == '-')
+	    {
+		--tab_number;
+		if (tab_number < unaccept_arg0)
+		    eap->errmsg = e_invarg;
+	    }
+	}
+    }
+    else
+    {
+	switch (eap->cmdidx)
+	{
+	case CMD_tabnext:
+	    tab_number = tabpage_index(curtab) + 1;
+	    if (tab_number > LAST_TAB_NR)
+		tab_number = 1;
+	    break;
+	case CMD_tabmove:
+	    tab_number = LAST_TAB_NR;
+	    break;
+	default:
+	    tab_number = tabpage_index(curtab);
+	}
+    }
+
+theend:
+    return tab_number;
+}
+
+/*
  * ":abbreviate" and friends.
  */
     static void
@@ -7444,6 +7574,7 @@ ex_win_close(
 ex_tabclose(exarg_T *eap)
 {
     tabpage_T	*tp;
+    int		tab_number;
 
 # ifdef FEAT_CMDWIN
     if (cmdwin_type != 0)
@@ -7454,9 +7585,10 @@ ex_tabclose(exarg_T *eap)
 	    EMSG(_("E784: Cannot close last tab page"));
 	else
 	{
-	    if (eap->addr_count > 0)
-	    {
-		tp = find_tabpage((int)eap->line2);
+	    tab_number = get_tabpage_arg(eap);
+	    if (eap->errmsg == NULL)
+	    {
+		tp = find_tabpage(tab_number);
 		if (tp == NULL)
 		{
 		    beep_flush();
@@ -7467,13 +7599,13 @@ ex_tabclose(exarg_T *eap)
 		    tabpage_close_other(tp, eap->forceit);
 		    return;
 		}
-	    }
-	    if (!text_locked()
+		else if (!text_locked()
 #ifdef FEAT_AUTOCMD
-		    && !curbuf_locked()
-#endif
-	       )
-		tabpage_close(eap->forceit);
+			&& !curbuf_locked()
+#endif
+		)
+		    tabpage_close(eap->forceit);
+	    }
 	}
 }
 
@@ -7485,6 +7617,7 @@ ex_tabonly(exarg_T *eap)
 {
     tabpage_T	*tp;
     int		done;
+    int		tab_number;
 
 # ifdef FEAT_CMDWIN
     if (cmdwin_type != 0)
@@ -7495,24 +7628,27 @@ ex_tabonly(exarg_T *eap)
 	    MSG(_("Already only one tab page"));
 	else
 	{
-	    if (eap->addr_count > 0)
-		goto_tabpage(eap->line2);
-	    /* Repeat this up to a 1000 times, because autocommands may mess
-	     * up the lists. */
-	    for (done = 0; done < 1000; ++done)
-	    {
-		FOR_ALL_TABPAGES(tp)
-		    if (tp->tp_topframe != topframe)
-		    {
-			tabpage_close_other(tp, eap->forceit);
-			/* if we failed to close it quit */
-			if (valid_tabpage(tp))
-			    done = 1000;
-			/* start over, "tp" is now invalid */
+	    tab_number = get_tabpage_arg(eap);
+	    if (eap->errmsg == NULL)
+	    {
+		goto_tabpage(tab_number);
+		/* Repeat this up to a 1000 times, because autocommands may
+		 * mess up the lists. */
+		for (done = 0; done < 1000; ++done)
+		{
+		    FOR_ALL_TABPAGES(tp)
+			if (tp->tp_topframe != topframe)
+			{
+			    tabpage_close_other(tp, eap->forceit);
+			    /* if we failed to close it quit */
+			    if (valid_tabpage(tp))
+				done = 1000;
+			    /* start over, "tp" is now invalid */
+			    break;
+			}
+		    if (first_tabpage->tp_next == NULL)
 			break;
-		    }
-		if (first_tabpage->tp_next == NULL)
-		    break;
+		}
 	    }
 	}
 }
@@ -8254,6 +8390,8 @@ tabpage_new(void)
     static void
 ex_tabnext(exarg_T *eap)
 {
+    int tab_number;
+
     switch (eap->cmdidx)
     {
 	case CMD_tabfirst:
@@ -8265,10 +8403,40 @@ ex_tabnext(exarg_T *eap)
 	    break;
 	case CMD_tabprevious:
 	case CMD_tabNext:
-	    goto_tabpage(eap->addr_count == 0 ? -1 : -(int)eap->line2);
+	    if (eap->arg && *eap->arg != NUL)
+	    {
+		char_u *p = eap->arg;
+		char_u *p_save = p;
+
+		tab_number = getdigits(&p);
+		if (p == p_save || *p_save == '-' || *p != NUL
+			    || tab_number == 0)
+		{
+		    /* No numbers as argument. */
+		    eap->errmsg = e_invarg;
+		    return;
+		}
+	    }
+	    else
+	    {
+		if (eap->addr_count == 0)
+		    tab_number = 1;
+		else
+		{
+		    tab_number = eap->line2;
+		    if (tab_number < 1)
+		    {
+			eap->errmsg = e_invrange;
+			return;
+		    }
+		}
+	    }
+	    goto_tabpage(-tab_number);
 	    break;
 	default: /* CMD_tabnext */
-	    goto_tabpage(eap->addr_count == 0 ? 0 : (int)eap->line2);
+	    tab_number = get_tabpage_arg(eap);
+	    if (eap->errmsg == NULL)
+		goto_tabpage(tab_number);
 	    break;
     }
 }
@@ -8281,59 +8449,9 @@ ex_tabmove(exarg_T *eap)
 {
     int tab_number;
 
-    if (eap->arg && *eap->arg != NUL)
-    {
-	char_u *p = eap->arg;
-	int    relative = 0; /* argument +N/-N means: move N places to the
-			      * right/left relative to the current position. */
-
-	if (*eap->arg == '-')
-	{
-	    relative = -1;
-	    p = eap->arg + 1;
-	}
-	else if (*eap->arg == '+')
-	{
-	    relative = 1;
-	    p = eap->arg + 1;
-	}
-	else
-	    p = eap->arg;
-
-	if (relative == 0)
-	{
-	    if (STRCMP(p, "$") == 0)
-		tab_number = LAST_TAB_NR;
-	    else if (p == skipdigits(p))
-	    {
-		/* No numbers as argument. */
-		eap->errmsg = e_invarg;
-		return;
-	    }
-	    else
-		tab_number = getdigits(&p);
-	}
-	else
-	{
-	    if (*p != NUL)
-		tab_number = getdigits(&p);
-	    else
-		tab_number = 1;
-	    tab_number = tab_number * relative + tabpage_index(curtab);
-	    if (relative == -1)
-		--tab_number;
-	}
-    }
-    else if (eap->addr_count != 0)
-    {
-	tab_number = eap->line2;
-	if (**eap->cmdlinep == '-')
-	    --tab_number;
-    }
-    else
-	tab_number = LAST_TAB_NR;
-
-    tabpage_move(tab_number);
+    tab_number = get_tabpage_arg(eap);
+    if (eap->errmsg == NULL)
+	tabpage_move(tab_number);
 }
 
 /*
--- a/src/testdir/test_tabpage.vim
+++ b/src/testdir/test_tabpage.vim
@@ -94,10 +94,6 @@ function Test_tabpage()
   call assert_equal(7, tabpagenr())
   tabmove
   call assert_equal(10, tabpagenr())
-  tabmove -20
-  call assert_equal(1, tabpagenr())
-  tabmove +20
-  call assert_equal(10, tabpagenr())
   0tabmove
   call assert_equal(1, tabpagenr())
   $tabmove
@@ -110,7 +106,16 @@ function Test_tabpage()
   call assert_equal(4, tabpagenr())
   7tabmove 5
   call assert_equal(5, tabpagenr())
+  call assert_fails("99tabmove", 'E16:')
+  call assert_fails("+99tabmove", 'E16:')
+  call assert_fails("-99tabmove", 'E16:')
   call assert_fails("tabmove foo", 'E474:')
+  call assert_fails("tabmove 99", 'E474:')
+  call assert_fails("tabmove +99", 'E474:')
+  call assert_fails("tabmove -99", 'E474:')
+  call assert_fails("tabmove -3+", 'E474:')
+  call assert_fails("tabmove $3", 'E474:')
+  1tabonly!
 endfunc
 
 " Test autocommands
@@ -118,7 +123,6 @@ function Test_tabpage_with_autocmd()
   if !has('autocmd')
     return
   endif
-  tabonly!
   command -nargs=1 -bar C :call add(s:li, '=== ' . <q-args> . ' ===')|<args>
   augroup TestTabpageGroup
     au!
@@ -183,8 +187,10 @@ function Test_tabpage_with_autocmd()
 
   autocmd TabDestructive TabEnter * nested :C tabnext 2 | C tabclose 3
   let s:li = []
-  C tabnext 3
-  call assert_equal(['=== tabnext 3 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', 'BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ===', 'BufEnter', '=== tabclose 3 ==='], s:li)
+  call assert_equal(3, tabpagenr('$'))
+  C tabnext 2
+  call assert_equal(2, tabpagenr('$'))
+  call assert_equal(['=== tabnext 2 ===', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', '=== tabnext 2 ===', '=== tabclose 3 ==='], s:li)
   call assert_equal(['2/2'], [tabpagenr().'/'.tabpagenr('$')])
 
   delcommand C
@@ -192,8 +198,7 @@ function Test_tabpage_with_autocmd()
   augroup! TabDestructive
   autocmd! TestTabpageGroup
   augroup! TestTabpageGroup
-  tabonly!
-  bw!
+  1tabonly!
 endfunction
 
 function Test_tabpage_with_tab_modifier()
@@ -224,8 +229,223 @@ function Test_tabpage_with_tab_modifier(
   call assert_fails('-99tab help', 'E16:')
 
   delfunction s:check_tab
-  tabonly!
-  bw!
+  1tabonly!
+endfunction
+
+function Check_tab_count(pre_nr, cmd, post_nr)
+  exec 'tabnext' a:pre_nr
+  normal! G
+  exec a:cmd
+  call assert_equal(a:post_nr, tabpagenr(), a:cmd)
+endfunc
+
+" Test for [count] of tabnext
+function Test_tabpage_with_tabnext()
+  for n in range(4)
+    tabedit
+    call setline(1, ['', '', '3'])
+  endfor
+
+  call Check_tab_count(1, 'tabnext', 2)
+  call Check_tab_count(1, '3tabnext', 3)
+  call Check_tab_count(1, '.tabnext', 1)
+  call Check_tab_count(1, '.+1tabnext', 2)
+  call Check_tab_count(2, '+tabnext', 3)
+  call Check_tab_count(2, '+2tabnext', 4)
+  call Check_tab_count(4, '-tabnext', 3)
+  call Check_tab_count(4, '-2tabnext', 2)
+  call Check_tab_count(3, '$tabnext', 5)
+  call assert_fails('0tabnext', 'E16:')
+  call assert_fails('99tabnext', 'E16:')
+  call assert_fails('+99tabnext', 'E16:')
+  call assert_fails('-99tabnext', 'E16:')
+  call Check_tab_count(1, 'tabnext 3', 3)
+  call Check_tab_count(2, 'tabnext +', 3)
+  call Check_tab_count(2, 'tabnext +2', 4)
+  call Check_tab_count(4, 'tabnext -', 3)
+  call Check_tab_count(4, 'tabnext -2', 2)
+  call Check_tab_count(3, 'tabnext $', 5)
+  call assert_fails('tabnext 0', 'E474:')
+  call assert_fails('tabnext .', 'E474:')
+  call assert_fails('tabnext -+', 'E474:')
+  call assert_fails('tabnext +2-', 'E474:')
+  call assert_fails('tabnext $3', 'E474:')
+  call assert_fails('tabnext 99', 'E474:')
+  call assert_fails('tabnext +99', 'E474:')
+  call assert_fails('tabnext -99', 'E474:')
+
+  1tabonly!
+endfunction
+
+" Test for [count] of tabprevious
+function Test_tabpage_with_tabprevious()
+  for n in range(5)
+    tabedit
+    call setline(1, ['', '', '3'])
+  endfor
+
+  for cmd in ['tabNext', 'tabprevious']
+    call Check_tab_count(6, cmd, 5)
+    call Check_tab_count(6, '3' . cmd, 3)
+    call Check_tab_count(6, '8' . cmd, 4)
+    call Check_tab_count(6, cmd . ' 3', 3)
+    call Check_tab_count(6, cmd . ' 8', 4)
+    for n in range(2)
+      for c in ['0', '.+3', '+', '+2' , '-', '-2' , '$', '+99', '-99']
+        if n == 0 " pre count
+          let entire_cmd = c . cmd
+          let err_code = 'E16:'
+        else
+          let entire_cmd = cmd . ' ' . c
+          let err_code = 'E474:'
+        endif
+        call assert_fails(entire_cmd, err_code)
+      endfor
+    endfor
+  endfor
+
+  1tabonly!
+endfunction
+
+function s:reconstruct_tabpage_for_test(nr)
+  let n = (a:nr > 2) ? a:nr - 2 : 1
+  1tabonly!
+  0tabedit n0
+  for n in range(1, n)
+    exec '$tabedit n' . n
+    if n == 1
+      call setline(1, ['', '', '3'])
+    endif
+  endfor
+endfunc
+
+" Test for [count] of tabclose
+function Test_tabpage_with_tabclose()
+
+  " pre count
+  call s:reconstruct_tabpage_for_test(6)
+  call Check_tab_count(3, 'tabclose!', 3)
+  call Check_tab_count(1, '3tabclose', 1)
+  call Check_tab_count(4, '4tabclose', 3)
+  call Check_tab_count(3, '1tabclose', 2)
+  call Check_tab_count(2, 'tabclose', 1)
+  call assert_equal(1, tabpagenr('$'))
+  call assert_equal('', bufname(''))
+
+  call s:reconstruct_tabpage_for_test(6)
+  call Check_tab_count(2, '$tabclose', 2)
+  call Check_tab_count(4, '.tabclose', 4)
+  call Check_tab_count(3, '.+tabclose', 3)
+  call Check_tab_count(3, '.-2tabclose', 2)
+  call Check_tab_count(1, '.+1tabclose!', 1)
+  call assert_equal(1, tabpagenr('$'))
+  call assert_equal('', bufname(''))
+
+  " post count
+  call s:reconstruct_tabpage_for_test(6)
+  call Check_tab_count(3, 'tabclose!', 3)
+  call Check_tab_count(1, 'tabclose 3', 1)
+  call Check_tab_count(4, 'tabclose 4', 3)
+  call Check_tab_count(3, 'tabclose 1', 2)
+  call Check_tab_count(2, 'tabclose', 1)
+  call assert_equal(1, tabpagenr('$'))
+  call assert_equal('', bufname(''))
+
+  call s:reconstruct_tabpage_for_test(6)
+  call Check_tab_count(2, 'tabclose $', 2)
+  call Check_tab_count(4, 'tabclose', 4)
+  call Check_tab_count(3, 'tabclose +', 3)
+  call Check_tab_count(3, 'tabclose -2', 2)
+  call Check_tab_count(1, 'tabclose! +1', 1)
+  call assert_equal(1, tabpagenr('$'))
+  call assert_equal('', bufname(''))
+
+  call s:reconstruct_tabpage_for_test(6)
+  for n in range(2)
+    for c in ['0', '$3', '99', '+99', '-99']
+      if n == 0 " pre count
+        let entire_cmd = c . 'tabclose'
+        let err_code = 'E16:'
+      else
+        let entire_cmd = 'tabclose ' . c
+        let err_code = 'E474:'
+      endif
+      call assert_fails(entire_cmd, err_code)
+      call assert_equal(6, tabpagenr('$'))
+    endfor
+  endfor
+
+  call assert_fails('3tabclose', 'E37:')
+  call assert_fails('tabclose 3', 'E37:')
+  call assert_fails('tabclose -+', 'E474:')
+  call assert_fails('tabclose +2-', 'E474:')
+  call assert_equal(6, tabpagenr('$'))
+
+  1tabonly!
+endfunction
+
+" Test for [count] of tabonly
+function Test_tabpage_with_tabonly()
+
+  " Test for the normal behavior (pre count only)
+  let tc = [ [4, '.', '!'], [2, '.+', ''], [3, '.-2', '!'], [1, '.+1', '!'] ]
+  for c in tc
+    call s:reconstruct_tabpage_for_test(6)
+    let entire_cmd = c[1] . 'tabonly' . c[2]
+    call Check_tab_count(c[0], entire_cmd, 1)
+    call assert_equal(1, tabpagenr('$'))
+  endfor
+
+  " Test for the normal behavior
+  let tc2 = [ [3, '', ''], [1, '3', ''], [4, '4', '!'], [3, '1', '!'],
+        \    [2, '', '!'],
+        \    [2, '$', '!'], [3, '+', '!'], [3, '-2', '!'], [3, '+1', '!']
+        \  ]
+  for n in range(2)
+    for c in tc2
+      call s:reconstruct_tabpage_for_test(6)
+      if n == 0 " pre count
+        let entire_cmd = c[1] . 'tabonly' . c[2]
+      else
+        let entire_cmd = 'tabonly' . c[2] . ' ' . c[1]
+      endif
+      call Check_tab_count(c[0], entire_cmd, 1)
+      call assert_equal(1, tabpagenr('$'))
+    endfor
+  endfor
+
+  " Test for the error behavior
+  for n in range(2)
+    for c in ['0', '$3', '99', '+99', '-99']
+      call s:reconstruct_tabpage_for_test(6)
+      if n == 0 " pre count
+        let entire_cmd = c . 'tabonly'
+        let err_code = 'E16:'
+      else
+        let entire_cmd = 'tabonly ' . c
+        let err_code = 'E474:'
+      endif
+      call assert_fails(entire_cmd, err_code)
+      call assert_equal(6, tabpagenr('$'))
+    endfor
+  endfor
+
+  " Test for the error behavior (post count only)
+  for c in tc
+    call s:reconstruct_tabpage_for_test(6)
+    let entire_cmd = 'tabonly' . c[2] . ' ' . c[1]
+    let err_code = 'E474:'
+    call assert_fails(entire_cmd, err_code)
+    call assert_equal(6, tabpagenr('$'))
+  endfor
+
+  call assert_fails('tabonly -+', 'E474:')
+  call assert_fails('tabonly +2-', 'E474:')
+  call assert_equal(6, tabpagenr('$'))
+
+  1tabonly!
+  new
+  only!
 endfunction
 
 func Test_tabnext_on_buf_unload1()
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    259,
+/**/
     258,
 /**/
     257,