Merge #9662 'vim-patch:8.0.{0643-0646}'

This commit is contained in:
Justin M. Keyes 2019-03-08 13:21:11 +01:00
commit 4352d41db0
18 changed files with 271 additions and 159 deletions

View File

@ -3907,10 +3907,11 @@ static int ins_compl_get_exp(pos_T *ini)
compl_direction, compl_pattern); compl_direction, compl_pattern);
} else } else
found_new_match = searchit(NULL, ins_buf, pos, found_new_match = searchit(NULL, ins_buf, pos,
compl_direction, compl_direction,
compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, compl_pattern, 1L,
RE_LAST, (linenr_T)0, NULL); SEARCH_KEEP + SEARCH_NFMSG,
--msg_silent; RE_LAST, (linenr_T)0, NULL, NULL);
msg_silent--;
if (!compl_started || set_match_pos) { if (!compl_started || set_match_pos) {
/* set "compl_started" even on fail */ /* set "compl_started" even on fail */
compl_started = TRUE; compl_started = TRUE;

View File

@ -13806,7 +13806,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
pos = save_cursor = curwin->w_cursor; pos = save_cursor = curwin->w_cursor;
subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1, subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1,
options, RE_SEARCH, (linenr_T)lnum_stop, &tm); options, RE_SEARCH, (linenr_T)lnum_stop, &tm, NULL);
if (subpatnum != FAIL) { if (subpatnum != FAIL) {
if (flags & SP_SUBPAT) if (flags & SP_SUBPAT)
retval = subpatnum; retval = subpatnum;
@ -14308,10 +14308,11 @@ do_searchpair(
pat = pat3; pat = pat3;
for (;; ) { for (;; ) {
n = searchit(curwin, curbuf, &pos, dir, pat, 1L, n = searchit(curwin, curbuf, &pos, dir, pat, 1L,
options, RE_SEARCH, lnum_stop, &tm); options, RE_SEARCH, lnum_stop, &tm, NULL);
if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) {
/* didn't find it or found the first match again: FAIL */ // didn't find it or found the first match again: FAIL
break; break;
}
if (firstpos.lnum == 0) if (firstpos.lnum == 0)
firstpos = pos; firstpos = pos;

View File

@ -3432,7 +3432,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
|| lnum <= curwin->w_botline); || lnum <= curwin->w_botline);
lnum++) { lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum, long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL); (colnr_T)0, NULL, NULL);
if (nmatch) { if (nmatch) {
colnr_T copycol; colnr_T copycol;
colnr_T matchcol; colnr_T matchcol;
@ -3951,8 +3951,8 @@ skip:
if (lastone if (lastone
|| nmatch_tl > 0 || nmatch_tl > 0
|| (nmatch = vim_regexec_multi(&regmatch, curwin, || (nmatch = vim_regexec_multi(&regmatch, curwin,
curbuf, sub_firstlnum, curbuf, sub_firstlnum,
matchcol, NULL)) == 0 matchcol, NULL, NULL)) == 0
|| regmatch.startpos[0].lnum > 0) { || regmatch.startpos[0].lnum > 0) {
if (new_start != NULL) { if (new_start != NULL) {
/* /*
@ -4016,7 +4016,7 @@ skip:
} }
if (nmatch == -1 && !lastone) if (nmatch == -1 && !lastone)
nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, nmatch = vim_regexec_multi(&regmatch, curwin, curbuf,
sub_firstlnum, matchcol, NULL); sub_firstlnum, matchcol, NULL, NULL);
/* /*
* 5. break if there isn't another match in this line * 5. break if there isn't another match in this line
@ -4314,7 +4314,7 @@ void ex_global(exarg_T *eap)
if (global_busy) { if (global_busy) {
lnum = curwin->w_cursor.lnum; lnum = curwin->w_cursor.lnum;
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum, match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL); (colnr_T)0, NULL, NULL);
if ((type == 'g' && match) || (type == 'v' && !match)) { if ((type == 'g' && match) || (type == 'v' && !match)) {
global_exe_one(cmd, lnum); global_exe_one(cmd, lnum);
} }
@ -4323,7 +4323,7 @@ void ex_global(exarg_T *eap)
for (lnum = eap->line1; lnum <= eap->line2 && !got_int; lnum++) { for (lnum = eap->line1; lnum <= eap->line2 && !got_int; lnum++) {
// a match on this line? // a match on this line?
match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum, match = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL); (colnr_T)0, NULL, NULL);
if ((type == 'g' && match) || (type == 'v' && !match)) { if ((type == 'g' && match) || (type == 'v' && !match)) {
ml_setmarked(lnum); ml_setmarked(lnum);
ndone++; ndone++;

View File

@ -3699,7 +3699,7 @@ static linenr_T get_address(exarg_T *eap,
} }
searchcmdlen = 0; searchcmdlen = 0;
if (!do_search(NULL, c, cmd, 1L, if (!do_search(NULL, c, cmd, 1L,
SEARCH_HIS | SEARCH_MSG, NULL)) { SEARCH_HIS | SEARCH_MSG, NULL, NULL)) {
curwin->w_cursor = pos; curwin->w_cursor = pos;
cmd = NULL; cmd = NULL;
goto error; goto error;
@ -3737,7 +3737,7 @@ static linenr_T get_address(exarg_T *eap,
if (searchit(curwin, curbuf, &pos, if (searchit(curwin, curbuf, &pos,
*cmd == '?' ? BACKWARD : FORWARD, *cmd == '?' ? BACKWARD : FORWARD,
(char_u *)"", 1L, SEARCH_MSG, (char_u *)"", 1L, SEARCH_MSG,
i, (linenr_T)0, NULL) != FAIL) i, (linenr_T)0, NULL, NULL) != FAIL)
lnum = pos.lnum; lnum = pos.lnum;
else { else {
cmd = NULL; cmd = NULL;

View File

@ -1061,7 +1061,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
s->i = searchit(curwin, curbuf, &t, s->i = searchit(curwin, curbuf, &t,
next_match ? FORWARD : BACKWARD, next_match ? FORWARD : BACKWARD,
pat, s->count, search_flags, pat, s->count, search_flags,
RE_SEARCH, 0, NULL); RE_SEARCH, 0, NULL, NULL);
emsg_off--; emsg_off--;
ui_busy_stop(); ui_busy_stop();
if (s->i) { if (s->i) {
@ -1847,7 +1847,7 @@ static int command_line_changed(CommandLineState *s)
} }
s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count, s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count,
search_flags, search_flags,
&tm); &tm, NULL);
emsg_off--; emsg_off--;
// if interrupted while searching, behave like it failed // if interrupted while searching, behave like it failed
if (got_int) { if (got_int) {

View File

@ -3782,9 +3782,10 @@ find_decl (
valid = false; valid = false;
(void)valid; // Avoid "dead assignment" warning. (void)valid; // Avoid "dead assignment" warning.
t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD, t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD,
pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL); pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL, NULL);
if (curwin->w_cursor.lnum >= old_pos.lnum) if (curwin->w_cursor.lnum >= old_pos.lnum) {
t = false; /* match after start is failure too */ t = false; // match after start is failure too
}
if (thisblock && t != false) { if (thisblock && t != false) {
const int64_t maxtravel = old_pos.lnum - curwin->w_cursor.lnum + 1; const int64_t maxtravel = old_pos.lnum - curwin->w_cursor.lnum + 1;
@ -5384,7 +5385,7 @@ static int normal_search(
curwin->w_set_curswant = true; curwin->w_set_curswant = true;
i = do_search(cap->oap, dir, pat, cap->count1, i = do_search(cap->oap, dir, pat, cap->count1,
opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL); opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, NULL, NULL);
if (i == 0) { if (i == 0) {
clearop(cap->oap); clearop(cap->oap);
} else { } else {

View File

@ -2128,8 +2128,9 @@ win_found:
save_cursor = curwin->w_cursor; save_cursor = curwin->w_cursor;
curwin->w_cursor.lnum = 0; curwin->w_cursor.lnum = 0;
if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1, if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1,
SEARCH_KEEP, NULL)) SEARCH_KEEP, NULL, NULL)) {
curwin->w_cursor = save_cursor; curwin->w_cursor = save_cursor;
}
} }
if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped) if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped)
@ -3758,7 +3759,7 @@ void ex_vimgrep(exarg_T *eap)
++lnum) { ++lnum) {
col = 0; col = 0;
while (vim_regexec_multi(&regmatch, curwin, buf, lnum, while (vim_regexec_multi(&regmatch, curwin, buf, lnum,
col, NULL) > 0) { col, NULL, NULL) > 0) {
// Pass the buffer number so that it gets used even for a // Pass the buffer number so that it gets used even for a
// dummy buffer, unless duplicate_name is set, then the // dummy buffer, unless duplicate_name is set, then the
// buffer will be wiped out below. // buffer will be wiped out below.

View File

@ -1210,6 +1210,31 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp)
return p; return p;
} }
/// Return TRUE if the back reference is legal. We must have seen the close
/// brace.
/// TODO(vim): Should also check that we don't refer to something repeated
/// (+*=): what instance of the repetition should we match?
static int seen_endbrace(int refnum)
{
if (!had_endbrace[refnum]) {
char_u *p;
// Trick: check if "@<=" or "@<!" follows, in which case
// the \1 can appear before the referenced match.
for (p = regparse; *p != NUL; p++) {
if (p[0] == '@' && p[1] == '<' && (p[2] == '!' || p[2] == '=')) {
break;
}
}
if (*p == NUL) {
EMSG(_("E65: Illegal back reference"));
rc_did_emsg = true;
return false;
}
}
return TRUE;
}
/* /*
* bt_regcomp() - compile a regular expression into internal code for the * bt_regcomp() - compile a regular expression into internal code for the
@ -1928,22 +1953,8 @@ static char_u *regatom(int *flagp)
int refnum; int refnum;
refnum = c - Magic('0'); refnum = c - Magic('0');
/* if (!seen_endbrace(refnum)) {
* Check if the back reference is legal. We must have seen the return NULL;
* close brace.
* TODO: Should also check that we don't refer to something
* that is repeated (+*=): what instance of the repetition
* should we match?
*/
if (!had_endbrace[refnum]) {
/* Trick: check if "@<=" or "@<!" follows, in which case
* the \1 can appear before the referenced match. */
for (p = regparse; *p != NUL; ++p)
if (p[0] == '@' && p[1] == '<'
&& (p[2] == '!' || p[2] == '='))
break;
if (*p == NUL)
EMSG_RET_NULL(_("E65: Illegal back reference"));
} }
ret = regnode(BACKREF + refnum); ret = regnode(BACKREF + refnum);
} }
@ -3297,7 +3308,7 @@ bt_regexec_nl (
rex.reg_icombine = false; rex.reg_icombine = false;
rex.reg_maxcol = 0; rex.reg_maxcol = 0;
long r = bt_regexec_both(line, col, NULL); long r = bt_regexec_both(line, col, NULL, NULL);
assert(r <= INT_MAX); assert(r <= INT_MAX);
return (int)r; return (int)r;
} }
@ -3357,7 +3368,8 @@ static inline char_u *cstrchr(const char_u *const s, const int c)
/// @return zero if there is no match and number of lines contained in the match /// @return zero if there is no match and number of lines contained in the match
/// otherwise. /// otherwise.
static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
linenr_T lnum, colnr_T col, proftime_T *tm) linenr_T lnum, colnr_T col,
proftime_T *tm, int *timed_out)
{ {
rex.reg_match = NULL; rex.reg_match = NULL;
rex.reg_mmatch = rmp; rex.reg_mmatch = rmp;
@ -3370,18 +3382,16 @@ static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
rex.reg_icombine = false; rex.reg_icombine = false;
rex.reg_maxcol = rmp->rmm_maxcol; rex.reg_maxcol = rmp->rmm_maxcol;
return bt_regexec_both(NULL, col, tm); return bt_regexec_both(NULL, col, tm, timed_out);
} }
/* /// Match a regexp against a string ("line" points to the string) or multiple
* Match a regexp against a string ("line" points to the string) or multiple /// lines ("line" is NULL, use reg_getline()).
* lines ("line" is NULL, use reg_getline()). /// @return 0 for failure, or number of lines contained in the match.
* Returns 0 for failure, number of lines contained in the match otherwise.
*/
static long bt_regexec_both(char_u *line, static long bt_regexec_both(char_u *line,
colnr_T col, /* column to start looking for match */ colnr_T col, // column to start search
proftime_T *tm /* timeout limit or NULL */ proftime_T *tm, // timeout limit or NULL
) int *timed_out) // flag set on timeout or NULL
{ {
bt_regprog_T *prog; bt_regprog_T *prog;
char_u *s; char_u *s;
@ -3483,7 +3493,7 @@ static long bt_regexec_both(char_u *line,
&& (utf_fold(prog->regstart) == utf_fold(c) && (utf_fold(prog->regstart) == utf_fold(c)
|| (c < 255 && prog->regstart < 255 || (c < 255 && prog->regstart < 255
&& mb_tolower(prog->regstart) == mb_tolower(c))))) { && mb_tolower(prog->regstart) == mb_tolower(c))))) {
retval = regtry(prog, col); retval = regtry(prog, col, tm, timed_out);
} else { } else {
retval = 0; retval = 0;
} }
@ -3507,9 +3517,10 @@ static long bt_regexec_both(char_u *line,
break; break;
} }
retval = regtry(prog, col); retval = regtry(prog, col, tm, timed_out);
if (retval > 0) if (retval > 0) {
break; break;
}
/* if not currently on the first line, get it again */ /* if not currently on the first line, get it again */
if (reglnum != 0) { if (reglnum != 0) {
@ -3525,8 +3536,12 @@ static long bt_regexec_both(char_u *line,
/* Check for timeout once in a twenty times to avoid overhead. */ /* Check for timeout once in a twenty times to avoid overhead. */
if (tm != NULL && ++tm_count == 20) { if (tm != NULL && ++tm_count == 20) {
tm_count = 0; tm_count = 0;
if (profile_passed_limit(*tm)) if (profile_passed_limit(*tm)) {
if (timed_out != NULL) {
*timed_out = true;
}
break; break;
}
} }
} }
} }
@ -3582,11 +3597,12 @@ void unref_extmatch(reg_extmatch_T *em)
} }
} }
/* /// Try match of "prog" with at regline["col"].
* regtry - try match of "prog" with at regline["col"]. /// @returns 0 for failure, or number of lines contained in the match.
* Returns 0 for failure, number of lines contained in the match otherwise. static long regtry(bt_regprog_T *prog,
*/ colnr_T col,
static long regtry(bt_regprog_T *prog, colnr_T col) proftime_T *tm, // timeout limit or NULL
int *timed_out) // flag set on timeout or NULL
{ {
reginput = regline + col; reginput = regline + col;
need_clear_subexpr = TRUE; need_clear_subexpr = TRUE;
@ -3594,8 +3610,9 @@ static long regtry(bt_regprog_T *prog, colnr_T col)
if (prog->reghasz == REX_SET) if (prog->reghasz == REX_SET)
need_clear_zsubexpr = TRUE; need_clear_zsubexpr = TRUE;
if (regmatch(prog->program + 1) == 0) if (regmatch(prog->program + 1, tm, timed_out) == 0) {
return 0; return 0;
}
cleanup_subexpr(); cleanup_subexpr();
if (REG_MULTI) { if (REG_MULTI) {
@ -3736,24 +3753,23 @@ static int reg_match_visual(void)
static long bl_minval; static long bl_minval;
static long bl_maxval; static long bl_maxval;
/* /// Main matching routine
* regmatch - main matching routine ///
* /// Conceptually the strategy is simple: Check to see whether the current node
* Conceptually the strategy is simple: Check to see whether the current node /// matches, push an item onto the regstack and loop to see whether the rest
* matches, push an item onto the regstack and loop to see whether the rest /// matches, and then act accordingly. In practice we make some effort to
* matches, and then act accordingly. In practice we make some effort to /// avoid using the regstack, in particular by going through "ordinary" nodes
* avoid using the regstack, in particular by going through "ordinary" nodes /// (that don't need to know whether the rest of the match failed) by a nested
* (that don't need to know whether the rest of the match failed) by a nested /// loop.
* loop. ///
* /// Returns TRUE when there is a match. Leaves reginput and reglnum just after
* Returns TRUE when there is a match. Leaves reginput and reglnum just after /// the last matched character.
* the last matched character. /// Returns FALSE when there is no match. Leaves reginput and reglnum in an
* Returns FALSE when there is no match. Leaves reginput and reglnum in an /// undefined state!
* undefined state! static int regmatch(
*/ char_u *scan, // Current node.
static int proftime_T *tm, // timeout limit or NULL
regmatch ( int *timed_out // flag set on timeout or NULL
char_u *scan /* Current node. */
) )
{ {
char_u *next; /* Next node. */ char_u *next; /* Next node. */
@ -3761,15 +3777,16 @@ regmatch (
int c; int c;
regitem_T *rp; regitem_T *rp;
int no; int no;
int status; /* one of the RA_ values: */ int status; // one of the RA_ values:
#define RA_FAIL 1 /* something failed, abort */ int tm_count = 0;
#define RA_CONT 2 /* continue in inner loop */ #define RA_FAIL 1 // something failed, abort
#define RA_BREAK 3 /* break inner loop */ #define RA_CONT 2 // continue in inner loop
#define RA_MATCH 4 /* successful match */ #define RA_BREAK 3 // break inner loop
#define RA_NOMATCH 5 /* didn't match */ #define RA_MATCH 4 // successful match
#define RA_NOMATCH 5 // didn't match
/* Make "regstack" and "backpos" empty. They are allocated and freed in // Make "regstack" and "backpos" empty. They are allocated and freed in
* bt_regexec_both() to reduce malloc()/free() calls. */ // bt_regexec_both() to reduce malloc()/free() calls.
regstack.ga_len = 0; regstack.ga_len = 0;
backpos.ga_len = 0; backpos.ga_len = 0;
@ -3797,6 +3814,17 @@ regmatch (
status = RA_FAIL; status = RA_FAIL;
break; break;
} }
// Check for timeout once in a 100 times to avoid overhead.
if (tm != NULL && ++tm_count == 100) {
tm_count = 0;
if (profile_passed_limit(*tm)) {
if (timed_out != NULL) {
*timed_out = true;
}
status = RA_FAIL;
break;
}
}
status = RA_CONT; status = RA_CONT;
#ifdef REGEXP_DEBUG #ifdef REGEXP_DEBUG
@ -7275,12 +7303,13 @@ int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col)
/// Return zero if there is no match. Return number of lines contained in the /// Return zero if there is no match. Return number of lines contained in the
/// match otherwise. /// match otherwise.
long vim_regexec_multi( long vim_regexec_multi(
regmmatch_T *rmp, regmmatch_T *rmp,
win_T *win, /* window in which to search or NULL */ win_T *win, // window in which to search or NULL
buf_T *buf, /* buffer in which to search */ buf_T *buf, // buffer in which to search
linenr_T lnum, /* nr of line to start looking for match */ linenr_T lnum, // nr of line to start looking for match
colnr_T col, /* column to start looking for match */ colnr_T col, // column to start looking for match
proftime_T *tm /* timeout limit or NULL */ proftime_T *tm, // timeout limit or NULL
int *timed_out // flag is set when timeout limit reached
) )
{ {
regexec_T rex_save; regexec_T rex_save;
@ -7293,7 +7322,7 @@ long vim_regexec_multi(
rex_in_use = true; rex_in_use = true;
int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
tm); tm, timed_out);
// NFA engine aborted because it's very slow, use backtracking engine instead. // NFA engine aborted because it's very slow, use backtracking engine instead.
if (rmp->regprog->re_engine == AUTOMATIC_ENGINE if (rmp->regprog->re_engine == AUTOMATIC_ENGINE
@ -7313,7 +7342,7 @@ long vim_regexec_multi(
if (rmp->regprog != NULL) { if (rmp->regprog != NULL) {
result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
tm); tm, timed_out);
} }
xfree(pat); xfree(pat);

View File

@ -156,11 +156,11 @@ struct reg_extmatch {
}; };
struct regengine { struct regengine {
regprog_T *(*regcomp)(char_u*, int); regprog_T *(*regcomp)(char_u *, int);
void (*regfree)(regprog_T *); void (*regfree)(regprog_T *);
int (*regexec_nl)(regmatch_T*, char_u*, colnr_T, bool); int (*regexec_nl)(regmatch_T *, char_u *, colnr_T, bool);
long (*regexec_multi)(regmmatch_T*, win_T*, buf_T*, linenr_T, colnr_T, long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T,
proftime_T*); proftime_T *, int *);
char_u *expr; char_u *expr;
}; };

View File

@ -1338,8 +1338,15 @@ static int nfa_regatom(void)
case Magic('7'): case Magic('7'):
case Magic('8'): case Magic('8'):
case Magic('9'): case Magic('9'):
EMIT(NFA_BACKREF1 + (no_Magic(c) - '1')); {
nfa_has_backref = TRUE; int refnum = no_Magic(c) - '1';
if (!seen_endbrace(refnum + 1)) {
return FAIL;
}
EMIT(NFA_BACKREF1 + refnum);
nfa_has_backref = true;
}
break; break;
case Magic('z'): case Magic('z'):
@ -3568,6 +3575,7 @@ static char *pim_info(nfa_pim_T *pim)
// Used during execution: whether a match has been found. // Used during execution: whether a match has been found.
static int nfa_match; static int nfa_match;
static proftime_T *nfa_time_limit; static proftime_T *nfa_time_limit;
static int *nfa_timed_out;
static int nfa_time_count; static int nfa_time_count;
// Copy postponed invisible match info from "from" to "to". // Copy postponed invisible match info from "from" to "to".
@ -4939,6 +4947,17 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text)
#undef PTR2LEN #undef PTR2LEN
} }
static int nfa_did_time_out(void)
{
if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) {
if (nfa_timed_out != NULL) {
*nfa_timed_out = true;
}
return true;
}
return false;
}
/// Main matching routine. /// Main matching routine.
/// ///
/// Run NFA to determine whether it matches reginput. /// Run NFA to determine whether it matches reginput.
@ -4982,7 +5001,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
#endif #endif
return false; return false;
} }
if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) { if (nfa_did_time_out()) {
#ifdef NFA_REGEXP_DEBUG_LOG #ifdef NFA_REGEXP_DEBUG_LOG
fclose(debug); fclose(debug);
#endif #endif
@ -5095,8 +5114,20 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start,
if (thislist->n == 0) if (thislist->n == 0)
break; break;
/* compute nextlist */ // compute nextlist
for (listidx = 0; listidx < thislist->n; ++listidx) { for (listidx = 0; listidx < thislist->n; listidx++) {
// If the list gets very long there probably is something wrong.
// At least allow interrupting with CTRL-C.
fast_breakcheck();
if (got_int) {
break;
}
if (nfa_time_limit != NULL && ++nfa_time_count == 20) {
nfa_time_count = 0;
if (nfa_did_time_out()) {
break;
}
}
t = &thislist->t[listidx]; t = &thislist->t[listidx];
#ifdef NFA_REGEXP_DEBUG_LOG #ifdef NFA_REGEXP_DEBUG_LOG
@ -6218,7 +6249,7 @@ nextchar:
// Check for timeout once every twenty times to avoid overhead. // Check for timeout once every twenty times to avoid overhead.
if (nfa_time_limit != NULL && ++nfa_time_count == 20) { if (nfa_time_limit != NULL && ++nfa_time_count == 20) {
nfa_time_count = 0; nfa_time_count = 0;
if (profile_passed_limit(*nfa_time_limit)) { if (nfa_did_time_out()) {
break; break;
} }
} }
@ -6245,7 +6276,10 @@ theend:
// Try match of "prog" with at regline["col"]. // Try match of "prog" with at regline["col"].
// Returns <= 0 for failure, number of lines contained in the match otherwise. // Returns <= 0 for failure, number of lines contained in the match otherwise.
static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm) static long nfa_regtry(nfa_regprog_T *prog,
colnr_T col,
proftime_T *tm, // timeout limit or NULL
int *timed_out) // flag set on timeout or NULL
{ {
int i; int i;
regsubs_T subs, m; regsubs_T subs, m;
@ -6256,6 +6290,7 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
reginput = regline + col; reginput = regline + col;
nfa_time_limit = tm; nfa_time_limit = tm;
nfa_timed_out = timed_out;
nfa_time_count = 0; nfa_time_count = 0;
#ifdef REGEXP_DEBUG #ifdef REGEXP_DEBUG
@ -6364,10 +6399,12 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm)
/// @param line String in which to search or NULL /// @param line String in which to search or NULL
/// @param startcol Column to start looking for match /// @param startcol Column to start looking for match
/// @param tm Timeout limit or NULL /// @param tm Timeout limit or NULL
/// @param timed_out Flag set on timeout or NULL
/// ///
/// @return <= 0 if there is no match and number of lines contained in the /// @return <= 0 if there is no match and number of lines contained in the
/// match otherwise. /// match otherwise.
static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) static long nfa_regexec_both(char_u *line, colnr_T startcol,
proftime_T *tm, int *timed_out)
{ {
nfa_regprog_T *prog; nfa_regprog_T *prog;
long retval = 0L; long retval = 0L;
@ -6449,7 +6486,7 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm)
prog->state[i].lastlist[1] = 0; prog->state[i].lastlist[1] = 0;
} }
retval = nfa_regtry(prog, col, tm); retval = nfa_regtry(prog, col, tm, timed_out);
nfa_regengine.expr = NULL; nfa_regengine.expr = NULL;
@ -6596,7 +6633,7 @@ nfa_regexec_nl (
rex.reg_ic = rmp->rm_ic; rex.reg_ic = rmp->rm_ic;
rex.reg_icombine = false; rex.reg_icombine = false;
rex.reg_maxcol = 0; rex.reg_maxcol = 0;
return nfa_regexec_both(line, col, NULL); return nfa_regexec_both(line, col, NULL, NULL);
} }
/// Matches a regexp against multiple lines. /// Matches a regexp against multiple lines.
@ -6608,6 +6645,7 @@ nfa_regexec_nl (
/// @param lnum Number of line to start looking for match /// @param lnum Number of line to start looking for match
/// @param col Column to start looking for match /// @param col Column to start looking for match
/// @param tm Timeout limit or NULL /// @param tm Timeout limit or NULL
/// @param timed_out Flag set on timeout or NULL
/// ///
/// @return <= 0 if there is no match and number of lines contained in the match /// @return <= 0 if there is no match and number of lines contained in the match
/// otherwise. /// otherwise.
@ -6634,7 +6672,8 @@ nfa_regexec_nl (
/// @par /// @par
/// FIXME if this behavior is not compatible. /// FIXME if this behavior is not compatible.
static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
linenr_T lnum, colnr_T col, proftime_T *tm) linenr_T lnum, colnr_T col,
proftime_T *tm, int *timed_out)
{ {
rex.reg_match = NULL; rex.reg_match = NULL;
rex.reg_mmatch = rmp; rex.reg_mmatch = rmp;
@ -6647,5 +6686,5 @@ static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf,
rex.reg_icombine = false; rex.reg_icombine = false;
rex.reg_maxcol = rmp->rmm_maxcol; rex.reg_maxcol = rmp->rmm_maxcol;
return nfa_regexec_both(NULL, col, tm); return nfa_regexec_both(NULL, col, tm, timed_out);
} }

View File

@ -5651,13 +5651,15 @@ next_search_hl (
&& cur != NULL && cur != NULL
&& shl == &cur->hl && shl == &cur->hl
&& cur->match.regprog == cur->hl.rm.regprog); && cur->match.regprog == cur->hl.rm.regprog);
int timed_out = false;
nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, &(shl->tm)); nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol,
/* Copy the regprog, in case it got freed and recompiled. */ &(shl->tm), &timed_out);
// Copy the regprog, in case it got freed and recompiled.
if (regprog_is_copy) { if (regprog_is_copy) {
cur->match.regprog = cur->hl.rm.regprog; cur->match.regprog = cur->hl.rm.regprog;
} }
if (called_emsg || got_int) { if (called_emsg || got_int || timed_out) {
// Error while handling regexp: stop using this regexp. // Error while handling regexp: stop using this regexp.
if (shl == &search_hl) { if (shl == &search_hl) {
// don't free regprog in the match list, it's a copy // don't free regprog in the match list, it's a copy

View File

@ -523,9 +523,10 @@ int searchit(
char_u *pat, char_u *pat,
long count, long count,
int options, int options,
int pat_use, /* which pattern to use when "pat" is empty */ int pat_use, // which pattern to use when "pat" is empty
linenr_T stop_lnum, /* stop after this line number when != 0 */ linenr_T stop_lnum, // stop after this line number when != 0
proftime_T *tm /* timeout limit or NULL */ proftime_T *tm, // timeout limit or NULL
int *timed_out // set when timed out or NULL
) )
{ {
int found; int found;
@ -620,9 +621,9 @@ int searchit(
// Look for a match somewhere in line "lnum". // Look for a match somewhere in line "lnum".
colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0;
nmatched = vim_regexec_multi(&regmatch, win, buf, nmatched = vim_regexec_multi(&regmatch, win, buf,
lnum, col, tm); lnum, col, tm, timed_out);
// Abort searching on an error (e.g., out of stack). // Abort searching on an error (e.g., out of stack).
if (called_emsg) { if (called_emsg || (timed_out != NULL && *timed_out)) {
break; break;
} }
if (nmatched > 0) { if (nmatched > 0) {
@ -686,8 +687,9 @@ int searchit(
} }
if (ptr[matchcol] == NUL if (ptr[matchcol] == NUL
|| (nmatched = vim_regexec_multi(&regmatch, win, buf, lnum, || (nmatched = vim_regexec_multi(&regmatch, win, buf,
matchcol, tm)) == 0) { lnum, matchcol, tm,
timed_out)) == 0) {
match_ok = false; match_ok = false;
break; break;
} }
@ -771,7 +773,7 @@ int searchit(
if (ptr[matchcol] == NUL if (ptr[matchcol] == NUL
|| (nmatched = vim_regexec_multi( || (nmatched = vim_regexec_multi(
&regmatch, win, buf, lnum + matchpos.lnum, matchcol, &regmatch, win, buf, lnum + matchpos.lnum, matchcol,
tm)) == 0) { tm, timed_out)) == 0) {
// If the search timed out, we did find a match // If the search timed out, we did find a match
// but it might be the wrong one, so that's not // but it might be the wrong one, so that's not
// OK. // OK.
@ -855,30 +857,35 @@ int searchit(
* twice. * twice.
*/ */
if (!p_ws || stop_lnum != 0 || got_int || called_emsg if (!p_ws || stop_lnum != 0 || got_int || called_emsg
|| (timed_out != NULL && timed_out)
|| break_loop || break_loop
|| found || loop) || found || loop) {
break; break;
}
/* //
* If 'wrapscan' is set we continue at the other end of the file. // If 'wrapscan' is set we continue at the other end of the file.
* If 'shortmess' does not contain 's', we give a message. // If 'shortmess' does not contain 's', we give a message.
* This message is also remembered in keep_msg for when the screen // This message is also remembered in keep_msg for when the screen
* is redrawn. The keep_msg is cleared whenever another message is // is redrawn. The keep_msg is cleared whenever another message is
* written. // written.
*/ //
if (dir == BACKWARD) /* start second loop at the other end */ if (dir == BACKWARD) { // start second loop at the other end
lnum = buf->b_ml.ml_line_count; lnum = buf->b_ml.ml_line_count;
else } else {
lnum = 1; lnum = 1;
if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) }
if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) {
give_warning((char_u *)_(dir == BACKWARD give_warning((char_u *)_(dir == BACKWARD
? top_bot_msg : bot_top_msg), true); ? top_bot_msg : bot_top_msg), true);
}
} }
if (got_int || called_emsg if (got_int || called_emsg
|| (timed_out != NULL && *timed_out)
|| break_loop || break_loop
) ) {
break; break;
} while (--count > 0 && found); /* stop after count matches or no match */ }
} while (--count > 0 && found); // stop after count matches or no match
vim_regfree(regmatch.regprog); vim_regfree(regmatch.regprog);
@ -965,7 +972,8 @@ int do_search(
char_u *pat, char_u *pat,
long count, long count,
int options, int options,
proftime_T *tm /* timeout limit or NULL */ proftime_T *tm, // timeout limit or NULL
int *timed_out // flag set on timeout or NULL
) )
{ {
pos_T pos; /* position of the last match */ pos_T pos; /* position of the last match */
@ -1195,7 +1203,7 @@ int do_search(
& (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG & (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG
+ SEARCH_START + SEARCH_START
+ ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))), + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))),
RE_LAST, (linenr_T)0, tm); RE_LAST, (linenr_T)0, tm, timed_out);
if (dircp != NULL) if (dircp != NULL)
*dircp = dirc; /* restore second '/' or '?' for normal_cmd() */ *dircp = dirc; /* restore second '/' or '?' for normal_cmd() */
@ -3978,7 +3986,7 @@ current_search(
result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD), result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD),
spats[last_idx].pat, i ? count : 1, spats[last_idx].pat, i ? count : 1,
SEARCH_KEEP | flags, RE_SEARCH, 0, NULL); SEARCH_KEEP | flags, RE_SEARCH, 0, NULL, NULL);
/* First search may fail, but then start searching from the /* First search may fail, but then start searching from the
* beginning of the file (cursor might be on the search match) * beginning of the file (cursor might be on the search match)
@ -4025,7 +4033,7 @@ current_search(
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
result = searchit(curwin, curbuf, &pos, direction, result = searchit(curwin, curbuf, &pos, direction,
spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH,
0, NULL); 0, NULL, NULL);
// Search successfull, break out from the loop // Search successfull, break out from the loop
if (result) { if (result) {
break; break;
@ -4104,14 +4112,15 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur,
flag = SEARCH_START; flag = SEARCH_START;
} }
if (searchit(curwin, curbuf, &pos, direction, pattern, 1, if (searchit(curwin, curbuf, &pos, direction, pattern, 1,
SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) { SEARCH_KEEP + flag, RE_SEARCH, 0, NULL, NULL) != FAIL) {
// Zero-width pattern should match somewhere, then we can check if // Zero-width pattern should match somewhere, then we can check if
// start and end are in the same position. // start and end are in the same position.
called_emsg = false; called_emsg = false;
do { do {
regmatch.startpos[0].col++; regmatch.startpos[0].col++;
nmatched = vim_regexec_multi(&regmatch, curwin, curbuf, nmatched = vim_regexec_multi(&regmatch, curwin, curbuf,
pos.lnum, regmatch.startpos[0].col, NULL); pos.lnum, regmatch.startpos[0].col,
NULL, NULL);
if (!nmatched) { if (!nmatched) {
break; break;
} }

View File

@ -3048,9 +3048,10 @@ void ex_spellrepall(exarg_T *eap)
sub_nlines = 0; sub_nlines = 0;
curwin->w_cursor.lnum = 0; curwin->w_cursor.lnum = 0;
while (!got_int) { while (!got_int) {
if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL) == 0 if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL, NULL) == 0
|| u_save_cursor() == FAIL) || u_save_cursor() == FAIL) {
break; break;
}
// Only replace when the right word isn't there yet. This happens // Only replace when the right word isn't there yet. This happens
// when changing "etc" to "etc.". // when changing "etc" to "etc.".

View File

@ -2902,7 +2902,7 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
} }
rmp->rmm_maxcol = syn_buf->b_p_smc; rmp->rmm_maxcol = syn_buf->b_p_smc;
r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL); r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL, NULL);
if (l_syn_time_on) { if (l_syn_time_on) {
pt = profile_end(pt); pt = profile_end(pt);

View File

@ -2508,9 +2508,9 @@ static int jumpto_tag(
save_lnum = curwin->w_cursor.lnum; save_lnum = curwin->w_cursor.lnum;
curwin->w_cursor.lnum = 0; /* start search before first line */ curwin->w_cursor.lnum = 0; /* start search before first line */
if (do_search(NULL, pbuf[0], pbuf + 1, (long)1, if (do_search(NULL, pbuf[0], pbuf + 1, (long)1,
search_options, NULL)) search_options, NULL, NULL)) {
retval = OK; retval = OK;
else { } else {
int found = 1; int found = 1;
int cc; int cc;
@ -2519,23 +2519,22 @@ static int jumpto_tag(
*/ */
p_ic = TRUE; p_ic = TRUE;
if (!do_search(NULL, pbuf[0], pbuf + 1, (long)1, if (!do_search(NULL, pbuf[0], pbuf + 1, (long)1,
search_options, NULL)) { search_options, NULL, NULL)) {
/* // Failed to find pattern, take a guess: "^func ("
* Failed to find pattern, take a guess: "^func ("
*/
found = 2; found = 2;
(void)test_for_static(&tagp); (void)test_for_static(&tagp);
cc = *tagp.tagname_end; cc = *tagp.tagname_end;
*tagp.tagname_end = NUL; *tagp.tagname_end = NUL;
sprintf((char *)pbuf, "^%s\\s\\*(", tagp.tagname); snprintf((char *)pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
if (!do_search(NULL, '/', pbuf, (long)1, if (!do_search(NULL, '/', pbuf, (long)1,
search_options, NULL)) { search_options, NULL, NULL)) {
/* Guess again: "^char * \<func (" */ // Guess again: "^char * \<func ("
sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", snprintf((char *)pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
tagp.tagname); tagp.tagname);
if (!do_search(NULL, '/', pbuf, (long)1, if (!do_search(NULL, '/', pbuf, (long)1,
search_options, NULL)) search_options, NULL, NULL)) {
found = 0; found = 0;
}
} }
*tagp.tagname_end = cc; *tagp.tagname_end = cc;
} }

View File

@ -32,6 +32,25 @@ function! Test_hlsearch()
enew! enew!
endfunction endfunction
func Test_hlsearch_hangs()
if !has('reltime') || !has('float')
return
endif
" This pattern takes a long time to match, it should timeout.
new
call setline(1, ['aaa', repeat('abc ', 100000), 'ccc'])
let start = reltime()
set hlsearch nolazyredraw redrawtime=101
let @/ = '\%#=1a*.*X\@<=b*'
redraw
let elapsed = reltimefloat(reltime(start))
call assert_true(elapsed > 0.1)
call assert_true(elapsed < 1.0)
set nohlsearch redrawtime&
bwipe!
endfunc
func Test_hlsearch_eol_highlight() func Test_hlsearch_eol_highlight()
new new
call append(1, repeat([''], 9)) call append(1, repeat([''], 9))

View File

@ -63,3 +63,13 @@ func Test_rex_init()
bwipe! bwipe!
set re=0 set re=0
endfunc endfunc
func Test_backref()
new
call setline(1, ['one', 'two', 'three', 'four', 'five'])
call assert_equal(3, search('\%#=1\(e\)\1'))
call assert_equal(3, search('\%#=2\(e\)\1'))
call assert_fails('call search("\\%#=1\\(e\\1\\)")', 'E65:')
call assert_fails('call search("\\%#=2\\(e\\1\\)")', 'E65:')
bwipe!
endfunc

View File

@ -223,7 +223,7 @@ func Test_statusline()
set statusline=ab%(cd%q%)de set statusline=ab%(cd%q%)de
call assert_match('^abde\s*$', s:get_statusline()) call assert_match('^abde\s*$', s:get_statusline())
copen copen
call assert_match('^abcd\[Quickfix List\1]de\s*$', s:get_statusline()) call assert_match('^abcd\[Quickfix List]de\s*$', s:get_statusline())
cclose cclose
" %#: Set highlight group. The name must follow and then a # again. " %#: Set highlight group. The name must follow and then a # again.