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

📄 search.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved	by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */
/*
 * search.c: code for normal mode searching commands
 */

#include "vim.h"

static int inmacro __ARGS((char_u *, char_u *));
static int check_linecomment __ARGS((char_u *line));
static int cls __ARGS((void));
static int skip_chars __ARGS((int, int));
#ifdef TEXT_OBJECTS
static void back_in_line __ARGS((void));
static void find_first_blank __ARGS((FPOS *));
static void findsent_forward __ARGS((long count, int at_start_sent));
#endif
#ifdef FIND_IN_PATH
static void show_pat_in_path __ARGS((char_u *, int,
					 int, int, FILE *, linenr_t *, long));
#endif
#ifdef VIMINFO
static void wvsp_one __ARGS((FILE *fp, int idx, char *s, char *sc));
#endif

static char_u *top_bot_msg = (char_u *)"search hit TOP, continuing at BOTTOM";
static char_u *bot_top_msg = (char_u *)"search hit BOTTOM, continuing at TOP";

/*
 * This file contains various searching-related routines. These fall into
 * three groups:
 * 1. string searches (for /, ?, n, and N)
 * 2. character searches within a single line (for f, F, t, T, etc)
 * 3. "other" kinds of searches like the '%' command, and 'word' searches.
 */

/*
 * String searches
 *
 * The string search functions are divided into two levels:
 * lowest:  searchit(); uses an FPOS for starting position and found match.
 * Highest: do_search(); uses curwin->w_cursor; calls searchit().
 *
 * The last search pattern is remembered for repeating the same search.
 * This pattern is shared between the :g, :s, ? and / commands.
 * This is in search_regcomp().
 *
 * The actual string matching is done using a heavily modified version of
 * Henry Spencer's regular expression library.  See regexp.c.
 */

/* The offset for a search command is store in a soff struct */
/* Note: only spats[0].off is really used */
struct soffset
{
    int		dir;		/* search direction */
    int		line;		/* search has line offset */
    int		end;		/* search set cursor at end */
    long	off;		/* line or char offset */
};

/* A search pattern and its attributes are stored in a spat struct */
struct spat
{
    char_u	    *pat;	/* the pattern (in allocated memory) or NULL */
    int		    magic;	/* magicness of the pattern */
    int		    no_scs;	/* no smarcase for this pattern */
    struct soffset  off;
};

/*
 * Two search patterns are remembered: One for the :substitute command and
 * one for other searches.  last_idx points to the one that was used the last
 * time.
 */
static struct spat spats[2] =
{
    {NULL, TRUE, FALSE, {'/', 0, 0, 0L}},	/* last used search pat */
    {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}	/* last used substitute pat */
};

static int last_idx = 0;	/* index in spats[] for RE_LAST */

#if defined(AUTOCMD) || defined(WANT_EVAL) || defined(PROTO)
/* copy of spats[], for keeping the search patterns while executing autocmds */
static struct spat  saved_spats[2];
static int	    saved_last_idx = 0;
#endif

static char_u	    *mr_pattern = NULL;	/* pattern used by search_regcomp() */

#ifdef FIND_IN_PATH
/*
 * Type used by find_pattern_in_path() to remember which included files have
 * been searched already.
 */
typedef struct SearchedFile
{
    FILE	*fp;		/* File pointer */
    char_u	*name;		/* Full name of file */
    linenr_t	lnum;		/* Line we were up to in file */
    int		matched;	/* Found a match in this file */
} SearchedFile;
#endif

/*
 * translate search pattern for vim_regcomp()
 *
 * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd)
 * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command)
 * pat_save == RE_BOTH: save pat in both patterns (:global command)
 * pat_use  == RE_SEARCH: use previous search pattern if "pat" is NULL
 * pat_use  == RE_SUBST: use previous sustitute pattern if "pat" is NULL
 * pat_use  == RE_LAST: use last used pattern if "pat" is NULL
 * options & SEARCH_HIS: put search string in history
 * options & SEARCH_KEEP: keep previous search pattern
 *
 */
    vim_regexp *
