📄 ex_cmds.c
字号:
which_pat = RE_LAST; /* use last used regexp */
delimiter = *cmd++; /* remember delimiter character */
pat = cmd; /* remember start of search pat */
cmd = skip_regexp(cmd, delimiter, p_magic);
if (cmd[0] == delimiter) /* end delimiter found */
*cmd++ = NUL; /* replace it with a NUL */
}
/*
* Small incompatibility: vi sees '\n' as end of the command, but in
* Vim we want to use '\n' to find/substitute a NUL.
*/
sub = cmd; /* remember the start of the substitution */
while (cmd[0])
{
if (cmd[0] == delimiter) /* end delimiter found */
{
*cmd++ = NUL; /* replace it with a NUL */
break;
}
if (cmd[0] == '\\' && cmd[1] != 0) /* skip escaped characters */
++cmd;
++cmd;
}
if (!eap->skip)
{
vim_free(old_sub);
old_sub = vim_strsave(sub);
}
}
else if (!eap->skip) /* use previous pattern and substitution */
{
if (old_sub == NULL) /* there is no previous command */
{
emsg(e_nopresub);
return;
}
pat = NULL; /* search_regcomp() will use previous pattern */
sub = old_sub;
}
/*
* find trailing options
*/
if (!p_ed)
{
if (p_gd) /* default is global on */
do_all = TRUE;
else
do_all = FALSE;
do_ask = FALSE;
}
while (*cmd)
{
/*
* Note that 'g' and 'c' are always inverted, also when p_ed is off.
* 'r' is never inverted.
*/
if (*cmd == 'g')
do_all = !do_all;
else if (*cmd == 'c')
do_ask = !do_ask;
else if (*cmd == 'e')
do_error = !do_error;
else if (*cmd == 'r') /* use last used regexp */
which_pat = RE_LAST;
else if (*cmd == 'p')
do_print = TRUE;
else if (*cmd == 'i') /* ignore case */
do_ic = 'i';
else if (*cmd == 'I') /* don't ignore case */
do_ic = 'I';
else
break;
++cmd;
}
/*
* check for a trailing count
*/
cmd = skipwhite(cmd);
if (isdigit(*cmd))
{
i = getdigits(&cmd);
if (i <= 0 && !eap->skip && do_error)
{
emsg(e_zerocount);
return;
}
eap->line1 = eap->line2;
eap->line2 += i - 1;
}
/*
* check for trailing command or garbage
*/
cmd = skipwhite(cmd);
if (*cmd && *cmd != '\"') /* if not end-of-line or comment */
{
eap->nextcmd = check_nextcmd(cmd);
if (eap->nextcmd == NULL)
{
emsg(e_trailing);
return;
}
}
if (eap->skip) /* not executing commands, only parsing */
return;
if ((prog = search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS)) == NULL)
{
if (do_error)
emsg(e_invcmd);
return;
}
/* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */
if (do_ic == 'i')
reg_ic = TRUE;
else if (do_ic == 'I')
reg_ic = FALSE;
/*
* ~ in the substitute pattern is replaced with the old pattern.
* We do it here once to avoid it to be replaced over and over again.
*/
sub = regtilde(sub, p_magic);
old_line = NULL;
for (lnum = eap->line1; lnum <= eap->line2 && !(got_int || got_quit);
++lnum)
{
ptr = ml_get(lnum);
if (vim_regexec(prog, ptr, TRUE)) /* a match on this line */
{
char_u *new_end, *new_start = NULL;
char_u *old_match, *old_copy;
char_u *prev_old_match = NULL;
char_u *p1;
int did_sub = FALSE;
int match, lastone;
unsigned len, needed_len;
unsigned new_start_len = 0;
/* make a copy of the line, so it won't be taken away when updating
the screen */
if ((old_line = vim_strsave(ptr)) == NULL)
continue;
vim_regexec(prog, old_line, TRUE); /* match again on this line to
* update the pointers. TODO:
* remove extra vim_regexec() */
if (!got_match)
{
setpcmark();
got_match = TRUE;
}
old_copy = old_match = old_line;
for (;;) /* loop until nothing more to replace */
{
/*
* Save the position of the last change for the final cursor
* position (just like the real vi).
*/
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = (int)(prog->startp[0] - old_line);
changed_cline_bef_curs();
/*
* Match empty string does not count, except for first match.
* This reproduces the strange vi behaviour.
* This also catches endless loops.
*/
if (old_match == prev_old_match && old_match == prog->endp[0])
{
++old_match;
goto skip;
}
old_match = prog->endp[0];
prev_old_match = old_match;
/* update_screen() may change reg_ic: save it */
save_reg_ic = reg_ic;
/* change State to CONFIRM, so that the mouse works properly */
save_State = State;
State = CONFIRM;
#ifdef USE_MOUSE
setmouse(); /* disable mouse in xterm */
#endif
/*
* Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
*/
while (do_ask)
{
temp = RedrawingDisabled;
RedrawingDisabled = FALSE;
search_match_len = prog->endp[0] - prog->startp[0];
/* invert the matched string
* remove the inversion afterwards */
if (search_match_len == 0)
search_match_len = 1; /* show something! */
highlight_match = TRUE;
update_topline();
validate_cursor();
update_screen(NOT_VALID);
highlight_match = FALSE;
redraw_later(NOT_VALID);
if (msg_row == Rows - 1)
msg_didout = FALSE; /* avoid a scroll-up */
/* write message same highlighting as for wait_return */
smsg_attr(hl_attr(HLF_R),
(char_u *)"replace with %s (y/n/a/q/^E/^Y)?",
sub);
showruler(TRUE);
RedrawingDisabled = temp;
#ifdef USE_GUI_WIN32
dont_scroll = FALSE; /* allow scrolling here */
#endif
++no_mapping; /* don't map this key */
++allow_keys; /* allow special keys */
i = vgetc();
--allow_keys;
--no_mapping;
/* clear the question */
msg_didout = FALSE; /* don't scroll up */
msg_col = 0;
gotocmdline(TRUE);
if (i == 'q' || i == ESC || i == Ctrl('C')
#ifdef UNIX
|| i == intr_char
#endif
)
{
got_quit = TRUE;
break;
}
else if (i == 'n')
goto skip;
else if (i == 'y')
break;
else if (i == 'a')
{
do_ask = FALSE;
break;
}
else if (i == Ctrl('E'))
scrollup_clamp();
else if (i == Ctrl('Y'))
scrolldown_clamp();
}
if (got_quit)
break;
reg_ic = save_reg_ic;
State = save_State;
#ifdef USE_MOUSE
setmouse();
#endif
/* get length of substitution part */
sublen = vim_regsub(prog, sub, old_line, FALSE, p_magic);
if (new_start == NULL)
{
/*
* Get some space for a temporary buffer to do the
* substitution into (and some extra space to avoid
* too many calls to alloc()/free()).
*/
new_start_len = STRLEN(old_copy) + sublen + 25;
if ((new_start = alloc_check(new_start_len)) == NULL)
goto outofmem;
*new_start = NUL;
new_end = new_start;
}
else
{
/*
* Extend the temporary buffer to do the substitution into.
* Avoid an alloc()/free(), it takes a lot of time.
*/
len = STRLEN(new_start);
needed_len = len + STRLEN(old_copy) + sublen + 1;
if (needed_len > new_start_len)
{
needed_len += 20; /* get some extra */
if ((p1 = alloc_check(needed_len)) == NULL)
goto outofmem;
STRCPY(p1, new_start);
vim_free(new_start);
new_start = p1;
new_start_len = needed_len;
}
new_end = new_start + len;
}
/*
* copy the text up to the part that matched
*/
i = prog->startp[0] - old_copy;
mch_memmove(new_end, old_copy, (size_t)i);
new_end += i;
vim_regsub(prog, sub, new_end, TRUE, p_magic);
sub_nsubs++;
did_sub = TRUE;
/*
* Now the trick is to replace CTRL-Ms with a real line break.
* This would make it impossible to insert CTRL-Ms in the text.
* That is the way vi works. In Vim the line break can be
* avoided by preceding the CTRL-M with a CTRL-V. Now you can't
* precede a line break with a CTRL-V, big deal.
*/
while ((p1 = vim_strchr(new_end, CR)) != NULL)
{
if (p1 == new_end || p1[-1] != Ctrl('V'))
{
if (u_inssub(lnum) == OK) /* prepare for undo */
{
*p1 = NUL; /* truncate up to the CR */
mark_adjust(lnum, (linenr_t)MAXLNUM, 1L, 0L);
ml_append(lnum - 1, new_start,
(colnr_t)(p1 - new_start + 1), FALSE);
++lnum;
++eap->line2; /* number of lines increases */
STRCPY(new_start, p1 + 1); /* copy the rest */
new_end = new_start;
}
}
else /* remove CTRL-V */
{
STRCPY(p1 - 1, p1);
new_end = p1;
}
}
/* remember next character to be copied */
old_copy = prog->endp[0];
/*
* continue searching after the match
* prevent endless loop with patterns that match empty strings,
* e.g. :s/$/pat/g or :s/[a-z]* /(&)/g
*/
skip:
match = -1;
lastone = (*old_match == NUL || got_int || got_quit || !do_all);
if (lastone || do_ask ||
(match = vim_regexec(prog, old_match, (int)FALSE)) == 0)
{
if (new_start)
{
/*
* Copy the rest of the line, that didn't match.
* Old_match has to be adjusted, we use the end of the
* line as reference, because the substitute may have
* changed the number of characters.
*/
STRCAT(new_start, old_copy);
i = old_line + STRLEN(old_line) - old_match;
if (u_savesub(lnum) == OK)
ml_replace(lnum, new_start, TRUE);
/* When asking, undo is saved each time, must also set
* changed flag each time. */
if (do_ask)
changed();
#ifdef SYNTAX_HL
/* recompute syntax hl. for this line */
syn_changed(lnum);
#endif
vim_free(old_line); /* free the temp buffer */
old_line = new_start;
new_start = NULL;
old_match = old_line + STRLEN(old_line) - i;
if (old_match < old_line) /* safety check */
{
EMSG("do_sub internal error: old_match < old_line");
old_match = old_line;
}
old_copy = old_line;
}
if (match == -1 && !lastone)
match = vim_regexec(prog, old_match, (int)FALSE);
if (match <= 0) /* quit loop if there is no more match */
break;
}
line_breakcheck();
}
if (did_sub)
++sub_nlines;
vim_free(old_line); /* free the copy of the original line */
old_line = NULL;
}
line_breakcheck();
}
curbuf->b_op_start.lnum = eap->line1;
curbuf->b_op_end.lnum = eap->line2;
curbuf->b_op_start.col = curbuf->b_op_end.col = 0;
outofmem:
vim_free(old_line); /* may have to free an allocated copy of the line */
if (sub_nsubs)
{
changed();
approximate_botline();
if (!global_busy)
{
update_topline();
beginline(BL_WHITE | BL_FIX);
update_screen(NOT_VALID); /* need this to update LineSizes */
if (!do_sub_msg() && do_ask)
MSG("");
}
if (do_print)
print_line(curwin->w_cursor.lnum, FALSE);
}
else if (!global_busy)
{
if (got_int) /* interrupted */
emsg(e_interr);
else if (got_match) /* did find something but nothing substituted */
MSG("");
else if (do_error) /* nothing found */
emsg2(e_patnotf2, pat);
}
vim_free(prog);
}
/*
* Give message for number of substitutions.
* Can also be used after a ":global" command.
* Return TRUE if a message was given.
*/
static int
do_sub_msg()
{
/*
* Only report substitutions when:
* - more than 'report' substitutions
* - command was typed by user, or number of changed lines > 'report'
* - giving messages is not disabled by 'lazyredraw'
*/
if (sub_nsubs > p_report &&
(KeyTyped || sub_nlines > 1 || p_report < 1) &&
messaging())
{
sprintf((char *)msg_buf, "%s%ld substitution%s on %ld line%s",
got_int ? "(Interrupted) " : "",
sub_nsubs, plural(sub_nsubs),
(long)sub_nlines, plural((long)sub_nlines));
if (msg(msg_buf))
{
keep_msg = msg_buf;
keep_msg_attr = 0;
}
return TRUE;
}
if (got_int)
{
emsg(e_interr);
return TRUE;
}
return FALSE;
}
/*
* do_glob(cmd)
*
* Execute a global command of the form:
*
* g/pattern/X : execute X on all lines where pattern matches
* v/pattern/X : execute X on all lines where pattern does not match
*
* where 'X' is an EX command
*
* The command character (as well as the trailing slash) is optional, and
* is assumed to be 'p' if missing.
*
* This is implemented in two passes: first we scan the file for the pattern and
* set a mark for each line that (not) matches. secondly we execute the command
* for each line that has a mark. This is required because after deleting
* lines we do not know where to search for the next match.
*/
void
do_glob(eap)
EXARG *eap;
{
linenr_t lnum; /* line number according to old situation */
linenr_t old_lcount; /* b_ml.ml_line_count before the command */
int ndone;
int type; /* first char of cmd: 'v' or 'g' */
char_u *cmd; /* command argument */
char_u delim; /* delimiter, normally '/' */
char_u *pat;
vim_regexp *prog;
int match;
int which_pat;
if (global_busy)
{
EMSG("Cannot do :global recursive"); /* will increment global_busy */
return;
}
type = *eap->cmd;
cmd = eap->arg;
which_pat = RE_LAST; /* default: use last use
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -