📄 ex_docmd.c
字号:
|| (flags & DOCMD_REPEAT)));
vim_free(cmdline_copy);
#ifdef WANT_EVAL
free_cmdlines(&lines_ga);
ga_clear(&lines_ga);
#endif
/*
* If there was too much output to fit on the command line, ask the user to
* hit return before redrawing the screen. With the ":global" command we do
* this only once after the command is finished.
*/
if (did_inc)
{
--RedrawingDisabled;
--no_wait_return;
msg_scroll = FALSE;
/*
* When just finished an ":if"-":else" which was typed, no need to
* wait for hit-return. Also for an error situation.
*/
if (retval == FAIL
#ifdef WANT_EVAL
|| (did_endif && KeyTyped && !did_emsg)
#endif
)
{
need_wait_return = FALSE;
msg_didany = FALSE; /* don't wait when restarting edit */
redraw_later(NOT_VALID);
}
else if ((need_wait_return || (msg_check() && !dont_wait_return)))
{
/*
* The msg_start() above clears msg_didout. The wait_return we do
* here should not overwrite the command that may be shown before
* doing that.
*/
msg_didout = msg_didout_before_start;
wait_return(FALSE);
}
}
#ifdef USE_BROWSE
browse = save_browse;
#endif
#if defined(GUI_DIALOG) || defined(CON_DIALOG)
confirm = save_confirm;
#endif
return retval;
}
#ifdef WANT_EVAL
static void
free_cmdlines(gap)
struct growarray *gap;
{
while (gap->ga_len)
{
vim_free(((char_u **)(gap->ga_data))[gap->ga_len - 1]);
--gap->ga_len;
++gap->ga_room;
}
}
#endif
/*
* Execute one Ex command.
*
* If 'sourcing' is TRUE, the command will be included in the error message.
*
* 2. skip comment lines and leading space
* 3. parse range
* 4. parse command
* 5. parse arguments
* 6. switch on command name
*
* Note: "getline" can be NULL.
*
* This function may be called recursively!
*/
static char_u *
do_one_cmd(cmdlinep, sourcing,
#ifdef WANT_EVAL
cstack,
#endif
getline, cookie)
char_u **cmdlinep;
int sourcing;
#ifdef WANT_EVAL
struct condstack *cstack;
#endif
char_u *(*getline) __ARGS((int, void *, int));
void *cookie; /* argument for getline() */
{
char_u *p;
int i;
linenr_t lnum;
long n;
char_u *errormsg = NULL; /* error message */
EXARG ea; /* Ex command arguments */
vim_memset(&ea, 0, sizeof(ea));
ea.line1 = 1;
ea.line2 = 1;
/* when not editing the last file :q has to be typed twice */
if (quitmore)
--quitmore;
/*
* 2. skip comment lines and leading space and colons
*/
for (ea.cmd = *cmdlinep; *ea.cmd == ' ' || *ea.cmd == '\t'
|| *ea.cmd == ':'; ea.cmd++)
;
/* in ex mode, an empty line works like :+ */
if (*ea.cmd == NUL && exmode_active && getline == getexmodeline)
{
ea.cmd = (char_u *)"+";
ex_pressedreturn = TRUE;
}
if (*ea.cmd == '"' || *ea.cmd == NUL) /* ignore comment and empty lines */
goto doend;
#ifdef WANT_EVAL
ea.skip = did_emsg || (cstack->cs_idx >= 0
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE));
#else
ea.skip = FALSE;
#endif
/*
* 3. parse a range specifier of the form: addr [,addr] [;addr] ..
*
* where 'addr' is:
*
* % (entire file)
* $ [+-NUM]
* 'x [+-NUM] (where x denotes a currently defined mark)
* . [+-NUM]
* [+-NUM]..
* NUM
*
* The ea.cmd pointer is updated to point to the first character following the
* range spec. If an initial address is found, but no second, the upper bound
* is equal to the lower.
*/
/* repeat for all ',' or ';' separated addresses */
for (;;)
{
ea.line1 = ea.line2;
ea.line2 = curwin->w_cursor.lnum; /* default is current line number */
ea.cmd = skipwhite(ea.cmd);
lnum = get_address(&ea.cmd, ea.skip);
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;
++ea.addr_count;
}
/* '*' - visual area */
else if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
{
FPOS *fp;
++ea.cmd;
#ifdef WANT_EVAL
if (!ea.skip)
#endif
{
fp = getmark('<', FALSE);
if (check_mark(fp) == FAIL)
goto doend;
ea.line1 = fp->lnum;
fp = getmark('>', FALSE);
if (check_mark(fp) == FAIL)
goto doend;
ea.line2 = fp->lnum;
++ea.addr_count;
}
}
}
else
ea.line2 = lnum;
ea.addr_count++;
if (*ea.cmd == ';')
{
#ifdef WANT_EVAL
if (!ea.skip)
#endif
curwin->w_cursor.lnum = ea.line2;
}
else if (*ea.cmd != ',')
break;
++ea.cmd;
}
/* One address given: set start and end lines */
if (ea.addr_count == 1)
{
ea.line1 = ea.line2;
/* ... but only implicit: really no address given */
if (lnum == MAXLNUM)
ea.addr_count = 0;
}
/* Don't leave the cursor on an illegal line (caused by ';') */
check_cursor_lnum();
/*
* 4. parse command
*/
/*
* Skip ':' and any white space
*/
ea.cmd = skipwhite(ea.cmd);
while (*ea.cmd == ':')
ea.cmd = skipwhite(ea.cmd + 1);
/*
* 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)
{
/*
* strange vi behaviour:
* ":3" jumps to line 3
* ":3|..." prints line 3
* ":|" prints current line
*/
#ifdef WANT_EVAL
if (ea.skip) /* skip this if inside :if */
goto doend;
#endif
if (*ea.cmd == '|')
{
ea.cmdidx = CMD_print;
ea.argt = RANGE+COUNT+TRLBAR;
if ((errormsg = invalid_range(&ea)) == NULL)
{
correct_range(&ea);
do_print(&ea);
}
}
else if (ea.addr_count != 0)
{
if (ea.line2 == 0)
curwin->w_cursor.lnum = 1;
else if (ea.line2 > curbuf->b_ml.ml_line_count)
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
else
curwin->w_cursor.lnum = ea.line2;
beginline(BL_SOL | BL_FIX);
}
goto doend;
}
/*
* Isolate the command and search for it in the command table.
* Exeptions:
* - 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 is :sim[alt].
*/
if (*ea.cmd == 'k')
{
ea.cmdidx = CMD_k;
p = ea.cmd + 1;
}
else if (ea.cmd[0] == 's'
&& (ea.cmd[1] == 'c'
|| ea.cmd[1] == 'g'
|| (ea.cmd[1] == 'i' && ea.cmd[2] != 'm')
|| ea.cmd[1] == 'I'
|| (ea.cmd[1] == 'r' && ea.cmd[2] != 'e')))
{
ea.cmdidx = CMD_substitute;
p = ea.cmd + 1;
}
else
{
p = ea.cmd;
while (isalpha(*p))
++p;
/* check for non-alpha command */
if (p == ea.cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL)
++p;
i = (int)(p - ea.cmd);
if (*ea.cmd >= 'a' && *ea.cmd <= 'z')
ea.cmdidx = cmdidxs[*ea.cmd - 'a'];
else
ea.cmdidx = cmdidxs[26];
for ( ; (int)ea.cmdidx < (int)CMD_SIZE;
ea.cmdidx = (CMDIDX)((int)ea.cmdidx + 1))
if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, (char *)ea.cmd,
(size_t)i) == 0)
break;
#ifdef USER_COMMANDS
/* Look for a user defined command as a last resort */
if (ea.cmdidx == CMD_SIZE)
{
UCMD *cmd = USER_CMD(0);
int j;
int found = FALSE;
for (j = 0; j < ucmds.ga_len; ++j, ++cmd)
{
if (STRNCMP(cmd->uc_name, (char *)ea.cmd, (size_t)i) == 0)
{
if (found)
{
errormsg = (char_u *)"Ambiguous use of user-defined command";
goto doend;
}
found = TRUE;
ea.cmdidx = CMD_USER;
ea.argt = cmd->uc_argt;
ea.useridx = j;
/* Do not search for further abbreviations
* if this is an exact match
*/
if ((size_t)i == STRLEN(cmd->uc_name))
break;
}
}
}
#endif
if (i == 0 || ea.cmdidx == CMD_SIZE)
{
#ifdef WANT_EVAL
if (!ea.skip)
#endif
{
STRCPY(IObuff, "Not an editor command");
if (!sourcing)
{
STRCAT(IObuff, ": ");
STRNCAT(IObuff, *cmdlinep, 40);
}
errormsg = IObuff;
}
goto doend;
}
}
if (*p == '!' && ea.cmdidx != CMD_substitute) /* forced commands */
{
++p;
ea.forceit = TRUE;
}
else
ea.forceit = FALSE;
/*
* 5. parse arguments
*/
#ifdef USER_COMMANDS
if (ea.cmdidx != CMD_USER)
#endif
ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt;
if (!(ea.argt & RANGE) && ea.addr_count) /* no range allowed */
{
errormsg = e_norange;
goto doend;
}
if (!(ea.argt & BANG) && ea.forceit) /* no <!> allowed */
{
errormsg = e_nobang;
if (ea.cmdidx == CMD_help)
errormsg = (char_u *)"Don't panic!";
goto doend;
}
/*
* If the range is backwards, ask for confirmation and, if given, swap
* ea.line1 & ea.line2 so it's forwards again.
* When global command is busy, don't ask, will fail below.
*/
if (!global_busy && ea.line1 > ea.line2)
{
if (sourcing)
{
errormsg = (char_u *)"Backwards range given";
goto doend;
}
else if (ask_yesno((char_u *)
"Backwards range given, OK to swap", FALSE) != 'y')
goto doend;
lnum = ea.line1;
ea.line1 = ea.line2;
ea.line2 = lnum;
}
/*
* don't complain about the range if it is not used
* (could happen if line_count is accidently set to 0)
*/
if (!ea.skip && (errormsg = invalid_range(&ea)) != NULL)
goto doend;
if ((ea.argt & NOTADR) && ea.addr_count == 0) /* default is 1, not cursor */
ea.line2 = 1;
correct_range(&ea);
#ifdef QUICKFIX
/*
* For the :make command we insert the 'makeprg' option here,
* so things like % get expanded.
*/
if (ea.cmdidx == CMD_make || ea.cmdidx == CMD_grep)
{
char_u *new_cmdline;
char_u *program;
program = (ea.cmdidx == CMD_grep) ? p_gp : p_mp;
new_cmdline = alloc((int)(STRLEN(program) + STRLEN(p) + 2));
if (new_cmdline == NULL)
goto doend; /* out of memory */
STRCPY(new_cmdline, program);
STRCAT(new_cmdline, " ");
STRCAT(new_cmdline, p);
msg_make(p);
/* 'ea.cmd' is not set here, because it is not used at CMD_make */
vim_free(*cmdlinep);
*cmdlinep = new_cmdline;
p = new_cmdline;
}
#endif
/*
* Skip to start of argument.
* Don't do this for the ":!" command, because ":!! -l" needs the space.
*/
if (ea.cmdidx == CMD_bang)
ea.arg = p;
else
ea.arg = skipwhite(p);
if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
{
if (*ea.arg == '>') /* append */
{
if (*++ea.arg != '>') /* typed wrong */
{
errormsg = (char_u *)"Use w or w>>";
goto doend;
}
ea.arg = skipwhite(ea.arg + 1);
ea.append = TRUE;
}
else if (*ea.arg == '!' && ea.cmdidx == CMD_write) /* :w !filter */
{
++ea.arg;
ea.usefilter = TRUE;
}
}
if (ea.cmdidx == CMD_read)
{
if (ea.forceit)
{
ea.usefilter = TRUE; /* :r! filter if ea.forceit */
ea.forceit = FALSE;
}
else if (*ea.arg == '!') /* :r !filter */
{
++ea.arg;
ea.usefilter = TRUE;
}
}
if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
{
ea.amount = 1;
while (*ea.arg == *ea.cmd) /* count number of '>' or '<' */
{
++ea.arg;
++ea.amount;
}
ea.arg = skipwhite(ea.arg);
}
/*
* Check for "+command" argument, before checking for next command.
* Don't do this for ":read !cmd" and ":write !cmd".
*/
if ((ea.argt & EDITCMD) && !ea.usefilter)
ea.do_ecmd_cmd = getargcmd(&ea.arg);
/*
* Check for '|' to separate commands and '"' to start comments.
* Don't do this for ":read !cmd" and ":write !cmd".
*/
if ((ea.argt & TRLBAR) && !ea.usefilter)
separate_nextcmd(&ea);
/*
* Check for <newline> to end a shell command.
* Also do this for ":read !cmd" and ":write !cmd".
*/
else if (ea.cmdidx == CMD_bang || ea.usefilter)
{
for (p = ea.arg; *p; ++p)
{
if (*p == '\\' && p[1])
++p;
else if (*p == '\n')
{
ea.nextcmd = p + 1;
*p = NUL;
break;
}
}
}
if ((ea.argt & DFLALL) && ea.addr_count == 0)
{
ea.line1 = 1;
ea.line2 = curbuf->b_ml.ml_line_count;
}
/* accept numbered register only when no count allowed (:put) */
if ( (ea.argt & REGSTR)
&& *ea.arg != NUL
#ifdef USER_COMMANDS
&& valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put
&& ea.cmdidx != CMD_USER))
/* Do not allow register = for user commands */
&& (ea.cmdidx != CMD_USER || *ea.arg != '=')
#else
&& valid_yank_reg(*ea.arg, ea.cmdidx != CMD_put)
#endif
&& !((ea.argt & COUNT) && isdigit(*ea.arg)))
{
ea.regname = *ea.arg++;
#ifdef WANT_EVAL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -