aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c2885
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,