⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ex_cmds.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
	    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 + -