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.
diff --git a/src/normal.c b/src/normal.c
index 74a5efe..fe1a182 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -5477,6 +5477,14 @@
#ifdef FEAT_SYN_HL
/* Clear all syntax states to force resyncing. */
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
redraw_later(CLEAR);
}
diff --git a/src/proto/syntax.pro b/src/proto/syntax.pro
index aae187f..a505ee7 100644
--- a/src/proto/syntax.pro
+++ b/src/proto/syntax.pro
@@ -1,5 +1,5 @@
/* 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_apply_changes(buf_T *buf);
void syntax_end_parsing(linenr_T lnum);
diff --git a/src/regexp.c b/src/regexp.c
index e13061f..c4745ce 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -5756,8 +5756,6 @@
printf("Premature EOL\n");
#endif
}
- if (status == RA_FAIL)
- got_int = TRUE;
return (status == RA_MATCH);
}
@@ -8224,8 +8222,6 @@
}
#endif
-static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, int nl);
-
/*
* Match a regexp against a string.
* "rmp->regprog" is a compiled regexp as returned by vim_regcomp().
@@ -8236,7 +8232,7 @@
* Return TRUE if there is a match, FALSE if not.
*/
static int
-vim_regexec_both(
+vim_regexec_string(
regmatch_T *rmp,
char_u *line, /* string to match against */
colnr_T col, /* column to start looking for match */
@@ -8299,12 +8295,12 @@
char_u *line,
colnr_T col)
{
- int r;
- regmatch_T regmatch;
+ int r;
+ regmatch_T regmatch;
regmatch.regprog = *prog;
regmatch.rm_ic = ignore_case;
- r = vim_regexec_both(®match, line, col, FALSE);
+ r = vim_regexec_string(®match, line, col, FALSE);
*prog = regmatch.regprog;
return r;
}
@@ -8316,7 +8312,7 @@
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);
}
#if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) \
@@ -8329,7 +8325,7 @@
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);
}
#endif
diff --git a/src/screen.c b/src/screen.c
index c6d8f0e..617051c 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -124,7 +124,7 @@
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);
#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);
#ifdef FEAT_RIGHTLEFT
static void screen_line(int row, int coloff, int endcol, int clear_width, int rlflag);
@@ -185,6 +185,11 @@
static int screen_char_attr = 0;
#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).
* Set must_redraw only if not already set to a higher value.
@@ -923,6 +928,9 @@
{
int row;
int j;
+#ifdef SYN_TIME_LIMIT
+ proftime_T syntax_tm;
+#endif
/* Don't do anything if the screen structures are (not yet) valid. */
if (!screen_valid(TRUE) || updating_screen)
@@ -931,6 +939,10 @@
if (lnum >= wp->w_topline && lnum < wp->w_botline
&& 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();
row = 0;
@@ -944,7 +956,13 @@
start_search_hl();
prepare_search_hl(wp, lnum);
# 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)
end_search_hl();
# endif
@@ -1140,6 +1158,9 @@
#if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA)
int save_got_int;
#endif
+#ifdef SYN_TIME_LIMIT
+ proftime_T syntax_tm;
+#endif
type = wp->w_redr_type;
@@ -1792,6 +1813,10 @@
save_got_int = got_int;
got_int = 0;
#endif
+#ifdef SYN_TIME_LIMIT
+ /* Set the time limit to 'redrawtime'. */
+ profile_setlimit(p_rdt, &syntax_tm);
+#endif
#ifdef FEAT_FOLDING
win_foldinfo.fi_level = 0;
#endif
@@ -2086,7 +2111,13 @@
/*
* 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
wp->w_lines[idx].wl_folded = FALSE;
@@ -2957,7 +2988,8 @@
linenr_T lnum,
int startrow,
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 */
unsigned off; /* offset in ScreenLines/ScreenAttrs */
@@ -3158,20 +3190,29 @@
extra_check = 0;
#endif
#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
* 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;
+#ifdef SYN_TIME_LIMIT
+ if (!wp->w_s->b_syn_slow)
+#endif
+ {
+ has_syntax = TRUE;
+ extra_check = TRUE;
+ }
}
}
@@ -3548,7 +3589,7 @@
# ifdef FEAT_SYN_HL
/* Need to restart syntax highlighting for this line. */
if (has_syntax)
- syntax_start(wp, lnum);
+ syntax_start(wp, lnum, syntax_tm);
# endif
}
#endif
@@ -4491,6 +4532,10 @@
}
else
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
* have made it invalid. */
diff --git a/src/structs.h b/src/structs.h
index d79b225..c7aa8f2 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1797,6 +1797,9 @@
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 */
+# ifdef FEAT_RELTIME
+ int b_syn_slow; /* TRUE when 'redrawtime' reached */
+# endif
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/syntax.c b/src/syntax.c
index a7b3084..6aec63d 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -367,6 +367,9 @@
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 */
+#ifdef FEAT_RELTIME
+static proftime_T *syn_tm;
+#endif
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
@@ -494,7 +497,7 @@
* window.
*/
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 *last_valid = NULL;
@@ -524,6 +527,9 @@
}
changedtick = CHANGEDTICK(syn_buf);
syn_win = wp;
+#ifdef FEAT_RELTIME
+ syn_tm = syntax_tm;
+#endif
/*
* Allocate syntax stack when needed.
@@ -3295,6 +3301,9 @@
syn_time_T *st UNUSED)
{
int r;
+#ifdef FEAT_RELTIME
+ int timed_out = FALSE;
+#endif
#ifdef FEAT_PROFILE
proftime_T pt;
@@ -3303,7 +3312,13 @@
#endif
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
if (syn_time_on)
@@ -3317,6 +3332,10 @@
++st->match;
}
#endif
+#ifdef FEAT_RELTIME
+ if (timed_out)
+ syn_win->w_s->b_syn_slow = TRUE;
+#endif
if (r > 0)
{
@@ -3575,6 +3594,9 @@
int i;
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_spell = SYNSPL_DEFAULT; /* default spell checking */
block->b_syn_containedin = FALSE;
@@ -6542,7 +6564,7 @@
if (wp->w_buffer != syn_buf
|| lnum != current_lnum
|| col < current_col)
- syntax_start(wp, lnum);
+ syntax_start(wp, lnum, NULL);
else if (wp->w_buffer == syn_buf
&& lnum == current_lnum
&& col > current_col)
@@ -6611,9 +6633,14 @@
int i;
/* 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)
if (CUR_STATE(i).si_flags & HL_FOLD)
diff --git a/src/testdir/test_syntax.vim b/src/testdir/test_syntax.vim
index eb093c8..427ed7b 100644
--- a/src/testdir/test_syntax.vim
+++ b/src/testdir/test_syntax.vim
@@ -424,3 +424,37 @@
hi Normal ctermbg=12
call assert_equal('dark', &bg)
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
diff --git a/src/version.c b/src/version.c
index 4683ba4..59f0d09 100644
--- a/src/version.c
+++ b/src/version.c
@@ -765,6 +765,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 647,
+/**/
646,
/**/
645,