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)

e5cdf153bc

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>
This commit is contained in:
Jan Edmund Lazo
2025-10-25 18:20:33 -04:00
committed by GitHub
parent df23952ce9
commit b38ce04283
7 changed files with 629 additions and 611 deletions

View File

@@ -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"

View File

@@ -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(&current_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, &current_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.

View File

@@ -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;

View File

@@ -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"

View File

@@ -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

View File

@@ -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(&current_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, &current_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.

View File

@@ -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"