diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/normal.c | 3 | ||||
-rw-r--r-- | src/nvim/regexp.c | 10 | ||||
-rw-r--r-- | src/nvim/screen.c | 27 | ||||
-rw-r--r-- | src/nvim/syntax.c | 20 | ||||
-rw-r--r-- | src/nvim/testdir/test_syntax.vim | 34 |
6 files changed, 75 insertions, 20 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2e6f24d9c4..28f3889dd3 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -391,6 +391,7 @@ typedef struct { hashtab_T b_keywtab; /* syntax keywords hash table */ hashtab_T b_keywtab_ic; /* idem, ignore case */ int b_syn_error; /* TRUE when error occurred in HL */ + bool b_syn_slow; // true when 'redrawtime' reached int b_syn_ic; /* ignore case for :syn cmds */ int b_syn_spell; /* SYNSPL_ values */ garray_T b_syn_patterns; /* table for syntax patterns */ diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 5217db5b42..ca586cca29 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4642,6 +4642,9 @@ static void nv_clear(cmdarg_T *cap) if (!checkclearop(cap->oap)) { /* Clear all syntax states to force resyncing. */ syn_stack_free_all(curwin->w_s); + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + wp->w_s->b_syn_slow = false; + } redraw_later(CLEAR); } } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index c602e7df0f..187609cd22 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -5098,8 +5098,6 @@ static int regmatch( printf("Premature EOL\n"); #endif } - if (status == RA_FAIL) - got_int = TRUE; return status == RA_MATCH; } @@ -7226,7 +7224,7 @@ static void report_re_switch(char_u *pat) /// @param nl /// /// @return TRUE if there is a match, FALSE if not. -static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl) +static int vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool nl) { regexec_T rex_save; bool rex_in_use_save = rex_in_use; @@ -7276,7 +7274,7 @@ int vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line, colnr_T col) { regmatch_T regmatch = {.regprog = *prog, .rm_ic = ignore_case}; - int r = vim_regexec_both(®match, line, col, false); + int r = vim_regexec_string(®match, line, col, false); *prog = regmatch.regprog; return r; } @@ -7285,7 +7283,7 @@ int vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line, // Return TRUE if there is a match, FALSE if not. int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col) { - return vim_regexec_both(rmp, line, col, false); + return vim_regexec_string(rmp, line, col, false); } // Like vim_regexec(), but consider a "\n" in "line" to be a line break. @@ -7293,7 +7291,7 @@ int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col) // Return TRUE if there is a match, FALSE if not. int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col) { - return vim_regexec_both(rmp, line, col, true); + return vim_regexec_string(rmp, line, col, true); } /// Match a regexp against multiple lines. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index b4ebf2ece5..8a51a0a21e 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1135,6 +1135,8 @@ static void win_update(win_T *wp) /* reset got_int, otherwise regexp won't work */ save_got_int = got_int; got_int = 0; + // Set the time limit to 'redrawtime'. + proftime_T syntax_tm = profile_setlimit(p_rdt); win_foldinfo.fi_level = 0; /* @@ -1362,7 +1364,7 @@ static void win_update(win_T *wp) /* * Display one line. */ - row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false); + row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false, &syntax_tm); wp->w_lines[idx].wl_folded = FALSE; wp->w_lines[idx].wl_lastlnum = lnum; @@ -1393,7 +1395,7 @@ static void win_update(win_T *wp) if (fold_count != 0) { fold_line(wp, fold_count, &win_foldinfo, lnum, row); } else { - (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true); + (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, &syntax_tm); } } @@ -2041,7 +2043,8 @@ win_line ( int startrow, int endrow, bool nochange, // not updating for changed text - bool number_only // only update the number column + bool number_only, // only update the number column + proftime_T *syntax_tm ) { int c = 0; // init for GCC @@ -2189,18 +2192,20 @@ win_line ( // To speed up the loop below, set extra_check when there is linebreak, // trailing white space and/or syntax processing to be done. extra_check = wp->w_p_lbr; - if (syntax_present(wp) && !wp->w_s->b_syn_error) { + if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow) { // Prepare for syntax highlighting in this line. When there is an // error, stop syntax highlighting. save_did_emsg = did_emsg; did_emsg = false; - syntax_start(wp, lnum); + syntax_start(wp, lnum, syntax_tm); if (did_emsg) { wp->w_s->b_syn_error = true; } else { did_emsg = save_did_emsg; - has_syntax = true; - extra_check = true; + if (!wp->w_s->b_syn_slow) { + has_syntax = true; + extra_check = true; + } } } @@ -2540,8 +2545,9 @@ win_line ( wp->w_cursor = pos; /* Need to restart syntax highlighting for this line. */ - if (has_syntax) - syntax_start(wp, lnum); + if (has_syntax) { + syntax_start(wp, lnum, syntax_tm); + } } } @@ -2587,6 +2593,9 @@ win_line ( } next_search_hl(wp, shl, lnum, (colnr_T)v, shl == &search_hl ? NULL : cur); + if (wp->w_s->b_syn_slow) { + has_syntax = false; + } // Need to get the line again, a multi-line regexp may have made it // invalid. diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 8c3ce823d3..1d959027f6 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -359,6 +359,7 @@ static reg_extmatch_T *next_match_extmatch = NULL; static win_T *syn_win; /* current window for highlighting */ static buf_T *syn_buf; /* current buffer for highlighting */ static synblock_T *syn_block; /* current buffer for highlighting */ +static proftime_T *syn_tm; static linenr_T current_lnum = 0; /* lnum of current state */ static colnr_T current_col = 0; /* column of current state */ static int current_state_stored = 0; /* TRUE if stored current state @@ -384,7 +385,7 @@ static int syn_time_on = FALSE; * it. Careful: curbuf and curwin are likely to point to another buffer and * window. */ -void syntax_start(win_T *wp, linenr_T lnum) +void syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm) { synstate_T *p; synstate_T *last_valid = NULL; @@ -411,6 +412,7 @@ void syntax_start(win_T *wp, linenr_T lnum) } changedtick = buf_get_changedtick(syn_buf); syn_win = wp; + syn_tm = syntax_tm; /* * Allocate syntax stack when needed. @@ -2887,6 +2889,7 @@ static char_u *syn_getcurline(void) static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st) { int r; + int timed_out = 0; proftime_T pt; const int l_syn_time_on = syn_time_on; @@ -2902,7 +2905,8 @@ 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, NULL); + r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, + syn_tm, &timed_out); if (l_syn_time_on) { pt = profile_end(pt); @@ -2914,6 +2918,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T if (r > 0) ++st->match; } + if (timed_out) { + syn_win->w_s->b_syn_slow = true; + } if (r > 0) { rmp->startpos[0].lnum += lnum; @@ -3144,6 +3151,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing) void syntax_clear(synblock_T *block) { block->b_syn_error = false; // clear previous error + block->b_syn_slow = false; // clear previous timeout block->b_syn_ic = false; // Use case, by default block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking block->b_syn_containedin = false; @@ -5682,7 +5690,7 @@ int syn_get_id( // When the position is not after the current position and in the same // line of the same buffer, need to restart parsing. if (wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) { - syntax_start(wp, lnum); + syntax_start(wp, lnum, NULL); } else if (col > current_col) { // next_match may not be correct when moving around, e.g. with the // "skip" expression in searchpair() @@ -5757,8 +5765,10 @@ int syn_get_foldlevel(win_T *wp, long lnum) int level = 0; /* Return quickly when there are no fold items at all. */ - if (wp->w_s->b_syn_folditems != 0) { - syntax_start(wp, lnum); + if (wp->w_s->b_syn_folditems != 0 + && !wp->w_s->b_syn_error + && !wp->w_s->b_syn_slow) { + syntax_start(wp, lnum, NULL); for (int i = 0; i < current_state.ga_len; ++i) { if (CUR_STATE(i).si_flags & HL_FOLD) { diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 6978faeb7b..850947f89f 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -503,3 +503,37 @@ func Test_syn_wrong_z_one() " call test_override("ALL", 0) bwipe! endfunc + +func Test_syntax_hangs() + if !has('reltime') || !has('float') || !has('syntax') + return + endif + + " This pattern takes a long time to match, it should timeout. + new + call setline(1, ['aaa', repeat('abc ', 1000), 'ccc']) + let start = reltime() + set nolazyredraw redrawtime=101 + syn match Error /\%#=1a*.*X\@<=b*/ + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed > 0.1) + call assert_true(elapsed < 1.0) + + " second time syntax HL is disabled + let start = reltime() + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed < 0.1) + + " after CTRL-L the timeout flag is reset + let start = reltime() + exe "normal \<C-L>" + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed > 0.1) + call assert_true(elapsed < 1.0) + + set redrawtime& + bwipe! +endfunc |