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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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/nvim') 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 4cd2749b102864a3e194b136cc88f4846b412dfe Mon Sep 17 00:00:00 2001 From: ckelsel Date: Thu, 10 Aug 2017 08:36:31 +0800 Subject: vim-patch:8.0.0174 Problem: For completion "locale -a" is executed on MS-Windows, even though it most likely won't work. Solution: Skip executing "locale -a" on MS-Windows. (Ken Takata) https://github.com/vim/vim/commit/b8f7bd68f6fdff51ca4db5b145dcde42ee7b804b --- src/nvim/ex_cmds2.c | 25 +++++++++++++++---------- src/nvim/version.c | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 1a728647ca..0fd74ccde7 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3610,18 +3610,11 @@ void ex_language(exarg_T *eap) static char_u **locales = NULL; // Array of all available locales -static bool did_init_locales = false; -/// Lazy initialization of all available locales. -static void init_locales(void) -{ - if (!did_init_locales) { - did_init_locales = true; - locales = find_locales(); - } -} +#ifndef WIN32 +static bool did_init_locales = false; -// Return an array of strings for all available locales + NULL for the +/// Return an array of strings for all available locales + NULL for the /// last element. Return NULL in case of error. static char_u **find_locales(void) { @@ -3653,6 +3646,18 @@ static char_u **find_locales(void) ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; return (char_u **)locales_ga.ga_data; } +#endif + +/// Lazy initialization of all available locales. +static void init_locales(void) +{ +#ifndef WIN32 + if (!did_init_locales) { + did_init_locales = true; + locales = find_locales(); + } +#endif +} # if defined(EXITFREE) void free_locales(void) diff --git a/src/nvim/version.c b/src/nvim/version.c index fa179b4f63..bc5035f215 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -778,7 +778,7 @@ static const int included_patches[] = { 177, 176, // 175, - // 174, + 174, // 173 NA 172, // 171, -- 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/nvim') 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 From 34e5654f2166fea0ae78a6260e8b961be4b07f1d Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 11 Mar 2017 15:58:51 +0100 Subject: ui: disable 'lazyredraw' during ui_refresh. Could also try `do_redraw = true` instead of save/restore `p_lz`, but the nice thing about save/restore of `p_lz` is that it is "atomic". The semantics of `do_redraw` are not clear to me. Closes #4884 References #6202 References https://github.com/neovim/neovim/pull/6202#issuecomment-284379503 References #3929 #5692 #6157 References #5866 --- src/nvim/screen.c | 12 +++++------- src/nvim/ui.c | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index f8d519ab36..0c2c4a9bee 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -7440,13 +7440,11 @@ int number_width(win_T *wp) return n; } -/* - * Set size of the Vim shell. - * If 'mustset' is TRUE, we must set Rows and Columns, do not get the real - * window size (this is used for the :win command). - * If 'mustset' is FALSE, we may try to get the real window size and if - * it fails use 'width' and 'height'. - */ +/// Set size of the Vim shell. +/// If 'mustset' is TRUE, we must set Rows and Columns, do not get the real +/// window size (this is used for the :win command). +/// If 'mustset' is FALSE, we may try to get the real window size and if +/// it fails use 'width' and 'height'. void screen_resize(int width, int height) { static int busy = FALSE; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index a60c061949..b85a01814d 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -193,7 +193,12 @@ void ui_refresh(void) } row = col = 0; + + int save_p_lz = p_lz; + p_lz = false; // convince redrawing() to return true ... screen_resize(width, height); + p_lz = save_p_lz; + for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) { ui_set_external(i, ext_widgets[i]); } -- cgit From f5938d9bcfdfb92d26a13659b4afe1e77d4df637 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 14 Aug 2017 01:26:45 +0200 Subject: doc: screen_resize(): remove mention of "mustset" "mustset" is from Vim term.c:set_shellsize(), not relevant here: we behave as if mustset=true always. --- src/nvim/screen.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0c2c4a9bee..95973354bc 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -7440,11 +7440,7 @@ int number_width(win_T *wp) return n; } -/// Set size of the Vim shell. -/// If 'mustset' is TRUE, we must set Rows and Columns, do not get the real -/// window size (this is used for the :win command). -/// If 'mustset' is FALSE, we may try to get the real window size and if -/// it fails use 'width' and 'height'. +/// Set dimensions of the Nvim application "shell". void screen_resize(int width, int height) { static int busy = FALSE; @@ -7529,8 +7525,8 @@ void screen_resize(int width, int height) --busy; } -// Check if the new shell size is valid, correct it if it's too small or way -// too big. +/// Check if the new Nvim application "shell" dimensions are valid. +/// Correct it if it's too small or way too big. void check_shellsize(void) { if (Rows < min_rows()) { -- cgit From 1f4090011ef95e2bdf217405d28a1b48300965bd Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Tue, 15 Aug 2017 18:26:41 +0200 Subject: coverty: solve  #7149 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nvim/mbyte.h | 3 +++ src/nvim/menu.c | 8 +++----- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index ad9e38004c..f718d17923 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -18,6 +18,9 @@ #define MB_BYTE2LEN(b) utf8len_tab[b] #define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b]) +// max length of an unicode char +#define MB_MAXCHAR 6 + /* properties used in enc_canon_table[] (first three mutually exclusive) */ #define ENC_8BIT 0x01 #define ENC_DBCS 0x02 diff --git a/src/nvim/menu.c b/src/nvim/menu.c index a498916e5e..0db250d111 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -666,8 +666,6 @@ static void free_menu_string(vimmenu_T *menu, int idx) static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) { dict_T *dict; - char buf[sizeof(menu->mnemonic)]; - int mnemonic_len; if (!menu || (menu->modes & modes) == 0x0) { return NULL; @@ -679,8 +677,8 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname)); if (menu->mnemonic) { - mnemonic_len = utf_char2bytes(menu->mnemonic, (u_char *)buf); - buf[mnemonic_len] = '\0'; + char buf[MB_MAXCHAR + 1] = { 0 }; // > max value of utf8_char2bytes + utf_char2bytes(menu->mnemonic, (char_u *)buf); tv_dict_add_str(dict, S_LEN("shortcut"), buf); } @@ -717,7 +715,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) list_T *children_list = tv_list_alloc(); for (menu = menu->children; menu != NULL; menu = menu->next) { dict_T *dic = menu_get_recursive(menu, modes); - if (dict && tv_dict_len(dict) > 0) { + if (tv_dict_len(dict) > 0) { tv_list_append_dict(children_list, dic); } } -- cgit From 7f22a27a10655dc40528f35034cbdf8c9543241c Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Wed, 24 Feb 2016 02:14:19 -0600 Subject: win: integrate winpty (WIP) Handling of process exit is still broken. It detects the moment when the child process exits, then quickly stops polling for process output. It should continue polling for output until the agent has scraped all of the process' output. This problem is easy to notice by running a command like "dir && exit", but even typing "exit" can manifest the problem -- the "t" might not appear. winpty's Cygwin adapter handles shutdown by waiting for the agent to close the CONOUT pipe, which it does after it has scraped the child's last output. AFAIK, neovim doesn't do anything interesting when winpty closes the CONOUT pipe. --- src/nvim/CMakeLists.txt | 7 ++ src/nvim/os/pty_process_win.c | 189 ++++++++++++++++++++++++++++++++++++++++++ src/nvim/os/pty_process_win.h | 25 ++++-- 3 files changed, 214 insertions(+), 7 deletions(-) create mode 100644 src/nvim/os/pty_process_win.c (limited to 'src/nvim') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index c46c0bed6d..5f9d08cfa3 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -111,6 +111,9 @@ foreach(sfile ${NVIM_SOURCES}) if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$") list(APPEND to_remove ${sfile}) endif() + if(NOT WIN32 AND ${f} MATCHES "^(pty_process_win.c)$") + list(APPEND to_remove ${sfile}) + endif() endforeach() list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) @@ -350,6 +353,10 @@ if(Iconv_LIBRARIES) list(APPEND NVIM_LINK_LIBRARIES ${Iconv_LIBRARIES}) endif() +if(WIN32) + list(APPEND NVIM_LINK_LIBRARIES ${WINPTY_LIBRARIES}) +endif() + # Put these last on the link line, since multiple things may depend on them. list(APPEND NVIM_LINK_LIBRARIES ${LIBUV_LIBRARIES} diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c new file mode 100644 index 0000000000..e75c92e7fb --- /dev/null +++ b/src/nvim/os/pty_process_win.c @@ -0,0 +1,189 @@ +#include +#include +#include + +#include "nvim/memory.h" +#include "nvim/os/pty_process_win.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_win.c.generated.h" +#endif + +static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) +{ + uv_async_t *finish_async = (uv_async_t *)context; + uv_async_send(finish_async); +} + +bool pty_process_spawn(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + bool success = false; + winpty_error_ptr_t err = NULL; + winpty_config_t *cfg = NULL; + winpty_spawn_config_t *spawncfg = NULL; + winpty_t *wp = NULL; + char *in_name = NULL, *out_name = NULL; + HANDLE process_handle = NULL; + + assert(proc->in && proc->out && !proc->err); + + if (!(cfg = winpty_config_new( + WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { + goto cleanup; + } + winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + + if (!(wp = winpty_open(cfg, &err))) { + goto cleanup; + } + + in_name = utf16_to_utf8(winpty_conin_name(wp)); + out_name = utf16_to_utf8(winpty_conout_name(wp)); + uv_pipe_connect( + xmalloc(sizeof(uv_connect_t)), + &proc->in->uv.pipe, + in_name, + pty_process_connect_cb); + uv_pipe_connect( + xmalloc(sizeof(uv_connect_t)), + &proc->out->uv.pipe, + out_name, + pty_process_connect_cb); + + // XXX: Provide the correct ptyprocess parameters (at least, the cmdline... + // probably cwd too? what about environ?) + if (!(spawncfg = winpty_spawn_config_new( + WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, + L"C:\\Windows\\System32\\cmd.exe", + L"C:\\Windows\\System32\\cmd.exe", + NULL, NULL, + &err))) { + goto cleanup; + } + if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { + goto cleanup; + } + + uv_async_init(&proc->loop->uv, &ptyproc->finish_async, pty_process_finish2); + if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, + pty_process_finish1, &ptyproc->finish_async, INFINITE, 0)) { + abort(); + } + + ptyproc->wp = wp; + ptyproc->process_handle = process_handle; + wp = NULL; + process_handle = NULL; + success = true; + +cleanup: + winpty_error_free(err); + winpty_config_free(cfg); + winpty_spawn_config_free(spawncfg); + winpty_free(wp); + xfree(in_name); + xfree(out_name); + if (process_handle != NULL) { + CloseHandle(process_handle); + } + return success; +} + +void pty_process_resize(PtyProcess *ptyproc, uint16_t width, + uint16_t height) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->wp != NULL) { + winpty_set_size(ptyproc->wp, width, height, NULL); + } +} + +void pty_process_close(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + + ptyproc->is_closing = true; + pty_process_close_master(ptyproc); + + uv_handle_t *finish_async_handle = (uv_handle_t *)&ptyproc->finish_async; + if (ptyproc->finish_wait != NULL) { + // Use INVALID_HANDLE_VALUE to block until either the wait is cancelled + // or the callback has signalled the uv_async_t. + UnregisterWaitEx(ptyproc->finish_wait, INVALID_HANDLE_VALUE); + uv_close(finish_async_handle, pty_process_finish_closing); + } else { + pty_process_finish_closing(finish_async_handle); + } +} + +void pty_process_close_master(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->wp != NULL) { + winpty_free(ptyproc->wp); + ptyproc->wp = NULL; + } +} + +void pty_process_teardown(Loop *loop) + FUNC_ATTR_NONNULL_ALL +{ +} + +// Returns a string freeable with xfree. Never returns NULL (OOM is a fatal +// error). Windows appears to replace invalid UTF-16 code points (i.e. +// unpaired surrogates) using U+FFFD (the replacement character). +static char *utf16_to_utf8(LPCWSTR str) + FUNC_ATTR_NONNULL_ALL +{ + int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + assert(len >= 1); // Even L"" has a non-zero length due to NUL terminator. + char *ret = xmalloc(len); + int len2 = WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL); + assert(len == len2); + return ret; +} + +static void pty_process_connect_cb(uv_connect_t *req, int status) +{ + assert(status == 0); + xfree(req); +} + +static void pty_process_finish2(uv_async_t *finish_async) +{ + PtyProcess *ptyproc = + (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); + Process *proc = (Process *)ptyproc; + + if (!ptyproc->is_closing) { + // If pty_process_close has already been called, be consistent and never + // call the internal_exit callback. + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = exit_code; + + if (proc->internal_exit_cb) { + proc->internal_exit_cb(proc); + } + } +} + +static void pty_process_finish_closing(uv_handle_t *finish_async) +{ + PtyProcess *ptyproc = + (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); + Process *proc = (Process *)ptyproc; + + if (ptyproc->process_handle != NULL) { + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; + } + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); + } +} diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 8e2b37a1c1..87b2b6545d 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -1,21 +1,23 @@ #ifndef NVIM_OS_PTY_PROCESS_WIN_H #define NVIM_OS_PTY_PROCESS_WIN_H +#include + +#include + #include "nvim/event/libuv_process.h" typedef struct pty_process { Process process; char *term_name; uint16_t width, height; + winpty_t *wp; + uv_async_t finish_async; + HANDLE finish_wait; + HANDLE process_handle; + bool is_closing; } PtyProcess; -#define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) -#define pty_process_close(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_resize(job, width, height) ( \ - (void)job, (void)width, (void)height, 0) -#define pty_process_teardown(loop) ((void)loop, 0) - static inline PtyProcess pty_process_init(Loop *loop, void *data) { PtyProcess rv; @@ -23,7 +25,16 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.term_name = NULL; rv.width = 80; rv.height = 24; + rv.wp = NULL; + // XXX: Zero rv.finish_async somehow? + rv.finish_wait = NULL; + rv.process_handle = NULL; + rv.is_closing = false; return rv; } +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_win.h.generated.h" +#endif + #endif // NVIM_OS_PTY_PROCESS_WIN_H -- cgit From a79785675564a4c1f02f9aa54249fc6000187c0d Mon Sep 17 00:00:00 2001 From: Rui Abreu Ferreira Date: Fri, 10 Feb 2017 00:04:27 +0000 Subject: win/install: winpty-agent.exe --- src/nvim/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 5f9d08cfa3..9bb3fbfcbe 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -422,6 +422,7 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tee.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tidy.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/win32yank.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty-agent.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/D3Dcompiler_47.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/libEGL.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ -- cgit From 4b1f21de75f9981007d80aca8355239e8615d6bd Mon Sep 17 00:00:00 2001 From: erw7 Date: Tue, 28 Mar 2017 18:07:58 +0900 Subject: win: support :terminal --- src/nvim/CMakeLists.txt | 1 + src/nvim/os/pty_process_win.c | 232 ++++++++++++++++++++++++++++++------------ src/nvim/os/pty_process_win.h | 13 ++- 3 files changed, 177 insertions(+), 69 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 9bb3fbfcbe..2e0a35d4ab 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -436,6 +436,7 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Network.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Svg.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Widgets.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/platforms/qwindows.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/platforms/ ) diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index e75c92e7fb..ea95c1bb09 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -2,30 +2,53 @@ #include #include +#include "nvim/vim.h" +#include "nvim/ascii.h" #include "nvim/memory.h" +#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 #include "nvim/os/pty_process_win.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/pty_process_win.c.generated.h" #endif +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = + (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + Process *proc = (Process *)ptyproc; + + if (!uv_is_readable(proc->out->uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } +} + static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) + FUNC_ATTR_NONNULL_ALL { - uv_async_t *finish_async = (uv_async_t *)context; - uv_async_send(finish_async); + PtyProcess *ptyproc = (PtyProcess *)context; + Process *proc = (Process *)ptyproc; + + uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); + ptyproc->wait_eof_timer.data = (void *)ptyproc; + uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } -bool pty_process_spawn(PtyProcess *ptyproc) +int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { Process *proc = (Process *)ptyproc; - bool success = false; + int status = 0; winpty_error_ptr_t err = NULL; winpty_config_t *cfg = NULL; winpty_spawn_config_t *spawncfg = NULL; winpty_t *wp = NULL; char *in_name = NULL, *out_name = NULL; HANDLE process_handle = NULL; + uv_connect_t *in_req = NULL, *out_req = NULL; + wchar_t *cmdline = NULL, *cwd = NULL; assert(proc->in && proc->out && !proc->err); @@ -33,52 +56,70 @@ bool pty_process_spawn(PtyProcess *ptyproc) WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { goto cleanup; } - winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_config_set_initial_size( + cfg, + ptyproc->width, + ptyproc->height); if (!(wp = winpty_open(cfg, &err))) { goto cleanup; } - in_name = utf16_to_utf8(winpty_conin_name(wp)); - out_name = utf16_to_utf8(winpty_conout_name(wp)); + if ((status = utf16_to_utf8(winpty_conin_name(wp), &in_name))) { + goto cleanup; + } + if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { + goto cleanup; + } + in_req = xmalloc(sizeof(uv_connect_t)); + out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + in_req, &proc->in->uv.pipe, in_name, pty_process_connect_cb); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + out_req, &proc->out->uv.pipe, out_name, pty_process_connect_cb); - // XXX: Provide the correct ptyprocess parameters (at least, the cmdline... - // probably cwd too? what about environ?) + if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { + goto cleanup; + } + if ((status = build_cmdline(proc->argv, &cmdline))) { + goto cleanup; + } if (!(spawncfg = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, - L"C:\\Windows\\System32\\cmd.exe", - L"C:\\Windows\\System32\\cmd.exe", - NULL, NULL, - &err))) { + NULL, cmdline, cwd, NULL, &err))) { goto cleanup; } if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { goto cleanup; } + proc->pid = GetProcessId(process_handle); - uv_async_init(&proc->loop->uv, &ptyproc->finish_async, pty_process_finish2); - if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, - pty_process_finish1, &ptyproc->finish_async, INFINITE, 0)) { + if (!RegisterWaitForSingleObject( + &ptyproc->finish_wait, + process_handle, pty_process_finish1, ptyproc, + INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { abort(); } + while (in_req->handle || out_req->handle) { + uv_run(&proc->loop->uv, UV_RUN_ONCE); + } + ptyproc->wp = wp; ptyproc->process_handle = process_handle; wp = NULL; process_handle = NULL; - success = true; cleanup: + if (err != NULL) { + status = (int)winpty_error_code(err); + } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); @@ -88,7 +129,11 @@ cleanup: if (process_handle != NULL) { CloseHandle(process_handle); } - return success; + xfree(in_req); + xfree(out_req); + xfree(cmdline); + xfree(cwd); + return status; } void pty_process_resize(PtyProcess *ptyproc, uint16_t width, @@ -105,17 +150,10 @@ void pty_process_close(PtyProcess *ptyproc) { Process *proc = (Process *)ptyproc; - ptyproc->is_closing = true; pty_process_close_master(ptyproc); - uv_handle_t *finish_async_handle = (uv_handle_t *)&ptyproc->finish_async; - if (ptyproc->finish_wait != NULL) { - // Use INVALID_HANDLE_VALUE to block until either the wait is cancelled - // or the callback has signalled the uv_async_t. - UnregisterWaitEx(ptyproc->finish_wait, INVALID_HANDLE_VALUE); - uv_close(finish_async_handle, pty_process_finish_closing); - } else { - pty_process_finish_closing(finish_async_handle); + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); } } @@ -133,57 +171,123 @@ void pty_process_teardown(Loop *loop) { } -// Returns a string freeable with xfree. Never returns NULL (OOM is a fatal -// error). Windows appears to replace invalid UTF-16 code points (i.e. -// unpaired surrogates) using U+FFFD (the replacement character). -static char *utf16_to_utf8(LPCWSTR str) - FUNC_ATTR_NONNULL_ALL -{ - int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); - assert(len >= 1); // Even L"" has a non-zero length due to NUL terminator. - char *ret = xmalloc(len); - int len2 = WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL); - assert(len == len2); - return ret; -} - static void pty_process_connect_cb(uv_connect_t *req, int status) + FUNC_ATTR_NONNULL_ALL { assert(status == 0); - xfree(req); + req->handle = NULL; } -static void pty_process_finish2(uv_async_t *finish_async) +static void pty_process_finish2(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); Process *proc = (Process *)ptyproc; - if (!ptyproc->is_closing) { - // If pty_process_close has already been called, be consistent and never - // call the internal_exit callback. + UnregisterWaitEx(ptyproc->finish_wait, NULL); + uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = (int)exit_code; - DWORD exit_code = 0; - GetExitCodeProcess(ptyproc->process_handle, &exit_code); - proc->status = exit_code; + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; - if (proc->internal_exit_cb) { - proc->internal_exit_cb(proc); + proc->internal_exit_cb(proc); +} + +static int build_cmdline(char **argv, wchar_t **cmdline) + FUNC_ATTR_NONNULL_ALL +{ + char *args = NULL; + size_t args_len = 0, argc = 0; + int ret; + QUEUE q; + QUEUE_INIT(&q); + + while (*argv) { + arg_T *arg = xmalloc(sizeof(arg_T)); + arg->arg = (char *)xmalloc(strlen(*argv) * 2 + 3); + quote_cmd_arg(arg->arg, *argv); + args_len += strlen(arg->arg); + QUEUE_INIT(&arg->node); + QUEUE_INSERT_TAIL(&q, &arg->node); + argc++; + argv++; + } + args_len += argc; + args = xmalloc(args_len); + *args = NUL; + while (1) { + QUEUE *head = QUEUE_HEAD(&q); + QUEUE_REMOVE(head); + arg_T *arg = QUEUE_DATA(head, arg_T, node); + xstrlcat(args, arg->arg, args_len); + xfree(arg->arg); + xfree(arg); + if (QUEUE_EMPTY(&q)) { + break; + } else { + xstrlcat(args, " ", args_len); } } + ret = utf8_to_utf16(args, cmdline); + xfree(args); + return ret; } -static void pty_process_finish_closing(uv_handle_t *finish_async) +// Emulate quote_cmd_arg of libuv and quotes command line arguments +static void quote_cmd_arg(char *target, const char *source) + FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); - Process *proc = (Process *)ptyproc; + size_t len = strlen(source); + size_t i; + bool quote_hit = true; + char *start = target; + char tmp; - if (ptyproc->process_handle != NULL) { - CloseHandle(ptyproc->process_handle); - ptyproc->process_handle = NULL; + if (len == 0) { + *(target++) = '"'; + *(target++) = '"'; + *target = NUL; + return; } - if (proc->internal_close_cb) { - proc->internal_close_cb(proc); + + if (NULL == strpbrk(source, " \t\"")) { + strcpy(target, source); + return; + } + + if (NULL == strpbrk(source, "\"\\")) { + *(target++) = '"'; + strncpy(target, source, len); + target += len; + *(target++) = '"'; + *target = NUL; + return; + } + + *(target++) = NUL; + *(target++) = '"'; + for (i = len; i > 0; --i) { + *(target++) = source[i - 1]; + + if (quote_hit && source[i - 1] == '\\') { + *(target++) = '\\'; + } else if (source[i - 1] == '"') { + quote_hit = true; + *(target++) = '\\'; + } else { + quote_hit = false; + } + } + *target = '"'; + while (start < target) { + tmp = *start; + *start = *target; + *target = tmp; + start++; + target--; } + return; } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 87b2b6545d..806857f130 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -5,19 +5,24 @@ #include -#include "nvim/event/libuv_process.h" +#include "nvim/event/process.h" +#include "nvim/lib/queue.h" typedef struct pty_process { Process process; char *term_name; uint16_t width, height; winpty_t *wp; - uv_async_t finish_async; HANDLE finish_wait; HANDLE process_handle; - bool is_closing; + uv_timer_t wait_eof_timer; } PtyProcess; +typedef struct arg_S { + char *arg; + QUEUE node; +} arg_T; + static inline PtyProcess pty_process_init(Loop *loop, void *data) { PtyProcess rv; @@ -26,10 +31,8 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.width = 80; rv.height = 24; rv.wp = NULL; - // XXX: Zero rv.finish_async somehow? rv.finish_wait = NULL; rv.process_handle = NULL; - rv.is_closing = false; return rv; } -- cgit From e635754e8e9a92d1537ccc980c63eaa2e9d732bb Mon Sep 17 00:00:00 2001 From: erw7 Date: Wed, 29 Mar 2017 21:39:58 +0900 Subject: win/pty: jobstart, jobstop: fix null-pointer dereference - Make sure that proc->in is not NULL, because nvim crashed when starting a job with pty. - Make sure that proc->out is not NULL, because nvim crashed when stopping a job opened with pty. --- src/nvim/os/pty_process_win.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index ea95c1bb09..101f468005 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -19,7 +19,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); Process *proc = (Process *)ptyproc; - if (!uv_is_readable(proc->out->uvstream)) { + if (!proc->out || !uv_is_readable(proc->out->uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); } @@ -50,7 +50,7 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *in_req = NULL, *out_req = NULL; wchar_t *cmdline = NULL, *cwd = NULL; - assert(proc->in && proc->out && !proc->err); + assert(!proc->err); if (!(cfg = winpty_config_new( WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { @@ -71,18 +71,22 @@ int pty_process_spawn(PtyProcess *ptyproc) if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { goto cleanup; } - in_req = xmalloc(sizeof(uv_connect_t)); - out_req = xmalloc(sizeof(uv_connect_t)); - uv_pipe_connect( - in_req, - &proc->in->uv.pipe, - in_name, - pty_process_connect_cb); - uv_pipe_connect( - out_req, - &proc->out->uv.pipe, - out_name, - pty_process_connect_cb); + if (proc->in) { + in_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + in_req, + &proc->in->uv.pipe, + in_name, + pty_process_connect_cb); + } + if (proc->out) { + out_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + out_req, + &proc->out->uv.pipe, + out_name, + pty_process_connect_cb); + } if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { goto cleanup; @@ -107,7 +111,7 @@ int pty_process_spawn(PtyProcess *ptyproc) abort(); } - while (in_req->handle || out_req->handle) { + while ((in_req && in_req->handle) || (out_req && out_req->handle)) { uv_run(&proc->loop->uv, UV_RUN_ONCE); } -- cgit From 3b992f16889b45215ab6f867edaec5201776d579 Mon Sep 17 00:00:00 2001 From: erw7 Date: Thu, 30 Mar 2017 18:30:40 +0900 Subject: win/pty: quote_cmd_arg(): check bounds --- src/nvim/os/pty_process_win.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 101f468005..f3efe87d78 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -210,9 +210,10 @@ static int build_cmdline(char **argv, wchar_t **cmdline) QUEUE_INIT(&q); while (*argv) { + size_t buf_len = strlen(*argv) * 2 + 3; arg_T *arg = xmalloc(sizeof(arg_T)); - arg->arg = (char *)xmalloc(strlen(*argv) * 2 + 3); - quote_cmd_arg(arg->arg, *argv); + arg->arg = (char *)xmalloc(buf_len); + quote_cmd_arg(arg->arg, buf_len, *argv); args_len += strlen(arg->arg); QUEUE_INIT(&arg->node); QUEUE_INSERT_TAIL(&q, &arg->node); @@ -241,50 +242,50 @@ static int build_cmdline(char **argv, wchar_t **cmdline) } // Emulate quote_cmd_arg of libuv and quotes command line arguments -static void quote_cmd_arg(char *target, const char *source) +static void quote_cmd_arg(char *target, size_t remain, const char *source) FUNC_ATTR_NONNULL_ALL { - size_t len = strlen(source); + size_t src_len = strlen(source); size_t i; bool quote_hit = true; char *start = target; char tmp; - if (len == 0) { - *(target++) = '"'; - *(target++) = '"'; - *target = NUL; + if (src_len == 0) { + snprintf(target, remain, "\"\""); return; } if (NULL == strpbrk(source, " \t\"")) { - strcpy(target, source); + xstrlcpy(target, source, remain); return; } if (NULL == strpbrk(source, "\"\\")) { - *(target++) = '"'; - strncpy(target, source, len); - target += len; - *(target++) = '"'; - *target = NUL; + snprintf(target, remain, "\"%s\"", source); return; } + assert(remain--); *(target++) = NUL; + assert(remain--); *(target++) = '"'; - for (i = len; i > 0; --i) { + for (i = src_len; i > 0; i--) { + assert(remain--); *(target++) = source[i - 1]; if (quote_hit && source[i - 1] == '\\') { + assert(remain--); *(target++) = '\\'; } else if (source[i - 1] == '"') { quote_hit = true; + assert(remain--); *(target++) = '\\'; } else { quote_hit = false; } } + assert(remain); *target = '"'; while (start < target) { tmp = *start; -- cgit From 84fb794da653fc3bcc2832816ca0cab01b4e0400 Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 2 Apr 2017 18:16:39 +0900 Subject: win/pyt: cleanup --- src/nvim/os/pty_process_win.c | 273 +++++++++++++++++++++++++----------------- src/nvim/os/pty_process_win.h | 13 +- 2 files changed, 173 insertions(+), 113 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index f3efe87d78..6a08e2868b 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -12,19 +12,6 @@ # include "os/pty_process_win.c.generated.h" #endif -static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) - FUNC_ATTR_NONNULL_ALL -{ - PtyProcess *ptyproc = - (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); - Process *proc = (Process *)ptyproc; - - if (!proc->out || !uv_is_readable(proc->out->uvstream)) { - uv_timer_stop(&ptyproc->wait_eof_timer); - pty_process_finish2(ptyproc); - } -} - static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) FUNC_ATTR_NONNULL_ALL { @@ -36,6 +23,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } +/// @returns zero on sucess, or error code of winpty or MultiByteToWideChar. int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -44,34 +32,39 @@ int pty_process_spawn(PtyProcess *ptyproc) winpty_error_ptr_t err = NULL; winpty_config_t *cfg = NULL; winpty_spawn_config_t *spawncfg = NULL; - winpty_t *wp = NULL; - char *in_name = NULL, *out_name = NULL; + winpty_t *winpty_object = NULL; + char *in_name = NULL; + char *out_name = NULL; HANDLE process_handle = NULL; - uv_connect_t *in_req = NULL, *out_req = NULL; - wchar_t *cmdline = NULL, *cwd = NULL; + uv_connect_t *in_req = NULL; + uv_connect_t *out_req = NULL; + wchar_t *cmd_line = NULL; + wchar_t *cwd = NULL; assert(!proc->err); - if (!(cfg = winpty_config_new( - WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { + cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); + if (cfg == NULL) { goto cleanup; } - winpty_config_set_initial_size( - cfg, - ptyproc->width, - ptyproc->height); - if (!(wp = winpty_open(cfg, &err))) { + winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_object = winpty_open(cfg, &err); + if (winpty_object == NULL) { goto cleanup; } - if ((status = utf16_to_utf8(winpty_conin_name(wp), &in_name))) { + status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); + if (status != 0) { goto cleanup; } - if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { + + status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); + if (status != 0) { goto cleanup; } - if (proc->in) { + + if (proc->in != NULL) { in_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( in_req, @@ -79,7 +72,8 @@ int pty_process_spawn(PtyProcess *ptyproc) in_name, pty_process_connect_cb); } - if (proc->out) { + + if (proc->out != NULL) { out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( out_req, @@ -88,46 +82,65 @@ int pty_process_spawn(PtyProcess *ptyproc) pty_process_connect_cb); } - if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { - goto cleanup; + if (proc->cwd != NULL) { + status = utf8_to_utf16(proc->cwd, &cwd); + if (status != 0) { + goto cleanup; + } } - if ((status = build_cmdline(proc->argv, &cmdline))) { + + status = build_cmd_line(proc->argv, &cmd_line); + if (status != 0) { goto cleanup; } - if (!(spawncfg = winpty_spawn_config_new( + + spawncfg = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, - NULL, cmdline, cwd, NULL, &err))) { + NULL, // Optional application name + cmd_line, + cwd, + NULL, // Optional environment variables + &err); + if (spawncfg == NULL) { goto cleanup; } - if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { + + if (!winpty_spawn(winpty_object, + spawncfg, + &process_handle, + NULL, // Optional thread handle + NULL, // Optional create process error + &err)) { goto cleanup; } proc->pid = GetProcessId(process_handle); if (!RegisterWaitForSingleObject( &ptyproc->finish_wait, - process_handle, pty_process_finish1, ptyproc, - INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { + process_handle, + pty_process_finish1, + ptyproc, + INFINITE, + WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { abort(); } - while ((in_req && in_req->handle) || (out_req && out_req->handle)) { + // Wait until pty_process_connect_cb is called. + while ((in_req != NULL && in_req->handle != NULL) + || (out_req != NULL && out_req->handle != NULL)) { uv_run(&proc->loop->uv, UV_RUN_ONCE); } - ptyproc->wp = wp; + ptyproc->winpty_object = winpty_object; ptyproc->process_handle = process_handle; - wp = NULL; + winpty_object = NULL; process_handle = NULL; cleanup: - if (err != NULL) { - status = (int)winpty_error_code(err); - } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); - winpty_free(wp); + winpty_free(winpty_object); xfree(in_name); xfree(out_name); if (process_handle != NULL) { @@ -135,7 +148,7 @@ cleanup: } xfree(in_req); xfree(out_req); - xfree(cmdline); + xfree(cmd_line); xfree(cwd); return status; } @@ -144,8 +157,8 @@ void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) FUNC_ATTR_NONNULL_ALL { - if (ptyproc->wp != NULL) { - winpty_set_size(ptyproc->wp, width, height, NULL); + if (ptyproc->winpty_object != NULL) { + winpty_set_size(ptyproc->winpty_object, width, height, NULL); } } @@ -164,9 +177,9 @@ void pty_process_close(PtyProcess *ptyproc) void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { - if (ptyproc->wp != NULL) { - winpty_free(ptyproc->wp); - ptyproc->wp = NULL; + if (ptyproc->winpty_object != NULL) { + winpty_free(ptyproc->winpty_object); + ptyproc->winpty_object = NULL; } } @@ -182,6 +195,19 @@ static void pty_process_connect_cb(uv_connect_t *req, int status) req->handle = NULL; } +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = + (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + Process *proc = (Process *)ptyproc; + + if (!proc->out || !uv_is_readable(proc->out->uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } +} + static void pty_process_finish2(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -200,99 +226,132 @@ static void pty_process_finish2(PtyProcess *ptyproc) proc->internal_exit_cb(proc); } -static int build_cmdline(char **argv, wchar_t **cmdline) +/// Build the command line to pass to CreateProcessW. +/// +/// @param[in] argv Array with string arguments. +/// @param[out] cmd_line Location where saved bulded cmd line. +/// +/// @returns zero on sucess, or error code of MultiByteToWideChar function. +/// +static int build_cmd_line(char **argv, wchar_t **cmd_line) FUNC_ATTR_NONNULL_ALL { - char *args = NULL; - size_t args_len = 0, argc = 0; - int ret; - QUEUE q; - QUEUE_INIT(&q); + size_t utf8_cmd_line_len = 0; + size_t argc = 0; + QUEUE args_q; + QUEUE_INIT(&args_q); while (*argv) { size_t buf_len = strlen(*argv) * 2 + 3; - arg_T *arg = xmalloc(sizeof(arg_T)); - arg->arg = (char *)xmalloc(buf_len); - quote_cmd_arg(arg->arg, buf_len, *argv); - args_len += strlen(arg->arg); - QUEUE_INIT(&arg->node); - QUEUE_INSERT_TAIL(&q, &arg->node); + ArgNode *arg_node = xmalloc(sizeof(*arg_node)); + arg_node->arg = xmalloc(buf_len); + quote_cmd_arg(arg_node->arg, buf_len, *argv); + utf8_cmd_line_len += strlen(arg_node->arg); + QUEUE_INIT(&arg_node->node); + QUEUE_INSERT_TAIL(&args_q, &arg_node->node); argc++; argv++; } - args_len += argc; - args = xmalloc(args_len); - *args = NUL; + + utf8_cmd_line_len += argc; + char *utf8_cmd_line = xmalloc(utf8_cmd_line_len); + *utf8_cmd_line = NUL; while (1) { - QUEUE *head = QUEUE_HEAD(&q); + QUEUE *head = QUEUE_HEAD(&args_q); QUEUE_REMOVE(head); - arg_T *arg = QUEUE_DATA(head, arg_T, node); - xstrlcat(args, arg->arg, args_len); - xfree(arg->arg); - xfree(arg); - if (QUEUE_EMPTY(&q)) { + ArgNode *arg_node = QUEUE_DATA(head, ArgNode, node); + xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len); + xfree(arg_node->arg); + xfree(arg_node); + if (QUEUE_EMPTY(&args_q)) { break; } else { - xstrlcat(args, " ", args_len); + xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len); } } - ret = utf8_to_utf16(args, cmdline); - xfree(args); - return ret; + + int result = utf8_to_utf16(utf8_cmd_line, cmd_line); + if (result != 0) { + } + xfree(utf8_cmd_line); + return result; } -// Emulate quote_cmd_arg of libuv and quotes command line arguments -static void quote_cmd_arg(char *target, size_t remain, const char *source) +/// Emulate quote_cmd_arg of libuv and quotes command line argument. +/// Most of the code came from libuv. +/// +/// @param[out] dist Location where saved quotes argument. +/// @param dist_remaining Deistnation buffer size. +/// @param[in] src Pointer to argument. +/// +static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) FUNC_ATTR_NONNULL_ALL { - size_t src_len = strlen(source); - size_t i; + size_t src_len = strlen(src); bool quote_hit = true; - char *start = target; - char tmp; + char *start = dist; if (src_len == 0) { - snprintf(target, remain, "\"\""); + // Need double quotation for empty argument. + snprintf(dist, dist_remaining, "\"\""); return; } - if (NULL == strpbrk(source, " \t\"")) { - xstrlcpy(target, source, remain); + if (NULL == strpbrk(src, " \t\"")) { + // No quotation needed. + xstrlcpy(dist, src, dist_remaining); return; } - if (NULL == strpbrk(source, "\"\\")) { - snprintf(target, remain, "\"%s\"", source); + if (NULL == strpbrk(src, "\"\\")) { + // No embedded double quotes or backlashes, so I can just wrap quote marks. + // around the whole thing. + snprintf(dist, dist_remaining, "\"%s\"", src); return; } - assert(remain--); - *(target++) = NUL; - assert(remain--); - *(target++) = '"'; - for (i = src_len; i > 0; i--) { - assert(remain--); - *(target++) = source[i - 1]; - - if (quote_hit && source[i - 1] == '\\') { - assert(remain--); - *(target++) = '\\'; - } else if (source[i - 1] == '"') { + // Expected input/output: + // input : hello"world + // output: "hello\"world" + // input : hello""world + // output: "hello\"\"world" + // input : hello\world + // output: hello\world + // input : hello\\world + // output: hello\\world + // input : hello\"world + // output: "hello\\\"world" + // input : hello\\"world + // output: "hello\\\\\"world" + // input : hello world\ + // output: "hello world\\" + + assert(dist_remaining--); + *(dist++) = NUL; + assert(dist_remaining--); + *(dist++) = '"'; + for (size_t i = src_len; i > 0; i--) { + assert(dist_remaining--); + *(dist++) = src[i - 1]; + if (quote_hit && src[i - 1] == '\\') { + assert(dist_remaining--); + *(dist++) = '\\'; + } else if (src[i - 1] == '"') { quote_hit = true; - assert(remain--); - *(target++) = '\\'; + assert(dist_remaining--); + *(dist++) = '\\'; } else { quote_hit = false; } } - assert(remain); - *target = '"'; - while (start < target) { - tmp = *start; - *start = *target; - *target = tmp; + assert(dist_remaining); + *dist = '"'; + + while (start < dist) { + char tmp = *start; + *start = *dist; + *dist = tmp; start++; - target--; + dist--; } - return; } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 806857f130..59e0fad7f7 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -12,16 +12,17 @@ typedef struct pty_process { Process process; char *term_name; uint16_t width, height; - winpty_t *wp; + winpty_t *winpty_object; HANDLE finish_wait; HANDLE process_handle; uv_timer_t wait_eof_timer; } PtyProcess; -typedef struct arg_S { - char *arg; - QUEUE node; -} arg_T; +// Structure used by build_cmd_line() +typedef struct arg_node { + char *arg; // pointer to argument. + QUEUE node; // QUEUE structure. +} ArgNode; static inline PtyProcess pty_process_init(Loop *loop, void *data) { @@ -30,7 +31,7 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.term_name = NULL; rv.width = 80; rv.height = 24; - rv.wp = NULL; + rv.winpty_object = NULL; rv.finish_wait = NULL; rv.process_handle = NULL; return rv; -- cgit From d3a8c4f99289f7b65a68bf9ed5eeab34aa688e0e Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 2 Apr 2017 18:32:23 +0900 Subject: win/pty: log errors --- src/nvim/os/pty_process_win.c | 112 ++++++++++++++++++++++++++++++------------ src/nvim/os/pty_process_win.h | 1 - 2 files changed, 80 insertions(+), 33 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 6a08e2868b..4bb5f64455 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -2,7 +2,9 @@ #include #include -#include "nvim/vim.h" +#include + +#include "nvim/os/os.h" #include "nvim/ascii.h" #include "nvim/memory.h" #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 @@ -23,7 +25,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } -/// @returns zero on sucess, or error code of winpty or MultiByteToWideChar. +/// @returns zero on success, or negative error code. int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -40,27 +42,32 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *out_req = NULL; wchar_t *cmd_line = NULL; wchar_t *cwd = NULL; + const char *emsg = NULL; assert(!proc->err); cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); if (cfg == NULL) { + emsg = "Failed, winpty_config_new."; goto cleanup; } winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); winpty_object = winpty_open(cfg, &err); if (winpty_object == NULL) { + emsg = "Failed, winpty_open."; goto cleanup; } status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); if (status != 0) { + emsg = "Failed to convert in_name from utf16 to utf8."; goto cleanup; } status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); if (status != 0) { + emsg = "Failed to convert out_name from utf16 to utf8."; goto cleanup; } @@ -85,12 +92,14 @@ int pty_process_spawn(PtyProcess *ptyproc) if (proc->cwd != NULL) { status = utf8_to_utf16(proc->cwd, &cwd); if (status != 0) { + emsg = "Failed to convert pwd form utf8 to utf16."; goto cleanup; } } status = build_cmd_line(proc->argv, &cmd_line); if (status != 0) { + emsg = "Failed to convert cmd line form utf8 to utf16."; goto cleanup; } @@ -102,15 +111,23 @@ int pty_process_spawn(PtyProcess *ptyproc) NULL, // Optional environment variables &err); if (spawncfg == NULL) { + emsg = "Failed winpty_spawn_config_new."; goto cleanup; } + DWORD win_err = 0; if (!winpty_spawn(winpty_object, spawncfg, &process_handle, NULL, // Optional thread handle - NULL, // Optional create process error + &win_err, &err)) { + if (win_err) { + status = (int)win_err; + emsg = "Failed spawn process."; + } else { + emsg = "Failed winpty_spawn."; + } goto cleanup; } proc->pid = GetProcessId(process_handle); @@ -137,6 +154,15 @@ int pty_process_spawn(PtyProcess *ptyproc) process_handle = NULL; cleanup: + if (status) { + // In the case of an error of MultiByteToWideChar or CreateProcessW. + ELOG("%s error code: %d", emsg, status); + status = os_translate_sys_error(status); + } else if (err != NULL) { + status = (int)winpty_error_code(err); + ELOG("%s error code: %d", emsg, status); + status = translate_winpty_error(status); + } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); @@ -198,8 +224,7 @@ static void pty_process_connect_cb(uv_connect_t *req, int status) static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + PtyProcess *ptyproc = wait_eof_timer->data; Process *proc = (Process *)ptyproc; if (!proc->out || !uv_is_readable(proc->out->uvstream)) { @@ -229,9 +254,9 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// Build the command line to pass to CreateProcessW. /// /// @param[in] argv Array with string arguments. -/// @param[out] cmd_line Location where saved bulded cmd line. +/// @param[out] cmd_line Location where saved builded cmd line. /// -/// @returns zero on sucess, or error code of MultiByteToWideChar function. +/// @returns zero on success, or error code of MultiByteToWideChar function. /// static int build_cmd_line(char **argv, wchar_t **cmd_line) FUNC_ATTR_NONNULL_ALL @@ -271,8 +296,6 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) } int result = utf8_to_utf16(utf8_cmd_line, cmd_line); - if (result != 0) { - } xfree(utf8_cmd_line); return result; } @@ -280,33 +303,33 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) /// Emulate quote_cmd_arg of libuv and quotes command line argument. /// Most of the code came from libuv. /// -/// @param[out] dist Location where saved quotes argument. -/// @param dist_remaining Deistnation buffer size. +/// @param[out] dest Location where saved quotes argument. +/// @param dest_remaining Destination buffer size. /// @param[in] src Pointer to argument. /// -static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) +static void quote_cmd_arg(char *dest, size_t dest_remaining, const char *src) FUNC_ATTR_NONNULL_ALL { size_t src_len = strlen(src); bool quote_hit = true; - char *start = dist; + char *start = dest; if (src_len == 0) { // Need double quotation for empty argument. - snprintf(dist, dist_remaining, "\"\""); + snprintf(dest, dest_remaining, "\"\""); return; } if (NULL == strpbrk(src, " \t\"")) { // No quotation needed. - xstrlcpy(dist, src, dist_remaining); + xstrlcpy(dest, src, dest_remaining); return; } if (NULL == strpbrk(src, "\"\\")) { // No embedded double quotes or backlashes, so I can just wrap quote marks. // around the whole thing. - snprintf(dist, dist_remaining, "\"%s\"", src); + snprintf(dest, dest_remaining, "\"%s\"", src); return; } @@ -326,32 +349,57 @@ static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) // input : hello world\ // output: "hello world\\" - assert(dist_remaining--); - *(dist++) = NUL; - assert(dist_remaining--); - *(dist++) = '"'; + assert(dest_remaining--); + *(dest++) = NUL; + assert(dest_remaining--); + *(dest++) = '"'; for (size_t i = src_len; i > 0; i--) { - assert(dist_remaining--); - *(dist++) = src[i - 1]; + assert(dest_remaining--); + *(dest++) = src[i - 1]; if (quote_hit && src[i - 1] == '\\') { - assert(dist_remaining--); - *(dist++) = '\\'; + assert(dest_remaining--); + *(dest++) = '\\'; } else if (src[i - 1] == '"') { quote_hit = true; - assert(dist_remaining--); - *(dist++) = '\\'; + assert(dest_remaining--); + *(dest++) = '\\'; } else { quote_hit = false; } } - assert(dist_remaining); - *dist = '"'; + assert(dest_remaining); + *dest = '"'; - while (start < dist) { + while (start < dest) { char tmp = *start; - *start = *dist; - *dist = tmp; + *start = *dest; + *dest = tmp; start++; - dist--; + dest--; + } +} + +/// Translate winpty error code to libuv error. +/// +/// @param[in] winpty_errno Winpty error code returned by winpty_error_code +/// function. +/// +/// @returns Error code of libuv error. +int translate_winpty_error(int winpty_errno) +{ + if (winpty_errno <= 0) { + return winpty_errno; // If < 0 then it's already a libuv error. + } + + switch (winpty_errno) { + case WINPTY_ERROR_OUT_OF_MEMORY: return UV_ENOMEM; + case WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED: return UV_EAI_FAIL; + case WINPTY_ERROR_LOST_CONNECTION: return UV_ENOTCONN; + case WINPTY_ERROR_AGENT_EXE_MISSING: return UV_ENOENT; + case WINPTY_ERROR_UNSPECIFIED: return UV_UNKNOWN; + case WINPTY_ERROR_AGENT_DIED: return UV_ESRCH; + case WINPTY_ERROR_AGENT_TIMEOUT: return UV_ETIMEDOUT; + case WINPTY_ERROR_AGENT_CREATION_FAILED: return UV_EAI_FAIL; + default: return UV_UNKNOWN; } } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 59e0fad7f7..1a4019e654 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -2,7 +2,6 @@ #define NVIM_OS_PTY_PROCESS_WIN_H #include - #include #include "nvim/event/process.h" -- cgit From 8c1782b84070d786cc1a7dfd6242a9ebc0923ad3 Mon Sep 17 00:00:00 2001 From: erw7 Date: Fri, 14 Apr 2017 22:35:56 +0900 Subject: pty_process_win: avoid quoting for cmd.exe --- src/nvim/os/pty_process_win.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 4bb5f64455..ef8a699c56 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -97,7 +97,8 @@ int pty_process_spawn(PtyProcess *ptyproc) } } - status = build_cmd_line(proc->argv, &cmd_line); + status = build_cmd_line(proc->argv, &cmd_line, + os_shell_is_cmdexe(proc->argv[0])); if (status != 0) { emsg = "Failed to convert cmd line form utf8 to utf16."; goto cleanup; @@ -258,7 +259,7 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// /// @returns zero on success, or error code of MultiByteToWideChar function. /// -static int build_cmd_line(char **argv, wchar_t **cmd_line) +static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe) FUNC_ATTR_NONNULL_ALL { size_t utf8_cmd_line_len = 0; @@ -267,10 +268,14 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) QUEUE_INIT(&args_q); while (*argv) { - size_t buf_len = strlen(*argv) * 2 + 3; + size_t buf_len = is_cmdexe ? (strlen(*argv) + 1) : (strlen(*argv) * 2 + 3); ArgNode *arg_node = xmalloc(sizeof(*arg_node)); arg_node->arg = xmalloc(buf_len); - quote_cmd_arg(arg_node->arg, buf_len, *argv); + if (is_cmdexe) { + xstrlcpy(arg_node->arg, *argv, buf_len); + } else { + quote_cmd_arg(arg_node->arg, buf_len, *argv); + } utf8_cmd_line_len += strlen(arg_node->arg); QUEUE_INIT(&arg_node->node); QUEUE_INSERT_TAIL(&args_q, &arg_node->node); -- cgit From 8642f05fd91d7285a7d8d2bb13b4d63fe5c371fa Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 6 Aug 2017 14:28:51 +0200 Subject: single-includes: ignore os/pty_process_win.h --- src/nvim/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2e0a35d4ab..688912eda6 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -535,6 +535,7 @@ endfunction() set(NO_SINGLE_CHECK_HEADERS os/win_defs.h + os/pty_process_win.h regexp_defs.h syntax_defs.h terminal.h -- cgit From f0a9b7ff637b1c33988b8ec20530657ba71644fc Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Jun 2017 06:43:26 +0200 Subject: vim-patch:8.0.0678 closing a window does not trigger resizing Closes #6748 Problem: When 'equalalways' is set and closing a window in a separate frame, not all window sizes are adjusted. (Glacambre) Solution: Resize all windows if the new current window is not in the same frame as the closed window. (closes vim/vim#1707) https://github.com/vim/vim/commit/8eeeba8c025ff844e6514c4a60cec11bf1fc1b35 --- src/nvim/testdir/test_window_cmd.vim | 44 ++++++++++++++++++++++++++++++++++++ src/nvim/version.c | 2 +- src/nvim/window.c | 5 +++- 3 files changed, 49 insertions(+), 2 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 188a7ed0f3..9d61921988 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -336,6 +336,50 @@ func Test_window_width() bw Xa Xb Xc endfunc +func Test_equalalways_on_close() + set equalalways + vsplit + windo split + split + wincmd J + " now we have a frame top-left with two windows, a frame top-right with two + " windows and a frame at the bottom, full-width. + let height_1 = winheight(1) + let height_2 = winheight(2) + let height_3 = winheight(3) + let height_4 = winheight(4) + " closing the bottom window causes all windows to be resized. + close + call assert_notequal(height_1, winheight(1)) + call assert_notequal(height_2, winheight(2)) + call assert_notequal(height_3, winheight(3)) + call assert_notequal(height_4, winheight(4)) + call assert_equal(winheight(1), winheight(3)) + call assert_equal(winheight(2), winheight(4)) + + 1wincmd w + split + 4wincmd w + resize + 5 + " left column has three windows, equalized heights. + " right column has two windows, top one a bit higher + let height_1 = winheight(1) + let height_2 = winheight(2) + let height_4 = winheight(4) + let height_5 = winheight(5) + 3wincmd w + " closing window in left column equalizes heights in left column but not in + " the right column + close + call assert_notequal(height_1, winheight(1)) + call assert_notequal(height_2, winheight(2)) + call assert_equal(height_4, winheight(3)) + call assert_equal(height_5, winheight(4)) + + only + set equalalways& +endfunc + func Test_window_jump_tag() help /iccf diff --git a/src/nvim/version.c b/src/nvim/version.c index f4984864f3..80b8b03258 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -274,7 +274,7 @@ static const int included_patches[] = { // 681, // 680, // 679, - // 678, + 678, // 677, // 676, // 675, diff --git a/src/nvim/window.c b/src/nvim/window.c index faf5bceb56..6a21844812 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1878,6 +1878,7 @@ int win_close(win_T *win, int free_buf) int dir; int help_window = FALSE; tabpage_T *prev_curtab = curtab; + frame_T *win_frame = win->w_frame; if (last_window()) { EMSG(_("E444: Cannot close last window")); @@ -2027,7 +2028,9 @@ int win_close(win_T *win, int free_buf) check_cursor(); } if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { - win_equal(curwin, true, dir); + // If the frame of the closed window contains the new current window, + // only resize that frame. Otherwise resize all windows. + win_equal(curwin, curwin->w_frame->fr_parent == win_frame->fr_parent, dir); } else { win_comp_pos(); } -- cgit From 1fb3339844b2244daaee9b44264137c8c6c67164 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 27 Jun 2017 02:32:08 +0200 Subject: vim-patch:8.0.0679 Problem: Using freed memory. Solution: Get the parent frame pointer earlier. https://github.com/vim/vim/commit/41cc038ff83498c589c7d25b3d2984145528eb92 --- src/nvim/version.c | 2 +- src/nvim/window.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/version.c b/src/nvim/version.c index 80b8b03258..bd90f28629 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -273,7 +273,7 @@ static const int included_patches[] = { // 682, // 681, // 680, - // 679, + 679, 678, // 677, // 676, diff --git a/src/nvim/window.c b/src/nvim/window.c index 6a21844812..081fc98816 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1878,7 +1878,7 @@ int win_close(win_T *win, int free_buf) int dir; int help_window = FALSE; tabpage_T *prev_curtab = curtab; - frame_T *win_frame = win->w_frame; + frame_T *win_frame = win->w_frame->fr_parent; if (last_window()) { EMSG(_("E444: Cannot close last window")); @@ -2030,7 +2030,7 @@ int win_close(win_T *win, int free_buf) if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { // If the frame of the closed window contains the new current window, // only resize that frame. Otherwise resize all windows. - win_equal(curwin, curwin->w_frame->fr_parent == win_frame->fr_parent, dir); + win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); } else { win_comp_pos(); } -- cgit From 8dddf5590796994254fb31b4cbe1736ec3faf9f6 Mon Sep 17 00:00:00 2001 From: ckelsel Date: Wed, 19 Jul 2017 22:48:05 +0800 Subject: vim-patch:8.0.0044 Problem: In diff mode the cursor may end up below the last line, resulting in an ml_get error. Solution: Check the line to be valid. https://github.com/vim/vim/commit/025e3e0bafbc85cc4e365145af711edf99d0a90d --- src/nvim/diff.c | 67 ++++++++++++++++++-------------------- src/nvim/move.c | 12 +++---- src/nvim/testdir/test_diffmode.vim | 17 ++++++++++ src/nvim/version.c | 2 +- 4 files changed, 54 insertions(+), 44 deletions(-) (limited to 'src/nvim') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 7da6665cb7..17de4284ce 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1033,10 +1033,7 @@ void ex_diffsplit(exarg_T *eap) if (bufref_valid(&old_curbuf)) { // Move the cursor position to that of the old window. curwin->w_cursor.lnum = diff_get_corresponding_line( - old_curbuf.br_buf, - old_curwin->w_cursor.lnum, - curbuf, - curwin->w_cursor.lnum); + old_curbuf.br_buf, old_curwin->w_cursor.lnum); } } // Now that lines are folded scroll to show the cursor at the same @@ -2463,25 +2460,17 @@ int diff_move_to(int dir, long count) return OK; } -/// Finds the corresponding line in a diff. -/// -/// @param buf1 -/// @param lnum1 -/// @param buf2 -/// @param lnum3 -/// -/// @return The corresponding line. -linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2, - linenr_T lnum3) +/// Return the line number in the current window that is closest to "lnum1" in +/// "buf1" in diff mode. +static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1) { int idx1; int idx2; diff_T *dp; int baseline = 0; - linenr_T lnum2; idx1 = diff_buf_idx(buf1); - idx2 = diff_buf_idx(buf2); + idx2 = diff_buf_idx(curbuf); if ((idx1 == DB_COUNT) || (idx2 == DB_COUNT) @@ -2501,15 +2490,9 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2, for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { if (dp->df_lnum[idx1] > lnum1) { - lnum2 = lnum1 - baseline; - - // don't end up past the end of the file - if (lnum2 > buf2->b_ml.ml_line_count) { - lnum2 = buf2->b_ml.ml_line_count; - } - - return lnum2; - } else if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) { + return lnum1 - baseline; + } + if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) { // Inside the diffblock baseline = lnum1 - dp->df_lnum[idx1]; @@ -2518,30 +2501,42 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2, } return dp->df_lnum[idx2] + baseline; - } else if ((dp->df_lnum[idx1] == lnum1) - && (dp->df_count[idx1] == 0) - && (dp->df_lnum[idx2] <= lnum3) - && ((dp->df_lnum[idx2] + dp->df_count[idx2]) > lnum3)) { + } + if ((dp->df_lnum[idx1] == lnum1) + && (dp->df_count[idx1] == 0) + && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum) + && ((dp->df_lnum[idx2] + dp->df_count[idx2]) + > curwin->w_cursor.lnum)) { // Special case: if the cursor is just after a zero-count // block (i.e. all filler) and the target cursor is already // inside the corresponding block, leave the target cursor // unmoved. This makes repeated CTRL-W W operations work // as expected. - return lnum3; + return curwin->w_cursor.lnum; } baseline = (dp->df_lnum[idx1] + dp->df_count[idx1]) - - (dp->df_lnum[idx2] + dp->df_count[idx2]); + - (dp->df_lnum[idx2] + dp->df_count[idx2]); } // If we get here then the cursor is after the last diff - lnum2 = lnum1 - baseline; + return lnum1 - baseline; +} + +/// Finds the corresponding line in a diff. +/// +/// @param buf1 +/// @param lnum1 +/// +/// @return The corresponding line. +linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1) +{ + linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1); // don't end up past the end of the file - if (lnum2 > buf2->b_ml.ml_line_count) { - lnum2 = buf2->b_ml.ml_line_count; + if (lnum > curbuf->b_ml.ml_line_count) { + return curbuf->b_ml.ml_line_count; } - - return lnum2; + return lnum; } /// For line "lnum" in the current window find the equivalent lnum in window diff --git a/src/nvim/move.c b/src/nvim/move.c index 81d46a7f17..4d646f5a4b 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -2147,14 +2147,12 @@ void do_check_cursorbind(void) curbuf = curwin->w_buffer; /* skip original window and windows with 'noscrollbind' */ if (curwin != old_curwin && curwin->w_p_crb) { - if (curwin->w_p_diff) - curwin->w_cursor.lnum - = diff_get_corresponding_line(old_curbuf, - line, - curbuf, - curwin->w_cursor.lnum); - else + if (curwin->w_p_diff) { + curwin->w_cursor.lnum = + diff_get_corresponding_line(old_curbuf, line); + } else { curwin->w_cursor.lnum = line; + } curwin->w_cursor.col = col; curwin->w_cursor.coladd = coladd; curwin->w_curswant = curswant; diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 5de394de8e..f40e06ff33 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -218,3 +218,20 @@ func Test_diffoff() bwipe! bwipe! endfunc + +func Test_setting_cursor() + new Xtest1 + put =range(1,90) + wq + new Xtest2 + put =range(1,100) + wq + + tabe Xtest2 + $ + diffsp Xtest1 + tabclose + + call delete('Xtest1') + call delete('Xtest2') +endfunc diff --git a/src/nvim/version.c b/src/nvim/version.c index f4984864f3..86c87b08ef 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -908,7 +908,7 @@ static const int included_patches[] = { 47, 46, // 45 NA - // 44, + 44, 43, 42, 41, -- cgit