aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c4
-rw-r--r--src/nvim/buffer_defs.h37
-rw-r--r--src/nvim/normal.c3
-rw-r--r--src/nvim/regexp.c13
-rw-r--r--src/nvim/screen.c24
-rw-r--r--src/nvim/syntax.c40
-rw-r--r--src/nvim/testdir/test_quickfix.vim14
-rw-r--r--src/nvim/testdir/test_syntax.vim34
8 files changed, 122 insertions, 47 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index d628c517bb..c8ed7d0b37 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -5211,8 +5211,8 @@ char_u *buf_spname(buf_T *buf)
return (char_u *)_(msg_qflist);
}
}
- /* There is no _file_ when 'buftype' is "nofile", b_sfname
- * contains the name as specified by the user */
+ // There is no _file_ when 'buftype' is "nofile", b_sfname
+ // contains the name as specified by the user.
if (bt_nofile(buf)) {
if (buf->b_sfname != NULL) {
return buf->b_sfname;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 2e6f24d9c4..6d99a06eb9 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -388,24 +388,25 @@ typedef struct {
* a window may have its own instance.
*/
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 */
- int b_syn_ic; /* ignore case for :syn cmds */
- int b_syn_spell; /* SYNSPL_ values */
- garray_T b_syn_patterns; /* table for syntax patterns */
- garray_T b_syn_clusters; /* table for syntax clusters */
- int b_spell_cluster_id; /* @Spell cluster ID or 0 */
- int b_nospell_cluster_id; /* @NoSpell cluster ID or 0 */
- int b_syn_containedin; /* TRUE when there is an item with a
- "containedin" argument */
- int b_syn_sync_flags; /* flags about how to sync */
- short b_syn_sync_id; /* group to sync on */
- long b_syn_sync_minlines; /* minimal sync lines offset */
- long b_syn_sync_maxlines; /* maximal sync lines offset */
- long b_syn_sync_linebreaks; /* offset for multi-line pattern */
- char_u *b_syn_linecont_pat; /* line continuation pattern */
- regprog_T *b_syn_linecont_prog; /* line continuation program */
+ 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
+ garray_T b_syn_clusters; // table for syntax clusters
+ int b_spell_cluster_id; // @Spell cluster ID or 0
+ int b_nospell_cluster_id; // @NoSpell cluster ID or 0
+ int b_syn_containedin; // TRUE when there is an item with a
+ // "containedin" argument
+ int b_syn_sync_flags; // flags about how to sync
+ int16_t b_syn_sync_id; // group to sync on
+ long b_syn_sync_minlines; // minimal sync lines offset
+ long b_syn_sync_maxlines; // maximal sync lines offset
+ long b_syn_sync_linebreaks; // offset for multi-line pattern
+ char_u *b_syn_linecont_pat; // line continuation pattern
+ regprog_T *b_syn_linecont_prog; // line continuation program
syn_time_T b_syn_linecont_time;
int b_syn_linecont_ic; /* ignore-case flag for above */
int b_syn_topgrp; /* for ":syntax include" */
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 5217db5b42..ca586cca29 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -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);
}
}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index c602e7df0f..39ce7ff844 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -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,8 @@ 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;
@@ -7275,8 +7274,8 @@ static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl)
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(&regmatch, line, col, false);
+ regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case };
+ int r = vim_regexec_string(&regmatch, line, col, false);
*prog = regmatch.regprog;
return r;
}
@@ -7285,7 +7284,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 +7292,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.
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index b4ebf2ece5..cd472ea1e4 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1135,6 +1135,9 @@ 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);
+ syn_set_timeout(&syntax_tm);
win_foldinfo.fi_level = 0;
/*
@@ -1493,6 +1496,7 @@ static void win_update(win_T *wp)
if (wp->w_redr_type >= REDRAW_TOP) {
draw_vsep_win(wp, 0);
}
+ syn_set_timeout(NULL);
/* Reset the type of redrawing required, the window has been updated. */
wp->w_redr_type = 0;
@@ -2189,7 +2193,7 @@ 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;
@@ -2199,8 +2203,10 @@ win_line (
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;
+ }
}
}
@@ -2539,9 +2545,10 @@ win_line (
}
wp->w_cursor = pos;
- /* Need to restart syntax highlighting for this line. */
- if (has_syntax)
+ // Need to restart syntax highlighting for this line.
+ if (has_syntax) {
syntax_start(wp, lnum);
+ }
}
}
@@ -2587,6 +2594,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.
@@ -4821,13 +4831,13 @@ static void win_redr_status(win_T *wp)
p = NameBuff;
len = (int)STRLEN(p);
- if (wp->w_buffer->b_help
+ if (bt_help(wp->w_buffer)
|| wp->w_p_pvw
|| bufIsChanged(wp->w_buffer)
|| wp->w_buffer->b_p_ro) {
*(p + len++) = ' ';
}
- if (wp->w_buffer->b_help) {
+ if (bt_help(wp->w_buffer)) {
STRCPY(p + len, _("[Help]"));
len += (int)STRLEN(p + len);
}
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 8c3ce823d3..ed95cdbebb 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -356,15 +356,16 @@ static reg_extmatch_T *next_match_extmatch = NULL;
* The current state (within the line) of the recognition engine.
* When current_state.ga_itemsize is 0 the current state is invalid.
*/
-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 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
- * after setting current_finished */
-static int current_finished = 0; /* current line has been finished */
-static garray_T current_state /* current stack of state_items */
+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; // timeout limit
+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
+ // after setting current_finished
+static int current_finished = 0; // current line has been finished
+static garray_T current_state // current stack of state_items
= GA_EMPTY_INIT_VALUE;
static int16_t *current_next_list = NULL; // when non-zero, nextgroup list
static int current_next_flags = 0; // flags for current_next_list
@@ -375,7 +376,12 @@ static int current_line_id = 0; // unique number for current line
static int syn_time_on = FALSE;
# define IF_SYN_TIME(p) (p)
-
+// Set the timeout used for syntax highlighting.
+// Use NULL to reset, no timeout.
+void syn_set_timeout(proftime_T *tm)
+{
+ syn_tm = tm;
+}
/*
* Start the syntax recognition for a line. This function is normally called
@@ -2887,6 +2893,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 +2909,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 +2922,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 +3155,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;
@@ -5756,8 +5768,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) {
+ // Return quickly when there are no fold items at all.
+ if (wp->w_s->b_syn_folditems != 0
+ && !wp->w_s->b_syn_error
+ && !wp->w_s->b_syn_slow) {
syntax_start(wp, lnum);
for (int i = 0; i < current_state.ga_len; ++i) {
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index f50f33d255..ad4661eb3c 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -1379,6 +1379,11 @@ func XquickfixSetListWithAct(cchar)
call assert_fails("call g:Xsetlist(list1, 0)", 'E928:')
endfunc
+func Test_setqflist_invalid_nr()
+ " The following command used to crash Vim
+ call setqflist([], ' ', {'nr' : $XXX_DOES_NOT_EXIST})
+endfunc
+
func Test_quickfix_set_list_with_act()
call XquickfixSetListWithAct('c')
call XquickfixSetListWithAct('l')
@@ -2649,6 +2654,15 @@ func Test_qf_id()
call Xqfid_tests('l')
endfunc
+func Test_getqflist_invalid_nr()
+ " The following commands used to crash Vim
+ cexpr ""
+ call getqflist({'nr' : $XXX_DOES_NOT_EXIST_XXX})
+
+ " Cleanup
+ call setqflist([], 'r')
+endfunc
+
" Test for shortening/simplifying the file name when opening the
" quickfix window or when displaying the quickfix list
func Test_shorten_fname()
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 6978faeb7b..850947f89f 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -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