summaryrefslogtreecommitdiffstats
path: root/src/evalvars.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/evalvars.c')
-rw-r--r--src/evalvars.c320
1 files changed, 239 insertions, 81 deletions
diff --git a/src/evalvars.c b/src/evalvars.c
index d359c7f44c..ebb30dd22e 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -15,8 +15,6 @@
#if defined(FEAT_EVAL) || defined(PROTO)
-static char *e_letunexp = N_("E18: Unexpected characters in :let");
-
static dictitem_T globvars_var; // variable used for g:
static dict_T globvardict; // Dictionary with g: variables
#define globvarht globvardict.dv_hashtab
@@ -41,6 +39,8 @@ static hashtab_T compat_hashtab;
#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}
+typedef struct vimvar vimvar_T;
+
static struct vimvar
{
char *vv_name; // name of variable, without v:
@@ -163,17 +163,14 @@ static dict_T vimvardict; // Dictionary with v: variables
// for VIM_VERSION_ defines
#include "version.h"
-#define SCRIPT_SV(id) (SCRIPT_ITEM(id).sn_vars)
-#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
-
static void ex_let_const(exarg_T *eap, int is_const);
-static char_u *skip_var_one(char_u *arg);
+static char_u *skip_var_one(char_u *arg, int include_type);
static void list_glob_vars(int *first);
static void list_buf_vars(int *first);
static void list_win_vars(int *first);
static void list_tab_vars(int *first);
static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
-static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int is_const, char_u *endchars, char_u *op);
+static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep);
static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit);
static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock);
@@ -544,7 +541,7 @@ list_script_vars(int *first)
* indentation in the 'cmd' line) is stripped.
* Returns a List with {lines} or NULL.
*/
- static list_T *
+ list_T *
heredoc_get(exarg_T *eap, char_u *cmd)
{
char_u *theline;
@@ -669,6 +666,7 @@ heredoc_get(exarg_T *eap, char_u *cmd)
* ":let var .= expr" assignment command.
* ":let var ..= expr" assignment command.
* ":let [var1, var2] = expr" unpack list.
+ * ":let var =<< ..." heredoc
*/
void
ex_let(exarg_T *eap)
@@ -701,8 +699,13 @@ ex_let_const(exarg_T *eap, int is_const)
char_u *argend;
int first = TRUE;
int concat;
+ int flags = is_const ? LET_IS_CONST : 0;
+
+ // detect Vim9 assignment without ":let" or ":const"
+ if (eap->arg == eap->cmd)
+ flags |= LET_NO_COMMAND;
- argend = skip_var_list(arg, &var_count, &semicolon);
+ argend = skip_var_list(arg, TRUE, &var_count, &semicolon);
if (argend == NULL)
return;
if (argend > arg && argend[-1] == '.') // for var.='str'
@@ -749,7 +752,7 @@ ex_let_const(exarg_T *eap, int is_const)
op[0] = '=';
op[1] = NUL;
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
- is_const, op);
+ flags, op);
}
clear_tv(&rettv);
}
@@ -783,7 +786,7 @@ ex_let_const(exarg_T *eap, int is_const)
else if (i != FAIL)
{
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
- is_const, op);
+ flags, op);
clear_tv(&rettv);
}
}
@@ -804,7 +807,7 @@ ex_let_vars(
int copy, // copy values from "tv", don't move
int semicolon, // from skip_var_list()
int var_count, // from skip_var_list()
- int is_const, // lock variables for const
+ int flags, // LET_IS_CONST and/or LET_NO_COMMAND
char_u *op)
{
char_u *arg = arg_start;
@@ -816,7 +819,7 @@ ex_let_vars(
if (*arg != '[')
{
// ":let var = expr" or ":for var in list"
- if (ex_let_one(arg, tv, copy, is_const, op, op) == NULL)
+ if (ex_let_one(arg, tv, copy, flags, op, op) == NULL)
return FAIL;
return OK;
}
@@ -844,8 +847,7 @@ ex_let_vars(
while (*arg != ']')
{
arg = skipwhite(arg + 1);
- arg = ex_let_one(arg, &item->li_tv, TRUE, is_const,
- (char_u *)",;]", op);
+ arg = ex_let_one(arg, &item->li_tv, TRUE, flags, (char_u *)",;]", op);
item = item->li_next;
if (arg == NULL)
return FAIL;
@@ -869,7 +871,7 @@ ex_let_vars(
ltv.vval.v_list = l;
l->lv_refcount = 1;
- arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, is_const,
+ arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, flags,
(char_u *)"]", op);
clear_tv(&ltv);
if (arg == NULL)
@@ -896,6 +898,7 @@ ex_let_vars(
char_u *
skip_var_list(
char_u *arg,
+ int include_type,
int *var_count,
int *semicolon)
{
@@ -908,7 +911,7 @@ skip_var_list(
for (;;)
{
p = skipwhite(p + 1); // skip whites after '[', ';' or ','
- s = skip_var_one(p);
+ s = skip_var_one(p, TRUE);
if (s == p)
{
semsg(_(e_invarg2), p);
@@ -937,20 +940,29 @@ skip_var_list(
return p + 1;
}
else
- return skip_var_one(arg);
+ return skip_var_one(arg, include_type);
}
/*
* Skip one (assignable) variable name, including @r, $VAR, &option, d.key,
* l[idx].
+ * In Vim9 script also skip over ": type" if "include_type" is TRUE.
*/
static char_u *
-skip_var_one(char_u *arg)
+skip_var_one(char_u *arg, int include_type)
{
+ char_u *end;
+
if (*arg == '@' && arg[1] != NUL)
return arg + 2;
- return find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
+ end = find_name_end(*arg == '$' || *arg == '&' ? arg + 1 : arg,
NULL, NULL, FNE_INCL_BR | FNE_CHECK_START);
+ if (include_type && current_sctx.sc_version == SCRIPT_VERSION_VIM9
+ && *end == ':')
+ {
+ end = skip_type(skipwhite(end + 1));
+ }
+ return end;
}
/*
@@ -1141,7 +1153,7 @@ ex_let_one(
char_u *arg, // points to variable name
typval_T *tv, // value to assign to variable
int copy, // copy value from "tv"
- int is_const, // lock variable for const
+ int flags, // LET_IS_CONST and/or LET_NO_COMMAND
char_u *endchars, // valid chars after variable name or NULL
char_u *op) // "+", "-", "." or NULL
{
@@ -1156,7 +1168,7 @@ ex_let_one(
// ":let $VAR = expr": Set environment variable.
if (*arg == '$')
{
- if (is_const)
+ if (flags & LET_IS_CONST)
{
emsg(_("E996: Cannot lock an environment variable"));
return NULL;
@@ -1214,9 +1226,9 @@ ex_let_one(
// ":let &g:option = expr": Set global option value.
else if (*arg == '&')
{
- if (is_const)
+ if (flags & LET_IS_CONST)
{
- emsg(_("E996: Cannot lock an option"));
+ emsg(_(e_const_option));
return NULL;
}
// Find the end of the name.
@@ -1281,7 +1293,7 @@ ex_let_one(
// ":let @r = expr": Set register contents.
else if (*arg == '@')
{
- if (is_const)
+ if (flags & LET_IS_CONST)
{
emsg(_("E996: Cannot lock a register"));
return NULL;
@@ -1317,6 +1329,7 @@ ex_let_one(
}
// ":let var = expr": Set internal variable.
+ // ":let var: type = expr": Set internal variable with type.
// ":let {expr} = expr": Idem, name made with curly braces
else if (eval_isnamec1(*arg) || *arg == '{')
{
@@ -1325,11 +1338,12 @@ ex_let_one(
p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START);
if (p != NULL && lv.ll_name != NULL)
{
- if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL)
+ if (endchars != NULL && vim_strchr(endchars,
+ *skipwhite(lv.ll_name_end)) == NULL)
emsg(_(e_letunexp));
else
{
- set_var_lval(&lv, p, tv, copy, is_const, op);
+ set_var_lval(&lv, p, tv, copy, flags, op);
arg_end = p;
}
}
@@ -1657,12 +1671,13 @@ item_lock(typval_T *tv, int deep, int lock)
switch (tv->v_type)
{
case VAR_UNKNOWN:
+ case VAR_VOID:
case VAR_NUMBER:
+ case VAR_BOOL:
case VAR_STRING:
case VAR_FUNC:
case VAR_PARTIAL:
case VAR_FLOAT:
- case VAR_BOOL:
case VAR_SPECIAL:
case VAR_JOB:
case VAR_CHANNEL:
@@ -1901,6 +1916,22 @@ get_vimvar_dict(void)
}
/*
+ * Returns the index of a v:variable. Negative if not found.
+ */
+ int
+find_vim_var(char_u *name)
+{
+ dictitem_T *di = find_var_in_ht(&vimvarht, 0, name, TRUE);
+ struct vimvar *vv;
+
+ if (di == NULL)
+ return -1;
+ vv = (struct vimvar *)((char *)di - offsetof(vimvar_T, vv_di));
+ return (int)(vv - vimvars);
+}
+
+
+/*
* Set type of v: variable to "type".
*/
void
@@ -1919,6 +1950,12 @@ set_vim_var_nr(int idx, varnumber_T val)
vimvars[idx].vv_nr = val;
}
+ char *
+get_vim_var_name(int idx)
+{
+ return vimvars[idx].vv_name;
+}
+
/*
* Get typval_T v: variable value.
*/
@@ -2245,6 +2282,20 @@ get_var_tv(
*dip = v;
}
+ if (tv == NULL && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+ {
+ imported_T *import = find_imported(name, NULL);
+
+ // imported variable from another script
+ if (import != NULL)
+ {
+ scriptitem_T *si = &SCRIPT_ITEM(import->imp_sid);
+ svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
+ + import->imp_var_vals_idx;
+ tv = sv->sv_tv;
+ }
+ }
+
if (tv == NULL)
{
if (rettv != NULL && verbose)
@@ -2366,6 +2417,58 @@ find_var_in_ht(
}
/*
+ * Get the script-local hashtab. NULL if not in a script context.
+ */
+ hashtab_T *
+get_script_local_ht(void)
+{
+ scid_T sid = current_sctx.sc_sid;
+
+ if (sid > 0 && sid <= script_items.ga_len)
+ return &SCRIPT_VARS(sid);
+ return NULL;
+}
+
+/*
+ * Look for "name[len]" in script-local variables.
+ * Return -1 when not found.
+ */
+ int
+lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
+{
+ hashtab_T *ht = get_script_local_ht();
+ char_u buffer[30];
+ char_u *p;
+ int res;
+ hashitem_T *hi;
+
+ if (ht == NULL)
+ return -1;
+ if (len < sizeof(buffer) - 1)
+ {
+ vim_strncpy(buffer, name, len);
+ p = buffer;
+ }
+ else
+ {
+ p = vim_strnsave(name, (int)len);
+ if (p == NULL)
+ return -1;
+ }
+
+ hi = hash_find(ht, p);
+ res = HASHITEM_EMPTY(hi) ? -1 : 1;
+
+ // if not script-local, then perhaps imported
+ if (res == -1 && find_imported(p, NULL) != NULL)
+ res = 1;
+
+ if (p != buffer)
+ vim_free(p);
+ return res;
+}
+
+/*
* Find the hashtab used for a variable name.
* Return NULL if the name is not valid.
* Set "varname" to the start of name without ':'.
@@ -2395,9 +2498,18 @@ find_var_ht(char_u *name, char_u **varname)
}
ht = get_funccal_local_ht();
- if (ht == NULL)
- return &globvarht; // global variable
- return ht; // local variable
+ if (ht != NULL)
+ return ht; // local variable
+
+ // in Vim9 script items at the script level are script-local
+ if (current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+ {
+ ht = get_script_local_ht();
+ if (ht != NULL)
+ return ht;
+ }
+
+ return &globvarht; // global variable
}
*varname = name + 2;
if (*name == 'g') // global variable
@@ -2414,14 +2526,19 @@ find_var_ht(char_u *name, char_u **varname)
return &curtab->tp_vars->dv_hashtab;
if (*name == 'v') // v: variable
return &vimvarht;
- if (*name == 'a') // a: function argument
- return get_funccal_args_ht();
- if (*name == 'l') // l: local function variable
- return get_funccal_local_ht();
- if (*name == 's' // script variable
- && current_sctx.sc_sid > 0
- && current_sctx.sc_sid <= script_items.ga_len)
- return &SCRIPT_VARS(current_sctx.sc_sid);
+ if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
+ {
+ if (*name == 'a') // a: function argument
+ return get_funccal_args_ht();
+ if (*name == 'l') // l: local function variable
+ return get_funccal_local_ht();
+ }
+ if (*name == 's') // script variable
+ {
+ ht = get_script_local_ht();
+ if (ht != NULL)
+ return ht;
+ }
return NULL;
}
@@ -2617,7 +2734,7 @@ set_var(
typval_T *tv,
int copy) // make copy of value in "tv"
{
- set_var_const(name, tv, copy, FALSE);
+ set_var_const(name, NULL, tv, copy, 0);
}
/*
@@ -2628,13 +2745,15 @@ set_var(
void
set_var_const(
char_u *name,
+ type_T *type,
typval_T *tv,
int copy, // make copy of value in "tv"
- int is_const) // disallow to modify existing variable
+ int flags) // LET_IS_CONST and/or LET_NO_COMMAND
{
- dictitem_T *v;
+ dictitem_T *di;
char_u *varname;
hashtab_T *ht;
+ int is_script_local;
ht = find_var_ht(name, &varname);
if (ht == NULL || *varname == NUL)
@@ -2642,75 +2761,92 @@ set_var_const(
semsg(_(e_illvar), name);
return;
}
- v = find_var_in_ht(ht, 0, varname, TRUE);
+ is_script_local = ht == get_script_local_ht();
+
+ di = find_var_in_ht(ht, 0, varname, TRUE);
// Search in parent scope which is possible to reference from lambda
- if (v == NULL)
- v = find_var_in_scoped_ht(name, TRUE);
+ if (di == NULL)
+ di = find_var_in_scoped_ht(name, TRUE);
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
- && var_check_func_name(name, v == NULL))
+ && var_check_func_name(name, di == NULL))
return;
- if (v != NULL)
+ if (di != NULL)
{
- if (is_const)
+ if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
{
- emsg(_(e_cannot_mod));
- return;
+ if (flags & LET_IS_CONST)
+ {
+ emsg(_(e_cannot_mod));
+ return;
+ }
+
+ if (var_check_ro(di->di_flags, name, FALSE)
+ || var_check_lock(di->di_tv.v_lock, name, FALSE))
+ return;
+
+ if ((flags & LET_NO_COMMAND) == 0
+ && is_script_local
+ && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+ {
+ semsg(_("E1041: Redefining script item %s"), name);
+ return;
+ }
}
+ else
+ // can only redefine once
+ di->di_flags &= ~DI_FLAGS_RELOAD;
// existing variable, need to clear the value
- if (var_check_ro(v->di_flags, name, FALSE)
- || var_check_lock(v->di_tv.v_lock, name, FALSE))
- return;
- // Handle setting internal v: variables separately where needed to
+ // Handle setting internal di: variables separately where needed to
// prevent changing the type.
if (ht == &vimvarht)
{
- if (v->di_tv.v_type == VAR_STRING)
+ if (di->di_tv.v_type == VAR_STRING)
{
- VIM_CLEAR(v->di_tv.vval.v_string);
+ VIM_CLEAR(di->di_tv.vval.v_string);
if (copy || tv->v_type != VAR_STRING)
{
char_u *val = tv_get_string(tv);
// Careful: when assigning to v:errmsg and tv_get_string()
// causes an error message the variable will alrady be set.
- if (v->di_tv.vval.v_string == NULL)
- v->di_tv.vval.v_string = vim_strsave(val);
+ if (di->di_tv.vval.v_string == NULL)
+ di->di_tv.vval.v_string = vim_strsave(val);
}
else
{
// Take over the string to avoid an extra alloc/free.
- v->di_tv.vval.v_string = tv->vval.v_string;
+ di->di_tv.vval.v_string = tv->vval.v_string;
tv->vval.v_string = NULL;
}
return;
}
- else if (v->di_tv.v_type == VAR_NUMBER)
+ else if (di->di_tv.v_type == VAR_NUMBER)
{
- v->di_tv.vval.v_number = tv_get_number(tv);
+ di->di_tv.vval.v_number = tv_get_number(tv);
if (STRCMP(varname, "searchforward") == 0)
- set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
+ set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
#ifdef FEAT_SEARCH_EXTRA
else if (STRCMP(varname, "hlsearch") == 0)
{
- no_hlsearch = !v->di_tv.vval.v_number;
+ no_hlsearch = !di->di_tv.vval.v_number;
redraw_all_later(SOME_VALID);
}
#endif
return;
}
- else if (v->di_tv.v_type != tv->v_type)
+ else if (di->di_tv.v_type != tv->v_type)
{
semsg(_("E963: setting %s to value with wrong type"), name);
return;
}
}
- clear_tv(&v->di_tv);
+ clear_tv(&di->di_tv);
}
else // add a new variable
{
@@ -2725,31 +2861,53 @@ set_var_const(
if (!valid_varname(varname))
return;
- v = alloc(sizeof(dictitem_T) + STRLEN(varname));
- if (v == NULL)
+ di = alloc(sizeof(dictitem_T) + STRLEN(varname));
+ if (di == NULL)
return;
- STRCPY(v->di_key, varname);
- if (hash_add(ht, DI2HIKEY(v)) == FAIL)
+ STRCPY(di->di_key, varname);
+ if (hash_add(ht, DI2HIKEY(di)) == FAIL)
{
- vim_free(v);
+ vim_free(di);
return;
}
- v->di_flags = DI_FLAGS_ALLOC;
- if (is_const)
- v->di_flags |= DI_FLAGS_LOCK;
+ di->di_flags = DI_FLAGS_ALLOC;
+ if (flags & LET_IS_CONST)
+ di->di_flags |= DI_FLAGS_LOCK;
+
+ if (is_script_local && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
+ {
+ scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
+
+ // Store a pointer to the typval_T, so that it can be found by
+ // index instead of using a hastab lookup.
+ if (ga_grow(&si->sn_var_vals, 1) == OK)
+ {
+ svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
+ + si->sn_var_vals.ga_len;
+ sv->sv_name = di->di_key;
+ sv->sv_tv = &di->di_tv;
+ sv->sv_type = type == NULL ? &t_any : type;
+ sv->sv_const = (flags & LET_IS_CONST);
+ sv->sv_export = is_export;
+ ++si->sn_var_vals.ga_len;
+
+ // let ex_export() know the export worked.
+ is_export = FALSE;
+ }
+ }
}
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
- copy_tv(tv, &v->di_tv);
+ copy_tv(tv, &di->di_tv);
else
{
- v->di_tv = *tv;
- v->di_tv.v_lock = 0;
+ di->di_tv = *tv;
+ di->di_tv.v_lock = 0;
init_tv(tv);
}
- if (is_const)
- v->di_tv.v_lock |= VAR_LOCKED;
+ if (flags & LET_IS_CONST)
+ di->di_tv.v_lock |= VAR_LOCKED;
}
/*
@@ -3130,9 +3288,9 @@ var_redir_start(char_u *name, int append)
tv.v_type = VAR_STRING;
tv.vval.v_string = (char_u *)"";
if (append)
- set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)".");
+ set_var_lval(redir_lval, redir_endp, &tv, TRUE, 0, (char_u *)".");
else
- set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"=");
+ set_var_lval(redir_lval, redir_endp, &tv, TRUE, 0, (char_u *)"=");
clear_lval(redir_lval);
err = did_emsg;
did_emsg |= save_emsg;
@@ -3205,7 +3363,7 @@ var_redir_stop(void)
redir_endp = get_lval(redir_varname, NULL, redir_lval,
FALSE, FALSE, 0, FNE_CHECK_START);
if (redir_endp != NULL && redir_lval->ll_name != NULL)
- set_var_lval(redir_lval, redir_endp, &tv, FALSE, FALSE,
+ set_var_lval(redir_lval, redir_endp, &tv, FALSE, 0,
(char_u *)".");
clear_lval(redir_lval);
}