From 07a182c6b57e13e2563aef667131b976db8711cc Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 14 Apr 2019 22:34:58 -0400 Subject: vim-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. https://github.com/vim/vim/commit/06f1ed2f78c5c03af95054fc3a8665df39dec362 --- runtime/doc/options.txt | 10 +++++++--- src/nvim/buffer_defs.h | 1 + src/nvim/normal.c | 3 +++ src/nvim/regexp.c | 10 ++++------ src/nvim/screen.c | 27 ++++++++++++++++++--------- src/nvim/syntax.c | 20 +++++++++++++++----- src/nvim/testdir/test_syntax.vim | 34 ++++++++++++++++++++++++++++++++++ 7 files changed, 82 insertions(+), 23 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/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2e6f24d9c4..28f3889dd3 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -391,6 +391,7 @@ 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 */ + 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 */ 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..187609cd22 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,7 @@ 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; @@ -7276,7 +7274,7 @@ 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); + int r = vim_regexec_string(®match, line, col, false); *prog = regmatch.regprog; return r; } @@ -7285,7 +7283,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 +7291,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..8a51a0a21e 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1135,6 +1135,8 @@ 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); win_foldinfo.fi_level = 0; /* @@ -1362,7 +1364,7 @@ static void win_update(win_T *wp) /* * Display one line. */ - row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false); + row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false, &syntax_tm); wp->w_lines[idx].wl_folded = FALSE; wp->w_lines[idx].wl_lastlnum = lnum; @@ -1393,7 +1395,7 @@ static void win_update(win_T *wp) if (fold_count != 0) { fold_line(wp, fold_count, &win_foldinfo, lnum, row); } else { - (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true); + (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, &syntax_tm); } } @@ -2041,7 +2043,8 @@ win_line ( int startrow, int endrow, bool nochange, // not updating for changed text - bool number_only // only update the number column + bool number_only, // only update the number column + proftime_T *syntax_tm ) { int c = 0; // init for GCC @@ -2189,18 +2192,20 @@ 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; 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; + if (!wp->w_s->b_syn_slow) { + has_syntax = true; + extra_check = true; + } } } @@ -2540,8 +2545,9 @@ win_line ( wp->w_cursor = pos; /* Need to restart syntax highlighting for this line. */ - if (has_syntax) - syntax_start(wp, lnum); + if (has_syntax) { + syntax_start(wp, lnum, syntax_tm); + } } } @@ -2587,6 +2593,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. diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 8c3ce823d3..1d959027f6 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -359,6 +359,7 @@ 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 */ +static proftime_T *syn_tm; 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 @@ -384,7 +385,7 @@ static int syn_time_on = FALSE; * it. Careful: curbuf and curwin are likely to point to another buffer and * window. */ -void syntax_start(win_T *wp, linenr_T lnum) +void syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm) { synstate_T *p; synstate_T *last_valid = NULL; @@ -411,6 +412,7 @@ void syntax_start(win_T *wp, linenr_T lnum) } changedtick = buf_get_changedtick(syn_buf); syn_win = wp; + syn_tm = syntax_tm; /* * Allocate syntax stack when needed. @@ -2887,6 +2889,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 +2905,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 +2918,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 +3151,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; @@ -5682,7 +5690,7 @@ int syn_get_id( // When the position is not after the current position and in the same // line of the same buffer, need to restart parsing. if (wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) { - syntax_start(wp, lnum); + syntax_start(wp, lnum, NULL); } else if (col > current_col) { // next_match may not be correct when moving around, e.g. with the // "skip" expression in searchpair() @@ -5757,8 +5765,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) { - syntax_start(wp, lnum); + if (wp->w_s->b_syn_folditems != 0 + && !wp->w_s->b_syn_error + && !wp->w_s->b_syn_slow) { + syntax_start(wp, lnum, NULL); for (int i = 0; i < current_state.ga_len; ++i) { if (CUR_STATE(i).si_flags & HL_FOLD) { 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 \" + redraw + let elapsed = reltimefloat(reltime(start)) + call assert_true(elapsed > 0.1) + call assert_true(elapsed < 1.0) + + set redrawtime& + bwipe! +endfunc -- cgit From bbc32fc8313bc18080a3943a8129ba5bbf7081b9 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 14 Apr 2019 23:41:43 -0400 Subject: lint --- src/nvim/buffer_defs.h | 36 ++++++++++++++++++------------------ src/nvim/regexp.c | 5 +++-- src/nvim/screen.c | 8 +++++--- src/nvim/syntax.c | 20 ++++++++++---------- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 28f3889dd3..6d99a06eb9 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -388,25 +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 */ + 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 */ - 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 */ + 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/regexp.c b/src/nvim/regexp.c index 187609cd22..39ce7ff844 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -7224,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_string(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; @@ -7273,7 +7274,7 @@ static int vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool n 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}; + regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case }; int r = vim_regexec_string(®match, line, col, false); *prog = regmatch.regprog; return r; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 8a51a0a21e..38fc1ee021 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1364,7 +1364,8 @@ static void win_update(win_T *wp) /* * Display one line. */ - row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false, &syntax_tm); + row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false, + &syntax_tm); wp->w_lines[idx].wl_folded = FALSE; wp->w_lines[idx].wl_lastlnum = lnum; @@ -1395,7 +1396,8 @@ static void win_update(win_T *wp) if (fold_count != 0) { fold_line(wp, fold_count, &win_foldinfo, lnum, row); } else { - (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, &syntax_tm); + (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, + &syntax_tm); } } @@ -2544,7 +2546,7 @@ win_line ( } wp->w_cursor = pos; - /* Need to restart syntax highlighting for this line. */ + // Need to restart syntax highlighting for this line. if (has_syntax) { syntax_start(wp, lnum, syntax_tm); } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 1d959027f6..543c131b58 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -356,16 +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 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; -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 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 @@ -5764,7 +5764,7 @@ int syn_get_foldlevel(win_T *wp, long lnum) { int level = 0; - /* Return quickly when there are no fold items at all. */ + // 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) { -- cgit From 9e9015ee23a39a8d869c8deda3b421b782801115 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Apr 2019 01:48:52 -0400 Subject: vim-patch:8.0.1085: terminal debugger can't set breakpoints Problem: The terminal debugger can't set breakpoints. Solution: Add :Break and :Delete commands. Also commands for stepping through code. https://github.com/vim/vim/commit/e09ba7bae5c867f6d3abc184709dd27488318e97 --- .../pack/dist/opt/termdebug/plugin/termdebug.vim | 174 +++++++++++++++++---- 1 file changed, 142 insertions(+), 32 deletions(-) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 506179297a..46bd42112d 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -25,18 +25,26 @@ endif command -nargs=* -complete=file Termdebug call s:StartDebug() " 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. +" There can be only one. sign define debugPC linehl=debugPC +let s:pc_id = 12 +let s:break_id = 13 + +" Sign used to indicate a breakpoint. +" Can be used multiple times. +sign define debugBreakpoint text=>> texthl=debugBreakpoint + 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()) @@ -66,7 +74,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'), @@ -81,12 +89,24 @@ func s:StartDebug(cmd) " Connect gdb to the communication pty, using the GDB/MI interface call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") + + " Install debugger commands. + call s:InstallCommands() + + 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) endfunc " Handle a message received from gdb on the GDB/MI interface. @@ -100,34 +120,124 @@ 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=' + call s:HandleNewBreakpoint(msg) + elseif msg =~ '^=breakpoint-deleted,' + call s:HandleBreakpointDelete(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 NNext call s:SendCommand('-exec-next') + command Finish call s:SendCommand('-exec-finish') + command Continue call s:SendCommand('-exec-continue') +endfunc + +" Delete installed debugger commands in the current window. +func s:DeleteCommands() + delcommand Break + delcommand Delete + delcommand Step + delcommand NNext + delcommand Finish + delcommand Continue +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 + +" 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) + if a:msg =~ '^\*stopped' + let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') + 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 -- cgit From e6f22d0fddd6474ac5b2f1de5c32169df4f47991 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Apr 2019 01:50:25 -0400 Subject: vim-patch:8.0.1092: terminal debugger can't evaluate expressions Problem: Terminal debugger can't evaluate expressions. Solution: Add :Evaluate and K. Various other improvements. https://github.com/vim/vim/commit/45d5f26d11d9aac2383453d2c1a8582cad1c8a3d --- .../pack/dist/opt/termdebug/plugin/termdebug.vim | 63 ++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 46bd42112d..8d49b7b908 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -59,6 +59,7 @@ func s:StartDebug(cmd) 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', { @@ -86,12 +87,15 @@ 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") - " Install debugger commands. + " Install debugger commands in the text window. + call win_gotoid(s:startwin) call s:InstallCommands() + call win_gotoid(s:gdbwin) let s:breakpoints = {} endfunc @@ -121,10 +125,14 @@ func s:CommOutput(chan, msg) if msg != '' if msg =~ '^\*\(stopped\|running\)' call s:HandleCursor(msg) - elseif msg =~ '^\^done,bkpt=' + 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 @@ -135,9 +143,15 @@ func s:InstallCommands() command Break call s:SetBreakpoint() command Delete call s:DeleteBreakpoint() command Step call s:SendCommand('-exec-step') - command NNext call s:SendCommand('-exec-next') + 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(, ) + command Gdb call win_gotoid(s:gdbwin) + command Program call win_gotoid(s:ptywin) + + " TODO: can the K mapping be restored? + nnoremap K :Evaluate endfunc " Delete installed debugger commands in the current window. @@ -145,9 +159,21 @@ func s:DeleteCommands() delcommand Break delcommand Delete delcommand Step - delcommand NNext + delcommand Over delcommand Finish delcommand Continue + delcommand Evaluate + delcommand Gdb + delcommand Program + + nunmap K + sign undefine debugPC + sign undefine debugBreakpoint + exe 'sign unplace ' . s:pc_id + for key in keys(s:breakpoints) + exe 'sign unplace ' . (s:break_id + key) + endfor + unlet s:breakpoints endfunc " :Break - Set a breakpoint at the cursor position. @@ -176,6 +202,35 @@ 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('') + 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) -- cgit From b8128aee02e831267b63c3a52f5e5e6d38ae8308 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Apr 2019 01:51:14 -0400 Subject: vim-patch:8.0.1107: terminal debugger jumps to non-existing file Problem: Terminal debugger jumps to non-existing file. Solution: Check that the file exists. Add an option to make the Vim wide wide. Fix removing highlight groups. https://github.com/vim/vim/commit/38baa3e63427112d389de5e5942243414d9b1336 --- .../pack/dist/opt/termdebug/plugin/termdebug.vim | 36 +++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 8d49b7b908..78b1ae8ce8 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -29,16 +29,9 @@ if !exists('termdebugger') let termdebugger = 'gdb' endif -" Sign used to highlight the line where the program has stopped. -" There can be only one. -sign define debugPC linehl=debugPC let s:pc_id = 12 let s:break_id = 13 -" Sign used to indicate a breakpoint. -" Can be used multiple times. -sign define debugBreakpoint text=>> texthl=debugBreakpoint - if &background == 'light' hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue else @@ -50,9 +43,19 @@ 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' @@ -92,6 +95,14 @@ func s:StartDebug(cmd) " 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() @@ -111,6 +122,9 @@ func s:EndDebug(job, status) 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. @@ -167,12 +181,12 @@ func s:DeleteCommands() delcommand Program nunmap K - sign undefine debugPC - sign undefine debugBreakpoint 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 @@ -237,8 +251,8 @@ func s:HandleCursor(msg) let wid = win_getid(winnr()) if win_gotoid(s:startwin) - if a:msg =~ '^\*stopped' - let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') + 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 -- cgit From 6dacfe7217096df8c389a50754a3f0cbc615eefb Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Apr 2019 02:03:07 -0400 Subject: vim-patch:8.0.1133: syntax timeout not used correctly Problem: Syntax timeout not used correctly. Solution: Do not pass the timeout to syntax_start() but set it explicitly. (Yasuhiro Matsumoto, closes vim/vim#2139) https://github.com/vim/vim/commit/f3d769a585040ac47f7054057758809024ef6377 --- src/nvim/screen.c | 15 +++++++-------- src/nvim/syntax.c | 16 ++++++++++------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 38fc1ee021..6d4a5dee3a 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1137,6 +1137,7 @@ static void win_update(win_T *wp) 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; /* @@ -1364,8 +1365,7 @@ static void win_update(win_T *wp) /* * Display one line. */ - row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false, - &syntax_tm); + row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false); wp->w_lines[idx].wl_folded = FALSE; wp->w_lines[idx].wl_lastlnum = lnum; @@ -1396,8 +1396,7 @@ static void win_update(win_T *wp) if (fold_count != 0) { fold_line(wp, fold_count, &win_foldinfo, lnum, row); } else { - (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, - &syntax_tm); + (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true); } } @@ -1497,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; @@ -2045,8 +2045,7 @@ win_line ( int startrow, int endrow, bool nochange, // not updating for changed text - bool number_only, // only update the number column - proftime_T *syntax_tm + bool number_only // only update the number column ) { int c = 0; // init for GCC @@ -2199,7 +2198,7 @@ win_line ( // error, stop syntax highlighting. save_did_emsg = did_emsg; did_emsg = false; - syntax_start(wp, lnum, syntax_tm); + syntax_start(wp, lnum); if (did_emsg) { wp->w_s->b_syn_error = true; } else { @@ -2548,7 +2547,7 @@ win_line ( // Need to restart syntax highlighting for this line. if (has_syntax) { - syntax_start(wp, lnum, syntax_tm); + syntax_start(wp, lnum); } } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 543c131b58..ed95cdbebb 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -359,7 +359,7 @@ 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 -static proftime_T *syn_tm; +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 @@ -376,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 @@ -385,7 +390,7 @@ static int syn_time_on = FALSE; * it. Careful: curbuf and curwin are likely to point to another buffer and * window. */ -void syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm) +void syntax_start(win_T *wp, linenr_T lnum) { synstate_T *p; synstate_T *last_valid = NULL; @@ -412,7 +417,6 @@ void syntax_start(win_T *wp, linenr_T lnum, proftime_T *syntax_tm) } changedtick = buf_get_changedtick(syn_buf); syn_win = wp; - syn_tm = syntax_tm; /* * Allocate syntax stack when needed. @@ -5690,7 +5694,7 @@ int syn_get_id( // When the position is not after the current position and in the same // line of the same buffer, need to restart parsing. if (wp->w_buffer != syn_buf || lnum != current_lnum || col < current_col) { - syntax_start(wp, lnum, NULL); + syntax_start(wp, lnum); } else if (col > current_col) { // next_match may not be correct when moving around, e.g. with the // "skip" expression in searchpair() @@ -5768,7 +5772,7 @@ int syn_get_foldlevel(win_T *wp, long lnum) if (wp->w_s->b_syn_folditems != 0 && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow) { - syntax_start(wp, lnum, NULL); + syntax_start(wp, lnum); for (int i = 0; i < current_state.ga_len; ++i) { if (CUR_STATE(i).si_flags & HL_FOLD) { -- cgit From 3bc8639c1a92dac8a1bc9c71c74129bd1eba8269 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Apr 2019 02:52:57 -0400 Subject: vim-patch:8.0.0797: finished job in terminal window is not handled Problem: Finished job in terminal window is not handled. Solution: Add the scrollback buffer. Use it to fill the buffer when the job has ended. https://github.com/vim/vim/commit/d85f271bf8516dbd90be4d18f905f0abbfcd6db6 --- src/nvim/screen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 6d4a5dee3a..cd472ea1e4 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4831,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); } -- cgit From 439bbe00ece4c2f50fec453fc9b3c92d075b2431 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Apr 2019 03:00:18 -0400 Subject: vim-patch:8.0.0768: terminal window status shows "[Scratch]" Problem: Terminal window status shows "[Scratch]". Solution: Show "[Terminal]" when no title was set. (Yasuhiro Matsumoto) Store the terminal title that vterm sends and use it. Update the special buffer name. (closes vim/vim#1869) https://github.com/vim/vim/commit/2155441460a6dc0a72125f7860507693112a1460 --- src/nvim/buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 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; -- cgit From 37c7c964aef5f0d47c0bd0fb8abb15a5407e4b27 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Tue, 16 Apr 2019 23:57:12 -0400 Subject: vim-patch:8.0.1408: crash in setqflist() Problem: Crash in setqflist(). Solution: Check for string to be NULL. (Dominique Pelle, closes vim/vim#2464) https://github.com/vim/vim/commit/a0ca7d002d4efcf4bce0af6943146a339677ed3d --- src/nvim/testdir/test_quickfix.vim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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() -- cgit