diff options
Diffstat (limited to 'src/nvim/eval.c')
| -rw-r--r-- | src/nvim/eval.c | 2885 |
1 files changed, 1727 insertions, 1158 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4392ea306f..f4479d06a6 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1,23 +1,24 @@ -// 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 <assert.h> #include <ctype.h> -#include <inttypes.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <sys/types.h> #include "auto/config.h" +#include "nvim/api/private/converter.h" #include "nvim/api/private/defs.h" -#include "nvim/ascii.h" +#include "nvim/api/private/helpers.h" +#include "nvim/ascii_defs.h" #include "nvim/buffer.h" #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" @@ -28,7 +29,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" @@ -36,20 +36,21 @@ #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" #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" #include "nvim/lib/queue.h" -#include "nvim/locale.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" @@ -59,14 +60,16 @@ #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" +#include "nvim/os/fs.h" +#include "nvim/os/lang.h" #include "nvim/os/os.h" #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" @@ -74,28 +77,43 @@ #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" #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 #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 +#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[] + = 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 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_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_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_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"; @@ -109,22 +127,11 @@ 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" -// 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]' @@ -259,6 +266,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), @@ -293,6 +301,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 @@ -406,11 +421,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; @@ -440,8 +455,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); @@ -459,6 +474,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); @@ -473,7 +489,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,27 +504,28 @@ void eval_clear(void) hash_init(&vimvarht); // garbage_collect() will access it hash_clear(&compat_hashtab); - free_scriptnames(); -# 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++) { + // 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)); } - for (int i = 1; i <= ga_scripts.ga_len; i++) { - xfree(SCRIPT_SV(i)); - } - ga_clear(&ga_scripts); +} + +void eval_clear(void) +{ + evalvars_clear(); + free_scriptnames(); // must come after evalvars_clear(). +# ifdef HAVE_WORKING_LIBINTL + free_locales(); +# endif + + // autoloaded script names + free_autoload_scriptnames(); // unreferenced lists and dicts (void)garbage_collect(false); @@ -604,7 +621,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; @@ -654,19 +671,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; @@ -676,28 +701,57 @@ 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_free(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) { - 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) +{ + *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE }; + if (eap != NULL) { + if (getline_equal(eap->getline, eap->cookie, getsourceline)) { + evalarg->eval_getline = eap->getline; + evalarg->eval_cookie = eap->cookie; + } + } } /// Top level evaluation function, returning a boolean. @@ -706,15 +760,18 @@ 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; + evalarg_T evalarg; + + fill_evalarg_from_eap(&evalarg, eap, skip); if (skip) { emsg_skip++; } - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + if (eval0(arg, &tv, eap, &evalarg) == FAIL) { *error = true; } else { *error = false; @@ -726,19 +783,23 @@ int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip) if (skip) { emsg_skip--; } + clear_evalarg(&evalarg, eap); return retval; } /// 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; - const int ret = eval1(arg, rettv, evaluate); + fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); + + 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 @@ -750,6 +811,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) semsg(_(e_invexpr2), start); } } + clear_evalarg(&evalarg, eap); return ret; } @@ -762,39 +824,50 @@ 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; - 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; } 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 @@ -813,7 +886,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; } @@ -825,22 +898,23 @@ 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; char *retval; + evalarg_T evalarg; + fill_evalarg_from_eap(&evalarg, eap, skip); if (skip) { emsg_skip++; } - if (eval0((char *)arg, &tv, (char **)nextcmd, !skip) == FAIL || skip) { + if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) { retval = NULL; } else { retval = xstrdup(tv_get_string(&tv)); @@ -849,6 +923,7 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip if (skip) { emsg_skip--; } + clear_evalarg(&evalarg, eap); return retval; } @@ -856,48 +931,65 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, 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, false); + typval_T rettv; + int res = eval1(pp, &rettv, NULL); + + if (evalarg != NULL) { + evalarg->eval_flags = save_flags; + } + + return res; +} + +/// Convert "tv" 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) +{ + 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; + } + 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. -char *eval_to_string(char *arg, char **nextcmd, bool convert) +/// @return pointer to allocated memory, or NULL for failure. +char *eval_to_string(char *arg, bool convert) { typval_T tv; char *retval; - garray_T ga; - if (eval0(arg, &tv, nextcmd, true) == FAIL) { + 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); return retval; } @@ -906,7 +998,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, const bool use_sandbox) { char *retval; funccal_entry_T funccal_entry; @@ -916,7 +1008,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--; } @@ -937,7 +1029,7 @@ varnumber_T eval_to_number(char *expr) emsg_off++; - if (eval1(&p, &rettv, true) == FAIL) { + if (eval1(&p, &rettv, &EVALARG_EVALUATE) == FAIL) { retval = -1; } else { retval = tv_get_number_chk(&rettv, NULL); @@ -952,12 +1044,18 @@ 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, true) == 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, eap); return tv; } @@ -970,7 +1068,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); } } @@ -992,7 +1090,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); } } @@ -1005,7 +1103,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 { @@ -1023,6 +1121,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); @@ -1031,8 +1130,12 @@ 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, true) == OK) { + if (eval1(&p, &rettv, &EVALARG_EVALUATE) == OK) { if (rettv.v_type != VAR_LIST) { tv_clear(&rettv); } else { @@ -1044,6 +1147,7 @@ list_T *eval_spell_expr(char *badword, char *expr) emsg_off--; } restore_vimvar(VV_VAL, &save_val); + current_sctx = saved_sctx; return list; } @@ -1123,7 +1227,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; @@ -1153,7 +1257,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; } @@ -1167,11 +1271,13 @@ 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 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) { @@ -1179,7 +1285,10 @@ int eval_foldexpr(char *arg, int *cp) } textlock++; *cp = NUL; - if (eval0(arg, &tv, NULL, true) == FAIL) { + + typval_T tv; + varnumber_T retval; + if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { retval = 0; } else { // If the result is a number, just return the number. @@ -1191,22 +1300,61 @@ int eval_foldexpr(char *arg, 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 != '-') { - *cp = (char_u)(*s++); + if (*s != NUL && !ascii_isdigit(*s) && *s != '-') { + *cp = (uint8_t)(*s++); } retval = atol(s); } tv_clear(&tv); } + emsg_off--; if (use_sandbox) { sandbox--; } textlock--; + clear_evalarg(&EVALARG_EVALUATE, NULL); + current_sctx = saved_sctx; 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 @@ -1242,9 +1390,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. @@ -1277,8 +1424,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. @@ -1321,14 +1468,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")); @@ -1355,7 +1510,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, &EVALARG_EVALUATE) == FAIL) { // Recursive! return NULL; } if (!tv_check_str(&var1)) { @@ -1370,7 +1525,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; @@ -1389,7 +1544,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, &EVALARG_EVALUATE) == FAIL) { tv_clear(&var1); return NULL; } @@ -1425,25 +1581,23 @@ 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 // 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((const char *)key, lp->ll_di == NULL)) - || !valid_varname((const char *)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; } @@ -1480,7 +1634,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; @@ -1500,26 +1654,19 @@ 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); 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, quiet) == FAIL) { tv_clear(&var2); 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 (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; } } @@ -1532,24 +1679,15 @@ 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); 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_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); if (lp->ll_li == NULL) { tv_clear(&var2); - if (!quiet) { - semsg(_(e_listidx), (int64_t)lp->ll_n1); - } return NULL; } @@ -1558,27 +1696,11 @@ 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 (lp->ll_n2 < 0) { - 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); - } - 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_listidx), (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; } } @@ -1607, |