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

📄 search.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
 * flags: FM_BACKWARD	search backwards (when initc is '/', '*' or '#')
 *	  FM_FORWARD	search forwards (when initc is '/', '*' or '#')
 *	  FM_BLOCKSTOP	stop at start/end of block ({ or } in column 0)
 *	  FM_SKIPCOMM	skip comments (not implemented yet!)
 */

    FPOS *
findmatchlimit(oap, initc, flags, maxtravel)
    OPARG   *oap;
    int	    initc;
    int	    flags;
    int	    maxtravel;
{
    static FPOS	    pos;		/* current search position */
    int		    findc = 0;		/* matching brace */
    int		    c;
    int		    count = 0;		/* cumulative number of braces */
    int		    backwards = FALSE;	/* init for gcc */
    int		    inquote = FALSE;	/* TRUE when inside quotes */
    char_u	    *linep;		/* pointer to current line */
    char_u	    *ptr;
    int		    do_quotes;		/* check for quotes in current line */
    int		    at_start;		/* do_quotes value at start position */
    int		    hash_dir = 0;	/* Direction searched for # things */
    int		    comment_dir = 0;	/* Direction searched for comments */
    FPOS	    match_pos;		/* Where last slash-star was found */
    int		    start_in_quotes;	/* start position is in quotes */
    int		    traveled = 0;	/* how far we've searched so far */
    int		    ignore_cend = FALSE;    /* ignore comment end */
    int		    cpo_match;		/* vi compatible matching */
    int		    dir;		/* Direction to search */
    int		    comment_col = MAXCOL;   /* start of / / comment */

    pos = curwin->w_cursor;
    linep = ml_get(pos.lnum);

    cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);

    /* Direction to search when initc is '/', '*' or '#' */
    if (flags & FM_BACKWARD)
	dir = BACKWARD;
    else if (flags & FM_FORWARD)
	dir = FORWARD;
    else
	dir = 0;

    /*
     * if initc given, look in the table for the matching character
     * '/' and '*' are special cases: look for start or end of comment.
     * When '/' is used, we ignore running backwards into an star-slash, for
     * "[*" command, we just want to find any comment.
     */
    if (initc == '/' || initc == '*')
    {
	comment_dir = dir;
	if (initc == '/')
	    ignore_cend = TRUE;
	backwards = (dir == FORWARD) ? FALSE : TRUE;
	initc = NUL;
    }
    else if (initc != '#' && initc != NUL)
    {
	/* 'matchpairs' is "x:y,x:y" */
	for (ptr = curbuf->b_p_mps; *ptr; ptr += 2)
	{
	    if (*ptr == initc)
	    {
		findc = initc;
		initc = ptr[2];
		backwards = TRUE;
		break;
	    }
	    ptr += 2;
	    if (*ptr == initc)
	    {
		findc = initc;
		initc = ptr[-2];
		backwards = FALSE;
		break;
	    }
	}
	if (!findc)		/* invalid initc! */
	    return NULL;
    }
    /*
     * Either initc is '#', or no initc was given and we need to look under the
     * cursor.
     */
    else
    {
	if (initc == '#')
	{
	    hash_dir = dir;
	}
	else
	{
	    /*
	     * initc was not given, must look for something to match under
	     * or near the cursor.
	     * Only check for special things when 'cpo' doesn't have '%'.
	     */
	    if (!cpo_match)
	    {
		/* Are we before or at #if, #else etc.? */
		ptr = skipwhite(linep);
		if (*ptr == '#' && pos.col <= (colnr_t)(ptr - linep))
		{
		    ptr = skipwhite(ptr + 1);
		    if (   STRNCMP(ptr, "if", 2) == 0
			|| STRNCMP(ptr, "endif", 5) == 0
			|| STRNCMP(ptr, "el", 2) == 0)
			hash_dir = 1;
		}

		/* Are we on a comment? */
		else if (linep[pos.col] == '/')
		{
		    if (linep[pos.col + 1] == '*')
		    {
			comment_dir = FORWARD;
			backwards = FALSE;
			pos.col++;
		    }
		    else if (pos.col > 0 && linep[pos.col - 1] == '*')
		    {
			comment_dir = BACKWARD;
			backwards = TRUE;
			pos.col--;
		    }
		}
		else if (linep[pos.col] == '*')
		{
		    if (linep[pos.col + 1] == '/')
		    {
			comment_dir = BACKWARD;
			backwards = TRUE;
		    }
		    else if (pos.col > 0 && linep[pos.col - 1] == '/')
		    {
			comment_dir = FORWARD;
			backwards = FALSE;
		    }
		}
	    }

	    /*
	     * If we are not on a comment or the # at the start of a line, then
	     * look for brace anywhere on this line after the cursor.
	     */
	    if (!hash_dir && !comment_dir)
	    {
		/*
		 * Find the brace under or after the cursor.
		 * If beyond the end of the line, use the last character in
		 * the line.
		 */
		if (linep[pos.col] == NUL && pos.col)
		    --pos.col;
		for (;;)
		{
		    initc = linep[pos.col];
		    if (initc == NUL)
			break;

		    for (ptr = curbuf->b_p_mps; *ptr; ++ptr)
		    {
			if (*ptr == initc)
			{
			    findc = ptr[2];
			    backwards = FALSE;
			    break;
			}
			ptr += 2;
			if (*ptr == initc)
			{
			    findc = ptr[-2];
			    backwards = TRUE;
			    break;
			}
			if (!*++ptr)
			    break;
		    }
		    if (findc)
			break;
		    ++pos.col;
		}
		if (!findc)
		{
		    /* no brace in the line, maybe use "  #if" then */
		    if (!cpo_match && *skipwhite(linep) == '#')
			hash_dir = 1;
		    else
			return NULL;
		}
	    }
	}
	if (hash_dir)
	{
	    /*
	     * Look for matching #if, #else, #elif, or #endif
	     */
	    if (oap != NULL)
		oap->motion_type = MLINE;   /* Linewise for this case only */
	    if (initc != '#')
	    {
		ptr = skipwhite(skipwhite(linep) + 1);
		if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0)
		    hash_dir = 1;
		else if (STRNCMP(ptr, "endif", 5) == 0)
		    hash_dir = -1;
		else
		    return NULL;
	    }
	    pos.col = 0;
	    while (!got_int)
	    {
		if (hash_dir > 0)
		{
		    if (pos.lnum == curbuf->b_ml.ml_line_count)
			break;
		}
		else if (pos.lnum == 1)
		    break;
		pos.lnum += hash_dir;
		linep = ml_get(pos.lnum);
		line_breakcheck();	/* check for CTRL-C typed */
		ptr = skipwhite(linep);
		if (*ptr != '#')
		    continue;
		pos.col = ptr - linep;
		ptr = skipwhite(ptr + 1);
		if (hash_dir > 0)
		{
		    if (STRNCMP(ptr, "if", 2) == 0)
			count++;
		    else if (STRNCMP(ptr, "el", 2) == 0)
		    {
			if (count == 0)
			    return &pos;
		    }
		    else if (STRNCMP(ptr, "endif", 5) == 0)
		    {
			if (count == 0)
			    return &pos;
			count--;
		    }
		}
		else
		{
		    if (STRNCMP(ptr, "if", 2) == 0)
		    {
			if (count == 0)
			    return &pos;
			count--;
		    }
		    else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0)
		    {
			if (count == 0)
			    return &pos;
		    }
		    else if (STRNCMP(ptr, "endif", 5) == 0)
			count++;
		}
	    }
	    return NULL;
	}
    }

