mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:8.0.0647: syntax highlighting can make cause a freeze
Problem: Syntax highlighting can make cause a freeze.
Solution: Apply 'redrawtime' to syntax highlighting, per window.
06f1ed2f78
This commit is contained in:
parent
eada8f5aaa
commit
07a182c6b5
@ -4528,10 +4528,14 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
'redrawtime' 'rdt' number (default 2000)
|
||||
global
|
||||
Time in milliseconds for redrawing the display. Applies to
|
||||
'hlsearch', 'inccommand' and |:match| highlighting.
|
||||
'hlsearch', 'inccommand', |:match| highlighting and syntax
|
||||
highlighting.
|
||||
When redrawing takes more than this many milliseconds no further
|
||||
matches will be highlighted. This is used to avoid that Vim hangs
|
||||
when using a very complicated pattern.
|
||||
matches will be highlighted.
|
||||
For syntax highlighting the time applies per window. When over the
|
||||
limit syntax highlighting is disabled until |CTRL-L| is used.
|
||||
This is used to avoid that Vim hangs when using a very complicated
|
||||
pattern.
|
||||
|
||||
*'regexpengine'* *'re'*
|
||||
'regexpengine' 're' number (default 0)
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user