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.c522
1 files changed, 232 insertions, 290 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index f4479d06a6..7e3060100c 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -7,13 +7,14 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/types.h>
+#include <uv.h>
#include "auto/config.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
@@ -29,29 +30,31 @@
#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/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/ex_session.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/getchar.h"
-#include "nvim/gettext.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"
#include "nvim/keycodes.h"
-#include "nvim/lib/queue.h"
+#include "nvim/lib/queue_defs.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
#include "nvim/mark.h"
+#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -64,8 +67,10 @@
#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"
@@ -73,13 +78,16 @@
#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#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"
@@ -89,8 +97,6 @@
#define DICT_MAXNEST 100 // maximum nesting of lists and dicts
-#define MAX_CALLBACK_DEPTH 20
-
static const char *e_missbrac = N_("E111: Missing ']'");
static const char *e_list_end = N_("E697: Missing end of List ']': %s");
static const char e_cannot_slice_dictionary[]
@@ -188,6 +194,7 @@ static struct vimvar {
VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT + VV_RO),
VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX),
VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO),
+ VV(VV_TERMREQUEST, "termrequest", VAR_STRING, VV_RO),
VV(VV_FNAME, "fname", VAR_STRING, VV_RO),
VV(VV_LANG, "lang", VAR_STRING, VV_RO),
VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO),
@@ -301,11 +308,12 @@ static partial_T *vvlua_partial;
/// v: hashtab
#define vimvarht vimvardict.dv_hashtab
-/// Enum used by filter(), map() and mapnew()
+/// Enum used by filter(), map(), mapnew() and foreach()
typedef enum {
FILTERMAP_FILTER,
FILTERMAP_MAP,
FILTERMAP_MAPNEW,
+ FILTERMAP_FOREACH,
} filtermap_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -500,6 +508,10 @@ static void evalvars_clear(void)
p->vv_list = NULL;
}
}
+
+ partial_unref(vvlua_partial);
+ vimvars[VV_LUA].vv_partial = vvlua_partial = NULL;
+
hash_clear(&vimvarht);
hash_init(&vimvarht); // garbage_collect() will access it
hash_clear(&compat_hashtab);
@@ -528,7 +540,7 @@ void eval_clear(void)
free_autoload_scriptnames();
// unreferenced lists and dicts
- (void)garbage_collect(false);
+ garbage_collect(false);
// functions not garbage collected
free_all_functions();
@@ -559,7 +571,7 @@ static char *redir_varname = NULL;
/// @param append append to an existing variable
///
/// @return OK if successfully completed the setup. FAIL otherwise.
-int var_redir_start(char *name, int append)
+int var_redir_start(char *name, bool append)
{
// Catch a bad name early.
if (!eval_isnamec1(*name)) {
@@ -677,7 +689,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
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");
+ sctx_T *ctx = get_option_sctx(kOptCharconvert);
if (ctx != NULL) {
current_sctx = *ctx;
}
@@ -706,7 +718,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
- sctx_T *ctx = get_option_sctx("diffexpr");
+ sctx_T *ctx = get_option_sctx(kOptDiffexpr);
if (ctx != NULL) {
current_sctx = *ctx;
}
@@ -728,7 +740,7 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
set_vim_var_string(VV_FNAME_DIFF, difffile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
- sctx_T *ctx = get_option_sctx("patchexpr");
+ sctx_T *ctx = get_option_sctx(kOptPatchexpr);
if (ctx != NULL) {
current_sctx = *ctx;
}
@@ -746,11 +758,14 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
{
*evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE };
- if (eap != NULL) {
- if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
- evalarg->eval_getline = eap->getline;
- evalarg->eval_cookie = eap->cookie;
- }
+
+ if (eap == NULL) {
+ return;
+ }
+
+ if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
+ evalarg->eval_getline = eap->getline;
+ evalarg->eval_cookie = eap->cookie;
}
}
@@ -760,7 +775,7 @@ void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
/// @param skip only parse, don't execute
///
/// @return true or false.
-int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
+bool eval_to_bool(char *arg, bool *error, exarg_T *eap, bool skip)
{
typval_T tv;
bool retval = false;
@@ -1084,17 +1099,19 @@ bool is_compatht(const hashtab_T *ht)
}
/// Prepare v: variable "idx" to be used.
-/// Save the current typeval in "save_tv".
+/// Save the current typeval in "save_tv" and clear it.
/// When not used yet add the variable to the v: hashtable.
void prepare_vimvar(int idx, typval_T *save_tv)
{
*save_tv = vimvars[idx].vv_tv;
+ vimvars[idx].vv_str = NULL; // don't free it now
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
hash_add(&vimvarht, vimvars[idx].vv_di.di_key);
}
}
/// Restore v: variable "idx" to typeval "save_tv".
+/// Note that the v: variable must have been cleared already.
/// When no longer defined, remove the variable from the v: hashtable.
void restore_vimvar(int idx, typval_T *save_tv)
{
@@ -1130,7 +1147,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
if (p_verbose == 0) {
emsg_off++;
}
- sctx_T *ctx = get_option_sctx("spellsuggest");
+ sctx_T *ctx = get_option_sctx(kOptSpellsuggest);
if (ctx != NULL) {
current_sctx = *ctx;
}
@@ -1274,7 +1291,7 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
int eval_foldexpr(win_T *wp, int *cp)
{
const sctx_T saved_sctx = current_sctx;
- const bool use_sandbox = was_set_insecurely(wp, "foldexpr", OPT_LOCAL);
+ const bool use_sandbox = was_set_insecurely(wp, kOptFoldexpr, OPT_LOCAL);
char *arg = wp->w_p_fde;
current_sctx = wp->w_p_script_ctx[WV_FDE].script_ctx;
@@ -1322,7 +1339,7 @@ int eval_foldexpr(win_T *wp, int *cp)
/// 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);
+ const bool use_sandbox = was_set_insecurely(wp, kOptFoldtext, OPT_LOCAL);
char *arg = wp->w_p_fdt;
funccal_entry_T funccal_entry;
@@ -1338,7 +1355,7 @@ Object eval_foldtext(win_T *wp)
retval = STRING_OBJ(NULL_STRING);
} else {
if (tv.v_type == VAR_LIST) {
- retval = vim_to_object(&tv);
+ retval = vim_to_object(&tv, NULL, false);
} else {
retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv)));
}
@@ -1725,7 +1742,7 @@ void clear_lval(lval_T *lp)
/// @param endp points to just after the parsed name.
/// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=",
/// "%" for "%=", "." for ".=" or "=" for "=".
-void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool is_const,
+void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, bool copy, const bool is_const,
const char *op)
{
int cc;
@@ -1794,8 +1811,8 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
return;
}
- (void)tv_list_assign_range(lp->ll_list, rettv->vval.v_list,
- lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name);
+ tv_list_assign_range(lp->ll_list, rettv->vval.v_list,
+ lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name);
} else {
typval_T oldtv = TV_INITIAL_VALUE;
dict_T *dict = lp->ll_dict;
@@ -2312,20 +2329,22 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam
/// After using "evalarg" filled from "eap": free the memory.
void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
{
- if (evalarg != NULL) {
- if (evalarg->eval_tofree != NULL) {
- if (eap != NULL) {
- // We may need to keep the original command line, e.g. for
- // ":let" it has the variable names. But we may also need the
- // new one, "nextcmd" points into it. Keep both.
- xfree(eap->cmdline_tofree);
- eap->cmdline_tofree = *eap->cmdlinep;
- *eap->cmdlinep = evalarg->eval_tofree;
- } else {
- xfree(evalarg->eval_tofree);
- }
- evalarg->eval_tofree = NULL;
+ if (evalarg == NULL) {
+ return;
+ }
+
+ if (evalarg->eval_tofree != NULL) {
+ if (eap != NULL) {
+ // We may need to keep the original command line, e.g. for
+ // ":let" it has the variable names. But we may also need the
+ // new one, "nextcmd" points into it. Keep both.
+ xfree(eap->cmdline_tofree);
+ eap->cmdline_tofree = *eap->cmdlinep;
+ *eap->cmdlinep = evalarg->eval_tofree;
+ } else {
+ xfree(evalarg->eval_tofree);
}
+ evalarg->eval_tofree = NULL;
}
}
@@ -2858,7 +2877,8 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
} else {
bool error = false;
varnumber_T n1, n2;
- float_T f1 = 0, f2 = 0;
+ float_T f1 = 0;
+ float_T f2 = 0;
if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float;
@@ -2949,7 +2969,8 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
}
varnumber_T n1, n2;
- float_T f1 = 0, f2 = 0;
+ float_T f1 = 0;
+ float_T f2 = 0;
bool error = false;
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
if (evaluate) {
@@ -3165,12 +3186,10 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// Register contents: @r.
case '@':
(*arg)++;
+ int regname = mb_cptr2char_adv((const char**) arg);
if (evaluate) {
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc);
- }
- if (**arg != NUL) {
- (*arg)++;
+ rettv->vval.v_string = get_reg_contents(regname, kGRegExprSrc);
}
break;
@@ -3217,6 +3236,13 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
} else {
// skip the name
check_vars(s, (size_t)len);
+ // If evaluate is false rettv->v_type was not set, but it's needed
+ // in handle_subscript() to parse v:lua, so set it here.
+ if (rettv->v_type == VAR_UNKNOWN && !evaluate && strnequal(s, "v:lua.", 6)) {
+ rettv->v_type = VAR_PARTIAL;
+ rettv->vval.v_partial = vvlua_partial;
+ rettv->vval.v_partial->pt_refcount++;
+ }
ret = OK;
}
}
@@ -3421,7 +3447,7 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const
int len;
char *name = *arg;
char *lua_funcname = NULL;
- if (strncmp(name, "v:lua.", 6) == 0) {
+ 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
@@ -3610,12 +3636,14 @@ 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) {
- tv_copy(argvars, rettv);
- eval_index_inner(rettv, true, argvars + 1,
- argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
- true, NULL, 0, false);
+ if (check_can_index(argvars, true, false) != OK) {
+ return;
}
+
+ tv_copy(argvars, rettv);
+ eval_index_inner(rettv, true, argvars + 1,
+ argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
+ true, NULL, 0, false);
}
/// Apply index or range to "rettv".
@@ -3731,7 +3759,11 @@ static int eval_index_inner(typval_T *rettv, bool is_range, typval_T *var1, typv
dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, key, keylen);
if (item == NULL && verbose) {
- semsg(_(e_dictkey), key);
+ if (keylen > 0) {
+ semsg(_(e_dictkey_len), keylen, key);
+ } else {
+ semsg(_(e_dictkey), key);
+ }
}
if (item == NULL || tv_is_luafunc(&item->di_tv)) {
return FAIL;
@@ -3759,10 +3791,12 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua
FUNC_ATTR_NONNULL_ARG(1)
{
const bool working = (**arg == '+'); // has("+option")
+ OptIndex opt_idx;
int scope;
// Isolate the option name and find its value.
- char *option_end = (char *)find_option_end(arg, &scope);
+ char *const option_end = (char *)find_option_var_end(arg, &opt_idx, &scope);
+
if (option_end == NULL) {
if (rettv != NULL) {
semsg(_("E112: Option name missing: %s"), *arg);
@@ -3775,38 +3809,26 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua
return OK;
}
- int ret = OK;
- bool hidden;
char c = *option_end;
*option_end = NUL;
- OptVal value = get_option_value(*arg, NULL, scope, &hidden);
- if (rettv != NULL) {
- switch (value.type) {
- case kOptValTypeNil:
+ int ret = OK;
+ bool is_tty_opt = is_tty_option(*arg);
+
+ if (opt_idx == kOptInvalid && !is_tty_opt) {
+ // Only give error if result is going to be used.
+ if (rettv != NULL) {
semsg(_("E113: Unknown option: %s"), *arg);
- ret = FAIL;
- break;
- case kOptValTypeBoolean:
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = value.data.boolean;
- break;
- case kOptValTypeNumber:
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = value.data.number;
- break;
- case kOptValTypeString:
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = value.data.string.data;
- break;
}
- } else {
- // Value isn't being used, free it.
- optval_free(value);
- if (value.type == kOptValTypeNil || (working && hidden)) {
- ret = FAIL;
- }
+ ret = FAIL;
+ } else if (rettv != NULL) {
+ OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, scope);
+ assert(value.type != kOptValTypeNil);
+
+ *rettv = optval_as_tv(value, true);
+ } else if (working && !is_tty_opt && is_option_hidden(opt_idx)) {
+ ret = FAIL;
}
*option_end = c; // put back for error messages
@@ -4238,7 +4260,11 @@ static void partial_free(partial_T *pt)
/// becomes zero.
void partial_unref(partial_T *pt)
{
- if (pt != NULL && --pt->pt_refcount <= 0) {
+ if (pt == NULL) {
+ return;
+ }
+
+ if (--pt->pt_refcount <= 0) {
partial_free(pt);
}
}
@@ -4483,7 +4509,7 @@ bool garbage_collect(bool testing)
// registers (ShaDa additional data)
{
- const void *reg_iter = NULL;
+ iter_register_T reg_iter = ITER_REGISTER_NULL;
do {
yankreg_T reg;
char name = NUL;
@@ -4492,7 +4518,7 @@ bool garbage_collect(bool testing)
if (name != NUL) {
ABORTING(set_ref_dict)(reg.additional_data, copyID);
}
- } while (reg_iter != NULL);
+ } while (reg_iter != ITER_REGISTER_NULL);
}
// global marks (ShaDa additional data)
@@ -4548,7 +4574,7 @@ bool garbage_collect(bool testing)
// history items (ShaDa additional elements)
if (p_hi) {
- for (HistoryType i = 0; i < HIST_COUNT; i++) {
+ for (int i = 0; i < HIST_COUNT; i++) {
const void *iter = NULL;
do {
histentry_T hist;
@@ -4707,7 +4733,7 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
/// @param ht_stack Used to add hashtabs to be marked. Can be NULL.
///
/// @returns true if setting references failed somehow.
-bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack)
+bool set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
FUNC_ATTR_WARN_UNUSED_RESULT
{
bool abort = false;
@@ -4784,7 +4810,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack
// Didn't see this list yet.
ll->lv_copyID = copyID;
if (list_stack == NULL) {
- abort = set_ref_in_list(ll, copyID, ht_stack);
+ abort = set_ref_in_list_items(ll, copyID, ht_stack);
} else {
list_stack_T *const newitem = xmalloc(sizeof(list_stack_T));
newitem->list = ll;
@@ -5026,7 +5052,7 @@ size_t string2float(const char *const text, float_T *const ret_value)
return 3;
}
if (STRNICMP(text, "-inf", 3) == 0) {
- *ret_value = (float_T) - INFINITY;
+ *ret_value = (float_T)(-INFINITY);
return 4;
}
if (STRNICMP(text, "nan", 3) == 0) {
@@ -5089,7 +5115,8 @@ void assert_error(garray_T *gap)
tv_list_append_string(vimvars[VV_ERRORS].vv_list, gap->ga_data, (ptrdiff_t)gap->ga_len);
}
-/// Implementation of map() and filter() for a Dict.
+/// Implementation of map(), filter(), foreach() for a Dict. Apply "expr" to
+/// every item in Dict "d" and return the result in "rettv".
static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_name,
const char *arg_errmsg, typval_T *expr, typval_T *rettv)
{
@@ -5157,7 +5184,7 @@ static void filter_map_dict(dict_T *d, filtermap_T filtermap, const char *func_n
d->dv_lock = prev_lock;
}
-/// Implementation of map() and filter() for a Blob.
+/// Implementation of map(), filter(), foreach() for a Blob.
static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *expr,
const char *arg_errmsg, typval_T *rettv)
{
@@ -5200,20 +5227,22 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e
|| did_emsg) {
break;
}
- if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) {
- tv_clear(&newtv);
- emsg(_(e_invalblob));
- break;
- }
- if (filtermap != FILTERMAP_FILTER) {
- if (newtv.vval.v_number != val) {
- tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number);
+ if (filtermap != FILTERMAP_FOREACH) {
+ if (newtv.v_type != VAR_NUMBER && newtv.v_type != VAR_BOOL) {
+ tv_clear(&newtv);
+ emsg(_(e_invalblob));
+ break;
+ }
+ if (filtermap != FILTERMAP_FILTER) {
+ if (newtv.vval.v_number != val) {
+ tv_blob_set(b_ret, i, (uint8_t)newtv.vval.v_number);
+ }
+ } else if (rem) {
+ char *const p = (char *)blob_arg->bv_ga.ga_data;
+ memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1));
+ b->bv_ga.ga_len--;
+ i--;
}
- } else if (rem) {
- char *const p = (char *)blob_arg->bv_ga.ga_data;
- memmove(p + i, p + i + 1, (size_t)(b->bv_ga.ga_len - i - 1));
- b->bv_ga.ga_len--;
- i--;
}
idx++;
}
@@ -5221,7 +5250,7 @@ static void filter_map_blob(blob_T *blob_arg, filtermap_T filtermap, typval_T *e
b->bv_lock = prev_lock;
}
-/// Implementation of map() and filter() for a String.
+/// Implementation of map(), filter(), foreach() for a String.
static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *expr,
typval_T *rettv)
{
@@ -5250,7 +5279,8 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *
tv_clear(&newtv);
tv_clear(&tv);
break;
- } else if (filtermap != FILTERMAP_FILTER) {
+ }
+ if (filtermap == FILTERMAP_MAP || filtermap == FILTERMAP_MAPNEW) {
if (newtv.v_type != VAR_STRING) {
tv_clear(&newtv);
tv_clear(&tv);
@@ -5259,7 +5289,7 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *
} else {
ga_concat(&ga, newtv.vval.v_string);
}
- } else if (!rem) {
+ } else if (filtermap == FILTERMAP_FOREACH || !rem) {
ga_concat(&ga, tv.vval.v_string);
}
@@ -5272,7 +5302,8 @@ static void filter_map_string(const char *str, filtermap_T filtermap, typval_T *
rettv->vval.v_string = ga.ga_data;
}
-/// Implementation of map() and filter() for a List.
+/// Implementation of map(), filter(), foreach() for a List. Apply "expr" to
+/// every item in List "l" and return the result in "rettv".
static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_name,
const char *arg_errmsg, typval_T *expr, typval_T *rettv)
{
@@ -5336,21 +5367,25 @@ static void filter_map_list(list_T *l, filtermap_T filtermap, const char *func_n
tv_list_set_lock(l, prev_lock);
}
-/// Implementation of map() and filter().
+/// Implementation of map(), filter() and foreach().
static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap)
{
const char *const func_name = (filtermap == FILTERMAP_MAP
? "map()"
: (filtermap == FILTERMAP_MAPNEW
? "mapnew()"
- : "filter()"));
+ : (filtermap == FILTERMAP_FILTER
+ ? "filter()"
+ : "foreach()")));
const char *const arg_errmsg = (filtermap == FILTERMAP_MAP
? N_("map() argument")
: (filtermap == FILTERMAP_MAPNEW
? N_("mapnew() argument")
- : N_("filter() argument")));
+ : (filtermap == FILTERMAP_FILTER
+ ? N_("filter() argument")
+ : N_("foreach() argument"))));
- // map() and filter() return the first argument, also on failure.
+ // map(), filter(), foreach() return the first argument, also on failure.
if (filtermap != FILTERMAP_MAPNEW && argvars[0].v_type != VAR_STRING) {
tv_copy(&argvars[0], rettv);
}
@@ -5367,38 +5402,40 @@ static void filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap
// On type errors, the preceding call has already displayed an error
// message. Avoid a misleading error message for an empty string that
// was not passed as argument.
- if (expr->v_type != VAR_UNKNOWN) {
- typval_T save_val;
- prepare_vimvar(VV_VAL, &save_val);
-
- // We reset "did_emsg" to be able to detect whether an error
- // occurred during evaluation of the expression.
- int save_did_emsg = did_emsg;
- did_emsg = false;
-
- typval_T save_key;
- prepare_vimvar(VV_KEY, &save_key);
- if (argvars[0].v_type == VAR_DICT) {
- filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name,
- arg_errmsg, expr, rettv);
- } else if (argvars[0].v_type == VAR_BLOB) {
- filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, arg_errmsg, rettv);
- } else if (argvars[0].v_type == VAR_STRING) {
- filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv);
- } else {
- assert(argvars[0].v_type == VAR_LIST);
- filter_map_list(argvars[0].vval.v_list, filtermap, func_name,
- arg_errmsg, expr, rettv);
- }
+ if (expr->v_type == VAR_UNKNOWN) {
+ return;
+ }
- restore_vimvar(VV_KEY, &save_key);
- restore_vimvar(VV_VAL, &save_val);
+ typval_T save_val;
+ prepare_vimvar(VV_VAL, &save_val);
+
+ // We reset "did_emsg" to be able to detect whether an error
+ // occurred during evaluation of the expression.
+ int save_did_emsg = did_emsg;
+ did_emsg = false;
- did_emsg |= save_did_emsg;
+ typval_T save_key;
+ prepare_vimvar(VV_KEY, &save_key);
+ if (argvars[0].v_type == VAR_DICT) {
+ filter_map_dict(argvars[0].vval.v_dict, filtermap, func_name,
+ arg_errmsg, expr, rettv);
+ } else if (argvars[0].v_type == VAR_BLOB) {
+ filter_map_blob(argvars[0].vval.v_blob, filtermap, expr, arg_errmsg, rettv);
+ } else if (argvars[0].v_type == VAR_STRING) {
+ filter_map_string(tv_get_string(&argvars[0]), filtermap, expr, rettv);
+ } else {
+ assert(argvars[0].v_type == VAR_LIST);
+ filter_map_list(argvars[0].vval.v_list, filtermap, func_name,
+ arg_errmsg, expr, rettv);
}
+
+ restore_vimvar(VV_KEY, &save_key);
+ restore_vimvar(VV_VAL, &save_val);
+
+ did_emsg |= save_did_emsg;
}
-/// Handle one item for map() and filter().
+/// Handle one item for map(), filter(), foreach().
/// Sets v:val to "tv". Caller must set v:key.
///
/// @param tv original value
@@ -5413,6 +5450,17 @@ static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filter
int retval = FAIL;
tv_copy(tv, &vimvars[VV_VAL].vv_tv);
+
+ newtv->v_type = VAR_UNKNOWN;
+ if (filtermap == FILTERMAP_FOREACH && expr->v_type == VAR_STRING) {
+ // foreach() is not limited to an expression
+ do_cmdline_cmd(expr->vval.v_string);
+ if (!did_emsg) {
+ retval = OK;
+ }
+ goto theend;
+ }
+
argv[0] = vimvars[VV_KEY].vv_tv;
argv[1] = vimvars[VV_VAL].vv_tv;
if (eval_expr_typval(expr, false, argv, 2, newtv) == FAIL) {
@@ -5429,6 +5477,8 @@ static int filter_map_one(typval_T *tv, typval_T *expr, const filtermap_T filter
if (error) {
goto theend;
}
+ } else if (filtermap == FILTERMAP_FOREACH) {
+ tv_clear(newtv);
}
retval = OK;
theend:
@@ -5454,6 +5504,12 @@ void f_mapnew(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
filter_map(argvars, rettv, FILTERMAP_MAPNEW);
}
+/// "foreach()" function
+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)
@@ -6061,7 +6117,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
typval_T *const rettv)
FUNC_ATTR_NONNULL_ALL
{
- if (callback_depth > MAX_CALLBACK_DEPTH) {
+ if (callback_depth > p_mfd) {
emsg(_(e_command_too_recursive));
return false;
}
@@ -6092,8 +6148,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
break;
case kCallbackLua:
- rv = nlua_call_ref(callback->data.luaref, NULL, args, false, NULL);
- return (rv.type == kObjectTypeBoolean && rv.data.boolean == true);
+ rv = nlua_call_ref(callback->data.luaref, NULL, args, kRetNilBool, NULL, NULL);
+ return LUARET_TRUTHY(rv);
case kCallbackNone:
return false;
@@ -7563,6 +7619,10 @@ int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const e
const char *lua_funcname = NULL;
if (tv_is_luafunc(rettv)) {
+ if (!evaluate) {
+ tv_clear(rettv);
+ }
+
if (**arg != '.') {
tv_clear(rettv);
ret = FAIL;
@@ -8135,13 +8195,14 @@ void ex_execute(exarg_T *eap)
eap->nextcmd = check_nextcmd(arg);
}
-/// Skip over the name of an option: "&option", "&g:option" or "&l:option".
+/// Skip over the name of an option variable: "&option", "&g:option" or "&l:option".
///
-/// @param arg points to the "&" or '+' when called, to "option" when returning.
+/// @param[in,out] arg Points to the "&" or '+' when called, to "option" when returning.
+/// @param[out] opt_idxp Set to option index in options[] table.
+/// @param[out] scope Set to option scope.
///
-/// @return NULL when no option name found. Otherwise pointer to the char
-/// after the option name.
-const char *find_option_end(const char **const arg, int *const scope)
+/// @return NULL when no option name found. Otherwise pointer to the char after the option name.
+const char *find_option_var_end(const char **const arg, OptIndex *const opt_idxp, int *const scope)
{
const char *p = *arg;
@@ -8156,22 +8217,12 @@ const char *find_option_end(const char **const arg, int *const scope)
*scope = 0;
}
- if (!ASCII_ISALPHA(*p)) {
- return NULL;
- }
- *arg = p;
-
- if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) {
- p += 4; // t_xx/termcap option
- } else {
- while (ASCII_ISALPHA(*p)) {
- p++;
- }
- }
- return p;
+ const char *end = find_option_end(p, opt_idxp);
+ *arg = end == NULL ? *arg : p;
+ return end;
}
-static var_flavour_T var_flavour(char *varname)
+var_flavour_T var_flavour(char *varname)
FUNC_ATTR_PURE
{
char *p = varname;
@@ -8187,48 +8238,6 @@ static var_flavour_T var_flavour(char *varname)
return VAR_FLAVOUR_DEFAULT;
}
-/// Iterate over global variables
-///
-/// @warning No modifications to global variable dictionary must be performed
-/// while iteration is in progress.
-///
-/// @param[in] iter Iterator. Pass NULL to start iteration.
-/// @param[out] name Variable name.
-/// @param[out] rettv Variable value.
-///
-/// @return Pointer that needs to be passed to next `var_shada_iter` invocation
-/// or NULL to indicate that iteration is over.
-const void *var_shada_iter(const void *const iter, const char **const name, typval_T *rettv,
- var_flavour_T flavour)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3)
-{
- const hashitem_T *hi;
- const hashitem_T *hifirst = globvarht.ht_array;
- const size_t hinum = (size_t)globvarht.ht_mask + 1;
- *name = NULL;
- if (iter == NULL) {
- hi = globvarht.ht_array;
- while ((size_t)(hi - hifirst) < hinum
- && (HASHITEM_EMPTY(hi)
- || !(var_flavour(hi->hi_key) & flavour))) {
- hi++;
- }
- if ((size_t)(hi - hifirst) == hinum) {
- return NULL;
- }
- } else {
- hi = (const hashitem_T *)iter;
- }
- *name = TV_DICT_HI2DI(hi)->di_key;
- tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv);
- while ((size_t)(++hi - hifirst) < hinum) {
- if (!HASHITEM_EMPTY(hi) && (var_flavour(hi->hi_key) & flavour)) {
- return hi;
- }
- }
- return NULL;
-}
-
void var_set_global(const char *const name, typval_T vartv)
{
funccal_entry_T funccall_entry;
@@ -8238,50 +8247,6 @@ void var_set_global(const char *const name, typval_T vartv)
restore_funccal();
}
-int store_session_globals(FILE *fd)
-{
- TV_DICT_ITER(&globvardict, this_var, {
- if ((this_var->di_tv.v_type == VAR_NUMBER
- || this_var->di_tv.v_type == VAR_STRING)
- && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
- // Escape special characters with a backslash. Turn a LF and
- // CR into \n and \r.
- char *const p = vim_strsave_escaped(tv_get_string(&this_var->di_tv), "\\\"\n\r");
- for (char *t = p; *t != NUL; t++) {
- if (*t == '\n') {
- *t = 'n';
- } else if (*t == '\r') {
- *t = 'r';
- }
- }
- if ((fprintf(fd, "let %s = %c%s%c",
- this_var->di_key,
- ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' '),
- p,
- ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' ')) < 0)
- || put_eol(fd) == FAIL) {
- xfree(p);
- return FAIL;
- }
- xfree(p);
- } else if (this_var->di_tv.v_type == VAR_FLOAT
- && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
- float_T f = this_var->di_tv.vval.v_float;
- int sign = ' ';
-
- if (f < 0) {
- f = -f;
- sign = '-';
- }
- if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0)
- || put_eol(fd) == FAIL) {
- return FAIL;
- }
- }
- });
- return OK;
-}
-
/// Display script name where an item was last set.
/// Should only be invoked when 'verbose' is non-zero.
void last_set_msg(sctx_T script_ctx)
@@ -8298,21 +8263,24 @@ void last_set_msg(sctx_T script_ctx)
/// Should only be invoked when 'verbose' is non-zero.
void option_last_set_msg(LastSet last_set)
{
- if (last_set.script_ctx.sc_sid != 0) {
- bool should_free;
- char *p = get_scriptname(last_set, &should_free);
- verbose_enter();
- msg_puts(_("\n\tLast set from "));
- msg_puts(p);
- if (last_set.script_ctx.sc_lnum > 0) {
- msg_puts(_(line_msg));
- msg_outnum(last_set.script_ctx.sc_lnum);
- }
- if (should_free) {
- xfree(p);
- }
- verbose_leave();
+ if (last_set.script_ctx.sc_sid == 0) {
+ return;
}
+
+ bool should_free;
+ char *p = get_scriptname(last_set, &should_free);
+
+ verbose_enter();
+ msg_puts(_("\n\tLast set from "));
+ msg_puts(p);
+ if (last_set.script_ctx.sc_lnum > 0) {
+ msg_puts(_(line_msg));
+ msg_outnum(last_set.script_ctx.sc_lnum);
+ }
+ if (should_free) {
+ xfree(p);
+ }
+ verbose_leave();
}
// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal,
@@ -8683,9 +8651,9 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
int i = (int)(regmatch.startp[0] - tail);
memmove((char *)ga.ga_data + ga.ga_len, tail, (size_t)i);
// add the substituted text
- (void)vim_regsub(&regmatch, sub, expr,
- (char *)ga.ga_data + ga.ga_len + i, sublen,
- REGSUB_COPY | REGSUB_MAGIC);
+ vim_regsub(&regmatch, sub, expr,
+ (char *)ga.ga_data + ga.ga_len + i, sublen,
+ REGSUB_COPY | REGSUB_MAGIC);
ga.ga_len += i + sublen - 1;
tail = regmatch.endp[0];
if (*tail == NUL) {
@@ -8712,7 +8680,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char
// If it's still empty it was changed and restored, need to restore in
// the complicated way.
if (*p_cpo == NUL) {
- set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0);
+ set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0);
}
free_string_option(save_cpo);
}
@@ -8825,7 +8793,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
funcexe.fe_firstline = curwin->w_cursor.lnum;
funcexe.fe_lastline = curwin->w_cursor.lnum;
funcexe.fe_evaluate = true;
- (void)call_func(func, name_len, &rettv, 2, argvars, &funcexe);
+ call_func(func, name_len, &rettv, 2, argvars, &funcexe);
tv_list_unref(arguments);
// Restore caller scope information
@@ -8908,32 +8876,6 @@ void eval_fmt_source_name_line(char *buf, size_t bufsize)
}
}
-/// ":checkhealth [plugins]"
-void ex_checkhealth(exarg_T *eap)
-{
- Error err = ERROR_INIT;
- MAXSIZE_TEMP_ARRAY(args, 1);
- ADD_C(args, CSTR_AS_OBJ(eap->arg));
- NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err);
- if (!ERROR_SET(&err)) {
- return;
- }
-
- const char *vimruntime_env = os_getenv("VIMRUNTIME");
- if (vimruntime_env == NULL) {
- emsg(_("E5009: $VIMRUNTIME is empty or unset"));
- } else {
- bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env);
- if (rtp_ok) {
- semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env);
- } else {
- emsg(_("E5009: Invalid 'runtimepath'"));
- }
- }
- semsg_multiline(err.msg);
- api_clear_error(&err);
-}
-
void invoke_prompt_callback(void)
{
typval_T rettv;