#ifdef RIGHTLEFT
    if (curwin->w_p_rl)
	backwards = !backwards;
#endif

    do_quotes = -1;
    start_in_quotes = MAYBE;
    /* backward search: Check if this line contains a single-line comment */
    if (backwards && comment_dir)
	comment_col = check_linecomment(linep);
    while (!got_int)
    {
	/*
	 * Go to the next position, forward or backward. We could use
	 * inc() and dec() here, but that is much slower
	 */
	if (backwards)
	{
	    if (pos.col == 0)		/* at start of line, go to prev. one */
	    {
		if (pos.lnum == 1)	/* start of file */
		    break;
		--pos.lnum;

		if (maxtravel && traveled++ > maxtravel)
		    break;

		linep = ml_get(pos.lnum);
		pos.col = STRLEN(linep);    /* put pos.col on trailing NUL */
		do_quotes = -1;
		line_breakcheck();

		/* Check if this line contains a single-line comment */
		if (comment_dir)
		    comment_col = check_linecomment(linep);
	    }
	    else
		--pos.col;
	}
	else				/* forward search */
	{
	    if (linep[pos.col] == NUL)	/* at end of line, go to next one */
	    {
		if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
		    break;
		++pos.lnum;

		if (maxtravel && traveled++ > maxtravel)
		    break;

		linep = ml_get(pos.lnum);
		pos.col = 0;
		do_quotes = -1;
		line_breakcheck();
	    }
	    else
		++pos.col;
	}

	/*
	 * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
	 */
	if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
					 (linep[0] == '{' || linep[0] == '}'))
	{
	    if (linep[0] == findc && count == 0)	/* match! */
		return &pos;
	    break;					/* out of scope */
	}

	if (comment_dir)
	{
	    /* Note: comments do not nest, and we ignore quotes in them */
	    /* TODO: ignore comment brackets inside strings */
	    if (comment_dir == FORWARD)
	    {
		if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
		{
		    pos.col++;
		    return &pos;
		}
	    }
	    else    /* Searching backwards */
	    {
		/*
		 * A comment may contain / * or / /, it may also start or end
		 * with / * /.	Ignore a / * after / /.
		 */
		if (pos.col == 0)
		    continue;
		else if (  linep[pos.col - 1] == '/'
			&& linep[pos.col] == '*'
			&& (int)pos.col < comment_col)
		{
		    count++;
		    match_pos = pos;
		    match_pos.col--;
		}
		else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
		{
		    if (count > 0)
			pos = match_pos;
		    else if (pos.col > 1 && linep[pos.col - 2] == '/')
			pos.col -= 2;
		    else if (ignore_cend)
			continue;
		    else
			return NULL;
		    return &pos;
		}
	    }
	    continue;
	}

	/*
	 * If smart matching ('cpoptions' does not contain '%'), braces inside
	 * of quotes are ignored, but only if there is an even number of
	 * quotes in the line.
	 */
	if (cpo_match)
	    do_quotes = 0;
	else if (do_quotes == -1)
	{
	    /*
	     * count the number of quotes in the line, skipping \" and '"'
	     */
	    at_start = do_quotes;
	    for (ptr = linep; *ptr; ++ptr)
	    {
		if (ptr == linep + pos.col + backwards)
		    at_start = (do_quotes & 1);
		if (*ptr == '"' && (ptr == linep || ptr[-1] != '\\') &&
			    (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
		    ++do_quotes;
	    }
	    do_quotes &= 1;	    /* result is 1 with even number of quotes */

	    /*
	     * If we find an uneven count, check current line and previous
	     * one for a '\' at the end.
	     */
	    if (!do_quotes)
	    {
		inquote = FALSE;
		if (ptr[-1] == '\\')
		{
		    do_quotes = 1;
		    if (start_in_quotes == MAYBE)
		    {
			inquote = !at_start;
			if (inquote)
			    start_in_quotes = TRUE;
		    }
		    else if (backwards)
			inquote = TRUE;
		}
		if (pos.lnum > 1)
		{
		    ptr = ml_get(pos.lnum - 1);
		    if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
		    {
			do_quotes = 1;
			if (start_in_quotes == MAYBE)
			{
			    inquote = at_start;
			    if (inquote)
				start_in_quotes = TRUE;
			}
			else if (!backwards)
			    inquote = TRUE;
		    }
		}
	    }
	}
	if (start_in_quotes == MAYBE)
	    start_in_quotes = FALSE;

	/*
	 * If 'smartmatch' is set:
	 *   Things inside quotes are ignored by setting 'inquote'.  If we
	 *   find a quote without a preceding '\' invert 'inquote'.  At the
	 *   end of a line not ending in '\' we reset 'inquote'.
	 *
	 *   In lines with an uneven number of quotes (without preceding '\')
	 *   we do not know which part to ignore. Therefore we only set
	 *   inquote if the number of quotes in a line is even, unless this
	 *   line or the previous one ends in a '\'.  Complicated, isn't it?
	 */
	switch (c = linep[pos.col])
	{
	case NUL:
		/* at end of line without trailing backslash, reset inquote */
	    if (pos.col == 0 || linep[pos.col - 1] != '\\')
	    {
		inquote = FALSE;
		start_in_quotes = FALSE;
	    }
	    break;

	case '"':
		/* a quote that is preceded with a backslash is ignored */
	    if (do_quotes && (pos.col == 0 || linep[pos.col - 1] != '\\'))
	    {
		inquote = !inquote;
		start_in_quotes = FALSE;
	    }
	    break;

	/*
	 * If smart matching ('cpoptions' does not contain '%'):
	 *   Skip things in single quotes: 'x' or '\x'.  Be careful for single
	 *   single quotes, eg jon's.  Things like '\233' or '\x3f' are not
	 *   skipped, there is never a brace in them.
	 */
	case '\'':
	    if (!cpo_match)
	    {
		if (backwards)
		{
		    if (pos.col > 1)
		    {
			if (linep[pos.col - 2] == '\'')
			    pos.col -= 2;
			else if (linep[pos.col - 2] == '\\' &&
				    pos.col > 2 && linep[pos.col - 3] == '\'')
			    pos.col -= 3;
		    }
		}
		else if (linep[pos.col + 1])	/* forward search */
		{
		    if (linep[pos.col + 1] == '\\' &&
			    linep[pos.col + 2] && linep[pos.col + 3] == '\'')
			pos.col += 3;
		    else if (linep[pos.col + 2] == '\'')
			pos.col += 2;
		}
	    }
	    break;

	default:
	    /* Check for match outside of quotes, and inside of
	     * quotes when the start is also inside of quotes */
	    if (!inquote || start_in_quotes == TRUE)
	    {
		if (c == initc)
		    count++;
		else if (c == findc)
		{
		    if (count == 0)
			return &pos;
		    count--;
		}
	    }
	}
    }

    if (comment_dir == BACKWARD && count > 0)
    {
	pos = match_pos;

⌨️ 快捷键说明

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