diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2015-04-29 02:11:30 -0400 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2015-04-29 02:11:30 -0400 |
commit | 45b617afada9304cb265aad02645b22d76dfc2d5 (patch) | |
tree | 7f28a768d2c88e646f1bc4e1b9e5b1ea604cce21 /src/nvim/ex_docmd.c | |
parent | 6a8862ded4d6f1693ac591ac8e47d1acbd044c49 (diff) | |
parent | b46746b93ef298688cba3d8fbbcdae13e069935c (diff) | |
download | rneovim-45b617afada9304cb265aad02645b22d76dfc2d5.tar.gz rneovim-45b617afada9304cb265aad02645b22d76dfc2d5.tar.bz2 rneovim-45b617afada9304cb265aad02645b22d76dfc2d5.zip |
Merge pull request #2041 from fmoralesc/command-ranges
Command ranges (was PR #1793)
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r-- | src/nvim/ex_docmd.c | 681 |
1 files changed, 618 insertions, 63 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 51010983bb..646d64f9f8 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -85,6 +85,7 @@ typedef struct ucmd { char_u *uc_rep; /* The command's replacement string */ long uc_def; /* The default value for a range/count */ int uc_compl; /* completion type */ + int uc_addr_type; /* The command's address type */ scid_T uc_scriptID; /* SID where the command was defined */ char_u *uc_compl_arg; /* completion argument if any */ } ucmd_T; @@ -1066,16 +1067,178 @@ void * getline_cookie(LineGetter fgetline, } /* + * Helper function to apply an offset for buffer commands, i.e. ":bdelete", + * ":bwipeout", etc. + * Returns the buffer number. + */ +static int compute_buffer_local_count(int addr_type, int lnum, int offset) +{ + buf_T *buf; + buf_T *nextbuf; + 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; + nextbuf = (offset < 0) ? buf->b_prev : buf->b_next; + if (nextbuf == NULL) + break; + buf = nextbuf; + if (addr_type == ADDR_LOADED_BUFFERS) + /* skip over unloaded buffers */ + while (buf->b_ml.ml_mfp == NULL) { + nextbuf = (offset < 0) ? buf->b_prev : buf->b_next; + if (nextbuf == NULL) { + break; + } + buf = nextbuf; + } + } + // we might have gone too far, last buffer is not loaded + if (addr_type == ADDR_LOADED_BUFFERS) { + while (buf->b_ml.ml_mfp == NULL) { + nextbuf = (offset >= 0) ? buf->b_prev : buf->b_next; + if (nextbuf == NULL) + break; + buf = nextbuf; + } + } + return buf->b_fnum; +} + +static int current_win_nr(win_T *win) +{ + int nr = 0; + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + ++nr; + if (wp == win) + break; + } + return nr; +} + +static int current_tab_nr(tabpage_T *tab) +{ + int nr = 0; + + FOR_ALL_TABS(tp) { + ++nr; + if (tp == tab) + break; + } + return nr; +} + +#define CURRENT_WIN_NR current_win_nr(curwin) +#define LAST_WIN_NR current_win_nr(NULL) +#define CURRENT_TAB_NR current_tab_nr(curtab) +#define LAST_TAB_NR current_tab_nr(NULL) + +/* +* Figure out the address type for ":wincmd". +*/ +static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) +{ + switch (*arg) { + case 'S': + case Ctrl_S: + case 's': + case Ctrl_N: + case 'n': + case 'j': + case Ctrl_J: + case 'k': + case Ctrl_K: + case 'T': + case Ctrl_R: + case 'r': + case 'R': + case 'K': + case 'J': + case '+': + case '-': + case Ctrl__: + case '_': + case '|': + case ']': + case Ctrl_RSB: + case 'g': + case Ctrl_G: + case Ctrl_V: + case 'v': + case 'h': + case Ctrl_H: + case 'l': + case Ctrl_L: + case 'H': + case 'L': + case '>': + case '<': + case '}': + case 'f': + case 'F': + case Ctrl_F: + case 'i': + case Ctrl_I: + case 'd': + case Ctrl_D: + /* window size or any count */ + eap->addr_type = ADDR_LINES; + break; + + case Ctrl_HAT: + case '^': + /* buffer number */ + eap->addr_type = ADDR_BUFFERS; + break; + + case Ctrl_Q: + case 'q': + case Ctrl_C: + case 'c': + case Ctrl_O: + case 'o': + case Ctrl_W: + case 'w': + case 'W': + case 'x': + case Ctrl_X: + /* window number */ + eap->addr_type = ADDR_WINDOWS; + break; + + case Ctrl_Z: + case 'z': + case 'P': + case 't': + case Ctrl_T: + case 'b': + case Ctrl_B: + case 'p': + case Ctrl_P: + case '=': + case CAR: + /* no count */ + eap->addr_type = 0; + break; + } +} + +/* * Execute one Ex command. * * If 'sourcing' is TRUE, the command will be included in the error message. * * 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. skip over the range to find the command + * 4. parse the range + * 5. parse the command + * 6. parse arguments + * 7. switch on command name * * Note: "fgetline" can be NULL. * @@ -1100,6 +1263,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, int did_sandbox = FALSE; cmdmod_T save_cmdmod; int ni; /* set when Not Implemented */ + char_u *cmd; memset(&ea, 0, sizeof(ea)); ea.line1 = 1; @@ -1132,7 +1296,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.cmd = *cmdlinep; 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; @@ -1155,7 +1319,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, } /* - * 2. handle command modifiers. + * 2. Handle command modifiers. */ p = ea.cmd; if (ascii_isdigit(*ea.cmd)) @@ -1319,8 +1483,18 @@ static char_u * do_one_cmd(char_u **cmdlinep, (void)do_intthrow(cstack); } + // 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); + /* - * 3. parse a range specifier of the form: addr [,addr] [;addr] .. + * 4. Parse a range specifier of the form: addr [,addr] [;addr] .. * * where 'addr' is: * @@ -1336,25 +1510,109 @@ static char_u * do_one_cmd(char_u **cmdlinep, * is equal to the lower. */ + // ea.addr_type for user commands is set by find_ucmd + if (!IS_USER_CMDIDX(ea.cmdidx)) { + if (ea.cmdidx != CMD_SIZE) { + ea.addr_type = cmdnames[(int)ea.cmdidx].cmd_addr_type; + } else { + ea.addr_type = ADDR_LINES; + } + // :wincmd range depends on the argument + if (ea.cmdidx == CMD_wincmd && p != NULL) { + get_wincmd_addr_type(skipwhite(p), &ea); + } + } + /* repeat for all ',' or ';' separated addresses */ + ea.cmd = cmd; 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 = CURRENT_WIN_NR; + ea.line2 = lnum; + break; + case ADDR_ARGUMENTS: + ea.line2 = curwin->w_arg_idx + 1; + if (ea.line2 > ARGCOUNT) { + ea.line2 = ARGCOUNT; + } + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + ea.line2 = curbuf->b_fnum; + break; + case ADDR_TABS: + lnum = CURRENT_TAB_NR; + 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) { 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_LOADED_BUFFERS: { + buf_T *buf = firstbuf; + while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_next; + } + ea.line1 = buf->b_fnum; + buf = lastbuf; + while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_prev; + } + ea.line2 = buf->b_fnum; + break; + } + case ADDR_BUFFERS: + ea.line1 = firstbuf->b_fnum; + ea.line2 = lastbuf->b_fnum; + break; + case ADDR_WINDOWS: + case ADDR_TABS: + if (IS_USER_CMDIDX(ea.cmdidx)) { + ea.line1 = 1; + ea.line2 = + ea.addr_type == ADDR_WINDOWS ? LAST_WIN_NR : LAST_TAB_NR; + } else { + // there is no Vim command which uses '%' and + // ADDR_WINDOWS or ADDR_TABS + errormsg = (char_u *)_(e_invrange); + goto doend; + } + break; + case ADDR_ARGUMENTS: + if (ARGCOUNT == 0) { + ea.line1 = ea.line2 = 0; + } else { + ea.line1 = 1; + ea.line2 = ARGCOUNT; + } + break; + } ++ea.addr_count; } /* '*' - visual area */ else if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) { pos_T *fp; + if (ea.addr_type != ADDR_LINES) { + errormsg = (char_u *)_(e_invrange); + goto doend; + } + ++ea.cmd; if (!ea.skip) { fp = getmark('<', FALSE); @@ -1392,7 +1650,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, check_cursor_lnum(); /* - * 4. parse command + * 5. Parse the command. */ /* @@ -1446,9 +1704,6 @@ static char_u * do_one_cmd(char_u **cmdlinep, goto doend; } - /* Find the command and let "p" point to after it. */ - p = find_command(&ea, NULL); - // If this looks like an undefined user command and there are CmdUndefined // autocommands defined, trigger the matching autocommands. if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip @@ -1502,7 +1757,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.forceit = FALSE; /* - * 5. parse arguments + * 6. Parse arguments. */ if (!IS_USER_CMDIDX(ea.cmdidx)) { ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; @@ -1695,8 +1950,43 @@ static char_u * do_one_cmd(char_u **cmdlinep, } if ((ea.argt & DFLALL) && ea.addr_count == 0) { + buf_T *buf; + ea.line1 = 1; - ea.line2 = curbuf->b_ml.ml_line_count; + switch (ea.addr_type) { + case ADDR_LINES: + ea.line2 = curbuf->b_ml.ml_line_count; + break; + case ADDR_LOADED_BUFFERS: + buf = firstbuf; + while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_next; + } + ea.line1 = buf->b_fnum; + buf = lastbuf; + while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_prev; + } + ea.line2 = buf->b_fnum; + break; + case ADDR_BUFFERS: + ea.line1 = firstbuf->b_fnum; + ea.line2 = lastbuf->b_fnum; + break; + case ADDR_WINDOWS: + ea.line2 = LAST_WIN_NR; + break; + case ADDR_TABS: + ea.line2 = LAST_TAB_NR; + break; + case ADDR_ARGUMENTS: + if (ARGCOUNT == 0) { + ea.line1 = ea.line2 = 0; + } else { + ea.line2 = ARGCOUNT; + } + break; + } } /* accept numbered register only when no count allowed (:put) */ @@ -1888,7 +2178,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, } /* - * 6. switch on command name + * 7. Switch on command name. * * The "ea" structure holds the arguments that can be used. */ @@ -2057,7 +2347,7 @@ static char_u *find_command(exarg_T *eap, int *full) * Exceptions: * - the 'k' command can directly be followed by any character. * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' - * but :sre[wind] is another command, as are :scrip[tnames], + * but :sre[wind] is another command, as are :scr[iptnames], * :scs[cope], :sim[alt], :sig[ns] and :sil[ent]. * - the "d" command can directly be followed by 'l' or 'p' flag. */ @@ -2195,6 +2485,7 @@ find_ucmd ( eap->cmdidx = CMD_USER_BUF; eap->argt = uc->uc_argt; eap->useridx = j; + eap->addr_type = uc->uc_addr_type; if (compl != NULL) *compl = uc->uc_compl; @@ -2734,9 +3025,8 @@ set_one_cmd_context ( return NULL; } - /* For the -complete and -nargs attributes, we complete - * their arguments as well. - */ + // For the -complete, -nargs and -addr attributes, we complete + // their arguments as well. if (STRNICMP(arg, "complete", p - arg) == 0) { xp->xp_context = EXPAND_USER_COMPLETE; xp->xp_pattern = p + 1; @@ -2745,6 +3035,10 @@ set_one_cmd_context ( xp->xp_context = EXPAND_USER_NARGS; xp->xp_pattern = p + 1; return NULL; + } else if (STRNICMP(arg, "addr", p - arg) == 0) { + xp->xp_context = EXPAND_USER_ADDR_TYPE; + xp->xp_pattern = p + 1; + return NULL; } return NULL; } @@ -3098,12 +3392,11 @@ skip_range ( * * Return MAXLNUM when no Ex address was found. */ -static linenr_T -get_address ( - char_u **ptr, - int skip, /* only skip the address, don't use it */ - int to_other_file /* flag: may jump to other file */ -) +static linenr_T get_address(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 + ) { int c; int i; @@ -3112,6 +3405,7 @@ get_address ( pos_T pos; pos_T *fp; linenr_T lnum; + buf_T *buf; cmd = skipwhite(*ptr); lnum = MAXLNUM; @@ -3119,12 +3413,55 @@ get_address ( switch (*cmd) { case '.': /* '.' - Cursor position */ ++cmd; - lnum = curwin->w_cursor.lnum; + switch (addr_type) { + case ADDR_LINES: + lnum = curwin->w_cursor.lnum; + break; + case ADDR_WINDOWS: + lnum = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = curwin->w_arg_idx + 1; + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + lnum = curbuf->b_fnum; + break; + case ADDR_TABS: + lnum = CURRENT_TAB_NR; + break; + } break; case '$': /* '$' - last line */ ++cmd; - lnum = curbuf->b_ml.ml_line_count; + switch (addr_type) { + case ADDR_LINES: + lnum = curbuf->b_ml.ml_line_count; + break; + case ADDR_WINDOWS: + lnum = LAST_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = ARGCOUNT; + break; + case ADDR_LOADED_BUFFERS: + buf = lastbuf; + while (buf->b_ml.ml_mfp == NULL) { + if (buf->b_prev == NULL) { + break; + } + buf = buf->b_prev; + } + lnum = buf->b_fnum; + break; + case ADDR_BUFFERS: + lnum = lastbuf->b_fnum; + break; + case ADDR_TABS: + lnum = LAST_TAB_NR; + break; + } break; case '\'': /* ''' - mark */ @@ -3132,6 +3469,10 @@ get_address ( cmd = NULL; goto error; } + if (addr_type != ADDR_LINES) { + EMSG(_(e_invaddr)); + goto error; + } if (skip) ++cmd; else { @@ -3155,6 +3496,10 @@ get_address ( case '/': case '?': /* '/' or '?' - search */ c = *cmd++; + if (addr_type != ADDR_LINES) { + EMSG(_(e_invaddr)); + goto error; + } if (skip) { /* skip "/pat/" */ cmd = skip_regexp(cmd, c, p_magic, NULL); if (*cmd == c) @@ -3194,6 +3539,10 @@ get_address ( case '\\': /* "\?", "\/" or "\&", repeat search */ ++cmd; + if (addr_type != ADDR_LINES) { + EMSG(_(e_invaddr)); + goto error; + } if (*cmd == '&') i = RE_SUBST; else if (*cmd == '?' || *cmd == '/') @@ -3233,8 +3582,27 @@ get_address ( if (*cmd != '-' && *cmd != '+' && !ascii_isdigit(*cmd)) break; - if (lnum == MAXLNUM) - lnum = curwin->w_cursor.lnum; /* "+1" is same as ".+1" */ + if (lnum == MAXLNUM) { + switch (addr_type) { + case ADDR_LINES: + lnum = curwin->w_cursor.lnum; /* "+1" is same as ".+1" */ + break; + case ADDR_WINDOWS: + lnum = CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + lnum = curwin->w_arg_idx + 1; + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + lnum = curbuf->b_fnum; + break; + case ADDR_TABS: + lnum = CURRENT_TAB_NR; + break; + } + } + if (ascii_isdigit(*cmd)) i = '+'; /* "number" is same as "+number" */ else @@ -3242,8 +3610,10 @@ get_address ( if (!ascii_isdigit(*cmd)) /* '+' is '+1', but '+0' is not '+1' */ n = 1; else - n = getdigits_long(&cmd); - if (i == '-') + n = getdigits(&cmd); + if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) + lnum = compute_buffer_local_count(addr_type, lnum, (i == '-') ? -1 * n : n); + else if (i == '-') lnum -= n; else lnum += n; @@ -3295,15 +3665,65 @@ static void ex_script_ni(exarg_T *eap) */ static char_u *invalid_range(exarg_T *eap) { - if ( eap->line1 < 0 - || eap->line2 < 0 - || eap->line1 > eap->line2 - || ((eap->argt & RANGE) - && !(eap->argt & NOTADR) - && eap->line2 > curbuf->b_ml.ml_line_count - + (eap->cmdidx == CMD_diffget) - )) + buf_T *buf; + if (eap->line1 < 0 || eap->line2 < 0 || eap->line1 > eap->line2) { return (char_u *)_(e_invrange); + } + + if (eap->argt & RANGE) { + switch(eap->addr_type) { + case ADDR_LINES: + if (!(eap->argt & NOTADR) + && eap->line2 > curbuf->b_ml.ml_line_count + + (eap->cmdidx == CMD_diffget)) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_ARGUMENTS: + if (eap->line2 > ARGCOUNT + (!ARGCOUNT)) { // add 1 if ARGCOUNT is 0 + return (char_u *)_(e_invrange); + } + break; + case ADDR_BUFFERS: + if (eap->line1 < firstbuf->b_fnum + || eap->line2 > lastbuf->b_fnum) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_LOADED_BUFFERS: + buf = firstbuf; + while (buf->b_ml.ml_mfp == NULL) { + if (buf->b_next == NULL) { + return (char_u *)_(e_invrange); + } + buf = buf->b_next; + } + if (eap->line1 < buf->b_fnum) { + return (char_u *)_(e_invrange); + } + buf = lastbuf; + while (buf->b_ml.ml_mfp == NULL) { + if (buf->b_prev == NULL) { + return (char_u *)_(e_invrange); + } + buf = buf->b_prev; + } + if (eap->line2 > buf->b_fnum) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_WINDOWS: + if (eap->line2 > LAST_WIN_NR) { + return (char_u *)_(e_invrange); + } + break; + case ADDR_TABS: + if (eap->line2 > LAST_TAB_NR) { + return (char_u *)_(e_invrange); + } + break; + } + } return NULL; } @@ -4091,10 +4511,9 @@ char_u *get_command_name(expand_T *xp, int idx) return cmdnames[idx].cmd_name; } - static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, int compl, - char_u *compl_arg, int force) + char_u *compl_arg, int addr_type, int force) { ucmd_T *cmd = NULL; char_u *p; @@ -4169,6 +4588,7 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, cmd->uc_compl = compl; cmd->uc_scriptID = current_SID; cmd->uc_compl_arg = compl_arg; + cmd->uc_addr_type = addr_type; return OK; @@ -4178,6 +4598,21 @@ fail: return FAIL; } + +static struct { + int expand; + char *name; +} addr_type_complete[] = +{ + {ADDR_ARGUMENTS, "arguments"}, + {ADDR_LINES, "lines"}, + {ADDR_LOADED_BUFFERS, "loaded_buffers"}, + {ADDR_TABS, "tabs"}, + {ADDR_BUFFERS, "buffers"}, + {ADDR_WINDOWS, "windows"}, + {-1, NULL} +}; + /* * List of names for completion for ":command" with the EXPAND_ flag. * Must be alphabetical for completion. @@ -4226,6 +4661,7 @@ static struct { static void uc_list(char_u *name, size_t name_len) { + int i, j; int found = FALSE; ucmd_T *cmd; int len; @@ -4234,7 +4670,6 @@ static void uc_list(char_u *name, size_t name_len) gap = &curbuf->b_ucmds; for (;; ) { - int i; for (i = 0; i < gap->ga_len; ++i) { cmd = USER_CMD_GA(gap, i); a = cmd->uc_argt; @@ -4245,7 +4680,7 @@ static void uc_list(char_u *name, size_t name_len) /* Put out the title first time */ if (!found) - MSG_PUTS_TITLE(_("\n Name Args Range Complete Definition")); + MSG_PUTS_TITLE(_("\n Name Args Address Complete Definition")); found = TRUE; msg_putchar('\n'); if (got_int) @@ -4300,8 +4735,21 @@ static void uc_list(char_u *name, size_t name_len) IObuff[len++] = ' '; } while (len < 11); + /* Address Type */ + for (j = 0; addr_type_complete[j].expand != -1; ++j) + if (addr_type_complete[j].expand != ADDR_LINES && + addr_type_complete[j].expand == cmd->uc_addr_type) { + STRCPY(IObuff + len, addr_type_complete[j].name); + len += (int)STRLEN(IObuff + len); + break; + } + + do { + IObuff[len++] = ' '; + } while (len < 21); + /* Completion */ - for (int j = 0; command_complete[j].expand != 0; ++j) + for (j = 0; command_complete[j].expand != 0; ++j) if (command_complete[j].expand == cmd->uc_compl) { STRCPY(IObuff + len, command_complete[j].name); len += (int)STRLEN(IObuff + len); @@ -4310,7 +4758,7 @@ static void uc_list(char_u *name, size_t name_len) do { IObuff[len++] = ' '; - } while (len < 21); + } while (len < 35); IObuff[len] = '\0'; msg_outtrans(IObuff); @@ -4346,7 +4794,9 @@ static char_u *uc_fun_cmd(void) return IObuff; } -static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags, int *compl, char_u **compl_arg) +static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, + int *flags, int * compl, char_u **compl_arg, + int *addr_type_arg) { char_u *p; @@ -4445,6 +4895,18 @@ invalid_count: if (parse_compl_arg(val, (int)vallen, compl, argt, compl_arg) == FAIL) return FAIL; + } else if (STRNICMP(attr, "addr", attrlen) == 0) { + *argt |= RANGE; + if (val == NULL) { + EMSG(_("E179: argument required for -addr")); + return FAIL; + } + if (parse_addr_type_arg(val, (int)vallen, argt, addr_type_arg) == FAIL) { + return FAIL; + } + if (addr_type_arg != ADDR_LINES) { + *argt |= (ZEROR | NOTADR); + } } else { char_u ch = attr[len]; attr[len] = '\0'; @@ -4470,6 +4932,7 @@ static void ex_command(exarg_T *eap) int flags = 0; int compl = EXPAND_NOTHING; char_u *compl_arg = NULL; + int addr_type_arg = ADDR_LINES; int has_attr = (eap->arg[0] == '-'); int name_len; @@ -4479,7 +4942,7 @@ static void ex_command(exarg_T *eap) while (*p == '-') { ++p; end = skiptowhite(p); - if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, &compl_arg) + if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, &compl_arg, &addr_type_arg) == FAIL) return; p = skipwhite(end); @@ -4513,7 +4976,7 @@ static void ex_command(exarg_T *eap) return; } else uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, - eap->forceit); + addr_type_arg, eap->forceit); } /* @@ -4941,6 +5404,13 @@ static char_u *get_user_command_name(int idx) { return get_user_commands(NULL, idx - (int)CMD_SIZE); } +/* + * Function given to ExpandGeneric() to obtain the list of user address type names. + */ +char_u *get_user_cmd_addr_type(expand_T *xp, int idx) +{ + return (char_u *)addr_type_complete[idx].name; +} /* * Function given to ExpandGeneric() to obtain the list of user command names. @@ -4961,9 +5431,9 @@ char_u *get_user_commands(expand_T *xp, int idx) */ char_u *get_user_cmd_flags(expand_T *xp, int idx) { - static char *user_cmd_flags[] = - {"bang", "bar", "buffer", "complete", "count", - "nargs", "range", "register"}; + static char *user_cmd_flags[] = {"addr", "bang", "bar", + "buffer", "complete", "count", + "nargs", "range", "register"}; if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) return NULL; @@ -4990,6 +5460,36 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx) return (char_u *)command_complete[idx].name; } +/* + * Parse address type argument + */ +int parse_addr_type_arg(char_u *value, int vallen, uint32_t *argt, + int *addr_type_arg) +{ + int i, a, b; + for (i = 0; addr_type_complete[i].expand != -1; ++i) { + a = (int)STRLEN(addr_type_complete[i].name) == vallen; + b = STRNCMP(value, addr_type_complete[i].name, vallen) == 0; + if (a && b) { + *addr_type_arg = addr_type_complete[i].expand; + break; + } + } + + if (addr_type_complete[i].expand == -1) { + char_u *err = value; + for (i = 0; err[i] == NUL || !ascii_iswhite(err[i]); i++) + ; + err[i] = NUL; + EMSG2(_("E180: Invalid address type value: %s"), err); + return FAIL; + } + + if (*addr_type_arg != ADDR_LINES) + *argt |= NOTADR; + + return OK; +} /* * Parse a completion argument "value[vallen]". @@ -5089,7 +5589,7 @@ void not_exiting(void) } /* - * ":quit": quit current window, quit Vim if closed the last window. + * ":quit": quit current window, quit Vim if the last window is closed. */ static void ex_quit(exarg_T *eap) { @@ -5102,10 +5602,25 @@ static void ex_quit(exarg_T *eap) text_locked_msg(); return; } + + win_T *wp; + + if (eap->addr_count > 0) { + int wnr = eap->line2; + + for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next) { + if (--wnr <= 0) + break; + } + } else { + wp = curwin; + } + 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() || + (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_closing)) return; @@ -5127,7 +5642,7 @@ static void ex_quit(exarg_T *eap) getout(0); } /* 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); } } @@ -5174,12 +5689,24 @@ static void ex_quit_all(exarg_T *eap) */ static void ex_close(exarg_T *eap) { + win_T *win; + int winnr = 0; if (cmdwin_type != 0) cmdwin_result = Ctrl_C; - else if (!text_locked() - && !curbuf_locked() - ) - ex_win_close(eap->forceit, curwin, NULL); + else if (!text_locked() && !curbuf_locked()) { + 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); + } + } } /* @@ -5271,6 +5798,8 @@ static void ex_tabonly(exarg_T *eap) else if (first_tabpage->tp_next == NULL) 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 (int done = 0; done < 1000; ++done) { @@ -5341,6 +5870,18 @@ void tabpage_close_other(tabpage_T *tp, int forceit) */ static void ex_only(exarg_T *eap) { + win_T *wp; + int wnr; + 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); } @@ -5363,7 +5904,21 @@ static void ex_hide(exarg_T *eap) /* ":hide" or ":hide | cmd": hide current window */ eap->nextcmd = check_nextcmd(eap->arg); if (!eap->skip) { - win_close(curwin, FALSE); /* don't free buffer */ + if (eap->addr_count == 0) + win_close(curwin, FALSE); /* don't free buffer */ + else { + int winnr = 0; + win_T *win; + + for (win = firstwin; win != NULL; win = win->w_next) { + winnr++; + if (winnr == eap->line2) + break; + } + if (win == NULL) + win = lastwin; + win_close(win, FALSE); + } } } } @@ -6518,7 +7073,7 @@ static void ex_copymove(exarg_T *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; return; |