diff options
-rw-r--r-- | runtime/doc/options.txt | 10 | ||||
-rw-r--r-- | runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 247 | ||||
-rw-r--r-- | src/nvim/buffer.c | 4 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 37 | ||||
-rw-r--r-- | src/nvim/normal.c | 3 | ||||
-rw-r--r-- | src/nvim/regexp.c | 13 | ||||
-rw-r--r-- | src/nvim/screen.c | 24 | ||||
-rw-r--r-- | src/nvim/syntax.c | 40 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 14 | ||||
-rw-r--r-- | src/nvim/testdir/test_syntax.vim | 34 |
10 files changed, 342 insertions, 84 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 171fff8547..e773d27394 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4528,10 +4528,14 @@ A jump table for the options with a short description can be found at |Q_op|. 'redrawtime' 'rdt' number (default 2000) global Time in milliseconds for redrawing the display. Applies to - 'hlsearch', 'inccommand' and |:match| highlighting. + 'hlsearch', 'inccommand', |:match| highlighting and 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) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 506179297a..78b1ae8ce8 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -25,32 +25,44 @@ endif command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>) " Name of the gdb command, defaults to "gdb". -if !exists('debugger') - let debugger = 'gdb' +if !exists('termdebugger') + let termdebugger = 'gdb' endif -" Sign used to highlight the line where the program has stopped. -sign define debugPC linehl=debugPC +let s:pc_id = 12 +let s:break_id = 13 + if &background == 'light' - hi debugPC term=reverse ctermbg=lightblue guibg=lightblue + hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue else - hi debugPC term=reverse ctermbg=darkblue guibg=darkblue + hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue endif -let s:pc_id = 12 +hi default debugBreakpoint term=reverse ctermbg=red guibg=red func s:StartDebug(cmd) let s:startwin = win_getid(winnr()) let s:startsigncolumn = &signcolumn + if exists('g:termdebug_wide') && &columns < g:termdebug_wide + let s:save_columns = &columns + let &columns = g:termdebug_wide + let vertical = 1 + else + let s:save_columns = 0 + let vertical = 0 + endif + " Open a terminal window without a job, to run the debugged program let s:ptybuf = term_start('NONE', { \ 'term_name': 'gdb program', + \ 'vertical': vertical, \ }) if s:ptybuf == 0 echoerr 'Failed to open the program terminal window' return endif let pty = job_info(term_getjob(s:ptybuf))['tty_out'] + let s:ptywin = win_getid(winnr()) " Create a hidden terminal window to communicate with gdb let s:commbuf = term_start('NONE', { @@ -66,7 +78,7 @@ func s:StartDebug(cmd) let commpty = job_info(term_getjob(s:commbuf))['tty_out'] " Open a terminal window to run the debugger. - let cmd = [g:debugger, '-tty', pty, a:cmd] + let cmd = [g:termdebugger, '-tty', pty, a:cmd] echomsg 'executing "' . join(cmd) . '"' let gdbbuf = term_start(cmd, { \ 'exit_cb': function('s:EndDebug'), @@ -78,15 +90,41 @@ func s:StartDebug(cmd) exe 'bwipe! ' . s:commbuf return endif + let s:gdbwin = win_getid(winnr()) " Connect gdb to the communication pty, using the GDB/MI interface call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") + + " Sign used to highlight the line where the program has stopped. + " There can be only one. + sign define debugPC linehl=debugPC + + " Sign used to indicate a breakpoint. + " Can be used multiple times. + sign define debugBreakpoint text=>> texthl=debugBreakpoint + + " Install debugger commands in the text window. + call win_gotoid(s:startwin) + call s:InstallCommands() + call win_gotoid(s:gdbwin) + + let s:breakpoints = {} endfunc func s:EndDebug(job, status) exe 'bwipe! ' . s:ptybuf exe 'bwipe! ' . s:commbuf - call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn) + + let curwinid = win_getid(winnr()) + + call win_gotoid(s:startwin) + let &signcolumn = s:startsigncolumn + call s:DeleteCommands() + + call win_gotoid(curwinid) + if s:save_columns > 0 + let &columns = s:save_columns + endif endfunc " Handle a message received from gdb on the GDB/MI interface. @@ -100,34 +138,175 @@ func s:CommOutput(chan, msg) endif if msg != '' if msg =~ '^\*\(stopped\|running\)' - let wid = win_getid(winnr()) - - if win_gotoid(s:startwin) - if msg =~ '^\*stopped' - " TODO: proper parsing - let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '') - let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') - if lnum =~ '^[0-9]*$' - if expand('%:h') != fname - if &modified - " TODO: find existing window - exe 'split ' . fnameescape(fname) - let s:startwin = win_getid(winnr()) - else - exe 'edit ' . fnameescape(fname) - endif - endif - exe lnum - exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname) - setlocal signcolumn=yes - endif + call s:HandleCursor(msg) + elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' + call s:HandleNewBreakpoint(msg) + elseif msg =~ '^=breakpoint-deleted,' + call s:HandleBreakpointDelete(msg) + elseif msg =~ '^\^done,value=' + call s:HandleEvaluate(msg) + elseif msg =~ '^\^error,msg=' + call s:HandleError(msg) + endif + endif + endfor +endfunc + +" Install commands in the current window to control the debugger. +func s:InstallCommands() + command Break call s:SetBreakpoint() + command Delete call s:DeleteBreakpoint() + command Step call s:SendCommand('-exec-step') + command Over call s:SendCommand('-exec-next') + command Finish call s:SendCommand('-exec-finish') + command Continue call s:SendCommand('-exec-continue') + command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) + command Gdb call win_gotoid(s:gdbwin) + command Program call win_gotoid(s:ptywin) + + " TODO: can the K mapping be restored? + nnoremap K :Evaluate<CR> +endfunc + +" Delete installed debugger commands in the current window. +func s:DeleteCommands() + delcommand Break + delcommand Delete + delcommand Step + delcommand Over + delcommand Finish + delcommand Continue + delcommand Evaluate + delcommand Gdb + delcommand Program + + nunmap K + exe 'sign unplace ' . s:pc_id + for key in keys(s:breakpoints) + exe 'sign unplace ' . (s:break_id + key) + endfor + sign undefine debugPC + sign undefine debugBreakpoint + unlet s:breakpoints +endfunc + +" :Break - Set a breakpoint at the cursor position. +func s:SetBreakpoint() + call term_sendkeys(s:commbuf, '-break-insert --source ' + \ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r") +endfunc + +" :Delete - Delete a breakpoint at the cursor position. +func s:DeleteBreakpoint() + let fname = fnameescape(expand('%:p')) + let lnum = line('.') + for [key, val] in items(s:breakpoints) + if val['fname'] == fname && val['lnum'] == lnum + call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r") + " Assume this always wors, the reply is simply "^done". + exe 'sign unplace ' . (s:break_id + key) + unlet s:breakpoints[key] + break + endif + endfor +endfunc + +" :Next, :Continue, etc - send a command to gdb +func s:SendCommand(cmd) + call term_sendkeys(s:commbuf, a:cmd . "\r") +endfunc + +" :Evaluate - evaluate what is under the cursor +func s:Evaluate(range, arg) + if a:arg != '' + let expr = a:arg + elseif a:range == 2 + let pos = getcurpos() + let reg = getreg('v', 1, 1) + let regt = getregtype('v') + normal! gv"vy + let expr = @v + call setpos('.', pos) + call setreg('v', reg, regt) + else + let expr = expand('<cexpr>') + endif + call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . expr . "\"\r") + let s:evalexpr = expr +endfunc + +" Handle the result of data-evaluate-expression +func s:HandleEvaluate(msg) + echomsg '"' . s:evalexpr . '": ' . substitute(a:msg, '.*value="\(.*\)"', '\1', '') +endfunc + +" Handle an error. +func s:HandleError(msg) + echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') +endfunc + +" Handle stopping and running message from gdb. +" Will update the sign that shows the current position. +func s:HandleCursor(msg) + let wid = win_getid(winnr()) + + if win_gotoid(s:startwin) + let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') + if a:msg =~ '^\*stopped' && filereadable(fname) + let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') + if lnum =~ '^[0-9]*$' + if expand('%:h') != fname + if &modified + " TODO: find existing window + exe 'split ' . fnameescape(fname) + let s:startwin = win_getid(winnr()) else - exe 'sign unplace ' . s:pc_id + exe 'edit ' . fnameescape(fname) endif - - call win_gotoid(wid) endif + exe lnum + exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname) + setlocal signcolumn=yes endif + else + exe 'sign unplace ' . s:pc_id endif - endfor + + call win_gotoid(wid) + endif +endfunc + +" Handle setting a breakpoint +" Will update the sign that shows the breakpoint +func s:HandleNewBreakpoint(msg) + let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 + if nr == 0 + return + endif + + if has_key(s:breakpoints, nr) + let entry = s:breakpoints[nr] + else + let entry = {} + let s:breakpoints[nr] = entry + endif + + let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') + let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') + + exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname) + + let entry['fname'] = fname + let entry['lnum'] = lnum +endfunc + +" Handle deleting a breakpoint +" Will remove the sign that shows the breakpoint +func s:HandleBreakpointDelete(msg) + let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 + if nr == 0 + return + endif + exe 'sign unplace ' . (s:break_id + nr) + unlet s:breakpoints[nr] endfunc 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(®match, line, col, false); + regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case }; + int r = vim_regexec_string(®match, 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 |