1
0
forked from aniani/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.
This commit is contained in:
Bram Moolenaar
2017-06-18 22:41:03 +02:00
parent 0946326580
commit 06f1ed2f78
9 changed files with 148 additions and 29 deletions

View File

@@ -1,4 +1,4 @@
*options.txt* For Vim version 8.0. Last change: 2017 Jun 13 *options.txt* For Vim version 8.0. Last change: 2017 Jun 18
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -5945,10 +5945,14 @@ A jump table for the options with a short description can be found at |Q_op|.
{only available when compiled with the |+reltime| {only available when compiled with the |+reltime|
feature} feature}
The time in milliseconds for redrawing the display. This applies to The time in milliseconds for redrawing the display. This applies to
searching for patterns for 'hlsearch' and |:match| highlighting. searching for patterns for 'hlsearch', |:match| highlighting an syntax
highlighting.
When redrawing takes more than this many milliseconds no further When redrawing takes more than this many milliseconds no further
matches will be highlighted. This is used to avoid that Vim hangs matches will be highlighted.
when using a very complicated pattern. 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'*
'regexpengine' 're' number (default 0) 'regexpengine' 're' number (default 0)

View File

@@ -5477,6 +5477,14 @@ nv_clear(cmdarg_T *cap)
#ifdef FEAT_SYN_HL #ifdef FEAT_SYN_HL
/* Clear all syntax states to force resyncing. */ /* Clear all syntax states to force resyncing. */
syn_stack_free_all(curwin->w_s); syn_stack_free_all(curwin->w_s);
# ifdef FEAT_RELTIME
{
win_T *wp;
FOR_ALL_WINDOWS(wp)
wp->w_s->b_syn_slow = FALSE;
}
# endif
#endif #endif
redraw_later(CLEAR); redraw_later(CLEAR);
} }

View File

@@ -1,5 +1,5 @@
/* syntax.c */ /* syntax.c */
void syntax_start(win_T *wp, linenr_T lnum); void syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm);
void syn_stack_free_all(synblock_T *block); void syn_stack_free_all(synblock_T *block);
void syn_stack_apply_changes(buf_T *buf); void syn_stack_apply_changes(buf_T *buf);
void syntax_end_parsing(linenr_T lnum); void syntax_end_parsing(linenr_T lnum);

View File

@@ -5756,8 +5756,6 @@ regmatch(
printf("Premature EOL\n"); printf("Premature EOL\n");
#endif #endif
} }
if (status == RA_FAIL)
got_int = TRUE;
return (status == RA_MATCH); return (status == RA_MATCH);
} }
@@ -8224,8 +8222,6 @@ report_re_switch(char_u *pat)
} }
#endif #endif
static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, int nl);
/* /*
* Match a regexp against a string. * Match a regexp against a string.
* "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). * "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
@@ -8236,7 +8232,7 @@ static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, int nl);
* Return TRUE if there is a match, FALSE if not. * Return TRUE if there is a match, FALSE if not.
*/ */
static int static int
vim_regexec_both( vim_regexec_string(
regmatch_T *rmp, regmatch_T *rmp,
char_u *line, /* string to match against */ char_u *line, /* string to match against */
colnr_T col, /* column to start looking for match */ colnr_T col, /* column to start looking for match */
@@ -8299,12 +8295,12 @@ vim_regexec_prog(
char_u *line, char_u *line,
colnr_T col) colnr_T col)
{ {
int r; int r;
regmatch_T regmatch; regmatch_T regmatch;
regmatch.regprog = *prog; regmatch.regprog = *prog;
regmatch.rm_ic = ignore_case; regmatch.rm_ic = ignore_case;
r = vim_regexec_both(&regmatch, line, col, FALSE); r = vim_regexec_string(&regmatch, line, col, FALSE);
*prog = regmatch.regprog; *prog = regmatch.regprog;
return r; return r;
} }
@@ -8316,7 +8312,7 @@ vim_regexec_prog(
int int
vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col) 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);
} }
#if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) \ #if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) \
@@ -8329,7 +8325,7 @@ vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
int int
vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col) 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);
} }
#endif #endif

View File

