commit b38ce04283e3c166fb6aa44c60f0490cbe657c40
parent df23952ce9509b90440a66b7ef863e6a1dfcebbc
Author: Jan Edmund Lazo <jan.lazo@mail.utoronto.ca>
Date: Sat, 25 Oct 2025 18:20:33 -0400
vim-patch:8.1.1939: code for handling v: variables in generic eval file (#36312)
Problem: Code for handling v: variables in generic eval file.
Solution: Move v: variables to evalvars.c. (Yegappan Lakshmanan,
closes vim/vim#4872)
https://github.com/vim/vim/commit/e5cdf153bcb348c68011b308c8988cea42d6ddeb
Cherry-pick get_vim_var_name() from 8.2.0149.
Cherry-pick evalvars.c changes from 8.2.1788.
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Diffstat:
7 files changed, 629 insertions(+), 611 deletions(-)
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
@@ -25,6 +25,7 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
@@ -80,7 +80,6 @@
#include "nvim/tag.h"
#include "nvim/types_defs.h"
#include "nvim/undo.h"
-#include "nvim/version.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -113,13 +112,6 @@ static const char e_cannot_use_partial_here[]
static char * const namespace_char = "abglstvw";
-/// Variable used for g:
-static ScopeDictDictItem globvars_var;
-
-/// Old Vim variables such as "v:version" are also available without the "v:".
-/// Also in functions. We need a special hashtable for them.
-static hashtab_T compat_hashtab;
-
/// Used for checking if local variables or arguments used in a lambda.
bool *eval_lavars_used = NULL;
@@ -143,186 +135,11 @@ typedef enum {
GLV_STOP,
} glv_status_T;
-// values for vv_flags:
-#define VV_COMPAT 1 // compatible, also used without "v:"
-#define VV_RO 2 // read-only
-#define VV_RO_SBX 4 // read-only in the sandbox
-
-#define VV(idx, name, type, flags) \
- [idx] = { \
- .vv_name = (name), \
- .vv_di = { \
- .di_tv = { .v_type = (type) }, \
- .di_flags = 0, \
- .di_key = { 0 }, \
- }, \
- .vv_flags = (flags), \
- }
-
-#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables
-
-// Array to hold the value of v: variables.
-// The value is in a dictitem, so that it can also be used in the v: scope.
-// The reason to use this table anyway is for very quick access to the
-// variables with the VV_ defines.
-static struct vimvar {
- char *vv_name; ///< Name of the variable, without v:.
- TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars).
- char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
-} vimvars[] = {
- // VV_ tails differing from upcased string literals:
- // VV_CC_FROM "charconvert_from"
- // VV_CC_TO "charconvert_to"
- // VV_SEND_SERVER "servername"
- // VV_REG "register"
- // VV_OP "operator"
- VV(VV_COUNT, "count", VAR_NUMBER, VV_RO),
- VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
- VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
- VV(VV_ERRMSG, "errmsg", VAR_STRING, 0),
- VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0),
- VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
- VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO),
- VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0),
- 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),
- VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO),
- VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO),
- VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO),
- VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO),
- VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO),
- VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO),
- VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO),
- VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO),
- VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX),
- VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX),
- VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX),
- VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX),
- VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO),
- VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO),
- VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
- VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
- VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
- VV(VV_REG, "register", VAR_STRING, VV_RO),
- VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
- VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
- VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO),
- VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO),
- VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO),
- VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO),
- VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0),
- VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO),
- VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO),
- VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0),
- VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO),
- VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0),
- VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO),
- VV(VV_CHAR, "char", VAR_STRING, 0),
- VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0),
- VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0),
- VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0),
- VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0),
- VV(VV_OP, "operator", VAR_STRING, VV_RO),
- VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0),
- VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0),
- VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0),
- VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX),
- VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO),
- VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, 0),
- VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO),
- VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
- VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO),
- VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO),
- VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO),
- VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO),
- VV(VV_ERRORS, "errors", VAR_LIST, 0),
- VV(VV_FALSE, "false", VAR_BOOL, VV_RO),
- VV(VV_TRUE, "true", VAR_BOOL, VV_RO),
- VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
- VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO),
- VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO),
- VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO),
- VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO),
- VV(VV_TESTING, "testing", VAR_NUMBER, 0),
- VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
- VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO),
- VV(VV_EVENT, "event", VAR_DICT, VV_RO),
- VV(VV_VERSIONLONG, "versionlong", VAR_NUMBER, VV_RO),
- VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
- 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),
- VV(VV_STACKTRACE, "stacktrace", VAR_LIST, VV_RO),
- // Neovim
- VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
- VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
- VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO),
- VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
- VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
- VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
- VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
- VV(VV_RELNUM, "relnum", VAR_NUMBER, VV_RO),
- VV(VV_VIRTNUM, "virtnum", VAR_NUMBER, VV_RO),
-};
-#undef VV
-
-// shorthand
-#define vv_type vv_di.di_tv.v_type
-#define vv_nr vv_di.di_tv.vval.v_number
-#define vv_str vv_di.di_tv.vval.v_string
-#define vv_list vv_di.di_tv.vval.v_list
-#define vv_partial vv_di.di_tv.vval.v_partial
-#define vv_tv vv_di.di_tv
-
-#define vimvarht get_vimvar_dict()->dv_hashtab
-
-/// Variable used for v:
-static ScopeDictDictItem vimvars_var;
-
-static partial_T *vvlua_partial;
-
#include "eval.c.generated.h"
static uint64_t last_timer_id = 1;
static PMap(uint64_t) timers = MAP_INIT;
-static const char *const msgpack_type_names[] = {
- [kMPNil] = "nil",
- [kMPBoolean] = "boolean",
- [kMPInteger] = "integer",
- [kMPFloat] = "float",
- [kMPString] = "string",
- [kMPArray] = "array",
- [kMPMap] = "map",
- [kMPExt] = "ext",
-};
-const list_T *eval_msgpack_type_lists[] = {
- [kMPNil] = NULL,
- [kMPBoolean] = NULL,
- [kMPInteger] = NULL,
- [kMPFloat] = NULL,
- [kMPString] = NULL,
- [kMPArray] = NULL,
- [kMPMap] = NULL,
- [kMPExt] = NULL,
-};
-
dict_T *get_v_event(save_v_event_T *sve)
{
dict_T *v_event = get_vim_var_dict(VV_EVENT);
@@ -384,130 +201,11 @@ varnumber_T num_modulus(varnumber_T n1, varnumber_T n2)
/// Initialize the global and v: variables.
void eval_init(void)
{
- dict_T *vimvardict = get_vimvar_dict();
- vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
-
- init_var_dict(get_globvar_dict(), &globvars_var, VAR_DEF_SCOPE);
- init_var_dict(vimvardict, &vimvars_var, VAR_SCOPE);
- vimvardict->dv_lock = VAR_FIXED;
- hash_init(&compat_hashtab);
+ evalvars_init();
func_init();
-
- for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
- struct vimvar *p = &vimvars[i];
- assert(strlen(p->vv_name) <= VIMVAR_KEY_LEN);
- STRCPY(p->vv_di.di_key, p->vv_name);
- if (p->vv_flags & VV_RO) {
- p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- } else if (p->vv_flags & VV_RO_SBX) {
- p->vv_di.di_flags = DI_FLAGS_RO_SBX | DI_FLAGS_FIX;
- } else {
- p->vv_di.di_flags = DI_FLAGS_FIX;
- }
-
- // add to v: scope dict, unless the value is not always available
- if (p->vv_type != VAR_UNKNOWN) {
- hash_add(&vimvarht, p->vv_di.di_key);
- }
- if (p->vv_flags & VV_COMPAT) {
- // add to compat scope dict
- hash_add(&compat_hashtab, p->vv_di.di_key);
- }
- }
- vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
- vimvars[VV_VERSIONLONG].vv_nr = VIM_VERSION_100 * 10000 + highest_patch();
-
- dict_T *const msgpack_types_dict = tv_dict_alloc();
- for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
- list_T *const type_list = tv_list_alloc(0);
- tv_list_set_lock(type_list, VAR_FIXED);
- tv_list_ref(type_list);
- dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]);
- di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX;
- di->di_tv = (typval_T) {
- .v_type = VAR_LIST,
- .vval = { .v_list = type_list, },
- };
- eval_msgpack_type_lists[i] = type_list;
- if (tv_dict_add(msgpack_types_dict, di) == FAIL) {
- // There must not be duplicate items in this dictionary by definition.
- abort();
- }
- }
- msgpack_types_dict->dv_lock = VAR_FIXED;
-
- set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
- set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
-
- 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, 1);
- set_vim_var_nr(VV_HLSEARCH, 1);
- set_vim_var_nr(VV_COUNT1, 1);
- set_vim_var_special(VV_EXITING, kSpecialVarNull);
-
- set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
- set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
- set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC);
- set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST);
- set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT);
- set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
- set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
- set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB);
-
- set_vim_var_bool(VV_FALSE, kBoolVarFalse);
- set_vim_var_bool(VV_TRUE, kBoolVarTrue);
- set_vim_var_special(VV_NULL, kSpecialVarNull);
- set_vim_var_nr(VV_NUMBERMAX, VARNUMBER_MAX);
- set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN);
- set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8);
- set_vim_var_nr(VV_MAXCOL, MAXCOL);
-
- set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
-
- vimvars[VV_LUA].vv_type = VAR_PARTIAL;
- vvlua_partial = xcalloc(1, sizeof(partial_T));
- vimvars[VV_LUA].vv_partial = vvlua_partial;
- // this value shouldn't be printed, but if it is, do not crash
- vvlua_partial->pt_name = xmallocz(0);
- vvlua_partial->pt_refcount++;
-
- set_reg_var(0); // default for v:register is not 0 but '"'
}
#if defined(EXITFREE)
-static void evalvars_clear(void)
-{
- for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
- struct vimvar *p = &vimvars[i];
- if (p->vv_di.di_tv.v_type == VAR_STRING) {
- XFREE_CLEAR(p->vv_str);
- } else if (p->vv_di.di_tv.v_type == VAR_LIST) {
- tv_list_unref(p->vv_list);
- 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);
-
- // global variables
- vars_clear(get_globvar_ht());
-
- // Script-local variables. Clear all the variables here.
- // The scriptvar_T is cleared later in free_scriptnames(), because a
- // variable in one script might hold a reference to the whole scope of
- // another script.
- for (int i = 1; i <= script_items.ga_len; i++) {
- vars_clear(&SCRIPT_VARS(i));
- }
-}
-
void eval_clear(void)
{
evalvars_clear();
@@ -921,41 +619,6 @@ typval_T *eval_expr_ext(char *arg, exarg_T *eap, const bool use_simple_function)
return tv;
}
-bool is_compatht(const hashtab_T *ht)
-{
- return ht == &compat_hashtab;
-}
-
-/// Prepare v: variable "idx" to be used.
-/// 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)
-{
- vimvars[idx].vv_tv = *save_tv;
- if (vimvars[idx].vv_type != VAR_UNKNOWN) {
- return;
- }
-
- hashitem_T *hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
- if (HASHITEM_EMPTY(hi)) {
- internal_error("restore_vimvar()");
- } else {
- hash_remove(&vimvarht, hi);
- }
-}
-
/// Call some Vim script function and return the result in "*rettv".
/// Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc]
/// should have type VAR_UNKNOWN.
@@ -975,7 +638,7 @@ int call_vim_function(const char *func, int argc, typval_T *argv, typval_T *rett
ret = FAIL;
goto fail;
}
- pt = vvlua_partial;
+ pt = get_vim_var_partial(VV_LUA);
}
rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this.
@@ -2000,116 +1663,6 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
xp->xp_pattern = arg;
}
-/// Local string buffer for the next two functions to store a variable name
-/// with its prefix. Allocated in cat_prefix_varname(), freed later in
-/// get_user_var_name().
-
-static char *varnamebuf = NULL;
-static size_t varnamebuflen = 0;
-
-/// Function to concatenate a prefix and a variable name.
-char *cat_prefix_varname(int prefix, const char *name)
- FUNC_ATTR_NONNULL_ALL
-{
- size_t len = strlen(name) + 3;
-
- if (len > varnamebuflen) {
- xfree(varnamebuf);
- len += 10; // some additional space
- varnamebuf = xmalloc(len);
- varnamebuflen = len;
- }
- *varnamebuf = (char)prefix;
- varnamebuf[1] = ':';
- STRCPY(varnamebuf + 2, name);
- return varnamebuf;
-}
-
-/// Function given to ExpandGeneric() to obtain the list of user defined
-/// (global/buffer/window/built-in) variable names.
-char *get_user_var_name(expand_T *xp, int idx)
-{
- static size_t gdone;
- static size_t bdone;
- static size_t wdone;
- static size_t tdone;
- static size_t vidx;
- static hashitem_T *hi;
-
- if (idx == 0) {
- gdone = bdone = wdone = vidx = 0;
- tdone = 0;
- }
-
- // Global variables
- hashtab_T *globvarht = get_globvar_ht();
- if (gdone < globvarht->ht_used) {
- if (gdone++ == 0) {
- hi = globvarht->ht_array;
- } else {
- hi++;
- }
- while (HASHITEM_EMPTY(hi)) {
- hi++;
- }
- if (strncmp("g:", xp->xp_pattern, 2) == 0) {
- return cat_prefix_varname('g', hi->hi_key);
- }
- return hi->hi_key;
- }
-
- // b: variables
- const hashtab_T *ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab;
- if (bdone < ht->ht_used) {
- if (bdone++ == 0) {
- hi = ht->ht_array;
- } else {
- hi++;
- }
- while (HASHITEM_EMPTY(hi)) {
- hi++;
- }
- return cat_prefix_varname('b', hi->hi_key);
- }
-
- // w: variables
- ht = &prevwin_curwin()->w_vars->dv_hashtab;
- if (wdone < ht->ht_used) {
- if (wdone++ == 0) {
- hi = ht->ht_array;
- } else {
- hi++;
- }
- while (HASHITEM_EMPTY(hi)) {
- hi++;
- }
- return cat_prefix_varname('w', hi->hi_key);
- }
-
- // t: variables
- ht = &curtab->tp_vars->dv_hashtab;
- if (tdone < ht->ht_used) {
- if (tdone++ == 0) {
- hi = ht->ht_array;
- } else {
- hi++;
- }
- while (HASHITEM_EMPTY(hi)) {
- hi++;
- }
- return cat_prefix_varname('t', hi->hi_key);
- }
-
- // v: variables
- if (vidx < ARRAY_SIZE(vimvars)) {
- return cat_prefix_varname('v', vimvars[vidx++].vv_name);
- }
-
- XFREE_CLEAR(varnamebuf);
- varnamebuflen = 0;
- return NULL;
-}
-
/// Does not use 'cpo' and always uses 'magic'.
///
/// @return true if "pat" matches "text".
@@ -3172,7 +2725,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// 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 = get_vim_var_partial(VV_LUA);
rettv->vval.v_partial->pt_refcount++;
}
ret = OK;
@@ -3470,7 +3023,7 @@ static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const
} else if (lua_funcname != NULL) {
if (evaluate) {
rettv->v_type = VAR_PARTIAL;
- rettv->vval.v_partial = vvlua_partial;
+ rettv->vval.v_partial = get_vim_var_partial(VV_LUA);
rettv->vval.v_partial->pt_refcount++;
}
ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname);
@@ -5322,7 +4875,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
if (len == 0) {
return false;
}
- partial = vvlua_partial;
+ partial = get_vim_var_partial(VV_LUA);
} else {
partial = NULL;
}
@@ -6181,12 +5734,6 @@ bool eval_isdictc(int c)
return ASCII_ISALNUM(c) || c == '_';
}
-/// Get typval_T v: variable value.
-typval_T *get_vim_var_tv(const VimVarIndex idx)
-{
- return &vimvars[idx].vv_tv;
-}
-
/// Set the v:argv list.
void set_argv_var(char **argv, int argc)
{
@@ -6204,7 +5751,7 @@ void set_argv_var(char **argv, int argc)
bool is_luafunc(partial_T *partial)
FUNC_ATTR_PURE
{
- return partial == vvlua_partial;
+ return partial == get_vim_var_partial(VV_LUA);
}
/// check if special v:lua value for calling lua functions
@@ -6439,148 +5986,6 @@ void set_selfdict(typval_T *const rettv, dict_T *const selfdict)
make_partial(selfdict, rettv);
}
-/// Find variable in hashtab.
-/// When "varname" is empty returns curwin/curtab/etc vars dictionary.
-///
-/// @param[in] ht Hashtab to find variable in.
-/// @param[in] htname Hashtab name (first character).
-/// @param[in] varname Variable name.
-/// @param[in] varname_len Variable name length.
-/// @param[in] no_autoload If true then autoload scripts will not be sourced
-/// if autoload variable was not found.
-///
-/// @return pointer to the dictionary item with the found variable or NULL if it
-/// was not found.
-dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const varname,
- const size_t varname_len, int no_autoload)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
-{
- if (varname_len == 0) {
- // Must be something like "s:", otherwise "ht" would be NULL.
- switch (htname) {
- case 's':
- return (dictitem_T *)&SCRIPT_SV(current_sctx.sc_sid)->sv_var;
- case 'g':
- return (dictitem_T *)&globvars_var;
- case 'v':
- return (dictitem_T *)&vimvars_var;
- case 'b':
- return (dictitem_T *)&curbuf->b_bufvar;
- case 'w':
- return (dictitem_T *)&curwin->w_winvar;
- case 't':
- return (dictitem_T *)&curtab->tp_winvar;
- case 'l':
- return get_funccal_local_var();
- case 'a':
- return get_funccal_args_var();
- }
- return NULL;
- }
-
- hashitem_T *hi = hash_find_len(ht, varname, varname_len);
- if (HASHITEM_EMPTY(hi)) {
- // For global variables we may try auto-loading the script. If it
- // worked find the variable again. Don't auto-load a script if it was
- // loaded already, otherwise it would be loaded every time when
- // checking if a function name is a Funcref variable.
- if (ht == get_globvar_ht() && !no_autoload) {
- // Note: script_autoload() may make "hi" invalid. It must either
- // be obtained again or not used.
- if (!script_autoload(varname, varname_len, false) || aborting()) {
- return NULL;
- }
- hi = hash_find_len(ht, varname, varname_len);
- }
- if (HASHITEM_EMPTY(hi)) {
- return NULL;
- }
- }
- return TV_DICT_HI2DI(hi);
-}
-
-/// Finds the dict (g:, l:, s:, …) and hashtable used for a variable.
-///
-/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994
-///
-/// @param[in] name Variable name, possibly with scope prefix.
-/// @param[in] name_len Variable name length.
-/// @param[out] varname Will be set to the start of the name without scope
-/// prefix.
-/// @param[out] d Scope dictionary.
-///
-/// @return Scope hashtab, NULL if name is not valid.
-hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname,
- dict_T **d)
-{
- funccall_T *funccal = get_funccal();
- *d = NULL;
-
- if (name_len == 0) {
- return NULL;
- }
- if (name_len == 1 || name[1] != ':') {
- // name has implicit scope
- if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) {
- // The name must not start with a colon or #.
- return NULL;
- }
- *varname = name;
-
- // "version" is "v:version" in all scopes
- hashitem_T *hi = hash_find_len(&compat_hashtab, name, name_len);
- if (!HASHITEM_EMPTY(hi)) {
- return &compat_hashtab;
- }
-
- if (funccal == NULL) { // global variable
- *d = get_globvar_dict();
- } else { // l: variable
- *d = &funccal->fc_l_vars;
- }
- goto end;
- }
-
- *varname = name + 2;
- if (*name == 'g') { // global variable
- *d = get_globvar_dict();
- } else if (name_len > 2
- && (memchr(name + 2, ':', name_len - 2) != NULL
- || memchr(name + 2, AUTOLOAD_CHAR, name_len - 2) != NULL)) {
- // There must be no ':' or '#' in the rest of the name if g: was not used
- return NULL;
- }
-
- if (*name == 'b') { // buffer variable
- *d = curbuf->b_vars;
- } else if (*name == 'w') { // window variable
- *d = curwin->w_vars;
- } else if (*name == 't') { // tab page variable
- *d = curtab->tp_vars;
- } else if (*name == 'v') { // v: variable
- *d = get_vimvar_dict();
- } else if (*name == 'a' && funccal != NULL) { // function argument
- *d = &funccal->fc_l_avars;
- } else if (*name == 'l' && funccal != NULL) { // local variable
- *d = &funccal->fc_l_vars;
- } else if (*name == 's' // script variable
- && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR
- || current_sctx.sc_sid == SID_LUA)
- && current_sctx.sc_sid <= script_items.ga_len) {
- // For anonymous scripts without a script item, create one now so script vars can be used
- // Try to resolve lua filename & linenr so it can be shown in last-set messages.
- nlua_set_sctx(¤t_sctx);
- if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) {
- // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994
- new_script_item(NULL, ¤t_sctx.sc_sid);
- }
- *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict;
- }
-
-end:
- return *d ? &(*d)->dv_hashtab : NULL;
-}
-
/// Make a copy of an item
///
/// Lists and Dictionaries are also copied.
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
@@ -74,9 +74,6 @@ typedef enum {
VAR_FLAVOUR_SHADA = 4, // all uppercase
} var_flavour_T;
-/// Array mapping values from MessagePackType to corresponding list pointers
-extern const list_T *eval_msgpack_type_lists[NUM_MSGPACK_TYPES];
-
// Struct passed to get_v_event() and restore_v_event().
typedef struct {
bool sve_did_save;
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
@@ -16,6 +16,7 @@
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/vars.h"
#include "nvim/eval_defs.h"
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
@@ -255,6 +255,7 @@
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_encode.h"
+#include "nvim/eval/vars.h"
#include "nvim/func_attr.h"
/// Dummy variable used because some macros need lvalue
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
@@ -33,6 +33,7 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
+#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
@@ -45,6 +46,7 @@
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
+#include "nvim/version.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -65,14 +67,313 @@ static const char e_setting_v_str_to_value_with_wrong_type[]
static const char e_missing_end_marker_str[] = N_("E990: Missing end marker '%s'");
static const char e_cannot_use_heredoc_here[] = N_("E991: Cannot use =<< here");
+/// Variable used for g:
+static ScopeDictDictItem globvars_var;
static dict_T globvardict; // Dict with g: variables
/// g: value
#define globvarht globvardict.dv_hashtab
+/// Old Vim variables such as "v:version" are also available without the "v:".
+/// Also in functions. We need a special hashtable for them.
+static hashtab_T compat_hashtab;
+
+// values for vv_flags:
+#define VV_COMPAT 1 // compatible, also used without "v:"
+#define VV_RO 2 // read-only
+#define VV_RO_SBX 4 // read-only in the sandbox
+
+#define VV(idx, name, type, flags) \
+ [idx] = { \
+ .vv_name = (name), \
+ .vv_di = { \
+ .di_tv = { .v_type = (type) }, \
+ .di_flags = 0, \
+ .di_key = { 0 }, \
+ }, \
+ .vv_flags = (flags), \
+ }
+
+#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables
+
+// Array to hold the value of v: variables.
+// The value is in a dictitem, so that it can also be used in the v: scope.
+// The reason to use this table anyway is for very quick access to the
+// variables with the VV_ defines.
+static struct vimvar {
+ char *vv_name; ///< Name of the variable, without v:.
+ TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars).
+ char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX.
+} vimvars[] = {
+ // VV_ tails differing from upcased string literals:
+ // VV_CC_FROM "charconvert_from"
+ // VV_CC_TO "charconvert_to"
+ // VV_SEND_SERVER "servername"
+ // VV_REG "register"
+ // VV_OP "operator"
+ VV(VV_COUNT, "count", VAR_NUMBER, VV_RO),
+ VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO),
+ VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO),
+ VV(VV_ERRMSG, "errmsg", VAR_STRING, 0),
+ VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0),
+ VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0),
+ VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO),
+ VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0),
+ 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),
+ VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO),
+ VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO),
+ VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO),
+ VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO),
+ VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO),
+ VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO),
+ VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO),
+ VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO),
+ VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX),
+ VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO),
+ VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO),
+ VV(VV_DYING, "dying", VAR_NUMBER, VV_RO),
+ VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO),
+ VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO),
+ VV(VV_REG, "register", VAR_STRING, VV_RO),
+ VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO),
+ VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO),
+ VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO),
+ VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO),
+ VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO),
+ VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO),
+ VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0),
+ VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO),
+ VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO),
+ VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0),
+ VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO),
+ VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0),
+ VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO),
+ VV(VV_CHAR, "char", VAR_STRING, 0),
+ VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0),
+ VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0),
+ VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0),
+ VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0),
+ VV(VV_OP, "operator", VAR_STRING, VV_RO),
+ VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0),
+ VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0),
+ VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0),
+ VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX),
+ VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO),
+ VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, 0),
+ VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO),
+ VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO),
+ VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO),
+ VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO),
+ VV(VV_ERRORS, "errors", VAR_LIST, 0),
+ VV(VV_FALSE, "false", VAR_BOOL, VV_RO),
+ VV(VV_TRUE, "true", VAR_BOOL, VV_RO),
+ VV(VV_NULL, "null", VAR_SPECIAL, VV_RO),
+ VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO),
+ VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO),
+ VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO),
+ VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO),
+ VV(VV_TESTING, "testing", VAR_NUMBER, 0),
+ VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
+ VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO),
+ VV(VV_EVENT, "event", VAR_DICT, VV_RO),
+ VV(VV_VERSIONLONG, "versionlong", VAR_NUMBER, VV_RO),
+ VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
+ 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),
+ VV(VV_STACKTRACE, "stacktrace", VAR_LIST, VV_RO),
+ // Neovim
+ VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
+ VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
+ VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO),
+ VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO),
+ VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO),
+ VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO),
+ VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO),
+ VV(VV_RELNUM, "relnum", VAR_NUMBER, VV_RO),
+ VV(VV_VIRTNUM, "virtnum", VAR_NUMBER, VV_RO),
+};
+#undef VV
+
+// shorthand
+#define vv_type vv_di.di_tv.v_type
+#define vv_str vv_di.di_tv.vval.v_string
+#define vv_list vv_di.di_tv.vval.v_list
+#define vv_tv vv_di.di_tv
+
+/// Variable used for v:
+static ScopeDictDictItem vimvars_var;
static dict_T vimvardict; // Dict with v: variables
/// v: hashtab
#define vimvarht vimvardict.dv_hashtab
+static const char *const msgpack_type_names[] = {
+ [kMPNil] = "nil",
+ [kMPBoolean] = "boolean",
+ [kMPInteger] = "integer",
+ [kMPFloat] = "float",
+ [kMPString] = "string",
+ [kMPArray] = "array",
+ [kMPMap] = "map",
+ [kMPExt] = "ext",
+};
+const list_T *eval_msgpack_type_lists[] = {
+ [kMPNil] = NULL,
+ [kMPBoolean] = NULL,
+ [kMPInteger] = NULL,
+ [kMPFloat] = NULL,
+ [kMPString] = NULL,
+ [kMPArray] = NULL,
+ [kMPMap] = NULL,
+ [kMPExt] = NULL,
+};
+
+#define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars)
+#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
+
+void evalvars_init(void)
+{
+ init_var_dict(get_globvar_dict(), &globvars_var, VAR_DEF_SCOPE);
+ init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
+ vimvardict.dv_lock = VAR_FIXED;
+ hash_init(&compat_hashtab);
+
+ for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
+ struct vimvar *p = &vimvars[i];
+ assert(strlen(p->vv_name) <= VIMVAR_KEY_LEN);
+ STRCPY(p->vv_di.di_key, p->vv_name);
+ if (p->vv_flags & VV_RO) {
+ p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ } else if (p->vv_flags & VV_RO_SBX) {
+ p->vv_di.di_flags = DI_FLAGS_RO_SBX | DI_FLAGS_FIX;
+ } else {
+ p->vv_di.di_flags = DI_FLAGS_FIX;
+ }
+
+ // add to v: scope dict, unless the value is not always available
+ if (p->vv_type != VAR_UNKNOWN) {
+ hash_add(&vimvarht, p->vv_di.di_key);
+ }
+ if (p->vv_flags & VV_COMPAT) {
+ // add to compat scope dict
+ hash_add(&compat_hashtab, p->vv_di.di_key);
+ }
+ }
+ set_vim_var_nr(VV_VERSION, VIM_VERSION_100);
+ set_vim_var_nr(VV_VERSIONLONG, VIM_VERSION_100 * 10000 + highest_patch());
+
+ dict_T *const msgpack_types_dict = tv_dict_alloc();
+ for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
+ list_T *const type_list = tv_list_alloc(0);
+ tv_list_set_lock(type_list, VAR_FIXED);
+ tv_list_ref(type_list);
+ dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]);
+ di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX;
+ di->di_tv = (typval_T) {
+ .v_type = VAR_LIST,
+ .vval = { .v_list = type_list, },
+ };
+ eval_msgpack_type_lists[i] = type_list;
+ if (tv_dict_add(msgpack_types_dict, di) == FAIL) {
+ // There must not be duplicate items in this dictionary by definition.
+ abort();
+ }
+ }
+ msgpack_types_dict->dv_lock = VAR_FIXED;
+
+ set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
+ set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
+
+ 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, 1);
+ set_vim_var_nr(VV_HLSEARCH, 1);
+ set_vim_var_nr(VV_COUNT1, 1);
+ set_vim_var_special(VV_EXITING, kSpecialVarNull);
+
+ set_vim_var_nr(VV_TYPE_NUMBER, VAR_TYPE_NUMBER);
+ set_vim_var_nr(VV_TYPE_STRING, VAR_TYPE_STRING);
+ set_vim_var_nr(VV_TYPE_FUNC, VAR_TYPE_FUNC);
+ set_vim_var_nr(VV_TYPE_LIST, VAR_TYPE_LIST);
+ set_vim_var_nr(VV_TYPE_DICT, VAR_TYPE_DICT);
+ set_vim_var_nr(VV_TYPE_FLOAT, VAR_TYPE_FLOAT);
+ set_vim_var_nr(VV_TYPE_BOOL, VAR_TYPE_BOOL);
+ set_vim_var_nr(VV_TYPE_BLOB, VAR_TYPE_BLOB);
+
+ set_vim_var_bool(VV_FALSE, kBoolVarFalse);
+ set_vim_var_bool(VV_TRUE, kBoolVarTrue);
+ set_vim_var_special(VV_NULL, kSpecialVarNull);
+ set_vim_var_nr(VV_NUMBERMAX, VARNUMBER_MAX);
+ set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN);
+ set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8);
+ set_vim_var_nr(VV_MAXCOL, MAXCOL);
+
+ set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
+
+ // vimvars[VV_LUA].vv_type = VAR_PARTIAL;
+ partial_T *vvlua_partial = xcalloc(1, sizeof(partial_T));
+ // this value shouldn't be printed, but if it is, do not crash
+ vvlua_partial->pt_name = xmallocz(0);
+ vvlua_partial->pt_refcount++;
+ set_vim_var_partial(VV_LUA, vvlua_partial);
+
+ set_reg_var(0); // default for v:register is not 0 but '"'
+}
+
+#if defined(EXITFREE)
+void evalvars_clear(void)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
+ struct vimvar *p = &vimvars[i];
+ if (p->vv_di.di_tv.v_type == VAR_STRING) {
+ XFREE_CLEAR(p->vv_str);
+ } else if (p->vv_di.di_tv.v_type == VAR_LIST) {
+ tv_list_unref(p->vv_list);
+ p->vv_list = NULL;
+ }
+ }
+
+ partial_unref(get_vim_var_partial(VV_LUA));
+ set_vim_var_partial(VV_LUA, NULL);
+ hash_clear(&vimvarht);
+ hash_init(&vimvarht); // garbage_collect() will access it
+ hash_clear(&compat_hashtab);
+
+ // global variables
+ vars_clear(get_globvar_ht());
+
+ // 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));
+ }
+}
+#endif
+
int garbage_collect_globvars(int copyID)
{
return set_ref_in_ht(&globvarht, copyID, NULL);
@@ -254,6 +555,36 @@ int get_spellword(list_T *const list, const char **ret_word)
return (int)tv_list_find_nr(list, -1, NULL);
}
+/// Prepare v: variable "idx" to be used.
+/// 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)
+{
+ vimvars[idx].vv_tv = *save_tv;
+ if (vimvars[idx].vv_type != VAR_UNKNOWN) {
+ return;
+ }
+
+ hashitem_T *hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key);
+ if (HASHITEM_EMPTY(hi)) {
+ internal_error("restore_vimvar()");
+ } else {
+ hash_remove(&vimvarht, hi);
+ }
+}
+
/// List Vim variables.
static void list_vim_vars(int *first)
{
@@ -1378,7 +1709,7 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
if (d == NULL) {
if (ht == &globvarht) {
d = &globvardict;
- } else if (is_compatht(ht)) {
+ } else if (ht == &compat_hashtab) {
d = &vimvardict;
} else {
dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false);
@@ -1533,9 +1864,21 @@ dict_T *get_vimvar_dict(void)
/// @param[in] val Value to set to. Will be copied.
void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv)
{
- typval_T *vv_tv = get_vim_var_tv(idx);
- tv_clear(vv_tv);
- tv_copy(tv, vv_tv);
+ typval_T *tv_out = get_vim_var_tv(idx);
+ tv_clear(tv_out);
+ tv_copy(tv, tv_out);
+}
+
+char *get_vim_var_name(const VimVarIndex idx)
+ FUNC_ATTR_NONNULL_RET
+{
+ return vimvars[idx].vv_name;
+}
+
+/// Get typval_T v: variable value.
+typval_T *get_vim_var_tv(const VimVarIndex idx)
+{
+ return &vimvars[idx].vv_tv;
}
/// Get number v: variable value.
@@ -1570,6 +1913,123 @@ char *get_vim_var_str(const VimVarIndex idx)
return (char *)tv_get_string(get_vim_var_tv(idx));
}
+/// Get Partial v: variable value. Caller must take care of reference count
+/// when needed.
+partial_T *get_vim_var_partial(const VimVarIndex idx) FUNC_ATTR_PURE
+{
+ typval_T *tv = get_vim_var_tv(idx);
+ return tv->vval.v_partial;
+}
+
+/// Local string buffer for the next two functions to store a variable name
+/// with its prefix. Allocated in cat_prefix_varname(), freed later in
+/// get_user_var_name().
+
+static char *varnamebuf = NULL;
+static size_t varnamebuflen = 0;
+
+/// Function to concatenate a prefix and a variable name.
+char *cat_prefix_varname(int prefix, const char *name)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t len = strlen(name) + 3;
+
+ if (len > varnamebuflen) {
+ xfree(varnamebuf);
+ len += 10; // some additional space
+ varnamebuf = xmalloc(len);
+ varnamebuflen = len;
+ }
+ *varnamebuf = (char)prefix;
+ varnamebuf[1] = ':';
+ STRCPY(varnamebuf + 2, name);
+ return varnamebuf;
+}
+
+/// Function given to ExpandGeneric() to obtain the list of user defined
+/// (global/buffer/window/built-in) variable names.
+char *get_user_var_name(expand_T *xp, int idx)
+{
+ static size_t gdone;
+ static size_t bdone;
+ static size_t wdone;
+ static size_t tdone;
+ static size_t vidx;
+ static hashitem_T *hi;
+
+ if (idx == 0) {
+ gdone = bdone = wdone = vidx = 0;
+ tdone = 0;
+ }
+
+ // Global variables
+ if (gdone < globvarht.ht_used) {
+ if (gdone++ == 0) {
+ hi = globvarht.ht_array;
+ } else {
+ hi++;
+ }
+ while (HASHITEM_EMPTY(hi)) {
+ hi++;
+ }
+ if (strncmp("g:", xp->xp_pattern, 2) == 0) {
+ return cat_prefix_varname('g', hi->hi_key);
+ }
+ return hi->hi_key;
+ }
+
+ // b: variables
+ const hashtab_T *ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab;
+ if (bdone < ht->ht_used) {
+ if (bdone++ == 0) {
+ hi = ht->ht_array;
+ } else {
+ hi++;
+ }
+ while (HASHITEM_EMPTY(hi)) {
+ hi++;
+ }
+ return cat_prefix_varname('b', hi->hi_key);
+ }
+
+ // w: variables
+ ht = &prevwin_curwin()->w_vars->dv_hashtab;
+ if (wdone < ht->ht_used) {
+ if (wdone++ == 0) {
+ hi = ht->ht_array;
+ } else {
+ hi++;
+ }
+ while (HASHITEM_EMPTY(hi)) {
+ hi++;
+ }
+ return cat_prefix_varname('w', hi->hi_key);
+ }
+
+ // t: variables
+ ht = &curtab->tp_vars->dv_hashtab;
+ if (tdone < ht->ht_used) {
+ if (tdone++ == 0) {
+ hi = ht->ht_array;
+ } else {
+ hi++;
+ }
+ while (HASHITEM_EMPTY(hi)) {
+ hi++;
+ }
+ return cat_prefix_varname('t', hi->hi_key);
+ }
+
+ // v: variables
+ if (vidx < ARRAY_SIZE(vimvars)) {
+ return cat_prefix_varname('v', get_vim_var_name((VimVarIndex)vidx++));
+ }
+
+ XFREE_CLEAR(varnamebuf);
+ varnamebuflen = 0;
+ return NULL;
+}
+
/// Set type of v: variable to the given type.
///
/// @param[in] idx Index of variable to set.
@@ -1680,6 +2140,17 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val)
tv_dict_set_keys_readonly(val);
}
+/// Set partial v: variable to the given value
+/// Note that this does not set the type, use set_vim_var_type() for that.
+///
+/// @param[in] idx Index of variable to set.
+/// @param[in] val Value to set to.
+void set_vim_var_partial(const VimVarIndex idx, partial_T *val)
+{
+ typval_T *tv = get_vim_var_tv(idx);
+ tv->vval.v_partial = val;
+}
+
/// Set v:register if needed.
void set_reg_var(int c)
{
@@ -1939,6 +2410,148 @@ dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **
return find_var_in_scoped_ht(name, name_len, no_autoload || htp != NULL);
}
+/// Find variable in hashtab.
+/// When "varname" is empty returns curwin/curtab/etc vars dictionary.
+///
+/// @param[in] ht Hashtab to find variable in.
+/// @param[in] htname Hashtab name (first character).
+/// @param[in] varname Variable name.
+/// @param[in] varname_len Variable name length.
+/// @param[in] no_autoload If true then autoload scripts will not be sourced
+/// if autoload variable was not found.
+///
+/// @return pointer to the dictionary item with the found variable or NULL if it
+/// was not found.
+dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const varname,
+ const size_t varname_len, int no_autoload)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ if (varname_len == 0) {
+ // Must be something like "s:", otherwise "ht" would be NULL.
+ switch (htname) {
+ case 's':
+ return (dictitem_T *)&SCRIPT_SV(current_sctx.sc_sid)->sv_var;
+ case 'g':
+ return (dictitem_T *)&globvars_var;
+ case 'v':
+ return (dictitem_T *)&vimvars_var;
+ case 'b':
+ return (dictitem_T *)&curbuf->b_bufvar;
+ case 'w':
+ return (dictitem_T *)&curwin->w_winvar;
+ case 't':
+ return (dictitem_T *)&curtab->tp_winvar;
+ case 'l':
+ return get_funccal_local_var();
+ case 'a':
+ return get_funccal_args_var();
+ }
+ return NULL;
+ }
+
+ hashitem_T *hi = hash_find_len(ht, varname, varname_len);
+ if (HASHITEM_EMPTY(hi)) {
+ // For global variables we may try auto-loading the script. If it
+ // worked find the variable again. Don't auto-load a script if it was
+ // loaded already, otherwise it would be loaded every time when
+ // checking if a function name is a Funcref variable.
+ if (ht == get_globvar_ht() && !no_autoload) {
+ // Note: script_autoload() may make "hi" invalid. It must either
+ // be obtained again or not used.
+ if (!script_autoload(varname, varname_len, false) || aborting()) {
+ return NULL;
+ }
+ hi = hash_find_len(ht, varname, varname_len);
+ }
+ if (HASHITEM_EMPTY(hi)) {
+ return NULL;
+ }
+ }
+ return TV_DICT_HI2DI(hi);
+}
+
+/// Finds the dict (g:, l:, s:, …) and hashtable used for a variable.
+///
+/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994
+///
+/// @param[in] name Variable name, possibly with scope prefix.
+/// @param[in] name_len Variable name length.
+/// @param[out] varname Will be set to the start of the name without scope
+/// prefix.
+/// @param[out] d Scope dictionary.
+///
+/// @return Scope hashtab, NULL if name is not valid.
+static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname,
+ dict_T **d)
+{
+ funccall_T *funccal = get_funccal();
+ *d = NULL;
+
+ if (name_len == 0) {
+ return NULL;
+ }
+ if (name_len == 1 || name[1] != ':') {
+ // name has implicit scope
+ if (name[0] == ':' || name[0] == AUTOLOAD_CHAR) {
+ // The name must not start with a colon or #.
+ return NULL;
+ }
+ *varname = name;
+
+ // "version" is "v:version" in all scopes
+ hashitem_T *hi = hash_find_len(&compat_hashtab, name, name_len);
+ if (!HASHITEM_EMPTY(hi)) {
+ return &compat_hashtab;
+ }
+
+ if (funccal == NULL) { // global variable
+ *d = get_globvar_dict();
+ } else { // l: variable
+ *d = &funccal->fc_l_vars;
+ }
+ goto end;
+ }
+
+ *varname = name + 2;
+ if (*name == 'g') { // global variable
+ *d = get_globvar_dict();
+ } else if (name_len > 2
+ && (memchr(name + 2, ':', name_len - 2) != NULL
+ || memchr(name + 2, AUTOLOAD_CHAR, name_len - 2) != NULL)) {
+ // There must be no ':' or '#' in the rest of the name if g: was not used
+ return NULL;
+ }
+
+ if (*name == 'b') { // buffer variable
+ *d = curbuf->b_vars;
+ } else if (*name == 'w') { // window variable
+ *d = curwin->w_vars;
+ } else if (*name == 't') { // tab page variable
+ *d = curtab->tp_vars;
+ } else if (*name == 'v') { // v: variable
+ *d = get_vimvar_dict();
+ } else if (*name == 'a' && funccal != NULL) { // function argument
+ *d = &funccal->fc_l_avars;
+ } else if (*name == 'l' && funccal != NULL) { // local variable
+ *d = &funccal->fc_l_vars;
+ } else if (*name == 's' // script variable
+ && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR
+ || current_sctx.sc_sid == SID_LUA)
+ && current_sctx.sc_sid <= script_items.ga_len) {
+ // For anonymous scripts without a script item, create one now so script vars can be used
+ // Try to resolve lua filename & linenr so it can be shown in last-set messages.
+ nlua_set_sctx(¤t_sctx);
+ if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) {
+ // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994
+ new_script_item(NULL, ¤t_sctx.sc_sid);
+ }
+ *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict;
+ }
+
+end:
+ return *d ? &(*d)->dv_hashtab : NULL;
+}
+
/// Find the hashtable used for a variable
///
/// @param[in] name Variable name, possibly with scope prefix.
diff --git a/src/nvim/eval/vars.h b/src/nvim/eval/vars.h
@@ -9,7 +9,7 @@
#include "nvim/option_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
-#include "eval/vars.h.generated.h"
+/// Array mapping values from MessagePackType to corresponding list pointers
+extern const list_T *eval_msgpack_type_lists[NUM_MSGPACK_TYPES];
-#define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars)
-#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
+#include "eval/vars.h.generated.h"