To: vim_dev@googlegroups.com Subject: Patch 8.0.0647 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.0647 Problem: Syntax highlighting can cause a freeze. Solution: Apply 'redrawtime' to syntax highlighting, per window. Files: src/structs.h, src/screen.c, src/syntax.c, src/normal.c, src/regexp.c, src/proto/syntax.pro, src/testdir/test_syntax.vim, runtime/doc/options.txt *** ../vim-8.0.0646/src/structs.h 2017-06-04 14:57:57.308461406 +0200 --- src/structs.h 2017-06-18 22:34:55.806759923 +0200 *************** *** 1797,1802 **** --- 1797,1805 ---- 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 */ *** ../vim-8.0.0646/src/screen.c 2017-06-17 18:44:16.990001010 +0200 --- src/screen.c 2017-06-18 22:37:21.649768994 +0200 *************** *** 124,130 **** 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 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); --- 124,130 ---- 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, 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,190 **** --- 185,195 ---- 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,928 **** --- 928,936 ---- { 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,936 **** --- 939,948 ---- 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,950 **** start_search_hl(); prepare_search_hl(wp, lnum); # endif ! win_line(wp, lnum, row, row + wp->w_lines[j].wl_size, FALSE); # if defined(FEAT_SEARCH_EXTRA) end_search_hl(); # endif --- 956,968 ---- start_search_hl(); prepare_search_hl(wp, lnum); # endif ! 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,1145 **** --- 1158,1166 ---- #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,1797 **** --- 1813,1822 ---- 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,2092 **** /* * Display one line. */ ! row = win_line(wp, lnum, srow, wp->w_height, mod_top == 0); #ifdef FEAT_FOLDING wp->w_lines[idx].wl_folded = FALSE; --- 2111,2123 ---- /* * Display one line. */ ! 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,2963 **** linenr_T lnum, int startrow, int endrow, ! int nochange UNUSED) /* not updating for changed text */ { int col = 0; /* visual column on screen */ unsigned off; /* offset in ScreenLines/ScreenAttrs */ --- 2988,2995 ---- linenr_T lnum, int startrow, int endrow, ! 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,3177 **** extra_check = 0; #endif #ifdef FEAT_SYN_HL ! if (syntax_present(wp) && !wp->w_s->b_syn_error) { /* 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); if (did_emsg) wp->w_s->b_syn_error = TRUE; else { did_emsg = save_did_emsg; ! has_syntax = TRUE; ! extra_check = TRUE; } } --- 3190,3218 ---- extra_check = 0; #endif #ifdef FEAT_SYN_HL ! 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_tm); if (did_emsg) wp->w_s->b_syn_error = TRUE; else { did_emsg = save_did_emsg; ! #ifdef SYN_TIME_LIMIT ! if (!wp->w_s->b_syn_slow) ! #endif ! { ! has_syntax = TRUE; ! extra_check = TRUE; ! } } } *************** *** 3548,3554 **** # ifdef FEAT_SYN_HL /* Need to restart syntax highlighting for this line. */ if (has_syntax) ! syntax_start(wp, lnum); # endif } #endif --- 3589,3595 ---- # ifdef FEAT_SYN_HL /* Need to restart syntax highlighting for this line. */ if (has_syntax) ! syntax_start(wp, lnum, syntax_tm); # endif } #endif *************** *** 4491,4496 **** --- 4532,4541 ---- } 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. */ *** ../vim-8.0.0646/src/syntax.c 2017-06-17 18:44:16.998000950 +0200 --- src/syntax.c 2017-06-18 22:34:02.871119550 +0200 *************** *** 367,372 **** --- 367,375 ---- 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,500 **** * window. */ void ! syntax_start(win_T *wp, linenr_T lnum) { synstate_T *p; synstate_T *last_valid = NULL; --- 497,503 ---- * window. */ void ! syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm UNUSED) { synstate_T *p; synstate_T *last_valid = NULL; *************** *** 524,529 **** --- 527,535 ---- } changedtick = CHANGEDTICK(syn_buf); syn_win = wp; + #ifdef FEAT_RELTIME + syn_tm = syntax_tm; + #endif /* * Allocate syntax stack when needed. *************** *** 3295,3300 **** --- 3301,3309 ---- syn_time_T *st UNUSED) { int r; + #ifdef FEAT_RELTIME + int timed_out = FALSE; + #endif #ifdef FEAT_PROFILE proftime_T pt; *************** *** 3303,3309 **** #endif rmp->rmm_maxcol = syn_buf->b_p_smc; ! r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL, NULL); #ifdef FEAT_PROFILE if (syn_time_on) --- 3312,3324 ---- #endif rmp->rmm_maxcol = syn_buf->b_p_smc; ! 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,3322 **** --- 3332,3341 ---- ++st->match; } #endif + #ifdef FEAT_RELTIME + if (timed_out) + syn_win->w_s->b_syn_slow = TRUE; + #endif if (r > 0) { *************** *** 3575,3580 **** --- 3594,3602 ---- 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,6548 **** if (wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) ! syntax_start(wp, lnum); else if (wp->w_buffer == syn_buf && lnum == current_lnum && col > current_col) --- 6564,6570 ---- if (wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) ! syntax_start(wp, lnum, NULL); else if (wp->w_buffer == syn_buf && lnum == current_lnum && col > current_col) *************** *** 6611,6619 **** int i; /* Return quickly when there are no fold items at all. */ ! if (wp->w_s->b_syn_folditems != 0) { ! syntax_start(wp, lnum); for (i = 0; i < current_state.ga_len; ++i) if (CUR_STATE(i).si_flags & HL_FOLD) --- 6633,6646 ---- int i; /* Return quickly when there are no fold items at all. */ ! 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, NULL); for (i = 0; i < current_state.ga_len; ++i) if (CUR_STATE(i).si_flags & HL_FOLD) *** ../vim-8.0.0646/src/normal.c 2017-06-17 18:44:17.006000891 +0200 --- src/normal.c 2017-06-18 21:16:22.014677454 +0200 *************** *** 5477,5482 **** --- 5477,5490 ---- #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); } *** ../vim-8.0.0646/src/regexp.c 2017-06-17 20:55:02.560050893 +0200 --- src/regexp.c 2017-06-18 22:23:11.955539687 +0200 *************** *** 5756,5763 **** printf("Premature EOL\n"); #endif } - if (status == RA_FAIL) - got_int = TRUE; return (status == RA_MATCH); } --- 5756,5761 ---- *************** *** 8224,8231 **** } #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(). --- 8222,8227 ---- *************** *** 8236,8242 **** * Return TRUE if there is a match, FALSE if not. */ static int ! vim_regexec_both( regmatch_T *rmp, char_u *line, /* string to match against */ colnr_T col, /* column to start looking for match */ --- 8232,8238 ---- * Return TRUE if there is a match, FALSE if not. */ static int ! vim_regexec_string( regmatch_T *rmp, char_u *line, /* string to match against */ colnr_T col, /* column to start looking for match */ *************** *** 8299,8310 **** char_u *line, colnr_T col) { ! int r; ! regmatch_T regmatch; regmatch.regprog = *prog; regmatch.rm_ic = ignore_case; ! r = vim_regexec_both(®match, line, col, FALSE); *prog = regmatch.regprog; return r; } --- 8295,8306 ---- char_u *line, colnr_T col) { ! int r; ! regmatch_T regmatch; regmatch.regprog = *prog; regmatch.rm_ic = ignore_case; ! r = vim_regexec_string(®match, line, col, FALSE); *prog = regmatch.regprog; return r; } *************** *** 8316,8322 **** int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col) { ! return vim_regexec_both(rmp, line, col, FALSE); } #if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) \ --- 8312,8318 ---- int vim_regexec(regmatch_T *rmp, char_u *line, colnr_T col) { ! return vim_regexec_string(rmp, line, col, FALSE); } #if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) \ *************** *** 8329,8335 **** int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col) { ! return vim_regexec_both(rmp, line, col, TRUE); } #endif --- 8325,8331 ---- int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col) { ! return vim_regexec_string(rmp, line, col, TRUE); } #endif *** ../vim-8.0.0646/src/proto/syntax.pro 2017-03-26 13:50:02.528929457 +0200 --- src/proto/syntax.pro 2017-06-18 22:02:10.308118953 +0200 *************** *** 1,5 **** /* syntax.c */ ! void syntax_start(win_T *wp, linenr_T lnum); void syn_stack_free_all(synblock_T *block); void syn_stack_apply_changes(buf_T *buf); void syntax_end_parsing(linenr_T lnum); --- 1,5 ---- /* syntax.c */ ! 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); *** ../vim-8.0.0646/src/testdir/test_syntax.vim 2017-06-04 21:06:05.082813802 +0200 --- src/testdir/test_syntax.vim 2017-06-18 22:11:03.856488746 +0200 *************** *** 424,426 **** --- 424,460 ---- 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 \" + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed > 0.1) + call assert_true(elapsed < 1.0) + + set redrawtime& + bwipe! + endfunc *** ../vim-8.0.0646/runtime/doc/options.txt 2017-06-13 17:20:35.691782326 +0200 --- runtime/doc/options.txt 2017-06-18 21:13:04.459998172 +0200 *************** *** 5859,5868 **** {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. 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. *'regexpengine'* *'re'* 'regexpengine' 're' number (default 0) --- 5945,5958 ---- {only available when compiled with the |+reltime| feature} The time in milliseconds for redrawing the display. This applies to ! searching for patterns for 'hlsearch', |:match| highlighting an syntax ! highlighting. When redrawing takes more than this many milliseconds no further ! 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) *** ../vim-8.0.0646/src/version.c 2017-06-17 20:55:02.564050863 +0200 --- src/version.c 2017-06-18 21:11:24.128668186 +0200 *************** *** 766,767 **** --- 766,769 ---- { /* Add new patch number below this line */ + /**/ + 647, /**/ -- hundred-and-one symptoms of being an internet addict: 42. Your virtual girlfriend finds a new net sweetheart with a larger bandwidth. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///