diff options
Diffstat (limited to 'src/evalvars.c')
-rw-r--r-- | src/evalvars.c | 320 |
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), <v, FALSE, is_const, + arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, flags, (char_u *)"]", op); clear_tv(<v); 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); } |