diff src/ex_docmd.c @ 6398:5a76e36f07b1 v7.4.530

updated for version 7.4.530 Problem: Many commands take a count or range that is not using line numbers. Solution: For each command specify what kind of count it uses. For windows, buffers and arguments have "$" and "." have a relevant meaning. (Marcin Szamotulski)
author Bram Moolenaar <bram@vim.org>
date Thu, 27 Nov 2014 16:22:48 +0100
parents 60659773c73b
children 2c0cddd0df8c
line wrap: on
line diff
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -60,6 +60,7 @@ static char_u *get_user_command_name __A
 # define IS_USER_CMDIDX(idx) (FALSE)
 #endif
 
+static int compute_buffer_local_count __ARGS((int addr_type, int lnum, int local));
 #ifdef FEAT_EVAL
 static char_u	*do_one_cmd __ARGS((char_u **, int, struct condstack *, char_u *(*fgetline)(int, void *, int), void *cookie));
 #else
@@ -133,7 +134,7 @@ static int	getargopt __ARGS((exarg_T *ea
 #endif
 
 static int	check_more __ARGS((int, int));
-static linenr_T get_address __ARGS((char_u **, int skip, int to_other_file));
+static linenr_T get_address __ARGS((char_u **, int addr_type, int skip, int to_other_file));
 static void	get_flags __ARGS((exarg_T *eap));
 #if !defined(FEAT_PERL) \
 	|| !defined(FEAT_PYTHON) || !defined(FEAT_PYTHON3) \
@@ -1680,6 +1681,39 @@ getline_cookie(fgetline, cookie)
 }
 #endif
 
+
+/*
+ * Helper function to apply an offset for buffer commands, i.e. ":bdelete",
+ * ":bwipeout", etc.
+ * Returns the buffer number.
+ */
+    static int
+compute_buffer_local_count(addr_type, lnum, offset)
+    int	    addr_type;
+    int	    lnum;
+    int	    offset;
+{
+    buf_T   *buf;
+    int     count = offset;
+
+    buf = firstbuf;
+    while (buf->b_next != NULL && buf->b_fnum < lnum)
+	buf = buf->b_next;
+    while (count != 0)
+    {
+	count += (count < 0) ? 1 : -1;
+	if (buf->b_prev == NULL)
+	    break;
+	buf = (count < 0) ? buf->b_prev : buf->b_next;
+	if (addr_type == ADDR_LOADED_BUFFERS)
+	    /* skip over unloaded buffers */
+	    while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL)
+		buf = (count < 0) ? buf->b_prev : buf->b_next;
+    }
+    return buf->b_fnum;
+}
+
+
 /*
  * Execute one Ex command.
  *
@@ -1687,10 +1721,10 @@ getline_cookie(fgetline, cookie)
  *
  * 1. skip comment lines and leading space
  * 2. handle command modifiers
- * 3. parse range
- * 4. parse command
- * 5. parse arguments
- * 6. switch on command name
+ * 3. parse command
+ * 4. parse range
+ * 6. parse arguments
+ * 7. switch on command name
  *
  * Note: "fgetline" can be NULL.
  *
@@ -1730,6 +1764,9 @@ do_one_cmd(cmdlinep, sourcing,
 #endif
     cmdmod_T		save_cmdmod;
     int			ni;			/* set when Not Implemented */
+    win_T		*wp;
+    tabpage_T		*tp;
+    char_u		*cmd;
 
     vim_memset(&ea, 0, sizeof(ea));
     ea.line1 = 1;
