📄 search.c
字号:
* 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 + -