@@ -124,7 +124,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T
static void fill_foldcolumn(char_u *p, win_T *wp, int closed, linenr_T lnum); static void fill_foldcolumn(char_u *p, win_T *wp, int closed, linenr_T lnum);
static void copy_text_attr(int off, char_u *buf, int len, int attr); static void copy_text_attr(int off, char_u *buf, int len, int attr);
#endif #endif
static int win_line(win_T *, linenr_T, int, int, int nochange); static int win_line(win_T *, linenr_T, int, int, int nochange, proftime_T *syntax_tm);
static int char_needs_redraw(int off_from, int off_to, int cols); static int char_needs_redraw(int off_from, int off_to, int cols);
#ifdef FEAT_RIGHTLEFT #ifdef FEAT_RIGHTLEFT
static void screen_line(int row, int coloff, int endcol, int clear_width, int rlflag); static void screen_line(int row, int coloff, int endcol, int clear_width, int rlflag);
@@ -185,6 +185,11 @@ static void win_redr_ruler(win_T *wp, int always);
static int screen_char_attr = 0; static int screen_char_attr = 0;
#endif #endif
#if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME)
/* Can limit syntax highlight time to 'redrawtime'. */
# define SYN_TIME_LIMIT 1
#endif
/* /*
* Redraw the current window later, with update_screen(type). * Redraw the current window later, with update_screen(type).
* Set must_redraw only if not already set to a higher value. * Set must_redraw only if not already set to a higher value.
@@ -923,6 +928,9 @@ update_single_line(win_T *wp, linenr_T lnum)
{ {
int row; int row;
int j; int j;
#ifdef SYN_TIME_LIMIT
proftime_T syntax_tm;
#endif
/* Don't do anything if the screen structures are (not yet) valid. */ /* Don't do anything if the screen structures are (not yet) valid. */
if (!screen_valid(TRUE) || updating_screen) if (!screen_valid(TRUE) || updating_screen)
@@ -931,6 +939,10 @@ update_single_line(win_T *wp, linenr_T lnum)
if (lnum >= wp->w_topline && lnum < wp->w_botline if (lnum >= wp->w_topline && lnum < wp->w_botline
&& foldedCount(wp, lnum, &win_foldinfo) == 0) && foldedCount(wp, lnum, &win_foldinfo) == 0)
{ {
#ifdef SYN_TIME_LIMIT
/* Set the time limit to 'redrawtime'. */
profile_setlimit(p_rdt, &syntax_tm);
#endif
update_prepare(); update_prepare();
row = 0; row = 0;
@@ -944,7 +956,13 @@ update_single_line(win_T *wp, linenr_T lnum)
start_search_hl(); start_search_hl();
prepare_search_hl(wp, lnum); prepare_search_hl(wp, lnum);
# endif # endif
win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE); win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE,
#ifdef SYN_TIME_LIMIT
&syntax_tm
#else
NULL
#endif
);
# if defined(FEAT_SEARCH_EXTRA) # if defined(FEAT_SEARCH_EXTRA)
end_search_hl(); end_search_hl();
# endif # endif
@@ -1140,6 +1158,9 @@ win_update(win_T *wp)
#if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
int save_got_int; int save_got_int;
#endif #endif
#ifdef SYN_TIME_LIMIT
proftime_T syntax_tm;
#endif
type = wp->w_redr_type; type = wp->w_redr_type;
@@ -1792,6 +1813,10 @@ win_update(win_T *wp)
save_got_int = got_int; save_got_int = got_int;
got_int = 0; got_int = 0;
#endif #endif
#ifdef SYN_TIME_LIMIT
/* Set the time limit to 'redrawtime'. */
profile_setlimit(p_rdt, &syntax_tm);
#endif
#ifdef FEAT_FOLDING #ifdef FEAT_FOLDING
win_foldinfo.fi_level = 0; win_foldinfo.fi_level = 0;
#endif #endif
@@ -2086,7 +2111,13 @@ win_update(win_T *wp)
/* /*
* Display one line. * Display one line.
*/ */
row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0); row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0,
#ifdef SYN_TIME_LIMIT
&syntax_tm
#else
NULL
#endif
);
#ifdef FEAT_FOLDING #ifdef FEAT_FOLDING
wp->w_lines[idx].wl_folded = FALSE; wp->w_lines[idx].wl_folded = FALSE;
@@ -2957,7 +2988,8 @@ win_line(
linenr_T lnum, linenr_T lnum,
int startrow, int startrow,
int endrow, int endrow,
int nochange UNUSED) /* not updating for changed text */ int nochange UNUSED, /* not updating for changed text */
proftime_T *syntax_tm)
{ {
int col = 0; /* visual column on screen */ int col = 0; /* visual column on screen */
unsigned off; /* offset in ScreenLines/ScreenAttrs */ unsigned off; /* offset in ScreenLines/ScreenAttrs */
@@ -3158,20 +3190,29 @@ win_line(
extra_check = 0; extra_check = 0;
#endif #endif
#ifdef FEAT_SYN_HL #ifdef FEAT_SYN_HL
if (syntax_present(wp) && !wp->w_s->b_syn_error) if (syntax_present(wp) && !wp->w_s->b_syn_error
# ifdef SYN_TIME_LIMIT
&& !wp->w_s->b_syn_slow
# endif
)
{ {
/* Prepare for syntax highlighting in this line. When there is an /* Prepare for syntax highlighting in this line. When there is an
* error, stop syntax highlighting. */ * error, stop syntax highlighting. */
save_did_emsg = did_emsg; save_did_emsg = did_emsg;
did_emsg = FALSE; did_emsg = FALSE;
syntax_start(wp, lnum); syntax_start(wp, lnum, syntax_tm);
if (did_emsg) if (did_emsg)
wp->w_s->b_syn_error = TRUE; wp->w_s->b_syn_error = TRUE;
else else
{ {
did_emsg = save_did_emsg; did_emsg = save_did_emsg;
has_syntax = TRUE; #ifdef SYN_TIME_LIMIT
extra_check = TRUE; if (!wp->w_s->b_syn_slow)
#endif
{
has_syntax = TRUE;
extra_check = TRUE;
}
} }
} }
@@ -3548,7 +3589,7 @@ win_line(
# ifdef FEAT_SYN_HL # ifdef FEAT_SYN_HL
/* Need to restart syntax highlighting for this line. */ /* Need to restart syntax highlighting for this line. */
if (has_syntax) if (has_syntax)
syntax_start(wp, lnum); syntax_start(wp, lnum, syntax_tm);
# endif # endif
} }
#endif #endif
@@ -4491,6 +4532,10 @@ win_line(
} }
else else
did_emsg = save_did_emsg; did_emsg = save_did_emsg;
#ifdef SYN_TIME_LIMIT
if (wp->w_s->b_syn_slow)
has_syntax = FALSE;
#endif
/* Need to get the line again, a multi-line regexp may /* Need to get the line again, a multi-line regexp may
* have made it invalid. */ * have made it invalid. */

View File

@@ -1797,6 +1797,9 @@ typedef struct {
hashtab_T b_keywtab; /* syntax keywords hash table */ hashtab_T b_keywtab; /* syntax keywords hash table */
hashtab_T b_keywtab_ic; /* idem, ignore case */ hashtab_T b_keywtab_ic; /* idem, ignore case */
int b_syn_error; /* TRUE when error occurred in HL */ int b_syn_error; /* TRUE when error occurred in HL */
# ifdef FEAT_RELTIME
int b_syn_slow; /* TRUE when 'redrawtime' reached */
# endif
int b_syn_ic; /* ignore case for :syn cmds */ int b_syn_ic; /* ignore case for :syn cmds */
int b_syn_spell; /* SYNSPL_ values */ int b_syn_spell; /* SYNSPL_ values */
garray_T b_syn_patterns; /* table for syntax patterns */ garray_T b_syn_patterns; /* table for syntax patterns */

View File

@@ -367,6 +367,9 @@ static reg_extmatch_T *next_match_extmatch = NULL;
static win_T *syn_win; /* current window for highlighting */ static win_T *syn_win; /* current window for highlighting */
static buf_T *syn_buf; /* current buffer for highlighting */ static buf_T *syn_buf; /* current buffer for highlighting */
static synblock_T *syn_block; /* current buffer for highlighting */ static synblock_T *syn_block; /* current buffer for highlighting */
#ifdef FEAT_RELTIME
static proftime_T *syn_tm;
#endif
static linenr_T current_lnum = 0; /* lnum of current state */ static linenr_T current_lnum = 0; /* lnum of current state */
static colnr_T current_col = 0; /* column of current state */ static colnr_T current_col = 0; /* column of current state */
static int current_state_stored = 0; /* TRUE if stored current state static int current_state_stored = 0; /* TRUE if stored current state
@@ -494,7 +497,7 @@ static void syn_incl_toplevel(int id, int *flagsp);
* window. * window.
*/ */
void void
syntax_start(win_T *wp, linenr_T lnum) syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm UNUSED)
{ {
synstate_T *p; synstate_T *p;
synstate_T *last_valid = NULL; synstate_T *last_valid = NULL;
@@ -524,6 +527,9 @@ syntax_start(win_T *wp, linenr_T lnum)
} }
changedtick = CHANGEDTICK(syn_buf); changedtick = CHANGEDTICK(syn_buf);
syn_win = wp; syn_win = wp;
#ifdef FEAT_RELTIME
syn_tm = syntax_tm;
#endif
/* /*
* Allocate syntax stack when needed. * Allocate syntax stack when needed.
@@ -3295,6 +3301,9 @@ syn_regexec(
syn_time_T *st UNUSED) syn_time_T *st UNUSED)
{ {
int r; int r;
#ifdef FEAT_RELTIME
int timed_out = FALSE;
#endif
#ifdef FEAT_PROFILE #ifdef FEAT_PROFILE
proftime_T pt; proftime_T pt;
@@ -3303,7 +3312,13 @@ syn_regexec(
#endif #endif
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, NULL); r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col,
#ifdef FEAT_RELTIME
syn_tm, &timed_out
#else
NULL, NULL
#endif
);
#ifdef FEAT_PROFILE #ifdef FEAT_PROFILE
if (syn_time_on) if (syn_time_on)
@@ -3317,6 +3332,10 @@ syn_regexec(
++st->match; ++st->match;
} }
#endif #endif
#ifdef FEAT_RELTIME
if (timed_out)
syn_win->w_s->b_syn_slow = TRUE;
#endif
if (r > 0) if (r > 0)
{ {
@@ -3575,6 +3594,9 @@ syntax_clear(synblock_T *block)
int i; int i;
block->b_syn_error = FALSE; /* clear previous error */ block->b_syn_error = FALSE; /* clear previous error */
#ifdef FEAT_RELTIME
block->b_syn_slow = FALSE; /* clear previous timeout */
#endif
block->b_syn_ic = FALSE; /* Use case, by default */ block->b_syn_ic = FALSE; /* Use case, by default */
block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */ block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
block->b_syn_containedin = FALSE; block->b_syn_containedin = FALSE;
@@ -6542,7 +6564,7 @@ syn_get_id(
if (wp->w_buffer != syn_buf if (wp->w_buffer != syn_buf
|| lnum != current_lnum || lnum != current_lnum
|| col < current_col) || col < current_col)
syntax_start(wp, lnum); syntax_start(wp, lnum, NULL);
else if (wp->w_buffer == syn_buf else if (wp->w_buffer == syn_buf
&& lnum == current_lnum && lnum == current_lnum
&& col > current_col) && col > current_col)
@@ -6611,9 +6633,14 @@ syn_get_foldlevel(win_T *wp, long lnum)
int i; int i;
/* Return quickly when there are no fold items at all. */ /* Return quickly when there are no fold items at all. */
if (wp->w_s->b_syn_folditems != 0) if (wp->w_s->b_syn_folditems != 0
&& !wp->w_s->b_syn_error
# ifdef SYN_TIME_LIMIT
&& !wp->w_s->b_syn_slow
# endif
)
{ {
syntax_start(wp, lnum); syntax_start(wp, lnum, NULL);
for (i = 0; i < current_state.ga_len; ++i) for (i = 0; i < current_state.ga_len; ++i)
if (CUR_STATE(i).si_flags & HL_FOLD) if (CUR_STATE(i).si_flags & HL_FOLD)

View File

@@ -424,3 +424,37 @@ func Test_bg_detection()
hi Normal ctermbg=12 hi Normal ctermbg=12
call assert_equal('dark', &bg) call assert_equal('dark', &bg)
endfunc 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

View File

@@ -764,6 +764,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
647,
/**/ /**/
646, 646,
/**/ /**/