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(-) (limited to 'src') 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(-) (limited to 'src') 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(-) (limited to 'src') 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 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 ++ 3 files changed, 75 insertions(+), 22 deletions(-) (limited to 'src') 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 -- 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 --- src/nvim/eval.c | 12 ++++++-- src/nvim/ex_getln.c | 88 +++++++++++++++++++++++++++++++---------------------- src/nvim/ex_getln.h | 2 ++ 3 files changed, 64 insertions(+), 38 deletions(-) (limited to 'src') 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 -- 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(+) (limited to 'src') 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 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 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'src') 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; -- 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 +++++++++++++++++++++ 2 files changed, 51 insertions(+), 22 deletions(-) (limited to 'src') 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. -- 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(-) (limited to 'src') 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(-) (limited to 'src') 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 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src') 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; } -- 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 ++++++++++----- 3 files changed, 69 insertions(+), 5 deletions(-) (limited to 'src') 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) { -- 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(-) (limited to 'src') 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(-) (limited to 'src') 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 -- 1 file changed, 2 deletions(-) (limited to 'src') 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; -- 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(-) (limited to 'src') 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 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(-) (limited to 'src') 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 ++++--- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'src') 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 -- 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 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') 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, -- 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(-) (limited to 'src') 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(+) (limited to 'src') 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 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(-) (limited to 'src') 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 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 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 38 deletions(-) (limited to 'src') 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; } -- 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(+) (limited to 'src') 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 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(-) (limited to 'src') 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 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 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') 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); -- cgit