diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2019-03-08 13:21:11 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2019-03-08 13:21:11 +0100 |
commit | 4352d41db0d93ab9266c64be48eda872cb5ea589 (patch) | |
tree | a10aa3db77c6db9c7c809200569a9bdd15119f92 /src | |
parent | 0355c1ed9c481d4ad3bf24887e0af869834e1b40 (diff) | |
parent | 96e2c3945f13453070894c70c74e4da29d421dab (diff) | |
download | rneovim-4352d41db0d93ab9266c64be48eda872cb5ea589.tar.gz rneovim-4352d41db0d93ab9266c64be48eda872cb5ea589.tar.bz2 rneovim-4352d41db0d93ab9266c64be48eda872cb5ea589.zip |
Merge #9662 'vim-patch:8.0.{0643-0646}'
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/edit.c | 9 | ||||
-rw-r--r-- | src/nvim/eval.c | 9 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 12 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/normal.c | 9 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 5 | ||||
-rw-r--r-- | src/nvim/regexp.c | 173 | ||||
-rw-r--r-- | src/nvim/regexp_defs.h | 8 | ||||
-rw-r--r-- | src/nvim/regexp_nfa.c | 63 | ||||
-rw-r--r-- | src/nvim/screen.c | 8 | ||||
-rw-r--r-- | src/nvim/search.c | 67 | ||||
-rw-r--r-- | src/nvim/spell.c | 5 | ||||
-rw-r--r-- | src/nvim/syntax.c | 2 | ||||
-rw-r--r-- | src/nvim/tag.c | 23 | ||||
-rw-r--r-- | src/nvim/testdir/test_hlsearch.vim | 19 | ||||
-rw-r--r-- | src/nvim/testdir/test_regexp_latin.vim | 10 | ||||
-rw-r--r-- | src/nvim/testdir/test_statusline.vim | 2 |
18 files changed, 272 insertions, 160 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 6b31406b0c..667bc54e2e 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3907,10 +3907,11 @@ static int ins_compl_get_exp(pos_T *ini) compl_direction, compl_pattern); } else found_new_match = searchit(NULL, ins_buf, pos, - compl_direction, - compl_pattern, 1L, SEARCH_KEEP + SEARCH_NFMSG, - RE_LAST, (linenr_T)0, NULL); - --msg_silent; + compl_direction, + compl_pattern, 1L, + SEARCH_KEEP + SEARCH_NFMSG, + RE_LAST, (linenr_T)0, NULL, NULL); + msg_silent--; if (!compl_started || set_match_pos) { /* set "compl_started" even on fail */ compl_started = TRUE; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7b6990e077..57777f049a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13806,7 +13806,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) pos = save_cursor = curwin->w_cursor; 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 (flags & SP_SUBPAT) retval = subpatnum; @@ -14308,10 +14308,11 @@ do_searchpair( pat = pat3; for (;; ) { n = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, lnum_stop, &tm); - if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) - /* didn't find it or found the first match again: FAIL */ + options, RE_SEARCH, lnum_stop, &tm, NULL); + if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) { + // didn't find it or found the first match again: FAIL break; + } if (firstpos.lnum == 0) firstpos = pos; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 2a5793f0d4..4356767cc9 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3432,7 +3432,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + (colnr_T)0, NULL, NULL); if (nmatch) { colnr_T copycol; colnr_T matchcol; @@ -3951,8 +3951,8 @@ skip: if (lastone || nmatch_tl > 0 || (nmatch = vim_regexec_multi(®match, curwin, - curbuf, sub_firstlnum, - matchcol, NULL)) == 0 + curbuf, sub_firstlnum, + matchcol, NULL, NULL)) == 0 || regmatch.startpos[0].lnum > 0) { if (new_start != NULL) { /* @@ -4016,7 +4016,7 @@ skip: } if (nmatch == -1 && !lastone) nmatch = vim_regexec_multi(®match, curwin, curbuf, - sub_firstlnum, matchcol, NULL); + sub_firstlnum, matchcol, NULL, NULL); /* * 5. break if there isn't another match in this line @@ -4314,7 +4314,7 @@ void ex_global(exarg_T *eap) if (global_busy) { lnum = curwin->w_cursor.lnum; match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + (colnr_T)0, NULL, NULL); if ((type == 'g' && match) || (type == 'v' && !match)) { 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++) { // a match on this line? match = vim_regexec_multi(®match, curwin, curbuf, lnum, - (colnr_T)0, NULL); + (colnr_T)0, NULL, NULL); if ((type == 'g' && match) || (type == 'v' && !match)) { ml_setmarked(lnum); ndone++; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6b39ad8e87..5b9b4fed12 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3699,7 +3699,7 @@ static linenr_T get_address(exarg_T *eap, } searchcmdlen = 0; if (!do_search(NULL, c, cmd, 1L, - SEARCH_HIS | SEARCH_MSG, NULL)) { + SEARCH_HIS | SEARCH_MSG, NULL, NULL)) { curwin->w_cursor = pos; cmd = NULL; goto error; @@ -3737,7 +3737,7 @@ static linenr_T get_address(exarg_T *eap, if (searchit(curwin, curbuf, &pos, *cmd == '?' ? BACKWARD : FORWARD, (char_u *)"", 1L, SEARCH_MSG, - i, (linenr_T)0, NULL) != FAIL) + i, (linenr_T)0, NULL, NULL) != FAIL) lnum = pos.lnum; else { cmd = NULL; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a50e18efce..8e6fc5ad4f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1061,7 +1061,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) s->i = searchit(curwin, curbuf, &t, next_match ? FORWARD : BACKWARD, pat, s->count, search_flags, - RE_SEARCH, 0, NULL); + RE_SEARCH, 0, NULL, NULL); emsg_off--; ui_busy_stop(); 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, search_flags, - &tm); + &tm, NULL); emsg_off--; // if interrupted while searching, behave like it failed if (got_int) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 01e7ef6781..f12abd362f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3782,9 +3782,10 @@ find_decl ( valid = false; (void)valid; // Avoid "dead assignment" warning. t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD, - pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL); - if (curwin->w_cursor.lnum >= old_pos.lnum) - t = false; /* match after start is failure too */ + pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL, NULL); + if (curwin->w_cursor.lnum >= old_pos.lnum) { + t = false; // match after start is failure too + } if (thisblock && t != false) { 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; 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) { clearop(cap->oap); } else { diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index f0c37c0e38..ee1cf2182f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2128,8 +2128,9 @@ win_found: save_cursor = curwin->w_cursor; curwin->w_cursor.lnum = 0; if (!do_search(NULL, '/', qf_ptr->qf_pattern, (long)1, - SEARCH_KEEP, NULL)) + SEARCH_KEEP, NULL, NULL)) { curwin->w_cursor = save_cursor; + } } if ((fdo_flags & FDO_QUICKFIX) && old_KeyTyped) @@ -3758,7 +3759,7 @@ void ex_vimgrep(exarg_T *eap) ++lnum) { col = 0; while (vim_regexec_multi(®match, curwin, buf, lnum, - col, NULL) > 0) { + col, NULL, NULL) > 0) { // Pass the buffer number so that it gets used even for a // dummy buffer, unless duplicate_name is set, then the // buffer will be wiped out below. diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index c043d4a173..ab1a7c7b3e 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1210,6 +1210,31 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp) 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 @@ -1928,22 +1953,8 @@ static char_u *regatom(int *flagp) int refnum; refnum = c - Magic('0'); - /* - * Check if the back reference is legal. We must have seen the - * 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")); + if (!seen_endbrace(refnum)) { + return NULL; } ret = regnode(BACKREF + refnum); } @@ -3297,7 +3308,7 @@ bt_regexec_nl ( rex.reg_icombine = false; 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); 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 /// otherwise. 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_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_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 - * lines ("line" is NULL, use reg_getline()). - * Returns 0 for failure, number of lines contained in the match otherwise. - */ +/// Match a regexp against a string ("line" points to the string) or multiple +/// lines ("line" is NULL, use reg_getline()). +/// @return 0 for failure, or number of lines contained in the match. static long bt_regexec_both(char_u *line, - colnr_T col, /* column to start looking for match */ - proftime_T *tm /* timeout limit or NULL */ - ) + colnr_T col, // column to start search + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag set on timeout or NULL { bt_regprog_T *prog; char_u *s; @@ -3483,7 +3493,7 @@ static long bt_regexec_both(char_u *line, && (utf_fold(prog->regstart) == utf_fold(c) || (c < 255 && prog->regstart < 255 && mb_tolower(prog->regstart) == mb_tolower(c))))) { - retval = regtry(prog, col); + retval = regtry(prog, col, tm, timed_out); } else { retval = 0; } @@ -3507,9 +3517,10 @@ static long bt_regexec_both(char_u *line, break; } - retval = regtry(prog, col); - if (retval > 0) + retval = regtry(prog, col, tm, timed_out); + if (retval > 0) { break; + } /* if not currently on the first line, get it again */ 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. */ if (tm != NULL && ++tm_count == 20) { tm_count = 0; - if (profile_passed_limit(*tm)) + if (profile_passed_limit(*tm)) { + if (timed_out != NULL) { + *timed_out = true; + } break; + } } } } @@ -3582,11 +3597,12 @@ void unref_extmatch(reg_extmatch_T *em) } } -/* - * regtry - try match of "prog" with at regline["col"]. - * Returns 0 for failure, number of lines contained in the match otherwise. - */ -static long regtry(bt_regprog_T *prog, colnr_T col) +/// Try match of "prog" with at regline["col"]. +/// @returns 0 for failure, or number of lines contained in the match. +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; need_clear_subexpr = TRUE; @@ -3594,8 +3610,9 @@ static long regtry(bt_regprog_T *prog, colnr_T col) if (prog->reghasz == REX_SET) need_clear_zsubexpr = TRUE; - if (regmatch(prog->program + 1) == 0) + if (regmatch(prog->program + 1, tm, timed_out) == 0) { return 0; + } cleanup_subexpr(); if (REG_MULTI) { @@ -3736,24 +3753,23 @@ static int reg_match_visual(void) static long bl_minval; static long bl_maxval; -/* - * regmatch - main matching routine - * - * 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, and then act accordingly. In practice we make some effort to - * 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 - * loop. - * - * Returns TRUE when there is a match. Leaves reginput and reglnum just after - * the last matched character. - * Returns FALSE when there is no match. Leaves reginput and reglnum in an - * undefined state! - */ -static int -regmatch ( - char_u *scan /* Current node. */ +/// Main matching routine +/// +/// 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, and then act accordingly. In practice we make some effort to +/// 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 +/// loop. +/// +/// Returns TRUE when there is a match. Leaves reginput and reglnum just after +/// the last matched character. +/// Returns FALSE when there is no match. Leaves reginput and reglnum in an +/// undefined state! +static int regmatch( + char_u *scan, // Current node. + proftime_T *tm, // timeout limit or NULL + int *timed_out // flag set on timeout or NULL ) { char_u *next; /* Next node. */ @@ -3761,15 +3777,16 @@ regmatch ( int c; regitem_T *rp; int no; - int status; /* one of the RA_ values: */ -#define RA_FAIL 1 /* something failed, abort */ -#define RA_CONT 2 /* continue in inner loop */ -#define RA_BREAK 3 /* break inner loop */ -#define RA_MATCH 4 /* successful match */ -#define RA_NOMATCH 5 /* didn't match */ - - /* Make "regstack" and "backpos" empty. They are allocated and freed in - * bt_regexec_both() to reduce malloc()/free() calls. */ + int status; // one of the RA_ values: + int tm_count = 0; +#define RA_FAIL 1 // something failed, abort +#define RA_CONT 2 // continue in inner loop +#define RA_BREAK 3 // break inner loop +#define RA_MATCH 4 // successful match +#define RA_NOMATCH 5 // didn't match + + // Make "regstack" and "backpos" empty. They are allocated and freed in + // bt_regexec_both() to reduce malloc()/free() calls. regstack.ga_len = 0; backpos.ga_len = 0; @@ -3797,6 +3814,17 @@ regmatch ( status = RA_FAIL; 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; #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 /// match otherwise. long vim_regexec_multi( - regmmatch_T *rmp, - win_T *win, /* window in which to search or NULL */ - buf_T *buf, /* buffer in which to search */ - linenr_T lnum, /* nr of line to start looking for match */ - colnr_T col, /* column to start looking for match */ - proftime_T *tm /* timeout limit or NULL */ + regmmatch_T *rmp, + win_T *win, // window in which to search or NULL + buf_T *buf, // buffer in which to search + linenr_T lnum, // nr of line to start looking for match + colnr_T col, // column to start looking for match + proftime_T *tm, // timeout limit or NULL + int *timed_out // flag is set when timeout limit reached ) { regexec_T rex_save; @@ -7293,7 +7322,7 @@ long vim_regexec_multi( rex_in_use = true; 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. if (rmp->regprog->re_engine == AUTOMATIC_ENGINE @@ -7313,7 +7342,7 @@ long vim_regexec_multi( if (rmp->regprog != NULL) { result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, - tm); + tm, timed_out); } xfree(pat); diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h index b5d56e07fc..298d82fc6b 100644 --- a/src/nvim/regexp_defs.h +++ b/src/nvim/regexp_defs.h @@ -156,11 +156,11 @@ struct reg_extmatch { }; struct regengine { - regprog_T *(*regcomp)(char_u*, int); + regprog_T *(*regcomp)(char_u *, int); void (*regfree)(regprog_T *); - int (*regexec_nl)(regmatch_T*, char_u*, colnr_T, bool); - long (*regexec_multi)(regmmatch_T*, win_T*, buf_T*, linenr_T, colnr_T, - proftime_T*); + int (*regexec_nl)(regmatch_T *, char_u *, colnr_T, bool); + long (*regexec_multi)(regmmatch_T *, win_T *, buf_T *, linenr_T, colnr_T, + proftime_T *, int *); char_u *expr; }; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 95030974d8..e0e8820b87 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1338,8 +1338,15 @@ static int nfa_regatom(void) case Magic('7'): case Magic('8'): 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; case Magic('z'): @@ -3568,6 +3575,7 @@ static char *pim_info(nfa_pim_T *pim) // Used during execution: whether a match has been found. static int nfa_match; static proftime_T *nfa_time_limit; +static int *nfa_timed_out; static int nfa_time_count; // 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 } +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. /// /// 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 return false; } - if (nfa_time_limit != NULL && profile_passed_limit(*nfa_time_limit)) { + if (nfa_did_time_out()) { #ifdef NFA_REGEXP_DEBUG_LOG fclose(debug); #endif @@ -5095,8 +5114,20 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, if (thislist->n == 0) break; - /* compute nextlist */ - for (listidx = 0; listidx < thislist->n; ++listidx) { + // compute nextlist + 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]; #ifdef NFA_REGEXP_DEBUG_LOG @@ -6218,7 +6249,7 @@ nextchar: // Check for timeout once every twenty times to avoid overhead. if (nfa_time_limit != NULL && ++nfa_time_count == 20) { nfa_time_count = 0; - if (profile_passed_limit(*nfa_time_limit)) { + if (nfa_did_time_out()) { break; } } @@ -6245,7 +6276,10 @@ theend: // Try match of "prog" with at regline["col"]. // 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; 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; nfa_time_limit = tm; + nfa_timed_out = timed_out; nfa_time_count = 0; #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 startcol Column to start looking for match /// @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 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; 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; } - retval = nfa_regtry(prog, col, tm); + retval = nfa_regtry(prog, col, tm, timed_out); nfa_regengine.expr = NULL; @@ -6596,7 +6633,7 @@ nfa_regexec_nl ( rex.reg_ic = rmp->rm_ic; rex.reg_icombine = false; 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. @@ -6608,6 +6645,7 @@ nfa_regexec_nl ( /// @param lnum Number of line to start looking for match /// @param col Column to start looking for match /// @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 /// otherwise. @@ -6634,7 +6672,8 @@ nfa_regexec_nl ( /// @par /// FIXME if this behavior is not compatible. 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_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_maxcol = rmp->rmm_maxcol; - return nfa_regexec_both(NULL, col, tm); + return nfa_regexec_both(NULL, col, tm, timed_out); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 08bb4e4a52..5255fd2a51 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5651,13 +5651,15 @@ next_search_hl ( && cur != NULL && shl == &cur->hl && cur->match.regprog == cur->hl.rm.regprog); + int timed_out = false; - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, &(shl->tm)); - /* Copy the regprog, in case it got freed and recompiled. */ + nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, + &(shl->tm), &timed_out); + // Copy the regprog, in case it got freed and recompiled. if (regprog_is_copy) { 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. if (shl == &search_hl) { // don't free regprog in the match list, it's a copy diff --git a/src/nvim/search.c b/src/nvim/search.c index f6b80d1b79..777ea07a21 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -523,9 +523,10 @@ int searchit( char_u *pat, long count, int options, - int pat_use, /* which pattern to use when "pat" is empty */ - linenr_T stop_lnum, /* stop after this line number when != 0 */ - proftime_T *tm /* timeout limit or NULL */ + int pat_use, // which pattern to use when "pat" is empty + linenr_T stop_lnum, // stop after this line number when != 0 + proftime_T *tm, // timeout limit or NULL + int *timed_out // set when timed out or NULL ) { int found; @@ -620,9 +621,9 @@ int searchit( // Look for a match somewhere in line "lnum". colnr_T col = at_first_line && (options & SEARCH_COL) ? pos->col : 0; nmatched = vim_regexec_multi(®match, win, buf, - lnum, col, tm); + lnum, col, tm, timed_out); // Abort searching on an error (e.g., out of stack). - if (called_emsg) { + if (called_emsg || (timed_out != NULL && *timed_out)) { break; } if (nmatched > 0) { @@ -686,8 +687,9 @@ int searchit( } if (ptr[matchcol] == NUL - || (nmatched = vim_regexec_multi(®match, win, buf, lnum, - matchcol, tm)) == 0) { + || (nmatched = vim_regexec_multi(®match, win, buf, + lnum, matchcol, tm, + timed_out)) == 0) { match_ok = false; break; } @@ -771,7 +773,7 @@ int searchit( if (ptr[matchcol] == NUL || (nmatched = vim_regexec_multi( ®match, win, buf, lnum + matchpos.lnum, matchcol, - tm)) == 0) { + tm, timed_out)) == 0) { // If the search timed out, we did find a match // but it might be the wrong one, so that's not // OK. @@ -855,30 +857,35 @@ int searchit( * twice. */ if (!p_ws || stop_lnum != 0 || got_int || called_emsg + || (timed_out != NULL && timed_out) || break_loop - || found || loop) + || found || loop) { 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 */ + } + // + // 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; - else + } else { lnum = 1; - if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) + } + if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) { 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 + || (timed_out != NULL && *timed_out) || break_loop - ) + ) { 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); @@ -965,7 +972,8 @@ int do_search( char_u *pat, long count, 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 */ @@ -1195,7 +1203,7 @@ int do_search( & (SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS + SEARCH_MSG + SEARCH_START + ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF)))), - RE_LAST, (linenr_T)0, tm); + RE_LAST, (linenr_T)0, tm, timed_out); if (dircp != NULL) *dircp = dirc; /* restore second '/' or '?' for normal_cmd() */ @@ -3978,7 +3986,7 @@ current_search( result = searchit(curwin, curbuf, &pos, (dir ? FORWARD : BACKWARD), 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 * beginning of the file (cursor might be on the search match) @@ -4025,7 +4033,7 @@ current_search( for (int i = 0; i < 2; i++) { result = searchit(curwin, curbuf, &pos, direction, spats[last_idx].pat, 0L, flags | SEARCH_KEEP, RE_SEARCH, - 0, NULL); + 0, NULL, NULL); // Search successfull, break out from the loop if (result) { break; @@ -4104,14 +4112,15 @@ static int is_one_char(char_u *pattern, bool move, pos_T *cur, flag = SEARCH_START; } 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 // start and end are in the same position. called_emsg = false; do { regmatch.startpos[0].col++; nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, regmatch.startpos[0].col, NULL); + pos.lnum, regmatch.startpos[0].col, + NULL, NULL); if (!nmatched) { break; } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index ff61c2e5de..ec4da88ea1 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3048,9 +3048,10 @@ void ex_spellrepall(exarg_T *eap) sub_nlines = 0; curwin->w_cursor.lnum = 0; while (!got_int) { - if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL) == 0 - || u_save_cursor() == FAIL) + if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP, NULL, NULL) == 0 + || u_save_cursor() == FAIL) { break; + } // Only replace when the right word isn't there yet. This happens // when changing "etc" to "etc.". diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index b6b7dfff11..0104f0d834 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -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; - 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) { pt = profile_end(pt); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 75bad047ac..410b9dfcbd 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2508,9 +2508,9 @@ static int jumpto_tag( save_lnum = curwin->w_cursor.lnum; curwin->w_cursor.lnum = 0; /* start search before first line */ if (do_search(NULL, pbuf[0], pbuf + 1, (long)1, - search_options, NULL)) + search_options, NULL, NULL)) { retval = OK; - else { + } else { int found = 1; int cc; @@ -2519,23 +2519,22 @@ static int jumpto_tag( */ p_ic = TRUE; if (!do_search(NULL, pbuf[0], pbuf + 1, (long)1, - search_options, NULL)) { - /* - * Failed to find pattern, take a guess: "^func (" - */ + search_options, NULL, NULL)) { + // Failed to find pattern, take a guess: "^func (" found = 2; (void)test_for_static(&tagp); cc = *tagp.tagname_end; *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, - search_options, NULL)) { - /* Guess again: "^char * \<func (" */ - sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", - tagp.tagname); + search_options, NULL, NULL)) { + // Guess again: "^char * \<func (" + snprintf((char *)pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(", + tagp.tagname); if (!do_search(NULL, '/', pbuf, (long)1, - search_options, NULL)) + search_options, NULL, NULL)) { found = 0; + } } *tagp.tagname_end = cc; } diff --git a/src/nvim/testdir/test_hlsearch.vim b/src/nvim/testdir/test_hlsearch.vim index 1fc7b04f88..97f6ae7b51 100644 --- a/src/nvim/testdir/test_hlsearch.vim +++ b/src/nvim/testdir/test_hlsearch.vim @@ -32,6 +32,25 @@ function! Test_hlsearch() enew! 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() new call append(1, repeat([''], 9)) diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 8f9b1eb7ca..0619e9c027 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -63,3 +63,13 @@ func Test_rex_init() bwipe! set re=0 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 diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index cf85bd58ac..351b119acd 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -223,7 +223,7 @@ func Test_statusline() set statusline=ab%(cd%q%)de call assert_match('^abde\s*$', s:get_statusline()) 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 " %#: Set highlight group. The name must follow and then a # again. |