diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 1706 |
1 files changed, 707 insertions, 999 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 64883e69a2..8682139b32 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6,7 +6,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/stat.h> #include <uv.h> #include "auto/config.h" @@ -21,9 +20,9 @@ #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" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" @@ -33,18 +32,15 @@ #include "nvim/eval/vars.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" -#include "nvim/event/process.h" +#include "nvim/event/proc.h" #include "nvim/event/time.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/ex_getln.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" -#include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" -#include "nvim/grid_defs.h" #include "nvim/hashtab.h" #include "nvim/highlight_group.h" #include "nvim/insexpand.h" @@ -66,14 +62,11 @@ #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" -#include "nvim/os/fileio.h" #include "nvim/os/fs.h" -#include "nvim/os/fs_defs.h" #include "nvim/os/lang.h" #include "nvim/os/os.h" #include "nvim/os/os_defs.h" #include "nvim/os/shell.h" -#include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" @@ -82,14 +75,9 @@ #include "nvim/regexp_defs.h" #include "nvim/runtime.h" #include "nvim/runtime_defs.h" -#include "nvim/search.h" #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/types_defs.h" -#include "nvim/ui.h" -#include "nvim/ui_compositor.h" -#include "nvim/ui_defs.h" -#include "nvim/usercmd.h" #include "nvim/version.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -106,7 +94,6 @@ 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[] @@ -121,6 +108,8 @@ static const char e_empty_function_name[] = N_("E1192: Empty function name"); 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 const char e_cannot_use_partial_here[] + = N_("E1265: Cannot use a partial here"); static char * const namespace_char = "abglstvw"; @@ -151,6 +140,12 @@ typedef struct { int fi_byte_idx; // byte index in fi_string } forinfo_T; +typedef enum { + GLV_FAIL, + GLV_OK, + GLV_STOP, +} glv_status_T; + // values for vv_flags: #define VV_COMPAT 1 // compatible, also used without "v:" #define VV_RO 2 // read-only @@ -330,7 +325,6 @@ static const char *const msgpack_type_names[] = { [kMPInteger] = "integer", [kMPFloat] = "float", [kMPString] = "string", - [kMPBinary] = "binary", [kMPArray] = "array", [kMPMap] = "map", [kMPExt] = "ext", @@ -341,7 +335,6 @@ const list_T *eval_msgpack_type_lists[] = { [kMPInteger] = NULL, [kMPFloat] = NULL, [kMPString] = NULL, - [kMPBinary] = NULL, [kMPArray] = NULL, [kMPMap] = NULL, [kMPExt] = NULL, @@ -696,7 +689,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, } bool err = false; - if (eval_to_bool(p_ccv, &err, NULL, false)) { + if (eval_to_bool(p_ccv, &err, NULL, false, true)) { err = true; } @@ -725,7 +718,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); + typval_T *tv = eval_expr_ext(p_dex, NULL, true); tv_free(tv); set_vim_var_string(VV_FNAME_IN, NULL, -1); @@ -747,7 +740,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch } // errors are ignored - typval_T *tv = eval_expr(p_pex, NULL); + typval_T *tv = eval_expr_ext(p_pex, NULL, true); tv_free(tv); set_vim_var_string(VV_FNAME_IN, NULL, -1); @@ -776,7 +769,8 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) /// @param skip only parse, don't execute /// /// @return true or false. -bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip) +bool eval_to_bool(char *arg, bool *error, exarg_T *eap, const bool skip, + const bool use_simple_function) { typval_T tv; bool retval = false; @@ -787,7 +781,9 @@ bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip) if (skip) { emsg_skip++; } - if (eval0(arg, &tv, eap, &evalarg) == FAIL) { + int r = use_simple_function ? eval0_simple_funccal(arg, &tv, eap, &evalarg) + : eval0(arg, &tv, eap, &evalarg); + if (r == FAIL) { *error = true; } else { *error = false; @@ -840,6 +836,80 @@ 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)); } +/// Evaluate a partial. +/// Pass arguments "argv[argc]". +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_partial(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + 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_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + + return OK; +} + +/// Evaluate an expression which is a function. +/// Pass arguments "argv[argc]". +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_func(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + char buf[NUMBUFLEN]; + 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_T funcexe = FUNCEXE_INIT; + funcexe.fe_evaluate = true; + if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { + return FAIL; + } + + return OK; +} + +/// Evaluate an expression, which is a string. +/// Return the result in "rettv" and OK or FAIL. +static int eval_expr_string(const typval_T *expr, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + char buf[NUMBUFLEN]; + char *s = (char *)tv_get_string_buf_chk(expr, buf); + if (s == NULL) { + return FAIL; + } + + s = skipwhite(s); + if (eval1_emsg(&s, rettv, NULL) == FAIL) { + return FAIL; + } + + if (*skipwhite(s) != NUL) { // check for trailing chars after expr + tv_clear(rettv); + semsg(_(e_invexpr2), s); + return FAIL; + } + + return OK; +} + /// Evaluate an expression, which can be a function, partial or string. /// Pass arguments "argv[argc]". /// Return the result in "rettv" and OK or FAIL. @@ -849,49 +919,14 @@ int eval_expr_typval(const typval_T *expr, bool want_func, typval_T *argv, int a typval_T *rettv) FUNC_ATTR_NONNULL_ALL { - char buf[NUMBUFLEN]; - funcexe_T funcexe = FUNCEXE_INIT; - 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; - } + return eval_expr_partial(expr, argv, argc, rettv); } 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; - if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL) { - return FAIL; - } + return eval_expr_func(expr, argv, argc, rettv); } else { - char *s = (char *)tv_get_string_buf_chk(expr, buf); - if (s == NULL) { - return FAIL; - } - s = skipwhite(s); - if (eval1_emsg(&s, rettv, NULL) == FAIL) { - return FAIL; - } - if (*skipwhite(s) != NUL) { // check for trailing chars after expr - tv_clear(rettv); - semsg(_(e_invexpr2), s); - return FAIL; - } + return eval_expr_string(expr, rettv); } + return OK; } @@ -996,14 +1031,17 @@ static char *typval2string(typval_T *tv, bool join_list) /// @param join_list when true convert a List into a sequence of lines. /// /// @return pointer to allocated memory, or NULL for failure. -char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap) +char *eval_to_string_eap(char *arg, const bool join_list, exarg_T *eap, + const bool use_simple_function) { typval_T tv; char *retval; evalarg_T evalarg; fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); - if (eval0(arg, &tv, NULL, &evalarg) == FAIL) { + int r = use_simple_function ? eval0_simple_funccal(arg, &tv, NULL, &evalarg) + : eval0(arg, &tv, NULL, &evalarg); + if (r == FAIL) { retval = NULL; } else { retval = typval2string(&tv, join_list); @@ -1014,16 +1052,16 @@ char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap) return retval; } -char *eval_to_string(char *arg, bool join_list) +char *eval_to_string(char *arg, const bool join_list, const bool use_simple_function) { - return eval_to_string_eap(arg, join_list, NULL); + return eval_to_string_eap(arg, join_list, NULL, use_simple_function); } /// Call eval_to_string() without using current local variables and using /// textlock. /// /// @param use_sandbox when true, use the sandbox. -char *eval_to_string_safe(char *arg, const bool use_sandbox) +char *eval_to_string_safe(char *arg, const bool use_sandbox, const bool use_simple_function) { char *retval; funccal_entry_T funccal_entry; @@ -1033,7 +1071,7 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox) sandbox++; } textlock++; - retval = eval_to_string(arg, false); + retval = eval_to_string(arg, false, use_simple_function); if (use_sandbox) { sandbox--; } @@ -1046,15 +1084,22 @@ char *eval_to_string_safe(char *arg, const bool use_sandbox) /// Evaluates "expr" silently. /// /// @return -1 for an error. -varnumber_T eval_to_number(char *expr) +varnumber_T eval_to_number(char *expr, const bool use_simple_function) { typval_T rettv; varnumber_T retval; char *p = skipwhite(expr); + int r = NOTDONE; emsg_off++; - if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) { + if (use_simple_function) { + r = may_call_simple_func(expr, &rettv); + } + if (r == NOTDONE) { + r = eval1(&p, &rettv, &EVALARG_EVALUATE); + } + if (r == FAIL) { retval = -1; } else { retval = tv_get_number_chk(&rettv, NULL); @@ -1071,12 +1116,26 @@ varnumber_T eval_to_number(char *expr) /// NULL when there is an error. typval_T *eval_expr(char *arg, exarg_T *eap) { + return eval_expr_ext(arg, eap, false); +} + +static typval_T *eval_expr_ext(char *arg, exarg_T *eap, const bool use_simple_function) +{ typval_T *tv = xmalloc(sizeof(*tv)); evalarg_T evalarg; fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); - if (eval0(arg, tv, eap, &evalarg) == FAIL) { + int r = NOTDONE; + + if (use_simple_function) { + r = eval0_simple_funccal(arg, tv, eap, &evalarg); + } + if (r == NOTDONE) { + r = eval0(arg, tv, eap, &evalarg); + } + + if (r == FAIL) { XFREE_CLEAR(tv); } @@ -1162,7 +1221,11 @@ list_T *eval_spell_expr(char *badword, char *expr) current_sctx = *ctx; } - if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) { + int r = may_call_simple_func(p, &rettv); + if (r == NOTDONE) { + r = eval1(&p, &rettv, &EVALARG_EVALUATE); + } + if (r == OK) { if (rettv.v_type != VAR_LIST) { tv_clear(&rettv); } else { @@ -1303,7 +1366,7 @@ int eval_foldexpr(win_T *wp, int *cp) const sctx_T saved_sctx = current_sctx; const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL); - char *arg = wp->w_p_fde; + char *arg = skipwhite(wp->w_p_fde); current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx; emsg_off++; @@ -1315,7 +1378,9 @@ int eval_foldexpr(win_T *wp, int *cp) typval_T tv; varnumber_T retval; - if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { + // Evaluate the expression. If the expression is "FuncName()" call the + // function directly. + if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = 0; } else { // If the result is a number, just return the number. @@ -1361,7 +1426,7 @@ Object eval_foldtext(win_T *wp) typval_T tv; Object retval; - if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { + if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = STRING_OBJ(NULL_STRING); } else { if (tv.v_type == VAR_LIST) { @@ -1382,22 +1447,232 @@ Object eval_foldtext(win_T *wp) return retval; } +/// Find the end of a variable or function name. Unlike find_name_end() this +/// does not recognize magic braces. +/// When "use_namespace" is true recognize "b:", "s:", etc. +/// Return a pointer to just after the name. Equal to "arg" if there is no +/// valid name. +static const char *to_name_end(const char *arg, bool use_namespace) +{ + // Quick check for valid starting character. + if (!eval_isnamec1(*arg)) { + return arg; + } + + const char *p; + for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p)) { + // Include a namespace such as "s:var" and "v:var". But "n:" is not + // and can be used in slice "[n:]". + if (*p == ':' && (p != arg + 1 + || !use_namespace + || vim_strchr("bgstvw", *arg) == NULL)) { + break; + } + } + return p; +} + +/// Get an Dict lval variable that can be assigned a value to: "name", +/// "name[expr]", "name[expr][expr]", "name.key", "name.key[expr]" etc. +/// "name" points to the start of the name. +/// If "rettv" is not NULL it points to the value to be assigned. +/// "unlet" is true for ":unlet": slightly different behavior when something is +/// wrong; must end in space or cmd separator. +/// +/// flags: +/// GLV_QUIET: do not give error messages +/// GLV_READ_ONLY: will not change the variable +/// GLV_NO_AUTOLOAD: do not use script autoloading +/// +/// The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on +/// failure. Returns GLV_STOP to stop processing the characters following +/// 'key_end'. +static glv_status_T get_lval_dict_item(lval_T *lp, char *name, char *key, int len, char **key_end, + typval_T *var1, int flags, bool unlet, typval_T *rettv) +{ + bool quiet = flags & GLV_QUIET; + char *p = *key_end; + + if (len == -1) { + // "[key]": get key from "var1" + key = (char *)tv_get_string(var1); // is number or string + } + lp->ll_list = NULL; + + // a NULL dict is equivalent with an empty dict + if (lp->ll_tv->vval.v_dict == NULL) { + lp->ll_tv->vval.v_dict = tv_dict_alloc(); + lp->ll_tv->vval.v_dict->dv_refcount++; + } + lp->ll_dict = lp->ll_tv->vval.v_dict; + + 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 + // g: dictionary). Disallow overwriting a builtin function. + if (rettv != NULL && lp->ll_dict->dv_scope != 0) { + char prevval; + if (len != -1) { + prevval = key[len]; + key[len] = NUL; + } else { + prevval = 0; // Avoid compiler warning. + } + 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; + } + if (wrong) { + return GLV_FAIL; + } + } + + if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) + && len == -1 && rettv == NULL) { + semsg(e_illvar, "v:['lua']"); + return GLV_FAIL; + } + + if (lp->ll_di == NULL) { + // Can't add "v:" or "a:" variable. + if (lp->ll_dict == &vimvardict + || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { + semsg(_(e_illvar), name); + return GLV_FAIL; + } + + // Key does not exist in dict: may need to add it. + if (*p == '[' || *p == '.' || unlet) { + if (!quiet) { + semsg(_(e_dictkey), key); + } + return GLV_FAIL; + } + if (len == -1) { + lp->ll_newkey = xstrdup(key); + } else { + lp->ll_newkey = xmemdupz(key, (size_t)len); + } + *key_end = p; + return GLV_STOP; + // existing variable, need to check if it can be changed + } else if (!(flags & GLV_READ_ONLY) + && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) + || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { + return GLV_FAIL; + } + + lp->ll_tv = &lp->ll_di->di_tv; + + return GLV_OK; +} + +/// Get an blob lval variable that can be assigned a value to: "name", +/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc. +/// +/// 'var1' specifies the starting blob index and 'var2' specifies the ending +/// blob index. If the first index is not specified in a range, then 'empty1' +/// is true. If 'quiet' is true, then error messages are not displayed for +/// invalid indexes. +/// +/// The blob is returned in 'lp'. Returns OK on success and FAIL on failure. +static int get_lval_blob(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, bool quiet) +{ + const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); + + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (int)tv_get_number(var1); + } + + if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { + return FAIL; + } + if (lp->ll_range && !lp->ll_empty2) { + lp->ll_n2 = (int)tv_get_number(var2); + if (tv_blob_check_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL) { + return FAIL; + } + } + + lp->ll_blob = lp->ll_tv->vval.v_blob; + lp->ll_tv = NULL; + + return OK; +} + +/// Get a List lval variable that can be assigned a value to: "name", +/// "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc. +/// +/// 'var1' specifies the starting List index and 'var2' specifies the ending +/// List index. If the first index is not specified in a range, then 'empty1' +/// is true. If 'quiet' is true, then error messages are not displayed for +/// invalid indexes. +/// +/// The List is returned in 'lp'. Returns OK on success and FAIL on failure. +static int get_lval_list(lval_T *lp, typval_T *var1, typval_T *var2, bool empty1, int flags, + bool quiet) +{ + // Get the number and item for the only or first index of the List. + if (empty1) { + lp->ll_n1 = 0; + } else { + // Is number or string. + lp->ll_n1 = (int)tv_get_number(var1); + } + + lp->ll_dict = NULL; + lp->ll_list = lp->ll_tv->vval.v_list; + lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); + if (lp->ll_li == NULL) { + return FAIL; + } + + // May need to find the item or absolute index for the second + // index of a range. + // 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 = (int)tv_get_number(var2); // Is number or string. + if (tv_list_check_range_index_two(lp->ll_list, + &lp->ll_n1, lp->ll_li, + &lp->ll_n2, quiet) == FAIL) { + return FAIL; + } + } + + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); + + return OK; +} + /// Get the lval of a list/dict/blob subitem starting at "p". Loop /// until no more [idx] or .key is following. /// +/// If "rettv" is not NULL it points to the value to be assigned. +/// "unlet" is true for ":unlet". +/// /// @param[in] flags @see GetLvalFlags. /// /// @return A pointer to the character after the subscript on success or NULL on /// failure. static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv, hashtab_T *ht, - dictitem_T *v, int unlet, int flags) + dictitem_T *v, bool unlet, int flags) { - int quiet = flags & GLV_QUIET; + bool quiet = flags & GLV_QUIET; typval_T var1; var1.v_type = VAR_UNKNOWN; typval_T var2; var2.v_type = VAR_UNKNOWN; bool empty1 = false; + int rc = FAIL; // Loop until no more [idx] or .key is following. while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { @@ -1427,7 +1702,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_("E708: [:] must come last")); } - return NULL; + goto done; } int len = -1; @@ -1451,12 +1726,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } else { empty1 = false; if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) { // Recursive! - return NULL; + goto done; } if (!tv_check_str(&var1)) { // Not a number or string. - tv_clear(&var1); - return NULL; + goto done; } p = skipwhite(p); } @@ -1467,8 +1741,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_(e_cannot_slice_dictionary)); } - tv_clear(&var1); - return NULL; + goto done; } if (rettv != NULL && !(rettv->v_type == VAR_LIST && rettv->vval.v_list != NULL) @@ -1476,8 +1749,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_("E709: [:] requires a List or Blob value")); } - tv_clear(&var1); - return NULL; + goto done; } p = skipwhite(p + 1); if (*p == ']') { @@ -1486,14 +1758,11 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv lp->ll_empty2 = false; // Recursive! if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL) { - tv_clear(&var1); - return NULL; + goto done; } if (!tv_check_str(&var2)) { // Not a number or string. - tv_clear(&var1); - tv_clear(&var2); - return NULL; + goto done; } } lp->ll_range = true; @@ -1505,9 +1774,7 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv if (!quiet) { emsg(_(e_missbrac)); } - tv_clear(&var1); - tv_clear(&var2); - return NULL; + goto done; } // Skip to past ']'. @@ -1515,142 +1782,37 @@ static char *get_lval_subscript(lval_T *lp, char *p, char *name, typval_T *rettv } if (lp->ll_tv->v_type == VAR_DICT) { - if (len == -1) { - // "[key]": get key from "var1" - key = (char *)tv_get_string(&var1); // is number or string - } - lp->ll_list = NULL; - lp->ll_dict = lp->ll_tv->vval.v_dict; - 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 - // g: dictionary). Disallow overwriting a builtin function. - if (rettv != NULL && lp->ll_dict->dv_scope != 0) { - char prevval; - if (len != -1) { - prevval = key[len]; - key[len] = NUL; - } else { - prevval = 0; // Avoid compiler warning. - } - 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; - } - if (wrong) { - tv_clear(&var1); - return NULL; - } - } - - if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) - && len == -1 && rettv == NULL) { - tv_clear(&var1); - semsg(e_illvar, "v:['lua']"); - return NULL; + glv_status_T glv_status = get_lval_dict_item(lp, name, key, len, &p, &var1, + flags, unlet, rettv); + if (glv_status == GLV_FAIL) { + goto done; } - - if (lp->ll_di == NULL) { - // Can't add "v:" or "a:" variable. - if (lp->ll_dict == &vimvardict - || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { - semsg(_(e_illvar), name); - tv_clear(&var1); - return NULL; - } - - // Key does not exist in dict: may need to add it. - if (*p == '[' || *p == '.' || unlet) { - if (!quiet) { - semsg(_(e_dictkey), key); - } - tv_clear(&var1); - return NULL; - } - if (len == -1) { - lp->ll_newkey = xstrdup(key); - } else { - lp->ll_newkey = xmemdupz(key, (size_t)len); - } - tv_clear(&var1); + if (glv_status == GLV_STOP) { break; - // existing variable, need to check if it can be changed - } else if (!(flags & GLV_READ_ONLY) - && (var_check_ro(lp->ll_di->di_flags, name, (size_t)(p - name)) - || var_check_lock(lp->ll_di->di_flags, name, (size_t)(p - name)))) { - tv_clear(&var1); - return NULL; } - - tv_clear(&var1); - lp->ll_tv = &lp->ll_di->di_tv; } else if (lp->ll_tv->v_type == VAR_BLOB) { - // Get the number and item for the only or first index of the List. - if (empty1) { - lp->ll_n1 = 0; - } else { - // Is number or string. - lp->ll_n1 = (int)tv_get_number(&var1); + if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL) { + goto done; } - tv_clear(&var1); - - const int bloblen = tv_blob_len(lp->ll_tv->vval.v_blob); - if (tv_blob_check_index(bloblen, lp->ll_n1, quiet) == FAIL) { - tv_clear(&var2); - return NULL; - } - if (lp->ll_range && !lp->ll_empty2) { - 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; - } - } - lp->ll_blob = lp->ll_tv->vval.v_blob; - lp->ll_tv = NULL; break; } else { - // Get the number and item for the only or first index of the List. - if (empty1) { - lp->ll_n1 = 0; - } else { - // Is number or string. - lp->ll_n1 = (int)tv_get_number(&var1); - } - tv_clear(&var1); - - lp->ll_dict = NULL; - lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); - if (lp->ll_li == NULL) { - tv_clear(&var2); - return NULL; - } - - // May need to find the item or absolute index for the second - // index of a range. - // 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 = (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, - &lp->ll_n2, quiet) == FAIL) { - return NULL; - } + if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL) { + goto done; } - - lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); } + + tv_clear(&var1); + tv_clear(&var2); + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; } + rc = OK; + +done: tv_clear(&var1); - return p; + tv_clear(&var2); + return rc == OK ? p : NULL; } /// Get an lvalue @@ -2381,9 +2543,10 @@ void clear_evalarg(evalarg_T *evalarg, exarg_T *eap) } } -/// 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. +/// The "eval" functions have an "evalarg" argument: When NULL or +/// "evalarg->eval_flags" does not have EVAL_EVALUATE, then the argument is only +/// parsed but not executed. The functions may return OK, but the rettv will be +/// of type VAR_UNKNOWN. The functions still returns FAIL for a syntax error. /// Handle zero level expression. /// This calls eval1() and handles error message and nextcmd. @@ -2442,6 +2605,42 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) return ret; } +/// If "arg" is a simple function call without arguments then call it and return +/// the result. Otherwise return NOTDONE. +static int may_call_simple_func(const char *arg, typval_T *rettv) +{ + const char *parens = strstr(arg, "()"); + int r = NOTDONE; + + // If the expression is "FuncName()" then we can skip a lot of overhead. + if (parens != NULL && *skipwhite(parens + 2) == NUL) { + if (strnequal(arg, "v:lua.", 6)) { + const char *p = arg + 6; + if (p != parens && skip_luafunc_name(p) == parens) { + r = call_simple_luafunc(p, (size_t)(parens - p), rettv); + } + } else { + const char *p = strncmp(arg, "<SNR>", 5) == 0 ? skipdigits(arg + 5) : arg; + if (to_name_end(p, true) == parens) { + r = call_simple_func(arg, (size_t)(parens - arg), rettv); + } + } + } + return r; +} + +/// Handle zero level expression with optimization for a simple function call. +/// Same arguments and return value as eval0(). +static int eval0_simple_funccal(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg) +{ + int r = may_call_simple_func(arg, rettv); + + if (r == NOTDONE) { + r = eval0(arg, rettv, eap, evalarg); + } + return r; +} + /// Handle top level expression: /// expr2 ? expr1 : expr1 /// expr2 ?? expr1 @@ -2832,6 +3031,93 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2) return OK; } +/// Concatenate strings "tv1" and "tv2" and store the result in "tv1". +static int eval_concat_str(typval_T *tv1, typval_T *tv2) +{ + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(tv1, buf1); + const char *const s2 = tv_get_string_buf_chk(tv2, buf2); + if (s2 == NULL) { // Type error? + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + + char *p = concat_str(s1, s2); + tv_clear(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = p; + + return OK; +} + +/// Add or subtract numbers "tv1" and "tv2" and store the result in "tv1". +/// The numbers can be whole numbers or floats. +static int eval_addsub_number(typval_T *tv1, typval_T *tv2, int op) +{ + bool error = false; + varnumber_T n1, n2; + float_T f1 = 0; + float_T f2 = 0; + + if (tv1->v_type == VAR_FLOAT) { + f1 = tv1->vval.v_float; + n1 = 0; + } else { + n1 = tv_get_number_chk(tv1, &error); + if (error) { + // This can only happen for "list + non-list" or + // "blob + non-blob". For "non-list + ..." or + // "something - ...", we returned before evaluating the + // 2nd operand. + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + if (tv2->v_type == VAR_FLOAT) { + f1 = (float_T)n1; + } + } + if (tv2->v_type == VAR_FLOAT) { + f2 = tv2->vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(tv2, &error); + if (error) { + tv_clear(tv1); + tv_clear(tv2); + return FAIL; + } + if (tv1->v_type == VAR_FLOAT) { + f2 = (float_T)n2; + } + } + tv_clear(tv1); + + // If there is a float on either side the result is a float. + if (tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT) { + if (op == '+') { + f1 = f1 + f2; + } else { + f1 = f1 - f2; + } + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f1; + } else { + if (op == '+') { + n1 = n1 + n2; + } else { + n1 = n1 - n2; + } + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n1; + } + + return OK; +} + /// Handle fourth level expression: /// + number addition, concatenation of list or blob /// - number subtraction @@ -2887,20 +3173,9 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) if (evaluate) { // Compute the result. if (op == '.') { - char buf1[NUMBUFLEN]; - char buf2[NUMBUFLEN]; - // s1 already checked - const char *const s1 = tv_get_string_buf(rettv, buf1); - const char *const s2 = tv_get_string_buf_chk(&var2, buf2); - if (s2 == NULL) { // Type error? - tv_clear(rettv); - tv_clear(&var2); + if (eval_concat_str(rettv, &var2) == FAIL) { return FAIL; } - char *p = concat_str(s1, s2); - 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) { eval_addblob(rettv, &var2); } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { @@ -2908,62 +3183,8 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) return FAIL; } } else { - bool error = false; - varnumber_T n1, n2; - float_T f1 = 0; - float_T f2 = 0; - - if (rettv->v_type == VAR_FLOAT) { - f1 = rettv->vval.v_float; - n1 = 0; - } else { - n1 = tv_get_number_chk(rettv, &error); - if (error) { - // This can only happen for "list + non-list" or - // "blob + non-blob". For "non-list + ..." or - // "something - ...", we returned before evaluating the - // 2nd operand. - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } - if (var2.v_type == VAR_FLOAT) { - f1 = (float_T)n1; - } - } - if (var2.v_type == VAR_FLOAT) { - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = tv_get_number_chk(&var2, &error); - if (error) { - tv_clear(rettv); - tv_clear(&var2); - return FAIL; - } - if (rettv->v_type == VAR_FLOAT) { - f2 = (float_T)n2; - } - } - tv_clear(rettv); - - // If there is a float on either side the result is a float. - if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { - if (op == '+') { - f1 = f1 + f2; - } else { - f1 = f1 - f2; - } - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f1; - } else { - if (op == '+') { - n1 = n1 + n2; - } else { - n1 = n1 - n2; - } - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; + if (eval_addsub_number(rettv, &var2, op) == FAIL) { + return FAIL; } } tv_clear(&var2); @@ -2972,6 +3193,85 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) return OK; } +/// Multiply or divide or compute the modulo of numbers "tv1" and "tv2" and +/// store the result in "tv1". The numbers can be whole numbers or floats. +static int eval_multdiv_number(typval_T *tv1, typval_T *tv2, int op) + FUNC_ATTR_NO_SANITIZE_UNDEFINED +{ + varnumber_T n1, n2; + bool use_float = false; + + float_T f1 = 0; + float_T f2 = 0; + bool error = false; + if (tv1->v_type == VAR_FLOAT) { + f1 = tv1->vval.v_float; + use_float = true; + n1 = 0; + } else { + n1 = tv_get_number_chk(tv1, &error); + } + tv_clear(tv1); + if (error) { + tv_clear(tv2); + return FAIL; + } + + if (tv2->v_type == VAR_FLOAT) { + if (!use_float) { + f1 = (float_T)n1; + use_float = true; + } + f2 = tv2->vval.v_float; + n2 = 0; + } else { + n2 = tv_get_number_chk(tv2, &error); + tv_clear(tv2); + if (error) { + return FAIL; + } + if (use_float) { + f2 = (float_T)n2; + } + } + + // Compute the result. + // When either side is a float the result is a float. + if (use_float) { + if (op == '*') { + f1 = f1 * f2; + } else if (op == '/') { + // uncrustify:off + + // Division by zero triggers error from AddressSanitizer + f1 = (f2 == 0 ? ( +#ifdef NAN + f1 == 0 ? (float_T)NAN : +#endif + (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); + + // uncrustify:on + } else { + emsg(_("E804: Cannot use '%' with Float")); + return FAIL; + } + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f1; + } else { + if (op == '*') { + n1 = n1 * n2; + } else if (op == '/') { + n1 = num_divide(n1, n2); + } else { + n1 = num_modulus(n1, n2); + } + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n1; + } + + return OK; +} + /// Handle fifth level expression: /// - * number multiplication /// - / number division @@ -2985,10 +3285,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) /// float /// @return OK or FAIL. static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) - FUNC_ATTR_NO_SANITIZE_UNDEFINED { - bool use_float = false; - // Get the first variable. if (eval7(arg, rettv, evalarg, want_string) == FAIL) { return FAIL; @@ -3001,26 +3298,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan break; } - varnumber_T n1, n2; - float_T f1 = 0; - float_T 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; - use_float = true; - n1 = 0; - } else { - n1 = tv_get_number_chk(rettv, &error); - } - tv_clear(rettv); - if (error) { - return FAIL; - } - } else { - n1 = 0; - } // Get the second variable. *arg = skipwhite(*arg + 1); @@ -3030,56 +3308,9 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan } if (evaluate) { - if (var2.v_type == VAR_FLOAT) { - if (!use_float) { - f1 = (float_T)n1; - use_float = true; - } - f2 = var2.vval.v_float; - n2 = 0; - } else { - n2 = tv_get_number_chk(&var2, &error); - tv_clear(&var2); - if (error) { - return FAIL; - } - if (use_float) { - f2 = (float_T)n2; - } - } - // Compute the result. - // When either side is a float the result is a float. - if (use_float) { - if (op == '*') { - f1 = f1 * f2; - } else if (op == '/') { - // uncrustify:off - - // Division by zero triggers error from AddressSanitizer - f1 = (f2 == 0 ? ( -#ifdef NAN - f1 == 0 ? (float_T)NAN : -#endif - (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); - - // uncrustify:on - } else { - emsg(_("E804: Cannot use '%' with Float")); - return FAIL; - } - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = f1; - } else { - if (op == '*') { - n1 = n1 * n2; - } else if (op == '/') { - n1 = num_divide(n1, n2); - } else { - n1 = num_modulus(n1, n2); - } - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = n1; + if (eval_multdiv_number(rettv, &var2, op) == FAIL) { + return FAIL; } } } @@ -3183,14 +3414,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan ret = eval_list(arg, rettv, evalarg); break; - // Dictionary: #{key: val, key: val} + // Literal Dictionary: #{key: val, key: val} case '#': - if ((*arg)[1] == '{') { - (*arg)++; - ret = eval_dict(arg, rettv, evalarg, true); - } else { - ret = NOTDONE; - } + ret = eval_lit_dict(arg, rettv, evalarg); break; // Lambda: {arg, arg -> expr} @@ -3480,20 +3706,22 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const int len; char *name = *arg; char *lua_funcname = NULL; + char *alias = NULL; if (strnequal(name, "v:lua.", 6)) { lua_funcname = name + 6; *arg = (char *)skip_luafunc_name(lua_funcname); *arg = skipwhite(*arg); // to detect trailing whitespace later len = (int)(*arg - lua_funcname); } else { - char *alias; len = get_name_len((const char **)arg, &alias, evaluate, true); if (alias != NULL) { name = alias; } } - int ret; + char *tofree = NULL; + int ret = OK; + if (len <= 0) { if (verbose) { if (lua_funcname == NULL) { @@ -3504,25 +3732,79 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const } ret = FAIL; } else { - if (**arg != '(') { - if (verbose) { - semsg(_(e_missingparen), name); - } - ret = FAIL; - } else if (ascii_iswhite((*arg)[-1])) { - if (verbose) { - emsg(_(e_nowhitespace)); + *arg = skipwhite(*arg); + + // If there is no "(" immediately following, but there is further on, + // it can be "dict.Func()", "list[nr]", etc. + // Does not handle anything where "(" is part of the expression. + char *paren; + if (**arg != '(' && lua_funcname == NULL && alias == NULL + && (paren = vim_strchr(*arg, '(')) != NULL) { + *arg = name; + *paren = NUL; + typval_T ref; + ref.v_type = VAR_UNKNOWN; + if (eval7(arg, &ref, evalarg, false) == FAIL) { + *arg = name + len; + ret = FAIL; + } else if (*skipwhite(*arg) != NUL) { + if (verbose) { + semsg(_(e_trailing_arg), *arg); + } + ret = FAIL; + } else if (ref.v_type == VAR_FUNC && ref.vval.v_string != NULL) { + name = ref.vval.v_string; + ref.vval.v_string = NULL; + tofree = name; + len = (int)strlen(name); + } else if (ref.v_type == VAR_PARTIAL && ref.vval.v_partial != NULL) { + if (ref.vval.v_partial->pt_argc > 0 || ref.vval.v_partial->pt_dict != NULL) { + if (verbose) { + emsg(_(e_cannot_use_partial_here)); + } + ret = FAIL; + } else { + name = xstrdup(partial_name(ref.vval.v_partial)); + tofree = name; + if (name == NULL) { + ret = FAIL; + name = *arg; + } else { + len = (int)strlen(name); + } + } + } else { + if (verbose) { + semsg(_(e_not_callable_type_str), name); + } + ret = FAIL; } - ret = FAIL; - } else if (lua_funcname != NULL) { - if (evaluate) { - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = vvlua_partial; - rettv->vval.v_partial->pt_refcount++; + tv_clear(&ref); + *paren = '('; + } + + if (ret == OK) { + if (**arg != '(') { + if (verbose) { + semsg(_(e_missingparen), name); + } + ret = FAIL; + } else if (ascii_iswhite((*arg)[-1])) { + if (verbose) { + emsg(_(e_nowhitespace)); + } + ret = FAIL; + } else if (lua_funcname != NULL) { + if (evaluate) { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = vvlua_partial; + rettv->vval.v_partial->pt_refcount++; + } + ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname); + } else { + ret = eval_func(arg, evalarg, name, len, rettv, + evaluate ? EVAL_EVALUATE : 0, &base); } - ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname); - } else { - ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base); } } @@ -3531,6 +3813,11 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const if (evaluate) { tv_clear(&base); } + xfree(tofree); + + if (alias != NULL) { + xfree(alias); + } return ret; } @@ -3669,7 +3956,7 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) /// slice() function void f_slice(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - if (check_can_index(argvars, true, false) != OK) { + if (check_can_index(&argvars[0], true, false) != OK) { return; } @@ -4386,7 +4673,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) if (d1 != d2) { return false; } - } else if (!tv_dict_equal(d1, d2, ic, true)) { + } else if (!tv_dict_equal(d1, d2, ic)) { return false; } @@ -4398,7 +4685,7 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic) } for (int i = 0; i < a1; i++) { if (!tv_equal(tv1->vval.v_partial->pt_argv + i, - tv2->vval.v_partial->pt_argv + i, ic, true)) { + tv2->vval.v_partial->pt_argv + i, ic)) { return false; } } @@ -4493,19 +4780,6 @@ bool garbage_collect(bool testing) FOR_ALL_BUFFERS(buf) { // buffer-local variables ABORTING(set_ref_in_item)(&buf->b_bufvar.di_tv, copyID, NULL, NULL); - // buffer marks (ShaDa additional data) - ABORTING(set_ref_in_fmark)(buf->b_last_cursor, copyID); - ABORTING(set_ref_in_fmark)(buf->b_last_insert, copyID); - ABORTING(set_ref_in_fmark)(buf->b_last_change, copyID); - for (size_t i = 0; i < NMARKS; i++) { - ABORTING(set_ref_in_fmark)(buf->b_namedm[i], copyID); - } - // buffer change list (ShaDa additional data) - for (int i = 0; i < buf->b_changelistlen; i++) { - ABORTING(set_ref_in_fmark)(buf->b_changelist[i], copyID); - } - // buffer ShaDa additional data - ABORTING(set_ref_dict)(buf->additional_data, copyID); // buffer callback functions ABORTING(set_ref_in_callback)(&buf->b_prompt_callback, copyID, NULL, NULL); @@ -4528,10 +4802,6 @@ bool garbage_collect(bool testing) FOR_ALL_TAB_WINDOWS(tp, wp) { // window-local variables ABORTING(set_ref_in_item)(&wp->w_winvar.di_tv, copyID, NULL, NULL); - // window jump list (ShaDa additional data) - for (int i = 0; i < wp->w_jumplistlen; i++) { - ABORTING(set_ref_in_fmark)(wp->w_jumplist[i].fmark, copyID); - } } // window-local variables in autocmd windows for (int i = 0; i < AUCMD_WIN_COUNT; i++) { @@ -4548,9 +4818,6 @@ bool garbage_collect(bool testing) char name = NUL; bool is_unnamed = false; reg_iter = op_global_reg_iter(reg_iter, &name, ®, &is_unnamed); - if (name != NUL) { - ABORTING(set_ref_dict)(reg.additional_data, copyID); - } } while (reg_iter != ITER_REGISTER_NULL); } @@ -4561,9 +4828,6 @@ bool garbage_collect(bool testing) xfmark_T fm; char name = NUL; mark_iter = mark_global_iter(mark_iter, &name, &fm); - if (name != NUL) { - ABORTING(set_ref_dict)(fm.fmark.additional_data, copyID); - } } while (mark_iter != NULL); } @@ -4605,36 +4869,6 @@ bool garbage_collect(bool testing) // v: vars ABORTING(set_ref_in_ht)(&vimvarht, copyID, NULL); - // history items (ShaDa additional elements) - if (p_hi) { - for (int i = 0; i < HIST_COUNT; i++) { - const void *iter = NULL; - do { - histentry_T hist; - iter = hist_iter(iter, (uint8_t)i, false, &hist); - if (hist.hisstr != NULL) { - ABORTING(set_ref_list)(hist.additional_elements, copyID); - } - } while (iter != NULL); - } - } - - // previously used search/substitute patterns (ShaDa additional data) - { - SearchPattern pat; - get_search_pattern(&pat); - ABORTING(set_ref_dict)(pat.additional_data, copyID); - get_substitute_pattern(&pat); - ABORTING(set_ref_dict)(pat.additional_data, copyID); - } - - // previously used replacement string - { - SubReplacementString sub; - sub_get_replacement(&sub); - ABORTING(set_ref_list)(sub.additional_elements, copyID); - } - ABORTING(set_ref_in_quickfix)(copyID); bool did_free = false; @@ -4916,52 +5150,6 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack return abort; } -/// Mark all lists and dicts referenced in given mark -/// -/// @return true if setting references failed somehow. -static inline bool set_ref_in_fmark(fmark_T fm, int copyID) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (fm.additional_data != NULL - && fm.additional_data->dv_copyID != copyID) { - fm.additional_data->dv_copyID = copyID; - return set_ref_in_ht(&fm.additional_data->dv_hashtab, copyID, NULL); - } - return false; -} - -/// Mark all lists and dicts referenced in given list and the list itself -/// -/// @return true if setting references failed somehow. -static inline bool set_ref_list(list_T *list, int copyID) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (list != NULL) { - typval_T tv = (typval_T) { - .v_type = VAR_LIST, - .vval = { .v_list = list } - }; - return set_ref_in_item(&tv, copyID, NULL, NULL); - } - return false; -} - -/// Mark all lists and dicts referenced in given dict and the dict itself -/// -/// @return true if setting references failed somehow. -static inline bool set_ref_dict(dict_T *dict, int copyID) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (dict != NULL) { - typval_T tv = (typval_T) { - .v_type = VAR_DICT, - .vval = { .v_dict = dict } - }; - return set_ref_in_item(&tv, copyID, NULL, NULL); - } - return false; -} - /// Get the key for #{key: val} into "tv" and advance "arg". /// /// @return FAIL when there is no valid key. @@ -5093,6 +5281,24 @@ failret: return OK; } +/// Evaluate a literal dictionary: #{key: val, key: val} +/// "*arg" points to the "#". +/// On return, "*arg" points to the character after the Dict. +/// Return OK or FAIL. Returns NOTDONE for {expr}. +static int eval_lit_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg) +{ + int ret = OK; + + if ((*arg)[1] == '{') { + (*arg)++; + ret = eval_dict(arg, rettv, evalarg, true); + } else { + ret = NOTDONE; + } + + return ret; +} + /// Convert the string to a floating point number /// /// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to @@ -5569,317 +5775,6 @@ void f_foreach(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) filter_map(argvars, rettv, FILTERMAP_FOREACH); } -/// "function()" function -/// "funcref()" function -void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) -{ - char *s; - char *name; - bool use_string = false; - partial_T *arg_pt = NULL; - char *trans_name = NULL; - - if (argvars[0].v_type == VAR_FUNC) { - // function(MyFunc, [arg], dict) - s = argvars[0].vval.v_string; - } else if (argvars[0].v_type == VAR_PARTIAL - && argvars[0].vval.v_partial != NULL) { - // function(dict.MyFunc, [arg]) - arg_pt = argvars[0].vval.v_partial; - s = partial_name(arg_pt); - // TODO(bfredl): do the entire nlua_is_table_from_lua dance - } else { - // function('MyFunc', [arg], dict) - s = (char *)tv_get_string(&argvars[0]); - use_string = true; - } - - if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { - name = s; - trans_name = save_function_name(&name, false, - TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL); - if (*name != NUL) { - s = NULL; - } - } - 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]) : 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(trans_name))) { - semsg(_("E700: Unknown function: %s"), s); - } else { - int dict_idx = 0; - int arg_idx = 0; - list_T *list = NULL; - if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { - // Expand s: and <SID> into <SNR>nr_, so that the function can - // also be called from another script. Using trans_function_name() - // would also work, but some plugins depend on the name being - // printable text. - name = get_scriptlocal_funcname(s); - } else { - name = xstrdup(s); - } - - if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) { - // function(name, [args], dict) - arg_idx = 1; - dict_idx = 2; - } else if (argvars[1].v_type == VAR_DICT) { - // function(name, dict) - dict_idx = 1; - } else { - // function(name, [args]) - arg_idx = 1; - } - if (dict_idx > 0) { - if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) { - xfree(name); - goto theend; - } - if (argvars[dict_idx].vval.v_dict == NULL) { - dict_idx = 0; - } - } - if (arg_idx > 0) { - if (argvars[arg_idx].v_type != VAR_LIST) { - emsg(_("E923: Second argument of function() must be " - "a list or a dict")); - xfree(name); - goto theend; - } - list = argvars[arg_idx].vval.v_list; - if (tv_list_len(list) == 0) { - arg_idx = 0; - } else if (tv_list_len(list) > MAX_FUNC_ARGS) { - emsg_funcname(e_toomanyarg, s); - xfree(name); - goto theend; - } - } - } - if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { - partial_T *const pt = xcalloc(1, sizeof(*pt)); - - // result is a VAR_PARTIAL - if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { - const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); - const int lv_len = tv_list_len(list); - - pt->pt_argc = arg_len + lv_len; - pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc); - int i = 0; - for (; i < arg_len; i++) { - tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); - } - if (lv_len > 0) { - TV_LIST_ITER(list, li, { - tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); - }); - } - } - - // For "function(dict.func, [], dict)" and "func" is a partial - // use "dict". That is backwards compatible. - if (dict_idx > 0) { - // The dict is bound explicitly, pt_auto is false - pt->pt_dict = argvars[dict_idx].vval.v_dict; - (pt->pt_dict->dv_refcount)++; - } else if (arg_pt != NULL) { - // If the dict was bound automatically the result is also - // bound automatically. - pt->pt_dict = arg_pt->pt_dict; - pt->pt_auto = arg_pt->pt_auto; - if (pt->pt_dict != NULL) { - (pt->pt_dict->dv_refcount)++; - } - } - - pt->pt_refcount = 1; - if (arg_pt != NULL && arg_pt->pt_func != NULL) { - pt->pt_func = arg_pt->pt_func; - func_ptr_ref(pt->pt_func); - xfree(name); - } else if (is_funcref) { - pt->pt_func = find_func(trans_name); - func_ptr_ref(pt->pt_func); - xfree(name); - } else { - pt->pt_name = name; - func_ref(name); - } - - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = pt; - } else { - // result is a VAR_FUNC - rettv->v_type = VAR_FUNC; - rettv->vval.v_string = name; - func_ref(name); - } - } -theend: - xfree(trans_name); -} - -/// Get the line number from Vimscript object -/// -/// @note Unlike tv_get_lnum(), this one supports only "$" special string. -/// -/// @param[in] tv Object to get value from. Is expected to be a number or -/// a special string "$". -/// @param[in] buf Buffer to take last line number from in case tv is "$". May -/// be NULL, in this case "$" results in zero return. -/// -/// @return Line number or 0 in case of error. -linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (tv->v_type == VAR_STRING - && tv->vval.v_string != NULL - && tv->vval.v_string[0] == '$' - && tv->vval.v_string[1] == NUL - && buf != NULL) { - return buf->b_ml.ml_line_count; - } - return (linenr_T)tv_get_number_chk(tv, NULL); -} - -/// This function is used by f_input() and f_inputdialog() functions. The third -/// argument to f_input() specifies the type of completion to use at the -/// prompt. The third argument to f_inputdialog() specifies the value to return -/// when the user cancels the prompt. -void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, - const bool secret) - FUNC_ATTR_NONNULL_ALL -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - const char *prompt; - const char *defstr = ""; - typval_T *cancelreturn = NULL; - typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; - const char *xp_name = NULL; - Callback input_callback = { .type = kCallbackNone }; - char prompt_buf[NUMBUFLEN]; - char defstr_buf[NUMBUFLEN]; - char cancelreturn_buf[NUMBUFLEN]; - char xp_name_buf[NUMBUFLEN]; - char def[1] = { 0 }; - if (argvars[0].v_type == VAR_DICT) { - if (argvars[1].v_type != VAR_UNKNOWN) { - emsg(_("E5050: {opts} must be the only argument")); - return; - } - dict_T *const dict = argvars[0].vval.v_dict; - prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); - if (prompt == NULL) { - return; - } - defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); - if (defstr == NULL) { - return; - } - dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); - if (cancelreturn_di != NULL) { - cancelreturn = &cancelreturn_di->di_tv; - } - xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), - xp_name_buf, def); - if (xp_name == NULL) { // error - return; - } - if (xp_name == def) { // default to NULL - xp_name = NULL; - } - if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { - return; - } - } else { - prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); - if (prompt == NULL) { - return; - } - if (argvars[1].v_type != VAR_UNKNOWN) { - defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); - if (defstr == NULL) { - return; - } - if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); - if (strarg2 == NULL) { - return; - } - if (inputdialog) { - cancelreturn_strarg2.v_type = VAR_STRING; - cancelreturn_strarg2.vval.v_string = (char *)strarg2; - cancelreturn = &cancelreturn_strarg2; - } else { - xp_name = strarg2; - } - } - } - } - - int xp_type = EXPAND_NOTHING; - char *xp_arg = NULL; - if (xp_name != NULL) { - // input() with a third argument: completion - const int xp_namelen = (int)strlen(xp_name); - - uint32_t argt = 0; - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, - &argt, &xp_arg) == FAIL) { - return; - } - } - - const bool cmd_silent_save = cmd_silent; - - cmd_silent = false; // Want to see the prompt. - // Only the part of the message after the last NL is considered as - // prompt for the command line, unlsess cmdline is externalized - const char *p = prompt; - if (!ui_has(kUICmdline)) { - const char *lastnl = strrchr(prompt, '\n'); - if (lastnl != NULL) { - p = lastnl + 1; - msg_start(); - msg_clr_eos(); - msg_puts_len(prompt, p - prompt, echo_attr); - msg_didout = false; - msg_starthere(); - } - } - cmdline_row = msg_row; - - stuffReadbuffSpec(defstr); - - const int save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, echo_attr, xp_type, xp_arg, - input_callback); - ex_normal_busy = save_ex_normal_busy; - callback_free(&input_callback); - - if (rettv->vval.v_string == NULL && cancelreturn != NULL) { - tv_copy(cancelreturn, rettv); - } - - xfree(xp_arg); - - // Since the user typed this, no need to wait for return. - need_wait_return = false; - msg_didout = false; - cmd_silent = cmd_silent_save; -} - /// Builds a process argument vector from a Vimscript object (typval_T). /// /// @param[in] cmd_tv Vimscript object @@ -5948,56 +5843,6 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) return argv; } -void return_register(int regname, typval_T *rettv) -{ - char buf[2] = { (char)regname, 0 }; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = xstrdup(buf); -} - -void screenchar_adjust(ScreenGrid **grid, int *row, int *col) -{ - // TODO(bfredl): this is a hack for legacy tests which use screenchar() - // to check printed messages on the screen (but not floats etc - // as these are not legacy features). If the compositor is refactored to - // have its own buffer, this should just read from it instead. - msg_scroll_flush(); - - *grid = ui_comp_get_grid_at_coord(*row, *col); - - // Make `row` and `col` relative to the grid - *row -= (*grid)->comp_row; - *col -= (*grid)->comp_col; -} - -/// "stdpath()" helper for list results -void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) - FUNC_ATTR_NONNULL_ALL -{ - list_T *const list = tv_list_alloc(kListLenShouldKnow); - rettv->v_type = VAR_LIST; - rettv->vval.v_list = list; - tv_list_ref(list); - char *const dirs = stdpaths_get_xdg_var(xdg); - if (dirs == NULL) { - 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, appname, true); - tv_list_append_allocated_string(list, dir_with_nvim); - } - } while (iter != NULL); - xfree(dirs); -} - static list_T *string_to_list(const char *str, size_t len, const bool keepempty) { if (!keepempty && str[len - 1] == NL) { @@ -6417,149 +6262,6 @@ void timer_teardown(void) timer_stop_all(); } -/// Write "list" of strings to file "fd". -/// -/// @param fp File to write to. -/// @param[in] list List to write. -/// @param[in] binary Whether to write in binary mode. -/// -/// @return true in case of success, false otherwise. -bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary) - FUNC_ATTR_NONNULL_ARG(1) -{ - int error = 0; - TV_LIST_ITER_CONST(list, li, { - const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li)); - if (s == NULL) { - return false; - } - const char *hunk_start = s; - for (const char *p = hunk_start;; p++) { - if (*p == NUL || *p == NL) { - if (p != hunk_start) { - const ptrdiff_t written = file_write(fp, hunk_start, - (size_t)(p - hunk_start)); - if (written < 0) { - error = (int)written; - goto write_list_error; - } - } - if (*p == NUL) { - break; - } else { - hunk_start = p + 1; - const ptrdiff_t written = file_write(fp, (char[]){ NUL }, 1); - if (written < 0) { - error = (int)written; - break; - } - } - } - } - if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { - const ptrdiff_t written = file_write(fp, "\n", 1); - if (written < 0) { - error = (int)written; - goto write_list_error; - } - } - }); - if ((error = file_flush(fp)) != 0) { - goto write_list_error; - } - return true; -write_list_error: - semsg(_(e_write2), os_strerror(error)); - return false; -} - -/// Write a blob to file with descriptor `fp`. -/// -/// @param[in] fp File to write to. -/// @param[in] blob Blob to write. -/// -/// @return true on success, or false on failure. -bool write_blob(FileDescriptor *const fp, const blob_T *const blob) - FUNC_ATTR_NONNULL_ARG(1) -{ - int error = 0; - const int len = tv_blob_len(blob); - if (len > 0) { - const ptrdiff_t written = file_write(fp, blob->bv_ga.ga_data, (size_t)len); - if (written < (ptrdiff_t)len) { - error = (int)written; - goto write_blob_error; - } - } - error = file_flush(fp); - if (error != 0) { - goto write_blob_error; - } - return true; -write_blob_error: - semsg(_(e_write2), os_strerror(error)); - return false; -} - -/// Read blob from file "fd". -/// Caller has allocated a blob in "rettv". -/// -/// @param[in] fd File to read from. -/// @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 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 FAIL; // can't read the file, error - } - - int whence; - off_T size = size_arg; - const off_T file_size = (off_T)os_fileinfo_size(&file_info); - if (offset >= 0) { - // 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 { - // 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; - } - if (size <= 0) { - return OK; - } - if (offset != 0 && vim_fseek(fd, offset, whence) != 0) { - return OK; - } - - 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) { - // An empty blob is returned on error. - tv_blob_free(rettv->vval.v_blob); - rettv->vval.v_blob = NULL; - return FAIL; - } - return OK; -} - /// Saves a typval_T as a string. /// /// For lists or buffers, replaces NLs with NUL and separates items with NLs. @@ -7146,13 +6848,13 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex char c1 = *in_end; *in_end = NUL; - char *temp_result = eval_to_string(expr_start + 1, false); + char *temp_result = eval_to_string(expr_start + 1, false, 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); - STRCAT(retval, temp_result); - STRCAT(retval, expr_end + 1); + strcat(retval, temp_result); + strcat(retval, expr_end + 1); } xfree(temp_result); @@ -8201,6 +7903,12 @@ void ex_echohl(exarg_T *eap) echo_attr = syn_name2attr(eap->arg); } +/// Returns the :echo attribute +int get_echo_attr(void) +{ + return echo_attr; +} + /// ":execute expr1 ..." execute the result of an expression. /// ":echomsg expr1 ..." Print a message /// ":echoerr expr1 ..." Print an error @@ -8796,7 +8504,7 @@ Channel *find_job(uint64_t id, bool show_error) { Channel *data = find_channel(id); if (!data || data->streamtype != kChannelStreamProc - || process_is_stopped(&data->stream.proc)) { + || proc_is_stopped(&data->stream.proc)) { if (show_error) { if (data && data->streamtype != kChannelStreamProc) { emsg(_(e_invchanjob)); @@ -8907,7 +8615,7 @@ bool eval_has_provider(const char *feat, bool throw_if_fast) char name[32]; // Normalized: "python3_compiled" => "python3". snprintf(name, sizeof(name), "%s", feat); - strchrsub(name, '_', '\0'); // Chop any "_xx" suffix. + strchrsub(name, '_', NUL); // Chop any "_xx" suffix. char buf[256]; typval_T tv; @@ -9062,7 +8770,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) return FAIL; } else { // Compare two Lists for being equal or unequal. - n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic, false); + n1 = tv_list_equal(typ1->vval.v_list, typ2->vval.v_list, ic); if (type == EXPR_NEQUAL) { n1 = !n1; } @@ -9085,7 +8793,7 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) return FAIL; } else { // Compare two Dictionaries for being equal or unequal. - n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic, false); + n1 = tv_dict_equal(typ1->vval.v_dict, typ2->vval.v_dict, ic); if (type == EXPR_NEQUAL) { n1 = !n1; } @@ -9106,14 +8814,14 @@ int typval_compare(typval_T *typ1, typval_T *typ2, exprtype_T type, bool ic) if (typ1->v_type == VAR_FUNC && typ2->v_type == VAR_FUNC) { // strings are considered the same if their value is // the same - n1 = tv_equal(typ1, typ2, ic, false); + n1 = tv_equal(typ1, typ2, ic); } else if (typ1->v_type == VAR_PARTIAL && typ2->v_type == VAR_PARTIAL) { n1 = typ1->vval.v_partial == typ2->vval.v_partial; } else { n1 = false; } } else { - n1 = tv_equal(typ1, typ2, ic, false); + n1 = tv_equal(typ1, typ2, ic); } if (type == EXPR_NEQUAL || type == EXPR_ISNOT) { n1 = !n1; |