From 90f62cc749da78afc9891bb03a195b79d0bedcff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 01:34:13 +0300 Subject: ex_getln: Clean up draw_cmdline a bit --- src/nvim/ex_getln.c | 70 +++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 858374c68e..013f936137 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2174,69 +2174,64 @@ void free_cmdline_buf(void) */ static void draw_cmdline(int start, int len) { - int i; - - if (cmdline_star > 0) - for (i = 0; i < len; ++i) { + if (cmdline_star > 0) { + for (int i = 0; i < len; i++) { msg_putchar('*'); - if (has_mbyte) + if (has_mbyte) { i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1; + } } - else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) { + } else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) { static int buflen = 0; - char_u *p; - int j; - int newlen = 0; - int mb_l; - int pc, pc1 = 0; - int prev_c = 0; - int prev_c1 = 0; - int u8c; - int u8cc[MAX_MCO]; - int nc = 0; - /* - * Do arabic shaping into a temporary buffer. This is very - * inefficient! - */ + // Do arabic shaping into a temporary buffer. This is very + // inefficient! if (len * 2 + 2 > buflen) { - /* Re-allocate the buffer. We keep it around to avoid a lot of - * alloc()/free() calls. */ + // Re-allocate the buffer. We keep it around to avoid a lot of + // alloc()/free() calls. xfree(arshape_buf); buflen = len * 2 + 2; arshape_buf = xmalloc(buflen); } + int newlen = 0; if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) { - /* Prepend a space to draw the leading composing char on. */ + // Prepend a space to draw the leading composing char on. arshape_buf[0] = ' '; newlen = 1; } - for (j = start; j < start + len; j += mb_l) { - p = ccline.cmdbuff + j; - u8c = utfc_ptr2char_len(p, u8cc, start + len - j); - mb_l = utfc_ptr2len_len(p, start + len - j); + int mb_l; + int prev_c = 0; + int prev_c1 = 0; + for (int i = start; i < start + len; i += mb_l) { + char_u *p = ccline.cmdbuff + i; + int u8cc[MAX_MCO]; + int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); + mb_l = utfc_ptr2len_len(p, start + len - i); if (arabic_char(u8c)) { - /* Do Arabic shaping. */ + int pc; + int pc1 = 0; + int nc = 0; + // Do Arabic shaping. if (cmdmsg_rl) { - /* displaying from right to left */ + // Displaying from right to left. pc = prev_c; pc1 = prev_c1; prev_c1 = u8cc[0]; - if (j + mb_l >= start + len) + if (i + mb_l >= start + len) { nc = NUL; - else + } else { nc = utf_ptr2char(p + mb_l); + } } else { - /* displaying from left to right */ - if (j + mb_l >= start + len) + // Displaying from left to right. + if (i + mb_l >= start + len) { pc = NUL; - else { + } else { int pcc[MAX_MCO]; - pc = utfc_ptr2char_len(p + mb_l, pcc, - start + len - j - mb_l); + pc = utfc_ptr2char_len(p + mb_l, pcc, start + len - i - mb_l); pc1 = pcc[0]; } nc = prev_c; @@ -2260,8 +2255,9 @@ static void draw_cmdline(int start, int len) } msg_outtrans_len(arshape_buf, newlen); - } else + } else { msg_outtrans_len(ccline.cmdbuff + start, len); + } } /* -- cgit From 7db2f658e87278885a137ac2b0b88df6a1b546cb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 01:38:43 +0300 Subject: ex_getln: Do not do arabic shaping unless needed Should speed up execution without arabic characters a bit, slowing down with arabic characters. More necessary, this allows coloring prompt without caring about arabic shaping at the first iteration. --- src/nvim/ex_getln.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 013f936137..632f125b45 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2182,6 +2182,22 @@ static void draw_cmdline(int start, int len) } } } else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) { + bool do_arabicshape = false; + int mb_l; + for (int i = start; i < start + len; i += mb_l) { + char_u *p = ccline.cmdbuff + i; + int u8cc[MAX_MCO]; + int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); + mb_l = utfc_ptr2len_len(p, start + len - i); + if (arabic_char(u8c)) { + do_arabicshape = true; + break; + } + } + if (!do_arabicshape) { + goto draw_cmdline_no_arabicshape; + } + static int buflen = 0; // Do arabic shaping into a temporary buffer. This is very @@ -2201,7 +2217,6 @@ static void draw_cmdline(int start, int len) newlen = 1; } - int mb_l; int prev_c = 0; int prev_c1 = 0; for (int i = start; i < start + len; i += mb_l) { @@ -2256,6 +2271,7 @@ static void draw_cmdline(int start, int len) msg_outtrans_len(arshape_buf, newlen); } else { +draw_cmdline_no_arabicshape: msg_outtrans_len(ccline.cmdbuff + start, len); } } -- cgit From c1d21e9dd67cdea5e133bb1b79fc1765d20c191b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 03:40:59 +0300 Subject: ex_getln: Add basic support for coloring command-line prompt --- src/nvim/ex_getln.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 1 deletion(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 632f125b45..b08407233f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -62,6 +62,7 @@ #include "nvim/os/os.h" #include "nvim/event/loop.h" #include "nvim/os/time.h" +#include "nvim/lib/kvec.h" /* * Variables shared between getcmdline(), redrawcmdline() and others. @@ -127,6 +128,15 @@ typedef struct command_line_state { struct cmdline_info save_ccline; } CommandLineState; +/// Command-line colors +typedef struct { + int start; ///< Colored chunk start. + int end; ///< Colored chunk end (exclusive, > start). + int attr; ///< Highlight attr. +} ColoredCmdlineChunk; + +kvec_t(ColoredCmdlineChunk) ccline_colors; + /* The current cmdline_info. It is initialized in getcmdline() and after that * used by other functions. When invoking getcmdline() recursively it needs * to be saved with save_cmdline() and restored with restore_cmdline(). @@ -2168,6 +2178,139 @@ void free_cmdline_buf(void) # endif +/// Color command-line +/// +/// Should use built-in command parser or user-specified one. Currently only the +/// latter is supported. +/// +/// Operates on ccline, saving results to ccline_colors. +/// +/// Always colors the whole cmdline. +static void color_cmdline(void) +{ + kv_size(ccline_colors) = 0; + if (ccline.cmdfirstc != ':') { + return; + } + static Callback prev_cb = { .type = kCallbackNone }; + static int prev_cb_errors = 0; + Callback color_cb; + if (!tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), + &color_cb)) { + return; + } + if (color_cb.type == kCallbackNone) { + return; + } + if (prev_cb_errors == 5 && tv_callback_equal(&prev_cb, &color_cb)) { + return; + } + callback_free(&prev_cb); + prev_cb = color_cb; + bool arg_allocated; + typval_T arg = { + .v_type = VAR_STRING, + }; + if (ccline.cmdbuff[ccline.cmdlen] == NUL) { + arg_allocated = false; + arg.vval.v_string = ccline.cmdbuff; + } else { + arg_allocated = true; + arg.vval.v_string = xmemdupz((const char *)ccline.cmdbuff, + (size_t)ccline.cmdlen); + } + typval_T tv; + msg_silent++; + if (!callback_call(&color_cb, 1, &arg, &tv)) { + msg_silent--; + goto color_cmdline_end; + } + msg_silent--; + if (tv.v_type != VAR_LIST || tv.vval.v_list == NULL) { + emsgf(_("E5400: Callback should return list")); + goto color_cmdline_error; + } + varnumber_T prev_end = 0; + int i = 0; + for (const listitem_T *li = tv.vval.v_list->lv_first; + li != NULL; li = li->li_next, i++) { + if (li->li_tv.v_type != VAR_LIST) { + emsgf(_("E5401: List item %i is not a List"), i); + goto color_cmdline_error; + } + const list_T *const l = li->li_tv.vval.v_list; + if (tv_list_len(l) != 3) { + emsgf(_("E5402: List item %i has incorrect length: %li /= 3"), + i, tv_list_len(l)); + goto color_cmdline_error; + } + bool error = false; + const varnumber_T start = tv_get_number_chk(&l->lv_first->li_tv, &error); + if (error) { + goto color_cmdline_error; + } else if (!(prev_end <= start && start <= ccline.cmdlen)) { + emsgf(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range " + "[%" PRIdVARNUMBER ", %i]"), + i, start, prev_end, ccline.cmdlen); + goto color_cmdline_error; + } + if (start != prev_end) { + kv_push(ccline_colors, ((ColoredCmdlineChunk) { + .start = prev_end, + .end = start, + .attr = 0, + })); + } + const varnumber_T end = tv_get_number_chk(&l->lv_first->li_next->li_tv, + &error); + if (error) { + goto color_cmdline_error; + } else if (!(start < end && end <= ccline.cmdlen)) { + emsgf(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range " + "(%" PRIdVARNUMBER ", %i]"), + i, end, start, ccline.cmdlen); + goto color_cmdline_error; + } + prev_end = end; + const char *const group = tv_get_string_chk(&l->lv_last->li_tv); + if (group == NULL) { + goto color_cmdline_error; + } + const int id = syn_name2id((char_u *)group); + const int attr = (id == 0 ? 0 : syn_id2attr(id)); + kv_push(ccline_colors, ((ColoredCmdlineChunk) { + .start = start, + .end = end, + .attr = attr, + })); + } + if (prev_end < ccline.cmdlen) { + kv_push(ccline_colors, ((ColoredCmdlineChunk) { + .start = prev_end, + .end = ccline.cmdlen, + .attr = 0, + })); + } + prev_cb_errors = 0; +color_cmdline_end: + if (arg_allocated) { + tv_clear(&arg); + } + tv_clear(&tv); + return; +color_cmdline_error: + prev_cb_errors++; + did_emsg = false; + if (did_throw) { + discard_current_exception(); + } + if (msg_list != NULL && *msg_list != NULL) { + free_global_msglist(); + } + kv_size(ccline_colors) = 0; + goto color_cmdline_end; +} + /* * Draw part of the cmdline at the current cursor position. But draw stars * when cmdline_star is TRUE. @@ -2272,7 +2415,21 @@ static void draw_cmdline(int start, int len) msg_outtrans_len(arshape_buf, newlen); } else { draw_cmdline_no_arabicshape: - msg_outtrans_len(ccline.cmdbuff + start, len); + color_cmdline(); + if (kv_size(ccline_colors)) { + for (size_t i = 0; i < kv_size(ccline_colors); i++) { + ColoredCmdlineChunk chunk = kv_A(ccline_colors, i); + if (chunk.end <= start) { + continue; + } + const int chunk_start = MAX(chunk.start, start); + msg_outtrans_len_attr(ccline.cmdbuff + chunk_start, + chunk.end - chunk_start, + chunk.attr); + } + } else { + msg_outtrans_len(ccline.cmdbuff + start, len); + } } } -- cgit From 3d25200127bfec90982e82cb3d1fb65f8faff257 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 04:04:23 +0300 Subject: functests: Start adding some tests --- test/functional/ui/cmdline_highlight_spec.lua | 101 ++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/functional/ui/cmdline_highlight_spec.lua diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua new file mode 100644 index 0000000000..812841ab7e --- /dev/null +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -0,0 +1,101 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +local feed = helpers.feed +local clear = helpers.clear +local meths = helpers.meths +local source = helpers.source + +local screen + +before_each(function() + clear() + screen = Screen.new(40, 2) + screen:attach() + source([[ + highlight RBP1 guifg=Red + highlight RBP2 guifg=Yellow + highlight RBP3 guifg=Green + highlight RBP4 guifg=Blue + let g:NUM_LVLS = 4 + function Redraw() + redraw! + return '' + endfunction + cnoremap {REDRAW} Redraw() + function RainBowParens(cmdline) + let ret = [] + let i = 0 + let lvl = 0 + while i < len(a:cmdline) + if a:cmdline[i] is# '(' + call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) + let lvl += 1 + elseif a:cmdline[i] is# ')' + let lvl -= 1 + call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) + endif + let i += 1 + endwhile + return ret + endfunction + ]]) + screen:set_default_attr_ids({ + RBP1={foreground = Screen.colors.Red}, + RBP2={foreground = Screen.colors.Yellow}, + RBP3={foreground = Screen.colors.Green}, + RBP4={foreground = Screen.colors.Blue}, + }) +end) + +describe('Command-line coloring', function() + it('works', function() + meths.set_var('Nvim_color_cmdline', 'RainBowParens') + meths.set_option('more', false) + feed(':') + screen:expect([[ + | + :^ | + ]]) + feed('e') + screen:expect([[ + | + :e^ | + ]]) + feed('cho ') + screen:expect([[ + | + :echo ^ | + ]]) + feed('(') + screen:expect([[ + | + :echo {RBP1:(}^ | + ]]) + feed('(') + screen:expect([[ + | + :echo {RBP1:(}{RBP2:(}^ | + ]]) + feed('42') + screen:expect([[ + | + :echo {RBP1:(}{RBP2:(}42^ | + ]]) + feed('))') + screen:expect([[ + | + :echo {RBP1:(}{RBP2:(}42{RBP2:)}{RBP1:)}^ | + ]]) + feed('') + screen:expect([[ + | + :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | + ]]) + feed('{REDRAW}') + screen:expect([[ + | + :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | + ]]) + end) +end) -- cgit From d82741f8c04d003bb9925d9c46d7e07179810ed4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 17:25:03 +0300 Subject: ex_getln: Add some more tests, fix some found errors --- src/nvim/ex_getln.c | 92 +++++++--- src/nvim/mbyte.c | 2 +- src/nvim/mbyte.h | 3 + test/functional/ui/cmdline_highlight_spec.lua | 246 +++++++++++++++++++++++++- 4 files changed, 320 insertions(+), 23 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index b08407233f..e4914d6ebc 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2178,6 +2178,8 @@ void free_cmdline_buf(void) # endif +enum { MAX_CB_ERRORS = 5 }; + /// Color command-line /// /// Should use built-in command parser or user-specified one. Currently only the @@ -2186,50 +2188,77 @@ void free_cmdline_buf(void) /// Operates on ccline, saving results to ccline_colors. /// /// Always colors the whole cmdline. -static void color_cmdline(void) +/// +/// @return true if draw_cmdline may proceed, false if it does not need anything +/// to do. +static bool color_cmdline(void) { + bool ret = true; kv_size(ccline_colors) = 0; if (ccline.cmdfirstc != ':') { - return; + return ret; } + + const int saved_force_abort = force_abort; + force_abort = true; + bool arg_allocated = false; + typval_T arg = { + .v_type = VAR_STRING, + .vval.v_string = ccline.cmdbuff, + }; + typval_T tv = { .v_type = VAR_UNKNOWN }; + static Callback prev_cb = { .type = kCallbackNone }; static int prev_cb_errors = 0; Callback color_cb; if (!tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), &color_cb)) { - return; + goto color_cmdline_error; } if (color_cb.type == kCallbackNone) { - return; + goto color_cmdline_end; } - if (prev_cb_errors == 5 && tv_callback_equal(&prev_cb, &color_cb)) { - return; + if (!tv_callback_equal(&prev_cb, &color_cb)) { + prev_cb_errors = 0; + } else if (prev_cb_errors >= MAX_CB_ERRORS) { + callback_free(&color_cb); + goto color_cmdline_end; } callback_free(&prev_cb); prev_cb = color_cb; - bool arg_allocated; - typval_T arg = { - .v_type = VAR_STRING, - }; - if (ccline.cmdbuff[ccline.cmdlen] == NUL) { - arg_allocated = false; - arg.vval.v_string = ccline.cmdbuff; - } else { + if (ccline.cmdbuff[ccline.cmdlen] != NUL) { arg_allocated = true; arg.vval.v_string = xmemdupz((const char *)ccline.cmdbuff, (size_t)ccline.cmdlen); } - typval_T tv; + // msg_start() called by e.g. :echo may shift command-line to the first column + // even though msg_silent is here. Two ways to workaround this problem without + // altering message.c: use full_screen or save and restore msg_col. + // + // Saving and restoring full_screen does not work well with :redraw!. Saving + // and restoring msg_col is neither ideal, but while with full_screen it + // appears shifted one character to the right and cursor position is no longer + // correct, with msg_col it just misses leading `:`. Since `redraw!` in + // callback lags this is least of the user problems. + const int saved_msg_col = msg_col; msg_silent++; if (!callback_call(&color_cb, 1, &arg, &tv)) { msg_silent--; - goto color_cmdline_end; + msg_col = saved_msg_col; + goto color_cmdline_error; } msg_silent--; - if (tv.v_type != VAR_LIST || tv.vval.v_list == NULL) { + msg_col = saved_msg_col; + if (got_int || did_emsg) { + goto color_cmdline_error; + } + if (tv.v_type != VAR_LIST) { emsgf(_("E5400: Callback should return list")); goto color_cmdline_error; } + if (tv.vval.v_list == NULL) { + goto color_cmdline_end; + } varnumber_T prev_end = 0; int i = 0; for (const listitem_T *li = tv.vval.v_list->lv_first; @@ -2248,11 +2277,15 @@ static void color_cmdline(void) const varnumber_T start = tv_get_number_chk(&l->lv_first->li_tv, &error); if (error) { goto color_cmdline_error; - } else if (!(prev_end <= start && start <= ccline.cmdlen)) { + } else if (!(prev_end <= start && start < ccline.cmdlen)) { emsgf(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range " - "[%" PRIdVARNUMBER ", %i]"), + "[%" PRIdVARNUMBER ", %i)"), i, start, prev_end, ccline.cmdlen); goto color_cmdline_error; + } else if (utf8len_tab_zero[(uint8_t)ccline.cmdbuff[start]] == 0) { + emsgf(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits multibyte " + "character"), i, start); + goto color_cmdline_error; } if (start != prev_end) { kv_push(ccline_colors, ((ColoredCmdlineChunk) { @@ -2270,6 +2303,11 @@ static void color_cmdline(void) "(%" PRIdVARNUMBER ", %i]"), i, end, start, ccline.cmdlen); goto color_cmdline_error; + } else if (end < ccline.cmdlen + && utf8len_tab_zero[(uint8_t)ccline.cmdbuff[end]] == 0) { + emsgf(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte " + "character"), i, end); + goto color_cmdline_error; } prev_end = end; const char *const group = tv_get_string_chk(&l->lv_last->li_tv); @@ -2293,13 +2331,16 @@ static void color_cmdline(void) } prev_cb_errors = 0; color_cmdline_end: + force_abort = saved_force_abort; if (arg_allocated) { tv_clear(&arg); } tv_clear(&tv); - return; + return ret; color_cmdline_error: prev_cb_errors++; + const bool do_redraw = (did_emsg || got_int); + got_int = false; did_emsg = false; if (did_throw) { discard_current_exception(); @@ -2308,6 +2349,12 @@ color_cmdline_error: free_global_msglist(); } kv_size(ccline_colors) = 0; + if (do_redraw) { + prev_cb_errors += MAX_CB_ERRORS; + redrawcmdline(); + prev_cb_errors -= MAX_CB_ERRORS; + ret = false; + } goto color_cmdline_end; } @@ -2317,6 +2364,10 @@ color_cmdline_error: */ static void draw_cmdline(int start, int len) { + if (!color_cmdline()) { + return; + } + if (cmdline_star > 0) { for (int i = 0; i < len; i++) { msg_putchar('*'); @@ -2415,7 +2466,6 @@ static void draw_cmdline(int start, int len) msg_outtrans_len(arshape_buf, newlen); } else { draw_cmdline_no_arabicshape: - color_cmdline(); if (kv_size(ccline_colors)) { for (size_t i = 0; i < kv_size(ccline_colors); i++) { ColoredCmdlineChunk chunk = kv_A(ccline_colors, i); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index d5907da2ed..862ca7ea1a 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -75,7 +75,7 @@ struct interval { /* * Like utf8len_tab above, but using a zero for illegal lead bytes. */ -static uint8_t utf8len_tab_zero[256] = +const uint8_t utf8len_tab_zero[256] = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index ad9e38004c..02c6065672 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -1,6 +1,7 @@ #ifndef NVIM_MBYTE_H #define NVIM_MBYTE_H +#include #include #include @@ -67,6 +68,8 @@ typedef struct { ///< otherwise use '?'. } vimconv_T; +extern const uint8_t utf8len_tab_zero[256]; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 812841ab7e..eb0a65bea7 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -1,16 +1,18 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local eq = helpers.eq local feed = helpers.feed local clear = helpers.clear local meths = helpers.meths +local funcs = helpers.funcs local source = helpers.source local screen before_each(function() clear() - screen = Screen.new(40, 2) + screen = Screen.new(40, 8) screen:attach() source([[ highlight RBP1 guifg=Red @@ -39,12 +41,74 @@ before_each(function() endwhile return ret endfunction + function SplittedMultibyteStart(cmdline) + let ret = [] + let i = 0 + while i < len(a:cmdline) + let char = nr2char(char2nr(a:cmdline[i:])) + if a:cmdline[i:i + len(char) - 1] is# char + if len(char) > 1 + call add(ret, [i + 1, i + len(char), 'RBP2']) + endif + let i += len(char) + else + let i += 1 + endif + endwhile + return ret + endfunction + function SplittedMultibyteEnd(cmdline) + let ret = [] + let i = 0 + while i < len(a:cmdline) + let char = nr2char(char2nr(a:cmdline[i:])) + if a:cmdline[i:i + len(char) - 1] is# char + if len(char) > 1 + call add(ret, [i, i + 1, 'RBP1']) + endif + let i += len(char) + else + let i += 1 + endif + endwhile + return ret + endfunction + function Echoing(cmdline) + echo 'HERE' + return v:_null_list + endfunction + function Echoning(cmdline) + echon 'HERE' + return v:_null_list + endfunction + function Echomsging(cmdline) + echomsg 'HERE' + return v:_null_list + endfunction + function Echoerring(cmdline) + echoerr 'HERE' + return v:_null_list + endfunction + function Redrawing(cmdline) + redraw! + return v:_null_list + endfunction + function Throwing(cmdline) + throw "ABC" + return v:_null_list + endfunction + function Halting(cmdline) + while 1 + endwhile + endfunction ]]) screen:set_default_attr_ids({ RBP1={foreground = Screen.colors.Red}, RBP2={foreground = Screen.colors.Yellow}, RBP3={foreground = Screen.colors.Green}, RBP4={foreground = Screen.colors.Blue}, + EOB={bold = true, foreground = Screen.colors.Blue1}, + ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, }) end) @@ -55,47 +119,227 @@ describe('Command-line coloring', function() feed(':') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :^ | ]]) feed('e') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :e^ | ]]) feed('cho ') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :echo ^ | ]]) feed('(') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :echo {RBP1:(}^ | ]]) feed('(') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :echo {RBP1:(}{RBP2:(}^ | ]]) feed('42') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :echo {RBP1:(}{RBP2:(}42^ | ]]) feed('))') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :echo {RBP1:(}{RBP2:(}42{RBP2:)}{RBP1:)}^ | ]]) feed('') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | ]]) feed('{REDRAW}') screen:expect([[ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | ]]) end) + for _, func_part in ipairs({'', 'n', 'msg'}) do + it('disables :echo' .. func_part .. ' messages', function() + meths.set_var('Nvim_color_cmdline', 'Echo' .. func_part .. 'ing') + feed(':echo') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :echo^ | + ]]) + end) + end + it('does the right thing when hl start appears to split multibyte char', + function() + meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') + feed(':echo "«') + screen:expect([[ + {EOB:~ }| + :echo " | + {ERR:E5405: Chunk 0 start 7 splits multibyte }| + {ERR:character} | + :echo "« | + {ERR:E5405: Chunk 0 start 7 splits multibyte }| + {ERR:character} | + :echo "«^ | + ]]) + feed('»') + -- FIXME Does not work well with too much error messages: they overwrite + -- cmdline. + end) + it('does the right thing when hl end appears to split multibyte char', + function() + meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteEnd') + feed(':echo "«') + screen:expect([[ + {EOB:~ }| + :echo " | + {ERR:E5406: Chunk 0 end 7 splits multibyte ch}| + {ERR:aracter} | + :echo "« | + {ERR:E5406: Chunk 0 end 7 splits multibyte ch}| + {ERR:aracter} | + :echo "«^ | + ]]) + end) + it('does the right thing when errorring', function() + meths.set_var('Nvim_color_cmdline', 'Echoerring') + feed(':e') + -- FIXME Does not work well with :echoerr: error message overwrites cmdline. + end) + it('does the right thing when throwing', function() + meths.set_var('Nvim_color_cmdline', 'Throwing') + feed(':e') + -- FIXME Does not work well with :throw: error message overwrites cmdline. + end) + it('still executes command-line even if errored out', function() + meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') + feed(':let x = "«"\n') + eq('«', meths.get_var('x')) + local msg = 'E5405: Chunk 0 start 10 splits multibyte character' + eq('\n'..msg..'\n'..msg, funcs.execute('messages')) + end) + it('stops executing callback after a number of errors', function() + meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') + feed(':let x = "«»«»«»«»«»"\n') + eq('«»«»«»«»«»', meths.get_var('x')) + local msg = '\nE5405: Chunk 0 start 10 splits multibyte character' + eq(msg:rep(5), funcs.execute('messages')) + end) + it('allows interrupting callback with ', function() + meths.set_var('Nvim_color_cmdline', 'Halting') + feed(':echo 42') + for i = 1, 6 do + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + feed('') + end + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + Type :quit to exit Nvim | + ]]) + feed(':echo 42') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + 42 | + ]]) + end) + it('works fine with NUL, NL, CR', function() + meths.set_var('Nvim_color_cmdline', 'RainBowParens') + feed(':echo ("")') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :echo {RBP1:(}"{RBP4:^M^@^@}"{RBP1:)}^ | + ]]) + end) + -- TODO Check for all other errors end) -- cgit From 407abb3a6c5c1c706bf8797a1431e57e97a6b797 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Apr 2017 01:00:40 +0300 Subject: eval,ex_getln: Add support for coloring input() prompts --- runtime/doc/eval.txt | 17 ++++++ src/nvim/eval.c | 12 +++- src/nvim/ex_getln.c | 88 ++++++++++++++++----------- src/nvim/ex_getln.h | 2 + test/functional/eval/input_spec.lua | 57 +++++++++++++++++ test/functional/ui/cmdline_highlight_spec.lua | 50 +++++++++------ 6 files changed, 169 insertions(+), 57 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2a73590c76..2262c01374 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4699,6 +4699,7 @@ input({opts}) cancelreturn "" Same as {cancelreturn} from |inputdialog()|. Also works with input(). + highlight nothing Highlight handler: |Funcref|. The highlighting set with |:echohl| is used for the prompt. The input is entered just like a command-line, with the same @@ -4722,6 +4723,22 @@ input({opts}) more information. Example: > let fname = input("File: ", "", "file") < + The optional highlight key allows specifying function which + will be used for highlighting. This function receives user + input as its only argument and must return a list of 3-tuples + [hl_start_byte, hl_end_byte + 1, hl_group] where + hl_start_byte is the first highlighted byte, + hl_end_byte is the last highlighted byte (+ 1!), + hl_group is |:hl| group used for highlighting. + *E5403* *E5404* *E5405* *E5406* + Both hl_start_byte and hl_end_byte + 1 must point to the start + of the multibyte character (highlighting must not break + multibyte characters), hl_end_byte + 1 may be equal to the + input length. Start column must be in range [0, len(input)), + end column must be in range (hl_start_byte, len(input)], + sections must be ordered so that next hl_start_byte is greater + then or equal to previous hl_end_byte. + NOTE: This function must not be used in a startup file, for the versions that only run in GUI mode (e.g., the Win32 GUI). Note: When input() is called from within a mapping it will diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18aa5bf763..1cc4a3eb15 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11010,6 +11010,7 @@ void get_user_input(const typval_T *const argvars, const char *defstr = ""; const char *cancelreturn = NULL; const char *xp_name = NULL; + Callback input_callback = { .type = kCallbackNone }; char prompt_buf[NUMBUFLEN]; char defstr_buf[NUMBUFLEN]; char cancelreturn_buf[NUMBUFLEN]; @@ -11019,7 +11020,7 @@ void get_user_input(const typval_T *const argvars, emsgf(_("E5050: {opts} must be the only argument")); return; } - const dict_T *const dict = argvars[0].vval.v_dict; + dict_T *const dict = argvars[0].vval.v_dict; prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); if (prompt == NULL) { return; @@ -11045,6 +11046,9 @@ void get_user_input(const typval_T *const argvars, if (xp_name == def) { // default to NULL xp_name = NULL; } + if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { + return; + } } else { prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); if (prompt == NULL) { @@ -11103,12 +11107,16 @@ void get_user_input(const typval_T *const argvars, stuffReadbuffSpec(defstr); - int save_ex_normal_busy = ex_normal_busy; + const int save_ex_normal_busy = ex_normal_busy; + const Callback save_getln_input_callback = getln_input_callback; ex_normal_busy = 0; + getln_input_callback = input_callback; rettv->vval.v_string = getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, xp_type, (char_u *)xp_arg); + getln_input_callback = save_getln_input_callback; ex_normal_busy = save_ex_normal_busy; + callback_free(&input_callback); if (rettv->vval.v_string == NULL && cancelreturn != NULL) { rettv->vval.v_string = (char_u *)xstrdup(cancelreturn); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e4914d6ebc..a2cc6d2b65 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -70,23 +70,26 @@ * structure. */ struct cmdline_info { - char_u *cmdbuff; /* pointer to command line buffer */ - int cmdbufflen; /* length of cmdbuff */ - int cmdlen; /* number of chars in command line */ - int cmdpos; /* current cursor position */ - int cmdspos; /* cursor column on screen */ - int cmdfirstc; /* ':', '/', '?', '=', '>' or NUL */ - int cmdindent; /* number of spaces before cmdline */ - char_u *cmdprompt; /* message in front of cmdline */ - int cmdattr; /* attributes for prompt */ - int overstrike; /* Typing mode on the command line. Shared by - getcmdline() and put_on_cmdline(). */ - expand_T *xpc; /* struct being used for expansion, xp_pattern - may point into cmdbuff */ - int xp_context; /* type of expansion */ - char_u *xp_arg; /* user-defined expansion arg */ - int input_fn; /* when TRUE Invoked for input() function */ + char_u *cmdbuff; // pointer to command line buffer + int cmdbufflen; // length of cmdbuff + int cmdlen; // number of chars in command line + int cmdpos; // current cursor position + int cmdspos; // cursor column on screen + int cmdfirstc; // ':', '/', '?', '=', '>' or NUL + int cmdindent; // number of spaces before cmdline + char_u *cmdprompt; // message in front of cmdline + int cmdattr; // attributes for prompt + int overstrike; // Typing mode on the command line. Shared by + // getcmdline() and put_on_cmdline(). + expand_T *xpc; // struct being used for expansion, xp_pattern + // may point into cmdbuff + int xp_context; // type of expansion + char_u *xp_arg; // user-defined expansion arg + int input_fn; // when TRUE Invoked for input() function + unsigned prompt_id; ///< Prompt number, used to disable coloring on errors. }; +/// Last value of prompt_id, incremented when doing new prompt +static unsigned last_prompt_id = 0; typedef struct command_line_state { VimState state; @@ -135,6 +138,9 @@ typedef struct { int attr; ///< Highlight attr. } ColoredCmdlineChunk; +/// Callback used for coloring input() prompts +Callback getln_input_callback = { .type = kCallbackNone }; + kvec_t(ColoredCmdlineChunk) ccline_colors; /* The current cmdline_info. It is initialized in getcmdline() and after that @@ -188,6 +194,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) cmd_hkmap = 0; } + ccline.prompt_id = last_prompt_id++; ccline.overstrike = false; // always start in insert mode s->old_cursor = curwin->w_cursor; // needs to be restored later s->old_curswant = curwin->w_curswant; @@ -1703,6 +1710,7 @@ getcmdline_prompt ( int msg_col_save = msg_col; save_cmdline(&save_ccline); + ccline.prompt_id = last_prompt_id++; ccline.cmdprompt = prompt; ccline.cmdattr = attr; ccline.xp_context = xp_context; @@ -2178,7 +2186,7 @@ void free_cmdline_buf(void) # endif -enum { MAX_CB_ERRORS = 5 }; +enum { MAX_CB_ERRORS = 1 }; /// Color command-line /// @@ -2195,9 +2203,6 @@ static bool color_cmdline(void) { bool ret = true; kv_size(ccline_colors) = 0; - if (ccline.cmdfirstc != ':') { - return ret; - } const int saved_force_abort = force_abort; force_abort = true; @@ -2208,24 +2213,32 @@ static bool color_cmdline(void) }; typval_T tv = { .v_type = VAR_UNKNOWN }; - static Callback prev_cb = { .type = kCallbackNone }; - static int prev_cb_errors = 0; - Callback color_cb; - if (!tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), - &color_cb)) { - goto color_cmdline_error; + static unsigned prev_prompt_id = UINT_MAX; + static int prev_prompt_errors = 0; + Callback color_cb = { .type = kCallbackNone }; + bool can_free_cb = false; + + if (ccline.input_fn) { + color_cb = getln_input_callback; + } else if (ccline.cmdfirstc == ':') { + if (!tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), + &color_cb)) { + goto color_cmdline_error; + } + can_free_cb = true; + } else { + goto color_cmdline_end; } + if (color_cb.type == kCallbackNone) { goto color_cmdline_end; } - if (!tv_callback_equal(&prev_cb, &color_cb)) { - prev_cb_errors = 0; - } else if (prev_cb_errors >= MAX_CB_ERRORS) { - callback_free(&color_cb); + if (ccline.prompt_id != prev_prompt_id) { + prev_prompt_errors = 0; + prev_prompt_id = ccline.prompt_id; + } else if (prev_prompt_errors >= MAX_CB_ERRORS) { goto color_cmdline_end; } - callback_free(&prev_cb); - prev_cb = color_cb; if (ccline.cmdbuff[ccline.cmdlen] != NUL) { arg_allocated = true; arg.vval.v_string = xmemdupz((const char *)ccline.cmdbuff, @@ -2329,8 +2342,11 @@ static bool color_cmdline(void) .attr = 0, })); } - prev_cb_errors = 0; + prev_prompt_errors = 0; color_cmdline_end: + if (can_free_cb) { + callback_free(&color_cb); + } force_abort = saved_force_abort; if (arg_allocated) { tv_clear(&arg); @@ -2338,7 +2354,7 @@ color_cmdline_end: tv_clear(&tv); return ret; color_cmdline_error: - prev_cb_errors++; + prev_prompt_errors++; const bool do_redraw = (did_emsg || got_int); got_int = false; did_emsg = false; @@ -2350,9 +2366,9 @@ color_cmdline_error: } kv_size(ccline_colors) = 0; if (do_redraw) { - prev_cb_errors += MAX_CB_ERRORS; + prev_prompt_errors += MAX_CB_ERRORS; redrawcmdline(); - prev_cb_errors -= MAX_CB_ERRORS; + prev_prompt_errors -= MAX_CB_ERRORS; ret = false; } goto color_cmdline_end; diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 051564fbe1..92cb274010 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -52,6 +52,8 @@ typedef struct hist_entry { list_T *additional_elements; ///< Additional entries from ShaDa file. } histentry_T; +extern Callback getln_input_callback; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.h.generated.h" #endif diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 74ad32bc6c..f0c1314754 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -23,10 +23,41 @@ before_each(function() function CustomListCompl(...) return ['FOO'] endfunction + + highlight RBP1 guibg=Red + highlight RBP2 guibg=Yellow + highlight RBP3 guibg=Green + highlight RBP4 guibg=Blue + let g:NUM_LVLS = 4 + function Redraw() + redraw! + return '' + endfunction + cnoremap {REDRAW} Redraw() + function RainBowParens(cmdline) + let ret = [] + let i = 0 + let lvl = 0 + while i < len(a:cmdline) + if a:cmdline[i] is# '(' + call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) + let lvl += 1 + elseif a:cmdline[i] is# ')' + let lvl -= 1 + call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)]) + endif + let i += 1 + endwhile + return ret + endfunction ]]) screen:set_default_attr_ids({ EOB={bold = true, foreground = Screen.colors.Blue1}, T={foreground=Screen.colors.Red}, + RBP1={background=Screen.colors.Red}, + RBP2={background=Screen.colors.Yellow}, + RBP3={background=Screen.colors.Green}, + RBP4={background=Screen.colors.Blue}, }) end) @@ -196,6 +227,19 @@ describe('input()', function() eq('Vim(call):E118: Too many arguments for function: input', exc_exec('call input("prompt> ", "default", "file", "extra")')) end) + it('supports highlighting', function() + feed([[:call input({"highlight": "RainBowParens"})]]) + wait() + feed('(())') + wait() + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + {RBP1:(}{RBP2:()}{RBP1:)}^ | + ]]) + end) end) describe('inputdialog()', function() it('works with multiline prompts', function() @@ -363,4 +407,17 @@ describe('inputdialog()', function() eq('Vim(call):E118: Too many arguments for function: inputdialog', exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')) end) + it('supports highlighting', function() + feed([[:call inputdialog({"highlight": "RainBowParens"})]]) + wait() + feed('(())') + wait() + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + {RBP1:(}{RBP2:()}{RBP1:)}^ | + ]]) + end) end) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index eb0a65bea7..4ccfb44261 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -15,10 +15,10 @@ before_each(function() screen = Screen.new(40, 8) screen:attach() source([[ - highlight RBP1 guifg=Red - highlight RBP2 guifg=Yellow - highlight RBP3 guifg=Green - highlight RBP4 guifg=Blue + highlight RBP1 guibg=Red + highlight RBP2 guibg=Yellow + highlight RBP3 guibg=Green + highlight RBP4 guibg=Blue let g:NUM_LVLS = 4 function Redraw() redraw! @@ -103,12 +103,13 @@ before_each(function() endfunction ]]) screen:set_default_attr_ids({ - RBP1={foreground = Screen.colors.Red}, - RBP2={foreground = Screen.colors.Yellow}, - RBP3={foreground = Screen.colors.Green}, - RBP4={foreground = Screen.colors.Blue}, + RBP1={background = Screen.colors.Red}, + RBP2={background = Screen.colors.Yellow}, + RBP3={background = Screen.colors.Green}, + RBP4={background = Screen.colors.Blue}, EOB={bold = true, foreground = Screen.colors.Blue1}, ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + SK={foreground = Screen.colors.Blue}, }) end) @@ -237,40 +238,50 @@ describe('Command-line coloring', function() meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') feed(':echo "«') screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| {EOB:~ }| :echo " | {ERR:E5405: Chunk 0 start 7 splits multibyte }| {ERR:character} | - :echo "« | - {ERR:E5405: Chunk 0 start 7 splits multibyte }| - {ERR:character} | :echo "«^ | ]]) feed('»') - -- FIXME Does not work well with too much error messages: they overwrite - -- cmdline. + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :echo " | + {ERR:E5405: Chunk 0 start 7 splits multibyte }| + {ERR:character} | + :echo "«»^ | + ]]) end) it('does the right thing when hl end appears to split multibyte char', function() meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteEnd') feed(':echo "«') screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| {EOB:~ }| :echo " | {ERR:E5406: Chunk 0 end 7 splits multibyte ch}| {ERR:aracter} | - :echo "« | - {ERR:E5406: Chunk 0 end 7 splits multibyte ch}| - {ERR:aracter} | :echo "«^ | ]]) end) it('does the right thing when errorring', function() + if true then return pending('echoerr does not work well now') end meths.set_var('Nvim_color_cmdline', 'Echoerring') feed(':e') -- FIXME Does not work well with :echoerr: error message overwrites cmdline. end) it('does the right thing when throwing', function() + if true then return pending('Throwing does not work well now') end meths.set_var('Nvim_color_cmdline', 'Throwing') feed(':e') -- FIXME Does not work well with :throw: error message overwrites cmdline. @@ -280,16 +291,17 @@ describe('Command-line coloring', function() feed(':let x = "«"\n') eq('«', meths.get_var('x')) local msg = 'E5405: Chunk 0 start 10 splits multibyte character' - eq('\n'..msg..'\n'..msg, funcs.execute('messages')) + eq('\n'..msg, funcs.execute('messages')) end) it('stops executing callback after a number of errors', function() meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') feed(':let x = "«»«»«»«»«»"\n') eq('«»«»«»«»«»', meths.get_var('x')) local msg = '\nE5405: Chunk 0 start 10 splits multibyte character' - eq(msg:rep(5), funcs.execute('messages')) + eq(msg:rep(1), funcs.execute('messages')) end) it('allows interrupting callback with ', function() + if true then return pending(' does not work well enough now') end meths.set_var('Nvim_color_cmdline', 'Halting') feed(':echo 42') for i = 1, 6 do @@ -338,7 +350,7 @@ describe('Command-line coloring', function() {EOB:~ }| {EOB:~ }| {EOB:~ }| - :echo {RBP1:(}"{RBP4:^M^@^@}"{RBP1:)}^ | + :echo {RBP1:(}"{SK:^M^@^@}"{RBP1:)}^ | ]]) end) -- TODO Check for all other errors -- cgit From 95fe5614a02948bcd0993c751acede34d2acb3c8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 22 May 2017 03:25:46 +0300 Subject: functests: Add missing wait() --- test/functional/eval/input_spec.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index f0c1314754..5b259d5440 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq +local wait = helpers.wait local feed = helpers.feed local meths = helpers.meths local clear = helpers.clear -- cgit From 072a853fa212bdce88f756ac170fb915a4972625 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 22 May 2017 22:44:08 +0300 Subject: ex_getln: Enable coloring for expression mode --- src/nvim/ex_getln.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a2cc6d2b65..f9b2d6cda8 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2226,6 +2226,12 @@ static bool color_cmdline(void) goto color_cmdline_error; } can_free_cb = true; + } else if (ccline.cmdfirstc == '=') { + if (!tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"), + &color_cb)) { + goto color_cmdline_error; + } + can_free_cb = true; } else { goto color_cmdline_end; } -- cgit From 71616fce0b3db6618ef47e231f34853eef79fea0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 27 Jun 2017 01:54:08 +0300 Subject: functests: Abstract away some ways to enter cmdline coloring mode Reason: should actually switch to using input() coloring because other coloring variants are eventually going away. --- test/functional/ui/cmdline_highlight_spec.lua | 49 +++++++++++++++------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 4ccfb44261..278acf0bdf 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -113,11 +113,18 @@ before_each(function() }) end) +local function set_color_cb(funcname) + meths.set_var('Nvim_color_cmdline', funcname) +end +local function start_prompt(text) + feed(':' .. (text or '')) +end + describe('Command-line coloring', function() it('works', function() - meths.set_var('Nvim_color_cmdline', 'RainBowParens') + set_color_cb('RainBowParens') meths.set_option('more', false) - feed(':') + start_prompt() screen:expect([[ | {EOB:~ }| @@ -219,8 +226,8 @@ describe('Command-line coloring', function() end) for _, func_part in ipairs({'', 'n', 'msg'}) do it('disables :echo' .. func_part .. ' messages', function() - meths.set_var('Nvim_color_cmdline', 'Echo' .. func_part .. 'ing') - feed(':echo') + set_color_cb('Echo' .. func_part .. 'ing') + start_prompt('echo') screen:expect([[ | {EOB:~ }| @@ -235,8 +242,8 @@ describe('Command-line coloring', function() end it('does the right thing when hl start appears to split multibyte char', function() - meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') - feed(':echo "«') + set_color_cb('SplittedMultibyteStart') + start_prompt('echo "«') screen:expect([[ {EOB:~ }| {EOB:~ }| @@ -261,8 +268,8 @@ describe('Command-line coloring', function() end) it('does the right thing when hl end appears to split multibyte char', function() - meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteEnd') - feed(':echo "«') + set_color_cb('SplittedMultibyteEnd') + start_prompt('echo "«') screen:expect([[ {EOB:~ }| {EOB:~ }| @@ -276,34 +283,34 @@ describe('Command-line coloring', function() end) it('does the right thing when errorring', function() if true then return pending('echoerr does not work well now') end - meths.set_var('Nvim_color_cmdline', 'Echoerring') - feed(':e') + set_color_cb('Echoerring') + start_prompt('e') -- FIXME Does not work well with :echoerr: error message overwrites cmdline. end) it('does the right thing when throwing', function() if true then return pending('Throwing does not work well now') end - meths.set_var('Nvim_color_cmdline', 'Throwing') - feed(':e') + set_color_cb('Throwing') + start_prompt('e') -- FIXME Does not work well with :throw: error message overwrites cmdline. end) it('still executes command-line even if errored out', function() - meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') - feed(':let x = "«"\n') + set_color_cb('SplittedMultibyteStart') + start_prompt('let x = "«"\n') eq('«', meths.get_var('x')) local msg = 'E5405: Chunk 0 start 10 splits multibyte character' eq('\n'..msg, funcs.execute('messages')) end) it('stops executing callback after a number of errors', function() - meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') - feed(':let x = "«»«»«»«»«»"\n') + set_color_cb('SplittedMultibyteStart') + start_prompt('let x = "«»«»«»«»«»"\n') eq('«»«»«»«»«»', meths.get_var('x')) local msg = '\nE5405: Chunk 0 start 10 splits multibyte character' eq(msg:rep(1), funcs.execute('messages')) end) it('allows interrupting callback with ', function() if true then return pending(' does not work well enough now') end - meths.set_var('Nvim_color_cmdline', 'Halting') - feed(':echo 42') + set_color_cb('Halting') + start_prompt('echo 42') for i = 1, 6 do screen:expect([[ ^ | @@ -327,7 +334,7 @@ describe('Command-line coloring', function() {EOB:~ }| Type :quit to exit Nvim | ]]) - feed(':echo 42') + start_prompt('echo 42') screen:expect([[ ^ | {EOB:~ }| @@ -340,8 +347,8 @@ describe('Command-line coloring', function() ]]) end) it('works fine with NUL, NL, CR', function() - meths.set_var('Nvim_color_cmdline', 'RainBowParens') - feed(':echo ("")') + set_color_cb('RainBowParens') + start_prompt('echo ("")') screen:expect([[ | {EOB:~ }| -- cgit From 8e5134784c8e15aabd458480bee1e445e8a04e94 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 27 Jun 2017 01:55:21 +0300 Subject: functests: Comment out failing test --- test/functional/ui/cmdline_highlight_spec.lua | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 278acf0bdf..a30489ec97 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -212,17 +212,18 @@ describe('Command-line coloring', function() {EOB:~ }| :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | ]]) - feed('{REDRAW}') - screen:expect([[ - | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | - ]]) + -- FIXME + -- feed('{REDRAW}') + -- screen:expect([[ + -- | + -- {EOB:~ }| + -- {EOB:~ }| + -- {EOB:~ }| + -- {EOB:~ }| + -- {EOB:~ }| + -- {EOB:~ }| + -- :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | + -- ]]) end) for _, func_part in ipairs({'', 'n', 'msg'}) do it('disables :echo' .. func_part .. ' messages', function() -- cgit From edc2a7ee460b9a77705c87ecccc41a22f6134bf8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 27 Jun 2017 02:15:49 +0300 Subject: functests: Make tests work with input() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are still some issues: specifically, new “pending” test hangs busted. --- test/functional/ui/cmdline_highlight_spec.lua | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index a30489ec97..6bb06b2ed3 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -25,6 +25,7 @@ before_each(function() return '' endfunction cnoremap {REDRAW} Redraw() + nnoremap {PROMPT} input({"prompt": ":", "highlight": g:Nvim_color_input})[1:0] function RainBowParens(cmdline) let ret = [] let i = 0 @@ -114,10 +115,10 @@ before_each(function() end) local function set_color_cb(funcname) - meths.set_var('Nvim_color_cmdline', funcname) + meths.set_var('Nvim_color_input', funcname) end local function start_prompt(text) - feed(':' .. (text or '')) + feed('{PROMPT}' .. (text or '')) end describe('Command-line coloring', function() @@ -294,20 +295,13 @@ describe('Command-line coloring', function() start_prompt('e') -- FIXME Does not work well with :throw: error message overwrites cmdline. end) - it('still executes command-line even if errored out', function() - set_color_cb('SplittedMultibyteStart') - start_prompt('let x = "«"\n') - eq('«', meths.get_var('x')) - local msg = 'E5405: Chunk 0 start 10 splits multibyte character' - eq('\n'..msg, funcs.execute('messages')) - end) - it('stops executing callback after a number of errors', function() + pending('stops executing callback after a number of errors'--[[, function() set_color_cb('SplittedMultibyteStart') start_prompt('let x = "«»«»«»«»«»"\n') eq('«»«»«»«»«»', meths.get_var('x')) local msg = '\nE5405: Chunk 0 start 10 splits multibyte character' eq(msg:rep(1), funcs.execute('messages')) - end) + end]]) it('allows interrupting callback with ', function() if true then return pending(' does not work well enough now') end set_color_cb('Halting') @@ -363,3 +357,14 @@ describe('Command-line coloring', function() end) -- TODO Check for all other errors end) +describe('Ex commands coloring support', function() + it('still executes command-line even if errored out', function() + meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') + feed(':let x = "«"\n') + eq('«', meths.get_var('x')) + local msg = 'E5405: Chunk 0 start 10 splits multibyte character' + eq('\n'..msg, funcs.execute('messages')) + end) +end) + +-- TODO Specifically test for coloring in cmdline and expr modes -- cgit From 36a84d8f4ab104050e6ec52da9ca6e10f9361a0c Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 13:54:04 +0300 Subject: functests: Fix typo --- test/functional/ui/screen.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 5408e1e195..a6b7fb2997 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -251,7 +251,7 @@ function Screen:expect(expected, attr_ids, attr_ignore, condition, any) ..'Expected:\n |'..table.concat(msg_expected_rows, '|\n |')..'|\n' ..'Actual:\n |'..table.concat(actual_rows, '|\n |')..'|\n\n'..[[ To print the expect() call that would assert the current screen state, use -screen:snaphot_util(). In case of non-deterministic failures, use +screen:snapshot_util(). In case of non-deterministic failures, use screen:redraw_debug() to show all intermediate screen states. ]]) end end -- cgit From 493d250446e5323e64c80399f0c5a15621c4f15c Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 13:58:51 +0300 Subject: functests: Make “stops executing callback” test work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needed to be adjusted to use input() (previously relied on side-effects of executing `:cmd`) and dismiss something (hidden “Press ENTER” message?). --- test/functional/ui/cmdline_highlight_spec.lua | 30 +++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 6bb06b2ed3..12577a4db4 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -24,8 +24,9 @@ before_each(function() redraw! return '' endfunction + let g:EMPTY = '' cnoremap {REDRAW} Redraw() - nnoremap {PROMPT} input({"prompt": ":", "highlight": g:Nvim_color_input})[1:0] + nnoremap {PROMPT} extend(g:, {"out": input({"prompt": ":", "highlight": g:Nvim_color_input})}).EMPTY function RainBowParens(cmdline) let ret = [] let i = 0 @@ -295,13 +296,34 @@ describe('Command-line coloring', function() start_prompt('e') -- FIXME Does not work well with :throw: error message overwrites cmdline. end) - pending('stops executing callback after a number of errors'--[[, function() + it('stops executing callback after a number of errors', function() set_color_cb('SplittedMultibyteStart') start_prompt('let x = "«»«»«»«»«»"\n') - eq('«»«»«»«»«»', meths.get_var('x')) + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :let x = " | + {ERR:E5405: Chunk 0 start 10 splits multibyte}| + {ERR: character} | + ^:let x = "«»«»«»«»«»" | + ]]) + feed('\n') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + eq('let x = "«»«»«»«»«»"', meths.get_var('out')) local msg = '\nE5405: Chunk 0 start 10 splits multibyte character' eq(msg:rep(1), funcs.execute('messages')) - end]]) + end) it('allows interrupting callback with ', function() if true then return pending(' does not work well enough now') end set_color_cb('Halting') -- cgit From 0ed95423de714edac11ccff177b8aee6b7987bac Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 14:26:23 +0300 Subject: ex_getln: Call highlight callback inside :try --- src/nvim/ex_getln.c | 31 +++++++++++++-------------- test/functional/ui/cmdline_highlight_spec.lua | 15 +++++++++---- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f9b2d6cda8..9c494a2640 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -63,6 +63,7 @@ #include "nvim/event/loop.h" #include "nvim/os/time.h" #include "nvim/lib/kvec.h" +#include "nvim/api/private/helpers.h" /* * Variables shared between getcmdline(), redrawcmdline() and others. @@ -2217,6 +2218,7 @@ static bool color_cmdline(void) static int prev_prompt_errors = 0; Callback color_cb = { .type = kCallbackNone }; bool can_free_cb = false; + Error err = ERROR_INIT; if (ccline.input_fn) { color_cb = getln_input_callback; @@ -2259,16 +2261,16 @@ static bool color_cmdline(void) // appears shifted one character to the right and cursor position is no longer // correct, with msg_col it just misses leading `:`. Since `redraw!` in // callback lags this is least of the user problems. + // + // Also using try_start() because error messages may overwrite typed + // command-line which is not expected. + try_start(); const int saved_msg_col = msg_col; msg_silent++; - if (!callback_call(&color_cb, 1, &arg, &tv)) { - msg_silent--; - msg_col = saved_msg_col; - goto color_cmdline_error; - } + const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv); msg_silent--; msg_col = saved_msg_col; - if (got_int || did_emsg) { + if (try_end(&err) || !cbcall_ret) { goto color_cmdline_error; } if (tv.v_type != VAR_LIST) { @@ -2350,6 +2352,7 @@ static bool color_cmdline(void) } prev_prompt_errors = 0; color_cmdline_end: + assert(!ERROR_SET(&err)); if (can_free_cb) { callback_free(&color_cb); } @@ -2360,18 +2363,14 @@ color_cmdline_end: tv_clear(&tv); return ret; color_cmdline_error: - prev_prompt_errors++; - const bool do_redraw = (did_emsg || got_int); - got_int = false; - did_emsg = false; - if (did_throw) { - discard_current_exception(); - } - if (msg_list != NULL && *msg_list != NULL) { - free_global_msglist(); + if (ERROR_SET(&err)) { + emsgf(_("E5407: Callback has thrown an exception: %s"), err.msg); + api_clear_error(&err); } + prev_prompt_errors++; kv_size(ccline_colors) = 0; - if (do_redraw) { + if (did_emsg) { + did_emsg = false; prev_prompt_errors += MAX_CB_ERRORS; redrawcmdline(); prev_prompt_errors -= MAX_CB_ERRORS; diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 12577a4db4..8207a903f4 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -285,16 +285,23 @@ describe('Command-line coloring', function() ]]) end) it('does the right thing when errorring', function() - if true then return pending('echoerr does not work well now') end set_color_cb('Echoerring') start_prompt('e') - -- FIXME Does not work well with :echoerr: error message overwrites cmdline. + -- FIXME Does not work well with :echoerr: error message not shown. end) it('does the right thing when throwing', function() - if true then return pending('Throwing does not work well now') end set_color_cb('Throwing') start_prompt('e') - -- FIXME Does not work well with :throw: error message overwrites cmdline. + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + : | + {ERR:E5407: Callback has thrown an exception:}| + {ERR: ABC} | + :e^ | + ]]) end) it('stops executing callback after a number of errors', function() set_color_cb('SplittedMultibyteStart') -- cgit From 5e4976559a0f3e104b3f349593ad184d08c56a89 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 14:34:12 +0300 Subject: functests: Partially uncomment test --- test/functional/ui/cmdline_highlight_spec.lua | 40 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 8207a903f4..f356db88b5 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -332,22 +332,34 @@ describe('Command-line coloring', function() eq(msg:rep(1), funcs.execute('messages')) end) it('allows interrupting callback with ', function() - if true then return pending(' does not work well enough now') end set_color_cb('Halting') start_prompt('echo 42') - for i = 1, 6 do - screen:expect([[ - ^ | - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - {EOB:~ }| - | - ]]) - feed('') - end + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + feed('') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + : | + {ERR:E5407: Callback has thrown an exception:}| + {ERR: Keyboard interrupt} | + ^ | + ]]) + if true then return pending(' should only cancel callback, not input()') end + feed('{REDRAW}') + screen:snapshot_util() + feed('') + eq('echo 42', meths.get_var('out')) screen:expect([[ ^ | {EOB:~ }| -- cgit From 9ccb3abbb55ec8ed7f3b4d2d2516fd451eca9a68 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 14:39:52 +0300 Subject: functests: Uncomment `{REDRAW}` part of “works” test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/functional/ui/cmdline_highlight_spec.lua | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index f356db88b5..a3dc9af360 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -214,18 +214,20 @@ describe('Command-line coloring', function() {EOB:~ }| :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | ]]) - -- FIXME - -- feed('{REDRAW}') - -- screen:expect([[ - -- | - -- {EOB:~ }| - -- {EOB:~ }| - -- {EOB:~ }| - -- {EOB:~ }| - -- {EOB:~ }| - -- {EOB:~ }| - -- :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | - -- ]]) + -- Bug in input() handling: {REDRAW} will erase the whole prompt up until + -- user types something. It exists in Vim as well, so using `h` as + -- a workaround. + feed('{REDRAW}h') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | + ]]) end) for _, func_part in ipairs({'', 'n', 'msg'}) do it('disables :echo' .. func_part .. ' messages', function() -- cgit From 3da49cd68e7d5c968cc99a926819038ff57f488f Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 22:09:10 +0300 Subject: ex_getln: Fix “echoerr msg not shown” problem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also attempted to fix problem with cancelling input() on error by avoiding standard error printing facilities (assumed thrown error message is the problem), but with no luck so far. --- src/nvim/ex_getln.c | 52 +++++++++++++++------------ src/nvim/message.c | 21 +++++++++++ test/functional/ui/cmdline_highlight_spec.lua | 11 +++++- 3 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9c494a2640..444a3e5b11 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -64,6 +64,7 @@ #include "nvim/os/time.h" #include "nvim/lib/kvec.h" #include "nvim/api/private/helpers.h" +#include "nvim/highlight_defs.h" /* * Variables shared between getcmdline(), redrawcmdline() and others. @@ -2202,6 +2203,13 @@ enum { MAX_CB_ERRORS = 1 }; /// to do. static bool color_cmdline(void) { + bool printed_errmsg = false; +#define PRINT_ERRMSG(...) \ + do { \ + msg_putchar('\n'); \ + msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, __VA_ARGS__); \ + printed_errmsg = true; \ + } while (0) bool ret = true; kv_size(ccline_colors) = 0; @@ -2274,7 +2282,7 @@ static bool color_cmdline(void) goto color_cmdline_error; } if (tv.v_type != VAR_LIST) { - emsgf(_("E5400: Callback should return list")); + PRINT_ERRMSG(_("E5400: Callback should return list")); goto color_cmdline_error; } if (tv.vval.v_list == NULL) { @@ -2285,13 +2293,13 @@ static bool color_cmdline(void) for (const listitem_T *li = tv.vval.v_list->lv_first; li != NULL; li = li->li_next, i++) { if (li->li_tv.v_type != VAR_LIST) { - emsgf(_("E5401: List item %i is not a List"), i); + PRINT_ERRMSG(_("E5401: List item %i is not a List"), i); goto color_cmdline_error; } const list_T *const l = li->li_tv.vval.v_list; if (tv_list_len(l) != 3) { - emsgf(_("E5402: List item %i has incorrect length: %li /= 3"), - i, tv_list_len(l)); + PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"), + i, tv_list_len(l)); goto color_cmdline_error; } bool error = false; @@ -2299,13 +2307,13 @@ static bool color_cmdline(void) if (error) { goto color_cmdline_error; } else if (!(prev_end <= start && start < ccline.cmdlen)) { - emsgf(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range " - "[%" PRIdVARNUMBER ", %i)"), - i, start, prev_end, ccline.cmdlen); + PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range " + "[%" PRIdVARNUMBER ", %i)"), + i, start, prev_end, ccline.cmdlen); goto color_cmdline_error; } else if (utf8len_tab_zero[(uint8_t)ccline.cmdbuff[start]] == 0) { - emsgf(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits multibyte " - "character"), i, start); + PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits " + "multibyte character"), i, start); goto color_cmdline_error; } if (start != prev_end) { @@ -2320,14 +2328,14 @@ static bool color_cmdline(void) if (error) { goto color_cmdline_error; } else if (!(start < end && end <= ccline.cmdlen)) { - emsgf(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range " - "(%" PRIdVARNUMBER ", %i]"), - i, end, start, ccline.cmdlen); + PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range " + "(%" PRIdVARNUMBER ", %i]"), + i, end, start, ccline.cmdlen); goto color_cmdline_error; } else if (end < ccline.cmdlen && utf8len_tab_zero[(uint8_t)ccline.cmdbuff[end]] == 0) { - emsgf(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte " - "character"), i, end); + PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte " + "character"), i, end); goto color_cmdline_error; } prev_end = end; @@ -2364,19 +2372,19 @@ color_cmdline_end: return ret; color_cmdline_error: if (ERROR_SET(&err)) { - emsgf(_("E5407: Callback has thrown an exception: %s"), err.msg); + PRINT_ERRMSG(_("E5407: Callback has thrown an exception: %s"), err.msg); api_clear_error(&err); } + assert(printed_errmsg); + prev_prompt_errors++; kv_size(ccline_colors) = 0; - if (did_emsg) { - did_emsg = false; - prev_prompt_errors += MAX_CB_ERRORS; - redrawcmdline(); - prev_prompt_errors -= MAX_CB_ERRORS; - ret = false; - } + prev_prompt_errors += MAX_CB_ERRORS; + redrawcmdline(); + prev_prompt_errors -= MAX_CB_ERRORS; + ret = false; goto color_cmdline_end; +#undef PRINT_ERRMSG } /* diff --git a/src/nvim/message.c b/src/nvim/message.c index 057ce75f79..ab918712e5 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1611,6 +1611,27 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) } } +/// Print a formatted message +/// +/// Message printed is limited by #IOSIZE. Must not be used from inside +/// msg_puts_attr(). +/// +/// @param[in] attr Highlight attributes. +/// @param[in] fmt Format string. +void msg_printf_attr(const int attr, const char *const fmt, ...) + FUNC_ATTR_NONNULL_ALL +{ + static char msgbuf[IOSIZE]; + + va_list ap; + va_start(ap, fmt); + const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap, NULL); + va_end(ap); + + msg_scroll = true; + msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr); +} + /* * The display part of msg_puts_attr_len(). * May be called recursively to display scroll-back text. diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index a3dc9af360..dbabf65b68 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -289,7 +289,16 @@ describe('Command-line coloring', function() it('does the right thing when errorring', function() set_color_cb('Echoerring') start_prompt('e') - -- FIXME Does not work well with :echoerr: error message not shown. + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + : | + {ERR:E5407: Callback has thrown an exception:}| + {ERR: Vim(echoerr):HERE} | + :e^ | + ]]) end) it('does the right thing when throwing', function() set_color_cb('Throwing') -- cgit From 99079a164db6ebea6d62f349e0c69894c6b9f799 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 22:20:47 +0300 Subject: ex_getln: Make sure standard error reporting facility is not used --- src/nvim/ex_getln.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 444a3e5b11..7330bebe62 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2227,24 +2227,30 @@ static bool color_cmdline(void) Callback color_cb = { .type = kCallbackNone }; bool can_free_cb = false; Error err = ERROR_INIT; + const char *err_errmsg = (const char *)e_intern2; + bool dgc_ret = true; + try_start(); if (ccline.input_fn) { color_cb = getln_input_callback; } else if (ccline.cmdfirstc == ':') { - if (!tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), - &color_cb)) { - goto color_cmdline_error; - } + err_errmsg = N_( + "E5408: Unable to get Nvim_color_cmdline callback from g:: %s"); + dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), + &color_cb); can_free_cb = true; } else if (ccline.cmdfirstc == '=') { - if (!tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"), - &color_cb)) { - goto color_cmdline_error; - } + err_errmsg = N_( + "E5409: Unable to get Nvim_color_expr callback from g:: %s"); + dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"), + &color_cb); can_free_cb = true; } else { goto color_cmdline_end; } + if (try_end(&err) || !dgc_ret) { + goto color_cmdline_error; + } if (color_cb.type == kCallbackNone) { goto color_cmdline_end; @@ -2273,6 +2279,7 @@ static bool color_cmdline(void) // Also using try_start() because error messages may overwrite typed // command-line which is not expected. try_start(); + err_errmsg = N_("E5407: Callback has thrown an exception: %s"); const int saved_msg_col = msg_col; msg_silent++; const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv); @@ -2372,7 +2379,7 @@ color_cmdline_end: return ret; color_cmdline_error: if (ERROR_SET(&err)) { - PRINT_ERRMSG(_("E5407: Callback has thrown an exception: %s"), err.msg); + PRINT_ERRMSG(_(err_errmsg), err.msg); api_clear_error(&err); } assert(printed_errmsg); -- cgit From 564d5f921c837e278f7e1aefeb7d832d08a63e97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 22:21:37 +0300 Subject: ex_getln: Fix indent --- src/nvim/ex_getln.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 7330bebe62..e10a485f76 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2232,7 +2232,7 @@ static bool color_cmdline(void) try_start(); if (ccline.input_fn) { - color_cb = getln_input_callback; + color_cb = getln_input_callback; } else if (ccline.cmdfirstc == ':') { err_errmsg = N_( "E5408: Unable to get Nvim_color_cmdline callback from g:: %s"); -- cgit From ea75966e4232dc4a3693cbc4a572f2116c49b138 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 28 Jun 2017 22:54:13 +0300 Subject: ex_getln: Do not make interrupt input() after interrupting hl cb --- src/nvim/ex_getln.c | 17 +++++++++-- test/functional/ui/cmdline_highlight_spec.lua | 42 ++++++++++++++++++--------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e10a485f76..c23d6089a3 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -166,6 +166,12 @@ static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0}; /* identifying (unique) number of newest history entry */ static int hislen = 0; /* actual length of history tables */ +/// Flag for command_line_handle_key to ignore +/// +/// Used if it was received while processing highlight function in order for +/// user interrupting highlight function to not interrupt command-line. +static bool getln_interrupted_highlight = false; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.c.generated.h" @@ -1026,8 +1032,11 @@ static int command_line_handle_key(CommandLineState *s) case ESC: // get here if p_wc != ESC or when ESC typed twice case Ctrl_C: // In exmode it doesn't make sense to return. Except when - // ":normal" runs out of characters. - if (exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) { + // ":normal" runs out of characters. Also when highlight callback is active + // should interrupt only it. + if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) + || (getln_interrupted_highlight && s->c == Ctrl_C)) { + getln_interrupted_highlight = false; return command_line_not_changed(s); } @@ -2278,6 +2287,7 @@ static bool color_cmdline(void) // // Also using try_start() because error messages may overwrite typed // command-line which is not expected. + getln_interrupted_highlight = false; try_start(); err_errmsg = N_("E5407: Callback has thrown an exception: %s"); const int saved_msg_col = msg_col; @@ -2285,6 +2295,9 @@ static bool color_cmdline(void) const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv); msg_silent--; msg_col = saved_msg_col; + if (got_int) { + getln_interrupted_highlight = true; + } if (try_end(&err) || !cbcall_ret) { goto color_cmdline_error; } diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index dbabf65b68..671490e668 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -10,6 +10,13 @@ local source = helpers.source local screen +-- Bug in input() handling: {REDRAW} will erase the whole prompt up until +-- user types something. It exists in Vim as well, so using `h` as +-- a workaround. +local function redraw_input() + feed('{REDRAW}h') +end + before_each(function() clear() screen = Screen.new(40, 8) @@ -214,10 +221,7 @@ describe('Command-line coloring', function() {EOB:~ }| :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ | ]]) - -- Bug in input() handling: {REDRAW} will erase the whole prompt up until - -- user types something. It exists in Vim as well, so using `h` as - -- a workaround. - feed('{REDRAW}h') + redraw_input() screen:expect([[ | {EOB:~ }| @@ -364,24 +368,33 @@ describe('Command-line coloring', function() : | {ERR:E5407: Callback has thrown an exception:}| {ERR: Keyboard interrupt} | - ^ | + :echo 42^ | ]]) - if true then return pending(' should only cancel callback, not input()') end - feed('{REDRAW}') - screen:snapshot_util() - feed('') - eq('echo 42', meths.get_var('out')) + redraw_input() screen:expect([[ - ^ | + | {EOB:~ }| {EOB:~ }| {EOB:~ }| {EOB:~ }| {EOB:~ }| {EOB:~ }| - Type :quit to exit Nvim | + :echo 42^ | ]]) - start_prompt('echo 42') + feed('\n') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ^:echo 42 | + ]]) + feed('\n') + eq('echo 42', meths.get_var('out')) + feed('') screen:expect([[ ^ | {EOB:~ }| @@ -390,7 +403,7 @@ describe('Command-line coloring', function() {EOB:~ }| {EOB:~ }| {EOB:~ }| - 42 | + Type :quit to exit Nvim | ]]) end) it('works fine with NUL, NL, CR', function() @@ -420,3 +433,4 @@ describe('Ex commands coloring support', function() end) -- TODO Specifically test for coloring in cmdline and expr modes +-- TODO Check using highlighted input() from inside highlighted input() -- cgit From 7ab152aaa58f493e54d03a15960b8a288196e588 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 1 Jul 2017 15:34:25 +0300 Subject: ex_getln: Save and restore try state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: when processing cycle such as :for pat in [' \ze*', ' \zs*'] : try : let l = matchlist('x x', pat) : $put ='E888 NOT detected for ' . pat : catch : $put ='E888 detected for ' . pat : endtry :endfor `:let l = …` throwing an error causes this error to be caught after color_cmdline attempts to get callback for highlighting next line (the one with `$put = 'E888 NOT…`). Saving/restoring state prevents this from happening. --- src/nvim/api/private/helpers.c | 46 +++++++++++++++++++++++++++ src/nvim/api/private/helpers.h | 12 +++++++ src/nvim/ex_getln.c | 16 +++++++--- test/functional/ui/cmdline_highlight_spec.lua | 24 ++++++++++++++ 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d401ae52a0..883a5a2fd1 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -37,6 +37,52 @@ typedef struct { # include "api/private/ui_events_metadata.generated.h" #endif +/// Start block that may cause VimL exceptions while evaluating another code +/// +/// Used when caller is supposed to be operating when other VimL code is being +/// processed and that “other VimL code” must not be affected. +/// +/// @param[out] tstate Location where try state should be saved. +void try_enter(TryState *const tstate) +{ + *tstate = (TryState) { + .trylevel = trylevel, + .got_int = got_int, + .did_throw = did_throw, + .msg_list = (const struct msglist *const *)msg_list, + .private_msg_list = NULL, + }; + trylevel = 1; + got_int = false; + did_throw = false; + msg_list = &tstate->private_msg_list; +} + +/// End try block, set the error message if any and restore previous state +/// +/// @warning Return is consistent with most functions (false on error), not with +/// try_end (true on error). +/// +/// @param[in] tstate Previous state to restore. +/// @param[out] err Location where error should be saved. +/// +/// @return false if error occurred, true otherwise. +bool try_leave(const TryState *const tstate, Error *const err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + const bool ret = !try_end(err); + assert(trylevel == 0); + assert(!got_int); + assert(!did_throw); + assert(msg_list == &tstate->private_msg_list); + assert(*msg_list == NULL); + trylevel = tstate->trylevel; + got_int = tstate->got_int; + did_throw = tstate->did_throw; + msg_list = (struct msglist **)tstate->msg_list; + return ret; +} + /// Start block that may cause vimscript exceptions void try_start(void) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 159b9d5c2a..112d785bfd 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -82,6 +82,18 @@ #define api_free_window(value) #define api_free_tabpage(value) +/// Structure used for saving state for :try +/// +/// Used when caller is supposed to be operating when other VimL code is being +/// processed and that “other VimL code” must not be affected. +typedef struct { + int trylevel; + int got_int; + int did_throw; + struct msglist *private_msg_list; + const struct msglist *const *msg_list; +} TryState; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.h.generated.h" #endif diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index c23d6089a3..e6cd7398ab 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2222,6 +2222,11 @@ static bool color_cmdline(void) bool ret = true; kv_size(ccline_colors) = 0; + if (ccline.cmdbuff == NULL || *ccline.cmdbuff == NUL) { + // Nothing to do, exiting. + return ret; + } + const int saved_force_abort = force_abort; force_abort = true; bool arg_allocated = false; @@ -2235,11 +2240,12 @@ static bool color_cmdline(void) static int prev_prompt_errors = 0; Callback color_cb = { .type = kCallbackNone }; bool can_free_cb = false; + TryState tstate; Error err = ERROR_INIT; const char *err_errmsg = (const char *)e_intern2; bool dgc_ret = true; - try_start(); + try_enter(&tstate); if (ccline.input_fn) { color_cb = getln_input_callback; } else if (ccline.cmdfirstc == ':') { @@ -2257,7 +2263,7 @@ static bool color_cmdline(void) } else { goto color_cmdline_end; } - if (try_end(&err) || !dgc_ret) { + if (!try_leave(&tstate, &err) || !dgc_ret) { goto color_cmdline_error; } @@ -2285,10 +2291,10 @@ static bool color_cmdline(void) // correct, with msg_col it just misses leading `:`. Since `redraw!` in // callback lags this is least of the user problems. // - // Also using try_start() because error messages may overwrite typed + // Also using try_enter() because error messages may overwrite typed // command-line which is not expected. getln_interrupted_highlight = false; - try_start(); + try_enter(&tstate); err_errmsg = N_("E5407: Callback has thrown an exception: %s"); const int saved_msg_col = msg_col; msg_silent++; @@ -2298,7 +2304,7 @@ static bool color_cmdline(void) if (got_int) { getln_interrupted_highlight = true; } - if (try_end(&err) || !cbcall_ret) { + if (!try_leave(&tstate, &err) || !cbcall_ret) { goto color_cmdline_error; } if (tv.v_type != VAR_LIST) { diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 671490e668..62c0694c8c 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -7,6 +7,8 @@ local clear = helpers.clear local meths = helpers.meths local funcs = helpers.funcs local source = helpers.source +local dedent = helpers.dedent +local curbufmeths = helpers.curbufmeths local screen @@ -421,6 +423,8 @@ describe('Command-line coloring', function() ]]) end) -- TODO Check for all other errors + -- TODO Check for colored input() called in a cycle which previously errorred + -- out end) describe('Ex commands coloring support', function() it('still executes command-line even if errored out', function() @@ -430,7 +434,27 @@ describe('Ex commands coloring support', function() local msg = 'E5405: Chunk 0 start 10 splits multibyte character' eq('\n'..msg, funcs.execute('messages')) end) + it('does not error out when called from a errorred out cycle', function() + -- Apparently when there is a cycle in which one of the commands errors out + -- this error may be caught by color_cmdline before it is presented to the + -- user. + feed(dedent([[ + :set regexpengine=2 + :for pat in [' \ze*', ' \zs*'] + : try + : let l = matchlist('x x', pat) + : $put ='E888 NOT detected for ' . pat + : catch + : $put ='E888 detected for ' . pat + : endtry + :endfor + ]])) + eq({'', 'E888 detected for \\ze*', 'E888 detected for \\zs*'}, + curbufmeths.get_lines(0, -1, false)) + eq('', funcs.execute('messages')) + end) end) -- TODO Specifically test for coloring in cmdline and expr modes +-- TODO Check for errors from tv_dict_get_callback() -- TODO Check using highlighted input() from inside highlighted input() -- cgit From 3660535f0229afc4ce3391d94794253f685ec400 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Jul 2017 21:17:24 +0300 Subject: oldtests: Use $(TOOL) in place of $(VALGRIND) --- src/nvim/testdir/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 510e8820f4..9ba4e4b3c1 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -201,7 +201,7 @@ nolog: # New style of tests uses Vim script with assert calls. These are easier # to write and a lot easier to read and debug. # Limitation: Only works with the +eval feature. -RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(NVIM_PRG) -u unix.vim -U NONE --noplugin +RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE --noplugin newtests: newtestssilent @/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \ -- cgit From 2a6423eba732b005e277bac393f2246308dcc378 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 16 Jul 2017 22:03:31 +0300 Subject: api helpers: Save/restore more values in try_enter/try_leave This fixes memory leak reported by ASAN. This also somehow fixes test40, though I have no idea why except that that test yields memory leak report. --- src/nvim/api/private/helpers.c | 9 +++++++++ src/nvim/api/private/helpers.h | 3 +++ src/nvim/ex_eval.c | 2 ++ src/nvim/ex_getln.c | 3 --- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index a04cc9a312..6ff56709cd 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -49,13 +49,17 @@ void try_enter(TryState *const tstate) .trylevel = trylevel, .got_int = got_int, .did_throw = did_throw, + .need_rethrow = need_rethrow, + .current_exception = current_exception, .msg_list = (const struct msglist *const *)msg_list, .private_msg_list = NULL, }; trylevel = 1; got_int = false; did_throw = false; + need_rethrow = false; msg_list = &tstate->private_msg_list; + current_exception = NULL; } /// End try block, set the error message if any and restore previous state @@ -72,14 +76,17 @@ bool try_leave(const TryState *const tstate, Error *const err) { const bool ret = !try_end(err); assert(trylevel == 0); + assert(!need_rethrow); assert(!got_int); assert(!did_throw); assert(msg_list == &tstate->private_msg_list); assert(*msg_list == NULL); + assert(current_exception == NULL); trylevel = tstate->trylevel; got_int = tstate->got_int; did_throw = tstate->did_throw; msg_list = (struct msglist **)tstate->msg_list; + current_exception = tstate->current_exception; return ret; } @@ -96,6 +103,8 @@ void try_start(void) /// @return true if an error occurred bool try_end(Error *err) { + // Note: all globals manipulated here should be saved/restored in + // try_enter/try_leave. --trylevel; // Without this it stops processing all subsequent VimL commands and diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 112d785bfd..0b2cf883a6 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -6,6 +6,7 @@ #include "nvim/api/private/defs.h" #include "nvim/vim.h" #include "nvim/memory.h" +#include "nvim/ex_eval.h" #include "nvim/lib/kvec.h" #define OBJECT_OBJ(o) o @@ -90,6 +91,8 @@ typedef struct { int trylevel; int got_int; int did_throw; + int need_rethrow; + except_T *current_exception; struct msglist *private_msg_list; const struct msglist *const *msg_list; } TryState; diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 5d664b94a8..139305998d 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -565,6 +565,8 @@ static void discard_exception(except_T *excp, int was_finished) void discard_current_exception(void) { discard_exception(current_exception, FALSE); + // Note: all globals manipulated here should be saved/restored in + // try_enter/try_leave. current_exception = NULL; did_throw = FALSE; need_rethrow = FALSE; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6c61e30f3d..275e1b7fdd 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2341,8 +2341,6 @@ static bool color_cmdline(void) return ret; } - const int saved_force_abort = force_abort; - force_abort = true; bool arg_allocated = false; typval_T arg = { .v_type = VAR_STRING, @@ -2504,7 +2502,6 @@ color_cmdline_end: if (can_free_cb) { callback_free(&color_cb); } - force_abort = saved_force_abort; if (arg_allocated) { tv_clear(&arg); } -- cgit From f4744e18219726d2eaa57b26198166ea255c62a4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Jul 2017 01:55:10 +0300 Subject: ex_getln: Do not goto color_cmdline_end without first cleaning up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The issue with debug mode was actually not cleaning up after `try_enter`: location `&tstate` was pointing to got invalidated and received some “garbage” (actually, values that got stored on the stack afterwards). But pointer to that garbage was still stored in `msg_list`, so next attempt to check it resulted in a crash. --- src/nvim/ex_getln.c | 2 -- test/functional/ui/cmdline_highlight_spec.lua | 36 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 275e1b7fdd..1052053ddf 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2372,8 +2372,6 @@ static bool color_cmdline(void) dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"), &color_cb); can_free_cb = true; - } else { - goto color_cmdline_end; } if (!try_leave(&tstate, &err) || !dgc_ret) { goto color_cmdline_error; diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 62c0694c8c..0621b990de 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -121,6 +121,7 @@ before_each(function() EOB={bold = true, foreground = Screen.colors.Blue1}, ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red}, SK={foreground = Screen.colors.Blue}, + PE={bold = true, foreground = Screen.colors.SeaGreen4} }) end) @@ -453,6 +454,41 @@ describe('Ex commands coloring support', function() curbufmeths.get_lines(0, -1, false)) eq('', funcs.execute('messages')) end) + it('does not crash when using `n` in debug mode', function() + feed(':debug execute "echo 1"\n') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + Entering Debug mode. Type "cont" to con| + tinue. | + cmd: execute "echo 1" | + >^ | + ]]) + feed('n\n') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + Entering Debug mode. Type "cont" to con| + tinue. | + cmd: execute "echo 1" | + >n | + 1 | + {PE:Press ENTER or type command to continue}^ | + ]]) + feed('\n') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + end) end) -- TODO Specifically test for coloring in cmdline and expr modes -- cgit From dc0a496d41ed23106632d12bbc33679997281c73 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Jul 2017 01:57:27 +0300 Subject: ex_getln: Do not do useless try_enter/try_leave calls These are actually needed for two modes only. And even for these modes they should eventually go away. --- src/nvim/ex_getln.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 1052053ddf..9bcabeee72 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2356,24 +2356,28 @@ static bool color_cmdline(void) Error err = ERROR_INIT; const char *err_errmsg = (const char *)e_intern2; bool dgc_ret = true; + bool tl_ret = true; - try_enter(&tstate); if (ccline.input_fn) { color_cb = getln_input_callback; } else if (ccline.cmdfirstc == ':') { + try_enter(&tstate); err_errmsg = N_( "E5408: Unable to get Nvim_color_cmdline callback from g:: %s"); dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), &color_cb); + tl_ret = try_leave(&tstate, &err); can_free_cb = true; } else if (ccline.cmdfirstc == '=') { + try_enter(&tstate); err_errmsg = N_( "E5409: Unable to get Nvim_color_expr callback from g:: %s"); dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"), &color_cb); + tl_ret = try_leave(&tstate, &err); can_free_cb = true; } - if (!try_leave(&tstate, &err) || !dgc_ret) { + if (!tl_ret || !dgc_ret) { goto color_cmdline_error; } -- cgit From cb3c71eac916eacc2964d824ddc23460d0e91c47 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Jul 2017 02:32:32 +0300 Subject: doc: Adjust documentation a bit 2 spaces after sentense ends, clarify what will be highlighted. --- runtime/doc/eval.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e51f0f5dfc..b45eceb97a 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4725,9 +4725,10 @@ input({opts}) let fname = input("File: ", "", "file") < The optional highlight key allows specifying function which - will be used for highlighting. This function receives user - input as its only argument and must return a list of 3-tuples - [hl_start_byte, hl_end_byte + 1, hl_group] where + will be used for highlighting user input. This function + receives user input as its only argument and must return + a list of 3-tuples [hl_start_byte, hl_end_byte + 1, hl_group] + where hl_start_byte is the first highlighted byte, hl_end_byte is the last highlighted byte (+ 1!), hl_group is |:hl| group used for highlighting. @@ -4735,7 +4736,7 @@ input({opts}) Both hl_start_byte and hl_end_byte + 1 must point to the start of the multibyte character (highlighting must not break multibyte characters), hl_end_byte + 1 may be equal to the - input length. Start column must be in range [0, len(input)), + input length. Start column must be in range [0, len(input)), end column must be in range (hl_start_byte, len(input)], sections must be ordered so that next hl_start_byte is greater then or equal to previous hl_end_byte. -- cgit From 3a923ad2db87b2bece89616b28a14ab9826d569a Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 17 Jul 2017 02:33:18 +0300 Subject: ex_getln: Replace global with entry in save_ccline --- src/nvim/eval.c | 7 ++--- src/nvim/eval/typval.h | 2 +- src/nvim/ex_cmds2.c | 3 ++- src/nvim/ex_getln.c | 70 ++++++++++++++++++++++++++++---------------------- src/nvim/ex_getln.h | 2 -- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 694c34731d..afa6711645 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11108,13 +11108,10 @@ void get_user_input(const typval_T *const argvars, stuffReadbuffSpec(defstr); const int save_ex_normal_busy = ex_normal_busy; - const Callback save_getln_input_callback = getln_input_callback; ex_normal_busy = 0; - getln_input_callback = input_callback; rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, - xp_type, (char_u *)xp_arg); - getln_input_callback = save_getln_input_callback; + (char_u *)getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, + xp_type, xp_arg, input_callback); ex_normal_busy = save_ex_normal_busy; callback_free(&input_callback); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3f8ed3b3f9..c44b85644d 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -43,7 +43,7 @@ typedef struct partial_S partial_T; typedef struct ufunc ufunc_T; typedef enum { - kCallbackNone, + kCallbackNone = 0, kCallbackFuncref, kCallbackPartial, } CallbackType; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 1a728647ca..79882973b1 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -189,7 +189,8 @@ void do_debug(char_u *cmd) } xfree(cmdline); - cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); + cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL, + CALLBACK_NONE); if (typeahead_saved) { restore_typeahead(&typeaheadbuf); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9bcabeee72..13bae4dadc 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -89,6 +89,7 @@ struct cmdline_info { char_u *xp_arg; // user-defined expansion arg int input_fn; // when TRUE Invoked for input() function unsigned prompt_id; ///< Prompt number, used to disable coloring on errors. + Callback highlight_callback; ///< Callback used for coloring user input. }; /// Last value of prompt_id, incremented when doing new prompt static unsigned last_prompt_id = 0; @@ -148,9 +149,6 @@ typedef struct { int attr; ///< Highlight attr. } ColoredCmdlineChunk; -/// Callback used for coloring input() prompts -Callback getln_input_callback = { .type = kCallbackNone }; - kvec_t(ColoredCmdlineChunk) ccline_colors; /* The current cmdline_info. It is initialized in getcmdline() and after that @@ -1815,42 +1813,50 @@ getcmdline ( return command_line_enter(firstc, count, indent); } -/* - * Get a command line with a prompt. - * This is prepared to be called recursively from getcmdline() (e.g. by - * f_input() when evaluating an expression from CTRL-R =). - * Returns the command line in allocated memory, or NULL. - */ -char_u * -getcmdline_prompt ( - int firstc, - char_u *prompt, /* command line prompt */ - int attr, /* attributes for prompt */ - int xp_context, /* type of expansion */ - char_u *xp_arg /* user-defined expansion argument */ -) +/// Get a command line with a prompt +/// +/// This is prepared to be called recursively from getcmdline() (e.g. by +/// f_input() when evaluating an expression from `=`). +/// +/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug. +/// @param[in] prompt Prompt string: what is displayed before the user text. +/// @param[in] attr Prompt highlighting. +/// @param[in] xp_context Type of expansion. +/// @param[in] xp_arg User-defined expansion argument. +/// @param[in] highlight_callback Callback used for highlighting user input. +/// +/// @return [allocated] Command line or NULL. +char *getcmdline_prompt(const char firstc, const char *const prompt, + const int attr, const int xp_context, + const char *const xp_arg, + const Callback highlight_callback) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - char_u *s; - struct cmdline_info save_ccline; - int msg_col_save = msg_col; + const int msg_col_save = msg_col; + struct cmdline_info save_ccline; save_cmdline(&save_ccline); + ccline.prompt_id = last_prompt_id++; - ccline.cmdprompt = prompt; + ccline.cmdprompt = (char_u *)prompt; ccline.cmdattr = attr; ccline.xp_context = xp_context; - ccline.xp_arg = xp_arg; + ccline.xp_arg = (char_u *)xp_arg; ccline.input_fn = (firstc == '@'); - s = getcmdline(firstc, 1L, 0); + ccline.highlight_callback = highlight_callback; + + char *const ret = (char *)getcmdline(firstc, 1L, 0); + restore_cmdline(&save_ccline); - /* Restore msg_col, the prompt from input() may have changed it. - * But only if called recursively and the commandline is therefore being - * restored to an old one; if not, the input() prompt stays on the screen, - * so we need its modified msg_col left intact. */ - if (ccline.cmdbuff != NULL) + // Restore msg_col, the prompt from input() may have changed it. + // But only if called recursively and the commandline is therefore being + // restored to an old one; if not, the input() prompt stays on the screen, + // so we need its modified msg_col left intact. + if (ccline.cmdbuff != NULL) { msg_col = msg_col_save; + } - return s; + return ret; } /* @@ -2358,8 +2364,10 @@ static bool color_cmdline(void) bool dgc_ret = true; bool tl_ret = true; - if (ccline.input_fn) { - color_cb = getln_input_callback; + if (ccline.highlight_callback.type != kCallbackNone) { + // Currently this should only happen while processing input() prompts. + assert(ccline.input_fn); + color_cb = ccline.highlight_callback; } else if (ccline.cmdfirstc == ':') { try_enter(&tstate); err_errmsg = N_( diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 92cb274010..051564fbe1 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -52,8 +52,6 @@ typedef struct hist_entry { list_T *additional_elements; ///< Additional entries from ShaDa file. } histentry_T; -extern Callback getln_input_callback; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.h.generated.h" #endif -- cgit From cfb1d937a64fcec836fdf26d6ea67024aeafabeb Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 18 Jul 2017 00:08:57 +0300 Subject: api helpers: Also save and restore did_emsg --- src/nvim/api/private/helpers.c | 19 +++++++++----- src/nvim/api/private/helpers.h | 7 ++--- test/functional/ui/cmdline_highlight_spec.lua | 38 +++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 6ff56709cd..b6ecc319c1 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -46,20 +46,22 @@ typedef struct { void try_enter(TryState *const tstate) { *tstate = (TryState) { + .current_exception = current_exception, + .msg_list = (const struct msglist *const *)msg_list, + .private_msg_list = NULL, .trylevel = trylevel, .got_int = got_int, .did_throw = did_throw, .need_rethrow = need_rethrow, - .current_exception = current_exception, - .msg_list = (const struct msglist *const *)msg_list, - .private_msg_list = NULL, + .did_emsg = did_emsg, }; + msg_list = &tstate->private_msg_list; + current_exception = NULL; trylevel = 1; got_int = false; did_throw = false; need_rethrow = false; - msg_list = &tstate->private_msg_list; - current_exception = NULL; + did_emsg = false; } /// End try block, set the error message if any and restore previous state @@ -79,14 +81,17 @@ bool try_leave(const TryState *const tstate, Error *const err) assert(!need_rethrow); assert(!got_int); assert(!did_throw); + assert(!did_emsg); assert(msg_list == &tstate->private_msg_list); assert(*msg_list == NULL); assert(current_exception == NULL); + msg_list = (struct msglist **)tstate->msg_list; + current_exception = tstate->current_exception; trylevel = tstate->trylevel; got_int = tstate->got_int; did_throw = tstate->did_throw; - msg_list = (struct msglist **)tstate->msg_list; - current_exception = tstate->current_exception; + need_rethrow = tstate->need_rethrow; + did_emsg = tstate->did_emsg; return ret; } diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 0b2cf883a6..87f334ac30 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -88,13 +88,14 @@ /// Used when caller is supposed to be operating when other VimL code is being /// processed and that “other VimL code” must not be affected. typedef struct { + except_T *current_exception; + struct msglist *private_msg_list; + const struct msglist *const *msg_list; int trylevel; int got_int; int did_throw; int need_rethrow; - except_T *current_exception; - struct msglist *private_msg_list; - const struct msglist *const *msg_list; + int did_emsg; } TryState; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 0621b990de..6698ced596 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -489,6 +489,44 @@ describe('Ex commands coloring support', function() | ]]) end) + it('does not prevent mapping error from cancelling prompt', function() + meths.command("cnoremap x execute('throw 42')[-1]") + feed(':#x') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :# | + {ERR:Error detected while processing :} | + {ERR:E605: Exception not caught: 42} | + :#^ | + ]]) + feed('') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + feed('') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + eq('\nError detected while processing :\nE605: Exception not caught: 42', + meths.command_output('messages')) + end) end) -- TODO Specifically test for coloring in cmdline and expr modes -- cgit From 8a581b918b339b84b5abd80919416a84932eb13f Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 18 Jul 2017 00:20:21 +0300 Subject: ex_getln: Check prev_prompt_errors before running redrawcmdline Otherwise there will be infinite recursion and shortly a crash. Running redrawcmdline recursively occurs under color_cmdline_error label. --- src/nvim/ex_getln.c | 12 ++++++------ test/functional/ui/cmdline_highlight_spec.lua | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 13bae4dadc..4be0dd0e0d 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2364,6 +2364,12 @@ static bool color_cmdline(void) bool dgc_ret = true; bool tl_ret = true; + if (ccline.prompt_id != prev_prompt_id) { + prev_prompt_errors = 0; + prev_prompt_id = ccline.prompt_id; + } else if (prev_prompt_errors >= MAX_CB_ERRORS) { + goto color_cmdline_end; + } if (ccline.highlight_callback.type != kCallbackNone) { // Currently this should only happen while processing input() prompts. assert(ccline.input_fn); @@ -2392,12 +2398,6 @@ static bool color_cmdline(void) if (color_cb.type == kCallbackNone) { goto color_cmdline_end; } - if (ccline.prompt_id != prev_prompt_id) { - prev_prompt_errors = 0; - prev_prompt_id = ccline.prompt_id; - } else if (prev_prompt_errors >= MAX_CB_ERRORS) { - goto color_cmdline_end; - } if (ccline.cmdbuff[ccline.cmdlen] != NUL) { arg_allocated = true; arg.vval.v_string = xmemdupz((const char *)ccline.cmdbuff, diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 6698ced596..e12961039a 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -527,6 +527,22 @@ describe('Ex commands coloring support', function() eq('\nError detected while processing :\nE605: Exception not caught: 42', meths.command_output('messages')) end) + it('errors out when failing to get callback', function() + meths.set_var('Nvim_color_cmdline', 42) + feed(':#') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + : | + {ERR:E5408: Unable to get Nvim_color_cmdline }| + {ERR:callback from g:: Vim:E6000: Argument is}| + {ERR: not a function or function name} | + :#^ | + ]]) + end) +end) +describe('Expressions coloring support', function() end) -- TODO Specifically test for coloring in cmdline and expr modes -- cgit From 759f71d50e506112f63d3db80ce823e013681476 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 18 Jul 2017 00:34:39 +0300 Subject: functests: Check for previously unchecked errors --- test/functional/ui/cmdline_highlight_spec.lua | 100 +++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index e12961039a..8bfeaf35ab 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -8,6 +8,7 @@ local meths = helpers.meths local funcs = helpers.funcs local source = helpers.source local dedent = helpers.dedent +local command = helpers.command local curbufmeths = helpers.curbufmeths local screen @@ -112,6 +113,12 @@ before_each(function() while 1 endwhile endfunction + function ReturningGlobal(cmdline) + return g:callback_return + endfunction + function ReturningGlobal2(cmdline) + return g:callback_return[:len(a:cmdline)-1] + endfunction ]]) screen:set_default_attr_ids({ RBP1={background = Screen.colors.Red}, @@ -125,8 +132,11 @@ before_each(function() }) end) -local function set_color_cb(funcname) +local function set_color_cb(funcname, callback_return) meths.set_var('Nvim_color_input', funcname) + if callback_return then + meths.set_var('callback_return', callback_return) + end end local function start_prompt(text) feed('{PROMPT}' .. (text or '')) @@ -423,7 +433,77 @@ describe('Command-line coloring', function() :echo {RBP1:(}"{SK:^M^@^@}"{RBP1:)}^ | ]]) end) - -- TODO Check for all other errors + it('errors out when callback returns something wrong', function() + command('cnoremap + ++') + set_color_cb('ReturningGlobal', '') + start_prompt('#') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + : | + {ERR:E5400: Callback should return list} | + :#^ | + ]]) + + feed('') + set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}, 42}) + start_prompt('#') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + : | + {ERR:E5401: List item 1 is not a List} | + :#^ | + ]]) + + feed('') + set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1}}) + start_prompt('+') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :+ | + {ERR:E5402: List item 1 has incorrect length:}| + {ERR: 1 /= 3} | + :++^ | + ]]) + + feed('') + set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {2, 3, 'Normal'}}) + start_prompt('+') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :+ | + {ERR:E5403: Chunk 1 start 2 not in range [1, }| + {ERR:2)} | + :++^ | + ]]) + + feed('') + set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1, 3, 'Normal'}}) + start_prompt('+') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :+ | + {ERR:E5404: Chunk 1 end 3 not in range (1, 2]}| + | + :++^ | + ]]) + end) -- TODO Check for colored input() called in a cycle which previously errorred -- out end) @@ -490,7 +570,7 @@ describe('Ex commands coloring support', function() ]]) end) it('does not prevent mapping error from cancelling prompt', function() - meths.command("cnoremap x execute('throw 42')[-1]") + command("cnoremap x execute('throw 42')[-1]") feed(':#x') screen:expect([[ {EOB:~ }| @@ -543,6 +623,20 @@ describe('Ex commands coloring support', function() end) end) describe('Expressions coloring support', function() + it('errors out when failing to get callback', function() + meths.set_var('Nvim_color_expr', 42) + feed(':=1') + screen:expect([[ + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + = | + {ERR:E5409: Unable to get Nvim_color_expr cal}| + {ERR:lback from g:: Vim:E6000: Argument is no}| + {ERR:t a function or function name} | + =1^ | + ]]) + end) end) -- TODO Specifically test for coloring in cmdline and expr modes -- cgit From 25f669049cced029e1530a74e9490e61432433ac Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 18 Jul 2017 01:16:38 +0300 Subject: functests: Test input() nesting support --- test/functional/ui/cmdline_highlight_spec.lua | 196 ++++++++++++++++++++++++-- 1 file changed, 184 insertions(+), 12 deletions(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 8bfeaf35ab..82d4fdffd2 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -34,9 +34,17 @@ before_each(function() redraw! return '' endfunction - let g:EMPTY = '' + let g:id = '' cnoremap {REDRAW} Redraw() - nnoremap {PROMPT} extend(g:, {"out": input({"prompt": ":", "highlight": g:Nvim_color_input})}).EMPTY + function DoPrompt(do_return) abort + let id = g:id + let Cb = g:Nvim_color_input{g:id} + let out = input({'prompt': ':', 'highlight': Cb}) + let g:out{id} = out + return (a:do_return ? out : '') + endfunction + nnoremap {PROMPT} DoPrompt(0) + cnoremap {PROMPT} DoPrompt(1) function RainBowParens(cmdline) let ret = [] let i = 0 @@ -119,6 +127,9 @@ before_each(function() function ReturningGlobal2(cmdline) return g:callback_return[:len(a:cmdline)-1] endfunction + function ReturningGlobalN(n, cmdline) + return g:callback_return{a:n} + endfunction ]]) screen:set_default_attr_ids({ RBP1={background = Screen.colors.Red}, @@ -132,10 +143,19 @@ before_each(function() }) end) -local function set_color_cb(funcname, callback_return) - meths.set_var('Nvim_color_input', funcname) - if callback_return then - meths.set_var('callback_return', callback_return) +local function set_color_cb(funcname, callback_return, id) + meths.set_var('id', id or '') + if id and id ~= '' and funcs.exists('*' .. funcname .. 'N') then + command(('let g:Nvim_color_input%s = {cmdline -> %sN(%s, cmdline)}'):format( + id, funcname, id)) + if callback_return then + meths.set_var('callback_return' .. id, callback_return) + end + else + meths.set_var('Nvim_color_input', funcname) + if callback_return then + meths.set_var('callback_return', callback_return) + end end end local function start_prompt(text) @@ -504,10 +524,152 @@ describe('Command-line coloring', function() :++^ | ]]) end) - -- TODO Check for colored input() called in a cycle which previously errorred - -- out + it('does not error out when called from a errorred out cycle', function() + set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}}) + feed(dedent([[ + :set regexpengine=2 + :for pat in [' \ze*', ' \zs*'] + : try + : let l = matchlist('x x', pat) + : $put =input({'prompt':'>','highlight':'ReturningGlobal'}) + : + : $put ='E888 NOT detected for ' . pat + : catch + : $put =input({'prompt':'>','highlight':'ReturningGlobal'}) + : + : $put ='E888 detected for ' . pat + : endtry + :endfor + : + : + : + : + : + : + ]])) + eq({'', ':', 'E888 detected for \\ze*', ':', 'E888 detected for \\zs*'}, + curbufmeths.get_lines(0, -1, false)) + eq('', funcs.execute('messages')) + end) + it('allows nesting input()s', function() + set_color_cb('ReturningGlobal', {{0, 1, 'RBP1'}}, '') + start_prompt('1') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :{RBP1:1}^ | + ]]) + + set_color_cb('ReturningGlobal', {{0, 1, 'RBP2'}}, '1') + start_prompt('2') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :{RBP2:2}^ | + ]]) + + set_color_cb('ReturningGlobal', {{0, 1, 'RBP3'}}, '2') + start_prompt('3') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :{RBP3:3}^ | + ]]) + + set_color_cb('ReturningGlobal', {{0, 1, 'RBP4'}}, '3') + start_prompt('4') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :{RBP4:4}^ | + ]]) + + feed('') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :{RBP3:3}4^ | + ]]) + feed('') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :{RBP2:2}34^ | + ]]) + feed('') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :{RBP1:1}234^ | + ]]) + feed('') + screen:expect([[ + ^ | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + | + ]]) + eq('1234', meths.get_var('out')) + eq('234', meths.get_var('out1')) + eq('34', meths.get_var('out2')) + eq('4', meths.get_var('out3')) + eq(0, funcs.exists('g:out4')) + end) end) describe('Ex commands coloring support', function() + it('works', function() + meths.set_var('Nvim_color_cmdline', 'RainBowParens') + feed(':echo (((1)))') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :echo {RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^ | + ]]) + end) it('still executes command-line even if errored out', function() meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart') feed(':let x = "«"\n') @@ -623,6 +785,20 @@ describe('Ex commands coloring support', function() end) end) describe('Expressions coloring support', function() + it('works', function() + meths.set_var('Nvim_color_expr', 'RainBowParens') + feed(':echo =(((1)))') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + ={RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^ | + ]]) + end) it('errors out when failing to get callback', function() meths.set_var('Nvim_color_expr', 42) feed(':=1') @@ -638,7 +814,3 @@ describe('Expressions coloring support', function() ]]) end) end) - --- TODO Specifically test for coloring in cmdline and expr modes --- TODO Check for errors from tv_dict_get_callback() --- TODO Check using highlighted input() from inside highlighted input() -- cgit From 25c6ac1af63c0d68b7993910e94d3b0f1b8bbfd7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 18 Jul 2017 01:21:23 +0300 Subject: *: Fix clint errors --- src/nvim/api/private/helpers.c | 2 +- src/nvim/ex_eval.c | 2 +- src/nvim/ex_getln.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index b6ecc319c1..cdaceddc11 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -110,7 +110,7 @@ bool try_end(Error *err) { // Note: all globals manipulated here should be saved/restored in // try_enter/try_leave. - --trylevel; + trylevel--; // Without this it stops processing all subsequent VimL commands and // generates strange error messages if I e.g. try calling Test() in a diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 139305998d..4434dbe1a5 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -564,7 +564,7 @@ static void discard_exception(except_T *excp, int was_finished) */ void discard_current_exception(void) { - discard_exception(current_exception, FALSE); + discard_exception(current_exception, false); // Note: all globals manipulated here should be saved/restored in // try_enter/try_leave. current_exception = NULL; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 4be0dd0e0d..6ac0d062ac 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2321,7 +2321,7 @@ enum { MAX_CB_ERRORS = 1 }; /// Color command-line /// -/// Should use built-in command parser or user-specified one. Currently only the +/// Should use built-in command parser or user-specified one. Currently only the /// latter is supported. /// /// Operates on ccline, saving results to ccline_colors. -- cgit From 740dcaef0d6a7dda08e025fba678b763994128af Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 18 Jul 2017 01:25:55 +0300 Subject: ex_getln: Avoid GCC “unused variable” warning from QB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 17:25:45,363 WARN - /home/quickbuild/buildagent/workspace/root/neovim/pull-requests-automated/src/nvim/ex_getln.c: In function ‘color_cmdline’: 17:25:45,363 WARN - /home/quickbuild/buildagent/workspace/root/neovim/pull-requests-automated/src/nvim/ex_getln.c:2335:8: error: variable ‘printed_errmsg’ set but not used [-Werror=unused-but-set-variable] 17:25:45,363 WARN - bool printed_errmsg = false; 17:25:45,363 WARN - ^ 17:25:45,399 WARN - cc1: all warnings being treated as errors --- src/nvim/ex_getln.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6ac0d062ac..6115ebf2cb 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2523,6 +2523,7 @@ color_cmdline_error: api_clear_error(&err); } assert(printed_errmsg); + (void)printed_errmsg; prev_prompt_errors++; kv_size(ccline_colors) = 0; -- cgit From 0a46ae3c0a7b2ca53ce16c3f5c5ecb8ae7bfee80 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 18 Jul 2017 01:29:41 +0300 Subject: functests: Add sleep to test --- test/functional/ui/cmdline_highlight_spec.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 82d4fdffd2..a81d671119 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -392,6 +392,7 @@ describe('Command-line coloring', function() {EOB:~ }| | ]]) + screen:sleep(500) feed('') screen:expect([[ {EOB:~ }| -- cgit From d23c0de0c17a665cf4aff3bf4772a08453c6f1dd Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Jul 2017 12:31:01 +0300 Subject: doc: Update documentation --- runtime/doc/eval.txt | 21 ++++++++++++--------- runtime/doc/vim_diff.txt | 12 +++++++++++- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index b45eceb97a..3a928c97ec 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4723,23 +4723,26 @@ input({opts}) "-complete=" argument. Refer to |:command-completion| for more information. Example: > let fname = input("File: ", "", "file") -< +< *E5400* *E5402* The optional highlight key allows specifying function which will be used for highlighting user input. This function receives user input as its only argument and must return - a list of 3-tuples [hl_start_byte, hl_end_byte + 1, hl_group] + a list of 3-tuples [hl_start_col, hl_end_col + 1, hl_group] where - hl_start_byte is the first highlighted byte, - hl_end_byte is the last highlighted byte (+ 1!), + hl_start_col is the first highlighted column, + hl_end_col is the last highlighted column (+ 1!), hl_group is |:hl| group used for highlighting. *E5403* *E5404* *E5405* *E5406* - Both hl_start_byte and hl_end_byte + 1 must point to the start + Both hl_start_col and hl_end_col + 1 must point to the start of the multibyte character (highlighting must not break - multibyte characters), hl_end_byte + 1 may be equal to the + multibyte characters), hl_end_col + 1 may be equal to the input length. Start column must be in range [0, len(input)), - end column must be in range (hl_start_byte, len(input)], - sections must be ordered so that next hl_start_byte is greater - then or equal to previous hl_end_byte. + end column must be in range (hl_start_col, len(input)], + sections must be ordered so that next hl_start_col is greater + then or equal to previous hl_end_col. + + Currently coloring is disabled when command-line contains + arabic characters. NOTE: This function must not be used in a startup file, for the versions that only run in GUI mode (e.g., the Win32 GUI). diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 5801da1132..42f273588a 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -126,7 +126,6 @@ Commands: Functions: |dictwatcheradd()| notifies a callback whenever a |Dict| is modified |dictwatcherdel()| - |execute()| works with |:redir| |msgpackdump()|, |msgpackparse()| provide msgpack de/serialization Events: @@ -143,6 +142,15 @@ Highlight groups: |hl-TermCursorNC| |hl-Whitespace| highlights 'listchars' whitespace +UI: + *E5408* *E5409* *g:Nvim_color_expr* *g:Nvim_color_cmdline* + UI now supports command-line coloring. Officially only |input()| and + |inputdialog()| may be colored, temporary for testing purposes expressions + (e.g. |i_CTRL-R_=|) and regular command-line (|:|) are colored by callbacks + defined in `g:Nvim_color_expr` and `g:Nvim_color_cmdline` respectively. + Callbacks are to be replaced by parser implemented in C which will also do + the coloring. + ============================================================================== 4. Changed features *nvim-features-changed* @@ -261,6 +269,8 @@ Lua interface (|if_lua.txt|): on cancel and completion respectively) via dictionary argument (replaces all other arguments if used). +|input()| and |inputdialog()| now support user-defined cmdline highlighting. + ============================================================================== 5. Missing legacy features *nvim-features-missing* -- cgit From 1ba21b4a31ea5853673ba3d1baae3e862c1ef04a Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Jul 2017 13:02:45 +0300 Subject: functests: Remove unneeded wait()s --- test/functional/eval/input_spec.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 5b259d5440..0571043efe 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -232,7 +232,6 @@ describe('input()', function() feed([[:call input({"highlight": "RainBowParens"})]]) wait() feed('(())') - wait() screen:expect([[ {EOB:~ }| {EOB:~ }| @@ -412,7 +411,6 @@ describe('inputdialog()', function() feed([[:call inputdialog({"highlight": "RainBowParens"})]]) wait() feed('(())') - wait() screen:expect([[ {EOB:~ }| {EOB:~ }| -- cgit From 2952a00d2e2bf0cc6988c9b865497b1f567190c1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Jul 2017 22:01:48 +0300 Subject: message: Only require second (format) argument to be not NULL --- src/nvim/message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 92dd5a4695..fd7043cea8 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1636,7 +1636,7 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) /// @param[in] attr Highlight attributes. /// @param[in] fmt Format string. void msg_printf_attr(const int attr, const char *const fmt, ...) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { static char msgbuf[IOSIZE]; -- cgit From e129607988b88719935bc4af517e7ee2689f5871 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Jul 2017 22:04:39 +0300 Subject: functests: Replace wait() with nvim_async --- test/functional/eval/input_spec.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 0571043efe..cfb91983c1 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -2,13 +2,13 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq -local wait = helpers.wait local feed = helpers.feed local meths = helpers.meths local clear = helpers.clear local source = helpers.source local command = helpers.command local exc_exec = helpers.exc_exec +local nvim_async = helpers.nvim_async local screen @@ -229,16 +229,16 @@ describe('input()', function() exc_exec('call input("prompt> ", "default", "file", "extra")')) end) it('supports highlighting', function() - feed([[:call input({"highlight": "RainBowParens"})]]) - wait() + nvim_async('command', 'call input({"highlight": "RainBowParens"})') feed('(())') screen:expect([[ + | {EOB:~ }| {EOB:~ }| {EOB:~ }| - | {RBP1:(}{RBP2:()}{RBP1:)}^ | ]]) + feed('') end) end) describe('inputdialog()', function() @@ -408,15 +408,15 @@ describe('inputdialog()', function() exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')) end) it('supports highlighting', function() - feed([[:call inputdialog({"highlight": "RainBowParens"})]]) - wait() + nvim_async('command', 'call inputdialog({"highlight": "RainBowParens"})') feed('(())') screen:expect([[ + | {EOB:~ }| {EOB:~ }| {EOB:~ }| - | {RBP1:(}{RBP2:()}{RBP1:)}^ | ]]) + feed('') end) end) -- cgit From c5857e3f3807d305598d7639949793d44b380e23 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 26 Jul 2017 22:56:48 +0300 Subject: ex_getln: Cache highlight callback calling results --- src/nvim/ex_getln.c | 121 ++++++++++++++++++-------- test/functional/ui/cmdline_highlight_spec.lua | 31 +++++++ 2 files changed, 114 insertions(+), 38 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6115ebf2cb..6eb975fea3 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -142,14 +142,37 @@ typedef struct command_line_state { struct cmdline_info save_ccline; } CommandLineState; -/// Command-line colors +/// Command-line colors: one chunk +/// +/// Defines a region which has the same highlighting. typedef struct { int start; ///< Colored chunk start. int end; ///< Colored chunk end (exclusive, > start). int attr; ///< Highlight attr. -} ColoredCmdlineChunk; +} CmdlineColorChunk; + +/// Command-line colors +/// +/// Holds data about all colors. +typedef kvec_t(CmdlineColorChunk) CmdlineColors; + +/// Command-line coloring +/// +/// Holds both what are the colors and what have been colored. Latter is used to +/// suppress unnecessary calls to coloring callbacks. +typedef struct { + unsigned prompt_id; ///< ID of the prompt which was colored last. + char *cmdbuff; ///< What exactly was colored last time or NULL. + CmdlineColors colors; ///< Last colors. +} ColoredCmdline; + +/// Last command-line colors. +ColoredCmdline last_ccline_colors = { + .cmdbuff = NULL, + .colors = KV_INITIAL_VALUE +}; -kvec_t(ColoredCmdlineChunk) ccline_colors; +typedef struct cmdline_info CmdlineInfo; /* The current cmdline_info. It is initialized in getcmdline() and after that * used by other functions. When invoking getcmdline() recursively it needs @@ -2324,13 +2347,20 @@ enum { MAX_CB_ERRORS = 1 }; /// Should use built-in command parser or user-specified one. Currently only the /// latter is supported. /// -/// Operates on ccline, saving results to ccline_colors. +/// @param[in] colored_ccline Command-line to color. +/// @param[out] ret_ccline_colors What should be colored. Also holds a cache: +/// if ->prompt_id and ->cmdbuff values happen +/// to be equal to those from colored_cmdline it +/// will just do nothing, assuming that ->colors +/// already contains needed data. /// /// Always colors the whole cmdline. /// /// @return true if draw_cmdline may proceed, false if it does not need anything /// to do. -static bool color_cmdline(void) +static bool color_cmdline(const CmdlineInfo *const colored_ccline, + ColoredCmdline *const ret_ccline_colors) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { bool printed_errmsg = false; #define PRINT_ERRMSG(...) \ @@ -2340,17 +2370,27 @@ static bool color_cmdline(void) printed_errmsg = true; \ } while (0) bool ret = true; - kv_size(ccline_colors) = 0; - if (ccline.cmdbuff == NULL || *ccline.cmdbuff == NUL) { + // Check whether result of the previous call is still valid. + if (ret_ccline_colors->prompt_id == colored_ccline->prompt_id + && ret_ccline_colors->cmdbuff != NULL + && STRCMP(ret_ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) { + return ret; + } + + kv_size(ret_ccline_colors->colors) = 0; + + if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) { // Nothing to do, exiting. + xfree(ret_ccline_colors->cmdbuff); + ret_ccline_colors->cmdbuff = NULL; return ret; } bool arg_allocated = false; typval_T arg = { .v_type = VAR_STRING, - .vval.v_string = ccline.cmdbuff, + .vval.v_string = colored_ccline->cmdbuff, }; typval_T tv = { .v_type = VAR_UNKNOWN }; @@ -2364,17 +2404,17 @@ static bool color_cmdline(void) bool dgc_ret = true; bool tl_ret = true; - if (ccline.prompt_id != prev_prompt_id) { + if (colored_ccline->prompt_id != prev_prompt_id) { prev_prompt_errors = 0; - prev_prompt_id = ccline.prompt_id; + prev_prompt_id = colored_ccline->prompt_id; } else if (prev_prompt_errors >= MAX_CB_ERRORS) { goto color_cmdline_end; } - if (ccline.highlight_callback.type != kCallbackNone) { + if (colored_ccline->highlight_callback.type != kCallbackNone) { // Currently this should only happen while processing input() prompts. - assert(ccline.input_fn); - color_cb = ccline.highlight_callback; - } else if (ccline.cmdfirstc == ':') { + assert(colored_ccline->input_fn); + color_cb = colored_ccline->highlight_callback; + } else if (colored_ccline->cmdfirstc == ':') { try_enter(&tstate); err_errmsg = N_( "E5408: Unable to get Nvim_color_cmdline callback from g:: %s"); @@ -2382,7 +2422,7 @@ static bool color_cmdline(void) &color_cb); tl_ret = try_leave(&tstate, &err); can_free_cb = true; - } else if (ccline.cmdfirstc == '=') { + } else if (colored_ccline->cmdfirstc == '=') { try_enter(&tstate); err_errmsg = N_( "E5409: Unable to get Nvim_color_expr callback from g:: %s"); @@ -2398,10 +2438,10 @@ static bool color_cmdline(void) if (color_cb.type == kCallbackNone) { goto color_cmdline_end; } - if (ccline.cmdbuff[ccline.cmdlen] != NUL) { + if (colored_ccline->cmdbuff[colored_ccline->cmdlen] != NUL) { arg_allocated = true; - arg.vval.v_string = xmemdupz((const char *)ccline.cmdbuff, - (size_t)ccline.cmdlen); + arg.vval.v_string = xmemdupz((const char *)colored_ccline->cmdbuff, + (size_t)colored_ccline->cmdlen); } // msg_start() called by e.g. :echo may shift command-line to the first column // even though msg_silent is here. Two ways to workaround this problem without @@ -2454,18 +2494,18 @@ static bool color_cmdline(void) const varnumber_T start = tv_get_number_chk(&l->lv_first->li_tv, &error); if (error) { goto color_cmdline_error; - } else if (!(prev_end <= start && start < ccline.cmdlen)) { + } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) { PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range " "[%" PRIdVARNUMBER ", %i)"), - i, start, prev_end, ccline.cmdlen); + i, start, prev_end, colored_ccline->cmdlen); goto color_cmdline_error; - } else if (utf8len_tab_zero[(uint8_t)ccline.cmdbuff[start]] == 0) { + } else if (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[start]] == 0) { PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits " "multibyte character"), i, start); goto color_cmdline_error; } if (start != prev_end) { - kv_push(ccline_colors, ((ColoredCmdlineChunk) { + kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { .start = prev_end, .end = start, .attr = 0, @@ -2475,13 +2515,14 @@ static bool color_cmdline(void) &error); if (error) { goto color_cmdline_error; - } else if (!(start < end && end <= ccline.cmdlen)) { + } else if (!(start < end && end <= colored_ccline->cmdlen)) { PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range " "(%" PRIdVARNUMBER ", %i]"), - i, end, start, ccline.cmdlen); + i, end, start, colored_ccline->cmdlen); goto color_cmdline_error; - } else if (end < ccline.cmdlen - && utf8len_tab_zero[(uint8_t)ccline.cmdbuff[end]] == 0) { + } else if (end < colored_ccline->cmdlen + && (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[end]] + == 0)) { PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte " "character"), i, end); goto color_cmdline_error; @@ -2493,16 +2534,16 @@ static bool color_cmdline(void) } const int id = syn_name2id((char_u *)group); const int attr = (id == 0 ? 0 : syn_id2attr(id)); - kv_push(ccline_colors, ((ColoredCmdlineChunk) { + kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { .start = start, .end = end, .attr = attr, })); } - if (prev_end < ccline.cmdlen) { - kv_push(ccline_colors, ((ColoredCmdlineChunk) { + if (prev_end < colored_ccline->cmdlen) { + kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { .start = prev_end, - .end = ccline.cmdlen, + .end = colored_ccline->cmdlen, .attr = 0, })); } @@ -2512,8 +2553,14 @@ color_cmdline_end: if (can_free_cb) { callback_free(&color_cb); } + xfree(ret_ccline_colors->cmdbuff); + // Note: errors “output” is cached just as well as regular results. + ret_ccline_colors->prompt_id = colored_ccline->prompt_id; if (arg_allocated) { - tv_clear(&arg); + ret_ccline_colors->cmdbuff = (char *)arg.vval.v_string; + } else { + ret_ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff, + (size_t)colored_ccline->cmdlen); } tv_clear(&tv); return ret; @@ -2526,10 +2573,8 @@ color_cmdline_error: (void)printed_errmsg; prev_prompt_errors++; - kv_size(ccline_colors) = 0; - prev_prompt_errors += MAX_CB_ERRORS; + kv_size(ret_ccline_colors->colors) = 0; redrawcmdline(); - prev_prompt_errors -= MAX_CB_ERRORS; ret = false; goto color_cmdline_end; #undef PRINT_ERRMSG @@ -2541,7 +2586,7 @@ color_cmdline_error: */ static void draw_cmdline(int start, int len) { - if (!color_cmdline()) { + if (!color_cmdline(&ccline, &last_ccline_colors)) { return; } @@ -2643,9 +2688,9 @@ static void draw_cmdline(int start, int len) msg_outtrans_len(arshape_buf, newlen); } else { draw_cmdline_no_arabicshape: - if (kv_size(ccline_colors)) { - for (size_t i = 0; i < kv_size(ccline_colors); i++) { - ColoredCmdlineChunk chunk = kv_A(ccline_colors, i); + if (kv_size(last_ccline_colors.colors)) { + for (size_t i = 0; i < kv_size(last_ccline_colors.colors); i++) { + CmdlineColorChunk chunk = kv_A(last_ccline_colors.colors, i); if (chunk.end <= start) { continue; } diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index a81d671119..b6288b746e 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -130,6 +130,11 @@ before_each(function() function ReturningGlobalN(n, cmdline) return g:callback_return{a:n} endfunction + let g:recording_calls = [] + function Recording(cmdline) + call add(g:recording_calls, a:cmdline) + return [] + endfunction ]]) screen:set_default_attr_ids({ RBP1={background = Screen.colors.Red}, @@ -655,6 +660,32 @@ describe('Command-line coloring', function() eq('4', meths.get_var('out3')) eq(0, funcs.exists('g:out4')) end) + it('runs callback with the same data only once', function() + local function new_recording_calls(...) + eq({...}, meths.get_var('recording_calls')) + meths.set_var('recording_calls', {}) + end + set_color_cb('Recording') + start_prompt('') + -- Regression test. Disambiguation: + -- + -- new_recording_calls(expected_result) -- (actual_before_fix) + -- + feed('a') + new_recording_calls('a') -- ('a', 'a') + feed('b') + new_recording_calls('ab') -- ('a', 'ab', 'ab') + feed('c') + new_recording_calls('abc') -- ('ab', 'abc', 'abc') + feed('') + new_recording_calls('ab') -- ('abc', 'ab', 'ab') + feed('') + new_recording_calls('a') -- ('ab', 'a', 'a') + feed('') + new_recording_calls() -- ('a') + feed('') + eq('', meths.get_var('out')) + end) end) describe('Ex commands coloring support', function() it('works', function() -- cgit From 1011462b40502e6039494e70a870f0360f152b1b Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 27 Jul 2017 18:49:13 +0300 Subject: Revert "functests: Replace wait() with nvim_async" This reverts commit e129607988b88719935bc4af517e7ee2689f5871. Tests stopped working in CI. --- test/functional/eval/input_spec.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index cfb91983c1..0571043efe 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -2,13 +2,13 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq +local wait = helpers.wait local feed = helpers.feed local meths = helpers.meths local clear = helpers.clear local source = helpers.source local command = helpers.command local exc_exec = helpers.exc_exec -local nvim_async = helpers.nvim_async local screen @@ -229,16 +229,16 @@ describe('input()', function() exc_exec('call input("prompt> ", "default", "file", "extra")')) end) it('supports highlighting', function() - nvim_async('command', 'call input({"highlight": "RainBowParens"})') + feed([[:call input({"highlight": "RainBowParens"})]]) + wait() feed('(())') screen:expect([[ - | {EOB:~ }| {EOB:~ }| {EOB:~ }| + | {RBP1:(}{RBP2:()}{RBP1:)}^ | ]]) - feed('') end) end) describe('inputdialog()', function() @@ -408,15 +408,15 @@ describe('inputdialog()', function() exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')) end) it('supports highlighting', function() - nvim_async('command', 'call inputdialog({"highlight": "RainBowParens"})') + feed([[:call inputdialog({"highlight": "RainBowParens"})]]) + wait() feed('(())') screen:expect([[ - | {EOB:~ }| {EOB:~ }| {EOB:~ }| + | {RBP1:(}{RBP2:()}{RBP1:)}^ | ]]) - feed('') end) end) -- cgit From 4d8ff5ec012cd517cf8010144f158d6f5c96d5bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Aug 2017 14:38:54 +0300 Subject: api/helpers: Clarify try_start() usage --- src/nvim/api/private/helpers.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index cdaceddc11..021d1d43ff 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -96,6 +96,13 @@ bool try_leave(const TryState *const tstate, Error *const err) } /// Start block that may cause vimscript exceptions +/// +/// Each try_start() call should be mirrorred by try_end() call. +/// +/// To be used as a replacement of `:try … catch … endtry` in C code, in cases +/// when error flag could not already be set. If there may be pending error +/// state at the time try_start() is executed which needs to be preserved, +/// try_enter()/try_leave() pair should be used instead. void try_start(void) { ++trylevel; -- cgit From efb03903eb667bc14992dffcd77d04385371abed Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Aug 2017 14:43:46 +0300 Subject: functests: Remove wait() from input_spec --- test/functional/eval/input_spec.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua index 0571043efe..5ae23e17d0 100644 --- a/test/functional/eval/input_spec.lua +++ b/test/functional/eval/input_spec.lua @@ -2,7 +2,6 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq -local wait = helpers.wait local feed = helpers.feed local meths = helpers.meths local clear = helpers.clear @@ -229,14 +228,14 @@ describe('input()', function() exc_exec('call input("prompt> ", "default", "file", "extra")')) end) it('supports highlighting', function() - feed([[:call input({"highlight": "RainBowParens"})]]) - wait() + command('nnoremap X input({"highlight": "RainBowParens"})[-1]') + feed([[X]]) feed('(())') screen:expect([[ + | {EOB:~ }| {EOB:~ }| {EOB:~ }| - | {RBP1:(}{RBP2:()}{RBP1:)}^ | ]]) end) @@ -408,14 +407,14 @@ describe('inputdialog()', function() exc_exec('call inputdialog("prompt> ", "default", "file", "extra")')) end) it('supports highlighting', function() - feed([[:call inputdialog({"highlight": "RainBowParens"})]]) - wait() + command('nnoremap X inputdialog({"highlight": "RainBowParens"})[-1]') + feed([[X]]) feed('(())') screen:expect([[ + | {EOB:~ }| {EOB:~ }| {EOB:~ }| - | {RBP1:(}{RBP2:()}{RBP1:)}^ | ]]) end) -- cgit From 36acfce4eac9dd131a39c4dbdff2836ab3e21d73 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 6 Aug 2017 15:23:05 +0300 Subject: api/helpers: Fix typo --- src/nvim/api/private/helpers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 021d1d43ff..e736e29e2d 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -97,7 +97,7 @@ bool try_leave(const TryState *const tstate, Error *const err) /// Start block that may cause vimscript exceptions /// -/// Each try_start() call should be mirrorred by try_end() call. +/// Each try_start() call should be mirrored by try_end() call. /// /// To be used as a replacement of `:try … catch … endtry` in C code, in cases /// when error flag could not already be set. If there may be pending error -- cgit From a5449f79ac21919d45544c1e9ce86ae003e04298 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 14 Aug 2017 01:17:16 +0300 Subject: functests: Check that input is correctly silenced --- test/functional/ui/cmdline_highlight_spec.lua | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index b6288b746e..262d706e4e 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -342,6 +342,51 @@ describe('Command-line coloring', function() :e^ | ]]) end) + it('silences :echo', function() + set_color_cb('Echoing') + start_prompt('e') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :e^ | + ]]) + eq('', meths.command_output('messages')) + end) + it('silences :echon', function() + set_color_cb('Echoning') + start_prompt('e') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :e^ | + ]]) + eq('', meths.command_output('messages')) + end) + it('silences :echomsg', function() + set_color_cb('Echomsging') + start_prompt('e') + screen:expect([[ + | + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + {EOB:~ }| + :e^ | + ]]) + eq('', meths.command_output('messages')) + end) it('does the right thing when throwing', function() set_color_cb('Throwing') start_prompt('e') -- cgit From f1ef94b87150bd8f1f70c670409bd668136b8258 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 14 Aug 2017 01:20:52 +0300 Subject: doc: Clarify how function is executed --- runtime/doc/eval.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 905d0b590b..279c5e4442 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4744,6 +4744,14 @@ input({opts}) sections must be ordered so that next hl_start_col is greater then or equal to previous hl_end_col. + Highlight function is called at least once for each new input + string, before command-line is redrawn. It is expected that + function is pure for the duration of one input() call, i.e. it + produces the same output for the same input, so output may be + memoized. Function is run like under |:silent| modifier, + additionally any errors from function cause it to be no longer + executed for the duration of the current input() call. + Currently coloring is disabled when command-line contains arabic characters. -- cgit From 0571b8cb0eba8f8ec10b33910bb5164a123af44d Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 14 Aug 2017 01:22:10 +0300 Subject: functests: Alter comment --- test/functional/ui/cmdline_highlight_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 262d706e4e..1d2a06b7f0 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -13,7 +13,7 @@ local curbufmeths = helpers.curbufmeths local screen --- Bug in input() handling: {REDRAW} will erase the whole prompt up until +-- Bug in input() handling: :redraw! will erase the whole prompt up until -- user types something. It exists in Vim as well, so using `h` as -- a workaround. local function redraw_input() -- cgit From 5c60cd2abb74a14a99bea87e0b76e4f35a50bb07 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 14 Aug 2017 01:40:21 +0300 Subject: doc: State that it is called for new *displayed* input --- runtime/doc/eval.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 279c5e4442..4e8b6527eb 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4744,13 +4744,14 @@ input({opts}) sections must be ordered so that next hl_start_col is greater then or equal to previous hl_end_col. - Highlight function is called at least once for each new input - string, before command-line is redrawn. It is expected that - function is pure for the duration of one input() call, i.e. it - produces the same output for the same input, so output may be - memoized. Function is run like under |:silent| modifier, - additionally any errors from function cause it to be no longer - executed for the duration of the current input() call. + Highlight function is called at least once for each new + displayed input string, before command-line is redrawn. It is + expected that function is pure for the duration of one input() + call, i.e. it produces the same output for the same input, so + output may be memoized. Function is run like under |:silent| + modifier, additionally any errors from function cause it to be + no longer executed for the duration of the current input() + call. Currently coloring is disabled when command-line contains arabic characters. -- cgit From 19a28352a925b0a8502d57ec4f42b1412639aa6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 14 Aug 2017 01:56:48 +0300 Subject: ex_getln: Make error messages look better --- src/nvim/ex_getln.c | 4 ++-- test/functional/ui/cmdline_highlight_spec.lua | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 6d839f0aa4..5e216925df 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2420,7 +2420,7 @@ static bool color_cmdline(const CmdlineInfo *const colored_ccline, } else if (colored_ccline->cmdfirstc == ':') { try_enter(&tstate); err_errmsg = N_( - "E5408: Unable to get Nvim_color_cmdline callback from g:: %s"); + "E5408: Unable to get g:Nvim_color_cmdline callback: %s"); dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), &color_cb); tl_ret = try_leave(&tstate, &err); @@ -2428,7 +2428,7 @@ static bool color_cmdline(const CmdlineInfo *const colored_ccline, } else if (colored_ccline->cmdfirstc == '=') { try_enter(&tstate); err_errmsg = N_( - "E5409: Unable to get Nvim_color_expr callback from g:: %s"); + "E5409: Unable to get g:Nvim_color_expr callback: %s"); dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_expr"), &color_cb); tl_ret = try_leave(&tstate, &err); diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua index 1d2a06b7f0..d87ce72599 100644 --- a/test/functional/ui/cmdline_highlight_spec.lua +++ b/test/functional/ui/cmdline_highlight_spec.lua @@ -854,9 +854,9 @@ describe('Ex commands coloring support', function() {EOB:~ }| {EOB:~ }| : | - {ERR:E5408: Unable to get Nvim_color_cmdline }| - {ERR:callback from g:: Vim:E6000: Argument is}| - {ERR: not a function or function name} | + {ERR:E5408: Unable to get g:Nvim_color_cmdlin}| + {ERR:e callback: Vim:E6000: Argument is not a}| + {ERR: function or function name} | :#^ | ]]) end) @@ -884,9 +884,9 @@ describe('Expressions coloring support', function() {EOB:~ }| {EOB:~ }| = | - {ERR:E5409: Unable to get Nvim_color_expr cal}| - {ERR:lback from g:: Vim:E6000: Argument is no}| - {ERR:t a function or function name} | + {ERR:E5409: Unable to get g:Nvim_color_expr c}| + {ERR:allback: Vim:E6000: Argument is not a fu}| + {ERR:nction or function name} | =1^ | ]]) end) -- cgit