search_regcomp(pat, pat_save, pat_use, options)
    char_u  *pat;
    int	    pat_save;
    int	    pat_use;
    int	    options;
{
    int	    magic;
    int	    i;

    rc_did_emsg = FALSE;
    magic = p_magic;

    /*
     * If no pattern given, use a previously defined pattern.
     */
    if (pat == NULL || *pat == NUL)
    {
	if (pat_use == RE_LAST)
	    i = last_idx;
	else
	    i = pat_use;
	if (spats[i].pat == NULL)	/* pattern was never defined */
	{
	    if (pat_use == RE_SUBST)
		emsg(e_nopresub);
	    else
		emsg(e_noprevre);
	    rc_did_emsg = TRUE;
	    return (vim_regexp *)NULL;
	}
	pat = spats[i].pat;
	magic = spats[i].magic;
	no_smartcase = spats[i].no_scs;
    }
    else if (options & SEARCH_HIS)	/* put new pattern in history */
	add_to_history(HIST_SEARCH, pat);

    mr_pattern = pat;

    /*
     * Save the currently used pattern in the appropriate place,
     * unless the pattern should not be remembered.
     */
    if (!(options & SEARCH_KEEP))
    {
	/*
	 * search or global command
	 */
	if (pat_save == RE_SEARCH || pat_save == RE_BOTH)
	{
	    if (spats[RE_SEARCH].pat != pat)
	    {
		vim_free(spats[RE_SEARCH].pat);
		spats[RE_SEARCH].pat = vim_strsave(pat);
		spats[RE_SEARCH].magic = magic;
		spats[RE_SEARCH].no_scs = no_smartcase;
		last_idx = RE_SEARCH;
		/* If 'hlsearch' set and search pat changed: need redraw. */
#ifdef EXTRA_SEARCH
		if (p_hls)
		    redraw_all_later(NOT_VALID);
		no_hlsearch = FALSE;
#endif
	    }
	}
	/*
	 * substitute or global command
	 */
	if (pat_save == RE_SUBST || pat_save == RE_BOTH)
	{
	    if (spats[RE_SUBST].pat != pat)
	    {
		vim_free(spats[RE_SUBST].pat);
		spats[RE_SUBST].pat = vim_strsave(pat);
		spats[RE_SUBST].magic = magic;
		spats[RE_SUBST].no_scs = no_smartcase;
		last_idx = RE_SUBST;
		/* If 'hlsearch' set and search pat changed: need redraw. */
#ifdef EXTRA_SEARCH
		if (p_hls)
		    redraw_all_later(NOT_VALID);
		no_hlsearch = FALSE;
#endif
	    }
	}
    }

    set_reg_ic(pat);		/* tell the vim_regexec routine how to search */
    return vim_regcomp(pat, magic);
}

#if defined(AUTOCMD) || defined(WANT_EVAL) || defined(PROTO)
/*
 * Save the search patterns, so they can be restored later.
 * Used before/after executing autocommands.
 */
static int save_level = 0;

    void
save_search_patterns()
{
    if (save_level++ == 0)
    {
	saved_spats[0] = spats[0];
	if (spats[0].pat != NULL)
	    saved_spats[0].pat = vim_strsave(spats[0].pat);
	saved_spats[1] = spats[1];
	if (spats[1].pat != NULL)
	    saved_spats[1].pat = vim_strsave(spats[1].pat);
	saved_last_idx = last_idx;
    }
}

    void
restore_search_patterns()
{
    if (--save_level == 0)
    {
	vim_free(spats[0].pat);
	spats[0] = saved_spats[0];
	vim_free(spats[1].pat);
	spats[1] = saved_spats[1];
	last_idx = saved_last_idx;
    }
}
#endif

/*
 * Set reg_ic according to p_ic, p_scs and the search pattern.
 */
    void
set_reg_ic(pat)
    char_u  *pat;
{
    char_u *p;

    reg_ic = p_ic;
    if (reg_ic && !no_smartcase && p_scs
#ifdef INSERT_EXPAND
				&& !(ctrl_x_mode && curbuf->b_p_inf)
#endif
								    )
    {
	/* don't ignore case if pattern has uppercase */
	for (p = pat; *p; )
	    if (isupper(*p++))
		reg_ic = FALSE;
    }
    no_smartcase = FALSE;
}

#ifdef EXTRA_SEARCH
/*
 * Get a regexp program for the last used search pattern.
 * This is used for highlighting all matches in a window.
 */
    vim_regexp *
last_pat_prog()
{
    vim_regexp *prog;

    if (spats[last_idx].pat == NULL)
	return NULL;
    emsg_off = TRUE;	/* So it doesn't beep if bad expr */
    prog = search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP);
    emsg_off = FALSE;
    return prog;
}
#endif

/*
 * lowest level search function.
 * Search for 'count'th occurrence of 'str' in direction 'dir'.
 * Start at position 'pos' and return the found position in 'pos'.
 *
 * if (options & SEARCH_MSG) == 0 don't give any messages
 * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
 * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
 * if (options & SEARCH_HIS) put search pattern in history
 * if (options & SEARCH_END) return position at end of match
 * if (options & SEARCH_START) accept match at pos itself
 * if (options & SEARCH_KEEP) keep previous search pattern
 *
 * Return OK for success, FAIL for failure.
 */
    int
