5296 lines · 125.2 KB · C
RawDownload
/* 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. * See README.txt for an overview of the Vim source code. *//* * search.c: code for normal mode searching commands */ #include "vim.h" static void save_re_pat __ARGS((int idx, char_u *pat, int magic));#ifdef FEAT_EVALstatic int first_submatch __ARGS((regmmatch_T *rp));#endifstatic int check_prevcol __ARGS((char_u *linep, int col, int ch, int *prevcol));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 FEAT_TEXTOBJstatic void back_in_line __ARGS((void));static void find_first_blank __ARGS((pos_T *));static void findsent_forward __ARGS((long count, int at_start_sent));#endif#ifdef FEAT_FIND_IDstatic void show_pat_in_path __ARGS((char_u *, int,					 int, int, FILE *, linenr_T *, long));#endif#ifdef FEAT_VIMINFOstatic void wvsp_one __ARGS((FILE *fp, int idx, char *s, int sc));#endif /* * 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 pos_T 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(FEAT_AUTOCMD) || defined(FEAT_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;# ifdef FEAT_SEARCH_EXTRAstatic int	    saved_no_hlsearch = 0;# endif#endif static char_u	    *mr_pattern = NULL;	/* pattern used by search_regcomp() */#ifdef FEAT_RIGHTLEFTstatic int	    mr_pattern_alloced = FALSE; /* mr_pattern was allocated */static char_u	    *reverse_text __ARGS((char_u *s));#endif #ifdef FEAT_FIND_ID/* * 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 substitute 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 * * returns FAIL if failed, OK otherwise. */    intsearch_regcomp(pat, pat_save, pat_use, options, regmatch)    char_u	*pat;    int		pat_save;    int		pat_use;    int		options;    regmmatch_T	*regmatch;	/* return: pattern and ignore-case flag */{    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 FAIL;	}	pat = spats[i].pat;	magic = spats[i].magic;	no_smartcase = spats[i].no_scs;    }#ifdef FEAT_CMDHIST    else if (options & SEARCH_HIS)	/* put new pattern in history */	add_to_history(HIST_SEARCH, pat, TRUE, NUL);#endif #ifdef FEAT_RIGHTLEFT    if (mr_pattern_alloced)    {	vim_free(mr_pattern);	mr_pattern_alloced = FALSE;    }     if (curwin->w_p_rl && *curwin->w_p_rlc == 's')    {	char_u *rev_pattern; 	rev_pattern = reverse_text(pat);	if (rev_pattern == NULL)	    mr_pattern = pat;	    /* out of memory, keep normal pattern. */	else	{	    mr_pattern = rev_pattern;	    mr_pattern_alloced = TRUE;	}    }    else#endif	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)	    save_re_pat(RE_SEARCH, pat, magic);	/* substitute or global command */	if (pat_save == RE_SUBST || pat_save == RE_BOTH)	    save_re_pat(RE_SUBST, pat, magic);    }     regmatch->rmm_ic = ignorecase(pat);    regmatch->rmm_maxcol = 0;    regmatch->regprog = vim_regcomp(pat, magic ? RE_MAGIC : 0);    if (regmatch->regprog == NULL)	return FAIL;    return OK;} /* * Get search pattern used by search_regcomp(). */    char_u *get_search_pat(){    return mr_pattern;} #ifdef FEAT_RIGHTLEFT/* * Reverse text into allocated memory. * Returns the allocated string, NULL when out of memory. */    static char_u *reverse_text(s)    char_u *s;{    unsigned	len;    unsigned	s_i, rev_i;    char_u	*rev;     /*     * Reverse the pattern.     */    len = (unsigned)STRLEN(s);    rev = alloc(len + 1);    if (rev != NULL)    {	rev_i = len;	for (s_i = 0; s_i < len; ++s_i)	{# ifdef FEAT_MBYTE	    if (has_mbyte)	    {		int	mb_len; 		mb_len = (*mb_ptr2len)(s + s_i);		rev_i -= mb_len;		mch_memmove(rev + rev_i, s + s_i, mb_len);		s_i += mb_len - 1;	    }	    else# endif		rev[--rev_i] = s[s_i]; 	}	rev[len] = NUL;    }    return rev;}#endif     static voidsave_re_pat(idx, pat, magic)    int		idx;    char_u	*pat;    int		magic;{    if (spats[idx].pat != pat)    {	vim_free(spats[idx].pat);	spats[idx].pat = vim_strsave(pat);	spats[idx].magic = magic;	spats[idx].no_scs = no_smartcase;	last_idx = idx;#ifdef FEAT_SEARCH_EXTRA	/* If 'hlsearch' set and search pat changed: need redraw. */	if (p_hls)	    redraw_all_later(SOME_VALID);	no_hlsearch = FALSE;#endif    }} #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)/* * Save the search patterns, so they can be restored later. * Used before/after executing autocommands and user functions. */static int save_level = 0;     voidsave_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;# ifdef FEAT_SEARCH_EXTRA	saved_no_hlsearch = no_hlsearch;# endif    }}     voidrestore_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;# ifdef FEAT_SEARCH_EXTRA	no_hlsearch = saved_no_hlsearch;# endif    }}#endif #if defined(EXITFREE) || defined(PROTO)    voidfree_search_patterns(){    vim_free(spats[0].pat);    vim_free(spats[1].pat);}#endif /* * Return TRUE when case should be ignored for search pattern "pat". * Uses the 'ignorecase' and 'smartcase' options. */    intignorecase(pat)    char_u	*pat;{    char_u	*p;    int		ic;     ic = p_ic;    if (ic && !no_smartcase && p_scs#ifdef FEAT_INS_EXPAND				&& !(ctrl_x_mode && curbuf->b_p_inf)#endif								    )    {	/* don't ignore case if pattern has uppercase */	for (p = pat; *p; )	{#ifdef FEAT_MBYTE	    int		l; 	    if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1)	    {		if (enc_utf8 && utf_isupper(utf_ptr2char(p)))		{		    ic = FALSE;		    break;		}		p += l;	    }	    else#endif		if (*p == '\\' && p[1] != NUL)	/* skip "\S" et al. */		    p += 2;		else if (isupper(*p))		{		    ic = FALSE;		    break;		}		else		    ++p;	}    }    no_smartcase = FALSE;     return ic;}     char_u *last_search_pat(){    return spats[last_idx].pat;} /* * Reset search direction to forward.  For "gd" and "gD" commands. */    voidreset_search_dir(){    spats[0].off.dir = '/';} #if defined(FEAT_EVAL) || defined(FEAT_VIMINFO)/* * Set the last search pattern.  For ":let @/ =" and viminfo. * Also set the saved search pattern, so that this works in an autocommand. */    voidset_last_search_pat(s, idx, magic, setlast)    char_u	*s;    int		idx;    int		magic;    int		setlast;{    vim_free(spats[idx].pat);    /* An empty string means that nothing should be matched. */    if (*s == NUL)	spats[idx].pat = NULL;    else	spats[idx].pat = vim_strsave(s);    spats[idx].magic = magic;    spats[idx].no_scs = FALSE;    spats[idx].off.dir = '/';    spats[idx].off.line = FALSE;    spats[idx].off.end = FALSE;    spats[idx].off.off = 0;    if (setlast)	last_idx = idx;    if (save_level)    {	vim_free(saved_spats[idx].pat);	saved_spats[idx] = spats[0];	if (spats[idx].pat == NULL)	    saved_spats[idx].pat = NULL;	else	    saved_spats[idx].pat = vim_strsave(spats[idx].pat);	saved_last_idx = last_idx;    }# ifdef FEAT_SEARCH_EXTRA    /* If 'hlsearch' set and search pat changed: need redraw. */    if (p_hls && idx == last_idx && !no_hlsearch)	redraw_all_later(SOME_VALID);# endif}#endif #ifdef FEAT_SEARCH_EXTRA/* * Get a regexp program for the last used search pattern. * This is used for highlighting all matches in a window. * Values returned in regmatch->regprog and regmatch->rmm_ic. */    voidlast_pat_prog(regmatch)    regmmatch_T	*regmatch;{    if (spats[last_idx].pat == NULL)    {	regmatch->regprog = NULL;	return;    }    ++emsg_off;		/* So it doesn't beep if bad expr */    (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);    --emsg_off;}#endif /* * lowest level search function. * Search for 'count'th occurrence of pattern 'pat' 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 * if (options & SEARCH_FOLD) match only once in a closed fold * if (options & SEARCH_PEEK) check for typed char, cancel search * * Return FAIL (zero) for failure, non-zero for success. * When FEAT_EVAL is defined, returns the index of the first matching * subpattern plus one; one if there was none. */    intsearchit(win, buf, pos, dir, pat, count, options, pat_use, stop_lnum)    win_T	*win;		/* window to search in; can be NULL for a

Lines 1–500 of 5296.