@@ -1769,7 +1806,7 @@ do_one_cmd(cmdlinep, sourcing,
     for (;;)
     {
 /*
- * 1. skip comment lines and leading white space and colons
+ * 1. Skip comment lines and leading white space and colons.
  */
 	while (*ea.cmd == ' ' || *ea.cmd == '\t' || *ea.cmd == ':')
 	    ++ea.cmd;
@@ -1794,7 +1831,7 @@ do_one_cmd(cmdlinep, sourcing,
 	}
 
 /*
- * 2. handle command modifiers.
+ * 2. Handle command modifiers.
  */
 	p = ea.cmd;
 	if (VIM_ISDIGIT(*ea.cmd))
@@ -2003,7 +2040,18 @@ do_one_cmd(cmdlinep, sourcing,
 #endif
 
 /*
- * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
+ * 3. Skip over the range to find the command.  Let "p" point to after it.
+ *
+ * We need the command to know what kind of range it uses.
+ */
+    cmd = ea.cmd;
+    ea.cmd = skip_range(ea.cmd, NULL);
+    if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
+	ea.cmd = skipwhite(ea.cmd + 1);
+    p = find_command(&ea, NULL);
+
+/*
+ * 4. parse a range specifier of the form: addr [,addr] [;addr] ..
  *
  * where 'addr' is:
  *
@@ -2019,13 +2067,52 @@ do_one_cmd(cmdlinep, sourcing,
  * is equal to the lower.
  */
 
+    if (ea.cmdidx != CMD_SIZE)
+	ea.addr_type = cmdnames[(int)ea.cmdidx].cmd_addr_type;
+    else
+	ea.addr_type = ADDR_LINES;
+    ea.cmd = cmd;
+
     /* repeat for all ',' or ';' separated addresses */
     for (;;)
     {
 	ea.line1 = ea.line2;
-	ea.line2 = curwin->w_cursor.lnum;   /* default is current line number */
+	switch (ea.addr_type)
+	{
+	    case ADDR_LINES:
+		/* default is current line number */
+		ea.line2 = curwin->w_cursor.lnum;
+		break;
+	    case ADDR_WINDOWS:
+		lnum = 0;
+		for (wp = firstwin; wp != NULL; wp = wp->w_next)
+		{
+		    lnum++;
+		    if (wp == curwin)
+			break;
+		}
+		ea.line2 = lnum;
+		break;
+	    case ADDR_ARGUMENTS:
+		ea.line2 = curwin->w_arg_idx + 1;
+		break;
+	    case ADDR_LOADED_BUFFERS:
+	    case ADDR_UNLOADED_BUFFERS:
+		ea.line2 = curbuf->b_fnum;
+		break;
+	    case ADDR_TABS:
+		lnum = 0;
+		for(tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+		{
+		    lnum++;
+		    if (tp == curtab)
+			break;
+		}
+		ea.line2 = lnum;
+		break;
+	}
 	ea.cmd = skipwhite(ea.cmd);
-	lnum = get_address(&ea.cmd, ea.skip, ea.addr_count == 0);
+	lnum = get_address(&ea.cmd, ea.addr_type, ea.skip, ea.addr_count == 0);
 	if (ea.cmd == NULL)		    /* error detected */
 	    goto doend;
 	if (lnum == MAXLNUM)
@@ -2033,8 +2120,24 @@ do_one_cmd(cmdlinep, sourcing,
 	    if (*ea.cmd == '%')		    /* '%' - all lines */
 	    {
 		++ea.cmd;
-		ea.line1 = 1;
-		ea.line2 = curbuf->b_ml.ml_line_count;
+		switch (ea.addr_type)
+		{
+		    case ADDR_LINES:
+			ea.line1 = 1;
+			ea.line2 = curbuf->b_ml.ml_line_count;
+			break;
+		    case ADDR_WINDOWS:
+		    case ADDR_LOADED_BUFFERS:
+		    case ADDR_UNLOADED_BUFFERS:
+		    case ADDR_TABS:
+			errormsg = (char_u *)_(e_invrange);
+			goto doend;
+			break;
+		    case ADDR_ARGUMENTS:
+			ea.line1 = 1;
+			ea.line2 = ARGCOUNT;
+			break;
+		}
 		++ea.addr_count;
 	    }
 					    /* '*' - visual area */
@@ -2042,6 +2145,12 @@ do_one_cmd(cmdlinep, sourcing,
 	    {
 		pos_T	    *fp;
 
+		if (ea.addr_type != ADDR_LINES)
+		{
+		    errormsg = (char_u *)_(e_invrange);
+		    goto doend;
+		}
+
 		++ea.cmd;
 		if (!ea.skip)
 		{
@@ -2084,7 +2193,7 @@ do_one_cmd(cmdlinep, sourcing,
     check_cursor_lnum();
 
 /*
- * 4. parse command
+ * 5. Parse the command.
  */
 
     /*
@@ -2098,8 +2207,8 @@ do_one_cmd(cmdlinep, sourcing,
      * If we got a line, but no command, then go to the line.
      * If we find a '|' or '\n' we set ea.nextcmd.
      */
-    if (*ea.cmd == NUL || *ea.cmd == '"' ||
-			       (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL)
+    if (*ea.cmd == NUL || *ea.cmd == '"'
+			      || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL)
     {
 	/*
 	 * strange vi behaviour:
@@ -2145,9 +2254,6 @@ do_one_cmd(cmdlinep, sourcing,
 	goto doend;
     }
 
-    /* Find the command and let "p" point to after it. */
-    p = find_command(&ea, NULL);
-
 #ifdef FEAT_AUTOCMD
     /* If this looks like an undefined user command and there are CmdUndefined
      * autocommands defined, trigger the matching autocommands. */
@@ -2229,7 +2335,7 @@ do_one_cmd(cmdlinep, sourcing,
 	ea.forceit = FALSE;
 
 /*
- * 5. parse arguments
+ * 5. Parse arguments.
  */
     if (!IS_USER_CMDIDX(ea.cmdidx))
 	ea.argt = (long)cmdnames[(int)ea.cmdidx].cmd_argt;
@@ -2676,7 +2782,7 @@ do_one_cmd(cmdlinep, sourcing,
 #endif
 
 /*
- * 6. switch on command name
+ * 6. Switch on command name.
  *
  * The "ea" structure holds the arguments that can be used.
  */
@@ -4082,8 +4188,9 @@ skip_range(cmd, ctx)
  * Return MAXLNUM when no Ex address was found.
  */
     static linenr_T
-get_address(ptr, skip, to_other_file)
+get_address(ptr, addr_type, skip, to_other_file)
     char_u	**ptr;
+    int		addr_type;  /* flag: one of ADDR_LINES, ... */
     int		skip;	    /* only skip the address, don't use it */
     int		to_other_file;  /* flag: may jump to other file */
 {
@@ -4094,6 +4201,8 @@ get_address(ptr, skip, to_other_file)
     pos_T	pos;
     pos_T	*fp;
     linenr_T	lnum;
+    win_T	*wp;
+    tabpage_T	*tp;
 
     cmd = skipwhite(*ptr);
     lnum = MAXLNUM;
@@ -4102,137 +4211,204 @@ get_address(ptr, skip, to_other_file)
 	switch (*cmd)
 	{
 	    case '.':			    /* '.' - Cursor position */
-			++cmd;
+		++cmd;
+		switch (addr_type)
+		{
+		    case ADDR_LINES:
 			lnum = curwin->w_cursor.lnum;
 			break;
+		    case ADDR_WINDOWS:
+			lnum = 0;
+			for (wp = firstwin; wp != NULL; wp = wp->w_next)
+			{
+			    lnum++;
+			    if (wp == curwin)
+				break;
+			}
+			break;
+		    case ADDR_ARGUMENTS:
+			lnum = curwin->w_arg_idx + 1;
+			break;
+		    case ADDR_LOADED_BUFFERS:
+		    case ADDR_UNLOADED_BUFFERS:
+			lnum = curbuf->b_fnum;
+			break;
+		    case ADDR_TABS:
+			lnum = 0;
+			for(tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+			{
+			    lnum++;
+			    if (tp == curtab)
+				break;
+			}
+			break;
+		}
+		break;
 
 	    case '$':			    /* '$' - last line */
-			++cmd;
+		++cmd;
+		switch (addr_type)
+		{
+		    case ADDR_LINES:
 			lnum = curbuf->b_ml.ml_line_count;
 			break;
+		    case ADDR_WINDOWS:
+			lnum = 0;
+			for (wp = firstwin; wp != NULL; wp = wp->w_next)
+			    lnum++;
+			break;
+		    case ADDR_ARGUMENTS:
+			lnum = ARGCOUNT;
+			break;
+		    case ADDR_LOADED_BUFFERS:
+		    case ADDR_UNLOADED_BUFFERS:
+			lnum = lastbuf->b_fnum;
+			break;
+		    case ADDR_TABS:
+			lnum = 0;
+			for(tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+			    lnum++;
+			break;
+		}
+		break;
 
 	    case '\'':			    /* ''' - mark */
-			if (*++cmd == NUL)
+		if (*++cmd == NUL)
+		{
+		    cmd = NULL;
+		    goto error;
+		}
+		if (addr_type != ADDR_LINES)
+		{
+		    EMSG(_(e_invaddr));
+		    goto error;
+		}
+		if (skip)
+		    ++cmd;
+		else
+		{
+		    /* Only accept a mark in another file when it is
+		     * used by itself: ":'M". */
+		    fp = getmark(*cmd, to_other_file && cmd[1] == NUL);
+		    ++cmd;
+		    if (fp == (pos_T *)-1)
+			/* Jumped to another file. */
+			lnum = curwin->w_cursor.lnum;
+		    else
+		    {
+			if (check_mark(fp) == FAIL)
 			{
 			    cmd = NULL;
 			    goto error;
 			}
-			if (skip)
-			    ++cmd;
-			else
-			{
-			    /* Only accept a mark in another file when it is
-			     * used by itself: ":'M". */
-			    fp = getmark(*cmd, to_other_file && cmd[1] == NUL);
-			    ++cmd;
-			    if (fp == (pos_T *)-1)
-				/* Jumped to another file. */
-				lnum = curwin->w_cursor.lnum;
-			    else
-			    {
-				if (check_mark(fp) == FAIL)
-				{
-				    cmd = NULL;
-				    goto error;
-				}
-				lnum = fp->lnum;
-			    }
-			}
-			break;
+			lnum = fp->lnum;
+		    }
+		}
+		break;
 
 	    case '/':
 	    case '?':			/* '/' or '?' - search */
-			c = *cmd++;
-			if (skip)	/* skip "/pat/" */
-			{
-			    cmd = skip_regexp(cmd, c, (int)p_magic, NULL);
-			    if (*cmd == c)
-				++cmd;
-			}
-			else
-			{
-			    pos = curwin->w_cursor; /* save curwin->w_cursor */
-			    /*
-			     * When '/' or '?' follows another address, start
-			     * from there.
-			     */
-			    if (lnum != MAXLNUM)
-				curwin->w_cursor.lnum = lnum;
-			    /*
-			     * Start a forward search at the end of the line.
-			     * Start a backward search at the start of the line.
-			     * This makes sure we never match in the current
-			     * line, and can match anywhere in the
-			     * next/previous line.
-			     */
-			    if (c == '/')
-				curwin->w_cursor.col = MAXCOL;
-			    else
-				curwin->w_cursor.col = 0;
-			    searchcmdlen = 0;
-			    if (!do_search(NULL, c, cmd, 1L,
-					       SEARCH_HIS | SEARCH_MSG, NULL))
-			    {
-				curwin->w_cursor = pos;
-				cmd = NULL;
-				goto error;
-			    }
-			    lnum = curwin->w_cursor.lnum;
-			    curwin->w_cursor = pos;
-			    /* adjust command string pointer */
-			    cmd += searchcmdlen;
-			}
-			break;
+		c = *cmd++;
+		if (addr_type != ADDR_LINES)
+		{
+		    EMSG(_(e_invaddr));
+		    goto error;
+		}
+		if (skip)	/* skip "/pat/" */
+		{
+		    cmd = skip_regexp(cmd, c, (int)p_magic, NULL);
+		    if (*cmd == c)
+			++cmd;
+		}
+		else
+		{
+		    pos = curwin->w_cursor; /* save curwin->w_cursor */
+		    /*
+		     * When '/' or '?' follows another address, start
+		     * from there.
+		     */
+		    if (lnum != MAXLNUM)
+			curwin->w_cursor.lnum = lnum;
+		    /*
+		     * Start a forward search at the end of the line.
+		     * Start a backward search at the start of the line.
+		     * This makes sure we never match in the current
+		     * line, and can match anywhere in the
+		     * next/previous line.
+		     */
+		    if (c == '/')
+			curwin->w_cursor.col = MAXCOL;
+		    else
+			curwin->w_cursor.col = 0;
+		    searchcmdlen = 0;
+		    if (!do_search(NULL, c, cmd, 1L,
+				       SEARCH_HIS | SEARCH_MSG, NULL))
+		    {
+			curwin->w_cursor = pos;
+			cmd = NULL;
+			goto error;
+		    }
+		    lnum = curwin->w_cursor.lnum;
+		    curwin->w_cursor = pos;
+		    /* adjust command string pointer */
+		    cmd += searchcmdlen;
+		}
+		break;
 
 	    case '\\':		    /* "\?", "\/" or "\&", repeat search */
-			++cmd;
-			if (*cmd == '&')
-			    i = RE_SUBST;
-			else if (*cmd == '?' || *cmd == '/')
-			    i = RE_SEARCH;
-			else
-			{
-			    EMSG(_(e_backslash));
-			    cmd = NULL;
-			    goto error;
-			}
-
-			if (!skip)
-			{
-			    /*
-			     * When search follows another address, start from
-			     * there.
-			     */
-			    if (lnum != MAXLNUM)
-				pos.lnum = lnum;
-			    else
-				pos.lnum = curwin->w_cursor.lnum;
-
-			    /*
-			     * Start the search just like for the above
-			     * do_search().
-			     */
-			    if (*cmd != '?')
-				pos.col = MAXCOL;
-			    else
-				pos.col = 0;
-			    if (searchit(curwin, curbuf, &pos,
-					*cmd == '?' ? BACKWARD : FORWARD,
-					(char_u *)"", 1L, SEARCH_MSG,
-						i, (linenr_T)0, NULL) != FAIL)
-				lnum = pos.lnum;
-			    else
-			    {
-				cmd = NULL;
-				goto error;
-			    }
-			}
-			++cmd;
-			break;
+		++cmd;
+		if (addr_type != ADDR_LINES)
+		{
+		    EMSG(_(e_invaddr));
+		    goto error;
+		}
+		if (*cmd == '&')
+		    i = RE_SUBST;
+		else if (*cmd == '?' || *cmd == '/')
+		    i = RE_SEARCH;
+		else
+		{
+		    EMSG(_(e_backslash));
+		    cmd = NULL;
+		    goto error;
+		}
+
+		if (!skip)
+		{
+		    /*
+		     * When search follows another address, start from
+		     * there.
+		     */
+		    if (lnum != MAXLNUM)
+			pos.lnum = lnum;
+		    else
+			pos.lnum = curwin->w_cursor.lnum;
+
+		    /*
+		     * Start the search just like for the above
+		     * do_search().
+		     */
+		    if (*cmd != '?')
+			pos.col = MAXCOL;
+		    else
+			pos.col = 0;
+		    if (searchit(curwin, curbuf, &pos,
+				*cmd == '?' ? BACKWARD : FORWARD,
+				(char_u *)"", 1L, SEARCH_MSG,
+					i, (linenr_T)0, NULL) != FAIL)
+			lnum = pos.lnum;
+		    else
+		    {
+			cmd = NULL;
+			goto error;
+		    }
+		}
+		++cmd;
+		break;
 
 	    default:
-			if (VIM_ISDIGIT(*cmd))	/* absolute line number */
-			    lnum = getdigits(&cmd);
+		if (VIM_ISDIGIT(*cmd))	/* absolute line number */
+		    lnum = getdigits(&cmd);
 	}
 
 	for (;;)
@@ -4242,7 +4418,40 @@ get_address(ptr, skip, to_other_file)
 		break;
 
 	    if (lnum == MAXLNUM)
-		lnum = curwin->w_cursor.lnum;	/* "+1" is same as ".+1" */
+	    {
+		switch (addr_type)
+		{
+		    case ADDR_LINES:
+			lnum = curwin->w_cursor.lnum;	/* "+1" is same as ".+1" */
+			break;
+		    case ADDR_WINDOWS:
+			lnum = 0;
+			for (wp = firstwin; wp != NULL; wp = wp->w_next)
+			{
+			    lnum++;
+			    if (wp == curwin)
+				break;
+			}
+			break;
+		    case ADDR_ARGUMENTS:
+			lnum = curwin->w_arg_idx + 1;
+			break;
+		    case ADDR_LOADED_BUFFERS:
+		    case ADDR_UNLOADED_BUFFERS:
+			lnum = curbuf->b_fnum;
+			break;
+		    case ADDR_TABS:
+			lnum = 0;
+			for(tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+			{
+			    lnum++;
+			    if (tp == curtab)
+				break;
+			}
+			break;
+		}
+	    }
+
 	    if (VIM_ISDIGIT(*cmd))
 		i = '+';		/* "number" is same as "+number" */
 	    else
@@ -4251,10 +4460,59 @@ get_address(ptr, skip, to_other_file)
 		n = 1;
 	    else
 		n = getdigits(&cmd);
-	    if (i == '-')
+	    if (addr_type == ADDR_LOADED_BUFFERS
+		    || addr_type == ADDR_UNLOADED_BUFFERS)
+		lnum = compute_buffer_local_count(addr_type, lnum, n);
+	    else if (i == '-')
 		lnum -= n;
 	    else
 		lnum += n;
+
+	    switch (addr_type)
+	    {
+		case ADDR_LINES:
+		    break;
+		case ADDR_ARGUMENTS:
+		    if (lnum < 0)
+			lnum = 0;
+		    else if (lnum >= ARGCOUNT)
+			lnum = ARGCOUNT;
+		    break;
+		case ADDR_TABS:
+		    if (lnum < 0)
+		    {
+			lnum = 0;
+			break;
+		    }
+		    c = 0;
+		    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+			c++;
+		    if (lnum >= c)
+			lnum = c;
+		    break;
+		case ADDR_WINDOWS:
+		    if (lnum < 0)
+		    {
+			lnum = 0;
+			break;
+		    }
+		    c = 0;
+		    for (wp = firstwin; wp != NULL; wp = wp->w_next)
+			c++;
+		    if (lnum > c)
+			lnum = c;
+		    break;
+		case ADDR_LOADED_BUFFERS:
+		case ADDR_UNLOADED_BUFFERS:
+		    if (lnum < firstbuf->b_fnum)
+		    {
+			lnum = firstbuf->b_fnum;
+			break;
+		    }
+		    if (lnum > lastbuf->b_fnum)
+			lnum = lastbuf->b_fnum;
+		    break;
+	    }
 	}
     } while (*cmd == '/' || *cmd == '?');
 
@@ -6556,6 +6814,10 @@ not_exiting()
 ex_quit(eap)
     exarg_T	*eap;
 {
+    win_T	*wp;
+    buf_T	*buf;
+    int		wnr;
+
 #ifdef FEAT_CMDWIN
     if (cmdwin_type != 0)
     {
@@ -6569,11 +6831,28 @@ ex_quit(eap)
 	text_locked_msg();
 	return;
     }
+    if (eap->addr_count > 0)
+    {
+	wnr = eap->line2;
+	for (wp = firstwin; --wnr > 0; )
+	{
+	    if (wp->w_next == NULL)
+		break;
+	    else
+		wp = wp->w_next;
+	}
+	buf = wp->w_buffer;
+    }
+    else
+    {
+	wp = curwin;
+	buf = curbuf;
+    }
 #ifdef FEAT_AUTOCMD
     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() || (buf->b_nwindows == 1 && buf->b_closing))
 	return;
 #endif
 
@@ -6606,7 +6885,7 @@ ex_quit(eap)
 	need_mouse_correct = TRUE;
 # endif
 	/* close window; may free buffer */
-	win_close(curwin, !P_HID(curwin->w_buffer) || eap->forceit);
+	win_close(wp, !P_HID(wp->w_buffer) || eap->forceit);
 #endif
     }
 }
@@ -6668,6 +6947,8 @@ ex_quit_all(eap)
 ex_close(eap)
     exarg_T	*eap;
 {
+    win_T	*win;
+    int		winnr = 0;
 # ifdef FEAT_CMDWIN
     if (cmdwin_type != 0)
 	cmdwin_result = Ctrl_C;
@@ -6678,7 +6959,21 @@ ex_close(eap)
 		&& !curbuf_locked()
 #endif
 		)
-	    ex_win_close(eap->forceit, curwin, NULL);
+	{
+	    if (eap->addr_count == 0)
+		ex_win_close(eap->forceit, curwin, NULL);
+	    else {
+		for (win = firstwin; win != NULL; win = win->w_next)
+		{
+		    winnr++;
+		    if (winnr == eap->line2)
+			break;
+		}
+		if (win == NULL)
+		    win = lastwin;
+		ex_win_close(eap->forceit, win, NULL);
+	    }
+	}
 }
 
 # ifdef FEAT_QUICKFIX
@@ -6804,6 +7099,8 @@ ex_tabonly(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)
@@ -6882,9 +7179,23 @@ tabpage_close_other(tp, forceit)
 ex_only(eap)
     exarg_T	*eap;
 {
+    win_T   *wp;
+    int	    wnr;
 # ifdef FEAT_GUI
     need_mouse_correct = TRUE;
 # endif
+    if (eap->addr_count > 0)
+    {
+	wnr = eap->line2;
+	for (wp = firstwin; --wnr > 0; )
+	{
+	    if (wp->w_next == NULL)
+		break;
+	    else
+		wp = wp->w_next;
+	}
+	win_goto(wp);
+    }
     close_others(TRUE, eap->forceit);
 }
 
@@ -6906,6 +7217,9 @@ ex_all(eap)
 ex_hide(eap)
     exarg_T	*eap;
 {
+    win_T	*win;
+    int		winnr = 0;
+
     if (*eap->arg != NUL && check_nextcmd(eap->arg) == NULL)
 	eap->errmsg = e_invarg;
     else
@@ -6918,7 +7232,19 @@ ex_hide(eap)
 # ifdef FEAT_GUI
 	    need_mouse_correct = TRUE;
 # endif
-	    win_close(curwin, FALSE);	/* don't free buffer */
+	    if (eap->addr_count == 0)
+		win_close(curwin, FALSE);	/* don't free buffer */
+	    else {
+		for (win = firstwin; win != NULL; win = win->w_next)
+		{
+		    winnr++;
+		    if (winnr == eap->line2)
+			break;
+		}
+		if (win == NULL)
+		    win = lastwin;
+		win_close(win, FALSE);
+	    }
 	}
 #endif
     }
@@ -8652,7 +8978,7 @@ ex_copymove(eap)
 {
     long	n;
 
-    n = get_address(&eap->arg, FALSE, FALSE);
+    n = get_address(&eap->arg, eap->addr_type, FALSE, FALSE);
     if (eap->arg == NULL)	    /* error detected */
     {
 	eap->nextcmd = NULL;