searchit(buf, pos, dir, str, count, options, pat_use)
    BUF	    *buf;
    FPOS    *pos;
    int	    dir;
    char_u  *str;
    long    count;
    int	    options;
    int	    pat_use;
{
    int			found;
    linenr_t		lnum;		/* no init to shut up Apollo cc */
    vim_regexp		*prog;
    char_u		*ptr;
    char_u		*match = NULL, *matchend = NULL;    /* init for GCC */
    int			loop;
    FPOS		start_pos;
    int			at_first_line;
    int			extra_col;
    int			match_ok;
    char_u		*p;

    if ((prog = search_regcomp(str, RE_SEARCH, pat_use,
			     (options & (SEARCH_HIS + SEARCH_KEEP)))) == NULL)
    {
	if ((options & SEARCH_MSG) && !rc_did_emsg)
	    emsg2((char_u *)"Invalid search string: %s", mr_pattern);
	return FAIL;
    }

    if (options & SEARCH_START)
	extra_col = 0;
    else
	extra_col = 1;


/*
 * find the string
 */
    do	/* loop for count */
    {
	start_pos = *pos;	/* remember start pos for detecting no match */
	found = 0;		/* default: not found */
	at_first_line = TRUE;	/* default: start in first line */
	if (pos->lnum == 0)	/* correct lnum for when starting in line 0 */
	{
	    pos->lnum = 1;
	    pos->col = 0;
	    at_first_line = FALSE;  /* not in first line now */
	}

	/*
	 * Start searching in current line, unless searching backwards and
	 * we're in column 0.
	 */
	if (dir == BACKWARD && start_pos.col == 0)
	{
	    lnum = pos->lnum - 1;
	    at_first_line = FALSE;
	}
	else
	    lnum = pos->lnum;

	for (loop = 0; loop <= 1; ++loop)   /* loop twice if 'wrapscan' set */
	{
	    for ( ; lnum > 0 && lnum <= buf->b_ml.ml_line_count;
					   lnum += dir, at_first_line = FALSE)
	    {
		/*
		 * Look for a match somewhere in the line.
		 */
		ptr = ml_get_buf(buf, lnum, FALSE);
		if (vim_regexec(prog, ptr, TRUE))
		{
		    match = prog->startp[0];
		    matchend = prog->endp[0];

		    /*
		     * Forward search in the first line: match should be after
		     * the start position. If not, continue at the end of the
		     * match (this is vi compatible).
		     */
		    if (dir == FORWARD && at_first_line)
		    {
			match_ok = TRUE;
			/*
			 * When *match == NUL the cursor will be put one back
			 * afterwards, compare with that position, otherwise
			 * "/$" will get stuck on end of line.
			 */
			while ((options & SEARCH_END) ?
						 ((int)(matchend - ptr) - 1  <
					     (int)start_pos.col + extra_col) :
				  ((int)(match - ptr) - (int)(*match == NUL) <
					      (int)start_pos.col + extra_col))
			{
			    /*
			     * If vi-compatible searching, continue at the end
			     * of the match, otherwise continue one position
			     * forward.
			     */
			    if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
			    {
				p = matchend;
				if (match == p && *p != NUL)
				    ++p;
			    }
			    else
			    {
				p = match;
				if (*p != NUL)
				    ++p;
			    }
			    if (*p != NUL && vim_regexec(prog, p, FALSE))
			    {
				match = prog->startp[0];
				matchend = prog->endp[0];
			    }
			    else
			    {
				match_ok = FALSE;
				break;
			    }
			}
			if (!match_ok)
			    continue;
		    }
		    if (dir == BACKWARD)
		    {
			/*
			 * Now, if there are multiple matches on this line,
			 * we have to get the last one. Or the last one before
			 * the cursor, if we're on that line.
			 * When putting the new cursor at the end, compare
			 * relative to the end of the match.
			 */
			match_ok = FALSE;
			for (;;)
			{
			    if (!at_first_line || ((options & SEARCH_END) ?
					((prog->endp[0] - ptr) - 1 + extra_col
						      <= (int)start_pos.col) :
					  ((prog->startp[0] - ptr) + extra_col
						      <= (int)start_pos.col)))
			    {
				match_ok = TRUE;
				match = prog->startp[0];
				matchend = prog->endp[0];
			    }
			    else
				break;
			    /*
			     * If vi-compatible searching, continue at the end
			     * of the match, otherwise continue one position
			     * forward.
			     */
			    if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
			    {
				p = matchend;
				if (p == match && *p != NUL)
				    ++p;
			    }
			    else
			    {
				p = match;
				if (*p != NUL)
				    ++p;
			    }
			    if (*p == NUL || !vim_regexec(prog, p, (int)FALSE))
				break;
			}

			/*
			 * If there is only a match after the cursor, skip
			 * this match.
			 */
			if (!match_ok)
			    continue;
		    }

		    pos->lnum = lnum;
		    if (options & SEARCH_END && !(options & SEARCH_NOOF))
			pos->col = (int) (matchend - ptr - 1);
		    else
			pos->col = (int) (match - ptr);
		    found = 1;
		    break;
		}
		line_breakcheck();	/* stop if ctrl-C typed */
		if (got_int)
		    break;

		if (loop && lnum == start_pos.lnum)
		    break;	    /* if second loop, stop where started */
	    }
	    at_first_line = FALSE;

	    /*
	     * stop the search if wrapscan isn't set, after an interrupt and
	     * after a match
	     */
	    if (!p_ws || got_int || found)
		break;

	    /*
	     * If 'wrapscan' is set we continue at the other end of the file.
	     * If 'shortmess' does not contain 's', we give a message.
	     * This message is also remembered in keep_msg for when the screen
	     * is redrawn. The keep_msg is cleared whenever another message is
	     * written.
	     */
	    if (dir == BACKWARD)    /* start second loop at the other end */
	    {
		lnum = buf->b_ml.ml_line_count;
		if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
		    give_warning(top_bot_msg, TRUE);
	    }
	    else

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -