From 30b29a36e80bfeed50bb6ea618401fe35100490f Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 9 Feb 2023 20:56:30 +0100 Subject: refactor(ui): remove some superfluous ui_flush() calls - mapping has no business saving and restoring the low-level UI cursor. The cursor will be put in a reasonable position after input is processed, chill out. - TUI handles output needed for suspend - vgetc() family of function does flushing --- src/nvim/eval.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4392ea306f..4de4b7a080 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7469,7 +7469,6 @@ void ex_execute(exarg_T *eap) if (eap->cmdidx == CMD_echomsg) { msg_ext_set_kind("echomsg"); msg_attr(ga.ga_data, echo_attr); - ui_flush(); } else if (eap->cmdidx == CMD_echoerr) { // We don't want to abort following commands, restore did_emsg. int save_did_emsg = did_emsg; -- cgit From 7224c889e0d5d70b99ae377036baa6377c33a568 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 11 Feb 2023 10:25:24 +0100 Subject: build: enable MSVC level 3 warnings (#21934) MSVC has 4 different warning levels: 1 (severe), 2 (significant), 3 (production quality) and 4 (informational). Enabling level 3 warnings mostly revealed conversion problems, similar to GCC/clang -Wconversion flag. --- src/nvim/eval.c | 56 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4de4b7a080..edbeabc53d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3276,7 +3276,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) { bool empty1 = false; bool empty2 = false; - long n1, n2 = 0; + int n1, n2 = 0; ptrdiff_t len = -1; int range = false; char *key = NULL; @@ -3375,14 +3375,14 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) if (evaluate) { n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) { - n1 = tv_get_number(&var1); + n1 = (int)tv_get_number(&var1); tv_clear(&var1); } if (range) { if (empty2) { n2 = -1; } else { - n2 = tv_get_number(&var2); + n2 = (int)tv_get_number(&var2); tv_clear(&var2); } } @@ -3397,15 +3397,15 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) // The resulting variable is a substring. If the indexes // are out of range the result is empty. if (n1 < 0) { - n1 = len + n1; + n1 = (int)len + n1; if (n1 < 0) { n1 = 0; } } if (n2 < 0) { - n2 = len + n2; + n2 = (int)len + n2; } else if (n2 >= len) { - n2 = len; + n2 = (int)len; } if (n1 >= len || n2 < 0 || n1 > n2) { v = NULL; @@ -3433,15 +3433,15 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) // The resulting variable is a sub-blob. If the indexes // are out of range the result is empty. if (n1 < 0) { - n1 = len + n1; + n1 = (int)len + n1; if (n1 < 0) { n1 = 0; } } if (n2 < 0) { - n2 = len + n2; + n2 = (int)len + n2; } else if (n2 >= len) { - n2 = len - 1; + n2 = (int)len - 1; } if (n1 >= len || n2 < 0 || n1 > n2) { tv_clear(rettv); @@ -3449,8 +3449,8 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) rettv->vval.v_blob = NULL; } else { blob_T *const blob = tv_blob_alloc(); - ga_grow(&blob->bv_ga, (int)(n2 - n1 + 1)); - blob->bv_ga.ga_len = (int)(n2 - n1 + 1); + ga_grow(&blob->bv_ga, n2 - n1 + 1); + blob->bv_ga.ga_len = n2 - n1 + 1; for (long i = n1; i <= n2; i++) { tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i)); } @@ -3461,10 +3461,10 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) // The resulting variable is a byte value. // If the index is too big or negative that is an error. if (n1 < 0) { - n1 = len + n1; + n1 = (int)len + n1; } if (n1 < len && n1 >= 0) { - const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)n1); + const int v = (int)tv_blob_get(rettv->vval.v_blob, n1); tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = v; @@ -3476,7 +3476,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) case VAR_LIST: len = tv_list_len(rettv->vval.v_list); if (n1 < 0) { - n1 = len + n1; + n1 = (int)len + n1; } if (!empty1 && (n1 < 0 || n1 >= len)) { // For a range we allow invalid values and return an empty @@ -3487,22 +3487,22 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) } return FAIL; } - n1 = len; + n1 = (int)len; } if (range) { list_T *l; listitem_T *item; if (n2 < 0) { - n2 = len + n2; + n2 = (int)len + n2; } else if (n2 >= len) { - n2 = len - 1; + n2 = (int)len - 1; } if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { n2 = -1; } l = tv_list_alloc(n2 - n1 + 1); - item = tv_list_find(rettv->vval.v_list, (int)n1); + item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item); @@ -6220,25 +6220,25 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c } int i = 0; - long n; + int n; if (fnump != NULL) { - n = tv_list_find_nr(l, i++, NULL); // fnum + n = (int)tv_list_find_nr(l, i++, NULL); // fnum if (n < 0) { return FAIL; } if (n == 0) { n = curbuf->b_fnum; // Current buffer. } - *fnump = (int)n; + *fnump = n; } - n = tv_list_find_nr(l, i++, NULL); // lnum + n = (int)tv_list_find_nr(l, i++, NULL); // lnum if (n < 0) { return FAIL; } - posp->lnum = (linenr_T)n; + posp->lnum = n; - n = tv_list_find_nr(l, i++, NULL); // col + n = (int)tv_list_find_nr(l, i++, NULL); // col if (n < 0) { return FAIL; } @@ -6252,15 +6252,15 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c } n = buf_charidx_to_byteidx(buf, posp->lnum == 0 ? curwin->w_cursor.lnum : posp->lnum, - (int)n) + 1; + n) + 1; } - posp->col = (colnr_T)n; + posp->col = n; - n = tv_list_find_nr(l, i, NULL); // off + n = (int)tv_list_find_nr(l, i, NULL); // off if (n < 0) { posp->coladd = 0; } else { - posp->coladd = (colnr_T)n; + posp->coladd = n; } if (curswantp != NULL) { -- cgit From 4be6c6cf0ddf5e31d4103cb5df06651ba6f4897b Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 11 Feb 2023 11:05:57 +0100 Subject: refactor: replace char_u with char (#21901) refactor: replace char_u with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index edbeabc53d..fb69734457 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3915,7 +3915,7 @@ char *partial_name(partial_T *pt) if (pt->pt_name != NULL) { return pt->pt_name; } - return (char *)pt->pt_func->uf_name; + return pt->pt_func->uf_name; } static void partial_free(partial_T *pt) @@ -5052,7 +5052,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) if (tv_list_len(list) == 0) { arg_idx = 0; } else if (tv_list_len(list) > MAX_FUNC_ARGS) { - emsg_funcname((char *)e_toomanyarg, s); + emsg_funcname(e_toomanyarg, s); xfree(name); goto theend; } -- cgit From 47638706a37534ae9bc7ac4c57ddb9fb2b44fef0 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 12 Feb 2023 17:41:54 +0100 Subject: build: treat clang-tidy warnings as errors (#22238) --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fb69734457..1a10f8aebc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3410,7 +3410,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) if (n1 >= len || n2 < 0 || n1 > n2) { v = NULL; } else { - v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1)); + v = xmemdupz(s + n1, (size_t)n2 - (size_t)n1 + 1); } } else { // The resulting variable is a string of a single -- cgit From 5f72ab77bff1f1224be5cbbf9423bdddbc25635c Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 12 Feb 2023 18:48:49 +0100 Subject: refactor: reduce scope of locals as per the style guide 3 (#22221) refactor: reduce scope of locals as per the style guide --- src/nvim/eval.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1a10f8aebc..3ab704e250 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1949,7 +1949,6 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) FUNC_ATTR_NONNULL_ALL { bool got_eq = false; - int c; char *p; if (cmdidx == CMD_let || cmdidx == CMD_const) { @@ -1970,7 +1969,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) : EXPAND_EXPRESSION; } while ((xp->xp_pattern = strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#")) != NULL) { - c = (uint8_t)(*xp->xp_pattern); + int c = (uint8_t)(*xp->xp_pattern); if (c == '&') { c = (uint8_t)xp->xp_pattern[1]; if (c == '&') { @@ -2310,7 +2309,6 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) /// @return OK or FAIL. int eval1(char **arg, typval_T *rettv, int evaluate) { - bool result; typval_T var2; // Get the first variable. @@ -2319,7 +2317,7 @@ int eval1(char **arg, typval_T *rettv, int evaluate) } if ((*arg)[0] == '?') { - result = false; + bool result = false; if (evaluate) { bool error = false; @@ -2499,7 +2497,6 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) char *p; exprtype_T type = EXPR_UNKNOWN; int len = 2; - bool ic; // Get the first variable. if (eval5(arg, rettv, evaluate) == FAIL) { @@ -2552,6 +2549,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) // If there is a comparative operator, use it. if (type != EXPR_UNKNOWN) { + bool ic; // extra question mark appended: ignore case if (p[len] == '?') { ic = true; @@ -2594,7 +2592,6 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) { typval_T var2; typval_T var3; - int op; varnumber_T n1, n2; float_T f1 = 0, f2 = 0; char *p; @@ -2606,7 +2603,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) // Repeat computing, until no '+', '-' or '.' is following. for (;;) { - op = (char_u)(**arg); + int op = (char_u)(**arg); if (op != '+' && op != '-' && op != '.') { break; } @@ -3276,7 +3273,6 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) { bool empty1 = false; bool empty2 = false; - int n1, n2 = 0; ptrdiff_t len = -1; int range = false; char *key = NULL; @@ -3373,7 +3369,8 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) } if (evaluate) { - n1 = 0; + int n2 = 0; + int n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) { n1 = (int)tv_get_number(&var1); tv_clear(&var1); @@ -4784,8 +4781,6 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) const char *const arg_errmsg = (map ? N_("map() argument") : N_("filter() argument")); - int save_did_emsg; - int idx = 0; // Always return the first argument, also on failure. tv_copy(&argvars[0], rettv); @@ -4815,12 +4810,13 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) // message. Avoid a misleading error message for an empty string that // was not passed as argument. if (expr->v_type != VAR_UNKNOWN) { + int idx = 0; typval_T save_val; prepare_vimvar(VV_VAL, &save_val); // We reset "did_emsg" to be able to detect whether an error // occurred during evaluation of the expression. - save_did_emsg = did_emsg; + int save_did_emsg = did_emsg; did_emsg = false; typval_T save_key; @@ -8064,7 +8060,6 @@ repeat: /// @return an allocated string, NULL for error. char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char *flags) { - int sublen; regmatch_T regmatch; garray_T ga; char *zero_width = NULL; @@ -8080,6 +8075,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char regmatch.rm_ic = p_ic; regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { + int sublen; char *tail = str; char *end = str + strlen(str); while (vim_regexec_nl(®match, str, (colnr_T)(tail - str))) { -- cgit From e619fb1660595f9e6a0afad8d9a91e37a94f95a3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 16 Feb 2023 10:32:28 +0800 Subject: vim-patch:8.2.0114: info about sourced scripts is scattered Problem: Info about sourced scripts is scattered. Solution: Use scriptitem_T for info about a script, including s: variables. Drop ga_scripts. https://github.com/vim/vim/commit/7ebcba61b20d25d23109fff73d0346ad44ba1b3b Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 73 +++++++++++++++++++-------------------------------------- 1 file changed, 24 insertions(+), 49 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3ab704e250..a09bd72710 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -109,15 +109,7 @@ static hashtab_T compat_hashtab; /// Used for checking if local variables or arguments used in a lambda. bool *eval_lavars_used = NULL; -/// Array to hold the hashtab with variables local to each sourced script. -/// Each item holds a variable (nameless) that points to the dict_T. -typedef struct { - ScopeDictDictItem sv_var; - dict_T sv_dict; -} scriptvar_T; - -static garray_T ga_scripts = { 0, 0, sizeof(scriptvar_T *), 4, NULL }; -#define SCRIPT_SV(id) (((scriptvar_T **)ga_scripts.ga_data)[(id) - 1]) +#define SCRIPT_SV(id) (SCRIPT_ITEM(id).sn_vars) #define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab) static int echo_attr = 0; // attributes used for ":echo" @@ -473,7 +465,7 @@ void eval_init(void) } #if defined(EXITFREE) -void eval_clear(void) +static void evalvars_clear(void) { for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { struct vimvar *p = &vimvars[i]; @@ -488,28 +480,29 @@ void eval_clear(void) hash_init(&vimvarht); // garbage_collect() will access it hash_clear(&compat_hashtab); - free_scriptnames(); + // global variables + vars_clear(&globvarht); + + // Script-local variables. Clear all the variables here. + // The scriptvar_T is cleared later in free_scriptnames(), because a + // variable in one script might hold a reference to the whole scope of + // another script. + for (int i = 1; i <= script_items.ga_len; i++) { + vars_clear(&SCRIPT_VARS(i)); + } +} + +void eval_clear(void) +{ + evalvars_clear(); + free_scriptnames(); // must come after evalvars_clear(). # ifdef HAVE_WORKING_LIBINTL free_locales(); # endif - // global variables - vars_clear(&globvarht); - // autoloaded script names ga_clear_strings(&ga_loaded); - // Script-local variables. First clear all the variables and in a second - // loop free the scriptvar_T, because a variable in one script might hold - // a reference to the whole scope of another script. - for (int i = 1; i <= ga_scripts.ga_len; i++) { - vars_clear(&SCRIPT_VARS(i)); - } - for (int i = 1; i <= ga_scripts.ga_len; i++) { - xfree(SCRIPT_SV(i)); - } - ga_clear(&ga_scripts); - // unreferenced lists and dicts (void)garbage_collect(false); @@ -970,7 +963,7 @@ void list_vim_vars(int *first) /// List script-local variables, if there is a script. void list_script_vars(int *first) { - if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { + if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) { list_hashtable_vars(&SCRIPT_VARS(current_sctx.sc_sid), "s:", false, first); } } @@ -4116,7 +4109,7 @@ bool garbage_collect(bool testing) ABORTING(set_ref_in_previous_funccal)(copyID); // script-local variables - for (int i = 1; i <= ga_scripts.ga_len; i++) { + for (int i = 1; i <= script_items.ga_len; i++) { ABORTING(set_ref_in_ht)(&SCRIPT_VARS(i), copyID, NULL); } @@ -7131,7 +7124,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char } else if (*name == 's' // script variable && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) - && current_sctx.sc_sid <= ga_scripts.ga_len) { + && current_sctx.sc_sid <= script_items.ga_len) { // For anonymous scripts without a script item, create one now so script vars can be used if (current_sctx.sc_sid == SID_LUA) { // try to resolve lua filename & line no so it can be shown in lastset messages. @@ -7182,27 +7175,9 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var /// sourcing this script and when executing functions defined in the script. void new_script_vars(scid_T id) { - scriptvar_T *sv; - - ga_grow(&ga_scripts, id - ga_scripts.ga_len); - - // Re-allocating ga_data means that an ht_array pointing to - // ht_smallarray becomes invalid. We can recognize this: ht_mask is - // at its init value. Also reset "v_dict", it's always the same. - for (int i = 1; i <= ga_scripts.ga_len; i++) { - hashtab_T *ht = &SCRIPT_VARS(i); - if (ht->ht_mask == HT_INIT_SIZE - 1) { - ht->ht_array = ht->ht_smallarray; - } - sv = SCRIPT_SV(i); - sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict; - } - - while (ga_scripts.ga_len < id) { - sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T)); - init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE); - ga_scripts.ga_len++; - } + scriptvar_T *sv = xcalloc(1, sizeof(scriptvar_T)); + init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE); + SCRIPT_ITEM(id).sn_vars = sv; } /// Initialize dictionary "dict" as a scope and set variable "dict_var" to -- cgit From 0cbbe27e93e87a5336673e0529b011313c51a123 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 16 Feb 2023 11:00:48 +0800 Subject: vim-patch:8.2.0154: reallocating the list of scripts is inefficient Problem: Reallocating the list of scripts is inefficient. Solution: Instead of using a growarray of scriptitem_T, store pointers and allocate each scriptitem_T separately. Also avoids that the growarray pointers change when sourcing a new script. https://github.com/vim/vim/commit/21b9e9773d64de40994f8762173bdd8befa6acf7 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a09bd72710..92a8ffd646 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -109,7 +109,7 @@ static hashtab_T compat_hashtab; /// Used for checking if local variables or arguments used in a lambda. bool *eval_lavars_used = NULL; -#define SCRIPT_SV(id) (SCRIPT_ITEM(id).sn_vars) +#define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars) #define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab) static int echo_attr = 0; // attributes used for ":echo" @@ -7177,7 +7177,7 @@ void new_script_vars(scid_T id) { scriptvar_T *sv = xcalloc(1, sizeof(scriptvar_T)); init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE); - SCRIPT_ITEM(id).sn_vars = sv; + SCRIPT_ITEM(id)->sn_vars = sv; } /// Initialize dictionary "dict" as a scope and set variable "dict_var" to -- cgit From d34c64e342dfba9248d1055e702d02620a1b31a8 Mon Sep 17 00:00:00 2001 From: Ghjuvan Lacambre Date: Thu, 16 Feb 2023 13:15:02 +0100 Subject: feat: $NVIM_APPNAME #22128 This commit implements the ability to control all of the XDG paths Neovim should use. This is done by setting an environment variable named NVIM_APPNAME. For example, setting $NVIM_APPNAME makes Neovim look for its configuration directory in $XDG_CONFIG_HOME/$NVIM_APPNAME instead of $XDG_CONFIG_HOME/nvim. If NVIM_APPNAME is not set or is an empty string, "nvim" will be used as default. The usecase for this feature is to enable an easy way to switch from configuration to configuration. One might argue that the various $XDG environment variables can already be used for this usecase. However, setting $XDG environment variables also affects tools spawned by Neovim. For example, while setting $XDG_CONFIG_HOME will enable Neovim to use a different configuration directory, it will also prevent Git from finding its "default" configuration. Closes https://github.com/neovim/neovim/issues/21691 --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 92a8ffd646..ccea4f4951 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5368,13 +5368,14 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) return; } const void *iter = NULL; + const char *appname = get_appname(); do { size_t dir_len; const char *dir; iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len); if (dir != NULL && dir_len > 0) { char *dir_with_nvim = xmemdupz(dir, dir_len); - dir_with_nvim = concat_fnames_realloc(dir_with_nvim, "nvim", true); + dir_with_nvim = concat_fnames_realloc(dir_with_nvim, appname, true); tv_list_append_string(list, dir_with_nvim, (ssize_t)strlen(dir_with_nvim)); xfree(dir_with_nvim); } -- cgit From 47bc297d81cf7dab5a28608657ddab3c09f794d0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 24 Feb 2023 12:35:06 +0800 Subject: vim-patch:8.2.4318: various comment and indent mistakes, returning wrong zero (#22385) Problem: Various comment and indent mistakes, returning wrong zero. Solution: Fix the mistakes. Return NULL instead of FAIL. https://github.com/vim/vim/commit/54969f4ef5825205ecde09ea80f4087fc3b68e5d N/A patches for version.c: vim-patch:8.2.3781: the option window script is outdated Problem: The option window script is outdated. Solution: Add several changes. https://github.com/vim/vim/commit/a416861c643b03fe5dec9f3bf8c1c9e054b5a9c7 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ccea4f4951..841588d4c3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2572,7 +2572,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) } /// Handle fourth level expression: -/// + number addition +/// + number addition, concatenation of list or blob /// - number subtraction /// . string concatenation /// .. string concatenation -- cgit From adfa55ba99febaa1eb5ebe1800ec5f94c4b4b664 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 24 Feb 2023 14:36:20 +0800 Subject: vim-patch:8.2.2757: Vim9: blob tests for legacy and Vim9 script are separate Problem: Vim9: blob tests for legacy and Vim9 script are separate. Solution: Add CheckLegacyAndVim9Success(). Make blob index assign work. https://github.com/vim/vim/commit/68452177ca4cda4a9d5f93892e437447cf9404c8 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 841588d4c3..ebd13034d7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1620,17 +1620,9 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool lp->ll_n2 = tv_blob_len(lp->ll_blob) - 1; } - if (lp->ll_n2 - lp->ll_n1 + 1 != tv_blob_len(rettv->vval.v_blob)) { - emsg(_("E972: Blob value does not have the right number of bytes")); + if (tv_blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2, rettv) == FAIL) { return; } - if (lp->ll_empty2) { - lp->ll_n2 = tv_blob_len(lp->ll_blob); - } - - for (int il = (int)lp->ll_n1, ir = 0; il <= (int)lp->ll_n2; il++) { - tv_blob_set(lp->ll_blob, il, tv_blob_get(rettv->vval.v_blob, ir++)); - } } else { bool error = false; const char val = (char)tv_get_number_chk(rettv, &error); -- cgit From c554e989786be30fa306efcd7e504ba7cb97cb3b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 13:30:08 +0800 Subject: vim-patch:8.2.2765: Vim9: not all blob operations work Problem: Vim9: not all blob operations work. Solution: Run more tests also with Vim9 script and :def functions. Fix what doesn't work. https://github.com/vim/vim/commit/0e3ff1919603ee4c4a347fdf761dbdbdeb068015 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ebd13034d7..9fa2eca1da 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1498,21 +1498,14 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const tv_clear(&var1); const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); - if (lp->ll_n1 < 0 || lp->ll_n1 > bloblen - || (lp->ll_range && lp->ll_n1 == bloblen)) { - if (!quiet) { - semsg(_(e_blobidx), (int64_t)lp->ll_n1); - } + if (tv_blob_check_index(bloblen, lp->ll_n1, lp->ll_range, quiet) == FAIL) { tv_clear(&var2); return NULL; } if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (long)tv_get_number(&var2); tv_clear(&var2); - if (lp->ll_n2 < 0 || lp->ll_n2 >= bloblen || lp->ll_n2 < lp->ll_n1) { - if (!quiet) { - semsg(_(e_blobidx), (int64_t)lp->ll_n2); - } + if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { return NULL; } } -- cgit From 1f1227f12b69616484cc0f8b49d555df4a94678b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 13:39:59 +0800 Subject: vim-patch:8.2.2767: compiler warning for unused argument Problem: Compiler warning for unused argument. Solution: Remove the argument. https://github.com/vim/vim/commit/bd6406f15db210b78fa24dece3bd021a7ac085dc Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9fa2eca1da..cce7b9a13c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1498,7 +1498,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const tv_clear(&var1); const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); - if (tv_blob_check_index(bloblen, lp->ll_n1, lp->ll_range, quiet) == FAIL) { + if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { tv_clear(&var2); return NULL; } -- cgit From 99faac86444a7f4c0790d150307c67d98db7c62e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 15:05:40 +0800 Subject: vim-patch:8.2.1890: Vim9: strange error for subtracting from a list Problem: Vim9: strange error for subtracting from a list. Solution: Check getting a number, not a string. (closes vim/vim#7167) https://github.com/vim/vim/commit/081db1a66d17e46ac3b03b7514f11a004a35009a Cherry-pick eval_addblob() and eval_addlist() from patch 8.2.0149. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 66 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 27 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cce7b9a13c..f8110aa545 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2556,6 +2556,39 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) return OK; } +/// Make a copy of blob "tv1" and append blob "tv2". +static void eval_addblob(typval_T *tv1, typval_T *tv2) +{ + const blob_T *const b1 = tv1->vval.v_blob; + const blob_T *const b2 = tv2->vval.v_blob; + blob_T *const b = tv_blob_alloc(); + + for (int i = 0; i < tv_blob_len(b1); i++) { + ga_append(&b->bv_ga, tv_blob_get(b1, i)); + } + for (int i = 0; i < tv_blob_len(b2); i++) { + ga_append(&b->bv_ga, tv_blob_get(b2, i)); + } + + tv_clear(tv1); + tv_blob_set_ret(tv1, b); +} + +/// Make a copy of list "tv1" and append list "tv2". +static int eval_addlist(typval_T *tv1, typval_T *tv2) +{ + typval_T var3; + // Concatenate Lists. + if (tv_list_concat(tv1->vval.v_list, tv2->vval.v_list, &var3) == FAIL) { + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + tv_clear(tv1); + *tv1 = var3; + return OK; +} + /// Handle fourth level expression: /// + number addition, concatenation of list or blob /// - number subtraction @@ -2569,7 +2602,6 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) static int eval5(char **arg, typval_T *rettv, int evaluate) { typval_T var2; - typval_T var3; varnumber_T n1, n2; float_T f1 = 0, f2 = 0; char *p; @@ -2587,7 +2619,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) } if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) - && (op == '.' || rettv->v_type != VAR_FLOAT)) { + && (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) { // For "list + ...", an illegal use of the first operand as // a number cannot be determined before evaluating the 2nd // operand: if this is also a list, all is ok. @@ -2595,7 +2627,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) // we know that the first operand needs to be a string or number // without evaluating the 2nd operand. So check before to avoid // side effects after an error. - if (evaluate && !tv_check_str(rettv)) { + if ((op == '.' && !tv_check_str(rettv)) || (op != '.' && !tv_check_num(rettv))) { tv_clear(rettv); return FAIL; } @@ -2628,32 +2660,12 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; - } else if (op == '+' && rettv->v_type == VAR_BLOB - && var2.v_type == VAR_BLOB) { - const blob_T *const b1 = rettv->vval.v_blob; - const blob_T *const b2 = var2.vval.v_blob; - blob_T *const b = tv_blob_alloc(); - - for (int i = 0; i < tv_blob_len(b1); i++) { - ga_append(&b->bv_ga, tv_blob_get(b1, i)); - } - for (int i = 0; i < tv_blob_len(b2); i++) { - ga_append(&b->bv_ga, tv_blob_get(b2, i)); - } - - tv_clear(rettv); - tv_blob_set_ret(rettv, b); - } else if (op == '+' && rettv->v_type == VAR_LIST - && var2.v_type == VAR_LIST) { - // Concatenate Lists. - if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3) - == FAIL) { - tv_clear(rettv); - tv_clear(&var2); + } else if (op == '+' && rettv->v_type == VAR_BLOB && var2.v_type == VAR_BLOB) { + eval_addblob(rettv, &var2); + } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { + if (eval_addlist(rettv, &var2) == FAIL) { return FAIL; } - tv_clear(rettv); - *rettv = var3; } else { bool error = false; -- cgit From 88b70e7d4650795210222a598cbb233bf7fc98f8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 15:50:35 +0800 Subject: vim-patch:8.2.2782: Vim9: blob operations not fully tested Problem: Vim9: blob operations not fully tested. Solution: Make more blob tests run in Vim9 script. Fix filter(). Make insert() give an error for a null blob, like add(). https://github.com/vim/vim/commit/39211cba723a2cb58a97c7e08826713164b86efc vim-patch:8.2.3284: no error for insert() or remove() changing a locked blob Problem: No error for insert() or remove() changing a locked blob. Solution: Check a blob is not locked before changing it. (Sean Dewar, closes vim/vim#8696) https://github.com/vim/vim/commit/80d7395dcfe96158428da6bb3d28a6eee1244e28 Co-authored-by: Bram Moolenaar Co-authored-by: Sean Dewar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f8110aa545..37029e0dd7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4861,7 +4861,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) { break; } - if (tv.v_type != VAR_NUMBER) { + if (tv.v_type != VAR_NUMBER && tv.v_type != VAR_BOOL) { emsg(_(e_invalblob)); return; } -- cgit From 55d30c459c878224507e90f2a6c1657cdd070668 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 16:06:56 +0800 Subject: vim-patch:8.2.2783: duplicate code for setting byte in blob, blob test may fail Problem: Duplicate code for setting byte in blob, blob test may fail. Solution: Call blob_set_append(). Test sort failure with "N". https://github.com/vim/vim/commit/e8209b91b9974da95899b51dba4058b411d04d5b Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 37029e0dd7..698172442e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1620,18 +1620,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool bool error = false; const char val = (char)tv_get_number_chk(rettv, &error); if (!error) { - garray_T *const gap = &lp->ll_blob->bv_ga; - - // Allow for appending a byte. Setting a byte beyond - // the end is an error otherwise. - if (lp->ll_n1 < gap->ga_len || lp->ll_n1 == gap->ga_len) { - ga_grow(&lp->ll_blob->bv_ga, 1); - tv_blob_set(lp->ll_blob, (int)lp->ll_n1, (uint8_t)val); - if (lp->ll_n1 == gap->ga_len) { - gap->ga_len++; - } - } - // error for invalid range was already given in get_lval() + tv_blob_set_append(lp->ll_blob, (int)lp->ll_n1, (uint8_t)val); } } } else if (op != NULL && *op != '=') { -- cgit From bfa0bc7df0ca527fcec49dbd2055f1bac438663e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 19:04:32 +0800 Subject: vim-patch:9.0.0795: readblob() always reads the whole file Problem: readblob() always reads the whole file. Solution: Add arguments to read part of the file. (Ken Takata, closes vim/vim#11402) https://github.com/vim/vim/commit/11df3aeee548b959ccd4b9a4d3c44651eab6b3ce Remove trailing whitespace in test as done in patch 9.0.1257. Move the help for rand() before range(). Co-authored-by: K.Takata --- src/nvim/eval.c | 50 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 698172442e..c2f84735b2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5859,27 +5859,57 @@ write_blob_error: return false; } -/// Read a blob from a file `fd`. +/// Read blob from file "fd". +/// Caller has allocated a blob in "rettv". /// /// @param[in] fd File to read from. -/// @param[in,out] blob Blob to write to. +/// @param[in,out] rettv Blob to write to. +/// @param[in] offset Read the file from the specified offset. +/// @param[in] size Read the specified size, or -1 if no limit. /// -/// @return true on success, or false on failure. -bool read_blob(FILE *const fd, blob_T *const blob) +/// @return OK on success, or FAIL on failure. +int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg) FUNC_ATTR_NONNULL_ALL { + blob_T *const blob = rettv->vval.v_blob; FileInfo file_info; if (!os_fileinfo_fd(fileno(fd), &file_info)) { - return false; + return FAIL; // can't read the file, error + } + + int whence; + off_T size = size_arg; + if (offset >= 0) { + if (size == -1) { + // size may become negative, checked below + size = (off_T)os_fileinfo_size(&file_info) - offset; + } + whence = SEEK_SET; + } else { + if (size == -1) { + size = -offset; + } + whence = SEEK_END; + } + // Trying to read bytes that aren't there results in an empty blob, not an + // error. + if (size < 0 || size > (off_T)os_fileinfo_size(&file_info)) { + return OK; + } + if (vim_fseek(fd, offset, whence) != 0) { + return OK; } - const int size = (int)os_fileinfo_size(&file_info); - ga_grow(&blob->bv_ga, size); - blob->bv_ga.ga_len = size; + + ga_grow(&blob->bv_ga, (int)size); + blob->bv_ga.ga_len = (int)size; if (fread(blob->bv_ga.ga_data, 1, (size_t)blob->bv_ga.ga_len, fd) < (size_t)blob->bv_ga.ga_len) { - return false; + // An empty blob is returned on error. + tv_blob_free(rettv->vval.v_blob); + rettv->vval.v_blob = NULL; + return FAIL; } - return true; + return OK; } /// Saves a typval_T as a string. -- cgit From 4bd0611d7b07b56fc5a9e121669a313166ba540f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 19:23:28 +0800 Subject: vim-patch:9.0.0803: readblob() cannot read from character device Problem: readblob() cannot read from character device. Solution: Use S_ISCHR() to not check the size. (Ken Takata, closes vim/vim#11407) https://github.com/vim/vim/commit/43625762a9751cc6e6e4d8f54fbc8b82d98fb20d S_ISCHR is always defined in Nvim. Co-authored-by: K.Takata --- src/nvim/eval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c2f84735b2..92fd8a8de3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5893,10 +5893,11 @@ int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg) } // Trying to read bytes that aren't there results in an empty blob, not an // error. - if (size < 0 || size > (off_T)os_fileinfo_size(&file_info)) { + if (size < 0 || (!S_ISCHR(file_info.stat.st_mode) + && size > (off_T)os_fileinfo_size(&file_info))) { return OK; } - if (vim_fseek(fd, offset, whence) != 0) { + if (offset != 0 && vim_fseek(fd, offset, whence) != 0) { return OK; } -- cgit From 7aad75e293e3a01e292308ca2058e35083b83280 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 19:29:59 +0800 Subject: vim-patch:9.0.0810: readblob() returns empty when trying to read too much Problem: readblob() returns empty when trying to read too much. Solution: Return what is available. https://github.com/vim/vim/commit/5b2a3d77d320d76f12b1666938a9d58c2a848205 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 92fd8a8de3..b81384266c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5879,22 +5879,27 @@ int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg) int whence; off_T size = size_arg; + const off_T file_size = (off_T)os_fileinfo_size(&file_info); if (offset >= 0) { - if (size == -1) { + // The size defaults to the whole file. If a size is given it is + // limited to not go past the end of the file. + if (size == -1 || (size > file_size - offset && !S_ISCHR(file_info.stat.st_mode))) { // size may become negative, checked below size = (off_T)os_fileinfo_size(&file_info) - offset; } whence = SEEK_SET; } else { - if (size == -1) { + // limit the offset to not go before the start of the file + if (-offset > file_size && !S_ISCHR(file_info.stat.st_mode)) { + offset = -file_size; + } + // Size defaults to reading until the end of the file. + if (size == -1 || size > -offset) { size = -offset; } whence = SEEK_END; } - // Trying to read bytes that aren't there results in an empty blob, not an - // error. - if (size < 0 || (!S_ISCHR(file_info.stat.st_mode) - && size > (off_T)os_fileinfo_size(&file_info))) { + if (size <= 0) { return OK; } if (offset != 0 && vim_fseek(fd, offset, whence) != 0) { -- cgit From 278aeee3aed753d1084597378e653395bd472c42 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Feb 2023 21:07:56 +0800 Subject: vim-patch:9.0.0430: cannot use repeat() with a blob Problem: Cannot use repeat() with a blob. Solution: Implement blob repeat. (closes vim/vim#11090) https://github.com/vim/vim/commit/375141e1f80dced9be738568a3418f65813f4a2f Co-authored-by: Bakudankun --- src/nvim/eval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b81384266c..0fbf31a8cd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1613,7 +1613,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool lp->ll_n2 = tv_blob_len(lp->ll_blob) - 1; } - if (tv_blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2, rettv) == FAIL) { + if (tv_blob_set_range(lp->ll_blob, (int)lp->ll_n1, (int)lp->ll_n2, rettv) == FAIL) { return; } } else { @@ -4938,6 +4938,8 @@ theend: return retval; } +/// "function()" function +/// "funcref()" function void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) { char *s; -- cgit From 1b3c1f6c06d73e881bfc2a46e5ee3e0b24ba96d8 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 27 Feb 2023 19:37:43 +0100 Subject: refactor(build): graduate HAVE_LOCALE_H feature Merge locale.h into os/lang.h Having a source file with the same name as a system header we use is considered an anti-pattern. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0fbf31a8cd..da2a88346a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -45,7 +45,6 @@ #include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/lib/queue.h" -#include "nvim/locale.h" #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/main.h" @@ -62,6 +61,7 @@ #include "nvim/optionstr.h" #include "nvim/os/fileio.h" #include "nvim/os/fs_defs.h" +#include "nvim/os/lang.h" #include "nvim/os/os.h" #include "nvim/os/shell.h" #include "nvim/os/stdpaths_defs.h" -- cgit From 6cab36e5b7b0d741abe6c5a7c0e20bad30361034 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 4 Mar 2023 13:10:00 +0100 Subject: refactor: replace char_u with char or uint8_t (#22400) Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/eval.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index da2a88346a..67fede1aea 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1185,7 +1185,7 @@ int eval_foldexpr(char *arg, int *cp) // the number. char *s = tv.vval.v_string; if (!ascii_isdigit(*s) && *s != '-') { - *cp = (char_u)(*s++); + *cp = (uint8_t)(*s++); } retval = atol(s); } @@ -1597,7 +1597,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool dictitem_T *di; if (lp->ll_tv == NULL) { - cc = (char_u)(*endp); + cc = (uint8_t)(*endp); *endp = NUL; if (lp->ll_blob != NULL) { if (op != NULL && *op != '=') { @@ -1993,7 +1993,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) } arg = xp->xp_pattern; if (*arg != NUL) { - while ((c = (char_u)(*++arg)) != NUL && (c == ' ' || c == '\t')) {} + while ((c = (uint8_t)(*++arg)) != NUL && (c == ' ' || c == '\t')) {} } } @@ -2602,7 +2602,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) // Repeat computing, until no '+', '-' or '.' is following. for (;;) { - int op = (char_u)(**arg); + int op = (uint8_t)(**arg); if (op != '+' && op != '-' && op != '.') { break; } @@ -2747,7 +2747,7 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) // Repeat computing, until no '*', '/' or '%' is following. for (;;) { - op = (char_u)(**arg); + op = (uint8_t)(**arg); if (op != '*' && op != '/' && op != '%') { break; } @@ -7441,9 +7441,9 @@ void ex_execute(exarg_T *eap) const size_t len = strlen(argstr); ga_grow(&ga, (int)len + 2); if (!GA_EMPTY(&ga)) { - ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; + ((char *)(ga.ga_data))[ga.ga_len++] = ' '; } - memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1); + memcpy((char *)(ga.ga_data) + ga.ga_len, argstr, len + 1); if (eap->cmdidx != CMD_execute) { xfree((void *)argstr); } @@ -7838,7 +7838,7 @@ repeat: // ":~" - path relative to the home directory // ":8" - shortname path - postponed till after while (src[*usedlen] == ':' - && ((c = (char_u)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) { + && ((c = (uint8_t)src[*usedlen + 1]) == '.' || c == '~' || c == '8')) { *usedlen += 2; if (c == '8') { continue; @@ -8084,7 +8084,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char if (zero_width == regmatch.startp[0]) { // avoid getting stuck on a match with an empty string int i = utfc_ptr2len(tail); - memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + memmove((char *)ga.ga_data + ga.ga_len, tail, (size_t)i); ga.ga_len += i; tail += i; continue; @@ -8103,7 +8103,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char // copy the text up to where the match is int i = (int)(regmatch.startp[0] - tail); - memmove((char_u *)ga.ga_data + ga.ga_len, tail, (size_t)i); + memmove((char *)ga.ga_data + ga.ga_len, tail, (size_t)i); // add the substituted text (void)vim_regsub(®match, sub, expr, (char *)ga.ga_data + ga.ga_len + i, sublen, -- cgit From 419819b6245e120aba8897e3ddea711b2cd0246c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 5 Mar 2023 09:18:42 +0800 Subject: vim-patch:9.0.1380: CTRL-X on 2**64 subtracts two (#22530) Problem: CTRL-X on 2**64 subtracts two. (James McCoy) Solution: Correct computation for large number. (closes vim/vim#12103) https://github.com/vim/vim/commit/5fb78c3fa5c996c08a65431d698bd2c251eef5c7 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 67fede1aea..e10607772a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3678,7 +3678,7 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s // decimal, hex or octal number int len; varnumber_T n; - vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true); + vim_str2nr(*arg, NULL, &len, STR2NR_ALL, &n, NULL, 0, true, NULL); if (len == 0) { if (evaluate) { semsg(_(e_invexpr2), *arg); -- cgit From 2882b1543a4f30ee3d7b039a5d51bb994fd6aa1d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 7 Mar 2023 11:04:36 +0800 Subject: vim-patch:8.2.3969: value of MAXCOL not available in Vim script Problem: Value of MAXCOL not available in Vim script. Solution: Add v:maxcol. (Naohiro Ono, closes vim/vim#9451) https://github.com/vim/vim/commit/56200eed62e59ad831f6564dcafe346e6f97ac20 The variable is always 2147483647, but introducing it makes functions easier to document. Co-authored-by: naohiro ono --- src/nvim/eval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e10607772a..cbad5d04ff 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -251,6 +251,7 @@ static struct vimvar { VV(VV_ARGV, "argv", VAR_LIST, VV_RO), VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO), // Neovim VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), @@ -451,6 +452,7 @@ void eval_init(void) set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN); set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8); set_vim_var_special(VV_EXITING, kSpecialVarNull); + set_vim_var_nr(VV_MAXCOL, MAXCOL); set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); -- cgit From 3ad845882413944a8d6ed3bd9da68c6b4cee0afb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Mar 2023 13:47:01 +0800 Subject: vim-patch:8.2.1067: expression "!expr->func()" does not work (#22585) Problem: Expression "!expr->func()" does not work. Solution: Apply plus and minus earlier. (closes vim/vim#6348) https://github.com/vim/vim/commit/0b1cd52ff6bf690388f892be686a91721a082e55 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cbad5d04ff..9b42375120 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2906,6 +2906,12 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) case '8': case '9': ret = get_number_tv(arg, rettv, evaluate, want_string); + + // Apply prefixed "-" and "+" now. Matters especially when + // "->" follows. + if (ret == OK && evaluate && end_leader > start_leader) { + ret = eval7_leader(rettv, true, start_leader, &end_leader); + } break; // String constant: "string". @@ -3011,13 +3017,12 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) // Handle following '[', '(' and '.' for expr[expr], expr.name, // expr(expr), expr->name(expr) if (ret == OK) { - ret = handle_subscript((const char **)arg, rettv, evaluate, true, - (char *)start_leader, &end_leader); + ret = handle_subscript((const char **)arg, rettv, evaluate, true); } // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - ret = eval7_leader(rettv, (char *)start_leader, &end_leader); + ret = eval7_leader(rettv, false, start_leader, &end_leader); } recurse--; @@ -3027,9 +3032,11 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) /// Apply the leading "!" and "-" before an eval7 expression to "rettv". /// Adjusts "end_leaderp" until it is at "start_leader". /// +/// @param numeric_only if true only handle "+" and "-". +/// /// @return OK on success, FAIL on failure. -static int eval7_leader(typval_T *const rettv, const char *const start_leader, - const char **const end_leaderp) +static int eval7_leader(typval_T *const rettv, const bool numeric_only, + const char *const start_leader, const char **const end_leaderp) FUNC_ATTR_NONNULL_ALL { const char *end_leader = (char *)(*end_leaderp); @@ -3050,6 +3057,10 @@ static int eval7_leader(typval_T *const rettv, const char *const start_leader, while (end_leader > start_leader) { end_leader--; if (*end_leader == '!') { + if (numeric_only) { + end_leader++; + break; + } if (rettv->v_type == VAR_FLOAT) { f = !(bool)f; } else { @@ -6899,8 +6910,7 @@ int check_luafunc_name(const char *const str, const bool paren) /// @param verbose give error messages /// @param start_leader start of '!' and '-' prefixes /// @param end_leaderp end of '!' and '-' prefixes -int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int verbose, - const char *const start_leader, const char **const end_leaderp) +int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int verbose) { int ret = OK; dict_T *selfdict = NULL; @@ -6944,11 +6954,6 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int tv_dict_unref(selfdict); selfdict = NULL; } else if (**arg == '-') { - // Expression "-1.0->method()" applies the leader "-" before - // applying ->. - if (evaluate && *end_leaderp > start_leader) { - ret = eval7_leader(rettv, (char *)start_leader, end_leaderp); - } if (ret == OK) { if ((*arg)[2] == '{') { // expr->{lambda}() -- cgit From 402c31a82d2961172c6eaf8014762f28c60bd93e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 11 Mar 2023 17:58:05 +0800 Subject: refactor: move ga_loaded to runtime.c (#22626) --- src/nvim/eval.c | 79 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 78 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9b42375120..384e088bcf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -114,9 +114,6 @@ bool *eval_lavars_used = NULL; static int echo_attr = 0; // attributes used for ":echo" -// The names of packages that once were loaded are remembered. -static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL }; - /// Info used by a ":for" loop. typedef struct { int fi_semicolon; // true if ending in '; var]' @@ -503,7 +500,7 @@ void eval_clear(void) # endif // autoloaded script names - ga_clear_strings(&ga_loaded); + free_autoload_scriptnames(); // unreferenced lists and dicts (void)garbage_collect(false); @@ -7530,80 +7527,6 @@ const char *find_option_end(const char **const arg, int *const scope) return p; } -/// Return the autoload script name for a function or variable name -/// Caller must make sure that "name" contains AUTOLOAD_CHAR. -/// -/// @param[in] name Variable/function name. -/// @param[in] name_len Name length. -/// -/// @return [allocated] autoload script name. -char *autoload_name(const char *const name, const size_t name_len) - FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT -{ - // Get the script file name: replace '#' with '/', append ".vim". - char *const scriptname = xmalloc(name_len + sizeof("autoload/.vim")); - memcpy(scriptname, "autoload/", sizeof("autoload/") - 1); - memcpy(scriptname + sizeof("autoload/") - 1, name, name_len); - size_t auchar_idx = 0; - for (size_t i = sizeof("autoload/") - 1; - i - sizeof("autoload/") + 1 < name_len; - i++) { - if (scriptname[i] == AUTOLOAD_CHAR) { - scriptname[i] = '/'; - auchar_idx = i; - } - } - memcpy(scriptname + auchar_idx, ".vim", sizeof(".vim")); - - return scriptname; -} - -/// If name has a package name try autoloading the script for it -/// -/// @param[in] name Variable/function name. -/// @param[in] name_len Name length. -/// @param[in] reload If true, load script again when already loaded. -/// -/// @return true if a package was loaded. -bool script_autoload(const char *const name, const size_t name_len, const bool reload) -{ - // If there is no '#' after name[0] there is no package name. - const char *p = memchr(name, AUTOLOAD_CHAR, name_len); - if (p == NULL || p == name) { - return false; - } - - bool ret = false; - char *tofree = autoload_name(name, name_len); - char *scriptname = tofree; - - // Find the name in the list of previously loaded package names. Skip - // "autoload/", it's always the same. - int i = 0; - for (; i < ga_loaded.ga_len; i++) { - if (strcmp(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) { - break; - } - } - if (!reload && i < ga_loaded.ga_len) { - ret = false; // Was loaded already. - } else { - // Remember the name if it wasn't loaded already. - if (i == ga_loaded.ga_len) { - GA_APPEND(char *, &ga_loaded, scriptname); - tofree = NULL; - } - - // Try loading the package from $VIMRUNTIME/autoload/.vim - if (source_runtime(scriptname, 0) == OK) { - ret = true; - } - } - - xfree(tofree); - return ret; -} - static var_flavour_T var_flavour(char *varname) FUNC_ATTR_PURE { -- cgit From 714f6bf249fc6a9e6e389fafebdab3719bc06e71 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 12 Mar 2023 08:02:58 +0800 Subject: vim-patch:9.0.1401: condition is always true (#22638) Problem: Condition is always true. Solution: Remove the condition. (closes vim/vim#12139) https://github.com/vim/vim/commit/c481ad38f05c9f759ca7fd01a54c78acad794e85 --- src/nvim/eval.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 384e088bcf..14f2379504 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6951,14 +6951,12 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int tv_dict_unref(selfdict); selfdict = NULL; } else if (**arg == '-') { - if (ret == OK) { - if ((*arg)[2] == '{') { - // expr->{lambda}() - ret = eval_lambda((char **)arg, rettv, evaluate, verbose); - } else { - // expr->name() - ret = eval_method((char **)arg, rettv, evaluate, verbose); - } + if ((*arg)[2] == '{') { + // expr->{lambda}() + ret = eval_lambda((char **)arg, rettv, evaluate, verbose); + } else { + // expr->name() + ret = eval_method((char **)arg, rettv, evaluate, verbose); } } else { // **arg == '[' || **arg == '.' tv_dict_unref(selfdict); -- cgit From e3dab4b32609c63adfbb6bb425a4b19c1ff95cde Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Sun, 26 Mar 2023 01:22:14 +0100 Subject: fix: snprintf buffer overflow detected by -D_FORTIFY_SOURCE=3 (#22780) Problem: Wrong buffer size argument passed to snprintf() in set_cmdarg(): Thread no. 1 (24 frames) #8 snprintf at /usr/include/bits/stdio2.h:54 #9 set_cmdarg at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/eval.c:7044 #10 apply_autocmds_group at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/autocmd.c:1843 #11 apply_autocmds_exarg at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/autocmd.c:1549 #12 readfile at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/fileio.c:617 #13 buf_reload at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/fileio.c:5038 #14 buf_check_timestamp at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/fileio.c:4952 #15 check_timestamps at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/fileio.c:4678 #16 ex_checktime at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_cmds2.c:765 #17 execute_cmd0 at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:1620 #18 do_one_cmd at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:2275 #19 do_cmdline at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:584 #20 ex_execute at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/eval.c:7727 #21 execute_cmd0 at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:1620 #22 do_one_cmd at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:2275 #23 do_cmdline at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:584 #24 do_ucmd at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/usercmd.c:1661 #25 execute_cmd0 at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:1612 #26 do_one_cmd at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:2275 #27 do_cmdline at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/ex_docmd.c:584 #28 nv_colon at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/normal.c:4058 #29 normal_execute at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/normal.c:1172 #30 state_enter at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/state.c:88 #31 normal_enter at /usr/src/debug/neovim-0.8.2-2.fc38.x86_64/src/nvim/normal.c:471 Solution: Subtract the offset from the buffer size. Signed-off-by: Andreas Schneider --- src/nvim/eval.c | 81 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 21 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 14f2379504..fc9ddb75ef 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6771,20 +6771,18 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) { char *oldval = vimvars[VV_CMDARG].vv_str; if (eap == NULL) { - xfree(oldval); - vimvars[VV_CMDARG].vv_str = oldarg; - return NULL; + goto error; } size_t len = 0; if (eap->force_bin == FORCE_BIN) { - len = 6; + len += 6; // " ++bin" } else if (eap->force_bin == FORCE_NOBIN) { - len = 8; + len += 8; // " ++nobin" } if (eap->read_edit) { - len += 7; + len += 7; // " ++edit" } if (eap->force_ff != 0) { @@ -6797,48 +6795,89 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) len += 7 + 4; // " ++bad=" + "keep" or "drop" } if (eap->mkdir_p != 0) { - len += 4; + len += 4; // " ++p" } const size_t newval_len = len + 1; char *newval = xmalloc(newval_len); + size_t xlen = 0; + int rc = 0; if (eap->force_bin == FORCE_BIN) { - snprintf(newval, newval_len, " ++bin"); + rc = snprintf(newval, newval_len, " ++bin"); } else if (eap->force_bin == FORCE_NOBIN) { - snprintf(newval, newval_len, " ++nobin"); + rc = snprintf(newval, newval_len, " ++nobin"); } else { *newval = NUL; } + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; if (eap->read_edit) { - STRCAT(newval, " ++edit"); + rc = snprintf(newval + xlen, newval_len - xlen, " ++edit"); + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; } if (eap->force_ff != 0) { - snprintf(newval + strlen(newval), newval_len, " ++ff=%s", - eap->force_ff == 'u' ? "unix" : - eap->force_ff == 'd' ? "dos" : "mac"); + rc = snprintf(newval + xlen, + newval_len - xlen, + " ++ff=%s", + eap->force_ff == 'u' ? "unix" + : eap->force_ff == 'd' ? "dos" : "mac"); + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; } if (eap->force_enc != 0) { - snprintf(newval + strlen(newval), newval_len, " ++enc=%s", - eap->cmd + eap->force_enc); + rc = snprintf(newval + (xlen), newval_len - xlen, " ++enc=%s", eap->cmd + eap->force_enc); + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; } + if (eap->bad_char == BAD_KEEP) { - STRCPY(newval + strlen(newval), " ++bad=keep"); + rc = snprintf(newval + xlen, newval_len - xlen, " ++bad=keep"); + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; } else if (eap->bad_char == BAD_DROP) { - STRCPY(newval + strlen(newval), " ++bad=drop"); + rc = snprintf(newval + xlen, newval_len - xlen, " ++bad=drop"); + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; } else if (eap->bad_char != 0) { - snprintf(newval + strlen(newval), newval_len, " ++bad=%c", - eap->bad_char); + rc = snprintf(newval + xlen, newval_len - xlen, " ++bad=%c", eap->bad_char); + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; } - if (eap->mkdir_p) { - snprintf(newval, newval_len, " ++p"); + if (eap->mkdir_p != 0) { + rc = snprintf(newval + xlen, newval_len - xlen, " ++p"); + if (rc < 0) { + goto error; + } + xlen += (size_t)rc; } + assert(xlen <= newval_len); vimvars[VV_CMDARG].vv_str = newval; return oldval; + +error: + xfree(oldval); + vimvars[VV_CMDARG].vv_str = oldarg; + return NULL; } /// Check if variable "name[len]" is a local variable or an argument. -- cgit From d5f6176e6dc4b4e12fc5061ca6e87f4af533e46a Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Sat, 1 Apr 2023 02:49:51 +0200 Subject: refactor: add const and remove unnecessary casts (#22841) --- src/nvim/eval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fc9ddb75ef..edaa2bb809 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -596,7 +596,7 @@ int var_redir_start(char *name, int append) /// :redir => foo /// :let foo /// :redir END -void var_redir_str(char *value, int value_len) +void var_redir_str(const char *value, int value_len) { if (redir_lval == NULL) { return; @@ -2147,10 +2147,10 @@ int pattern_match(const char *pat, const char *text, bool ic) // avoid 'l' flag in 'cpoptions' char *save_cpo = p_cpo; p_cpo = empty_option; - regmatch.regprog = vim_regcomp((char *)pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = ic; - matches = vim_regexec_nl(®match, (char *)text, (colnr_T)0); + matches = vim_regexec_nl(®match, text, (colnr_T)0); vim_regfree(regmatch.regprog); } p_cpo = save_cpo; -- cgit From 371823d407d7d7519735131bcad4670c62a731a7 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Wed, 5 Apr 2023 21:13:53 +0200 Subject: refactor: make error message definitions const message.c functions now take const char * as a format. Error message definitions can be made const. --- src/nvim/eval.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index edaa2bb809..16a2dfa39b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -86,15 +86,15 @@ #define DICT_MAXNEST 100 // maximum nesting of lists and dicts -static char *e_missbrac = N_("E111: Missing ']'"); -static char *e_list_end = N_("E697: Missing end of List ']': %s"); -static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_nowhitespace +static const char *e_missbrac = N_("E111: Missing ']'"); +static const char *e_list_end = N_("E697: Missing end of List ']': %s"); +static const char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); +static const char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); -static char *e_write2 = N_("E80: Error while writing: %s"); -static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); -static char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); -static char e_dot_can_only_be_used_on_dictionary_str[] +static const char *e_write2 = N_("E80: Error while writing: %s"); +static const char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); +static const char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); +static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); static char * const namespace_char = "abglstvw"; -- cgit From 9408f2dcf7cade2631688300e9b58eed6bc5219a Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 19:40:57 +0200 Subject: refactor: remove redundant const char * casts --- src/nvim/eval.c | 51 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 16a2dfa39b..f875e4f06f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1234,9 +1234,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (skip) { // When skipping just find the end of the name. - lp->ll_name = (const char *)name; - return (char *)find_name_end(name, NULL, NULL, - FNE_INCL_BR | fne_flags); + lp->ll_name = name; + return (char *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); } // Find the end of the name. @@ -1269,8 +1268,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_name_len = strlen(lp->ll_name); } } else { - lp->ll_name = (const char *)name; - lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); + lp->ll_name = name; + lp->ll_name_len = (size_t)(p - lp->ll_name); } // Without [idx] or .key we are done. @@ -1417,7 +1416,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const } lp->ll_list = NULL; lp->ll_dict = lp->ll_tv->vval.v_dict; - lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len); + lp->ll_di = tv_dict_find(lp->ll_dict, key, len); // When assigning to a scope dictionary check that a function and // variable name is valid (only variable name unless it is l: or @@ -1434,8 +1433,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const } wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE && tv_is_func(*rettv) - && var_wrong_func_name((const char *)key, lp->ll_di == NULL)) - || !valid_varname((const char *)key)); + && var_wrong_func_name(key, lp->ll_di == NULL)) + || !valid_varname(key)); if (len != -1) { key[len] = prevval; } @@ -1728,7 +1727,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool } // Need to add an item to the Dictionary. - di = tv_dict_item_alloc((const char *)lp->ll_newkey); + di = tv_dict_item_alloc(lp->ll_newkey); if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { xfree(di); return; @@ -2172,13 +2171,13 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ int len = name_len; if (!evaluate) { - check_vars((const char *)s, (size_t)len); + check_vars(s, (size_t)len); } // If "s" is the name of a variable of type VAR_FUNC // use its contents. partial_T *partial; - s = deref_func_name((const char *)s, &len, &partial, !evaluate); + s = deref_func_name(s, &len, &partial, !evaluate); // Need to make a copy, in case evaluating the arguments makes // the name invalid. @@ -3000,9 +2999,9 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) if (**arg == '(') { // recursive! ret = eval_func(arg, s, len, rettv, evaluate, NULL); } else if (evaluate) { - ret = get_var_tv((const char *)s, len, rettv, NULL, true, false); + ret = get_var_tv(s, len, rettv, NULL, true, false); } else { - check_vars((const char *)s, (size_t)len); + check_vars(s, (size_t)len); ret = OK; } } @@ -3199,7 +3198,7 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu char *lua_funcname = NULL; if (strncmp(name, "v:lua.", 6) == 0) { lua_funcname = name + 6; - *arg = (char *)skip_luafunc_name((const char *)lua_funcname); + *arg = (char *)skip_luafunc_name(lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later len = (int)(*arg - lua_funcname); } else { @@ -3520,8 +3519,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) } } - dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, - (const char *)key, len); + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, len); if (item == NULL && verbose) { semsg(_(e_dictkey), key); @@ -4635,14 +4633,14 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal) goto failret; } if (evaluate) { - dictitem_T *item = tv_dict_find(d, (const char *)key, -1); + dictitem_T *item = tv_dict_find(d, key, -1); if (item != NULL) { semsg(_("E721: Duplicate key in Dictionary: \"%s\""), key); tv_clear(&tvkey); tv_clear(&tv); goto failret; } - item = tv_dict_item_alloc((const char *)key); + item = tv_dict_item_alloc(key); item->di_tv = tv; item->di_tv.v_lock = VAR_UNLOCKED; if (tv_dict_add(d, item) == FAIL) { @@ -4755,8 +4753,7 @@ void assert_error(garray_T *gap) // Make sure v:errors is a list. set_vim_var_list(VV_ERRORS, tv_list_alloc(1)); } - tv_list_append_string(vimvars[VV_ERRORS].vv_list, - (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); + tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len); } /// Implementation of map() and filter(). @@ -4983,14 +4980,12 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) } if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) || (is_funcref && trans_name == NULL)) { - semsg(_(e_invarg2), (use_string - ? tv_get_string(&argvars[0]) - : (const char *)s)); + semsg(_(e_invarg2), (use_string ? tv_get_string(&argvars[0]) : s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL && (is_funcref ? find_func(trans_name) == NULL - : !translated_function_exists((const char *)trans_name))) { + : !translated_function_exists(trans_name))) { semsg(_("E700: Unknown function: %s"), s); } else { int dict_idx = 0; @@ -6338,7 +6333,7 @@ int get_id_len(const char **const arg) } len = (int)(p - *arg); - *arg = (const char *)skipwhite(p); + *arg = skipwhite(p); return len; } @@ -6376,7 +6371,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo if (expr_start != NULL) { if (!evaluate) { len += (int)(p - *arg); - *arg = (const char *)skipwhite(p); + *arg = skipwhite(p); return len; } @@ -6387,7 +6382,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo return -1; } *alias = temp_string; - *arg = (const char *)skipwhite(p); + *arg = skipwhite(p); return (int)strlen(temp_string); } @@ -8165,7 +8160,7 @@ void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) } list_T *args = tv_list_alloc(1); - tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); + tv_list_append_string(args, argvars[0].vval.v_string, -1); *rettv = eval_call_provider(name, "eval", args, false); } -- cgit From 04933b1ea968f958d2541dd65fd33ebb503caac3 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:08:16 +0200 Subject: refactor: remove redundant casts --- src/nvim/eval.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f875e4f06f..f0b0e88b50 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -396,11 +396,11 @@ void eval_init(void) // add to v: scope dict, unless the value is not always available if (p->vv_type != VAR_UNKNOWN) { - hash_add(&vimvarht, (char *)p->vv_di.di_key); + hash_add(&vimvarht, p->vv_di.di_key); } if (p->vv_flags & VV_COMPAT) { // add to compat scope dict - hash_add(&compat_hashtab, (char *)p->vv_di.di_key); + hash_add(&compat_hashtab, p->vv_di.di_key); } } vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; @@ -3035,7 +3035,7 @@ static int eval7_leader(typval_T *const rettv, const bool numeric_only, const char *const start_leader, const char **const end_leaderp) FUNC_ATTR_NONNULL_ALL { - const char *end_leader = (char *)(*end_leaderp); + const char *end_leader = *end_leaderp; int ret = OK; bool error = false; varnumber_T val = 0; @@ -3263,7 +3263,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) bool empty2 = false; ptrdiff_t len = -1; int range = false; - char *key = NULL; + const char *key = NULL; switch (rettv->v_type) { case VAR_FUNC: @@ -3512,7 +3512,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) } if (len == -1) { - key = (char *)tv_get_string_chk(&var1); + key = tv_get_string_chk(&var1); if (key == NULL) { tv_clear(&var1); return FAIL; @@ -7607,7 +7607,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, typv } else { hi = (const hashitem_T *)iter; } - *name = (char *)TV_DICT_HI2DI(hi)->di_key; + *name = TV_DICT_HI2DI(hi)->di_key; tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); while ((size_t)(++hi - hifirst) < hinum) { if (!HASHITEM_EMPTY(hi) && (var_flavour(hi->hi_key) & flavour)) { @@ -7944,7 +7944,7 @@ repeat: // "path/to/this.file.ext" :r:r:r // ^ ^------------- tail // +--------------------- *fnamep - if (s > MAX(tail, (char *)(*fnamep))) { + if (s > MAX(tail, *fnamep)) { *fnamelen = (size_t)(s - *fnamep); } } -- cgit From 2d78e656b715119ca11d131a1a932f22f1b4ad36 Mon Sep 17 00:00:00 2001 From: ii14 <59243201+ii14@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:43:00 +0200 Subject: refactor: remove redundant casts --- src/nvim/eval.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f0b0e88b50..ff33fdeccb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -984,7 +984,7 @@ void prepare_vimvar(int idx, typval_T *save_tv) { *save_tv = vimvars[idx].vv_tv; if (vimvars[idx].vv_type == VAR_UNKNOWN) { - hash_add(&vimvarht, (char *)vimvars[idx].vv_di.di_key); + hash_add(&vimvarht, vimvars[idx].vv_di.di_key); } } @@ -997,7 +997,7 @@ void restore_vimvar(int idx, typval_T *save_tv) return; } - hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key); + hashitem_T *hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key); if (HASHITEM_EMPTY(hi)) { internal_error("restore_vimvar()"); } else { @@ -1145,7 +1145,7 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv) typval_T rettv; // All arguments are passed as strings, no conversion to number. - if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) { + if (call_vim_function(func, argc, argv, &rettv) == FAIL) { return NULL; } @@ -1763,7 +1763,7 @@ notify: } else { dictitem_T *di_ = lp->ll_di; assert(di_->di_key != NULL); - tv_dict_watcher_notify(dict, (char *)di_->di_key, lp->ll_tv, &oldtv); + tv_dict_watcher_notify(dict, di_->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } } @@ -1785,7 +1785,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) *errp = true; // Default: there is an error. - expr = skip_var_list((char *)arg, &fi->fi_varcount, &fi->fi_semicolon); + expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); if (expr == NULL) { return fi; } @@ -4828,7 +4828,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) break; } - vimvars[VV_KEY].vv_str = xstrdup((char *)di->di_key); + vimvars[VV_KEY].vv_str = xstrdup(di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); tv_clear(&vimvars[VV_KEY].vv_tv); if (r == FAIL || did_emsg) { @@ -7631,10 +7631,10 @@ int store_session_globals(FILE *fd) TV_DICT_ITER(&globvardict, this_var, { if ((this_var->di_tv.v_type == VAR_NUMBER || this_var->di_tv.v_type == VAR_STRING) - && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) { + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { // Escape special characters with a backslash. Turn a LF and // CR into \n and \r. - char *const p = (char *)vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r"); + char *const p = vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r"); for (char *t = p; *t != NUL; t++) { if (*t == '\n') { *t = 'n'; @@ -7653,7 +7653,7 @@ int store_session_globals(FILE *fd) } xfree(p); } else if (this_var->di_tv.v_type == VAR_FLOAT - && var_flavour((char *)this_var->di_key) == VAR_FLAVOUR_SESSION) { + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { float_T f = this_var->di_tv.vval.v_float; int sign = ' '; -- cgit From 8f69c5ed450337b9f77c50f9ee0d3eb32f649ca6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 07:11:59 +0800 Subject: vim-patch:8.2.{0695,0725,0734,0753,0818,0819,0822} (#23075) vim-patch:8.2.0695: Vim9: cannot define a function inside a function Problem: Vim9: cannot define a function inside a function. Solution: Initial support for :def inside :def. https://github.com/vim/vim/commit/04b12697838b232b8b17c553ccc74cf1f1bdb81c vim-patch:8.2.0725: Vim9: cannot call a function declared later in Vim9 script Problem: Vim9: cannot call a function declared later in Vim9 script. Solution: Make two passes through the script file. https://github.com/vim/vim/commit/09689a02840be40fa7bb10b1921fb5bc5b2908f1 vim-patch:8.2.0734: Vim9: leaking memory when using :finish Problem: Vim9: leaking memory when using :finish. Solution: Do not check for next line in third pass. https://github.com/vim/vim/commit/04816717dfea6e2469ff4c9d40f68b59aaf03724 vim-patch:8.2.0753: Vim9: expressions are evaluated in the discovery phase Problem: Vim9: expressions are evaluated in the discovery phase. Solution: Bail out if an expression is not a constant. Require a type for declared constants. https://github.com/vim/vim/commit/32e351179eacfc84f64cd5029e221582d400bb38 vim-patch:8.2.0818: Vim9: using a discovery phase doesn't work well Problem: Vim9: using a discovery phase doesn't work well. Solution: Remove the discovery phase, instead compile a function only when it is used. Add :defcompile to compile def functions earlier. https://github.com/vim/vim/commit/822ba24743af9ee1b5e7f656a7a61a38f3638bca vim-patch:8.2.0819: compiler warning for unused variable Problem: Compiler warning for unused variable. Solution: Remove the variable. https://github.com/vim/vim/commit/f40e51a880a95f94dbbbecc9476559506c2cc345 vim-patch:8.2.0822: Vim9: code left over from discovery phase Problem: Vim9: code left over from discovery phase. Solution: Remove the dead code. https://github.com/vim/vim/commit/2eec37926db6d31beb36f162ac00357a30c093c8 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 144 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 65 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ff33fdeccb..796c010f74 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -706,7 +706,7 @@ int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip) if (skip) { emsg_skip++; } - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL) { *error = true; } else { *error = false; @@ -730,7 +730,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; - const int ret = eval1(arg, rettv, evaluate); + const int ret = eval1(arg, rettv, evaluate ? EVAL_EVALUATE : 0); if (ret == FAIL) { // Report the invalid expression unless the expression evaluation has // been cancelled due to an aborting error, an interrupt, or an @@ -832,7 +832,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip if (skip) { emsg_skip++; } - if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) { + if (eval0((char *)arg, &tv, (char **)nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL || skip) { retval = NULL; } else { retval = xstrdup(tv_get_string(&tv)); @@ -853,7 +853,7 @@ int skip_expr(char **pp) typval_T rettv; *pp = skipwhite(*pp); - return eval1(pp, &rettv, false); + return eval1(pp, &rettv, 0); } /// Top level evaluation function, returning a string. @@ -868,7 +868,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert) char *retval; garray_T ga; - if (eval0(arg, &tv, nextcmd, true) == FAIL) { + if (eval0(arg, &tv, nextcmd, EVAL_EVALUATE) == FAIL) { retval = NULL; } else { if (convert && tv.v_type == VAR_LIST) { @@ -929,7 +929,7 @@ varnumber_T eval_to_number(char *expr) emsg_off++; - if (eval1(&p, &rettv, true) == FAIL) { + if (eval1(&p, &rettv, EVAL_EVALUATE) == FAIL) { retval = -1; } else { retval = tv_get_number_chk(&rettv, NULL); @@ -947,7 +947,7 @@ varnumber_T eval_to_number(char *expr) typval_T *eval_expr(char *arg) { typval_T *tv = xmalloc(sizeof(*tv)); - if (eval0(arg, tv, NULL, true) == FAIL) { + if (eval0(arg, tv, NULL, EVAL_EVALUATE) == FAIL) { XFREE_CLEAR(tv); } return tv; @@ -1024,7 +1024,7 @@ list_T *eval_spell_expr(char *badword, char *expr) emsg_off++; } - if (eval1(&p, &rettv, true) == OK) { + if (eval1(&p, &rettv, EVAL_EVALUATE) == OK) { if (rettv.v_type != VAR_LIST) { tv_clear(&rettv); } else { @@ -1171,7 +1171,7 @@ int eval_foldexpr(char *arg, int *cp) } textlock++; *cp = NUL; - if (eval0(arg, &tv, NULL, true) == FAIL) { + if (eval0(arg, &tv, NULL, EVAL_EVALUATE) == FAIL) { retval = 0; } else { // If the result is a number, just return the number. @@ -1346,7 +1346,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const empty1 = true; } else { empty1 = false; - if (eval1(&p, &var1, true) == FAIL) { // Recursive! + if (eval1(&p, &var1, EVAL_EVALUATE) == FAIL) { // Recursive! return NULL; } if (!tv_check_str(&var1)) { @@ -1380,7 +1380,8 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_empty2 = true; } else { lp->ll_empty2 = false; - if (eval1(&p, &var2, true) == FAIL) { // Recursive! + // Recursive! + if (eval1(&p, &var2, EVAL_EVALUATE) == FAIL) { tv_clear(&var1); return NULL; } @@ -1799,7 +1800,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) if (skip) { emsg_skip++; } - if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { + if (eval0(skipwhite(expr + 2), &tv, nextcmdp, skip ? 0 : EVAL_EVALUATE) == OK) { *errp = false; if (!skip) { if (tv.v_type == VAR_LIST) { @@ -2164,9 +2165,10 @@ int pattern_match(const char *pat, const char *text, bool ic) /// /// @return OK or FAIL. static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv, - const bool evaluate, typval_T *const basetv) + const int flags, typval_T *const basetv) FUNC_ATTR_NONNULL_ARG(1, 2, 4) { + const bool evaluate = flags & EVAL_EVALUATE; char *s = name; int len = name_len; @@ -2223,8 +2225,10 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ /// Put the result in "rettv" when returning OK and "evaluate" is true. /// Note: "rettv.v_lock" is not set. /// +/// @param flags has EVAL_EVALUATE and similar flags. +/// /// @return OK or FAIL. -int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) +int eval0(char *arg, typval_T *rettv, char **nextcmd, const int flags) { int ret; char *p; @@ -2233,7 +2237,7 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) bool end_error = false; p = skipwhite(arg); - ret = eval1(&p, rettv, evaluate); + ret = eval1(&p, rettv, flags); if (ret != FAIL) { end_error = !ends_excmd(*p); @@ -2246,7 +2250,8 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) // been cancelled due to an aborting error, an interrupt, or an // exception, or we already gave a more specific error. // Also check called_emsg for when using assert_fails(). - if (!aborting() && did_emsg == did_emsg_before + if (!aborting() + && did_emsg == did_emsg_before && called_emsg == called_emsg_before) { if (end_error) { semsg(_(e_trailing_arg), p); @@ -2272,18 +2277,20 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate) /// Note: "rettv.v_lock" is not set. /// /// @return OK or FAIL. -int eval1(char **arg, typval_T *rettv, int evaluate) +int eval1(char **arg, typval_T *rettv, const int flags) { typval_T var2; // Get the first variable. - if (eval2(arg, rettv, evaluate) == FAIL) { + if (eval2(arg, rettv, flags) == FAIL) { return FAIL; } if ((*arg)[0] == '?') { + const bool evaluate = flags & EVAL_EVALUATE; + bool result = false; - if (evaluate) { + if (flags & EVAL_EVALUATE) { bool error = false; if (tv_get_number_chk(rettv, &error) != 0) { @@ -2295,9 +2302,9 @@ int eval1(char **arg, typval_T *rettv, int evaluate) } } - // Get the second variable. + // Get the second variable. Recursive! *arg = skipwhite(*arg + 1); - if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive! + if (eval1(arg, rettv, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { return FAIL; } @@ -2310,9 +2317,9 @@ int eval1(char **arg, typval_T *rettv, int evaluate) return FAIL; } - // Get the third variable. + // Get the third variable. Recursive! *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive! + if (eval1(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { if (evaluate && result) { tv_clear(rettv); } @@ -2333,13 +2340,13 @@ int eval1(char **arg, typval_T *rettv, int evaluate) /// "arg" is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval2(char **arg, typval_T *rettv, int evaluate) +static int eval2(char **arg, typval_T *rettv, const int flags) { typval_T var2; bool error = false; // Get the first variable. - if (eval3(arg, rettv, evaluate) == FAIL) { + if (eval3(arg, rettv, flags) == FAIL) { return FAIL; } @@ -2347,6 +2354,8 @@ static int eval2(char **arg, typval_T *rettv, int evaluate) bool first = true; bool result = false; while ((*arg)[0] == '|' && (*arg)[1] == '|') { + const bool evaluate = flags & EVAL_EVALUATE; + if (evaluate && first) { if (tv_get_number_chk(rettv, &error) != 0) { result = true; @@ -2360,7 +2369,7 @@ static int eval2(char **arg, typval_T *rettv, int evaluate) // Get the second variable. *arg = skipwhite(*arg + 2); - if (eval3(arg, &var2, evaluate && !result) == FAIL) { + if (eval3(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { return FAIL; } @@ -2390,13 +2399,13 @@ static int eval2(char **arg, typval_T *rettv, int evaluate) /// `arg` is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval3(char **arg, typval_T *rettv, int evaluate) +static int eval3(char **arg, typval_T *rettv, const int flags) { typval_T var2; bool error = false; // Get the first variable. - if (eval4(arg, rettv, evaluate) == FAIL) { + if (eval4(arg, rettv, flags) == FAIL) { return FAIL; } @@ -2404,6 +2413,8 @@ static int eval3(char **arg, typval_T *rettv, int evaluate) bool first = true; bool result = true; while ((*arg)[0] == '&' && (*arg)[1] == '&') { + const bool evaluate = flags & EVAL_EVALUATE; + if (evaluate && first) { if (tv_get_number_chk(rettv, &error) == 0) { result = false; @@ -2417,7 +2428,7 @@ static int eval3(char **arg, typval_T *rettv, int evaluate) // Get the second variable. *arg = skipwhite(*arg + 2); - if (eval4(arg, &var2, evaluate && result) == FAIL) { + if (eval4(arg, &var2, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { return FAIL; } @@ -2456,7 +2467,7 @@ static int eval3(char **arg, typval_T *rettv, int evaluate) /// "arg" is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval4(char **arg, typval_T *rettv, int evaluate) +static int eval4(char **arg, typval_T *rettv, const int flags) { typval_T var2; char *p; @@ -2464,7 +2475,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) int len = 2; // Get the first variable. - if (eval5(arg, rettv, evaluate) == FAIL) { + if (eval5(arg, rettv, flags) == FAIL) { return FAIL; } @@ -2528,11 +2539,11 @@ static int eval4(char **arg, typval_T *rettv, int evaluate) // Get the second variable. *arg = skipwhite(p + len); - if (eval5(arg, &var2, evaluate) == FAIL) { + if (eval5(arg, &var2, flags) == FAIL) { tv_clear(rettv); return FAIL; } - if (evaluate) { + if (flags & EVAL_EVALUATE) { const int ret = typval_compare(rettv, &var2, type, ic); tv_clear(&var2); @@ -2586,7 +2597,7 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2) /// `arg` is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval5(char **arg, typval_T *rettv, int evaluate) +static int eval5(char **arg, typval_T *rettv, const int flags) { typval_T var2; varnumber_T n1, n2; @@ -2594,7 +2605,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) char *p; // Get the first variable. - if (eval6(arg, rettv, evaluate, false) == FAIL) { + if (eval6(arg, rettv, flags, false) == FAIL) { return FAIL; } @@ -2606,7 +2617,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) } if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) - && (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) { + && (op == '.' || rettv->v_type != VAR_FLOAT) && (flags & EVAL_EVALUATE)) { // For "list + ...", an illegal use of the first operand as // a number cannot be determined before evaluating the 2nd // operand: if this is also a list, all is ok. @@ -2625,12 +2636,12 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) (*arg)++; } *arg = skipwhite(*arg + 1); - if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { + if (eval6(arg, &var2, flags, op == '.') == FAIL) { tv_clear(rettv); return FAIL; } - if (evaluate) { + if (flags & EVAL_EVALUATE) { // Compute the result. if (op == '.') { char buf1[NUMBUFLEN]; @@ -2724,11 +2735,10 @@ static int eval5(char **arg, typval_T *rettv, int evaluate) /// expression. Is advanced to the next non-whitespace /// character after the recognized expression. /// @param[out] rettv Location where result is saved. -/// @param[in] evaluate If not true, rettv is not populated. /// @param[in] want_string True if "." is string_concatenation, otherwise /// float /// @return OK or FAIL. -static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) +static int eval6(char **arg, typval_T *rettv, const int flags, bool want_string) FUNC_ATTR_NO_SANITIZE_UNDEFINED { typval_T var2; @@ -2739,7 +2749,7 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) bool error = false; // Get the first variable. - if (eval7(arg, rettv, evaluate, want_string) == FAIL) { + if (eval7(arg, rettv, flags, want_string) == FAIL) { return FAIL; } @@ -2750,7 +2760,7 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) break; } - if (evaluate) { + if (flags & EVAL_EVALUATE) { if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; use_float = true; @@ -2768,11 +2778,11 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) // Get the second variable. *arg = skipwhite(*arg + 1); - if (eval7(arg, &var2, evaluate, false) == FAIL) { + if (eval7(arg, &var2, flags, false) == FAIL) { return FAIL; } - if (evaluate) { + if (flags & EVAL_EVALUATE) { if (var2.v_type == VAR_FLOAT) { if (!use_float) { f1 = (float_T)n1; @@ -2859,8 +2869,9 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string) /// @param want_string after "." operator /// /// @return OK or FAIL. -static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) +static int eval7(char **arg, typval_T *rettv, const int flags, bool want_string) { + const bool evaluate = flags & EVAL_EVALUATE; int ret = OK; static int recurse = 0; @@ -2922,14 +2933,14 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) // List: [expr, expr] case '[': - ret = get_list_tv(arg, rettv, evaluate); + ret = get_list_tv(arg, rettv, flags); break; // Dictionary: #{key: val, key: val} case '#': if ((*arg)[1] == '{') { (*arg)++; - ret = eval_dict(arg, rettv, evaluate, true); + ret = eval_dict(arg, rettv, flags, true); } else { ret = NOTDONE; } @@ -2940,7 +2951,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) case '{': ret = get_lambda_tv(arg, rettv, evaluate); if (ret == NOTDONE) { - ret = eval_dict(arg, rettv, evaluate, false); + ret = eval_dict(arg, rettv, flags, false); } break; @@ -2968,7 +2979,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) // nested expression: (expression). case '(': *arg = skipwhite(*arg + 1); - ret = eval1(arg, rettv, evaluate); // recursive! + ret = eval1(arg, rettv, flags); // recursive! if (**arg == ')') { (*arg)++; } else if (ret == OK) { @@ -2997,7 +3008,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) ret = FAIL; } else { if (**arg == '(') { // recursive! - ret = eval_func(arg, s, len, rettv, evaluate, NULL); + ret = eval_func(arg, s, len, rettv, flags, NULL); } else if (evaluate) { ret = get_var_tv(s, len, rettv, NULL, true, false); } else { @@ -3013,7 +3024,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string) // Handle following '[', '(' and '.' for expr[expr], expr.name, // expr(expr), expr->name(expr) if (ret == OK) { - ret = handle_subscript((const char **)arg, rettv, evaluate, true); + ret = handle_subscript((const char **)arg, rettv, flags, true); } // Apply logical NOT and unary '-', from right to left, ignore '+'. @@ -3238,7 +3249,7 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu } ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); } else { - ret = eval_func(arg, name, len, rettv, evaluate, &base); + ret = eval_func(arg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base); } } @@ -3257,8 +3268,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu /// @param verbose give error messages /// /// @returns FAIL or OK. "*arg" is advanced to after the ']'. -static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) +static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose) { + const bool evaluate = flags & EVAL_EVALUATE; bool empty1 = false; bool empty2 = false; ptrdiff_t len = -1; @@ -3313,7 +3325,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) *arg = skipwhite(*arg + 1); if (**arg == ':') { empty1 = true; - } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! + } else if (eval1(arg, &var1, flags) == FAIL) { // Recursive! return FAIL; } else if (evaluate && !tv_check_str(&var1)) { // Not a number or string. @@ -3327,7 +3339,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose) *arg = skipwhite(*arg + 1); if (**arg == ']') { empty2 = true; - } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive! + } else if (eval1(arg, &var2, flags) == FAIL) { // Recursive! if (!empty1) { tv_clear(&var1); } @@ -3930,8 +3942,9 @@ void partial_unref(partial_T *pt) /// Allocate a variable for a List and fill it from "*arg". /// /// @return OK or FAIL. -static int get_list_tv(char **arg, typval_T *rettv, int evaluate) +static int get_list_tv(char **arg, typval_T *rettv, const int flags) { + const bool evaluate = flags & EVAL_EVALUATE; list_T *l = NULL; if (evaluate) { @@ -3941,7 +3954,7 @@ static int get_list_tv(char **arg, typval_T *rettv, int evaluate) *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { typval_T tv; - if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (eval1(arg, &tv, flags) == FAIL) { // Recursive! goto failret; } if (evaluate) { @@ -4576,8 +4589,9 @@ static int get_literal_key(char **arg, typval_T *tv) /// "literal" is true for #{key: val} /// /// @return OK or FAIL. Returns NOTDONE for {expr}. -static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal) +static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) { + const bool evaluate = flags & EVAL_EVALUATE; typval_T tv; char *key = NULL; char *curly_expr = skipwhite(*arg + 1); @@ -4591,7 +4605,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal) // "#{abc}" is never a curly-braces expression. if (*curly_expr != '}' && !literal - && eval1(&curly_expr, &tv, false) == OK + && eval1(&curly_expr, &tv, 0) == OK && *skipwhite(curly_expr) == '}') { return NOTDONE; } @@ -4608,7 +4622,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal) while (**arg != '}' && **arg != NUL) { if ((literal ? get_literal_key(arg, &tvkey) - : eval1(arg, &tvkey, evaluate)) == FAIL) { // recursive! + : eval1(arg, &tvkey, flags)) == FAIL) { // recursive! goto failret; } if (**arg != ':') { @@ -4626,7 +4640,7 @@ static int eval_dict(char **arg, typval_T *rettv, int evaluate, bool literal) } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (eval1(arg, &tv, flags) == FAIL) { // Recursive! if (evaluate) { tv_clear(&tvkey); } @@ -6937,12 +6951,12 @@ int check_luafunc_name(const char *const str, const bool paren) /// /// Can all be combined in any order: dict.func(expr)[idx]['func'](expr)->len() /// -/// @param evaluate do more than finding the end /// @param verbose give error messages /// @param start_leader start of '!' and '-' prefixes /// @param end_leaderp end of '!' and '-' prefixes -int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int verbose) +int handle_subscript(const char **const arg, typval_T *rettv, const int flags, bool verbose) { + const bool evaluate = flags & EVAL_EVALUATE; int ret = OK; dict_T *selfdict = NULL; const char *lua_funcname = NULL; @@ -7002,7 +7016,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int } else { selfdict = NULL; } - if (eval_index((char **)arg, rettv, evaluate, verbose) == FAIL) { + if (eval_index((char **)arg, rettv, flags, verbose) == FAIL) { tv_clear(rettv); ret = FAIL; } @@ -7385,7 +7399,7 @@ void ex_echo(exarg_T *eap) { char *p = arg; - if (eval1(&arg, &rettv, !eap->skip) == FAIL) { + if (eval1(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE) == FAIL) { // Report the invalid expression unless the expression evaluation // has been cancelled due to an aborting error, an interrupt, or an // exception. -- cgit From bd83b587b18bb6f2ac555a992fa5b7d907de7e79 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 07:22:01 +0800 Subject: vim-patch:8.2.1047: Vim9: script cannot use line continuation like :def function Problem: Vim9: script cannot use line continuation like in a :def function. Solution: Pass the getline function pointer to the eval() functions. Use it for addition and multiplication operators. https://github.com/vim/vim/commit/5409f5d8c95007216ae1190565a7a8ee9ebd7100 Omit source_nextline() and eval_next_non_blank(): Vim9 script only. N/A patches for version.c: vim-patch:8.2.1048: build failure without the eval feature Problem: Build failure without the eval feature. Solution: Add dummy typedef. https://github.com/vim/vim/commit/9d40c63c7dc8c3eb3886c58dcd334bc7f37eceba vim-patch:8.2.1052: build failure with older compilers Problem: Build failure with older compilers. Solution: Move declaration to start of block. https://github.com/vim/vim/commit/7acde51832f383f9a6d2e740cd0420b433ea841a Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 148 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 62 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 796c010f74..4fe86c8399 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -706,7 +706,7 @@ int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip) if (skip) { emsg_skip++; } - if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL) { + if (eval0(arg, &tv, nextcmd, skip ? NULL : &EVALARG_EVALUATE) == FAIL) { *error = true; } else { *error = false; @@ -730,7 +730,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; - const int ret = eval1(arg, rettv, evaluate ? EVAL_EVALUATE : 0); + const int ret = eval1(arg, rettv, evaluate ? &EVALARG_EVALUATE : NULL); if (ret == FAIL) { // Report the invalid expression unless the expression evaluation has // been cancelled due to an aborting error, an interrupt, or an @@ -832,7 +832,8 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip if (skip) { emsg_skip++; } - if (eval0((char *)arg, &tv, (char **)nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL || skip) { + if (eval0((char *)arg, &tv, (char **)nextcmd, skip ? NULL : &EVALARG_EVALUATE) + == FAIL || skip) { retval = NULL; } else { retval = xstrdup(tv_get_string(&tv)); @@ -853,7 +854,7 @@ int skip_expr(char **pp) typval_T rettv; *pp = skipwhite(*pp); - return eval1(pp, &rettv, 0); + return eval1(pp, &rettv, NULL); } /// Top level evaluation function, returning a string. @@ -868,7 +869,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert) char *retval; garray_T ga; - if (eval0(arg, &tv, nextcmd, EVAL_EVALUATE) == FAIL) { + if (eval0(arg, &tv, nextcmd, &EVALARG_EVALUATE) == FAIL) { retval = NULL; } else { if (convert && tv.v_type == VAR_LIST) { @@ -929,7 +930,7 @@ varnumber_T eval_to_number(char *expr) emsg_off++; - if (eval1(&p, &rettv, EVAL_EVALUATE) == FAIL) { + if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) { retval = -1; } else { retval = tv_get_number_chk(&rettv, NULL); @@ -947,7 +948,7 @@ varnumber_T eval_to_number(char *expr) typval_T *eval_expr(char *arg) { typval_T *tv = xmalloc(sizeof(*tv)); - if (eval0(arg, tv, NULL, EVAL_EVALUATE) == FAIL) { + if (eval0(arg, tv, NULL, &EVALARG_EVALUATE) == FAIL) { XFREE_CLEAR(tv); } return tv; @@ -1024,7 +1025,7 @@ list_T *eval_spell_expr(char *badword, char *expr) emsg_off++; } - if (eval1(&p, &rettv, EVAL_EVALUATE) == OK) { + if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) { if (rettv.v_type != VAR_LIST) { tv_clear(&rettv); } else { @@ -1171,7 +1172,7 @@ int eval_foldexpr(char *arg, int *cp) } textlock++; *cp = NUL; - if (eval0(arg, &tv, NULL, EVAL_EVALUATE) == FAIL) { + if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = 0; } else { // If the result is a number, just return the number. @@ -1346,7 +1347,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const empty1 = true; } else { empty1 = false; - if (eval1(&p, &var1, EVAL_EVALUATE) == FAIL) { // Recursive! + if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive! return NULL; } if (!tv_check_str(&var1)) { @@ -1381,7 +1382,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const } else { lp->ll_empty2 = false; // Recursive! - if (eval1(&p, &var2, EVAL_EVALUATE) == FAIL) { + if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) { tv_clear(&var1); return NULL; } @@ -1784,6 +1785,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) typval_T tv; list_T *l; + evalarg_T evalarg = { .eval_flags = skip ? 0 : EVAL_EVALUATE }; *errp = true; // Default: there is an error. expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); @@ -1800,7 +1802,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) if (skip) { emsg_skip++; } - if (eval0(skipwhite(expr + 2), &tv, nextcmdp, skip ? 0 : EVAL_EVALUATE) == OK) { + if (eval0(skipwhite(expr + 2), &tv, nextcmdp, &evalarg) == OK) { *errp = false; if (!skip) { if (tv.v_type == VAR_LIST) { @@ -2225,10 +2227,10 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ /// Put the result in "rettv" when returning OK and "evaluate" is true. /// Note: "rettv.v_lock" is not set. /// -/// @param flags has EVAL_EVALUATE and similar flags. +/// @param evalarg can be NULL, &EVALARG_EVALUATE or a pointer. /// /// @return OK or FAIL. -int eval0(char *arg, typval_T *rettv, char **nextcmd, const int flags) +int eval0(char *arg, typval_T *rettv, char **nextcmd, evalarg_T *const evalarg) { int ret; char *p; @@ -2237,7 +2239,7 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, const int flags) bool end_error = false; p = skipwhite(arg); - ret = eval1(&p, rettv, flags); + ret = eval1(&p, rettv, evalarg); if (ret != FAIL) { end_error = !ends_excmd(*p); @@ -2277,20 +2279,20 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, const int flags) /// Note: "rettv.v_lock" is not set. /// /// @return OK or FAIL. -int eval1(char **arg, typval_T *rettv, const int flags) +int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) { - typval_T var2; - // Get the first variable. - if (eval2(arg, rettv, flags) == FAIL) { + if (eval2(arg, rettv, evalarg) == FAIL) { return FAIL; } if ((*arg)[0] == '?') { - const bool evaluate = flags & EVAL_EVALUATE; + evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; + const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; + const bool evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE; bool result = false; - if (flags & EVAL_EVALUATE) { + if (evaluate) { bool error = false; if (tv_get_number_chk(rettv, &error) != 0) { @@ -2304,7 +2306,8 @@ int eval1(char **arg, typval_T *rettv, const int flags) // Get the second variable. Recursive! *arg = skipwhite(*arg + 1); - if (eval1(arg, rettv, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { + nested_evalarg.eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + if (eval1(arg, rettv, &nested_evalarg) == FAIL) { return FAIL; } @@ -2319,7 +2322,9 @@ int eval1(char **arg, typval_T *rettv, const int flags) // Get the third variable. Recursive! *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { + typval_T var2; + nested_evalarg.eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + if (eval1(arg, &var2, &nested_evalarg) == FAIL) { if (evaluate && result) { tv_clear(rettv); } @@ -2340,13 +2345,13 @@ int eval1(char **arg, typval_T *rettv, const int flags) /// "arg" is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval2(char **arg, typval_T *rettv, const int flags) +static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg) { typval_T var2; bool error = false; // Get the first variable. - if (eval3(arg, rettv, flags) == FAIL) { + if (eval3(arg, rettv, evalarg) == FAIL) { return FAIL; } @@ -2354,7 +2359,9 @@ static int eval2(char **arg, typval_T *rettv, const int flags) bool first = true; bool result = false; while ((*arg)[0] == '|' && (*arg)[1] == '|') { - const bool evaluate = flags & EVAL_EVALUATE; + evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; + const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; + const bool evaluate = orig_flags & EVAL_EVALUATE; if (evaluate && first) { if (tv_get_number_chk(rettv, &error) != 0) { @@ -2369,7 +2376,8 @@ static int eval2(char **arg, typval_T *rettv, const int flags) // Get the second variable. *arg = skipwhite(*arg + 2); - if (eval3(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { + nested_evalarg.eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + if (eval3(arg, &var2, &nested_evalarg) == FAIL) { return FAIL; } @@ -2399,13 +2407,13 @@ static int eval2(char **arg, typval_T *rettv, const int flags) /// `arg` is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval3(char **arg, typval_T *rettv, const int flags) +static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg) { typval_T var2; bool error = false; // Get the first variable. - if (eval4(arg, rettv, flags) == FAIL) { + if (eval4(arg, rettv, evalarg) == FAIL) { return FAIL; } @@ -2413,7 +2421,9 @@ static int eval3(char **arg, typval_T *rettv, const int flags) bool first = true; bool result = true; while ((*arg)[0] == '&' && (*arg)[1] == '&') { - const bool evaluate = flags & EVAL_EVALUATE; + evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; + const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; + const bool evaluate = orig_flags & EVAL_EVALUATE; if (evaluate && first) { if (tv_get_number_chk(rettv, &error) == 0) { @@ -2428,7 +2438,8 @@ static int eval3(char **arg, typval_T *rettv, const int flags) // Get the second variable. *arg = skipwhite(*arg + 2); - if (eval4(arg, &var2, result ? flags : flags & ~EVAL_EVALUATE) == FAIL) { + nested_evalarg.eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + if (eval4(arg, &var2, &nested_evalarg) == FAIL) { return FAIL; } @@ -2467,7 +2478,7 @@ static int eval3(char **arg, typval_T *rettv, const int flags) /// "arg" is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval4(char **arg, typval_T *rettv, const int flags) +static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg) { typval_T var2; char *p; @@ -2475,7 +2486,7 @@ static int eval4(char **arg, typval_T *rettv, const int flags) int len = 2; // Get the first variable. - if (eval5(arg, rettv, flags) == FAIL) { + if (eval5(arg, rettv, evalarg) == FAIL) { return FAIL; } @@ -2539,11 +2550,11 @@ static int eval4(char **arg, typval_T *rettv, const int flags) // Get the second variable. *arg = skipwhite(p + len); - if (eval5(arg, &var2, flags) == FAIL) { + if (eval5(arg, &var2, evalarg) == FAIL) { tv_clear(rettv); return FAIL; } - if (flags & EVAL_EVALUATE) { + if (evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE)) { const int ret = typval_compare(rettv, &var2, type, ic); tv_clear(&var2); @@ -2597,27 +2608,25 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2) /// `arg` is advanced to the next non-white after the recognized expression. /// /// @return OK or FAIL. -static int eval5(char **arg, typval_T *rettv, const int flags) +static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) { - typval_T var2; - varnumber_T n1, n2; - float_T f1 = 0, f2 = 0; - char *p; + const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); // Get the first variable. - if (eval6(arg, rettv, flags, false) == FAIL) { + if (eval6(arg, rettv, evalarg, false) == FAIL) { return FAIL; } // Repeat computing, until no '+', '-' or '.' is following. for (;;) { int op = (uint8_t)(**arg); - if (op != '+' && op != '-' && op != '.') { + bool concat = op == '.'; + if (op != '+' && op != '-' && !concat) { break; } if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) - && (op == '.' || rettv->v_type != VAR_FLOAT) && (flags & EVAL_EVALUATE)) { + && (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) { // For "list + ...", an illegal use of the first operand as // a number cannot be determined before evaluating the 2nd // operand: if this is also a list, all is ok. @@ -2636,12 +2645,13 @@ static int eval5(char **arg, typval_T *rettv, const int flags) (*arg)++; } *arg = skipwhite(*arg + 1); - if (eval6(arg, &var2, flags, op == '.') == FAIL) { + typval_T var2; + if (eval6(arg, &var2, evalarg, op == '.') == FAIL) { tv_clear(rettv); return FAIL; } - if (flags & EVAL_EVALUATE) { + if (evaluate) { // Compute the result. if (op == '.') { char buf1[NUMBUFLEN]; @@ -2654,7 +2664,7 @@ static int eval5(char **arg, typval_T *rettv, const int flags) tv_clear(&var2); return FAIL; } - p = concat_str(s1, s2); + char *p = concat_str(s1, s2); tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -2666,6 +2676,8 @@ static int eval5(char **arg, typval_T *rettv, const int flags) } } else { bool error = false; + varnumber_T n1, n2; + float_T f1 = 0, f2 = 0; if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; @@ -2738,7 +2750,7 @@ static int eval5(char **arg, typval_T *rettv, const int flags) /// @param[in] want_string True if "." is string_concatenation, otherwise /// float /// @return OK or FAIL. -static int eval6(char **arg, typval_T *rettv, const int flags, bool want_string) +static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) FUNC_ATTR_NO_SANITIZE_UNDEFINED { typval_T var2; @@ -2749,18 +2761,19 @@ static int eval6(char **arg, typval_T *rettv, const int flags, bool want_string) bool error = false; // Get the first variable. - if (eval7(arg, rettv, flags, want_string) == FAIL) { + if (eval7(arg, rettv, evalarg, want_string) == FAIL) { return FAIL; } // Repeat computing, until no '*', '/' or '%' is following. for (;;) { + const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); op = (uint8_t)(**arg); if (op != '*' && op != '/' && op != '%') { break; } - if (flags & EVAL_EVALUATE) { + if (evaluate) { if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; use_float = true; @@ -2778,11 +2791,11 @@ static int eval6(char **arg, typval_T *rettv, const int flags, bool want_string) // Get the second variable. *arg = skipwhite(*arg + 1); - if (eval7(arg, &var2, flags, false) == FAIL) { + if (eval7(arg, &var2, evalarg, false) == FAIL) { return FAIL; } - if (flags & EVAL_EVALUATE) { + if (evaluate) { if (var2.v_type == VAR_FLOAT) { if (!use_float) { f1 = (float_T)n1; @@ -2869,9 +2882,10 @@ static int eval6(char **arg, typval_T *rettv, const int flags, bool want_string) /// @param want_string after "." operator /// /// @return OK or FAIL. -static int eval7(char **arg, typval_T *rettv, const int flags, bool want_string) +static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) { - const bool evaluate = flags & EVAL_EVALUATE; + const int flags = evalarg == NULL ? 0 : evalarg->eval_flags; + const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); int ret = OK; static int recurse = 0; @@ -2979,7 +2993,7 @@ static int eval7(char **arg, typval_T *rettv, const int flags, bool want_string) // nested expression: (expression). case '(': *arg = skipwhite(*arg + 1); - ret = eval1(arg, rettv, flags); // recursive! + ret = eval1(arg, rettv, evalarg); // recursive! if (**arg == ')') { (*arg)++; } else if (ret == OK) { @@ -3319,13 +3333,15 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose } *arg = skipwhite(key + len); } else { + evalarg_T evalarg = { .eval_flags = flags }; + // something[idx] // // Get the (first) variable from inside the []. *arg = skipwhite(*arg + 1); if (**arg == ':') { empty1 = true; - } else if (eval1(arg, &var1, flags) == FAIL) { // Recursive! + } else if (eval1(arg, &var1, &evalarg) == FAIL) { // Recursive! return FAIL; } else if (evaluate && !tv_check_str(&var1)) { // Not a number or string. @@ -3339,7 +3355,7 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose *arg = skipwhite(*arg + 1); if (**arg == ']') { empty2 = true; - } else if (eval1(arg, &var2, flags) == FAIL) { // Recursive! + } else if (eval1(arg, &var2, &evalarg) == FAIL) { // Recursive! if (!empty1) { tv_clear(&var1); } @@ -3947,6 +3963,8 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags) const bool evaluate = flags & EVAL_EVALUATE; list_T *l = NULL; + evalarg_T evalarg = { .eval_flags = flags }; + if (evaluate) { l = tv_list_alloc(kListLenShouldKnow); } @@ -3954,7 +3972,7 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags) *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { typval_T tv; - if (eval1(arg, &tv, flags) == FAIL) { // Recursive! + if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive! goto failret; } if (evaluate) { @@ -4586,7 +4604,9 @@ static int get_literal_key(char **arg, typval_T *tv) } /// Allocate a variable for a Dictionary and fill it from "*arg". -/// "literal" is true for #{key: val} +/// +/// @param literal true for #{key: val} +/// @param flags can have EVAL_EVALUATE and other EVAL_ flags. /// /// @return OK or FAIL. Returns NOTDONE for {expr}. static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) @@ -4597,6 +4617,8 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) char *curly_expr = skipwhite(*arg + 1); char buf[NUMBUFLEN]; + evalarg_T evalarg = { .eval_flags = flags }; + // First check if it's not a curly-braces expression: {expr}. // Must do this without evaluating, otherwise a function may be called // twice. Unfortunately this means we need to call eval1() twice for the @@ -4605,7 +4627,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) // "#{abc}" is never a curly-braces expression. if (*curly_expr != '}' && !literal - && eval1(&curly_expr, &tv, 0) == OK + && eval1(&curly_expr, &tv, NULL) == OK && *skipwhite(curly_expr) == '}') { return NOTDONE; } @@ -4622,7 +4644,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) while (**arg != '}' && **arg != NUL) { if ((literal ? get_literal_key(arg, &tvkey) - : eval1(arg, &tvkey, flags)) == FAIL) { // recursive! + : eval1(arg, &tvkey, &evalarg)) == FAIL) { // recursive! goto failret; } if (**arg != ':') { @@ -4640,7 +4662,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, flags) == FAIL) { // Recursive! + if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive! if (evaluate) { tv_clear(&tvkey); } @@ -7389,6 +7411,8 @@ void ex_echo(exarg_T *eap) const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; + evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE }; + if (eap->skip) { emsg_skip++; } @@ -7399,7 +7423,7 @@ void ex_echo(exarg_T *eap) { char *p = arg; - if (eval1(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE) == FAIL) { + if (eval1(&arg, &rettv, &evalarg) == FAIL) { // Report the invalid expression unless the expression evaluation // has been cancelled due to an aborting error, an interrupt, or an // exception. -- cgit From 8e2903d2fe810cfa3be41fc1e7a4d8394c84cf11 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 09:11:37 +0800 Subject: vim-patch:8.2.1049: Vim9: leaking memory when using continuation line Problem: Vim9: leaking memory when using continuation line. Solution: Keep a pointer to the continuation line in evalarg_T. Centralize checking for a next command. https://github.com/vim/vim/commit/b171fb179053fa631fec74911b5fb9374cb6a8a1 Omit eval_next_line(): Vim9 script only. vim-patch:8.2.1050: missing change in struct Problem: Missing change in struct. Solution: Add missing change. https://github.com/vim/vim/commit/65a8ed37f7bc61fbe5c612a7b0eb0dfc16ad3e11 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4fe86c8399..a9c5ca46f3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -698,7 +698,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch /// @param skip only parse, don't execute /// /// @return true or false. -int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip) +int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip) { typval_T tv; bool retval = false; @@ -706,7 +706,7 @@ int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip) if (skip) { emsg_skip++; } - if (eval0(arg, &tv, nextcmd, skip ? NULL : &EVALARG_EVALUATE) == FAIL) { + if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL) { *error = true; } else { *error = false; @@ -817,13 +817,12 @@ bool eval_expr_to_bool(const typval_T *expr, bool *error) /// Top level evaluation function, returning a string /// /// @param[in] arg String to evaluate. -/// @param nextcmd Pointer to the start of the next Ex command. /// @param[in] skip If true, only do parsing to nextcmd without reporting /// errors or actually evaluating anything. /// /// @return [allocated] string result of evaluation or NULL in case of error or /// when skipping. -char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip) +char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { typval_T tv; @@ -832,8 +831,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip if (skip) { emsg_skip++; } - if (eval0((char *)arg, &tv, (char **)nextcmd, skip ? NULL : &EVALARG_EVALUATE) - == FAIL || skip) { + if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) { retval = NULL; } else { retval = xstrdup(tv_get_string(&tv)); @@ -863,13 +861,13 @@ int skip_expr(char **pp) /// a Float to a String. /// /// @return pointer to allocated memory, or NULL for failure. -char *eval_to_string(char *arg, char **nextcmd, bool convert) +char *eval_to_string(char *arg, bool convert) { typval_T tv; char *retval; garray_T ga; - if (eval0(arg, &tv, nextcmd, &EVALARG_EVALUATE) == FAIL) { + if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = NULL; } else { if (convert && tv.v_type == VAR_LIST) { @@ -899,7 +897,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert) /// textlock. /// /// @param use_sandbox when true, use the sandbox. -char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox) +char *eval_to_string_safe(char *arg, int use_sandbox) { char *retval; funccal_entry_T funccal_entry; @@ -909,7 +907,7 @@ char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox) sandbox++; } textlock++; - retval = eval_to_string(arg, nextcmd, false); + retval = eval_to_string(arg, false); if (use_sandbox) { sandbox--; } @@ -1778,7 +1776,7 @@ notify: /// @param[out] *errp set to true for an error, false otherwise; /// /// @return a pointer that holds the info. Null when there is an error. -void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) +void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); const char *expr; @@ -1802,7 +1800,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip) if (skip) { emsg_skip++; } - if (eval0(skipwhite(expr + 2), &tv, nextcmdp, &evalarg) == OK) { + if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) { *errp = false; if (!skip) { if (tv.v_type == VAR_LIST) { @@ -2230,7 +2228,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ /// @param evalarg can be NULL, &EVALARG_EVALUATE or a pointer. /// /// @return OK or FAIL. -int eval0(char *arg, typval_T *rettv, char **nextcmd, evalarg_T *const evalarg) +int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) { int ret; char *p; @@ -2238,6 +2236,9 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, evalarg_T *const evalarg) const int called_emsg_before = called_emsg; bool end_error = false; + if (evalarg != NULL) { + evalarg->eval_tofree = NULL; + } p = skipwhite(arg); ret = eval1(&p, rettv, evalarg); @@ -2263,8 +2264,24 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, evalarg_T *const evalarg) } ret = FAIL; } - if (nextcmd != NULL) { - *nextcmd = check_nextcmd(p); + + if (eap != NULL) { + eap->nextcmd = check_nextcmd(p); + } + + if (evalarg != NULL) { + if (eap != NULL) { + if (evalarg->eval_tofree != NULL) { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + xfree(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + } + } else { + xfree(evalarg->eval_tofree); + } } return ret; @@ -6537,15 +6554,14 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex } char *retval = NULL; - char *nextcmd = NULL; *expr_start = NUL; *expr_end = NUL; char c1 = *in_end; *in_end = NUL; - char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false); - if (temp_result != NULL && nextcmd == NULL) { + char *temp_result = eval_to_string(expr_start + 1, false); + if (temp_result != NULL) { retval = xmalloc(strlen(temp_result) + (size_t)(expr_start - in_start) + (size_t)(in_end - expr_end) + 1); STRCPY(retval, in_start); -- cgit From 9ecfb42ec0b8959c291c5c1d28710fe12d3d070f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 12:48:08 +0800 Subject: vim-patch:8.2.1062: Vim9: no line break allowed inside "cond ? val1 : val2" Problem: Vim9: no line break allowed inside "cond ? val1 : val2". Solution: Check for operator after line break. https://github.com/vim/vim/commit/793648fb563359396a23740c72a6e04cb64df3a9 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a9c5ca46f3..29c294c754 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2303,7 +2303,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) return FAIL; } - if ((*arg)[0] == '?') { + char *p = *arg; + if (*p == '?') { evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; const bool evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE; @@ -2329,7 +2330,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) } // Check for the ":". - if ((*arg)[0] != ':') { + p = *arg; + if (*p != ':') { emsg(_("E109: Missing ':' after '?'")); if (evaluate && result) { tv_clear(rettv); -- cgit From 9781a21133238060a8b58b9c84d3960d830fa3e9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 12:49:19 +0800 Subject: vim-patch:8.2.1063: Vim9: no line break allowed before || or && Problem: Vim9: no line break allowed before || or &&. Solution: Check for operator after line break. https://github.com/vim/vim/commit/be7ee488761a5582a5605197c3951a17f20d072e Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 29c294c754..bb756fa324 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2377,7 +2377,8 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg) // Repeat until there is no following "||". bool first = true; bool result = false; - while ((*arg)[0] == '|' && (*arg)[1] == '|') { + char *p = *arg; + while (p[0] == '|' && p[1] == '|') { evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; const bool evaluate = orig_flags & EVAL_EVALUATE; @@ -2414,6 +2415,8 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg) rettv->v_type = VAR_NUMBER; rettv->vval.v_number = result; } + + p = *arg; } return OK; @@ -2439,7 +2442,8 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg) // Repeat until there is no following "&&". bool first = true; bool result = true; - while ((*arg)[0] == '&' && (*arg)[1] == '&') { + char *p = *arg; + while (p[0] == '&' && p[1] == '&') { evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; const bool evaluate = orig_flags & EVAL_EVALUATE; @@ -2476,6 +2480,8 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg) rettv->v_type = VAR_NUMBER; rettv->vval.v_number = result; } + + p = *arg; } return OK; -- cgit From 51f99a347d181e16ccd80604f553b4b985991817 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 12:50:56 +0800 Subject: vim-patch:8.2.1064: Vim9: no line break allowed before comperators Problem: Vim9: no line break allowed before comperators. Solution: Check for comperator after line break. https://github.com/vim/vim/commit/e6536aa766e95b6c64489678eb029e6909ee6a35 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bb756fa324..0396b01771 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2506,7 +2506,6 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg) static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg) { typval_T var2; - char *p; exprtype_T type = EXPR_UNKNOWN; int len = 2; @@ -2515,7 +2514,7 @@ static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg) return FAIL; } - p = *arg; + char *p = *arg; switch (p[0]) { case '=': if (p[1] == '=') { -- cgit From 2af6bbcfa70863df7b6068059f9d504c7894e154 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 12:55:09 +0800 Subject: vim-patch:8.2.1065: Vim9: no line break allowed inside a list Problem: Vim9: no line break allowed inside a list. Solution: Handle line break inside a list in Vim9 script. https://github.com/vim/vim/commit/7147820cb978f5b179cfec2f9d8b7774e28d43e0 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0396b01771..8ea78cb5d4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2971,7 +2971,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // List: [expr, expr] case '[': - ret = get_list_tv(arg, rettv, flags); + ret = get_list_tv(arg, rettv, evalarg); break; // Dictionary: #{key: val, key: val} @@ -3981,14 +3981,13 @@ void partial_unref(partial_T *pt) /// Allocate a variable for a List and fill it from "*arg". /// +/// @param arg "*arg" points to the "[". /// @return OK or FAIL. -static int get_list_tv(char **arg, typval_T *rettv, const int flags) +static int get_list_tv(char **arg, typval_T *rettv, evalarg_T *const evalarg) { - const bool evaluate = flags & EVAL_EVALUATE; + const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE; list_T *l = NULL; - evalarg_T evalarg = { .eval_flags = flags }; - if (evaluate) { l = tv_list_alloc(kListLenShouldKnow); } @@ -3996,7 +3995,7 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags) *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { typval_T tv; - if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive! + if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive! goto failret; } if (evaluate) { @@ -4004,14 +4003,20 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags) tv_list_append_owned_tv(l, tv); } + // the comma must comma after the value + bool had_comma = **arg == ','; + if (had_comma) { + *arg = skipwhite(*arg + 1); + } + if (**arg == ']') { break; } - if (**arg != ',') { + + if (!had_comma) { semsg(_("E696: Missing comma in List: %s"), *arg); goto failret; } - *arg = skipwhite(*arg + 1); } if (**arg != ']') { -- cgit From 4ce20f722943708ae16a80442e62edf58717901b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 12:59:06 +0800 Subject: vim-patch:8.2.1068: Vim9: no line break allowed inside a dict Problem: Vim9: no line break allowed inside a dict. Solution: Handle line break inside a dict in Vim9 script. https://github.com/vim/vim/commit/8ea9390b78da9e34a20e7418712921397c0c1989 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8ea78cb5d4..1bc343f7de 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2978,7 +2978,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan case '#': if ((*arg)[1] == '{') { (*arg)++; - ret = eval_dict(arg, rettv, flags, true); + ret = eval_dict(arg, rettv, evalarg, true); } else { ret = NOTDONE; } @@ -2989,7 +2989,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan case '{': ret = get_lambda_tv(arg, rettv, evaluate); if (ret == NOTDONE) { - ret = eval_dict(arg, rettv, flags, false); + ret = eval_dict(arg, rettv, evalarg, false); } break; @@ -4638,16 +4638,14 @@ static int get_literal_key(char **arg, typval_T *tv) /// @param flags can have EVAL_EVALUATE and other EVAL_ flags. /// /// @return OK or FAIL. Returns NOTDONE for {expr}. -static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) +static int eval_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool literal) { - const bool evaluate = flags & EVAL_EVALUATE; + const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE; typval_T tv; char *key = NULL; char *curly_expr = skipwhite(*arg + 1); char buf[NUMBUFLEN]; - evalarg_T evalarg = { .eval_flags = flags }; - // First check if it's not a curly-braces expression: {expr}. // Must do this without evaluating, otherwise a function may be called // twice. Unfortunately this means we need to call eval1() twice for the @@ -4673,7 +4671,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) while (**arg != '}' && **arg != NUL) { if ((literal ? get_literal_key(arg, &tvkey) - : eval1(arg, &tvkey, &evalarg)) == FAIL) { // recursive! + : eval1(arg, &tvkey, evalarg)) == FAIL) { // recursive! goto failret; } if (**arg != ':') { @@ -4691,7 +4689,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive! + if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive! if (evaluate) { tv_clear(&tvkey); } @@ -4714,14 +4712,19 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) } tv_clear(&tvkey); + // the comma must come after the value + bool had_comma = **arg == ','; + if (had_comma) { + *arg = skipwhite(*arg + 1); + } + if (**arg == '}') { break; } - if (**arg != ',') { + if (!had_comma) { semsg(_("E722: Missing comma in Dictionary: %s"), *arg); goto failret; } - *arg = skipwhite(*arg + 1); } if (**arg != '}') { -- cgit From 89ff05b258f9d5663b4659e9023ff83004021ce6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:01:50 +0800 Subject: vim-patch:8.2.1069: Vim9: fail to check for white space in list Problem: Vim9: fail to check for white space in list. Solution: Add check for white space. https://github.com/vim/vim/commit/e6e031739c9d0c4140e371031b58a249db0eb572 N/A patches for version.c: vim-patch:8.2.1070: Vim9: leaking memory when lacking white space in dict Problem: Vim9: leaking memory when lacking white space in dict. Solution: Clear the typval. https://github.com/vim/vim/commit/ab19d495fd880b25a38d58cbeb5b21e4d0ee5835 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1bc343f7de..39ca70ae93 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4003,7 +4003,7 @@ static int get_list_tv(char **arg, typval_T *rettv, evalarg_T *const evalarg) tv_list_append_owned_tv(l, tv); } - // the comma must comma after the value + // the comma must come after the value bool had_comma = **arg == ','; if (had_comma) { *arg = skipwhite(*arg + 1); -- cgit From d927128fccad1c234e4b87321ff0d6392b9d69d5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:03:04 +0800 Subject: vim-patch:8.2.1071: Vim9: no line break allowed inside a lambda Problem: Vim9: no line break allowed inside a lambda. Solution: Handle line break inside a lambda in Vim9 script. https://github.com/vim/vim/commit/e40fbc2ca9fda07332a4da5af1fcaba91bed865b Omit skip_expr_concatenate(). Apply the change to skip_expr() instead. Omit eval_ga: Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 72 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 34 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 39ca70ae93..d5dd8407a1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -847,12 +847,24 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip) /// Skip over an expression at "*pp". /// /// @return FAIL for an error, OK otherwise. -int skip_expr(char **pp) +int skip_expr(char **pp, evalarg_T *const evalarg) { - typval_T rettv; + const int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags; + + // Don't evaluate the expression. + if (evalarg != NULL) { + evalarg->eval_flags &= ~EVAL_EVALUATE; + } *pp = skipwhite(*pp); - return eval1(pp, &rettv, NULL); + typval_T rettv; + int res = eval1(pp, &rettv, NULL); + + if (evalarg != NULL) { + evalarg->eval_flags = save_flags; + } + + return res; } /// Top level evaluation function, returning a string. @@ -2236,9 +2248,6 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) const int called_emsg_before = called_emsg; bool end_error = false; - if (evalarg != NULL) { - evalarg->eval_tofree = NULL; - } p = skipwhite(arg); ret = eval1(&p, rettv, evalarg); @@ -2269,19 +2278,14 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) eap->nextcmd = check_nextcmd(p); } - if (evalarg != NULL) { - if (eap != NULL) { - if (evalarg->eval_tofree != NULL) { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - xfree(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; - } - } else { - xfree(evalarg->eval_tofree); - } + if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + xfree(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + evalarg->eval_tofree = NULL; } return ret; @@ -2987,7 +2991,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Lambda: {arg, arg -> expr} // Dictionary: {'key': val, 'key': val} case '{': - ret = get_lambda_tv(arg, rettv, evaluate); + ret = get_lambda_tv(arg, rettv, evalarg); if (ret == NOTDONE) { ret = eval_dict(arg, rettv, evalarg, false); } @@ -3062,7 +3066,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Handle following '[', '(' and '.' for expr[expr], expr.name, // expr(expr), expr->name(expr) if (ret == OK) { - ret = handle_subscript((const char **)arg, rettv, flags, true); + ret = handle_subscript((const char **)arg, rettv, evalarg, true); } // Apply logical NOT and unary '-', from right to left, ignore '+'. @@ -3192,16 +3196,17 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e /// @return FAIL or OK. /// /// @note "*arg" is advanced to after the ')'. -static int eval_lambda(char **const arg, typval_T *const rettv, const bool evaluate, +static int eval_lambda(char **const arg, typval_T *const rettv, evalarg_T *const evalarg, const bool verbose) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(1, 2) { + const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); // Skip over the ->. *arg += 2; typval_T base = *rettv; rettv->v_type = VAR_UNKNOWN; - int ret = get_lambda_tv(arg, rettv, evaluate); + int ret = get_lambda_tv(arg, rettv, evalarg); if (ret != OK) { return FAIL; } else if (**arg != '(') { @@ -3306,9 +3311,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu /// @param verbose give error messages /// /// @returns FAIL or OK. "*arg" is advanced to after the ']'. -static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose) +static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool verbose) { - const bool evaluate = flags & EVAL_EVALUATE; + const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); bool empty1 = false; bool empty2 = false; ptrdiff_t len = -1; @@ -3357,15 +3362,13 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose } *arg = skipwhite(key + len); } else { - evalarg_T evalarg = { .eval_flags = flags }; - // something[idx] // // Get the (first) variable from inside the []. *arg = skipwhite(*arg + 1); if (**arg == ':') { empty1 = true; - } else if (eval1(arg, &var1, &evalarg) == FAIL) { // Recursive! + } else if (eval1(arg, &var1, evalarg) == FAIL) { // Recursive! return FAIL; } else if (evaluate && !tv_check_str(&var1)) { // Not a number or string. @@ -3379,7 +3382,7 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose *arg = skipwhite(*arg + 1); if (**arg == ']') { empty2 = true; - } else if (eval1(arg, &var2, &evalarg) == FAIL) { // Recursive! + } else if (eval1(arg, &var2, evalarg) == FAIL) { // Recursive! if (!empty1) { tv_clear(&var1); } @@ -7007,9 +7010,10 @@ int check_luafunc_name(const char *const str, const bool paren) /// @param verbose give error messages /// @param start_leader start of '!' and '-' prefixes /// @param end_leaderp end of '!' and '-' prefixes -int handle_subscript(const char **const arg, typval_T *rettv, const int flags, bool verbose) +int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const evalarg, + bool verbose) { - const bool evaluate = flags & EVAL_EVALUATE; + const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); int ret = OK; dict_T *selfdict = NULL; const char *lua_funcname = NULL; @@ -7054,7 +7058,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b } else if (**arg == '-') { if ((*arg)[2] == '{') { // expr->{lambda}() - ret = eval_lambda((char **)arg, rettv, evaluate, verbose); + ret = eval_lambda((char **)arg, rettv, evalarg, verbose); } else { // expr->name() ret = eval_method((char **)arg, rettv, evaluate, verbose); @@ -7069,7 +7073,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b } else { selfdict = NULL; } - if (eval_index((char **)arg, rettv, flags, verbose) == FAIL) { + if (eval_index((char **)arg, rettv, evalarg, verbose) == FAIL) { tv_clear(rettv); ret = FAIL; } -- cgit From 9c29c07705101b0117ea23e22ac3e5bb24e8c6c5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:24:05 +0800 Subject: vim-patch:8.2.1073: Vim9: no line break allowed in () expression Problem: Vim9: no line break allowed in () expression. Solution: Skip a line break. https://github.com/vim/vim/commit/7a4981b93642b5b62018cd8150b3fb0dfa2417d4 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d5dd8407a1..5c0083bf7d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3021,7 +3021,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // nested expression: (expression). case '(': *arg = skipwhite(*arg + 1); + ret = eval1(arg, rettv, evalarg); // recursive! + if (**arg == ')') { (*arg)++; } else if (ret == OK) { -- cgit From 10b8c6481fdf1f6a5d16ac89462c806b12114d1f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:25:34 +0800 Subject: vim-patch:8.2.1074: Vim9: no line break allowed after some operators Problem: Vim9: no line break allowed after some operators. Solution: Skip a line break after the operator. Add eval_may_get_next_line() to simplify checking for a line break. https://github.com/vim/vim/commit/9215f01218b2ed2cfe49c1f43fcf342bd9ffdded Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5c0083bf7d..b07ff2972e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3023,7 +3023,6 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan *arg = skipwhite(*arg + 1); ret = eval1(arg, rettv, evalarg); // recursive! - if (**arg == ')') { (*arg)++; } else if (ret == OK) { -- cgit From 64a91f5ea2424674b0f2550e33f3f8512af75231 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:31:41 +0800 Subject: vim-patch:8.2.1075: Vim9: no line break allowed in :echo expression Problem: Vim9: no line break allowed in :echo expression. Solution: Skip linebreak. https://github.com/vim/vim/commit/7e8967fdcdf45caf08753bb791dc3779e78b34c8 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b07ff2972e..16db4e791b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7447,7 +7447,10 @@ void ex_echo(exarg_T *eap) const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; - evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE }; + evalarg_T evalarg = { + .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, + .eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, + }; if (eap->skip) { emsg_skip++; -- cgit From e99f28e57d69cfa1bd38ab531a0954f4f875ca47 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:32:47 +0800 Subject: vim-patch:8.2.1076: Vim9: no line break allowed in :if expression Problem: Vim9: no line break allowed in :if expression. Solution: Skip linebreak. https://github.com/vim/vim/commit/faf8626b79e380fe81e7ae2439a535ed7619d27b Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 16db4e791b..a79a464860 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -703,10 +703,15 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip) typval_T tv; bool retval = false; + evalarg_T evalarg = { + .eval_flags = skip ? 0 : EVAL_EVALUATE, + .eval_cookie = eap != NULL && eap->getline == getsourceline ? eap->cookie : NULL, + }; + if (skip) { emsg_skip++; } - if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL) { + if (eval0(arg, &tv, eap, &evalarg) == FAIL) { *error = true; } else { *error = false; @@ -718,6 +723,7 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip) if (skip) { emsg_skip--; } + clear_evalarg(&evalarg, eap); return retval; } @@ -2228,6 +2234,20 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ return ret; } +/// After using "evalarg" filled from "eap" free the memory. +void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) +{ + if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + xfree(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + evalarg->eval_tofree = NULL; + } +} + /// The "evaluate" argument: When false, the argument is only parsed but not /// executed. The function may return OK, but the rettv will be of type /// VAR_UNKNOWN. The function still returns FAIL for a syntax error. @@ -2278,15 +2298,7 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) eap->nextcmd = check_nextcmd(p); } - if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - xfree(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; - evalarg->eval_tofree = NULL; - } + clear_evalarg(evalarg, eap); return ret; } @@ -7502,6 +7514,7 @@ void ex_echo(exarg_T *eap) arg = skipwhite(arg); } eap->nextcmd = check_nextcmd(arg); + clear_evalarg(&evalarg, eap); if (eap->skip) { emsg_skip--; -- cgit From 4b84b2e2aa3d7ab6a4e346c7439826700682f5f1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:38:14 +0800 Subject: vim-patch:8.2.1079: Vim9: no line break allowed in a while loop Problem: Vim9: no line break allowed in a while loop. Solution: Update stored loop lines when finding line breaks. https://github.com/vim/vim/commit/d5053d015a957b343ad9c9e45e0abd2978f10cf0 Omit getline_peek(): Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a79a464860..e5193cdfb8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -705,8 +705,11 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip) evalarg_T evalarg = { .eval_flags = skip ? 0 : EVAL_EVALUATE, - .eval_cookie = eap != NULL && eap->getline == getsourceline ? eap->cookie : NULL, }; + if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (skip) { emsg_skip++; @@ -7461,8 +7464,11 @@ void ex_echo(exarg_T *eap) evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, - .eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, }; + if (getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (eap->skip) { emsg_skip++; -- cgit From 8729c41f44de3b164ad8d01bb3558c6400e27952 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 13:49:28 +0800 Subject: vim-patch:8.2.1080: Vim9: no line break allowed in a for loop Problem: Vim9: no line break allowed in a for loop. Solution: Skip line breaks in for command. https://github.com/vim/vim/commit/b7a78f7a6713f07d2fcad0b27dea22925c7b1cdf Omit *_break_count and skip_for_lines(): Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e5193cdfb8..0880fed71e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -849,6 +849,7 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip) if (skip) { emsg_skip--; } + clear_evalarg(&EVALARG_EVALUATE, eap); return retval; } @@ -910,6 +911,7 @@ char *eval_to_string(char *arg, bool convert) } tv_clear(&tv); } + clear_evalarg(&EVALARG_EVALUATE, NULL); return retval; } @@ -964,12 +966,13 @@ varnumber_T eval_to_number(char *expr) /// /// @return an allocated typval_T with the result or /// NULL when there is an error. -typval_T *eval_expr(char *arg) +typval_T *eval_expr(char *arg, exarg_T *eap) { typval_T *tv = xmalloc(sizeof(*tv)); - if (eval0(arg, tv, NULL, &EVALARG_EVALUATE) == FAIL) { + if (eval0(arg, tv, eap, &EVALARG_EVALUATE) == FAIL) { XFREE_CLEAR(tv); } + clear_evalarg(&EVALARG_EVALUATE, eap); return tv; } @@ -1215,6 +1218,7 @@ int eval_foldexpr(char *arg, int *cp) sandbox--; } textlock--; + clear_evalarg(&EVALARG_EVALUATE, NULL); return (int)retval; } @@ -1797,14 +1801,14 @@ notify: /// @param[out] *errp set to true for an error, false otherwise; /// /// @return a pointer that holds the info. Null when there is an error. -void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip) +void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const evalarg) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); const char *expr; typval_T tv; list_T *l; + const bool skip = !(evalarg->eval_flags & EVAL_EVALUATE); - evalarg_T evalarg = { .eval_flags = skip ? 0 : EVAL_EVALUATE }; *errp = true; // Default: there is an error. expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); @@ -1813,7 +1817,8 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip) } expr = skipwhite(expr); - if (expr[0] != 'i' || expr[1] != 'n' || !ascii_iswhite(expr[2])) { + if (expr[0] != 'i' || expr[1] != 'n' + || !(expr[2] == NUL || ascii_iswhite(expr[2]))) { emsg(_("E690: Missing \"in\" after :for")); return fi; } @@ -1821,7 +1826,8 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip) if (skip) { emsg_skip++; } - if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) { + expr = skipwhite(expr + 2); + if (eval0((char *)expr, &tv, eap, evalarg) == OK) { *errp = false; if (!skip) { if (tv.v_type == VAR_LIST) { @@ -2240,13 +2246,17 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ /// After using "evalarg" filled from "eap" free the memory. void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) { - if (evalarg != NULL && eap != NULL && evalarg->eval_tofree != NULL) { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - xfree(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; + if (evalarg != NULL && evalarg->eval_tofree != NULL) { + if (eap != NULL) { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + xfree(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + } else { + xfree(evalarg->eval_tofree); + } evalarg->eval_tofree = NULL; } } @@ -2301,8 +2311,6 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) eap->nextcmd = check_nextcmd(p); } - clear_evalarg(evalarg, eap); - return ret; } -- cgit From ff963d699bd8113913d3511c7b4ea1621eae8a06 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:11:24 +0800 Subject: vim-patch:8.2.1098: Vim9: cannot use line break in :throw argument Problem: Vim9: cannot use line break in :throw argument. Solution: Check for line break. https://github.com/vim/vim/commit/006ad48b8a15c3bace741d8caaf3195e592fbe78 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0880fed71e..1a2268504c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -837,10 +837,17 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip) typval_T tv; char *retval; + evalarg_T evalarg = { + .eval_flags = skip ? 0 : EVAL_EVALUATE, + }; + if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg.eval_getline = eap->getline; + evalarg.eval_cookie = eap->cookie; + } if (skip) { emsg_skip++; } - if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) { + if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) { retval = NULL; } else { retval = xstrdup(tv_get_string(&tv)); @@ -849,7 +856,7 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip) if (skip) { emsg_skip--; } - clear_evalarg(&EVALARG_EVALUATE, eap); + clear_evalarg(&evalarg, eap); return retval; } -- cgit From 78dd6100b1b00e4a30d389fccc3c7770132e91a4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:13:12 +0800 Subject: vim-patch:8.2.1099: Vim9: cannot use line break in :cexpr argument Problem: Vim9: cannot use line break in :cexpr argument. Solution: Check for line break. https://github.com/vim/vim/commit/37c837119579ff70b005a4e54c2e26ca42b74022 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1a2268504c..5944cebf4c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -692,6 +692,15 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch set_vim_var_string(VV_FNAME_OUT, NULL, -1); } +static void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) +{ + *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE }; + if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg->eval_getline = eap->getline; + evalarg->eval_cookie = eap->cookie; + } +} + /// Top level evaluation function, returning a boolean. /// Sets "error" to true if there was an error. /// @@ -702,14 +711,9 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip) { typval_T tv; bool retval = false; + evalarg_T evalarg; - evalarg_T evalarg = { - .eval_flags = skip ? 0 : EVAL_EVALUATE, - }; - if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + fill_evalarg_from_eap(&evalarg, eap, skip); if (skip) { emsg_skip++; @@ -836,14 +840,9 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip) { typval_T tv; char *retval; + evalarg_T evalarg; - evalarg_T evalarg = { - .eval_flags = skip ? 0 : EVAL_EVALUATE, - }; - if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + fill_evalarg_from_eap(&evalarg, eap, skip); if (skip) { emsg_skip++; } @@ -976,10 +975,15 @@ varnumber_T eval_to_number(char *expr) typval_T *eval_expr(char *arg, exarg_T *eap) { typval_T *tv = xmalloc(sizeof(*tv)); - if (eval0(arg, tv, eap, &EVALARG_EVALUATE) == FAIL) { + evalarg_T evalarg; + + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); + + if (eval0(arg, tv, eap, &evalarg) == FAIL) { XFREE_CLEAR(tv); } - clear_evalarg(&EVALARG_EVALUATE, eap); + + clear_evalarg(&evalarg, eap); return tv; } @@ -7476,14 +7480,9 @@ void ex_echo(exarg_T *eap) bool need_clear = true; const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; + evalarg_T evalarg; - evalarg_T evalarg = { - .eval_flags = eap->skip ? 0 : EVAL_EVALUATE, - }; - if (getline_equal(eap->getline, eap->cookie, getsourceline)) { - evalarg.eval_getline = eap->getline; - evalarg.eval_cookie = eap->cookie; - } + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); if (eap->skip) { emsg_skip++; -- cgit From 04b58cec86e24aaa395b27b2a9587b5cb329ea04 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:18:23 +0800 Subject: vim-patch:8.2.1100: Vim9: cannot use line break in :execute argument Problem: Vim9: cannot use line break in :execute, :echomsg and :echoerr argument. Solution: Check for line break. https://github.com/vim/vim/commit/47e880d6c13c3ec2888398fd9ba1f5a7180d791a Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5944cebf4c..ba5c615ffd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -736,14 +736,17 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip) } /// Call eval1() and give an error message if not done at a lower level. -static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) +static int eval1_emsg(char **arg, typval_T *rettv, exarg_T *eap) FUNC_ATTR_NONNULL_ARG(1, 2) { const char *const start = *arg; const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; + evalarg_T evalarg; + + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); - const int ret = eval1(arg, rettv, evaluate ? &EVALARG_EVALUATE : NULL); + const int ret = eval1(arg, rettv, &evalarg); if (ret == FAIL) { // Report the invalid expression unless the expression evaluation has // been cancelled due to an aborting error, an interrupt, or an @@ -755,6 +758,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) semsg(_(e_invexpr2), start); } } + clear_evalarg(&evalarg, eap); return ret; } @@ -799,7 +803,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r return FAIL; } s = skipwhite(s); - if (eval1_emsg(&s, rettv, true) == FAIL) { + if (eval1_emsg(&s, rettv, NULL) == FAIL) { return FAIL; } if (*skipwhite(s) != NUL) { // check for trailing chars after expr @@ -7573,7 +7577,7 @@ void ex_execute(exarg_T *eap) emsg_skip++; } while (*arg != NUL && *arg != '|' && *arg != '\n') { - ret = eval1_emsg(&arg, &rettv, !eap->skip); + ret = eval1_emsg(&arg, &rettv, eap); if (ret == FAIL) { break; } -- cgit From 9c65a18753404ec0419bac45969be6c9e5a2fbb6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:24:08 +0800 Subject: vim-patch:8.2.1103: Coverity reports an unnecessary NULL check Problem: Coverity reports an unnecessary NULL check. Solution: Remove the check for NULL. https://github.com/vim/vim/commit/e707c882b23a53d2c1f0d1f7fc3a7be247aca614 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ba5c615ffd..ed1a49f08d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7486,7 +7486,7 @@ void ex_echo(exarg_T *eap) const int called_emsg_before = called_emsg; evalarg_T evalarg; - fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); + fill_evalarg_from_eap(&evalarg, eap, eap->skip); if (eap->skip) { emsg_skip++; -- cgit From cf37630d1b1443427c13e0a35e0a12b39e1415db Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:24:50 +0800 Subject: vim-patch:8.2.1110: Vim9: line continuation does not work in function arguments Problem: Vim9: line continuation does not work in function arguments. Solution: Pass "evalarg" to get_func_tv(). Fix seeing double quoted string as comment. https://github.com/vim/vim/commit/e6b5324e3a3d354363f3c48e784c42ce3e77453f Omit skipwhite_and_linebreak_keep_string(): Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ed1a49f08d..907e380b85 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -692,7 +692,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch set_vim_var_string(VV_FNAME_OUT, NULL, -1); } -static void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) +void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) { *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE }; if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) { @@ -2206,9 +2206,10 @@ int pattern_match(const char *pat, const char *text, bool ic) /// @param basetv "expr" for "expr->name(arg)" /// /// @return OK or FAIL. -static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv, - const int flags, typval_T *const basetv) - FUNC_ATTR_NONNULL_ARG(1, 2, 4) +static int eval_func(char **const arg, evalarg_T *const evalarg, char *const name, + const int name_len, typval_T *const rettv, const int flags, + typval_T *const basetv) + FUNC_ATTR_NONNULL_ARG(1, 3, 5) { const bool evaluate = flags & EVAL_EVALUATE; char *s = name; @@ -2234,7 +2235,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ funcexe.fe_evaluate = evaluate; funcexe.fe_partial = partial; funcexe.fe_basetv = basetv; - int ret = get_func_tv(s, len, rettv, arg, &funcexe); + int ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe); xfree(s); @@ -3089,7 +3090,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan ret = FAIL; } else { if (**arg == '(') { // recursive! - ret = eval_func(arg, s, len, rettv, flags, NULL); + ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL); } else if (evaluate) { ret = get_var_tv(s, len, rettv, NULL, true, false); } else { @@ -3181,10 +3182,10 @@ static int eval7_leader(typval_T *const rettv, const bool numeric_only, /// to the name of the Lua function to call (after the /// "v:lua." prefix). /// @return OK on success, FAIL on failure. -static int call_func_rettv(char **const arg, typval_T *const rettv, const bool evaluate, - dict_T *const selfdict, typval_T *const basetv, +static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T *const rettv, + const bool evaluate, dict_T *const selfdict, typval_T *const basetv, const char *const lua_funcname) - FUNC_ATTR_NONNULL_ARG(1, 2) + FUNC_ATTR_NONNULL_ARG(1, 3) { partial_T *pt = NULL; typval_T functv; @@ -3216,7 +3217,7 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e funcexe.fe_selfdict = selfdict; funcexe.fe_basetv = basetv; const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, - arg, &funcexe); + arg, evalarg, &funcexe); // Clear the funcref afterwards, so that deleting it while // evaluating the arguments is possible (see test55). @@ -3259,7 +3260,7 @@ static int eval_lambda(char **const arg, typval_T *const rettv, evalarg_T *const tv_clear(rettv); ret = FAIL; } else { - ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); + ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, NULL); } // Clear the funcref afterwards, so that deleting it while @@ -3276,10 +3277,12 @@ static int eval_lambda(char **const arg, typval_T *const rettv, evalarg_T *const /// @param *arg points to the '-'. /// /// @return FAIL or OK. "*arg" is advanced to after the ')'. -static int eval_method(char **const arg, typval_T *const rettv, const bool evaluate, +static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const evalarg, const bool verbose) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(1, 2) { + const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); + // Skip over the ->. *arg += 2; typval_T base = *rettv; @@ -3329,9 +3332,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu rettv->vval.v_partial = vvlua_partial; rettv->vval.v_partial->pt_refcount++; } - ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); + ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname); } else { - ret = eval_func(arg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base); + ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base); } } @@ -7081,7 +7084,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const e && !ascii_iswhite(*(*arg - 1))) || (**arg == '-' && (*arg)[1] == '>'))) { if (**arg == '(') { - ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname); + ret = call_func_rettv((char **)arg, evalarg, rettv, evaluate, selfdict, NULL, lua_funcname); // Stop the expression evaluation when immediately aborting on // error, or when an interrupt occurred or an exception was thrown @@ -7100,7 +7103,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const e ret = eval_lambda((char **)arg, rettv, evalarg, verbose); } else { // expr->name() - ret = eval_method((char **)arg, rettv, evaluate, verbose); + ret = eval_method((char **)arg, rettv, evalarg, verbose); } } else { // **arg == '[' || **arg == '.' tv_dict_unref(selfdict); -- cgit From 562840a2a16b4532f6d216c137506a7432c2e0e3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:40:42 +0800 Subject: vim-patch:8.2.1125: Vim9: double quote can be a string or a comment Problem: Vim9: double quote can be a string or a comment. Solution: Only support comments starting with # to avoid confusion. https://github.com/vim/vim/commit/962d7213194647e90f9bdc608f693d39dd07cbd5 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 907e380b85..ec3c2b5e85 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4679,8 +4679,8 @@ static int get_literal_key(char **arg, typval_T *tv) /// Allocate a variable for a Dictionary and fill it from "*arg". /// +/// @param arg "*arg" points to the "{". /// @param literal true for #{key: val} -/// @param flags can have EVAL_EVALUATE and other EVAL_ flags. /// /// @return OK or FAIL. Returns NOTDONE for {expr}. static int eval_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool literal) -- cgit From cc7a50a9ae9384167bd0fc2ee2eb7f1b31842f27 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 15:05:12 +0800 Subject: vim-patch:8.2.1161: Vim9: using freed memory Problem: Vim9: using freed memory. Solution: Put pointer back in evalarg instead of freeing it. https://github.com/vim/vim/commit/8e2730a315b8b06192f5fc822dc218dbb3cff7ae Omit eval_tofree_lambda: Vim9 script only. N/A patches for version.c: vim-patch:8.2.1163: build error Problem: Build error. Solution: Add missing change to globals. https://github.com/vim/vim/commit/6e13530ca03dd9cad245221177dd65f712211448 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ec3c2b5e85..9660157592 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2259,21 +2259,23 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam return ret; } -/// After using "evalarg" filled from "eap" free the memory. +/// After using "evalarg" filled from "eap": free the memory. void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) { - if (evalarg != NULL && evalarg->eval_tofree != NULL) { - if (eap != NULL) { - // We may need to keep the original command line, e.g. for - // ":let" it has the variable names. But we may also need the - // new one, "nextcmd" points into it. Keep both. - xfree(eap->cmdline_tofree); - eap->cmdline_tofree = *eap->cmdlinep; - *eap->cmdlinep = evalarg->eval_tofree; - } else { - xfree(evalarg->eval_tofree); + if (evalarg != NULL) { + if (evalarg->eval_tofree != NULL) { + if (eap != NULL) { + // We may need to keep the original command line, e.g. for + // ":let" it has the variable names. But we may also need the + // new one, "nextcmd" points into it. Keep both. + xfree(eap->cmdline_tofree); + eap->cmdline_tofree = *eap->cmdlinep; + *eap->cmdlinep = evalarg->eval_tofree; + } else { + xfree(evalarg->eval_tofree); + } + evalarg->eval_tofree = NULL; } - evalarg->eval_tofree = NULL; } } -- cgit From 56cfecdd59b4a8e4fa5c23adce858371ade5620f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:50:17 +0800 Subject: vim-patch:8.2.1203: unused assignments in expression evaluation Problem: Unused assignments in expression evaluation. Solution: Move declarations and assignments to inner blocks where possible. https://github.com/vim/vim/commit/3ac9c4701a5f1e39303ca2885956db92215966db Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9660157592..a088e6d253 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2679,8 +2679,6 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2) /// @return OK or FAIL. static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) { - const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); - // Get the first variable. if (eval6(arg, rettv, evalarg, false) == FAIL) { return FAIL; @@ -2694,6 +2692,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) break; } + const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) && (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) { // For "list + ...", an illegal use of the first operand as @@ -2822,12 +2821,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) FUNC_ATTR_NO_SANITIZE_UNDEFINED { - typval_T var2; - int op; - varnumber_T n1, n2; bool use_float = false; - float_T f1 = 0, f2 = 0; - bool error = false; // Get the first variable. if (eval7(arg, rettv, evalarg, want_string) == FAIL) { @@ -2836,12 +2830,15 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Repeat computing, until no '*', '/' or '%' is following. for (;;) { - const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); - op = (uint8_t)(**arg); + int op = (uint8_t)(**arg); if (op != '*' && op != '/' && op != '%') { break; } + varnumber_T n1, n2; + float_T f1 = 0, f2 = 0; + bool error = false; + const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); if (evaluate) { if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; @@ -2860,6 +2857,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Get the second variable. *arg = skipwhite(*arg + 1); + typval_T var2; if (eval7(arg, &var2, evalarg, false) == FAIL) { return FAIL; } @@ -2953,7 +2951,6 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan /// @return OK or FAIL. static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) { - const int flags = evalarg == NULL ? 0 : evalarg->eval_flags; const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); int ret = OK; static int recurse = 0; @@ -3091,6 +3088,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan if (len <= 0) { ret = FAIL; } else { + const int flags = evalarg == NULL ? 0 : evalarg->eval_flags; if (**arg == '(') { // recursive! ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL); } else if (evaluate) { -- cgit From 9c66b48316d85d24ee92d917765700713862aa2d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 14:57:22 +0800 Subject: vim-patch:8.2.3216: Vim9: crash when using variable in a loop at script level Problem: Vim9: crash when using variable in a loop at script level. Solution: Do not clear the variable if a function was defined. Do not create a new entry in sn_var_vals every time. (closes vim/vim#8628) https://github.com/vim/vim/commit/2eb6fc3b52148f961e804ec2be361d531ff770d8 Omit eval_cstack: Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a088e6d253..ad2d854e1e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -695,9 +695,11 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) { *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE }; - if (eap != NULL && getline_equal(eap->getline, eap->cookie, getsourceline)) { - evalarg->eval_getline = eap->getline; - evalarg->eval_cookie = eap->cookie; + if (eap != NULL) { + if (getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg->eval_getline = eap->getline; + evalarg->eval_cookie = eap->cookie; + } } } -- cgit From 85a7f9dabe0bcd6a1b3a0941f0bc0d1983b01888 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 16:50:25 +0800 Subject: vim-patch:8.2.1189: Vim9: line continuation in lambda doesn't always work Problem: Vim9: line continuation in lambda doesn't always work. Solution: Do not use a local evalarg unless there isn't one. (closes vim/vim#6439) https://github.com/vim/vim/commit/8af81d656a4c501611f6211b6379ea9dd650c545 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 171 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 104 insertions(+), 67 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ad2d854e1e..7da9502fd6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2352,9 +2352,14 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) char *p = *arg; if (*p == '?') { - evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; - const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; - const bool evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE; + evalarg_T *evalarg_used = evalarg; + evalarg_T local_evalarg; + if (evalarg == NULL) { + local_evalarg = (evalarg_T){ .eval_flags = 0 }; + evalarg_used = &local_evalarg; + } + const int orig_flags = evalarg_used->eval_flags; + const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE; bool result = false; if (evaluate) { @@ -2371,8 +2376,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) // Get the second variable. Recursive! *arg = skipwhite(*arg + 1); - nested_evalarg.eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; - if (eval1(arg, rettv, &nested_evalarg) == FAIL) { + evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + if (eval1(arg, rettv, evalarg_used) == FAIL) { return FAIL; } @@ -2389,8 +2394,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) // Get the third variable. Recursive! *arg = skipwhite(*arg + 1); typval_T var2; - nested_evalarg.eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; - if (eval1(arg, &var2, &nested_evalarg) == FAIL) { + evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + if (eval1(arg, &var2, evalarg_used) == FAIL) { if (evaluate && result) { tv_clear(rettv); } @@ -2399,6 +2404,12 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (evaluate && !result) { *rettv = var2; } + + if (evalarg == NULL) { + clear_evalarg(&local_evalarg, NULL); + } else { + evalarg->eval_flags = orig_flags; + } } return OK; @@ -2413,24 +2424,27 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) /// @return OK or FAIL. static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg) { - typval_T var2; - bool error = false; - // Get the first variable. if (eval3(arg, rettv, evalarg) == FAIL) { return FAIL; } - // Repeat until there is no following "||". - bool first = true; - bool result = false; + // Handle the "||" operator. char *p = *arg; - while (p[0] == '|' && p[1] == '|') { - evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; - const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; - const bool evaluate = orig_flags & EVAL_EVALUATE; + if (p[0] == '|' && p[1] == '|') { + evalarg_T *evalarg_used = evalarg; + evalarg_T local_evalarg; + if (evalarg == NULL) { + local_evalarg = (evalarg_T){ .eval_flags = 0 }; + evalarg_used = &local_evalarg; + } + const int orig_flags = evalarg_used->eval_flags; + const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE; + + bool result = false; - if (evaluate && first) { + if (evaluate) { + bool error = false; if (tv_get_number_chk(rettv, &error) != 0) { result = true; } @@ -2438,32 +2452,42 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (error) { return FAIL; } - first = false; } - // Get the second variable. - *arg = skipwhite(*arg + 2); - nested_evalarg.eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; - if (eval3(arg, &var2, &nested_evalarg) == FAIL) { - return FAIL; - } + // Repeat until there is no following "||". + while (p[0] == '|' && p[1] == '|') { + // Get the second variable. + *arg = skipwhite(*arg + 2); + evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + typval_T var2; + if (eval3(arg, &var2, evalarg_used) == FAIL) { + return FAIL; + } - // Compute the result. - if (evaluate && !result) { - if (tv_get_number_chk(&var2, &error) != 0) { - result = true; + // Compute the result. + if (evaluate && !result) { + bool error = false; + if (tv_get_number_chk(&var2, &error) != 0) { + result = true; + } + tv_clear(&var2); + if (error) { + return FAIL; + } } - tv_clear(&var2); - if (error) { - return FAIL; + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = result; } - } - if (evaluate) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = result; + + p = *arg; } - p = *arg; + if (evalarg == NULL) { + clear_evalarg(&local_evalarg, NULL); + } else { + evalarg->eval_flags = orig_flags; + } } return OK; @@ -2478,24 +2502,27 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg) /// @return OK or FAIL. static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg) { - typval_T var2; - bool error = false; - // Get the first variable. if (eval4(arg, rettv, evalarg) == FAIL) { return FAIL; } - // Repeat until there is no following "&&". - bool first = true; - bool result = true; char *p = *arg; - while (p[0] == '&' && p[1] == '&') { - evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; - const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; - const bool evaluate = orig_flags & EVAL_EVALUATE; + // Handle the "&&" operator. + if (p[0] == '&' && p[1] == '&') { + evalarg_T *evalarg_used = evalarg; + evalarg_T local_evalarg; + if (evalarg == NULL) { + local_evalarg = (evalarg_T){ .eval_flags = 0 }; + evalarg_used = &local_evalarg; + } + const int orig_flags = evalarg_used->eval_flags; + const bool evaluate = evalarg_used->eval_flags & EVAL_EVALUATE; + + bool result = true; - if (evaluate && first) { + if (evaluate) { + bool error = false; if (tv_get_number_chk(rettv, &error) == 0) { result = false; } @@ -2503,32 +2530,42 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (error) { return FAIL; } - first = false; } - // Get the second variable. - *arg = skipwhite(*arg + 2); - nested_evalarg.eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; - if (eval4(arg, &var2, &nested_evalarg) == FAIL) { - return FAIL; - } + // Repeat until there is no following "&&". + while (p[0] == '&' && p[1] == '&') { + // Get the second variable. + *arg = skipwhite(*arg + 2); + evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + typval_T var2; + if (eval4(arg, &var2, evalarg_used) == FAIL) { + return FAIL; + } - // Compute the result. - if (evaluate && result) { - if (tv_get_number_chk(&var2, &error) == 0) { - result = false; + // Compute the result. + if (evaluate && result) { + bool error = false; + if (tv_get_number_chk(&var2, &error) == 0) { + result = false; + } + tv_clear(&var2); + if (error) { + return FAIL; + } } - tv_clear(&var2); - if (error) { - return FAIL; + if (evaluate) { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = result; } - } - if (evaluate) { - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = result; + + p = *arg; } - p = *arg; + if (evalarg == NULL) { + clear_evalarg(&local_evalarg, NULL); + } else { + evalarg->eval_flags = orig_flags; + } } return OK; -- cgit From aaacfd4a6d8f43367f7fb3ba8d81baad3fde6c8e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 17:06:52 +0800 Subject: vim-patch:8.2.1512: failure after trinary expression fails Problem: Failure after trinary expression fails. Solution: Restore eval_flags. (Yasuhiro Matsumoto, closes vim/vim#6776) https://github.com/vim/vim/commit/69e44552c567ff25b363ba0790ad3d43fa0397a7 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7da9502fd6..dab3afb212 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2378,6 +2378,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) *arg = skipwhite(*arg + 1); evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; if (eval1(arg, rettv, evalarg_used) == FAIL) { + evalarg_used->eval_flags = orig_flags; return FAIL; } @@ -2388,17 +2389,19 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (evaluate && result) { tv_clear(rettv); } + evalarg_used->eval_flags = orig_flags; return FAIL; } // Get the third variable. Recursive! *arg = skipwhite(*arg + 1); - typval_T var2; evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + typval_T var2; if (eval1(arg, &var2, evalarg_used) == FAIL) { if (evaluate && result) { tv_clear(rettv); } + evalarg_used->eval_flags = orig_flags; return FAIL; } if (evaluate && !result) { -- cgit From d6e2804ab4f8810293dbcd748bfb938d9e0c3d52 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 11:00:17 +0800 Subject: vim-patch:8.2.1794: no falsy Coalescing operator Problem: No falsy Coalescing operator. Solution: Add the "??" operator. Fix mistake with function argument count. https://github.com/vim/vim/commit/92f26c256e06277ff2ec4ce7adea1eb58c85abe0 Cherry-pick tv2bool() into eval/typval.c. Cherry-pick *??* tag from Vim runtime. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 65 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 25 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dab3afb212..59e998d50f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2336,6 +2336,7 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) /// Handle top level expression: /// expr2 ? expr1 : expr1 +/// expr2 ?? expr1 /// /// "arg" must point to the first non-white of the expression. /// "arg" is advanced to the next non-white after the recognized expression. @@ -2352,6 +2353,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) char *p = *arg; if (*p == '?') { + const bool op_falsy = p[1] == '?'; evalarg_T *evalarg_used = evalarg; evalarg_T local_evalarg; if (evalarg == NULL) { @@ -2365,49 +2367,62 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (evaluate) { bool error = false; - if (tv_get_number_chk(rettv, &error) != 0) { + if (op_falsy) { + result = tv2bool(rettv); + } else if (tv_get_number_chk(rettv, &error) != 0) { result = true; } - tv_clear(rettv); + if (error || !op_falsy || !result) { + tv_clear(rettv); + } if (error) { return FAIL; } } // Get the second variable. Recursive! - *arg = skipwhite(*arg + 1); - evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; - if (eval1(arg, rettv, evalarg_used) == FAIL) { - evalarg_used->eval_flags = orig_flags; - return FAIL; - } - - // Check for the ":". - p = *arg; - if (*p != ':') { - emsg(_("E109: Missing ':' after '?'")); - if (evaluate && result) { - tv_clear(rettv); - } - evalarg_used->eval_flags = orig_flags; - return FAIL; + if (op_falsy) { + (*arg)++; } - - // Get the third variable. Recursive! *arg = skipwhite(*arg + 1); - evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + evalarg_used->eval_flags = (op_falsy ? !result : result) + ? orig_flags : orig_flags & ~EVAL_EVALUATE; typval_T var2; if (eval1(arg, &var2, evalarg_used) == FAIL) { - if (evaluate && result) { - tv_clear(rettv); - } evalarg_used->eval_flags = orig_flags; return FAIL; } - if (evaluate && !result) { + if (!op_falsy || !result) { *rettv = var2; } + if (!op_falsy) { + // Check for the ":". + p = *arg; + if (*p != ':') { + emsg(_("E109: Missing ':' after '?'")); + if (evaluate && result) { + tv_clear(rettv); + } + evalarg_used->eval_flags = orig_flags; + return FAIL; + } + + // Get the third variable. Recursive! + *arg = skipwhite(*arg + 1); + evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + if (eval1(arg, &var2, evalarg_used) == FAIL) { + if (evaluate && result) { + tv_clear(rettv); + } + evalarg_used->eval_flags = orig_flags; + return FAIL; + } + if (evaluate && !result) { + *rettv = var2; + } + } + if (evalarg == NULL) { clear_evalarg(&local_evalarg, NULL); } else { -- cgit From 3be966f725bfefd7215acd0aad155c94b813d53f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 17:54:48 +0800 Subject: vim-patch:9.0.1452: code using EVAL_CONSTANT is dead, it is never set Problem: Code using EVAL_CONSTANT is dead, it is never set. Solution: Remove EVAL_CONSTANT. (closes vim/vim#12252) https://github.com/vim/vim/commit/b7f6f93475a3ad5c590a09a2577fd51590bea9e1 --- src/nvim/eval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 59e998d50f..d5a794da01 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2386,7 +2386,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) } *arg = skipwhite(*arg + 1); evalarg_used->eval_flags = (op_falsy ? !result : result) - ? orig_flags : orig_flags & ~EVAL_EVALUATE; + ? orig_flags : (orig_flags & ~EVAL_EVALUATE); typval_T var2; if (eval1(arg, &var2, evalarg_used) == FAIL) { evalarg_used->eval_flags = orig_flags; @@ -2410,7 +2410,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) // Get the third variable. Recursive! *arg = skipwhite(*arg + 1); - evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE); if (eval1(arg, &var2, evalarg_used) == FAIL) { if (evaluate && result) { tv_clear(rettv); @@ -2476,7 +2476,7 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg) while (p[0] == '|' && p[1] == '|') { // Get the second variable. *arg = skipwhite(*arg + 2); - evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE); typval_T var2; if (eval3(arg, &var2, evalarg_used) == FAIL) { return FAIL; @@ -2554,7 +2554,7 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg) while (p[0] == '&' && p[1] == '&') { // Get the second variable. *arg = skipwhite(*arg + 2); - evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE; + evalarg_used->eval_flags = result ? orig_flags : (orig_flags & ~EVAL_EVALUATE); typval_T var2; if (eval4(arg, &var2, evalarg_used) == FAIL) { return FAIL; -- cgit From 90efe85a99c7986acccc8af370cfb179ae18aeff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 19:28:39 +0800 Subject: vim-patch:8.2.1111: inconsistent naming of get_list_tv() and eval_dict() (#23086) Problem: Inconsistent naming of get_list_tv() and eval_dict(). Solution: Rename get_list_tv() to eval_list(). Similarly for eval_number(), eval_string(), eval_lit_string() and a few others. https://github.com/vim/vim/commit/9a78e6df17033223ebdf499f2b02b2538601c52d Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d5a794da01..6a8b24ceed 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1674,8 +1674,8 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool // handle +=, -=, *=, /=, %= and .= di = NULL; - if (get_var_tv(lp->ll_name, (int)strlen(lp->ll_name), - &tv, &di, true, false) == OK) { + if (eval_variable(lp->ll_name, (int)strlen(lp->ll_name), + &tv, &di, true, false) == OK) { if ((di == NULL || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) && !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING))) @@ -3049,7 +3049,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan case '7': case '8': case '9': - ret = get_number_tv(arg, rettv, evaluate, want_string); + ret = eval_number(arg, rettv, evaluate, want_string); // Apply prefixed "-" and "+" now. Matters especially when // "->" follows. @@ -3060,17 +3060,17 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // String constant: "string". case '"': - ret = get_string_tv(arg, rettv, evaluate); + ret = eval_string(arg, rettv, evaluate); break; // Literal string constant: 'str''ing'. case '\'': - ret = get_lit_string_tv(arg, rettv, evaluate); + ret = eval_lit_string(arg, rettv, evaluate); break; // List: [expr, expr] case '[': - ret = get_list_tv(arg, rettv, evalarg); + ret = eval_list(arg, rettv, evalarg); break; // Dictionary: #{key: val, key: val} @@ -3094,11 +3094,11 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // Option value: &name case '&': - ret = get_option_tv((const char **)arg, rettv, evaluate); + ret = eval_option((const char **)arg, rettv, evaluate); break; // Environment variable: $VAR. case '$': - ret = get_env_tv(arg, rettv, evaluate); + ret = eval_env_var(arg, rettv, evaluate); break; // Register contents: @r. @@ -3146,11 +3146,14 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan ret = FAIL; } else { const int flags = evalarg == NULL ? 0 : evalarg->eval_flags; - if (**arg == '(') { // recursive! + if (**arg == '(') { + // "name(..." recursive! ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL); } else if (evaluate) { - ret = get_var_tv(s, len, rettv, NULL, true, false); + // get value of variable + ret = eval_variable(s, len, rettv, NULL, true, false); } else { + // skip the name check_vars(s, (size_t)len); ret = OK; } @@ -3711,7 +3714,7 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo /// @param[in] evaluate If not true, rettv is not populated. /// /// @return OK or FAIL. -int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate) +int eval_option(const char **const arg, typval_T *const rettv, const bool evaluate) FUNC_ATTR_NONNULL_ARG(1) { const bool working = (**arg == '+'); // has("+option") @@ -3774,7 +3777,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval /// Allocate a variable for a number constant. Also deals with "0z" for blob. /// /// @return OK or FAIL. -static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_string) +static int eval_number(char **arg, typval_T *rettv, bool evaluate, bool want_string) { char *p = skipdigits(*arg + 1); bool get_float = false; @@ -3859,7 +3862,7 @@ static int get_number_tv(char **arg, typval_T *rettv, bool evaluate, bool want_s /// Allocate a variable for a string constant. /// /// @return OK or FAIL. -static int get_string_tv(char **arg, typval_T *rettv, int evaluate) +static int eval_string(char **arg, typval_T *rettv, int evaluate) { char *p; unsigned int extra = 0; @@ -3972,7 +3975,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate) if (extra != 0) { name += extra; if (name >= rettv->vval.v_string + len) { - iemsg("get_string_tv() used more space than allocated"); + iemsg("eval_string() used more space than allocated"); } break; } @@ -3999,7 +4002,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate) /// Allocate a variable for a 'str''ing' constant. /// /// @return OK or FAIL. -static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate) +static int eval_lit_string(char **arg, typval_T *rettv, int evaluate) { char *p; int reduce = 0; @@ -4085,7 +4088,7 @@ void partial_unref(partial_T *pt) /// /// @param arg "*arg" points to the "[". /// @return OK or FAIL. -static int get_list_tv(char **arg, typval_T *rettv, evalarg_T *const evalarg) +static int eval_list(char **arg, typval_T *rettv, evalarg_T *const evalarg) { const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE; list_T *l = NULL; @@ -4883,7 +4886,7 @@ size_t string2float(const char *const text, float_T *const ret_value) /// @param arg Points to the '$'. It is advanced to after the name. /// /// @return FAIL if the name is invalid. -static int get_env_tv(char **arg, typval_T *rettv, int evaluate) +static int eval_env_var(char **arg, typval_T *rettv, int evaluate) { (*arg)++; char *name = *arg; @@ -8420,14 +8423,14 @@ bool eval_has_provider(const char *feat) typval_T tv; // Get the g:loaded_xx_provider variable. int len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name); - if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) { + if (eval_variable(buf, len, &tv, NULL, false, true) == FAIL) { // Trigger autoload once. len = snprintf(buf, sizeof(buf), "provider#%s#bogus", name); script_autoload(buf, (size_t)len, false); // Retry the (non-autoload-style) variable. len = snprintf(buf, sizeof(buf), "g:loaded_%s_provider", name); - if (get_var_tv(buf, len, &tv, NULL, false, true) == FAIL) { + if (eval_variable(buf, len, &tv, NULL, false, true) == FAIL) { // Show a hint if Call() is defined but g:loaded_xx_provider is missing. snprintf(buf, sizeof(buf), "provider#%s#Call", name); if (!!find_func(buf) && p_lpl) { -- cgit From 4b49f312a05214d5d1974f7ac2702ffb407fc558 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 13:17:32 +0800 Subject: vim-patch:8.2.0633: crash when using null partial in filter() Problem: Crash when using null partial in filter(). Solution: Fix crash. Add more tests. (Yegappan Lakshmanan, closes vim/vim#5976) https://github.com/vim/vim/commit/9d8d0b5c644ea53364d04403740b3f23e57c1497 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6a8b24ceed..46cd837a73 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -789,6 +789,9 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r } } else if (expr->v_type == VAR_PARTIAL) { partial_T *const partial = expr->vval.v_partial; + if (partial == NULL) { + return FAIL; + } const char *const s = partial_name(partial); if (s == NULL || *s == NUL) { return FAIL; @@ -8640,8 +8643,9 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) } if ((typ1->v_type == VAR_PARTIAL && typ1->vval.v_partial == NULL) || (typ2->v_type == VAR_PARTIAL && typ2->vval.v_partial == NULL)) { - // when a partial is NULL assume not equal - n1 = false; + // When both partials are NULL, then they are equal. + // Otherwise they are not equal. + n1 = (typ1->vval.v_partial == typ2->vval.v_partial); } else if (type_is) { if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) { // strings are considered the same if their value is -- cgit From 85741677c86f7686e3597a310f8059608e7816fb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 13:31:30 +0800 Subject: vim-patch:8.2.0634: crash with null partial and blob Problem: Crash with null partial and blob. Solution: Check for NULL pointer. Add more tests. (Yegappan Lakshmanan, closes vim/vim#5984) https://github.com/vim/vim/commit/92b83ccfda7a1d654ccaaf161a9c8a8e01fbcf76 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 46cd837a73..f555d973e4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4059,7 +4059,10 @@ char *partial_name(partial_T *pt) if (pt->pt_name != NULL) { return pt->pt_name; } - return pt->pt_func->uf_name; + if (pt->pt_func != NULL) { + return pt->pt_func->uf_name; + } + return ""; } static void partial_free(partial_T *pt) -- cgit From 031cf60d4a4b42f6f41e85fe1d7838b8de5bd09e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 14:19:34 +0800 Subject: vim-patch:8.2.1014: using "name" for a string result is confusing Problem: Using "name" for a string result is confusing. Solution: Rename to "end". https://github.com/vim/vim/commit/1e0b7b11db61bd906266d3174fee0bbaf20a101f Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f555d973e4..6635ba1868 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3896,26 +3896,26 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) // Copy the string into allocated memory, handling backslashed // characters. - const int len = (int)(p - *arg + extra); - char *name = xmalloc((size_t)len); rettv->v_type = VAR_STRING; - rettv->vval.v_string = name; + const int len = (int)(p - *arg + extra); + rettv->vval.v_string = xmalloc((size_t)len); + char *end = rettv->vval.v_string; for (p = *arg + 1; *p != NUL && *p != '"';) { if (*p == '\\') { switch (*++p) { case 'b': - *name++ = BS; ++p; break; + *end++ = BS; ++p; break; case 'e': - *name++ = ESC; ++p; break; + *end++ = ESC; ++p; break; case 'f': - *name++ = FF; ++p; break; + *end++ = FF; ++p; break; case 'n': - *name++ = NL; ++p; break; + *end++ = NL; ++p; break; case 'r': - *name++ = CAR; ++p; break; + *end++ = CAR; ++p; break; case 't': - *name++ = TAB; ++p; break; + *end++ = TAB; ++p; break; case 'X': // hex: "\x1", "\x12" case 'x': @@ -3941,9 +3941,9 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) // For "\u" store the number according to // 'encoding'. if (c != 'X') { - name += utf_char2bytes(nr, name); + end += utf_char2bytes(nr, end); } else { - *name++ = (char)nr; + *end++ = (char)nr; } } break; @@ -3957,14 +3957,14 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) case '5': case '6': case '7': - *name = (char)(*p++ - '0'); + *end = (char)(*p++ - '0'); if (*p >= '0' && *p <= '7') { - *name = (char)((*name << 3) + *p++ - '0'); + *end = (char)((*end << 3) + *p++ - '0'); if (*p >= '0' && *p <= '7') { - *name = (char)((*name << 3) + *p++ - '0'); + *end = (char)((*end << 3) + *p++ - '0'); } } - name++; + end++; break; // Special key, e.g.: "\" @@ -3974,10 +3974,10 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) if (p[1] != '*') { flags |= FSK_SIMPLIFY; } - extra = trans_special((const char **)&p, strlen(p), name, flags, false, NULL); + extra = trans_special((const char **)&p, strlen(p), end, flags, false, NULL); if (extra != 0) { - name += extra; - if (name >= rettv->vval.v_string + len) { + end += extra; + if (end >= rettv->vval.v_string + len) { iemsg("eval_string() used more space than allocated"); } break; @@ -3986,14 +3986,14 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) FALLTHROUGH; default: - mb_copy_char((const char **)&p, &name); + mb_copy_char((const char **)&p, &end); break; } } else { - mb_copy_char((const char **)&p, &name); + mb_copy_char((const char **)&p, &end); } } - *name = NUL; + *end = NUL; if (*p != NUL) { // just in case p++; } -- cgit From 9636ae6a13afaddc8e73e305e2bdf3fc5e42a492 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 14:28:10 +0800 Subject: vim-patch:8.2.3329: v_lock not set when getting value of environment variable Problem: v_lock not set when getting value of environment variable. Solution: Set v_lock to zero. https://github.com/vim/vim/commit/16e63e6d353c8b7337470644ceac02dc5e569db9 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6635ba1868..bbe4fe055d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4918,6 +4918,7 @@ static int eval_env_var(char **arg, typval_T *rettv, int evaluate) name[len] = (char)cc; rettv->v_type = VAR_STRING; rettv->vval.v_string = string; + rettv->v_lock = VAR_UNLOCKED; } return OK; -- cgit From 700152fbf8e035e400e4bd86a2f629179b27e2ef Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 16:36:19 +0800 Subject: vim-patch:8.2.1378: cannot put space between function name and paren Problem: Cannot put space between function name and paren. Solution: Allow this for backwards compatibility. https://github.com/vim/vim/commit/bbd3e3c357487f7a5bdc704a819f63a7dd0dd66e This fixes a regression from patch 8.2.1365, which isn't ported yet. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bbe4fe055d..b240c36977 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3149,8 +3149,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan ret = FAIL; } else { const int flags = evalarg == NULL ? 0 : evalarg->eval_flags; - if (**arg == '(') { + if (*skipwhite(*arg) == '(') { // "name(..." recursive! + *arg = skipwhite(*arg); ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL); } else if (evaluate) { // get value of variable -- cgit From bacb5021d4eff33c67eb659fb01125b2abcacd79 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 14 Apr 2023 21:08:00 +0800 Subject: vim-patch:8.2.4883: string interpolation only works in heredoc Problem: String interpolation only works in heredoc. Solution: Support interpolated strings. Use syntax for heredoc consistent with strings, similar to C#. (closes vim/vim#10327) https://github.com/vim/vim/commit/2eaef106e4a7fc9dc74a7e672b5f550ec1f9786e Cherry-pick Test_Debugger_breakadd_expr() from Vim. Co-authored-by: LemonBoy --- src/nvim/eval.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b240c36977..f8a9326703 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3100,8 +3100,13 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan ret = eval_option((const char **)arg, rettv, evaluate); break; // Environment variable: $VAR. + // Interpolated string: $"string" or $'string'. case '$': - ret = eval_env_var(arg, rettv, evaluate); + if ((*arg)[1] == '"' || (*arg)[1] == '\'') { + ret = eval_interp_string(arg, rettv, evaluate); + } else { + ret = eval_env_var(arg, rettv, evaluate); + } break; // Register contents: @r. @@ -4053,6 +4058,32 @@ static int eval_lit_string(char **arg, typval_T *rettv, int evaluate) return OK; } +int eval_interp_string(char **arg, typval_T *rettv, int evaluate) +{ + // *arg is on the '$' character. + (*arg)++; + + rettv->v_type = VAR_STRING; + + typval_T tv; + int ret; + if (**arg == '"') { + ret = eval_string(arg, &tv, evaluate); + } else { + ret = eval_lit_string(arg, &tv, evaluate); + } + + if (ret == FAIL || !evaluate) { + return ret; + } + + rettv->vval.v_string = eval_all_expr_in_str(tv.vval.v_string); + + tv_clear(&tv); + + return rettv->vval.v_string != NULL ? OK : FAIL; +} + /// @return the function name of the partial. char *partial_name(partial_T *pt) FUNC_ATTR_PURE -- cgit From ef9af89da753235c64cbd8b7d700c686bc94dad7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 17:51:39 +0800 Subject: vim-patch:8.2.4930: interpolated string expression requires escaping Problem: Interpolated string expression requires escaping. Solution: Do not require escaping in the expression. https://github.com/vim/vim/commit/0abc2871c105882ed1c1effb9a7757fad8a395bd Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 144 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 36 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f8a9326703..da345e4b53 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3063,12 +3063,12 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan // String constant: "string". case '"': - ret = eval_string(arg, rettv, evaluate); + ret = eval_string(arg, rettv, evaluate, false); break; // Literal string constant: 'str''ing'. case '\'': - ret = eval_lit_string(arg, rettv, evaluate); + ret = eval_lit_string(arg, rettv, evaluate, false); break; // List: [expr, expr] @@ -3868,16 +3868,20 @@ static int eval_number(char **arg, typval_T *rettv, bool evaluate, bool want_str return OK; } -/// Allocate a variable for a string constant. +/// Evaluate a string constant and put the result in "rettv". +/// "*arg" points to the double quote or to after it when "interpolate" is true. +/// When "interpolate" is true reduce "{{" to "{", reduce "}}" to "}" and stop +/// at a single "{". /// /// @return OK or FAIL. -static int eval_string(char **arg, typval_T *rettv, int evaluate) +static int eval_string(char **arg, typval_T *rettv, bool evaluate, bool interpolate) { char *p; - unsigned int extra = 0; + unsigned int extra = interpolate ? 1 : 0; + const int off = interpolate ? 0 : 1; // Find the end of the string, skipping backslashed characters. - for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { + for (p = *arg + off; *p != NUL && *p != '"'; MB_PTR_ADV(p)) { if (*p == '\\' && p[1] != NUL) { p++; // A "\" form occupies at least 4 characters, and produces up @@ -3886,17 +3890,27 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) if (*p == '<') { extra += 5; } + } else if (interpolate && (*p == '{' || *p == '}')) { + if (*p == '{' && p[1] != '{') { // start of expression + break; + } + p++; + if (p[-1] == '}' && *p != '}') { // single '}' is an error + semsg(_(e_stray_closing_curly_str), *arg); + return FAIL; + } + extra--; // "{{" becomes "{", "}}" becomes "}" } } - if (*p != '"') { + if (*p != '"' && !(interpolate && *p == '{')) { semsg(_("E114: Missing quote: %s"), *arg); return FAIL; } // If only parsing, set *arg and return here if (!evaluate) { - *arg = p + 1; + *arg = p + off; return OK; } @@ -3907,7 +3921,7 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) rettv->vval.v_string = xmalloc((size_t)len); char *end = rettv->vval.v_string; - for (p = *arg + 1; *p != NUL && *p != '"';) { + for (p = *arg + off; *p != NUL && *p != '"';) { if (*p == '\\') { switch (*++p) { case 'b': @@ -3996,11 +4010,17 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) break; } } else { + if (interpolate && (*p == '{' || *p == '}')) { + if (*p == '{' && p[1] != '{') { // start of expression + break; + } + p++; // reduce "{{" to "{" and "}}" to "}" + } mb_copy_char((const char **)&p, &end); } } *end = NUL; - if (*p != NUL) { // just in case + if (*p == '"' && !interpolate) { p++; } *arg = p; @@ -4009,79 +4029,131 @@ static int eval_string(char **arg, typval_T *rettv, int evaluate) } /// Allocate a variable for a 'str''ing' constant. +/// When "interpolate" is true reduce "{{" to "{" and stop at a single "{". /// -/// @return OK or FAIL. -static int eval_lit_string(char **arg, typval_T *rettv, int evaluate) +/// @return OK when a "rettv" was set to the string. +/// FAIL on error, "rettv" is not set. +static int eval_lit_string(char **arg, typval_T *rettv, bool evaluate, bool interpolate) { char *p; - int reduce = 0; + int reduce = interpolate ? -1 : 0; + const int off = interpolate ? 0 : 1; // Find the end of the string, skipping ''. - for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) { + for (p = *arg + off; *p != NUL; MB_PTR_ADV(p)) { if (*p == '\'') { if (p[1] != '\'') { break; } reduce++; p++; + } else if (interpolate) { + if (*p == '{') { + if (p[1] != '{') { + break; + } + p++; + reduce++; + } else if (*p == '}') { + p++; + if (*p != '}') { + semsg(_(e_stray_closing_curly_str), *arg); + return FAIL; + } + reduce++; + } } } - if (*p != '\'') { + if (*p != '\'' && !(interpolate && *p == '{')) { semsg(_("E115: Missing quote: %s"), *arg); return FAIL; } // If only parsing return after setting "*arg" if (!evaluate) { - *arg = p + 1; + *arg = p + off; return OK; } - // Copy the string into allocated memory, handling '' to ' reduction. + // Copy the string into allocated memory, handling '' to ' reduction and + // any expressions. char *str = xmalloc((size_t)((p - *arg) - reduce)); rettv->v_type = VAR_STRING; rettv->vval.v_string = str; - for (p = *arg + 1; *p != NUL;) { + for (p = *arg + off; *p != NUL;) { if (*p == '\'') { if (p[1] != '\'') { break; } p++; + } else if (interpolate && (*p == '{' || *p == '}')) { + if (*p == '{' && p[1] != '{') { + break; + } + p++; } mb_copy_char((const char **)&p, &str); } *str = NUL; - *arg = p + 1; + *arg = p + off; return OK; } -int eval_interp_string(char **arg, typval_T *rettv, int evaluate) +/// Evaluate a single or double quoted string possibly containing expressions. +/// "arg" points to the '$'. The result is put in "rettv". +/// +/// @return OK or FAIL. +int eval_interp_string(char **arg, typval_T *rettv, bool evaluate) { - // *arg is on the '$' character. + int ret = OK; + + garray_T ga; + ga_init(&ga, 1, 80); + + // *arg is on the '$' character, move it to the first string character. + (*arg)++; + const int quote = (uint8_t)(**arg); (*arg)++; - rettv->v_type = VAR_STRING; + for (;;) { + typval_T tv; + // Get the string up to the matching quote or to a single '{'. + // "arg" is advanced to either the quote or the '{'. + if (quote == '"') { + ret = eval_string(arg, &tv, evaluate, true); + } else { + ret = eval_lit_string(arg, &tv, evaluate, true); + } + if (ret == FAIL) { + break; + } + if (evaluate) { + ga_concat(&ga, tv.vval.v_string); + tv_clear(&tv); + } - typval_T tv; - int ret; - if (**arg == '"') { - ret = eval_string(arg, &tv, evaluate); - } else { - ret = eval_lit_string(arg, &tv, evaluate); + if (**arg != '{') { + // found terminating quote + (*arg)++; + break; + } + char *p = eval_one_expr_in_str(*arg, &ga); + if (p == NULL) { + ret = FAIL; + break; + } + *arg = p; } - if (ret == FAIL || !evaluate) { - return ret; + rettv->v_type = VAR_STRING; + if (ret != FAIL && evaluate) { + ga_append(&ga, NUL); } - - rettv->vval.v_string = eval_all_expr_in_str(tv.vval.v_string); - - tv_clear(&tv); - - return rettv->vval.v_string != NULL ? OK : FAIL; + rettv->vval.v_string = ga.ga_data; + return OK; } /// @return the function name of the partial. -- cgit From 29efd54e0284727a7dde5608e5eedaef9c00c65f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 18:19:47 +0800 Subject: vim-patch:8.2.4934: string interpolation fails when not evaluating Problem: String interpolation fails when not evaluating. Solution: Skip the expression when not evaluating. (closes vim/vim#10398) https://github.com/vim/vim/commit/70c41241c2701f26a99085e433925a206ca265a3 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index da345e4b53..289489a182 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4140,7 +4140,7 @@ int eval_interp_string(char **arg, typval_T *rettv, bool evaluate) (*arg)++; break; } - char *p = eval_one_expr_in_str(*arg, &ga); + char *p = eval_one_expr_in_str(*arg, &ga, evaluate); if (p == NULL) { ret = FAIL; break; -- cgit From c6ebcd523dc91c8b1d52a8c9a61aca3d3a59250b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Apr 2023 18:22:44 +0800 Subject: vim-patch:9.0.0104: going beyond allocated memory when evaluating string constant Problem: Going beyond allocated memory when evaluating string constant. Solution: Properly skip over form. https://github.com/vim/vim/commit/1e56bda9048a9625bce6e660938c834c5c15b07d Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 289489a182..a30f9146c5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3877,6 +3877,7 @@ static int eval_number(char **arg, typval_T *rettv, bool evaluate, bool want_str static int eval_string(char **arg, typval_T *rettv, bool evaluate, bool interpolate) { char *p; + const char *const arg_end = *arg + strlen(*arg); unsigned int extra = interpolate ? 1 : 0; const int off = interpolate ? 0 : 1; @@ -3888,7 +3889,20 @@ static int eval_string(char **arg, typval_T *rettv, bool evaluate, bool interpol // to 9 characters (6 for the char and 3 for a modifier): // reserve space for 5 extra. if (*p == '<') { + int modifiers = 0; + int flags = FSK_KEYCODE | FSK_IN_STRING; + extra += 5; + + // Skip to the '>' to avoid using '{' inside for string + // interpolation. + if (p[1] != '*') { + flags |= FSK_SIMPLIFY; + } + if (find_special_key((const char **)&p, (size_t)(arg_end - p), + &modifiers, flags, NULL) != 0) { + p--; // leave "p" on the ">" + } } } else if (interpolate && (*p == '{' || *p == '}')) { if (*p == '{' && p[1] != '{') { // start of expression @@ -3994,7 +4008,8 @@ static int eval_string(char **arg, typval_T *rettv, bool evaluate, bool interpol if (p[1] != '*') { flags |= FSK_SIMPLIFY; } - extra = trans_special((const char **)&p, strlen(p), end, flags, false, NULL); + extra = trans_special((const char **)&p, (size_t)(arg_end - p), + end, flags, false, NULL); if (extra != 0) { end += extra; if (end >= rettv->vval.v_string + len) { -- cgit From c08b03076167837cff9eb66c19440d727e6dad31 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 15 Apr 2023 23:40:48 +0200 Subject: refactor: deprecate checkhealth functions The following functions are deprecated and will be removed in Nvim v0.11: - health#report_start() - health#report_info() - health#report_ok() - health#report_warn() - health#report_error() - vim.health.report_start() - vim.health.report_info() - vim.health.report_ok() - vim.health.report_warn() - vim.health.report_error() Users should instead use these: - vim.health.start() - vim.health.info() - vim.health.ok() - vim.health.warn() - vim.health.error() --- src/nvim/eval.c | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a30f9146c5..97cf0c6364 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8597,33 +8597,27 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize) /// ":checkhealth [plugins]" void ex_checkhealth(exarg_T *eap) { - bool found = !!find_func("health#check"); - if (!found - && script_autoload("health#check", sizeof("health#check") - 1, false)) { - found = !!find_func("health#check"); - } - if (!found) { - const char *vimruntime_env = os_getenv("VIMRUNTIME"); - if (vimruntime_env == NULL) { - emsg(_("E5009: $VIMRUNTIME is empty or unset")); - } else { - bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); - if (rtp_ok) { - semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); - } else { - emsg(_("E5009: Invalid 'runtimepath'")); - } - } + Error err = ERROR_INIT; + MAXSIZE_TEMP_ARRAY(args, 1); + ADD_C(args, STRING_OBJ(cstr_as_string(eap->arg))); + NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); + if (!ERROR_SET(&err)) { return; } - size_t bufsize = strlen(eap->arg) + sizeof("call health#check('')"); - char *buf = xmalloc(bufsize); - snprintf(buf, bufsize, "call health#check('%s')", eap->arg); - - do_cmdline_cmd(buf); - - xfree(buf); + const char *vimruntime_env = os_getenv("VIMRUNTIME"); + if (vimruntime_env == NULL) { + emsg(_("E5009: $VIMRUNTIME is empty or unset")); + } else { + bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env); + if (rtp_ok) { + semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); + } else { + emsg(_("E5009: Invalid 'runtimepath'")); + } + } + semsg_multiline(err.msg); + api_clear_error(&err); } void invoke_prompt_callback(void) -- cgit From 08121ef69f47f8ad8f8903a732920412e24d30c1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 08:55:42 +0800 Subject: vim-patch:8.2.2848: crash whn calling partial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Crash whn calling partial. Solution: Check for NULL pointer. (Dominique PellĆ©, closes vim/vim#8202) https://github.com/vim/vim/commit/fe8ebdbe5c4e116311c0c0d5937b89ded5c92d01 Co-authored-by: Dominique Pelle --- src/nvim/eval.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 97cf0c6364..87633365b6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4175,11 +4175,13 @@ int eval_interp_string(char **arg, typval_T *rettv, bool evaluate) char *partial_name(partial_T *pt) FUNC_ATTR_PURE { - if (pt->pt_name != NULL) { - return pt->pt_name; - } - if (pt->pt_func != NULL) { - return pt->pt_func->uf_name; + if (pt != NULL) { + if (pt->pt_name != NULL) { + return pt->pt_name; + } + if (pt->pt_func != NULL) { + return pt->pt_func->uf_name; + } } return ""; } -- cgit From f4d3e279e861dc37dd047c81a0807767a74d251b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 09:01:33 +0800 Subject: vim-patch:8.2.2977: crash when using a null function reference Problem: Crash when using a null function reference. (Naohiro Ono) Solution: Check for an invalid function name. (closes vim/vim#8367) https://github.com/vim/vim/commit/22db0d549f64aa3d8a6e366b70eb8d7e66933b82 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 87633365b6..a5c81666c1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -96,6 +96,7 @@ static const char *e_string_list_or_blob_required = N_("E1098: String, List or B static const char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); +static const char e_empty_function_name[] = N_("E1192: Empty function name"); static char * const namespace_char = "abglstvw"; @@ -3273,6 +3274,10 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T funcname = is_lua ? lua_funcname : partial_name(pt); } else { funcname = functv.vval.v_string; + if (funcname == NULL || *funcname == NUL) { + emsg(_(e_empty_function_name)); + goto theend; + } } } else { funcname = ""; @@ -3288,6 +3293,7 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, arg, evalarg, &funcexe); +theend: // Clear the funcref afterwards, so that deleting it while // evaluating the arguments is possible (see test55). if (evaluate) { -- cgit From 2e8cec5f2bf2dd1209052e870d9713a932dc7bfa Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 09:07:03 +0800 Subject: vim-patch:8.2.2978: warning for uninitialized variable Problem: Warning for uninitialized variable. Solution: Set return value to FAIL. https://github.com/vim/vim/commit/744aecf8777e86fac6d30f072e90e2de353b8ea1 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a5c81666c1..1d9061186a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3261,6 +3261,7 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T typval_T functv; const char *funcname; bool is_lua = false; + int ret; // need to copy the funcref so that we can clear rettv if (evaluate) { @@ -3276,6 +3277,7 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T funcname = functv.vval.v_string; if (funcname == NULL || *funcname == NUL) { emsg(_(e_empty_function_name)); + ret = FAIL; goto theend; } } @@ -3290,8 +3292,8 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T funcexe.fe_partial = pt; funcexe.fe_selfdict = selfdict; funcexe.fe_basetv = basetv; - const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, - arg, evalarg, &funcexe); + ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, + arg, evalarg, &funcexe); theend: // Clear the funcref afterwards, so that deleting it while -- cgit From 68ca16c376bd8786ffc0c7ce7619b9a0ce5657e8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 08:54:07 +0800 Subject: vim-patch:8.2.3783: confusing error for using a variable as a function Problem: Confusing error for using a variable as a function. Solution: If a function is not found but there is a variable, give a more useful error. (issue vim/vim#9310) https://github.com/vim/vim/commit/2ef9156b4284e4a52613c36e3d4667245273a28d Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1d9061186a..b8daa78c79 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2220,6 +2220,7 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam const bool evaluate = flags & EVAL_EVALUATE; char *s = name; int len = name_len; + bool found_var = false; if (!evaluate) { check_vars(s, (size_t)len); @@ -2228,7 +2229,7 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam // If "s" is the name of a variable of type VAR_FUNC // use its contents. partial_T *partial; - s = deref_func_name(s, &len, &partial, !evaluate); + s = deref_func_name(s, &len, &partial, !evaluate, &found_var); // Need to make a copy, in case evaluating the arguments makes // the name invalid. @@ -2241,6 +2242,7 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam funcexe.fe_evaluate = evaluate; funcexe.fe_partial = partial; funcexe.fe_basetv = basetv; + funcexe.fe_found_var = found_var; int ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe); xfree(s); -- cgit From 42e55ba009ecc14afa1b519c8de02cb30c0d1671 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 16:57:25 +0800 Subject: vim-patch:9.0.0398: members of funccall_T are inconsistently named (#23123) Problem: Members of funccall_T are inconsistently named. Solution: Use the "fc_" prefix for all members. https://github.com/vim/vim/commit/ca16c60f337ed33d5dd66a6e90aaf95b619c5e47 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b8daa78c79..8d517fa4d2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7466,7 +7466,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char if (funccal == NULL) { // global variable *d = &globvardict; } else { // l: variable - *d = &funccal->l_vars; + *d = &funccal->fc_l_vars; } goto end; } @@ -7490,9 +7490,9 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char } else if (*name == 'v') { // v: variable *d = &vimvardict; } else if (*name == 'a' && funccal != NULL) { // function argument - *d = &funccal->l_avars; + *d = &funccal->fc_l_avars; } else if (*name == 'l' && funccal != NULL) { // local variable - *d = &funccal->l_vars; + *d = &funccal->fc_l_vars; } else if (*name == 's' // script variable && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) -- cgit From 227f06b7dfe52f3cb15a26ba990237af5014e2b6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Apr 2023 18:43:15 +0800 Subject: vim-patch:9.0.1145: invalid memory access with recursive substitute expression (#23132) Problem: Invalid memory access with recursive substitute expression. Solution: Check the return value of vim_regsub(). https://github.com/vim/vim/commit/3ac1d97a1d9353490493d30088256360435f7731 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8d517fa4d2..8d38df8421 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8375,6 +8375,10 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char // - The substituted text. // - The text after the match. sublen = vim_regsub(®match, sub, expr, tail, 0, REGSUB_MAGIC); + if (sublen <= 0) { + ga_clear(&ga); + break; + } ga_grow(&ga, (int)((end - tail) + sublen - (regmatch.endp[0] - regmatch.startp[0]))); -- cgit From ba57566601725c5ac142a77b42d45d8d7f02a243 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 17 Apr 2023 15:17:58 +0800 Subject: vim-patch:8.2.4181: Vim9: cannot use an import in 'diffexpr' Problem: Vim9: cannot use an import in 'diffexpr'. Solution: Set the script context when evaluating 'diffexpr'. Do not require 'diffexpr' to return a bool, it was ignored anyway. https://github.com/vim/vim/commit/7b29f6a3949743914f08410b6f6bd6237c2f2038 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8d38df8421..5eb7e2260f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -669,15 +669,24 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) { - bool err = false; - + const sctx_T saved_sctx = current_sctx; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_NEW, newfile, -1); set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_dex, &err, NULL, false); + + sctx_T *ctx = get_option_sctx("diffexpr"); + if (ctx != NULL) { + current_sctx = *ctx; + } + + // errors are ignored + typval_T *tv = eval_expr(p_dex, NULL); + tv_clear(tv); + set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_FNAME_NEW, NULL, -1); set_vim_var_string(VV_FNAME_OUT, NULL, -1); + current_sctx = saved_sctx; } void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) -- cgit From 9cf59acfaa2a050ce7fbc68d5435bf777618dedb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 17 Apr 2023 15:21:03 +0800 Subject: vim-patch:8.2.4182: memory leak when evaluating 'diffexpr' Problem: Memory leak when evaluating 'diffexpr'. Solution: Use free_tv() instead of clear_tv(). https://github.com/vim/vim/commit/39b8944539a9cde553fe709e535fdfd37d0f9307 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5eb7e2260f..750d9df454 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -681,7 +681,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char // errors are ignored typval_T *tv = eval_expr(p_dex, NULL); - tv_clear(tv); + tv_free(tv); set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_FNAME_NEW, NULL, -1); -- cgit From cec42e07bc136f496135dfd8ed6e6c4d16a24e0d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 17 Apr 2023 15:25:34 +0800 Subject: vim-patch:8.2.4186: cannot use an import in 'patchexpr' Problem: Cannot use an import in 'patchexpr'. Solution: Set the script context when evaluating 'patchexpr'. Do not require 'patchexpr' to return a bool, it was ignored anyway. https://github.com/vim/vim/commit/36c2add7f82bc5dbbfc45db31953ef9633c635b3 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 750d9df454..9a94d414f9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -691,15 +691,24 @@ void eval_diff(const char *const origfile, const char *const newfile, const char void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) { - bool err = false; - + const sctx_T saved_sctx = current_sctx; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_DIFF, difffile, -1); set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_pex, &err, NULL, false); + + sctx_T *ctx = get_option_sctx("patchexpr"); + if (ctx != NULL) { + current_sctx = *ctx; + } + + // errors are ignored + typval_T *tv = eval_expr(p_pex, NULL); + tv_free(tv); + set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_FNAME_DIFF, NULL, -1); set_vim_var_string(VV_FNAME_OUT, NULL, -1); + current_sctx = saved_sctx; } void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) -- cgit From f560c970591a2d8355768deb79a8bd41a56b43dc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 17 Apr 2023 15:27:39 +0800 Subject: vim-patch:8.2.4193: cannot use an import in 'charconvert' Problem: Cannot use an import in 'charconvert'. Solution: Set the script context when evaluating 'charconvert'. Also expand script-local functions in 'charconvert'. https://github.com/vim/vim/commit/f4e88f2152c5975a6f4cfa7ccd745575fe4d1c78 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9a94d414f9..4096b5d0ee 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -647,19 +647,27 @@ void var_redir_stop(void) int eval_charconvert(const char *const enc_from, const char *const enc_to, const char *const fname_from, const char *const fname_to) { - bool err = false; + const sctx_T saved_sctx = current_sctx; set_vim_var_string(VV_CC_FROM, enc_from, -1); set_vim_var_string(VV_CC_TO, enc_to, -1); set_vim_var_string(VV_FNAME_IN, fname_from, -1); set_vim_var_string(VV_FNAME_OUT, fname_to, -1); + sctx_T *ctx = get_option_sctx("charconvert"); + if (ctx != NULL) { + current_sctx = *ctx; + } + + bool err = false; if (eval_to_bool(p_ccv, &err, NULL, false)) { err = true; } + set_vim_var_string(VV_CC_FROM, NULL, -1); set_vim_var_string(VV_CC_TO, NULL, -1); set_vim_var_string(VV_FNAME_IN, NULL, -1); set_vim_var_string(VV_FNAME_OUT, NULL, -1); + current_sctx = saved_sctx; if (err) { return FAIL; -- cgit From 481c6e6cac1d7219098408af5c396bada065be64 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 17 Apr 2023 15:30:42 +0800 Subject: vim-patch:8.2.4197: cannot use an import in the "expr" part of 'spellsuggest' Problem: Cannot use an import in the "expr" part of 'spellsuggest'. Solution: Set the script context when evaluating "expr" of 'spellsuggest'. https://github.com/vim/vim/commit/2a7aa834583dea157eccf3e69827d2ff1d9fe9c7 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4096b5d0ee..28ac1f3fbd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1085,6 +1085,7 @@ list_T *eval_spell_expr(char *badword, char *expr) typval_T rettv; list_T *list = NULL; char *p = skipwhite(expr); + const sctx_T saved_sctx = current_sctx; // Set "v:val" to the bad word. prepare_vimvar(VV_VAL, &save_val); @@ -1093,6 +1094,10 @@ list_T *eval_spell_expr(char *badword, char *expr) if (p_verbose == 0) { emsg_off++; } + sctx_T *ctx = get_option_sctx("spellsuggest"); + if (ctx != NULL) { + current_sctx = *ctx; + } if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) { if (rettv.v_type != VAR_LIST) { @@ -1106,6 +1111,7 @@ list_T *eval_spell_expr(char *badword, char *expr) emsg_off--; } restore_vimvar(VV_VAL, &save_val); + current_sctx = saved_sctx; return list; } -- cgit From 1c12f844ad6db504e1435b5e4b633eb87b932180 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 19 Apr 2023 11:29:10 +0800 Subject: refactor(eval): use tv_list_append_allocated_string() --- src/nvim/eval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 28ac1f3fbd..35738cdfa9 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5712,8 +5712,7 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) if (dir != NULL && dir_len > 0) { char *dir_with_nvim = xmemdupz(dir, dir_len); dir_with_nvim = concat_fnames_realloc(dir_with_nvim, appname, true); - tv_list_append_string(list, dir_with_nvim, (ssize_t)strlen(dir_with_nvim)); - xfree(dir_with_nvim); + tv_list_append_allocated_string(list, dir_with_nvim); } } while (iter != NULL); xfree(dirs); -- cgit From 43c49746d9cf82dba0d56b07d39722f9ebeecf90 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 25 Apr 2023 21:32:12 +0800 Subject: vim-patch:9.0.0335: checks for Dictionary argument often give a vague error (#23309) Problem: Checks for Dictionary argument often give a vague error message. Solution: Give a useful error message. (Yegappan Lakshmanan, closes vim/vim#11009) https://github.com/vim/vim/commit/04c4c5746e15884768d2cb41370c3276a196cd4c Cherry-pick removal of E922 from docs from patch 9.0.1403. Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 35738cdfa9..bc33697c62 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5357,8 +5357,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) arg_idx = 1; } if (dict_idx > 0) { - if (argvars[dict_idx].v_type != VAR_DICT) { - emsg(_("E922: expected a dict")); + if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) { xfree(name); goto theend; } @@ -8726,7 +8725,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) const bool type_is = type == EXPR_IS || type == EXPR_ISNOT; if (type_is && typ1->v_type != typ2->v_type) { - // For "is" a different type always means false, for "notis" + // For "is" a different type always means false, for "isnot" // it means true. n1 = type == EXPR_ISNOT; } else if (typ1->v_type == VAR_BLOB || typ2->v_type == VAR_BLOB) { -- cgit From 255e547e18e127ab70ffa2e423f7753786cb424e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 25 Apr 2023 22:21:19 +0800 Subject: fix(timer): allow timer_info() to get info about current timer --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bc33697c62..b3618c1811 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6002,7 +6002,7 @@ void add_timer_info_all(typval_T *rettv) tv_list_alloc_ret(rettv, map_size(&timers)); timer_T *timer; map_foreach_value(&timers, timer, { - if (!timer->stopped) { + if (!timer->stopped || timer->refcount > 1) { add_timer_info(rettv, timer); } }) -- cgit From 3b0df1780e2c8526bda5dead18ee7cc45925caba Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:23:44 +0200 Subject: refactor: uncrustify Notable changes: replace all infinite loops to `while(true)` and remove `int` from `unsigned int`. --- src/nvim/eval.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b3618c1811..1dfa853fd3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2082,7 +2082,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) || cmdidx == CMD_echon || cmdidx == CMD_echomsg) && xp->xp_context == EXPAND_EXPRESSION) { - for (;;) { + while (true) { char *const n = skiptowhite(arg); if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) { @@ -2780,7 +2780,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) } // Repeat computing, until no '+', '-' or '.' is following. - for (;;) { + while (true) { int op = (uint8_t)(**arg); bool concat = op == '.'; if (op != '+' && op != '-' && !concat) { @@ -2924,7 +2924,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan } // Repeat computing, until no '*', '/' or '%' is following. - for (;;) { + while (true) { int op = (uint8_t)(**arg); if (op != '*' && op != '/' && op != '%') { break; @@ -3920,7 +3920,7 @@ static int eval_string(char **arg, typval_T *rettv, bool evaluate, bool interpol { char *p; const char *const arg_end = *arg + strlen(*arg); - unsigned int extra = interpolate ? 1 : 0; + unsigned extra = interpolate ? 1 : 0; const int off = interpolate ? 0 : 1; // Find the end of the string, skipping backslashed characters. @@ -4175,7 +4175,7 @@ int eval_interp_string(char **arg, typval_T *rettv, bool evaluate) const int quote = (uint8_t)(**arg); (*arg)++; - for (;;) { + while (true) { typval_T tv; // Get the string up to the matching quote or to a single '{'. // "arg" is advanced to either the quote or the '{'. @@ -4686,7 +4686,7 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) ht_stack_T *ht_stack = NULL; hashtab_T *cur_ht = ht; - for (;;) { + while (true) { if (!abort) { // Mark each item in the hashtab. If the item contains a hashtab // it is added to ht_stack, if it contains a list it is added to @@ -4724,7 +4724,7 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) list_stack_T *list_stack = NULL; list_T *cur_l = l; - for (;;) { + while (true) { // Mark each item in the list. If the item contains a hashtab // it is added to ht_stack, if it contains a list it is added to // list_stack. @@ -6514,7 +6514,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret } pos = fm->mark; // Vimscript behavior, only provide fnum if mark is global. - *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum: *ret_fnum; + *ret_fnum = ASCII_ISUPPER(mname) || ascii_isdigit(mname) ? fm->fnum : *ret_fnum; } if (pos.lnum != 0) { if (charcol) { @@ -7162,7 +7162,7 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) rc = snprintf(newval + xlen, newval_len - xlen, " ++ff=%s", - eap->force_ff == 'u' ? "unix" + eap->force_ff == 'u' ? "unix" : eap->force_ff == 'd' ? "dos" : "mac"); if (rc < 0) { goto error; -- cgit From 1659cd15bebfd8553bb91f24d833789451705b03 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 13:54:55 +0800 Subject: vim-patch:8.2.0987: Vim9: cannot assign to [var; var] Problem: Vim9: cannot assign to [var; var]. Solution: Assign rest of items to a list. https://github.com/vim/vim/commit/9af78769eeae0318e07aa8b6af4d6e2244481ca7 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1dfa853fd3..18fadaa157 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3683,9 +3683,6 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo n1 = (int)len; } if (range) { - list_T *l; - listitem_T *item; - if (n2 < 0) { n2 = (int)len + n2; } else if (n2 >= len) { @@ -3694,12 +3691,7 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { n2 = -1; } - l = tv_list_alloc(n2 - n1 + 1); - item = tv_list_find(rettv->vval.v_list, n1); - while (n1++ <= n2) { - tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); - item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item); - } + list_T *l = tv_list_slice(rettv->vval.v_list, n1, n2); tv_clear(rettv); tv_list_set_ret(rettv, l); } else { -- cgit From f4043e290e10007e51596a489a5ef444e28e5de8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 14:03:32 +0800 Subject: vim-patch:8.2.1463: Vim9: list slice not supported yet Problem: Vim9: list slice not supported yet. Solution: Add support for list slicing. https://github.com/vim/vim/commit/ed5918771fcf9877d8445e74c62ab1ce6b8e28c1 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 40 +++++++++------------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18fadaa157..e72287031d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3470,7 +3470,7 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo bool empty1 = false; bool empty2 = false; ptrdiff_t len = -1; - int range = false; + bool range = false; const char *key = NULL; switch (rettv->v_type) { @@ -3667,37 +3667,15 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo } break; case VAR_LIST: - len = tv_list_len(rettv->vval.v_list); - if (n1 < 0) { - n1 = (int)len + n1; - } - if (!empty1 && (n1 < 0 || n1 >= len)) { - // For a range we allow invalid values and return an empty - // list. A list index out of range is an error. - if (!range) { - if (verbose) { - semsg(_(e_listidx), (int64_t)n1); - } - return FAIL; - } - n1 = (int)len; + if (empty1) { + n1 = 0; } - if (range) { - if (n2 < 0) { - n2 = (int)len + n2; - } else if (n2 >= len) { - n2 = (int)len - 1; - } - if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { - n2 = -1; - } - list_T *l = tv_list_slice(rettv->vval.v_list, n1, n2); - tv_clear(rettv); - tv_list_set_ret(rettv, l); - } else { - tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, (int)n1)), &var1); - tv_clear(rettv); - *rettv = var1; + if (empty2) { + n2 = -1; + } + if (tv_list_slice_or_index(rettv->vval.v_list, + range, n1, n2, rettv, verbose) == FAIL) { + return FAIL; } break; case VAR_DICT: { -- cgit From 7ac63906eae92b5d31fc7e380ef05b8bce73ad8b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 14:15:54 +0800 Subject: vim-patch:8.2.1466: Vim9: cannot index or slice a variable with type "any" Problem: Vim9: cannot index or slice a variable with type "any". Solution: Add runtime index and slice. https://github.com/vim/vim/commit/cc673e746ab98566556ff964d7a76f2fb46d7f84 Omit E1024 and E1062: Vim9 script only. Omit string_slice() and char_idx2byte(): Vim9 script only. Remove the first tv_is_luafunc() check because it always returns false. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 361 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 193 insertions(+), 168 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e72287031d..941bb8b2b3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -88,7 +88,10 @@ static const char *e_missbrac = N_("E111: Missing ']'"); static const char *e_list_end = N_("E697: Missing end of List ']': %s"); -static const char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); +static const char *e_cannot_slice_dictionary + = N_("E719: cannot slice a Dictionary"); +static const char e_cannot_index_special_variable[] + = N_("E909: Cannot index a special variable"); static const char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); static const char *e_write2 = N_("E80: Error while writing: %s"); @@ -1438,7 +1441,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (*p == ':') { if (lp->ll_tv->v_type == VAR_DICT) { if (!quiet) { - emsg(_(e_dictrange)); + emsg(_(e_cannot_slice_dictionary)); } tv_clear(&var1); return NULL; @@ -3469,39 +3472,12 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); bool empty1 = false; bool empty2 = false; - ptrdiff_t len = -1; bool range = false; const char *key = NULL; + ptrdiff_t keylen = -1; - switch (rettv->v_type) { - case VAR_FUNC: - case VAR_PARTIAL: - if (verbose) { - emsg(_("E695: Cannot index a Funcref")); - } - return FAIL; - case VAR_FLOAT: - if (verbose) { - emsg(_(e_float_as_string)); - } + if (check_can_index(rettv, evaluate, verbose) == FAIL) { return FAIL; - case VAR_BOOL: - case VAR_SPECIAL: - if (verbose) { - emsg(_("E909: Cannot index a special variable")); - } - return FAIL; - case VAR_UNKNOWN: - if (evaluate) { - return FAIL; - } - FALLTHROUGH; - case VAR_STRING: - case VAR_NUMBER: - case VAR_LIST: - case VAR_DICT: - case VAR_BLOB: - break; } typval_T var1 = TV_INITIAL_VALUE; @@ -3509,11 +3485,11 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo if (**arg == '.') { // dict.name key = *arg + 1; - for (len = 0; eval_isdictc(key[len]); len++) {} - if (len == 0) { + for (keylen = 0; eval_isdictc(key[keylen]); keylen++) {} + if (keylen == 0) { return FAIL; } - *arg = skipwhite(key + len); + *arg = skipwhite(key + keylen); } else { // something[idx] // @@ -3565,165 +3541,214 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo } if (evaluate) { - int n2 = 0; - int n1 = 0; - if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) { - n1 = (int)tv_get_number(&var1); + int res = eval_index_inner(rettv, range, + empty1 ? NULL : &var1, empty2 ? NULL : &var2, + key, keylen, verbose); + if (!empty1) { tv_clear(&var1); } if (range) { - if (empty2) { - n2 = -1; - } else { - n2 = (int)tv_get_number(&var2); - tv_clear(&var2); - } + tv_clear(&var2); } + return res; + } + return OK; +} - switch (rettv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - const char *const s = tv_get_string(rettv); - char *v; - len = (ptrdiff_t)strlen(s); - if (range) { - // The resulting variable is a substring. If the indexes - // are out of range the result is empty. - if (n1 < 0) { - n1 = (int)len + n1; - if (n1 < 0) { - n1 = 0; - } - } - if (n2 < 0) { - n2 = (int)len + n2; - } else if (n2 >= len) { - n2 = (int)len; - } - if (n1 >= len || n2 < 0 || n1 > n2) { - v = NULL; - } else { - v = xmemdupz(s + n1, (size_t)n2 - (size_t)n1 + 1); - } - } else { - // The resulting variable is a string of a single - // character. If the index is too big or negative the - // result is empty. - if (n1 >= len || n1 < 0) { - v = NULL; - } else { - v = xmemdupz(s + n1, 1); - } +/// Check if "rettv" can have an [index] or [sli:ce] +static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) +{ + switch (rettv->v_type) { + case VAR_FUNC: + case VAR_PARTIAL: + if (verbose) { + emsg(_("E695: Cannot index a Funcref")); + } + return FAIL; + case VAR_FLOAT: + if (verbose) { + emsg(_(e_float_as_string)); + } + return FAIL; + case VAR_BOOL: + case VAR_SPECIAL: + if (verbose) { + emsg(_(e_cannot_index_special_variable)); + } + return FAIL; + case VAR_UNKNOWN: + if (evaluate) { + emsg(_(e_cannot_index_special_variable)); + return FAIL; + } + FALLTHROUGH; + case VAR_STRING: + case VAR_NUMBER: + case VAR_LIST: + case VAR_DICT: + case VAR_BLOB: + break; + } + return OK; +} + +/// Apply index or range to "rettv". +/// "var1" is the first index, NULL for [:expr]. +/// "var2" is the second index, NULL for [expr] and [expr: ] +/// Alternatively, "key" is not NULL, then key[keylen] is the dict index. +static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typval_T *var2, + const char *key, ptrdiff_t keylen, bool verbose) +{ + int n1 = 0; + int n2 = 0; + if (var1 != NULL && rettv->v_type != VAR_DICT) { + n1 = (int)tv_get_number(var1); + } + + if (is_range) { + if (rettv->v_type == VAR_DICT) { + if (verbose) { + emsg(_(e_cannot_slice_dictionary)); } - tv_clear(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = v; - break; + return FAIL; } - case VAR_BLOB: - len = tv_blob_len(rettv->vval.v_blob); - if (range) { - // The resulting variable is a sub-blob. If the indexes - // are out of range the result is empty. - if (n1 < 0) { - n1 = (int)len + n1; - if (n1 < 0) { - n1 = 0; - } - } - if (n2 < 0) { - n2 = (int)len + n2; - } else if (n2 >= len) { - n2 = (int)len - 1; - } - if (n1 >= len || n2 < 0 || n1 > n2) { - tv_clear(rettv); - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = NULL; - } else { - blob_T *const blob = tv_blob_alloc(); - ga_grow(&blob->bv_ga, n2 - n1 + 1); - blob->bv_ga.ga_len = n2 - n1 + 1; - for (long i = n1; i <= n2; i++) { - tv_blob_set(blob, (int)(i - n1), tv_blob_get(rettv->vval.v_blob, (int)i)); - } - tv_clear(rettv); - tv_blob_set_ret(rettv, blob); - } - } else { - // The resulting variable is a byte value. - // If the index is too big or negative that is an error. + if (var2 == NULL) { + n2 = -1; + } else { + n2 = (int)tv_get_number(var2); + } + } + + switch (rettv->v_type) { + case VAR_BOOL: + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_UNKNOWN: + break; // Not evaluating, skipping over subscript + case VAR_NUMBER: + case VAR_STRING: { + const char *const s = tv_get_string(rettv); + char *v; + int len = (int)strlen(s); + if (is_range) { + // The resulting variable is a substring. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; if (n1 < 0) { - n1 = (int)len + n1; - } - if (n1 < len && n1 >= 0) { - const int v = (int)tv_blob_get(rettv->vval.v_blob, n1); - tv_clear(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = v; - } else { - semsg(_(e_blobidx), (int64_t)n1); + n1 = 0; } } - break; - case VAR_LIST: - if (empty1) { - n1 = 0; + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len; } - if (empty2) { - n2 = -1; + if (n1 >= len || n2 < 0 || n1 > n2) { + v = NULL; + } else { + v = xmemdupz(s + n1, (size_t)n2 - (size_t)n1 + 1); } - if (tv_list_slice_or_index(rettv->vval.v_list, - range, n1, n2, rettv, verbose) == FAIL) { - return FAIL; + } else { + // The resulting variable is a string of a single + // character. If the index is too big or negative the + // result is empty. + if (n1 >= len || n1 < 0) { + v = NULL; + } else { + v = xmemdupz(s + n1, 1); } - break; - case VAR_DICT: { - if (range) { - if (verbose) { - emsg(_(e_dictrange)); - } - if (len == -1) { - tv_clear(&var1); + } + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = v; + break; + } + case VAR_BLOB: { + int len = tv_blob_len(rettv->vval.v_blob); + if (is_range) { + // The resulting variable is a sub-blob. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; } - return FAIL; } - - if (len == -1) { - key = tv_get_string_chk(&var1); - if (key == NULL) { - tv_clear(&var1); - return FAIL; + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + tv_clear(rettv); + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } else { + blob_T *const blob = tv_blob_alloc(); + ga_grow(&blob->bv_ga, n2 - n1 + 1); + blob->bv_ga.ga_len = n2 - n1 + 1; + for (int i = n1; i <= n2; i++) { + tv_blob_set(blob, i - n1, tv_blob_get(rettv->vval.v_blob, i)); } + tv_clear(rettv); + tv_blob_set_ret(rettv, blob); } - - dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, len); - - if (item == NULL && verbose) { - semsg(_(e_dictkey), key); + } else { + // The resulting variable is a byte value. + // If the index is too big or negative that is an error. + if (n1 < 0) { + n1 = len + n1; } - if (len == -1) { - tv_clear(&var1); + if (n1 < len && n1 >= 0) { + const int v = (int)tv_blob_get(rettv->vval.v_blob, n1); + tv_clear(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = v; + } else { + semsg(_(e_blobidx), (int64_t)n1); } - if (item == NULL || tv_is_luafunc(&item->di_tv)) { + } + break; + } + case VAR_LIST: + if (var1 == NULL) { + n1 = 0; + } + if (var2 == NULL) { + n2 = -1; + } + if (tv_list_slice_or_index(rettv->vval.v_list, + is_range, n1, n2, rettv, verbose) == FAIL) { + return FAIL; + } + break; + case VAR_DICT: { + if (key == NULL) { + key = tv_get_string_chk(var1); + if (key == NULL) { return FAIL; } + } - tv_copy(&item->di_tv, &var1); - tv_clear(rettv); - *rettv = var1; - break; + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, keylen); + + if (item == NULL && verbose) { + semsg(_(e_dictkey), key); } - case VAR_BOOL: - case VAR_SPECIAL: - case VAR_FUNC: - case VAR_FLOAT: - case VAR_PARTIAL: - case VAR_UNKNOWN: - break; // Not evaluating, skipping over subscript + if (item == NULL || tv_is_luafunc(&item->di_tv)) { + return FAIL; } - } + typval_T tmp; + tv_copy(&item->di_tv, &tmp); + tv_clear(rettv); + *rettv = tmp; + break; + } + } return OK; } -- cgit From dd3d857c390bee1f467eda9ec82c29948672bbff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 17:10:58 +0800 Subject: vim-patch:8.2.1461: Vim9: string indexes are counted in bytes Problem: Vim9: string indexes are counted in bytes. Solution: Use character indexes. (closes vim/vim#6574) https://github.com/vim/vim/commit/e3c37d8ebf9dbbf210fde4a5fb28eb1f2a492a34 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 941bb8b2b3..b06c102e2c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7264,6 +7264,27 @@ int check_luafunc_name(const char *const str, const bool paren) return (int)(p - str); } +/// Return the character "str[index]" where "index" is the character index. If +/// "index" is out of range NULL is returned. +char *char_from_string(char *str, varnumber_T index) +{ + size_t nbyte = 0; + varnumber_T nchar = index; + + if (str == NULL || index < 0) { + return NULL; + } + size_t slen = strlen(str); + while (nchar > 0 && nbyte < slen) { + nbyte += (size_t)utf_ptr2len(str + nbyte); + nchar--; + } + if (nbyte >= slen) { + return NULL; + } + return xstrnsave(str + nbyte, (size_t)utf_ptr2len(str + nbyte)); +} + /// Handle: /// - expr[expr], expr[expr:expr] subscript /// - ".name" lookup -- cgit From f5cefd70e4f8796e82e6e42b6f50fa6fad0c36c3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 17:16:31 +0800 Subject: vim-patch:8.2.1462: Vim9: string slice not supported yet Problem: Vim9: string slice not supported yet. Solution: Add support for string slicing. https://github.com/vim/vim/commit/11107bab7ead9124f46a7ddf6aa3bb66b43a8246 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b06c102e2c..0a41ffd4bf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7285,6 +7285,58 @@ char *char_from_string(char *str, varnumber_T index) return xstrnsave(str + nbyte, (size_t)utf_ptr2len(str + nbyte)); } +/// Get the byte index for character index "idx" in string "str" with length +/// "str_len". +/// If going over the end return "str_len". +/// If "idx" is negative count from the end, -1 is the last character. +/// When going over the start return zero. +static size_t char_idx2byte(char *str, size_t str_len, varnumber_T idx) +{ + varnumber_T nchar = idx; + size_t nbyte = 0; + + if (nchar >= 0) { + while (nchar > 0 && nbyte < str_len) { + nbyte += (size_t)utf_ptr2len(str + nbyte); + nchar--; + } + } else { + nbyte = str_len; + while (nchar < 0 && nbyte > 0) { + nbyte--; + nbyte -= (size_t)utf_head_off(str, str + nbyte); + nchar++; + } + } + return nbyte; +} + +/// Return the slice "str[first:last]" using character indexes. +/// Return NULL when the result is empty. +char *string_slice(char *str, varnumber_T first, varnumber_T last) +{ + if (str == NULL) { + return NULL; + } + size_t slen = strlen(str); + size_t start_byte = char_idx2byte(str, slen, first); + size_t end_byte; + if (last == -1) { + end_byte = slen; + } else { + end_byte = char_idx2byte(str, slen, last); + if (end_byte < slen) { + // end index is inclusive + end_byte += (size_t)utf_ptr2len(str + end_byte); + } + } + + if (start_byte >= slen || end_byte <= start_byte) { + return NULL; + } + return xstrnsave(str + start_byte, end_byte - start_byte); +} + /// Handle: /// - expr[expr], expr[expr:expr] subscript /// - ".name" lookup -- cgit From 62351ff3d2fba336f09569d844a7b6f7f36a078d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 17:20:48 +0800 Subject: vim-patch:8.2.1466: Vim9: cannot index or slice a variable with type "any" Problem: Vim9: cannot index or slice a variable with type "any". Solution: Add runtime index and slice. https://github.com/vim/vim/commit/cc673e746ab98566556ff964d7a76f2fb46d7f84 Missing changes from the last PR. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0a41ffd4bf..a14b098caf 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7289,8 +7289,8 @@ char *char_from_string(char *str, varnumber_T index) /// "str_len". /// If going over the end return "str_len". /// If "idx" is negative count from the end, -1 is the last character. -/// When going over the start return zero. -static size_t char_idx2byte(char *str, size_t str_len, varnumber_T idx) +/// When going over the start return -1. +static ssize_t char_idx2byte(char *str, size_t str_len, varnumber_T idx) { varnumber_T nchar = idx; size_t nbyte = 0; @@ -7307,8 +7307,11 @@ static size_t char_idx2byte(char *str, size_t str_len, varnumber_T idx) nbyte -= (size_t)utf_head_off(str, str + nbyte); nchar++; } + if (nchar < 0) { + return -1; + } } - return nbyte; + return (ssize_t)nbyte; } /// Return the slice "str[first:last]" using character indexes. @@ -7319,22 +7322,25 @@ char *string_slice(char *str, varnumber_T first, varnumber_T last) return NULL; } size_t slen = strlen(str); - size_t start_byte = char_idx2byte(str, slen, first); - size_t end_byte; + ssize_t start_byte = char_idx2byte(str, slen, first); + if (start_byte < 0) { + start_byte = 0; // first index very negative: use zero + } + ssize_t end_byte; if (last == -1) { - end_byte = slen; + end_byte = (ssize_t)slen; } else { end_byte = char_idx2byte(str, slen, last); - if (end_byte < slen) { + if (end_byte >= 0 && end_byte < (ssize_t)slen) { // end index is inclusive - end_byte += (size_t)utf_ptr2len(str + end_byte); + end_byte += utf_ptr2len(str + end_byte); } } - if (start_byte >= slen || end_byte <= start_byte) { + if (start_byte >= (ssize_t)slen || end_byte <= start_byte) { return NULL; } - return xstrnsave(str + start_byte, end_byte - start_byte); + return xstrnsave(str + start_byte, (size_t)(end_byte - start_byte)); } /// Handle: -- cgit From b441dafdf53e367c7d43177274bd781c5c73e6e0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 16:46:38 +0800 Subject: vim-patch:8.2.2344: using inclusive index for slice is not always desired Problem: Using inclusive index for slice is not always desired. Solution: Add the slice() method, which has an exclusive index. (closes vim/vim#7408) https://github.com/vim/vim/commit/6601b62943a19d4f8818c3638440663d67a17b6a Cherry-pick a line in docs added later. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 79 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 25 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a14b098caf..930cda4bd4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1692,7 +1692,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool lp->ll_n2 = tv_blob_len(lp->ll_blob) - 1; } - if (tv_blob_set_range(lp->ll_blob, (int)lp->ll_n1, (int)lp->ll_n2, rettv) == FAIL) { + if (tv_blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2, rettv) == FAIL) { return; } } else { @@ -3542,7 +3542,7 @@ static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, boo if (evaluate) { int res = eval_index_inner(rettv, range, - empty1 ? NULL : &var1, empty2 ? NULL : &var2, + empty1 ? NULL : &var1, empty2 ? NULL : &var2, false, key, keylen, verbose); if (!empty1) { tv_clear(&var1); @@ -3592,17 +3592,31 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) return OK; } +/// slice() function +void f_slice(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + if (check_can_index(argvars, true, false) == OK) { + tv_copy(argvars, rettv); + eval_index_inner(rettv, true, argvars + 1, + argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2, + true, NULL, 0, false); + } +} + /// Apply index or range to "rettv". -/// "var1" is the first index, NULL for [:expr]. -/// "var2" is the second index, NULL for [expr] and [expr: ] +/// +/// @param var1 the first index, NULL for [:expr]. +/// @param var2 the second index, NULL for [expr] and [expr: ] +/// @param exclusive true for slice(): second index is exclusive, use character +/// index for string. /// Alternatively, "key" is not NULL, then key[keylen] is the dict index. static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typval_T *var2, - const char *key, ptrdiff_t keylen, bool verbose) + bool exclusive, const char *key, ptrdiff_t keylen, bool verbose) { - int n1 = 0; - int n2 = 0; + varnumber_T n1 = 0; + varnumber_T n2 = 0; if (var1 != NULL && rettv->v_type != VAR_DICT) { - n1 = (int)tv_get_number(var1); + n1 = tv_get_number(var1); } if (is_range) { @@ -3612,10 +3626,10 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv } return FAIL; } - if (var2 == NULL) { - n2 = -1; + if (var2 != NULL) { + n2 = tv_get_number(var2); } else { - n2 = (int)tv_get_number(var2); + n2 = VARNUMBER_MAX; } } @@ -3632,7 +3646,13 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv const char *const s = tv_get_string(rettv); char *v; int len = (int)strlen(s); - if (is_range) { + if (exclusive) { + if (is_range) { + v = string_slice(s, n1, n2, exclusive); + } else { + v = char_from_string(s, n1); + } + } else if (is_range) { // The resulting variable is a substring. If the indexes // are out of range the result is empty. if (n1 < 0) { @@ -3646,6 +3666,9 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv } else if (n2 >= len) { n2 = len; } + if (exclusive) { + n2--; + } if (n1 >= len || n2 < 0 || n1 > n2) { v = NULL; } else { @@ -3680,7 +3703,10 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv if (n2 < 0) { n2 = len + n2; } else if (n2 >= len) { - n2 = len - 1; + n2 = len - (exclusive ? 0 : 1); + } + if (exclusive) { + n2--; } if (n1 >= len || n2 < 0 || n1 > n2) { tv_clear(rettv); @@ -3688,10 +3714,10 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv rettv->vval.v_blob = NULL; } else { blob_T *const blob = tv_blob_alloc(); - ga_grow(&blob->bv_ga, n2 - n1 + 1); - blob->bv_ga.ga_len = n2 - n1 + 1; - for (int i = n1; i <= n2; i++) { - tv_blob_set(blob, i - n1, tv_blob_get(rettv->vval.v_blob, i)); + ga_grow(&blob->bv_ga, (int)(n2 - n1 + 1)); + blob->bv_ga.ga_len = (int)(n2 - n1 + 1); + for (int i = (int)n1; i <= (int)n2; i++) { + tv_blob_set(blob, i - (int)n1, tv_blob_get(rettv->vval.v_blob, i)); } tv_clear(rettv); tv_blob_set_ret(rettv, blob); @@ -3703,7 +3729,7 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv n1 = len + n1; } if (n1 < len && n1 >= 0) { - const int v = (int)tv_blob_get(rettv->vval.v_blob, n1); + const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)n1); tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = v; @@ -3718,10 +3744,10 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv n1 = 0; } if (var2 == NULL) { - n2 = -1; + n2 = VARNUMBER_MAX; } if (tv_list_slice_or_index(rettv->vval.v_list, - is_range, n1, n2, rettv, verbose) == FAIL) { + is_range, n1, n2, exclusive, rettv, verbose) == FAIL) { return FAIL; } break; @@ -7266,7 +7292,7 @@ int check_luafunc_name(const char *const str, const bool paren) /// Return the character "str[index]" where "index" is the character index. If /// "index" is out of range NULL is returned. -char *char_from_string(char *str, varnumber_T index) +char *char_from_string(const char *str, varnumber_T index) { size_t nbyte = 0; varnumber_T nchar = index; @@ -7290,7 +7316,7 @@ char *char_from_string(char *str, varnumber_T index) /// If going over the end return "str_len". /// If "idx" is negative count from the end, -1 is the last character. /// When going over the start return -1. -static ssize_t char_idx2byte(char *str, size_t str_len, varnumber_T idx) +static ssize_t char_idx2byte(const char *str, size_t str_len, varnumber_T idx) { varnumber_T nchar = idx; size_t nbyte = 0; @@ -7315,8 +7341,11 @@ static ssize_t char_idx2byte(char *str, size_t str_len, varnumber_T idx) } /// Return the slice "str[first:last]" using character indexes. +/// +/// @param exclusive true for slice(). +/// /// Return NULL when the result is empty. -char *string_slice(char *str, varnumber_T first, varnumber_T last) +char *string_slice(const char *str, varnumber_T first, varnumber_T last, bool exclusive) { if (str == NULL) { return NULL; @@ -7327,11 +7356,11 @@ char *string_slice(char *str, varnumber_T first, varnumber_T last) start_byte = 0; // first index very negative: use zero } ssize_t end_byte; - if (last == -1) { + if ((last == -1 && !exclusive) || last == VARNUMBER_MAX) { end_byte = (ssize_t)slen; } else { end_byte = char_idx2byte(str, slen, last); - if (end_byte >= 0 && end_byte < (ssize_t)slen) { + if (!exclusive && end_byte >= 0 && end_byte < (ssize_t)slen) { // end index is inclusive end_byte += utf_ptr2len(str + end_byte); } -- cgit From 8752da89b83281426e81e5c4a392308848f4bfb6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 17:51:14 +0800 Subject: vim-patch:8.2.2756: Vim9: blob index and slice not implemented yet Problem: Vim9: blob index and slice not implemented yet. Solution: Implement blob index and slice. https://github.com/vim/vim/commit/cfc3023cb6ce5aaec13f49bc4b821feb05e3fb03 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 55 ++++++------------------------------------------------- 1 file changed, 6 insertions(+), 49 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 930cda4bd4..c0756c1ed6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3641,6 +3641,7 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv case VAR_PARTIAL: case VAR_UNKNOWN: break; // Not evaluating, skipping over subscript + case VAR_NUMBER: case VAR_STRING: { const char *const s = tv_get_string(rettv); @@ -3689,56 +3690,11 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv rettv->vval.v_string = v; break; } - case VAR_BLOB: { - int len = tv_blob_len(rettv->vval.v_blob); - if (is_range) { - // The resulting variable is a sub-blob. If the indexes - // are out of range the result is empty. - if (n1 < 0) { - n1 = len + n1; - if (n1 < 0) { - n1 = 0; - } - } - if (n2 < 0) { - n2 = len + n2; - } else if (n2 >= len) { - n2 = len - (exclusive ? 0 : 1); - } - if (exclusive) { - n2--; - } - if (n1 >= len || n2 < 0 || n1 > n2) { - tv_clear(rettv); - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = NULL; - } else { - blob_T *const blob = tv_blob_alloc(); - ga_grow(&blob->bv_ga, (int)(n2 - n1 + 1)); - blob->bv_ga.ga_len = (int)(n2 - n1 + 1); - for (int i = (int)n1; i <= (int)n2; i++) { - tv_blob_set(blob, i - (int)n1, tv_blob_get(rettv->vval.v_blob, i)); - } - tv_clear(rettv); - tv_blob_set_ret(rettv, blob); - } - } else { - // The resulting variable is a byte value. - // If the index is too big or negative that is an error. - if (n1 < 0) { - n1 = len + n1; - } - if (n1 < len && n1 >= 0) { - const int v = (int)tv_blob_get(rettv->vval.v_blob, (int)n1); - tv_clear(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = v; - } else { - semsg(_(e_blobidx), (int64_t)n1); - } - } + + case VAR_BLOB: + tv_blob_slice_or_index(rettv->vval.v_blob, is_range, n1, n2, exclusive, rettv); break; - } + case VAR_LIST: if (var1 == NULL) { n1 = 0; @@ -3751,6 +3707,7 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv return FAIL; } break; + case VAR_DICT: { if (key == NULL) { key = tv_get_string_chk(var1); -- cgit From b16729f8162ce21a52f079c3849f5011b768d0ce Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 4 May 2023 23:17:43 +0800 Subject: vim-patch:8.2.1697: inconsistent capitalization of error messages (#23476) Problem: Inconsistent capitalization of error messages. Solution: Always start with a capital. https://github.com/vim/vim/commit/7707228aace9aff16434edf5377a354c6ad07316 Most of these errors are Vim9 script only. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c0756c1ed6..d9d4708b89 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -89,7 +89,7 @@ static const char *e_missbrac = N_("E111: Missing ']'"); static const char *e_list_end = N_("E697: Missing end of List ']': %s"); static const char *e_cannot_slice_dictionary - = N_("E719: cannot slice a Dictionary"); + = N_("E719: Cannot slice a Dictionary"); static const char e_cannot_index_special_variable[] = N_("E909: Cannot index a special variable"); static const char *e_nowhitespace -- cgit From 88cfb49bee3c9102082c7010acb92244e4ad1348 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 5 May 2023 07:14:39 +0800 Subject: vim-patch:8.2.4890: inconsistent capitalization in error messages Problem: Inconsistent capitalization in error messages. Solution: Make capitalization consistent. (Doug Kearns) https://github.com/vim/vim/commit/cf030578b26460643dca4a40e7f2e3bc19c749aa Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d9d4708b89..07f65bbf22 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -95,6 +95,8 @@ static const char e_cannot_index_special_variable[] static const char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); static const char *e_write2 = N_("E80: Error while writing: %s"); +static const char e_variable_nested_too_deep_for_making_copy[] + = N_("E698: Variable nested too deep for making a copy"); static const char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); static const char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); static const char e_dot_can_only_be_used_on_dictionary_str[] @@ -1613,7 +1615,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (lp->ll_li == NULL) { tv_clear(&var2); if (!quiet) { - semsg(_(e_listidx), (int64_t)lp->ll_n1); + semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n1); } return NULL; } @@ -1629,7 +1631,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const listitem_T *ni = tv_list_find(lp->ll_list, (int)lp->ll_n2); if (ni == NULL) { if (!quiet) { - semsg(_(e_listidx), (int64_t)lp->ll_n2); + semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); } return NULL; } @@ -1642,7 +1644,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const } if (lp->ll_n2 < lp->ll_n1) { if (!quiet) { - semsg(_(e_listidx), (int64_t)lp->ll_n2); + semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); } return NULL; } @@ -3567,7 +3569,7 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) return FAIL; case VAR_FLOAT: if (verbose) { - emsg(_(e_float_as_string)); + emsg(_(e_using_float_as_string)); } return FAIL; case VAR_BOOL: @@ -7692,7 +7694,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c int ret = OK; if (recurse >= DICT_MAXNEST) { - emsg(_("E698: variable nested too deep for making a copy")); + emsg(_(e_variable_nested_too_deep_for_making_copy)); return FAIL; } recurse++; -- cgit From aa5f3a7962c3b96d1a939a83bf2dacad72d8e898 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 5 May 2023 20:47:57 +0800 Subject: vim-patch:8.2.2094: when an expression fails getting next command may be wrong Problem: When an expression fails getting the next command may be wrong. Solution: Do not check for a next command after :eval fails. (closes vim/vim#7415) https://github.com/vim/vim/commit/d0fe620cbbf5f5e00446efa89893036265c5c302 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 07f65bbf22..c6a6ef6848 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2367,7 +2367,10 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) semsg(_(e_invexpr2), arg); } } - ret = FAIL; + + // Some of the expression may not have been consumed. Do not check for + // a next command to avoid more errors. + return FAIL; } if (eap != NULL) { -- cgit From ad7f9a701cf25f598cb8ee3d05ad0604fa9a9a65 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 5 May 2023 20:48:25 +0800 Subject: vim-patch:8.2.2141: a user command with try/catch may not catch an expression error Problem: A user command with try/catch may not catch an expression error. Solution: When an expression fails check for following "|". (closes vim/vim#7469) https://github.com/vim/vim/commit/8143a53c533bc7776c57e5db063d185bdd5750f3 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c6a6ef6848..f638b90caa 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2369,7 +2369,11 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) } // Some of the expression may not have been consumed. Do not check for - // a next command to avoid more errors. + // a next command to avoid more errors, unless "|" is following, which + // could only be a command separator. + if (eap != NULL && skipwhite(p)[0] == '|' && skipwhite(p)[1] != '|') { + eap->nextcmd = check_nextcmd(p); + } return FAIL; } -- cgit From 6b912dec8ef06535b164b0f4eb3d55775eb9f6a8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 6 May 2023 07:44:34 +0800 Subject: vim-patch:9.0.1508: catch does not work when lines are joined with a newline Problem: Catch does not work when lines are joined with a newline. Solution: Set "nextcmd" appropriately. (closes vim/vim#12348) https://github.com/vim/vim/commit/f2588b6fc90ba85b333ee8f747e8d1ebbc7e6300 --- src/nvim/eval.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f638b90caa..42e53cfdfc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2368,11 +2368,14 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) } } - // Some of the expression may not have been consumed. Do not check for - // a next command to avoid more errors, unless "|" is following, which - // could only be a command separator. - if (eap != NULL && skipwhite(p)[0] == '|' && skipwhite(p)[1] != '|') { - eap->nextcmd = check_nextcmd(p); + if (eap != NULL && p != NULL) { + // Some of the expression may not have been consumed. + // Only execute a next command if it cannot be a "||" operator. + // The next command may be "catch". + char *nextcmd = check_nextcmd(p); + if (nextcmd != NULL && *nextcmd != '|') { + eap->nextcmd = nextcmd; + } } return FAIL; } -- cgit From e8661133c533345e8d83a38b077e45922988fa90 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 6 May 2023 09:34:29 +0800 Subject: vim-patch:9.0.0904: various comment and indent flaws (#23498) Problem: Various comment and indent flaws. Solution: Improve comments and indenting. https://github.com/vim/vim/commit/88456cd3c49a3dd1fda17cf350daa9b8216b1aa6 Omit test_function_lists.vim change as that file is likely not applicable to Nvim due to the existence of Nvim-only functions. Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 42e53cfdfc..13299b0253 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1506,18 +1506,16 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const // g: dictionary). Disallow overwriting a builtin function. if (rettv != NULL && lp->ll_dict->dv_scope != 0) { char prevval; - int wrong; - if (len != -1) { prevval = key[len]; key[len] = NUL; } else { prevval = 0; // Avoid compiler warning. } - wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && tv_is_func(*rettv) - && var_wrong_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key)); + bool wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && tv_is_func(*rettv) + && var_wrong_func_name(key, lp->ll_di == NULL)) + || !valid_varname(key)); if (len != -1) { key[len] = prevval; } -- cgit From ca344b715b0755885fe1b45bd97db4f8bb1e1879 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 8 May 2023 09:49:40 +0800 Subject: vim-patch:8.2.2459: Coverity reports dead code (#23531) Problem: Coverity reports dead code. Solution: Remove the dead code. https://github.com/vim/vim/commit/8bead9a058907e7f10ad25893d8475d2d9dd173c Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 13299b0253..cb1f4d26fb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3677,9 +3677,6 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv } else if (n2 >= len) { n2 = len; } - if (exclusive) { - n2--; - } if (n1 >= len || n2 < 0 || n1 > n2) { v = NULL; } else { -- cgit From 84378c4dd56db9846b70a333530505a8048bd26e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 9 May 2023 09:18:21 +0800 Subject: test(old): remove python2 tests (#23547) Because python2 provider is no longer supported. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cb1f4d26fb..5c05b8faf7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8654,7 +8654,7 @@ bool eval_has_provider(const char *feat) return false; } - char name[32]; // Normalized: "python_compiled" => "python". + char name[32]; // Normalized: "python3_compiled" => "python3". snprintf(name, sizeof(name), "%s", feat); strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. -- cgit From e2fdd53d8c015913e8be4ff708fc3488558c8906 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 14 May 2023 18:45:56 +0200 Subject: refactor(map): avoid duplicated khash_t types for values This reduces the total number of khash_t instantiations from 22 to 8. Make the khash internal functions take the size of values as a runtime parameter. This is abstracted with typesafe Map containers which are still specialized for both key, value type. Introduce `Set(key)` type for when there is no value. Refactor shada.c to use Map/Set instead of khash directly. This requires `map_ref` operation to be more flexible. Return pointers to both key and value, plus an indicator for new_item. As a bonus, `map_key` is now redundant. Instead of Map(cstr_t, FileMarks), use a pointer map as the FileMarks struct is humongous. Make `event_strings` actually work like an intern pool instead of wtf it was doing before. --- src/nvim/eval.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5c05b8faf7..1b21f107a8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4520,7 +4520,7 @@ bool garbage_collect(bool testing) // Channels { Channel *data; - map_foreach_value(&channels, data, { + pmap_foreach_value(&channels, data, { set_ref_in_callback_reader(&data->on_data, copyID, NULL, NULL); set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); @@ -4530,7 +4530,7 @@ bool garbage_collect(bool testing) // Timers { timer_T *timer; - map_foreach_value(&timers, timer, { + pmap_foreach_value(&timers, timer, { set_ref_in_callback(&timer->callback, copyID, NULL, NULL); }) } @@ -5986,7 +5986,7 @@ void add_timer_info_all(typval_T *rettv) { tv_list_alloc_ret(rettv, map_size(&timers)); timer_T *timer; - map_foreach_value(&timers, timer, { + pmap_foreach_value(&timers, timer, { if (!timer->stopped || timer->refcount > 1) { add_timer_info(rettv, timer); } @@ -6084,7 +6084,7 @@ static void timer_close_cb(TimeWatcher *tw, void *data) timer_T *timer = (timer_T *)data; multiqueue_free(timer->tw.events); callback_free(&timer->callback); - pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id); + pmap_del(uint64_t)(&timers, (uint64_t)timer->timer_id, NULL); timer_decref(timer); } @@ -6098,7 +6098,7 @@ static void timer_decref(timer_T *timer) void timer_stop_all(void) { timer_T *timer; - map_foreach_value(&timers, timer, { + pmap_foreach_value(&timers, timer, { timer_stop(timer); }) } -- cgit From cfd4fdfea4d0e68ea50ad412b88b5289ded6fd6f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Tue, 23 May 2023 14:25:10 +0600 Subject: refactor(api): new helper macros Adds new API helper macros `CSTR_AS_OBJ()`, `STATIC_CSTR_AS_OBJ()`, and `STATIC_CSTR_TO_OBJ()`, which cleans up a lot of the current code. These macros will also be used extensively in the upcoming option refactor PRs because then API Objects will be used to get/set options. This PR also modifies pre-existing code to use old API helper macros like `CSTR_TO_OBJ()` to make them cleaner. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1b21f107a8..52924bf9a5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8712,7 +8712,7 @@ void ex_checkhealth(exarg_T *eap) { Error err = ERROR_INIT; MAXSIZE_TEMP_ARRAY(args, 1); - ADD_C(args, STRING_OBJ(cstr_as_string(eap->arg))); + ADD_C(args, CSTR_AS_OBJ(eap->arg)); NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err); if (!ERROR_SET(&err)) { return; -- cgit From 9f3c4c152664b21593636a59ce21e74ab7000b20 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 2 Jun 2023 08:48:49 +0800 Subject: vim-patch:9.0.1597: cursor ends up below the window after a put (#23873) Problem: Cursor ends up below the window after a put. Solution: Mark w_crow and w_botline invalid when changing the cursor line. (closes vim/vim#12465) https://github.com/vim/vim/commit/8509014adda188ee8bdf6a2e123fbf15a91b29d2 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 52924bf9a5..118c1d3012 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6511,6 +6511,10 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret pos.coladd = 0; if (name[0] == 'w' && dollar_lnum) { + // the "w_valid" flags are not reset when moving the cursor, but they + // do matter for update_topline() and validate_botline(). + check_cursor_moved(curwin); + pos.col = 0; if (name[1] == '0') { // "w0": first visible line update_topline(curwin); -- cgit From b3d5138fd0066fda26ef7724a542ae45eb42fc84 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Wed, 7 Jun 2023 06:05:16 +0600 Subject: refactor(options): remove `getoption_T` and introduce `OptVal` (#23850) Removes the `getoption_T` struct and also introduces the `OptVal` struct to unify the methods of getting/setting different option value types. This is the first of many PRs to reduce code duplication in the Vim option code as well as to make options easier to maintain. It also increases the flexibility and extensibility of options. Which opens the door for things like Array and Dictionary options. --- src/nvim/eval.c | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 118c1d3012..72005b1f35 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -58,6 +58,7 @@ #include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/optionstr.h" #include "nvim/os/fileio.h" #include "nvim/os/fs_defs.h" @@ -3770,38 +3771,38 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua return OK; } - long numval; - char *stringval; int ret = OK; - + bool hidden; char c = *option_end; *option_end = NUL; - getoption_T opt_type = get_option_value(*arg, &numval, - rettv == NULL ? NULL : &stringval, NULL, scope); + OptVal value = get_option_value(*arg, NULL, scope, &hidden); - if (opt_type == gov_unknown) { - if (rettv != NULL) { + if (rettv != NULL) { + switch (value.type) { + case kOptValTypeNil: semsg(_("E113: Unknown option: %s"), *arg); - } - ret = FAIL; - } else if (rettv != NULL) { - if (opt_type == gov_hidden_string) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - } else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number) { + ret = FAIL; + break; + case kOptValTypeBoolean: rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - } else if (opt_type == gov_bool || opt_type == gov_number) { + rettv->vval.v_number = value.data.boolean; + break; + case kOptValTypeNumber: rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = numval; - } else { // string option + rettv->vval.v_number = value.data.number; + break; + case kOptValTypeString: rettv->v_type = VAR_STRING; - rettv->vval.v_string = stringval; + rettv->vval.v_string = value.data.string.data; + break; + } + } else { + // Value isn't being used, free it. + optval_free(value); + + if (value.type == kOptValTypeNil || (working && hidden)) { + ret = FAIL; } - } else if (working && (opt_type == gov_hidden_bool - || opt_type == gov_hidden_number - || opt_type == gov_hidden_string)) { - ret = FAIL; } *option_end = c; // put back for error messages @@ -8516,7 +8517,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", 0L, save_cpo, 0); + set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); } free_string_option(save_cpo); } -- cgit From 971049f3189d4769db5e9896cd19b555719b3d09 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 7 Jun 2023 09:26:46 +0800 Subject: revert: "refactor: eliminate `autocmd_fname_full` global" This reverts commit 82cd0be2eaf71c0476e15c66ba3e83c76896d407. --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 72005b1f35..ec5437a0b2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8609,6 +8609,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo .es_entry = ((estack_T *)exestack.ga_data)[exestack.ga_len - 1], .autocmd_fname = autocmd_fname, .autocmd_match = autocmd_match, + .autocmd_fname_full = autocmd_fname_full, .autocmd_bufnr = autocmd_bufnr, .funccalp = (void *)get_current_funccal() }; -- cgit From a0cb53eca7a04326dd857cf33fac1154aff7773a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 12 Jun 2023 15:38:53 +0800 Subject: vim-patch:8.2.2533: Vim9: cannot use a range with :unlet Problem: Vim9: cannot use a range with :unlet. Solution: Implement ISN_UNLETRANGE. https://github.com/vim/vim/commit/5b5ae29bd3d7b832b6f15320430f7f191e0abd1f Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ec5437a0b2..50da0a8657 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1604,13 +1604,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); - if (lp->ll_li == NULL) { - if (lp->ll_n1 < 0) { - lp->ll_n1 = 0; - lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); - } - } + lp->ll_li = tv_list_find_index(lp->ll_list, &lp->ll_n1); if (lp->ll_li == NULL) { tv_clear(&var2); if (!quiet) { -- cgit From d83a196716e2834238cd2d28b2a6f5c8899a506f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 12 Jun 2023 18:10:35 +0800 Subject: vim-patch:8.2.3852: Vim9: not enough tests Problem: Vim9: not enough tests. Solution: Also run existing tests for Vim9 script. Make errors more consistent. https://github.com/vim/vim/commit/f47c5a8e2d8eda7c2c8a9cccf9568eb56c03a0cf Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 50da0a8657..b2729b0e67 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -89,20 +89,25 @@ static const char *e_missbrac = N_("E111: Missing ']'"); static const char *e_list_end = N_("E697: Missing end of List ']': %s"); -static const char *e_cannot_slice_dictionary +static const char e_cannot_slice_dictionary[] = N_("E719: Cannot slice a Dictionary"); static const char e_cannot_index_special_variable[] = N_("E909: Cannot index a special variable"); static const char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); static const char *e_write2 = N_("E80: Error while writing: %s"); +static const char e_cannot_index_a_funcref[] + = N_("E695: Cannot index a Funcref"); static const char e_variable_nested_too_deep_for_making_copy[] = N_("E698: Variable nested too deep for making a copy"); -static const char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); -static const char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); +static const char e_string_list_or_blob_required[] + = N_("E1098: String, List or Blob required"); +static const char e_expression_too_recursive_str[] + = N_("E1169: Expression too recursive: %s"); static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); -static const char e_empty_function_name[] = N_("E1192: Empty function name"); +static const char e_empty_function_name[] + = N_("E1192: Empty function name"); static char * const namespace_char = "abglstvw"; @@ -3567,7 +3572,7 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) case VAR_FUNC: case VAR_PARTIAL: if (verbose) { - emsg(_("E695: Cannot index a Funcref")); + emsg(_(e_cannot_index_a_funcref)); } return FAIL; case VAR_FLOAT: -- cgit From aa92a04beeed1fb10a82edd6a245e5783cd771c1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 12 Jun 2023 20:35:01 +0800 Subject: vim-patch:8.2.3332: Vim9: cannot assign to range in list Problem: Vim9: cannot assign to range in list. Solution: Implement overwriting a list range. https://github.com/vim/vim/commit/4f0884d6e24d1d45ec83fd86b372b403177d3298 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 79 +++++---------------------------------------------------- 1 file changed, 6 insertions(+), 73 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b2729b0e67..771824af71 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1609,12 +1609,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = tv_list_find_index(lp->ll_list, &lp->ll_n1); + lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); if (lp->ll_li == NULL) { tv_clear(&var2); - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n1); - } return NULL; } @@ -1625,25 +1622,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. tv_clear(&var2); - if (lp->ll_n2 < 0) { - listitem_T *ni = tv_list_find(lp->ll_list, (int)lp->ll_n2); - if (ni == NULL) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); - } - return NULL; - } - lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); - } - - // Check that lp->ll_n2 isn't before lp->ll_n1. - if (lp->ll_n1 < 0) { - lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); - } - if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); - } + if (tv_list_check_range_index_two(lp->ll_list, + &lp->ll_n1, lp->ll_li, + &lp->ll_n2, quiet) == FAIL) { return NULL; } } @@ -1672,7 +1653,6 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool const char *op) { int cc; - listitem_T *ri; dictitem_T *di; if (lp->ll_tv == NULL) { @@ -1733,60 +1713,13 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool lp->ll_name, TV_CSTRING)) { // Skip } else if (lp->ll_range) { - listitem_T *ll_li = lp->ll_li; - int ll_n1 = (int)lp->ll_n1; - if (is_const) { emsg(_("E996: Cannot lock a range")); return; } - // Check whether any of the list items is locked - for (ri = tv_list_first(rettv->vval.v_list); - ri != NULL && ll_li != NULL;) { - if (value_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, - TV_CSTRING)) { - return; - } - ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { - break; - } - ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); - ll_n1++; - } - - // Assign the List values to the list items. - for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) { - if (op != NULL && *op != '=') { - eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); - } else { - tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); - tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); - } - ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { - break; - } - assert(lp->ll_li != NULL); - if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { - // Need to add an empty item. - tv_list_append_number(lp->ll_list, 0); - // ll_li may have become invalid after append, don’t use it. - lp->ll_li = tv_list_last(lp->ll_list); // Valid again. - } else { - lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - } - lp->ll_n1++; - } - if (ri != NULL) { - emsg(_("E710: List value has more items than target")); - } else if (lp->ll_empty2 - ? (lp->ll_li != NULL - && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) - : lp->ll_n1 != lp->ll_n2) { - emsg(_("E711: List value has not enough items")); - } + (void)tv_list_assign_range(lp->ll_list, rettv->vval.v_list, + lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name); } else { typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; -- cgit From 2f17ef1fc4b96cf1106fd95ba090d34a2e4b977b Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 22 Jun 2023 04:09:14 -0700 Subject: fix(messages): use "Vimscript" instead of "VimL" #24111 followup to #24109 fix #16150 --- src/nvim/eval.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 771824af71..1c9fdef20d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5364,7 +5364,7 @@ theend: xfree(trans_name); } -/// Get the line number from VimL object +/// Get the line number from Vimscript object /// /// @note Unlike tv_get_lnum(), this one supports only "$" special string. /// @@ -5517,9 +5517,9 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const cmd_silent = cmd_silent_save; } -/// Builds a process argument vector from a VimL object (typval_T). +/// Builds a process argument vector from a Vimscript object (typval_T). /// -/// @param[in] cmd_tv VimL object +/// @param[in] cmd_tv Vimscript object /// @param[out] cmd Returns the command or executable name. /// @param[out] executable Returns `false` if argv[0] is not executable. /// @@ -6192,7 +6192,7 @@ int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg) /// @param[out] len Length of the resulting string or -1 on error. /// @param[in] endnl If true, the output will end in a newline (if a list). /// @param[in] crlf If true, list items will be joined with CRLF (if a list). -/// @returns an allocated string if `tv` represents a VimL string, list, or +/// @returns an allocated string if `tv` represents a Vimscript string, list, or /// number; NULL otherwise. char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crlf) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL @@ -6338,7 +6338,7 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx) return (int)(t - str); } -/// Translate a VimL object into a position +/// Translate a Vimscript object into a position /// /// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid /// type. -- cgit From 8cbb2477cf70ea29105e3df17308e6d6a067c8e6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 09:43:00 +0800 Subject: vim-patch:8.2.1969: Vim9: map() may change the list or dict item type Problem: Vim9: map() may change the list or dict item type. Solution: Add mapnew(). https://github.com/vim/vim/commit/ea696852e7abcdebaf7f17a7f23dc90df1f5e2ed Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 179 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 138 insertions(+), 41 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1c9fdef20d..d93b32fdcc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -295,6 +295,13 @@ static partial_T *vvlua_partial; /// v: hashtab #define vimvarht vimvardict.dv_hashtab +/// Enum used by filter(), map() and mapnew() +typedef enum { + FILTERMAP_FILTER, + FILTERMAP_MAP, + FILTERMAP_MAPNEW, +} filtermap_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -1838,7 +1845,7 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const // Make a copy, so that the iteration still works when the // blob is changed. - tv_blob_copy(&tv, &btv); + tv_blob_copy(tv.vval.v_blob, &btv); fi->fi_blob = btv.vval.v_blob; } tv_clear(&tv); @@ -5018,33 +5025,51 @@ void assert_error(garray_T *gap) } /// Implementation of map() and filter(). -void filter_map(typval_T *argvars, typval_T *rettv, int map) +static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) { list_T *l = NULL; dict_T *d = NULL; blob_T *b = NULL; int rem = false; - char *ermsg = map ? "map()" : "filter()"; - const char *const arg_errmsg = (map + const char *const ermsg = (filtermap == FILTERMAP_MAP ? "map()" + : filtermap == FILTERMAP_MAPNEW ? "mapnew()" : "filter()"); + const char *const arg_errmsg = (filtermap == FILTERMAP_MAP ? N_("map() argument") - : N_("filter() argument")); + : (filtermap == FILTERMAP_MAPNEW + ? N_("mapnew() argument") + : N_("filter() argument"))); - // Always return the first argument, also on failure. - tv_copy(&argvars[0], rettv); + // map() and filter() return the first argument, also on failure. + if (filtermap != FILTERMAP_MAPNEW) { + tv_copy(&argvars[0], rettv); + } if (argvars[0].v_type == VAR_BLOB) { + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } if ((b = argvars[0].vval.v_blob) == NULL) { return; } } else if (argvars[0].v_type == VAR_LIST) { + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } if ((l = argvars[0].vval.v_list) == NULL - || (!map + || (filtermap == FILTERMAP_FILTER && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = NULL; + } if ((d = argvars[0].vval.v_dict) == NULL - || (!map && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { + || (filtermap == FILTERMAP_FILTER + && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; } } else { @@ -5069,10 +5094,17 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) typval_T save_key; prepare_vimvar(VV_KEY, &save_key); if (argvars[0].v_type == VAR_DICT) { + const VarLockStatus prev_lock = d->dv_lock; + dict_T *d_ret = NULL; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_dict_alloc_ret(rettv); + d_ret = rettv->vval.v_dict; + } + vimvars[VV_KEY].vv_type = VAR_STRING; - const VarLockStatus prev_lock = d->dv_lock; - if (map && d->dv_lock == VAR_UNLOCKED) { + if (filtermap != FILTERMAP_FILTER && d->dv_lock == VAR_UNLOCKED) { d->dv_lock = VAR_LOCKED; } hashtab_T *ht = &d->dv_hashtab; @@ -5083,19 +5115,34 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) todo--; dictitem_T *di = TV_DICT_HI2DI(hi); - if (map + if (filtermap != FILTERMAP_FILTER && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { break; } vimvars[VV_KEY].vv_str = xstrdup(di->di_key); - int r = filter_map_one(&di->di_tv, expr, map, &rem); + typval_T newtv; + int r = filter_map_one(&di->di_tv, expr, filtermap, &newtv, &rem); tv_clear(&vimvars[VV_KEY].vv_tv); if (r == FAIL || did_emsg) { + tv_clear(&newtv); break; } - if (!map && rem) { + if (filtermap == FILTERMAP_MAP) { + // map(): replace the dict item value + tv_clear(&di->di_tv); + newtv.v_lock = VAR_UNLOCKED; + di->di_tv = newtv; + } else if (filtermap == FILTERMAP_MAPNEW) { + // mapnew(): add the item value to the new dict + r = tv_dict_add_tv(d_ret, di->di_key, strlen(di->di_key), &newtv); + tv_clear(&newtv); + if (r == FAIL) { + break; + } + } else if (filtermap == FILTERMAP_FILTER && rem) { + // filter(false): remove the item from the dict if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { break; @@ -5107,24 +5154,36 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) hash_unlock(ht); d->dv_lock = prev_lock; } else if (argvars[0].v_type == VAR_BLOB) { + blob_T *b_ret = b; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_blob_copy(b, rettv); + b_ret = rettv->vval.v_blob; + } + vimvars[VV_KEY].vv_type = VAR_NUMBER; for (int i = 0; i < b->bv_ga.ga_len; i++) { - typval_T tv; - tv.v_type = VAR_NUMBER; const varnumber_T val = tv_blob_get(b, i); - tv.vval.v_number = val; + typval_T tv = { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = val, + }; vimvars[VV_KEY].vv_nr = idx; - if (filter_map_one(&tv, expr, map, &rem) == FAIL || did_emsg) { + typval_T newtv; + if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + || did_emsg) { break; } - if (tv.v_type != VAR_NUMBER && tv.v_type != VAR_BOOL) { + if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { + tv_clear(&newtv); emsg(_(e_invalblob)); - return; + break; } - if (map) { - if (tv.vval.v_number != val) { - tv_blob_set(b, i, (uint8_t)tv.vval.v_number); + if (filtermap != FILTERMAP_FILTER) { + if (newtv.vval.v_number != val) { + tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); } } else if (rem) { char *const p = argvars[0].vval.v_blob->bv_ga.ga_data; @@ -5136,24 +5195,42 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } } else { assert(argvars[0].v_type == VAR_LIST); - vimvars[VV_KEY].vv_type = VAR_NUMBER; const VarLockStatus prev_lock = tv_list_locked(l); - if (map && tv_list_locked(l) == VAR_UNLOCKED) { + list_T *l_ret = NULL; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_list_alloc_ret(rettv, kListLenUnknown); + l_ret = rettv->vval.v_list; + } + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + if (filtermap != FILTERMAP_FILTER && tv_list_locked(l) == VAR_UNLOCKED) { tv_list_set_lock(l, VAR_LOCKED); } for (listitem_T *li = tv_list_first(l); li != NULL;) { - if (map + if (filtermap != FILTERMAP_FILTER && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, TV_TRANSLATE)) { break; } vimvars[VV_KEY].vv_nr = idx; - if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL + typval_T newtv; + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL || did_emsg) { break; } - if (!map && rem) { + if (filtermap == FILTERMAP_MAP) { + // map(): replace the list item value + tv_clear(TV_LIST_ITEM_TV(li)); + newtv.v_lock = VAR_UNLOCKED; + *TV_LIST_ITEM_TV(li) = newtv; + } else if (filtermap == FILTERMAP_MAPNEW) { + // mapnew(): append the list item value + tv_list_append_owned_tv(l_ret, newtv); + } + if (filtermap == FILTERMAP_FILTER && rem) { li = tv_list_item_remove(l, li); } else { li = TV_LIST_ITEM_NEXT(l, li); @@ -5170,30 +5247,32 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } } -static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) - FUNC_ATTR_NONNULL_ARG(1, 2) +/// Handle one item for map() and filter(). +/// Sets v:val to "tv". Caller must set v:key. +/// +/// @param tv original value +/// @param expr callback +/// @param newtv for map() an mapnew(): new value +/// @param remp for filter(): remove flag +static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filtermap, + typval_T *newtv, int *remp) + FUNC_ATTR_NONNULL_ALL { - typval_T rettv; typval_T argv[3]; int retval = FAIL; tv_copy(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; - if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) { + if (eval_expr_typval(expr, argv, 2, newtv) == FAIL) { goto theend; } - if (map) { - // map(): replace the list item value. - tv_clear(tv); - rettv.v_lock = VAR_UNLOCKED; - *tv = rettv; - } else { + if (filtermap == FILTERMAP_FILTER) { bool error = false; // filter(): when expr is zero remove the item - *remp = (tv_get_number_chk(&rettv, &error) == 0); - tv_clear(&rettv); + *remp = (tv_get_number_chk(newtv, &error) == 0); + tv_clear(newtv); // On type error, nothing has been removed; return FAIL to stop the // loop. The error message was given by tv_get_number_chk(). if (error) { @@ -5206,6 +5285,24 @@ theend: return retval; } +/// "filter()" function +void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + filter_map(argvars, rettv, FILTERMAP_FILTER); +} + +/// "map()" function +void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + filter_map(argvars, rettv, FILTERMAP_MAP); +} + +/// "mapnew()" function +void f_mapnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + filter_map(argvars, rettv, FILTERMAP_MAPNEW); +} + /// "function()" function /// "funcref()" function void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) @@ -7682,7 +7779,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c } break; case VAR_BLOB: - tv_blob_copy(from, to); + tv_blob_copy(from->vval.v_blob, to); break; case VAR_DICT: to->v_type = VAR_DICT; -- cgit From bc0c7dde17ab71b39db2a03086dec5694e083a40 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 10:44:32 +0800 Subject: vim-patch:8.2.1971: memory leak when map() fails Problem: Memory leak when map() fails. Solution: Clear the typval. https://github.com/vim/vim/commit/c56936e2ba4fd219f56aab616fcb283132455697 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d93b32fdcc..1b4630085c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5217,8 +5217,11 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap } vimvars[VV_KEY].vv_nr = idx; typval_T newtv; - if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL - || did_emsg) { + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL) { + break; + } + if (did_emsg) { + tv_clear(&newtv); break; } if (filtermap == FILTERMAP_MAP) { -- cgit From 90ad3c8f17d4d0be4149ea28c49d1e6980640d38 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 10:56:03 +0800 Subject: vim-patch:8.2.2075: error for const argument to mapnew() Problem: Error for const argument to mapnew(). Solution: Don't give an error. (closes vim/vim#7400) https://github.com/vim/vim/commit/57cf4973a283941c92744554474b2c52ce892fd1 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1b4630085c..23ff012a9c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5115,7 +5115,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap todo--; dictitem_T *di = TV_DICT_HI2DI(hi); - if (filtermap != FILTERMAP_FILTER + if (filtermap == FILTERMAP_MAP && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { break; @@ -5210,7 +5210,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap tv_list_set_lock(l, VAR_LOCKED); } for (listitem_T *li = tv_list_first(l); li != NULL;) { - if (filtermap != FILTERMAP_FILTER + if (filtermap == FILTERMAP_MAP && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, TV_TRANSLATE)) { break; -- cgit From d9e7dad13945ce1f4110c37d5388b5c532dec0d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 12:37:52 +0800 Subject: vim-patch:8.2.3818: cannot filter or map characters in a string Problem: Cannot filter or map characters in a string. Solution: Make filter() and map() work on a string. (Naruhiko Nishino, closes vim/vim#9327) https://github.com/vim/vim/commit/c479ce032f5d4d14bab9e479acbf42d758879893 Co-authored-by: rbtnn --- src/nvim/eval.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 23ff012a9c..4e3638b93f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -108,6 +108,8 @@ static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); static const char e_empty_function_name[] = N_("E1192: Empty function name"); +static char e_argument_of_str_must_be_list_string_dictionary_or_blob[] + = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); static char * const namespace_char = "abglstvw"; @@ -5072,8 +5074,11 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; } + } else if (argvars[0].v_type == VAR_STRING) { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; } else { - semsg(_(e_listdictblobarg), ermsg); + semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob), ermsg); return; } @@ -5193,6 +5198,51 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap } idx++; } + } else if (argvars[0].v_type == VAR_STRING) { + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + int len; + for (const char *p = tv_get_string(&argvars[0]); *p != NUL; p += len) { + len = utfc_ptr2len(p); + + typval_T tv = { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrnsave(p, (size_t)len), + }; + + vimvars[VV_KEY].vv_nr = idx; + typval_T newtv; + if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + || did_emsg) { + break; + } + if (did_emsg) { + tv_clear(&newtv); + tv_clear(&tv); + break; + } else if (filtermap != FILTERMAP_FILTER) { + if (newtv.v_type != VAR_STRING) { + tv_clear(&newtv); + tv_clear(&tv); + emsg(_(e_stringreq)); + break; + } else { + ga_concat(&ga, newtv.vval.v_string); + } + } else if (!rem) { + ga_concat(&ga, tv.vval.v_string); + } + + tv_clear(&newtv); + tv_clear(&tv); + + idx++; + } + ga_append(&ga, NUL); + rettv->vval.v_string = ga.ga_data; } else { assert(argvars[0].v_type == VAR_LIST); -- cgit From faef577e90cf17699bd7bc206b0fb64af65334d2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 12:59:16 +0800 Subject: vim-patch:8.2.3822: leaking memory in map() and filter(), no string in Vim9 Problem: Leaking memory in map() and filter(), cannot use a string argument in Vim9 script. Solution: Fix the leak, adjust the argument check, also run the tests as Vim9 script. (Yegappan Lakshmanan, closes vim/vim#9354) https://github.com/vim/vim/commit/2d877599ee1cede063ef4abe3a2272e67c116238 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4e3638b93f..50f5fa578c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5042,7 +5042,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap : N_("filter() argument"))); // map() and filter() return the first argument, also on failure. - if (filtermap != FILTERMAP_MAPNEW) { + if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING) { tv_copy(&argvars[0], rettv); } -- cgit From db64280be5ff866b80ffa80569838688bfd7caa5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 13:01:45 +0800 Subject: vim-patch:8.2.3908: cannot use a script-local function for 'foldtext' Problem: Cannot use a script-local function for 'foldtext'. Solution: Expand "s:" and "". (Yegappan Lakshmanan, closes vim/vim#9411) https://github.com/vim/vim/commit/27708e6c7b6f444fd599f3dc5015336b002b874d Cherry-pick test_filter_map.vim change from patch 8.2.3871. Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 50f5fa578c..dab0896d75 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5217,9 +5217,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap typval_T newtv; if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL || did_emsg) { - break; - } - if (did_emsg) { tv_clear(&newtv); tv_clear(&tv); break; -- cgit From c5576fcc8003280489c0aa0323a966e6de33e31f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 13:41:43 +0800 Subject: vim-patch:8.2.3848: cannot use reduce() for a string Problem: Cannot use reduce() for a string. Solution: Make reduce() work with a string. (Naruhiko Nishino, closes vim/vim#9366) https://github.com/vim/vim/commit/0ccb5842f5fb103763d106c7aa364d758343c35a Omit tv_get_first_char() as it doesn't really save much code. Co-authored-by: rbtnn --- src/nvim/eval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dab0896d75..b73744f21b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -108,7 +108,7 @@ static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); static const char e_empty_function_name[] = N_("E1192: Empty function name"); -static char e_argument_of_str_must_be_list_string_dictionary_or_blob[] +static const char e_argument_of_str_must_be_list_string_dictionary_or_blob[] = N_("E1250: Argument of %s must be a List, String, Dictionary or Blob"); static char * const namespace_char = "abglstvw"; @@ -5206,7 +5206,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap int len; for (const char *p = tv_get_string(&argvars[0]); *p != NUL; p += len) { len = utfc_ptr2len(p); - typval_T tv = { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, -- cgit From b193674b4a1dce1b348489fa13dd42254b9a3ebb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 14:12:24 +0800 Subject: vim-patch:partial:8.2.3849: functions implementing reduce and map are too long Problem: Functions implementing reduce and map are too long. Solution: Use a function for each type of value. Add a few more test cases and add to the help. (Yegappan Lakshmanan, closes vim/vim#9370) https://github.com/vim/vim/commit/389b72196e6aaeafe3f907c73d271f2c6b931140 Partial port as this doesn't include handling for non-materialized List. Co-authored-by: Yegappan Lakshmanan --- src/nvim/eval.c | 481 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 256 insertions(+), 225 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b73744f21b..589bee6f84 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5026,15 +5026,253 @@ void assert_error(garray_T *gap) tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len); } +/// Implementation of map() and filter() for a Dict. +static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name, + const char *arg_errmsg, typval_T *expr, typval_T *rettv) +{ + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = NULL; + } + if (d == NULL + || (filtermap == FILTERMAP_FILTER + && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { + return; + } + + const VarLockStatus prev_lock = d->dv_lock; + dict_T *d_ret = NULL; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_dict_alloc_ret(rettv); + d_ret = rettv->vval.v_dict; + } + + vimvars[VV_KEY].vv_type = VAR_STRING; + + if (filtermap != FILTERMAP_FILTER && d->dv_lock == VAR_UNLOCKED) { + d->dv_lock = VAR_LOCKED; + } + hash_lock(&d->dv_hashtab); + TV_DICT_ITER(d, di, { + if (filtermap == FILTERMAP_MAP + && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { + break; + } + + vimvars[VV_KEY].vv_str = xstrdup(di->di_key); + typval_T newtv; + bool rem; + int r = filter_map_one(&di->di_tv, expr, filtermap, &newtv, &rem); + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { + tv_clear(&newtv); + break; + } + if (filtermap == FILTERMAP_MAP) { + // map(): replace the dict item value + tv_clear(&di->di_tv); + newtv.v_lock = VAR_UNLOCKED; + di->di_tv = newtv; + } else if (filtermap == FILTERMAP_MAPNEW) { + // mapnew(): add the item value to the new dict + r = tv_dict_add_tv(d_ret, di->di_key, strlen(di->di_key), &newtv); + tv_clear(&newtv); + if (r == FAIL) { + break; + } + } else if (filtermap == FILTERMAP_FILTER && rem) { + // filter(false): remove the item from the dict + if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) + || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { + break; + } + tv_dict_item_remove(d, di); + } + }); + hash_unlock(&d->dv_hashtab); + d->dv_lock = prev_lock; +} + +/// Implementation of map() and filter() for a Blob. +static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, + typval_T *rettv) +{ + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } + blob_T *b; + if ((b = blob_arg) == NULL) { + return; + } + + blob_T *b_ret = b; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_blob_copy(b, rettv); + b_ret = rettv->vval.v_blob; + } + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + for (int i = 0, idx = 0; i < b->bv_ga.ga_len; i++) { + const varnumber_T val = tv_blob_get(b, i); + typval_T tv = { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = val, + }; + vimvars[VV_KEY].vv_nr = idx; + typval_T newtv; + bool rem; + if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + || did_emsg) { + break; + } + if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { + tv_clear(&newtv); + emsg(_(e_invalblob)); + break; + } + if (filtermap != FILTERMAP_FILTER) { + if (newtv.vval.v_number != val) { + tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); + } + } else if (rem) { + char *const p = (char *)blob_arg->bv_ga.ga_data; + memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); + b->bv_ga.ga_len--; + i--; + } + idx++; + } +} + +/// Implementation of map() and filter() for a String. +static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr, + typval_T *rettv) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + int len = 0; + int idx = 0; + for (const char *p = str; *p != NUL; p += len) { + len = utfc_ptr2len(p); + typval_T tv = { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = xstrnsave(p, (size_t)len), + }; + + vimvars[VV_KEY].vv_nr = idx; + typval_T newtv; + bool rem; + if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL + || did_emsg) { + tv_clear(&newtv); + tv_clear(&tv); + break; + } else if (filtermap != FILTERMAP_FILTER) { + if (newtv.v_type != VAR_STRING) { + tv_clear(&newtv); + tv_clear(&tv); + emsg(_(e_stringreq)); + break; + } else { + ga_concat(&ga, newtv.vval.v_string); + } + } else if (!rem) { + ga_concat(&ga, tv.vval.v_string); + } + + tv_clear(&newtv); + tv_clear(&tv); + + idx++; + } + ga_append(&ga, NUL); + rettv->vval.v_string = ga.ga_data; +} + +/// Implementation of map() and filter() for a List. +static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name, + const char *arg_errmsg, typval_T *expr, typval_T *rettv) +{ + if (filtermap == FILTERMAP_MAPNEW) { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + } + if (l == NULL + || (filtermap == FILTERMAP_FILTER + && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { + return; + } + + const VarLockStatus prev_lock = tv_list_locked(l); + list_T *l_ret = NULL; + + if (filtermap == FILTERMAP_MAPNEW) { + tv_list_alloc_ret(rettv, kListLenUnknown); + l_ret = rettv->vval.v_list; + } + + vimvars[VV_KEY].vv_type = VAR_NUMBER; + + if (filtermap != FILTERMAP_FILTER && tv_list_locked(l) == VAR_UNLOCKED) { + tv_list_set_lock(l, VAR_LOCKED); + } + + int idx = 0; + for (listitem_T *li = tv_list_first(l); li != NULL;) { + if (filtermap == FILTERMAP_MAP + && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, TV_TRANSLATE)) { + break; + } + vimvars[VV_KEY].vv_nr = idx; + typval_T newtv; + bool rem; + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL) { + break; + } + if (did_emsg) { + tv_clear(&newtv); + break; + } + if (filtermap == FILTERMAP_MAP) { + // map(): replace the list item value + tv_clear(TV_LIST_ITEM_TV(li)); + newtv.v_lock = VAR_UNLOCKED; + *TV_LIST_ITEM_TV(li) = newtv; + } else if (filtermap == FILTERMAP_MAPNEW) { + // mapnew(): append the list item value + tv_list_append_owned_tv(l_ret, newtv); + } + if (filtermap == FILTERMAP_FILTER && rem) { + li = tv_list_item_remove(l, li); + } else { + li = TV_LIST_ITEM_NEXT(l, li); + } + idx++; + } + + tv_list_set_lock(l, prev_lock); +} + /// Implementation of map() and filter(). static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) { - list_T *l = NULL; - dict_T *d = NULL; - blob_T *b = NULL; - int rem = false; - const char *const ermsg = (filtermap == FILTERMAP_MAP ? "map()" - : filtermap == FILTERMAP_MAPNEW ? "mapnew()" : "filter()"); + const char *const func_name = (filtermap == FILTERMAP_MAP + ? "map()" + : (filtermap == FILTERMAP_MAPNEW + ? "mapnew()" + : "filter()")); const char *const arg_errmsg = (filtermap == FILTERMAP_MAP ? N_("map() argument") : (filtermap == FILTERMAP_MAPNEW @@ -5046,39 +5284,11 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap tv_copy(&argvars[0], rettv); } - if (argvars[0].v_type == VAR_BLOB) { - if (filtermap == FILTERMAP_MAPNEW) { - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = NULL; - } - if ((b = argvars[0].vval.v_blob) == NULL) { - return; - } - } else if (argvars[0].v_type == VAR_LIST) { - if (filtermap == FILTERMAP_MAPNEW) { - rettv->v_type = VAR_LIST; - rettv->vval.v_list = NULL; - } - if ((l = argvars[0].vval.v_list) == NULL - || (filtermap == FILTERMAP_FILTER - && value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { - return; - } - } else if (argvars[0].v_type == VAR_DICT) { - if (filtermap == FILTERMAP_MAPNEW) { - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = NULL; - } - if ((d = argvars[0].vval.v_dict) == NULL - || (filtermap == FILTERMAP_FILTER - && value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { - return; - } - } else if (argvars[0].v_type == VAR_STRING) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - } else { - semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob), ermsg); + if (argvars[0].v_type != VAR_BLOB + && argvars[0].v_type != VAR_LIST + && argvars[0].v_type != VAR_DICT + && argvars[0].v_type != VAR_STRING) { + semsg(_(e_argument_of_str_must_be_list_string_dictionary_or_blob), func_name); return; } @@ -5087,7 +5297,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap // message. Avoid a misleading error message for an empty string that // was not passed as argument. if (expr->v_type != VAR_UNKNOWN) { - int idx = 0; typval_T save_val; prepare_vimvar(VV_VAL, &save_val); @@ -5099,194 +5308,16 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap typval_T save_key; prepare_vimvar(VV_KEY, &save_key); if (argvars[0].v_type == VAR_DICT) { - const VarLockStatus prev_lock = d->dv_lock; - dict_T *d_ret = NULL; - - if (filtermap == FILTERMAP_MAPNEW) { - tv_dict_alloc_ret(rettv); - d_ret = rettv->vval.v_dict; - } - - vimvars[VV_KEY].vv_type = VAR_STRING; - - if (filtermap != FILTERMAP_FILTER && d->dv_lock == VAR_UNLOCKED) { - d->dv_lock = VAR_LOCKED; - } - hashtab_T *ht = &d->dv_hashtab; - hash_lock(ht); - int todo = (int)ht->ht_used; - for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - - dictitem_T *di = TV_DICT_HI2DI(hi); - if (filtermap == FILTERMAP_MAP - && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) - || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { - break; - } - - vimvars[VV_KEY].vv_str = xstrdup(di->di_key); - typval_T newtv; - int r = filter_map_one(&di->di_tv, expr, filtermap, &newtv, &rem); - tv_clear(&vimvars[VV_KEY].vv_tv); - if (r == FAIL || did_emsg) { - tv_clear(&newtv); - break; - } - if (filtermap == FILTERMAP_MAP) { - // map(): replace the dict item value - tv_clear(&di->di_tv); - newtv.v_lock = VAR_UNLOCKED; - di->di_tv = newtv; - } else if (filtermap == FILTERMAP_MAPNEW) { - // mapnew(): add the item value to the new dict - r = tv_dict_add_tv(d_ret, di->di_key, strlen(di->di_key), &newtv); - tv_clear(&newtv); - if (r == FAIL) { - break; - } - } else if (filtermap == FILTERMAP_FILTER && rem) { - // filter(false): remove the item from the dict - if (var_check_fixed(di->di_flags, arg_errmsg, TV_TRANSLATE) - || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE)) { - break; - } - tv_dict_item_remove(d, di); - } - } - } - hash_unlock(ht); - d->dv_lock = prev_lock; + filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name, + arg_errmsg, expr, rettv); } else if (argvars[0].v_type == VAR_BLOB) { - blob_T *b_ret = b; - - if (filtermap == FILTERMAP_MAPNEW) { - tv_blob_copy(b, rettv); - b_ret = rettv->vval.v_blob; - } - - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - for (int i = 0; i < b->bv_ga.ga_len; i++) { - const varnumber_T val = tv_blob_get(b, i); - typval_T tv = { - .v_type = VAR_NUMBER, - .v_lock = VAR_UNLOCKED, - .vval.v_number = val, - }; - vimvars[VV_KEY].vv_nr = idx; - typval_T newtv; - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL - || did_emsg) { - break; - } - if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) { - tv_clear(&newtv); - emsg(_(e_invalblob)); - break; - } - if (filtermap != FILTERMAP_FILTER) { - if (newtv.vval.v_number != val) { - tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number); - } - } else if (rem) { - char *const p = argvars[0].vval.v_blob->bv_ga.ga_data; - memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1)); - b->bv_ga.ga_len--; - i--; - } - idx++; - } + filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, rettv); } else if (argvars[0].v_type == VAR_STRING) { - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - garray_T ga; - ga_init(&ga, (int)sizeof(char), 80); - int len; - for (const char *p = tv_get_string(&argvars[0]); *p != NUL; p += len) { - len = utfc_ptr2len(p); - typval_T tv = { - .v_type = VAR_STRING, - .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrnsave(p, (size_t)len), - }; - - vimvars[VV_KEY].vv_nr = idx; - typval_T newtv; - if (filter_map_one(&tv, expr, filtermap, &newtv, &rem) == FAIL - || did_emsg) { - tv_clear(&newtv); - tv_clear(&tv); - break; - } else if (filtermap != FILTERMAP_FILTER) { - if (newtv.v_type != VAR_STRING) { - tv_clear(&newtv); - tv_clear(&tv); - emsg(_(e_stringreq)); - break; - } else { - ga_concat(&ga, newtv.vval.v_string); - } - } else if (!rem) { - ga_concat(&ga, tv.vval.v_string); - } - - tv_clear(&newtv); - tv_clear(&tv); - - idx++; - } - ga_append(&ga, NUL); - rettv->vval.v_string = ga.ga_data; + filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv); } else { assert(argvars[0].v_type == VAR_LIST); - - const VarLockStatus prev_lock = tv_list_locked(l); - list_T *l_ret = NULL; - - if (filtermap == FILTERMAP_MAPNEW) { - tv_list_alloc_ret(rettv, kListLenUnknown); - l_ret = rettv->vval.v_list; - } - - vimvars[VV_KEY].vv_type = VAR_NUMBER; - - if (filtermap != FILTERMAP_FILTER && tv_list_locked(l) == VAR_UNLOCKED) { - tv_list_set_lock(l, VAR_LOCKED); - } - for (listitem_T *li = tv_list_first(l); li != NULL;) { - if (filtermap == FILTERMAP_MAP - && value_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, - TV_TRANSLATE)) { - break; - } - vimvars[VV_KEY].vv_nr = idx; - typval_T newtv; - if (filter_map_one(TV_LIST_ITEM_TV(li), expr, filtermap, &newtv, &rem) == FAIL) { - break; - } - if (did_emsg) { - tv_clear(&newtv); - break; - } - if (filtermap == FILTERMAP_MAP) { - // map(): replace the list item value - tv_clear(TV_LIST_ITEM_TV(li)); - newtv.v_lock = VAR_UNLOCKED; - *TV_LIST_ITEM_TV(li) = newtv; - } else if (filtermap == FILTERMAP_MAPNEW) { - // mapnew(): append the list item value - tv_list_append_owned_tv(l_ret, newtv); - } - if (filtermap == FILTERMAP_FILTER && rem) { - li = tv_list_item_remove(l, li); - } else { - li = TV_LIST_ITEM_NEXT(l, li); - } - idx++; - } - tv_list_set_lock(l, prev_lock); + filter_map_list(argvars[0].vval.v_list, filtermap, func_name, + arg_errmsg, expr, rettv); } restore_vimvar(VV_KEY, &save_key); @@ -5304,7 +5335,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap /// @param newtv for map() an mapnew(): new value /// @param remp for filter(): remove flag static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filtermap, - typval_T *newtv, int *remp) + typval_T *newtv, bool *remp) FUNC_ATTR_NONNULL_ALL { typval_T argv[3]; -- cgit From 8ac9abd4fad5837c2cc7a858827efb2e53b852be Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Aug 2023 16:58:05 +0800 Subject: vim-patch:9.0.1416: crash when collection is modified when using filter() Problem: Crash when collection is modified when using filter(). Solution: Lock the list/dict/blob. (Ernie Rael, closes vim/vim#12183) https://github.com/vim/vim/commit/e6d40dcdc7227594935d2db01eca29f0e575dcee Co-authored-by: Ernie Rael --- src/nvim/eval.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 589bee6f84..9baf3209a2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5040,7 +5040,6 @@ static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_n return; } - const VarLockStatus prev_lock = d->dv_lock; dict_T *d_ret = NULL; if (filtermap == FILTERMAP_MAPNEW) { @@ -5050,7 +5049,8 @@ static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_n vimvars[VV_KEY].vv_type = VAR_STRING; - if (filtermap != FILTERMAP_FILTER && d->dv_lock == VAR_UNLOCKED) { + const VarLockStatus prev_lock = d->dv_lock; + if (d->dv_lock == VAR_UNLOCKED) { d->dv_lock = VAR_LOCKED; } hash_lock(&d->dv_hashtab); @@ -5060,7 +5060,6 @@ static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_n || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { break; } - vimvars[VV_KEY].vv_str = xstrdup(di->di_key); typval_T newtv; bool rem; @@ -5097,14 +5096,16 @@ static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_n /// Implementation of map() and filter() for a Blob. static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr, - typval_T *rettv) + const char *arg_errmsg, typval_T *rettv) { if (filtermap == FILTERMAP_MAPNEW) { rettv->v_type = VAR_BLOB; rettv->vval.v_blob = NULL; } - blob_T *b; - if ((b = blob_arg) == NULL) { + blob_T *b = blob_arg; + if (b == NULL + || (filtermap == FILTERMAP_FILTER + && value_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE))) { return; } @@ -5117,6 +5118,11 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e vimvars[VV_KEY].vv_type = VAR_NUMBER; + const VarLockStatus prev_lock = b->bv_lock; + if (b->bv_lock == 0) { + b->bv_lock = VAR_LOCKED; + } + for (int i = 0, idx = 0; i < b->bv_ga.ga_len; i++) { const varnumber_T val = tv_blob_get(b, i); typval_T tv = { @@ -5148,6 +5154,8 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e } idx++; } + + b->bv_lock = prev_lock; } /// Implementation of map() and filter() for a String. @@ -5215,7 +5223,6 @@ static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_n return; } - const VarLockStatus prev_lock = tv_list_locked(l); list_T *l_ret = NULL; if (filtermap == FILTERMAP_MAPNEW) { @@ -5225,7 +5232,8 @@ static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_n vimvars[VV_KEY].vv_type = VAR_NUMBER; - if (filtermap != FILTERMAP_FILTER && tv_list_locked(l) == VAR_UNLOCKED) { + const VarLockStatus prev_lock = tv_list_locked(l); + if (tv_list_locked(l) == VAR_UNLOCKED) { tv_list_set_lock(l, VAR_LOCKED); } @@ -5311,7 +5319,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name, arg_errmsg, expr, rettv); } else if (argvars[0].v_type == VAR_BLOB) { - filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, rettv); + filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, arg_errmsg, rettv); } else if (argvars[0].v_type == VAR_STRING) { filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv); } else { -- cgit From bb29ef40084e1cea1f35cbbcbac794f41f46d5f8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 18 Aug 2023 05:09:24 +0800 Subject: vim-patch:9.0.1723: Fix regression in {func} argument of reduce() Problem: Fix regression in {func} argument of reduce() Solution: pass function name as string again Before patch 9.0.0548, passing a string as {func} argument of reduce() is treated as a function name, but after patch 9.0.0548 it is treated as an expression instead, which is useless as reduce() doesn't set any v: variables. This PR restores the behavior of {func} before that patch. Also correct an emsg() call, as e_string_list_or_blob_required doesn't contain format specifiers. closes: vim/vim#12824 https://github.com/vim/vim/commit/ad0c442f1fcc6fe9c433777ee3e5b9e6addc6d69 --- src/nvim/eval.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9baf3209a2..08c5ef743b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -820,36 +820,44 @@ bool eval_expr_valid_arg(const typval_T *const tv) && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL)); } -int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) - FUNC_ATTR_NONNULL_ARG(1, 2, 4) +/// Evaluate an expression, which can be a function, partial or string. +/// Pass arguments "argv[argc]". +/// Return the result in "rettv" and OK or FAIL. +/// +/// @param want_func if true, treat a string as a function name, not an expression +int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int argc, + typval_T *rettv) + FUNC_ATTR_NONNULL_ALL { + char buf[NUMBUFLEN]; funcexe_T funcexe = FUNCEXE_INIT; - if (expr->v_type == VAR_FUNC) { - const char *const s = expr->vval.v_string; + if (expr->v_type == VAR_PARTIAL) { + partial_T *const partial = expr->vval.v_partial; + if (partial == NULL) { + return FAIL; + } + const char *const s = partial_name(partial); if (s == NULL || *s == NUL) { return FAIL; } funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } - } else if (expr->v_type == VAR_PARTIAL) { - partial_T *const partial = expr->vval.v_partial; - if (partial == NULL) { - return FAIL; - } - const char *const s = partial_name(partial); + } else if (expr->v_type == VAR_FUNC || want_func) { + const char *const s = (expr->v_type == VAR_FUNC + ? expr->vval.v_string + : tv_get_string_buf_chk(expr, buf)); if (s == NULL || *s == NUL) { return FAIL; } funcexe.fe_evaluate = true; - funcexe.fe_partial = partial; if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { return FAIL; } } else { - char buf[NUMBUFLEN]; char *s = (char *)tv_get_string_buf_chk(expr, buf); if (s == NULL) { return FAIL; @@ -874,7 +882,7 @@ bool eval_expr_to_bool(const typval_T *expr, bool *error) { typval_T argv, rettv; - if (eval_expr_typval(expr, &argv, 0, &rettv) == FAIL) { + if (eval_expr_typval(expr, false, &argv, 0, &rettv) == FAIL) { *error = true; return false; } @@ -5352,7 +5360,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filter tv_copy(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; - if (eval_expr_typval(expr, argv, 2, newtv) == FAIL) { + if (eval_expr_typval(expr, false, argv, 2, newtv) == FAIL) { goto theend; } if (filtermap == FILTERMAP_FILTER) { -- cgit From 64ccfdaafef56b451e3a5eed94367fad93978ec8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 21 Aug 2023 11:22:25 +0800 Subject: vim-patch:8.1.2047: cannot check the current state Problem: Cannot check the current state. Solution: Add the state() function. https://github.com/vim/vim/commit/0e57dd859ecb1e8a3b91509d2f4343e839340eb8 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 08c5ef743b..052d67b05f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5994,6 +5994,13 @@ bool callback_from_typval(Callback *const callback, const typval_T *const arg) return true; } +static int callback_depth = 0; + +int get_callback_depth(void) +{ + return callback_depth; +} + /// @return whether the callback could be called. bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) @@ -6041,7 +6048,11 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co funcexe.fe_lastline = curwin->w_cursor.lnum; funcexe.fe_evaluate = true; funcexe.fe_partial = partial; - return call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); + + callback_depth++; + int ret = call_func(name, -1, rettv, argcount_in, argvars_in, &funcexe); + callback_depth--; + return ret; } bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack, -- cgit From cefd774fac76b91f5368833555818c80c992c3b1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 24 Aug 2023 15:14:23 +0200 Subject: refactor(memline): distinguish mutating uses of ml_get_buf() ml_get_buf() takes a third parameters to indicate whether the caller wants to mutate the memline data in place. However the vast majority of the call sites is using this function just to specify a buffer but without any mutation. This makes it harder to grep for the places which actually perform mutation. Solution: Remove the bool param from ml_get_buf(). it now works like ml_get() except for a non-current buffer. Add a new ml_get_buf_mut() function for the mutating use-case, which can be grepped along with the other ml_replace() etc functions which can modify the memline. --- src/nvim/eval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 052d67b05f..e7a28cfe67 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6423,7 +6423,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crl buf_T *buf = buflist_findnr((int)tv->vval.v_number); if (buf) { for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + for (char *p = ml_get_buf(buf, lnum); *p != NUL; p++) { *len += 1; } *len += 1; @@ -6441,7 +6441,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crl char *ret = xmalloc((size_t)(*len) + 1); char *end = ret; for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (char *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + for (char *p = ml_get_buf(buf, lnum); *p != NUL; p++) { *end++ = (*p == '\n') ? NUL : *p; } *end++ = '\n'; @@ -6493,7 +6493,7 @@ int buf_byteidx_to_charidx(buf_T *buf, linenr_T lnum, int byteidx) lnum = buf->b_ml.ml_line_count; } - char *str = ml_get_buf(buf, lnum, false); + char *str = ml_get_buf(buf, lnum); if (*str == NUL) { return 0; @@ -6531,7 +6531,7 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx) lnum = buf->b_ml.ml_line_count; } - char *str = ml_get_buf(buf, lnum, false); + char *str = ml_get_buf(buf, lnum); // Convert the character offset to a byte offset char *t = str; -- cgit From 5970157e1d22fd5e05ae5d3bd949f807fb7a744c Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 17 May 2023 16:08:06 +0200 Subject: refactor(map): enhanced implementation, Clean Codeā„¢, etc etc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This involves two redesigns of the map.c implementations: 1. Change of macro style and code organization The old khash.h and map.c implementation used huge #define blocks with a lot of backslash line continuations. This instead uses the "implementation file" .c.h pattern. Such a file is meant to be included multiple times, with different macros set prior to inclusion as parameters. we already use this pattern e.g. for eval/typval_encode.c.h to implement different typval encoders reusing a similar structure. We can structure this code into two parts. one that only depends on key type and is enough to implement sets, and one which depends on both key and value to implement maps (as a wrapper around sets, with an added value[] array) 2. Separate the main hash buckets from the key / value arrays Change the hack buckets to only contain an index into separate key / value arrays This is a common pattern in modern, state of the art hashmap implementations. Even though this leads to one more allocated array, it is this often is a net reduction of memory consumption. Consider key+value consuming at least 12 bytes per pair. On average, we will have twice as many buckets per item. Thus old implementation: 2*12 = 24 bytes per item New implementation 1*12 + 2*4 = 20 bytes per item And the difference gets bigger with larger items. One might think we have pulled a fast one here, as wouldn't the average size of the new key/value arrays be 1.5 slots per items due to amortized grows? But remember, these arrays are fully dense, and thus the accessed memory, measured in _cache lines_, the unit which actually matters, will be the fully used memory but just rounded up to the nearest cache line boundary. This has some other interesting properties, such as an insert-only set/map will be fully ordered by insert only. Preserving this ordering in face of deletions is more tricky tho. As we currently don't use ordered maps, the "delete" operation maintains compactness of the item arrays in the simplest way by breaking the ordering. It would be possible to implement an order-preserving delete although at some cost, like allowing the items array to become non-dense until the next rehash. Finally, in face of these two major changes, all code used in khash.h has been integrated into map.c and friends. Given the heavy edits it makes no sense to "layer" the code into a vendored and a wrapper part. Rather, the layered cake follows the specialization depth: code shared for all maps, code specialized to a key type (and its equivalence relation), and finally code specialized to value+key type. --- src/nvim/eval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e7a28cfe67..9be4cea059 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4470,7 +4470,7 @@ bool garbage_collect(bool testing) // Channels { Channel *data; - pmap_foreach_value(&channels, data, { + map_foreach_value(&channels, data, { set_ref_in_callback_reader(&data->on_data, copyID, NULL, NULL); set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); @@ -4480,7 +4480,7 @@ bool garbage_collect(bool testing) // Timers { timer_T *timer; - pmap_foreach_value(&timers, timer, { + map_foreach_value(&timers, timer, { set_ref_in_callback(&timer->callback, copyID, NULL, NULL); }) } @@ -6123,7 +6123,7 @@ void add_timer_info_all(typval_T *rettv) { tv_list_alloc_ret(rettv, map_size(&timers)); timer_T *timer; - pmap_foreach_value(&timers, timer, { + map_foreach_value(&timers, timer, { if (!timer->stopped || timer->refcount > 1) { add_timer_info(rettv, timer); } @@ -6235,7 +6235,7 @@ static void timer_decref(timer_T *timer) void timer_stop_all(void) { timer_T *timer; - pmap_foreach_value(&timers, timer, { + map_foreach_value(&timers, timer, { timer_stop(timer); }) } -- cgit From ac0fda2e46708f474a810e16407341c9f7b47dfe Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 17 Sep 2023 10:59:36 +0800 Subject: vim-patch:8.2.2356: Vim9: ":put =expr" does not handle a list properly Problem: Vim9: ":put =expr" does not handle a list properly. Solution: Use the same logic as eval_to_string_eap(). (closes vim/vim#7684) https://github.com/vim/vim/commit/883cf97f109d2ff281cf77f7b2e3bb44aced7cb3 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9be4cea059..fed5b01538 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -947,6 +947,34 @@ int skip_expr(char **pp, evalarg_T *const evalarg) return res; } +/// Convert "tv" to a string. +/// +/// @param convert when true convert a List into a sequence of lines and convert +/// a Float to a String. +/// +/// @return an allocated string. +static char *typval2string(typval_T *tv, bool convert) +{ + if (convert && tv->v_type == VAR_LIST) { + garray_T ga; + ga_init(&ga, (int)sizeof(char), 80); + if (tv->vval.v_list != NULL) { + tv_list_join(&ga, tv->vval.v_list, "\n"); + if (tv_list_len(tv->vval.v_list) > 0) { + ga_append(&ga, NL); + } + } + ga_append(&ga, NUL); + return (char *)ga.ga_data; + } + if (convert && tv->v_type == VAR_FLOAT) { + char numbuf[NUMBUFLEN]; + vim_snprintf(numbuf, NUMBUFLEN, "%g", tv->vval.v_float); + return xstrdup(numbuf); + } + return xstrdup(tv_get_string(tv)); +} + /// Top level evaluation function, returning a string. /// /// @param convert when true convert a List into a sequence of lines and convert @@ -957,28 +985,11 @@ char *eval_to_string(char *arg, bool convert) { typval_T tv; char *retval; - garray_T ga; if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = NULL; } else { - if (convert && tv.v_type == VAR_LIST) { - ga_init(&ga, (int)sizeof(char), 80); - if (tv.vval.v_list != NULL) { - tv_list_join(&ga, tv.vval.v_list, "\n"); - if (tv_list_len(tv.vval.v_list) > 0) { - ga_append(&ga, NL); - } - } - ga_append(&ga, NUL); - retval = (char *)ga.ga_data; - } else if (convert && tv.v_type == VAR_FLOAT) { - char numbuf[NUMBUFLEN]; - vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float); - retval = xstrdup(numbuf); - } else { - retval = xstrdup(tv_get_string(&tv)); - } + retval = typval2string(&tv, convert); tv_clear(&tv); } clear_evalarg(&EVALARG_EVALUATE, NULL); -- cgit From a1f0f0b2bc19f29948c3b9e6c71f3a5e80eaceea Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 17 Sep 2023 11:05:36 +0800 Subject: vim-patch:9.0.1633: duplicate code for converting float to string Problem: Duplicate code for converting float to string. Solution: Use tv_get_string(). (closes vim/vim#12521) https://github.com/vim/vim/commit/19dfa276c37dcf657922c6f9b48cf2954191e8b6 --- src/nvim/eval.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fed5b01538..ec90803884 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -949,8 +949,7 @@ int skip_expr(char **pp, evalarg_T *const evalarg) /// Convert "tv" to a string. /// -/// @param convert when true convert a List into a sequence of lines and convert -/// a Float to a String. +/// @param convert when true convert a List into a sequence of lines. /// /// @return an allocated string. static char *typval2string(typval_T *tv, bool convert) @@ -967,20 +966,14 @@ static char *typval2string(typval_T *tv, bool convert) ga_append(&ga, NUL); return (char *)ga.ga_data; } - if (convert && tv->v_type == VAR_FLOAT) { - char numbuf[NUMBUFLEN]; - vim_snprintf(numbuf, NUMBUFLEN, "%g", tv->vval.v_float); - return xstrdup(numbuf); - } return xstrdup(tv_get_string(tv)); } /// Top level evaluation function, returning a string. /// -/// @param convert when true convert a List into a sequence of lines and convert -/// a Float to a String. +/// @param convert when true convert a List into a sequence of lines. /// -/// @return pointer to allocated memory, or NULL for failure. +/// @return pointer to allocated memory, or NULL for failure. char *eval_to_string(char *arg, bool convert) { typval_T tv; -- cgit From 71530cc972576e6656431b6d000aec9b69a0997e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 17 Sep 2023 20:29:18 +0800 Subject: feat(folds): support virtual text format for 'foldtext' (#25209) Co-authored-by: Lewis Russell --- src/nvim/eval.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ec90803884..a279b6d051 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12,7 +12,9 @@ #include #include "auto/config.h" +#include "nvim/api/private/converter.h" #include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" @@ -994,7 +996,7 @@ char *eval_to_string(char *arg, bool convert) /// textlock. /// /// @param use_sandbox when true, use the sandbox. -char *eval_to_string_safe(char *arg, int use_sandbox) +char *eval_to_string_safe(char *arg, const bool use_sandbox) { char *retval; funccal_entry_T funccal_entry; @@ -1267,11 +1269,10 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv) /// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding /// it in "*cp". Doesn't give error messages. -int eval_foldexpr(char *arg, int *cp) +int eval_foldexpr(win_T *wp, int *cp) { - typval_T tv; - varnumber_T retval; - int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL); + const bool use_sandbox = was_set_insecurely(wp, "foldexpr", OPT_LOCAL); + char *arg = wp->w_p_fde; emsg_off++; if (use_sandbox) { @@ -1279,6 +1280,9 @@ int eval_foldexpr(char *arg, int *cp) } textlock++; *cp = NUL; + + typval_T tv; + varnumber_T retval; if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = 0; } else { @@ -1298,6 +1302,7 @@ int eval_foldexpr(char *arg, int *cp) } tv_clear(&tv); } + emsg_off--; if (use_sandbox) { sandbox--; @@ -1308,6 +1313,42 @@ int eval_foldexpr(char *arg, int *cp) return (int)retval; } +/// Evaluate 'foldtext', returning an Array or a String (NULL_STRING on failure). +Object eval_foldtext(win_T *wp) +{ + const bool use_sandbox = was_set_insecurely(wp, "foldtext", OPT_LOCAL); + char *arg = wp->w_p_fdt; + funccal_entry_T funccal_entry; + + save_funccal(&funccal_entry); + if (use_sandbox) { + sandbox++; + } + textlock++; + + typval_T tv; + Object retval; + if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { + retval = STRING_OBJ(NULL_STRING); + } else { + if (tv.v_type == VAR_LIST) { + retval = vim_to_object(&tv); + } else { + retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv))); + } + tv_clear(&tv); + } + clear_evalarg(&EVALARG_EVALUATE, NULL); + + if (use_sandbox) { + sandbox--; + } + textlock--; + restore_funccal(); + + return retval; +} + /// Get an lvalue /// /// Lvalue may be -- cgit From 211edceb4f4d4d0f6c41a6ee56891a6f9407e3a7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 17 Sep 2023 21:01:19 +0800 Subject: vim-patch:8.2.4173: cannot use an import in 'foldexpr' (#25215) Problem: Cannot use an import in 'foldexpr'. Solution: Set the script context to where 'foldexpr' was set. (closes vim/vim#9584) Fix that the script context was not set for all buffers. https://github.com/vim/vim/commit/e70dd11ef41f69bd5e94f630194e6b3c4f3f2102 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a279b6d051..21c7cdee7d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1271,8 +1271,11 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv) /// it in "*cp". Doesn't give error messages. int eval_foldexpr(win_T *wp, int *cp) { + const sctx_T saved_sctx = current_sctx; const bool use_sandbox = was_set_insecurely(wp, "foldexpr", OPT_LOCAL); + char *arg = wp->w_p_fde; + current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx; emsg_off++; if (use_sandbox) { @@ -1309,6 +1312,7 @@ int eval_foldexpr(win_T *wp, int *cp) } textlock--; clear_evalarg(&EVALARG_EVALUATE, NULL); + current_sctx = saved_sctx; return (int)retval; } -- cgit From b85f1dafc7c0a19704135617454f1c66f41202c1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 27 Sep 2023 22:21:17 +0200 Subject: refactor(messages): fold msg_attr into msg problem: there are too many different functions in message.c solution: fold some of the functions into themselves --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 21c7cdee7d..dc07c0fc90 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8102,7 +8102,7 @@ void ex_execute(exarg_T *eap) if (eap->cmdidx == CMD_echomsg) { msg_ext_set_kind("echomsg"); - msg_attr(ga.ga_data, echo_attr); + msg(ga.ga_data, echo_attr); } else if (eap->cmdidx == CMD_echoerr) { // We don't want to abort following commands, restore did_emsg. int save_did_emsg = did_emsg; -- cgit From 448d4837be7f7bd60ac0b5a3100c0217ac48a495 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 27 Sep 2023 22:24:50 +0200 Subject: refactor(messages): rename msg_trunc_attr and msg_multiline_attr without attr --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dc07c0fc90..f7614af548 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8018,7 +8018,7 @@ void ex_echo(exarg_T *eap) char *tofree = encode_tv2echo(&rettv, NULL); if (*tofree != NUL) { msg_ext_set_kind("echo"); - msg_multiline_attr(tofree, echo_attr, true, &need_clear); + msg_multiline(tofree, echo_attr, true, &need_clear); } xfree(tofree); } -- cgit From 8e11c18d4962c5367a0549bdb2288323545852b6 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 29 Sep 2023 16:10:28 +0200 Subject: refactor(message): msg_puts_attr_len -> msg_puts_len --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f7614af548..c7b706e0fc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5731,7 +5731,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const p = lastnl + 1; msg_start(); msg_clr_eos(); - msg_puts_attr_len(prompt, p - prompt, echo_attr); + msg_puts_len(prompt, p - prompt, echo_attr); msg_didout = false; msg_starthere(); } -- cgit From bc13bc154aa574e0bb58a50f2e0ca4570efa57c3 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 29 Sep 2023 16:10:54 +0200 Subject: refactor(message): smsg_attr -> smsg --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c7b706e0fc..c30009606f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5922,7 +5922,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist if (p_verbose > 3) { char *cmdstr = shell_argv_to_str(argv); verbose_enter_scroll(); - smsg(_("Executing command: \"%s\""), cmdstr); + smsg(0, _("Executing command: \"%s\""), cmdstr); msg_puts("\n\n"); verbose_leave_scroll(); xfree(cmdstr); -- cgit From cf8b2c0e74fd5e723b0c15c2ce84e6900fd322d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 12:05:28 +0800 Subject: build(iwyu): add a few more _defs.h mappings (#25435) --- src/nvim/eval.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c30009606f..5d0600c6c1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "auto/config.h" #include "nvim/api/private/converter.h" @@ -30,7 +31,6 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" -#include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/process.h" #include "nvim/ex_cmds.h" @@ -60,7 +60,6 @@ #include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/ops.h" #include "nvim/option.h" -#include "nvim/option_defs.h" #include "nvim/optionstr.h" #include "nvim/os/fileio.h" #include "nvim/os/fs_defs.h" -- cgit From dc6d0d2daf69e2fdadda81feb97906dbc962a239 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 14:41:34 +0800 Subject: refactor: reorganize option header files (#25437) - Move vimoption_T to option.h - option_defs.h is for option-related types - option_vars.h corresponds to Vim's option.h - option_defs.h and option_vars.h don't include each other --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5d0600c6c1..0bf26b2451 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -60,6 +60,7 @@ #include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fileio.h" #include "nvim/os/fs_defs.h" -- cgit From 09a17f91d0d362c6e58bfdbe3ccdeacffb0b44b9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 2 Oct 2023 10:45:33 +0800 Subject: refactor: move cmdline completion types to cmdexpand_defs.h (#25465) --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0bf26b2451..014335f5ed 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -21,6 +21,7 @@ #include "nvim/buffer_defs.h" #include "nvim/channel.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cmdhist.h" #include "nvim/cursor.h" #include "nvim/edit.h" -- cgit From fd791db0ecebf5d5bf8922ba519f8dd4eef3c5e6 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 3 Oct 2023 00:19:30 +0200 Subject: fix: fix ASAN errors on clang 17 (#25469) --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 014335f5ed..62ad6c057d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1226,7 +1226,7 @@ fail: /// /// @return [allocated] NULL when calling function fails, allocated string /// otherwise. -char *call_func_retstr(const char *const func, int argc, typval_T *argv) +void *call_func_retstr(const char *const func, int argc, typval_T *argv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; -- cgit From a9a48d6b5f00241e16e7131c997f0117bc5e9047 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 30 Sep 2023 10:31:55 +0200 Subject: refactor(message): simplify msg_puts_display and use batched grid updates msg_puts_display was more complex than necessary in nvim, as in nvim, it no longer talks directly with a terminal. In particular we don't need to scroll the grid before emiting the last char. The TUI already takes care of things like that, for terminals where it matters. --- src/nvim/eval.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 62ad6c057d..9f446c5387 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8094,13 +8094,6 @@ void ex_execute(exarg_T *eap) } if (ret != FAIL && ga.ga_data != NULL) { - if (eap->cmdidx == CMD_echomsg || eap->cmdidx == CMD_echoerr) { - // Mark the already saved text as finishing the line, so that what - // follows is displayed on a new line when scrolling back at the - // more prompt. - msg_sb_eol(); - } - if (eap->cmdidx == CMD_echomsg) { msg_ext_set_kind("echomsg"); msg(ga.ga_data, echo_attr); -- cgit From e72b546354cd90bf0cd8ee6dd045538d713009ad Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 29 Sep 2023 14:58:48 +0200 Subject: refactor: the long goodbye long is 32 bits on windows, while it is 64 bits on other architectures. This makes the type suboptimal for a codebase meant to be cross-platform. Replace it with more appropriate integer types. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9f446c5387..9a90e430a7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8290,7 +8290,7 @@ void option_last_set_msg(LastSet last_set) msg_puts(p); if (last_set.script_ctx.sc_lnum > 0) { msg_puts(_(line_msg)); - msg_outnum((long)last_set.script_ctx.sc_lnum); + msg_outnum(last_set.script_ctx.sc_lnum); } if (should_free) { xfree(p); -- cgit From 1ac588543ded834b254c7f6a73372583ce89f2ea Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 7 Oct 2023 06:32:06 +0800 Subject: vim-patch:9.0.1995: Invalid memory access with empty 'foldexpr' (#25530) Problem: Invalid memory access when 'foldexpr' returns empty string. Solution: Check for NUL. closes: vim/vim#13293 https://github.com/vim/vim/commit/a991ce9c083bb8c02b1b1ec34ed35728197050f3 --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9a90e430a7..12226dac91 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1299,7 +1299,7 @@ int eval_foldexpr(win_T *wp, int *cp) // If the result is a string, check if there is a non-digit before // the number. char *s = tv.vval.v_string; - if (!ascii_isdigit(*s) && *s != '-') { + if (*s != NUL && !ascii_isdigit(*s) && *s != '-') { *cp = (uint8_t)(*s++); } retval = atol(s); -- cgit From 8e932480f61d6101bf8bea1abc07ed93826221fd Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 29 Sep 2023 14:58:48 +0200 Subject: refactor: the long goodbye long is 32 bits on windows, while it is 64 bits on other architectures. This makes the type suboptimal for a codebase meant to be cross-platform. Replace it with more appropriate integer types. --- src/nvim/eval.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 12226dac91..b50d7a509f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1645,7 +1645,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_n1 = 0; } else { // Is number or string. - lp->ll_n1 = (long)tv_get_number(&var1); + lp->ll_n1 = (int)tv_get_number(&var1); } tv_clear(&var1); @@ -1655,7 +1655,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const return NULL; } if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = (long)tv_get_number(&var2); + lp->ll_n2 = (int)tv_get_number(&var2); tv_clear(&var2); if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { return NULL; @@ -1670,7 +1670,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_n1 = 0; } else { // Is number or string. - lp->ll_n1 = (long)tv_get_number(&var1); + lp->ll_n1 = (int)tv_get_number(&var1); } tv_clear(&var1); @@ -1687,7 +1687,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const // When no index given: "lp->ll_empty2" is true. // Otherwise "lp->ll_n2" is set to the second index. if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. + lp->ll_n2 = (int)tv_get_number(&var2); // Is number or string. tv_clear(&var2); if (tv_list_check_range_index_two(lp->ll_list, &lp->ll_n1, lp->ll_li, @@ -1746,7 +1746,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool bool error = false; const char val = (char)tv_get_number_chk(rettv, &error); if (!error) { - tv_blob_set_append(lp->ll_blob, (int)lp->ll_n1, (uint8_t)val); + tv_blob_set_append(lp->ll_blob, lp->ll_n1, (uint8_t)val); } } } else if (op != NULL && *op != '=') { @@ -5914,7 +5914,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist char **argv = tv_to_argv(&argvars[0], NULL, &executable); if (!argv) { if (!executable) { - set_vim_var_nr(VV_SHELL_ERROR, (long)-1); + set_vim_var_nr(VV_SHELL_ERROR, -1); } xfree(input); return; // Already did emsg. @@ -5944,7 +5944,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist xfree(input); - set_vim_var_nr(VV_SHELL_ERROR, (long)status); + set_vim_var_nr(VV_SHELL_ERROR, status); if (res == NULL) { if (retlist) { @@ -6231,7 +6231,7 @@ void timer_due_cb(TimeWatcher *tw, void *data) timer_decref(timer); } -uint64_t timer_start(const long timeout, const int repeat_count, const Callback *const callback) +uint64_t timer_start(const int64_t timeout, const int repeat_count, const Callback *const callback) { timer_T *timer = xmalloc(sizeof *timer); timer->refcount = 1; @@ -7122,7 +7122,7 @@ void set_vim_var_char(int c) /// Set v:count to "count" and v:count1 to "count1". /// /// @param set_prevcount if true, first set v:prevcount from v:count. -void set_vcount(long count, long count1, int set_prevcount) +void set_vcount(int64_t count, int64_t count1, bool set_prevcount) { if (set_prevcount) { vimvars[VV_PREVCOUNT].vv_nr = vimvars[VV_COUNT].vv_nr; -- cgit From 6c0f900699293937435591412e4285e313703bc7 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 6 Oct 2023 17:43:23 +0200 Subject: ci: enable clang-analyzer warnings This adds the checks in https://neovim.io/doc/reports/clang/ when using clang-tidy. The strategy is to enable all clang-analyzer checks, and disable only the checks for the warnings that exist currently. This allows us to eliminate each warning type without blocking ongoing work, but also without adding bugs for already eliminated warnings. The plan is to eventually eliminate https://neovim.io/doc/reports/clang/ by completely integrating it into the clang-tidy check. Also add make and cmake targets `clang-analyzer` to run this check. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b50d7a509f..c9a29b9913 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5641,7 +5641,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - const char *prompt = ""; + const char *prompt; const char *defstr = ""; typval_T *cancelreturn = NULL; typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; -- cgit From 9ff6f73f838a1f90d09922448c434033ba5e094e Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Mon, 9 Oct 2023 00:36:48 +0600 Subject: refactor: allow not having a `default` case for enum Problem: The style guide states that all switch statements that are not conditional on an enum must have a `default` case, but does not give any explicit guideline for switch statements that are conditional on enums. As a result, a `default` case is added in many enum switch statements, even when the switch statement is exhaustive. This is not ideal because it removes the ability to have compiler errors to easily detect unchanged switch statements when a new possible value for an enum is added. Solution: Add explicit guidelines for switch statements that are conditional on an enum, clarifying that a `default` case is not necessary if the switch statement is exhaustive. Also refactor pre-existing code with unnecessary `default` cases. --- src/nvim/eval.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c9a29b9913..ee0860a8d1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6088,9 +6088,6 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co case kCallbackNone: return false; break; - - default: - abort(); } funcexe_T funcexe = FUNCEXE_INIT; @@ -6120,7 +6117,7 @@ bool set_ref_in_callback(Callback *callback, int copyID, ht_stack_T **ht_stack, return set_ref_in_item(&tv, copyID, ht_stack, list_stack); break; - default: + case kCallbackLua: abort(); } return false; -- cgit From 3cbb02ce77087a820decd1a724a6e8b666e0ca36 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 12 Oct 2023 07:38:56 +0800 Subject: vim-patch:8.2.3064: Vim9: in script cannot set item in uninitialized list (#25605) Problem: Vim9: in script cannot set item in uninitialized list. Solution: When a list is NULL allocate an empty one. (closes vim/vim#8461) https://github.com/vim/vim/commit/e65081d1b591f16dc6e380a830d87565c5eb7b03 Co-authored-by: Bram Moolenaar --- src/nvim/eval.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ee0860a8d1..f9b32d758d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1467,14 +1467,22 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const } return NULL; } - if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) - && !(lp->ll_tv->v_type == VAR_DICT && lp->ll_tv->vval.v_dict != NULL) - && !(lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob != NULL)) { + if (lp->ll_tv->v_type != VAR_LIST + && lp->ll_tv->v_type != VAR_DICT + && lp->ll_tv->v_type != VAR_BLOB) { if (!quiet) { emsg(_("E689: Can only index a List, Dictionary or Blob")); } return NULL; } + + // a NULL list/blob works like an empty list/blob, allocate one now. + if (lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL) { + tv_list_alloc_ret(lp->ll_tv, kListLenUnknown); + } else if (lp->ll_tv->v_type == VAR_BLOB && lp->ll_tv->vval.v_blob == NULL) { + tv_blob_alloc_ret(lp->ll_tv); + } + if (lp->ll_range) { if (!quiet) { emsg(_("E708: [:] must come last")); -- cgit From ebe489d8f0edbb3538a59733289d8969d1ffea22 Mon Sep 17 00:00:00 2001 From: Jongwook Choi Date: Thu, 12 Oct 2023 19:27:45 -0400 Subject: fix: allow multiline message for echoerr (#25380) PROBLEM: Currently `:echoerr` prints multi-line strings in a single line as `:echom` does (Note: `:echon` can print multi-line strings well). This makes stacktrace printed via echoerr difficult to read. Example code: try lua error("lua stacktrace") catch echoerr v:exception endtry Output: Error detected while processing a.vim[5]..a.vim: line 4: Vim(lua):E5108: Error executing lua [string ":lua"]:1: lua stacktrace^@stack traceback:^@^I[C]: in function 'error'^@^I[string ":lua"]:1: in main chunk SOLUTION: Allow echoerr to print multiline messages (e.g., lua exceptions), because this command is usually used to print stacktraces. Output after the fix: Error detected while processing a.vim[5]..a.vim: line 4: Vim(lua):E5108: Error executing lua [string ":lua"]:1: lua stacktrace stack traceback: [C]: in function 'error' [string ":lua"]:1: in main chunk --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f9b32d758d..74b1dbfb72 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8106,7 +8106,7 @@ void ex_execute(exarg_T *eap) // We don't want to abort following commands, restore did_emsg. int save_did_emsg = did_emsg; msg_ext_set_kind("echoerr"); - emsg(ga.ga_data); + emsg_multiline(ga.ga_data, true); if (!force_abort) { did_emsg = save_did_emsg; } -- cgit From e5a424d78ed72613dd5ccc161578f9f6f853a0e8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 15 Oct 2023 17:02:17 +0800 Subject: vim-patch:9.0.2030: no max callback recursion limit (#25655) Problem: no max callback recursion limit Solution: bail out, if max call recursion for callback functions has been reached. This checks the 'maxfuncdepth' setting and throws E169 when a callback function recursively calls itself. closes: vim/vim#13337 closes: vim/vim#13339 https://github.com/vim/vim/commit/47510f3d6598a1218958c03ed11337a43b73f48d Co-authored-by: Christian Brabandt --- src/nvim/eval.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 74b1dbfb72..b5b40061b5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6064,6 +6064,11 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co typval_T *const rettv) FUNC_ATTR_NONNULL_ALL { + if (callback_depth > p_mfd) { + emsg(_(e_command_too_recursive)); + return false; + } + partial_T *partial; char *name; Array args = ARRAY_DICT_INIT; -- cgit From 5df4fdf253f9c9cc35f9f5f16c6d0ba9d87b4c71 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 8 Oct 2023 22:13:15 +0600 Subject: refactor(options)!: make OptionSet `v:` values use typval BREAKING CHANGE: This breaks the OptionSet autocommand, as the `v:` values associated with it (`v:option_new`, `v:option_old`, `v:option_oldlocal` and `v:option_oldglobal`) are now the same type as the option, instead of all option values being converted to strings. --- src/nvim/eval.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b5b40061b5..ad7d327de3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7226,6 +7226,17 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) tv_dict_set_keys_readonly(val); } +/// Set v:variable to tv. +/// +/// @param[in] idx Index of variable to set. +/// @param[in,out] val Value to set to. Reference count will be incremented. +/// Also keys of the dictionary will be made read-only. +void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv) +{ + tv_clear(&vimvars[idx].vv_di.di_tv); + vimvars[idx].vv_di.di_tv = *tv; +} + /// Set the v:argv list. void set_argv_var(char **argv, int argc) { -- cgit From af010e23f38a23bb74ea5b61e1b1a05e76410b5f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Fri, 13 Oct 2023 20:16:15 +0600 Subject: refactor(options): rename `empty_option` to `empty_string_option` --- src/nvim/eval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ad7d327de3..20d76334f6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2237,7 +2237,7 @@ int pattern_match(const char *pat, const char *text, bool ic) // avoid 'l' flag in 'cpoptions' char *save_cpo = p_cpo; - p_cpo = empty_option; + p_cpo = empty_string_option; regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = ic; @@ -8645,7 +8645,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char // Make 'cpoptions' empty, so that the 'l' flag doesn't work here char *save_cpo = p_cpo; - p_cpo = empty_option; + p_cpo = empty_string_option; ga_init(&ga, 1, 200); @@ -8710,7 +8710,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data); ga_clear(&ga); - if (p_cpo == empty_option) { + if (p_cpo == empty_string_option) { p_cpo = save_cpo; } else { // Darn, evaluating {sub} expression or {expr} changed the value. -- cgit From e19cc9c9b715d8171f7940632b8855104b5290b6 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sat, 14 Oct 2023 22:19:11 +0600 Subject: refactor(options)!: unify `set_option` and `set_string_option` While the interfaces for setting number and boolean options are now unified by #25394, there is still a separate `set_string_option` function that is used for setting a string option. This PR removes that function and merges it with set_option. BREAKING CHANGE: `v:option_old` is now the old global value for all global-local options, instead of just string global-local options. Local value for a global-local number/boolean option is now unset when the option is set (e.g. using `:set` or `nvim_set_option_value`) without a scope, which means they now behave the same way as string options. Ref: #25672 --- src/nvim/eval.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 20d76334f6..8ffc6fd179 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7229,12 +7229,11 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) /// Set v:variable to tv. /// /// @param[in] idx Index of variable to set. -/// @param[in,out] val Value to set to. Reference count will be incremented. -/// Also keys of the dictionary will be made read-only. +/// @param[in] val Value to set to. Will be copied. void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv) { tv_clear(&vimvars[idx].vv_di.di_tv); - vimvars[idx].vv_di.di_tv = *tv; + tv_copy(tv, &vimvars[idx].vv_di.di_tv); } /// Set the v:argv list. -- cgit From acc646ad8fc3ef11fcc63b69f3d8484e4a91accd Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 29 Sep 2023 14:58:48 +0200 Subject: refactor: the long goodbye long is 32 bits on windows, while it is 64 bits on other architectures. This makes the type suboptimal for a codebase meant to be cross-platform. Replace it with more appropriate integer types. --- src/nvim/eval.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8ffc6fd179..a3ea5169fd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -454,8 +454,8 @@ void eval_init(void) set_vim_var_dict(VV_EVENT, tv_dict_alloc_lock(VAR_FIXED)); set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown)); set_vim_var_nr(VV_STDERR, CHAN_STDERR); - set_vim_var_nr(VV_SEARCHFORWARD, 1L); - set_vim_var_nr(VV_HLSEARCH, 1L); + set_vim_var_nr(VV_SEARCHFORWARD, 1); + set_vim_var_nr(VV_HLSEARCH, 1); set_vim_var_nr(VV_COUNT1, 1); set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER); set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING); @@ -6629,14 +6629,14 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret } // Get the line number. - pos.lnum = (linenr_T)tv_list_find_nr(l, 0L, &error); + pos.lnum = (linenr_T)tv_list_find_nr(l, 0, &error); if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) { // Invalid line number. return NULL; } // Get the column number. - pos.col = (colnr_T)tv_list_find_nr(l, 1L, &error); + pos.col = (colnr_T)tv_list_find_nr(l, 1, &error); if (error) { return NULL; } @@ -6648,7 +6648,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret } // We accept "$" for the column number: last column. - listitem_T *li = tv_list_find(l, 1L); + listitem_T *li = tv_list_find(l, 1); if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING && TV_LIST_ITEM_TV(li)->vval.v_string != NULL && strcmp(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { @@ -6663,7 +6663,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret pos.col--; // Get the virtual offset. Defaults to zero. - pos.coladd = (colnr_T)tv_list_find_nr(l, 2L, &error); + pos.coladd = (colnr_T)tv_list_find_nr(l, 2, &error); if (error) { pos.coladd = 0; } -- cgit From cd63a9addd6e1114c3524fa041ece560550cfe7b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 10 Nov 2023 08:39:21 +0800 Subject: refactor: change some xstrndup() and xstrnsave() to xmemdupz() (#25959) When the given length is exactly the number of bytes to copy, xmemdupz() makes the intention clearer. --- src/nvim/eval.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a3ea5169fd..390cdbb377 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1633,7 +1633,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (len == -1) { lp->ll_newkey = xstrdup(key); } else { - lp->ll_newkey = xstrnsave(key, (size_t)len); + lp->ll_newkey = xmemdupz(key, (size_t)len); } tv_clear(&var1); break; @@ -1966,7 +1966,7 @@ bool next_for_item(void *fi_void, char *arg) typval_T tv; tv.v_type = VAR_STRING; tv.v_lock = VAR_FIXED; - tv.vval.v_string = xstrnsave(fi->fi_string + fi->fi_byte_idx, (size_t)len); + tv.vval.v_string = xmemdupz(fi->fi_string + fi->fi_byte_idx, (size_t)len); fi->fi_byte_idx += len; const int result = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; @@ -4893,7 +4893,7 @@ static int get_literal_key(char **arg, typval_T *tv) } for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; p++) {} tv->v_type = VAR_STRING; - tv->vval.v_string = xstrnsave(*arg, (size_t)(p - *arg)); + tv->vval.v_string = xmemdupz(*arg, (size_t)(p - *arg)); *arg = skipwhite(p); return OK; @@ -5242,7 +5242,7 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T * typval_T tv = { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, - .vval.v_string = xstrnsave(p, (size_t)len), + .vval.v_string = xmemdupz(p, (size_t)len), }; vimvars[VV_KEY].vv_nr = idx; @@ -7483,7 +7483,7 @@ char *char_from_string(const char *str, varnumber_T index) if (nbyte >= slen) { return NULL; } - return xstrnsave(str + nbyte, (size_t)utf_ptr2len(str + nbyte)); + return xmemdupz(str + nbyte, (size_t)utf_ptr2len(str + nbyte)); } /// Get the byte index for character index "idx" in string "str" with length @@ -7544,7 +7544,7 @@ char *string_slice(const char *str, varnumber_T first, varnumber_T last, bool ex if (start_byte >= (ssize_t)slen || end_byte <= start_byte) { return NULL; } - return xstrnsave(str + start_byte, (size_t)(end_byte - start_byte)); + return xmemdupz(str + start_byte, (size_t)(end_byte - start_byte)); } /// Handle: @@ -8586,13 +8586,13 @@ repeat: // find end of pattern p = vim_strchr(s, sep); if (p != NULL) { - char *const pat = xstrnsave(s, (size_t)(p - s)); + char *const pat = xmemdupz(s, (size_t)(p - s)); s = p + 1; // find end of substitution p = vim_strchr(s, sep); if (p != NULL) { - char *const sub = xstrnsave(s, (size_t)(p - s)); - char *const str = xstrnsave(*fnamep, *fnamelen); + char *const sub = xmemdupz(s, (size_t)(p - s)); + char *const str = xmemdupz(*fnamep, *fnamelen); *usedlen = (size_t)(p + 1 - src); s = do_string_sub(str, pat, sub, NULL, flags); *fnamep = s; -- cgit From 8e58d37f2e15ac8540377148e55ed08a039aadb6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 11 Nov 2023 11:20:08 +0100 Subject: refactor: remove redundant casts --- src/nvim/eval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 390cdbb377..e1216663cd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2241,7 +2241,7 @@ int pattern_match(const char *pat, const char *text, bool ic) regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = ic; - matches = vim_regexec_nl(®match, text, (colnr_T)0); + matches = vim_regexec_nl(®match, text, 0); vim_regfree(regmatch.regprog); } p_cpo = save_cpo; @@ -8794,7 +8794,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo return (typval_T){ .v_type = VAR_NUMBER, .v_lock = VAR_UNLOCKED, - .vval.v_number = (varnumber_T)0 + .vval.v_number = 0 }; } -- cgit From 353a4be7e84fdc101318215bdcc8a7e780d737fe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 13:13:58 +0100 Subject: build: remove PVS We already have an extensive suite of static analysis tools we use, which causes a fair bit of redundancy as we get duplicate warnings. PVS is also prone to give false warnings which creates a lot of work to identify and disable. --- src/nvim/eval.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e1216663cd..7b3726486f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // eval.c: Expression evaluation. #include -- cgit From 6d14f3ddab33144966e46487b6039440ac7c43f2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 13 Nov 2023 06:33:34 +0800 Subject: vim-patch:9.0.2103: recursive callback may cause issues on some archs (#26013) Problem: recursive callback may cause issues on some archs Solution: Decrease the limit drastically to 20 Recursive callback limit causes problems on some architectures Since commit 47510f3d6598a1218958c03ed11337a43b73f48d we have a test that causes a recursive popup callback function to be executed. However it seems the current limit of 'maxfuncdepth' option value is still too recursive for some 32bit architectures (e.g. 32bit ARM). So instead of allowing a default limit of 100 (default value for 'maxfuncdepth'), let's reduce this limit to 20. I don't think there is a use case where one would need such a high recursive callback limit and a limit of 20 seems reasonable (although it is currently hard-coded). closes: vim/vim#13495 closes: vim/vim#13502 https://github.com/vim/vim/commit/2076463e383901cef44685aaf4b63e4306444f9e Co-authored-by: Christian Brabandt --- src/nvim/eval.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7b3726486f..be9d0abc42 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -87,6 +87,8 @@ #define DICT_MAXNEST 100 // maximum nesting of lists and dicts +#define MAX_CALLBACK_DEPTH 20 + static const char *e_missbrac = N_("E111: Missing ']'"); static const char *e_list_end = N_("E697: Missing end of List ']': %s"); static const char e_cannot_slice_dictionary[] @@ -6061,7 +6063,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co typval_T *const rettv) FUNC_ATTR_NONNULL_ALL { - if (callback_depth > p_mfd) { + if (callback_depth > MAX_CALLBACK_DEPTH) { emsg(_(e_command_too_recursive)); return false; } -- cgit From 28f4f3c48498086307ed825d1761edb5789ca0e8 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 15:54:54 +0100 Subject: refactor: follow style guide - reduce variable scope - prefer initialization over declaration and assignment - use bool to represent boolean values --- src/nvim/eval.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index be9d0abc42..6d2c276df4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1867,14 +1867,13 @@ notify: void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const evalarg) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); - const char *expr; typval_T tv; list_T *l; const bool skip = !(evalarg->eval_flags & EVAL_EVALUATE); *errp = true; // Default: there is an error. - expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); + const char *expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); if (expr == NULL) { return fi; } @@ -2005,13 +2004,12 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx) FUNC_ATTR_NONNULL_ALL { bool got_eq = false; - char *p; if (cmdidx == CMD_let || cmdidx == CMD_const) { xp->xp_context = EXPAND_USER_VARS; if (strpbrk(arg, "\"'+-*/%.=!?~|&$([<>,#") == NULL) { // ":let var1 var2 ...": find last space. - for (p = arg + strlen(arg); p >= arg;) { + for (char *p = arg + strlen(arg); p >= arg;) { xp->xp_pattern = p; MB_PTR_BACK(arg, p); if (ascii_iswhite(*p)) { @@ -2343,14 +2341,12 @@ void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) /// @return OK or FAIL. int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) { - int ret; - char *p; const int did_emsg_before = did_emsg; const int called_emsg_before = called_emsg; bool end_error = false; - p = skipwhite(arg); - ret = eval1(&p, rettv, evalarg); + char *p = skipwhite(arg); + int ret = eval1(&p, rettv, evalarg); if (ret != FAIL) { end_error = !ends_excmd(*p); @@ -7239,10 +7235,9 @@ void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv) void set_argv_var(char **argv, int argc) { list_T *l = tv_list_alloc(argc); - int i; tv_list_set_lock(l, VAR_FIXED); - for (i = 0; i < argc; i++) { + for (int i = 0; i < argc; i++) { tv_list_append_string(l, (const char *const)argv[i], -1); TV_LIST_ITEM_TV(tv_list_last(l))->v_lock = VAR_FIXED; } -- cgit From 931f28841c46015cda70909917b4202e7c746492 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 16 Nov 2023 11:50:04 +0800 Subject: refactor: remove B_SPELL macro (#26063) --- src/nvim/eval.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6d2c276df4..ed70091077 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2,7 +2,6 @@ #include #include -#include #include #include #include -- cgit From b522cb1ac3fbdf6e68eed5d0b6e1cbeaf3ac2254 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 6 Nov 2023 14:52:27 +0100 Subject: refactor(grid): make screen rendering more multibyte than ever before Problem: buffer text with composing chars are converted from UTF-8 to an array of up to seven UTF-32 values and then converted back to UTF-8 strings. Solution: Convert buffer text directly to UTF-8 based schar_T values. The limit of the text size is now in schar_T bytes, which is currently 31+1 but easily could be raised as it no longer multiplies the size of the entire screen grid when not used, the full size is only required for temporary scratch buffers. Also does some general cleanup to win_line text handling, which was unnecessarily complicated due to multibyte rendering being an "opt-in" feature long ago. Nowadays, a char is just a char, regardless if it consists of one ASCII byte or multiple bytes. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ed70091077..c073f30547 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7117,7 +7117,7 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE /// Set v:char to character "c". void set_vim_var_char(int c) { - char buf[MB_MAXBYTES + 1]; + char buf[MB_MAXCHAR + 1]; buf[utf_char2bytes(c, buf)] = NUL; set_vim_var_string(VV_CHAR, buf, -1); -- cgit From a6e3d93421ba13c407a96fac9cc01fa41ec7ad98 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 16 Nov 2023 10:59:11 +0100 Subject: refactor: enable formatting for ternaries This requires removing the "Inner expression should be aligned" rule from clint as it prevents essentially any formatting regarding ternary operators. --- src/nvim/eval.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c073f30547..a4b6a5fc11 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2439,7 +2439,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg) } *arg = skipwhite(*arg + 1); evalarg_used->eval_flags = (op_falsy ? !result : result) - ? orig_flags : (orig_flags & ~EVAL_EVALUATE); + ? orig_flags : (orig_flags & ~EVAL_EVALUATE); typval_T var2; if (eval1(arg, &var2, evalarg_used) == FAIL) { evalarg_used->eval_flags = orig_flags; @@ -7352,7 +7352,7 @@ char *set_cmdarg(exarg_T *eap, char *oldarg) newval_len - xlen, " ++ff=%s", eap->force_ff == 'u' ? "unix" - : eap->force_ff == 'd' ? "dos" : "mac"); + : eap->force_ff == 'd' ? "dos" : "mac"); if (rc < 0) { goto error; } @@ -8086,10 +8086,10 @@ void ex_execute(exarg_T *eap) if (!eap->skip) { const char *const argstr = eap->cmdidx == CMD_execute - ? tv_get_string(&rettv) - : rettv.v_type == VAR_STRING - ? encode_tv2echo(&rettv, NULL) - : encode_tv2string(&rettv, NULL); + ? tv_get_string(&rettv) + : rettv.v_type == VAR_STRING + ? encode_tv2echo(&rettv, NULL) + : encode_tv2string(&rettv, NULL); const size_t len = strlen(argstr); ga_grow(&ga, (int)len + 2); if (!GA_EMPTY(&ga)) { @@ -8879,8 +8879,8 @@ bool eval_has_provider(const char *feat) } bool ok = (tv.v_type == VAR_NUMBER) - ? 2 == tv.vval.v_number // Value of 2 means "loaded and working". - : false; + ? 2 == tv.vval.v_number // Value of 2 means "loaded and working". + : false; if (ok) { // Call() must be defined if provider claims to be working. -- cgit From 38a20dd89f91c45ec8589bf1c50d50732882d38a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 27 Nov 2023 20:58:37 +0800 Subject: build(IWYU): replace most private mappings with pragmas (#26247) --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a4b6a5fc11..52ab610267 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -60,7 +60,7 @@ #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/fileio.h" -#include "nvim/os/fs_defs.h" +#include "nvim/os/fs.h" #include "nvim/os/lang.h" #include "nvim/os/os.h" #include "nvim/os/shell.h" -- cgit From 40139738eb479d0913ec6ce751ca5adfa50ad8c3 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 26 Nov 2023 21:36:02 +0100 Subject: build: enable IWYU on mac --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 52ab610267..d48e126229 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "auto/config.h" #include "nvim/api/private/converter.h" -- cgit From 8b428ca8b79ebb7b36c3e403ff3bcb6924a635a6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 16:00:21 +0100 Subject: build(IWYU): fix includes for func_attr.h --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d48e126229..701e6dd32d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -36,6 +36,7 @@ #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/ex_session.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" -- cgit From f4aedbae4cb1f206f5b7c6142697b71dd473059b Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 18:39:38 +0100 Subject: build(IWYU): fix includes for undo_defs.h --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 701e6dd32d..d02d9e0b49 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -68,7 +68,7 @@ #include "nvim/os/shell.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" -- cgit From 6c14ae6bfaf51415b555e9a6b85d1d280976358d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 20:27:32 +0100 Subject: refactor: rename types.h to types_defs.h --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d02d9e0b49..02e0be9a94 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -76,7 +76,7 @@ #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" #include "nvim/usercmd.h" -- cgit From 1a8f60c7d2699826b51f23b040b83b1d96a14930 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 28 Nov 2023 10:47:22 +0800 Subject: refactor: move hashtab types to hashtab_defs.h (#26262) --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 02e0be9a94..e7fe7e17bc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -42,6 +42,7 @@ #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/grid_defs.h" +#include "nvim/hashtab.h" #include "nvim/highlight_group.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" -- cgit From 79b6ff28ad1204fbb4199b9092f5c578d88cb28e Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 28 Nov 2023 20:31:00 +0100 Subject: refactor: fix headers with IWYU --- src/nvim/eval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e7fe7e17bc..f4479d06a6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13,7 +13,7 @@ #include "nvim/api/private/converter.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" @@ -48,9 +48,9 @@ #include "nvim/keycodes.h" #include "nvim/lib/queue.h" #include "nvim/lua/executor.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/main.h" -#include "nvim/map.h" +#include "nvim/map_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -82,7 +82,7 @@ #include "nvim/ui_compositor.h" #include "nvim/usercmd.h" #include "nvim/version.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead -- cgit