]> granicus.if.org Git - vim/commitdiff
patch 8.0.0647: syntax highlighting can make cause a freeze v8.0.0647
authorBram Moolenaar <Bram@vim.org>
Sun, 18 Jun 2017 20:41:03 +0000 (22:41 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 18 Jun 2017 20:41:03 +0000 (22:41 +0200)
Problem:    Syntax highlighting can make cause a freeze.
Solution:   Apply 'redrawtime' to syntax highlighting, per window.

runtime/doc/options.txt
src/normal.c
src/proto/syntax.pro
src/regexp.c
src/screen.c
src/structs.h
src/syntax.c
src/testdir/test_syntax.vim
src/version.c

index b310b9fd2c50f42fadaa5cd03855c5a891020f08..4a775fc7c480467d27e781d9dbd7942603df2688 100644 (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
@@ -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|
                        feature}
        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
-       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)
index 74a5efec99399f6f10a7787e3911b5663156f364..fe1a1829f0fbedc232070aee171adc17c5d54d33 100644 (file)
@@ -5477,6 +5477,14 @@ nv_clear(cmdarg_T *cap)
 #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);
     }
index aae187fe6859340261492febdd657afc0d1f309d..a505ee7a17623636ffbc91ce57c15cd738f8d088 100644 (file)
@@ -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);
index e13061fd37311e7cf7bad17ca622bcdfe5868134..c4745ce7a4109e6d268e5103e2037c823fec757c 100644 (file)
@@ -5756,8 +5756,6 @@ regmatch(
            printf("Premature EOL\n");
 #endif
        }
-       if (status == RA_FAIL)
-           got_int = TRUE;
        return (status == RA_MATCH);
     }
 
@@ -8224,8 +8222,6 @@ report_re_switch(char_u *pat)
 }
 #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 @@ 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.
  */
     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 @@ vim_regexec_prog(
     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(&regmatch, line, col, FALSE);
+    r = vim_regexec_string(&regmatch, line, col, FALSE);
     *prog = regmatch.regprog;
     return r;
 }
@@ -8316,7 +8312,7 @@ vim_regexec_prog(
     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 @@ vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col)
     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
 
index c6d8f0ee4adbe0d53f2d45ba736a562c533eb8bc..617051c95ec73f610b6768161d44a9fb21914e56 100644 (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 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 void win_redr_ruler(win_T *wp, int always);
 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 @@ update_single_line(win_T *wp, linenr_T lnum)
 {
     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 @@ update_single_line(win_T *wp, linenr_T lnum)
     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 @@ update_single_line(win_T *wp, linenr_T lnum)
                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 @@ win_update(win_T *wp)
 #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 @@ win_update(win_T *wp)
     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 @@ win_update(win_T *wp)
                /*
                 * 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 @@ win_line(
     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 @@ win_line(
     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 @@ win_line(
 # 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 @@ win_line(
                    }
                    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. */
index d79b2253f730562e9618f5c891ae48bb3d63688d..c7aa8f2eb3b46256e770149cdc9f2b4d1fd7554e 100644 (file)
@@ -1797,6 +1797,9 @@ 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 */
+# 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 */
index a7b308487f506a5109cb0ced6a045ddb388418ed..6aec63d32dc5db2416925d6e30187cd37e12f470 100644 (file)
@@ -367,6 +367,9 @@ 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 */
+#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 @@ static void syn_incl_toplevel(int id, int *flagsp);
  * 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 @@ syntax_start(win_T *wp, linenr_T lnum)
     }
     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_regexec(
     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 @@ syn_regexec(
 #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 @@ syn_regexec(
            ++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 @@ syntax_clear(synblock_T *block)
     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 @@ syn_get_id(
     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 @@ syn_get_foldlevel(win_T *wp, long lnum)
     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)
index eb093c8ff344f21d06685ac658b6cb190a701f5c..427ed7b4f0ae5f3d1a24cdea9eddc530c72bfc03 100644 (file)
@@ -424,3 +424,37 @@ func Test_bg_detection()
   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
index 4683ba46f353ffc3070b0bcdda51100ca278b012..59f0d097731ec720db6d2a5fe22297792aa6f331 100644 (file)
@@ -764,6 +764,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    647,
 /**/
     646,
 /**/