Merge pull request #36035 from janlazo/vim-8.1.1957

vim-patch:8.1.1957,8.2.0200
This commit is contained in:
zeertzjq
2025-10-06 06:05:43 +08:00
committed by GitHub
17 changed files with 367 additions and 334 deletions

View File

@@ -16,6 +16,7 @@
#include "nvim/buffer_defs.h" #include "nvim/buffer_defs.h"
#include "nvim/decoration.h" #include "nvim/decoration.h"
#include "nvim/decoration_defs.h" #include "nvim/decoration_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/extmark.h" #include "nvim/extmark.h"
#include "nvim/globals.h" #include "nvim/globals.h"
#include "nvim/highlight.h" #include "nvim/highlight.h"
@@ -578,7 +579,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Arena *arena, Error *err)
Object vim_set_var(String name, Object value, Arena *arena, Error *err) Object vim_set_var(String name, Object value, Arena *arena, Error *err)
FUNC_API_DEPRECATED_SINCE(1) FUNC_API_DEPRECATED_SINCE(1)
{ {
return dict_set_var(&globvardict, name, value, false, true, arena, err); return dict_set_var(get_globvar_dict(), name, value, false, true, arena, err);
} }
/// @deprecated /// @deprecated
@@ -586,7 +587,7 @@ Object vim_set_var(String name, Object value, Arena *arena, Error *err)
Object vim_del_var(String name, Arena *arena, Error *err) Object vim_del_var(String name, Arena *arena, Error *err)
FUNC_API_DEPRECATED_SINCE(1) FUNC_API_DEPRECATED_SINCE(1)
{ {
return dict_set_var(&globvardict, name, NIL, true, true, arena, err); return dict_set_var(get_globvar_dict(), name, NIL, true, true, arena, err);
} }
static int64_t convert_index(int64_t index) static int64_t convert_index(int64_t index)

View File

@@ -230,7 +230,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
rv = vim_to_object(&di->di_tv, arena, false); rv = vim_to_object(&di->di_tv, arena, false);
} }
bool type_error = false; bool type_error = false;
if (dict == &vimvardict if (dict == get_vimvar_dict()
&& !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) { && !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) {
tv_clear(&tv); tv_clear(&tv);
if (type_error) { if (type_error) {

View File

@@ -691,13 +691,13 @@ void nvim_del_current_line(Arena *arena, Error *err)
Object nvim_get_var(String name, Arena *arena, Error *err) Object nvim_get_var(String name, Arena *arena, Error *err)
FUNC_API_SINCE(1) FUNC_API_SINCE(1)
{ {
dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size); dictitem_T *di = tv_dict_find(get_globvar_dict(), name.data, (ptrdiff_t)name.size);
if (di == NULL) { // try to autoload script if (di == NULL) { // try to autoload script
bool found = script_autoload(name.data, name.size, false) && !aborting(); bool found = script_autoload(name.data, name.size, false) && !aborting();
VALIDATE(found, "Key not found: %s", name.data, { VALIDATE(found, "Key not found: %s", name.data, {
return (Object)OBJECT_INIT; return (Object)OBJECT_INIT;
}); });
di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size); di = tv_dict_find(get_globvar_dict(), name.data, (ptrdiff_t)name.size);
} }
VALIDATE((di != NULL), "Key not found: %s", name.data, { VALIDATE((di != NULL), "Key not found: %s", name.data, {
return (Object)OBJECT_INIT; return (Object)OBJECT_INIT;
@@ -713,7 +713,7 @@ Object nvim_get_var(String name, Arena *arena, Error *err)
void nvim_set_var(String name, Object value, Error *err) void nvim_set_var(String name, Object value, Error *err)
FUNC_API_SINCE(1) FUNC_API_SINCE(1)
{ {
dict_set_var(&globvardict, name, value, false, false, NULL, err); dict_set_var(get_globvar_dict(), name, value, false, false, NULL, err);
} }
/// Removes a global (g:) variable. /// Removes a global (g:) variable.
@@ -723,7 +723,7 @@ void nvim_set_var(String name, Object value, Error *err)
void nvim_del_var(String name, Error *err) void nvim_del_var(String name, Error *err)
FUNC_API_SINCE(1) FUNC_API_SINCE(1)
{ {
dict_set_var(&globvardict, name, NIL, true, false, NULL, err); dict_set_var(get_globvar_dict(), name, NIL, true, false, NULL, err);
} }
/// Gets a v: variable. /// Gets a v: variable.
@@ -734,7 +734,7 @@ void nvim_del_var(String name, Error *err)
Object nvim_get_vvar(String name, Arena *arena, Error *err) Object nvim_get_vvar(String name, Arena *arena, Error *err)
FUNC_API_SINCE(1) FUNC_API_SINCE(1)
{ {
return dict_get_value(&vimvardict, name, arena, err); return dict_get_value(get_vimvar_dict(), name, arena, err);
} }
/// Sets a v: variable, if it is not readonly. /// Sets a v: variable, if it is not readonly.
@@ -745,7 +745,7 @@ Object nvim_get_vvar(String name, Arena *arena, Error *err)
void nvim_set_vvar(String name, Object value, Error *err) void nvim_set_vvar(String name, Object value, Error *err)
FUNC_API_SINCE(6) FUNC_API_SINCE(6)
{ {
dict_set_var(&vimvardict, name, value, false, false, NULL, err); dict_set_var(get_vimvar_dict(), name, value, false, false, NULL, err);
} }
/// Prints a message given by a list of `[text, hl_group]` "chunks". /// Prints a message given by a list of `[text, hl_group]` "chunks".

View File

@@ -19,8 +19,8 @@
#include "nvim/change.h" #include "nvim/change.h"
#include "nvim/drawscreen.h" #include "nvim/drawscreen.h"
#include "nvim/errors.h" #include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h" #include "nvim/ex_cmds.h"
#include "nvim/ex_cmds_defs.h" #include "nvim/ex_cmds_defs.h"
#include "nvim/ex_eval.h" #include "nvim/ex_eval.h"

View File

@@ -28,8 +28,8 @@
#include "nvim/diff.h" #include "nvim/diff.h"
#include "nvim/drawscreen.h" #include "nvim/drawscreen.h"
#include "nvim/errors.h" #include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h" #include "nvim/ex_cmds.h"
#include "nvim/ex_cmds_defs.h" #include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h" #include "nvim/ex_docmd.h"

View File

@@ -292,6 +292,8 @@ static struct vimvar {
#define vv_partial vv_di.di_tv.vval.v_partial #define vv_partial vv_di.di_tv.vval.v_partial
#define vv_tv vv_di.di_tv #define vv_tv vv_di.di_tv
#define vimvarht get_vimvar_dict()->dv_hashtab
/// Variable used for v: /// Variable used for v:
static ScopeDictDictItem vimvars_var; static ScopeDictDictItem vimvars_var;
@@ -392,11 +394,12 @@ varnumber_T num_modulus(varnumber_T n1, varnumber_T n2)
/// Initialize the global and v: variables. /// Initialize the global and v: variables.
void eval_init(void) void eval_init(void)
{ {
dict_T *vimvardict = get_vimvar_dict();
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); init_var_dict(get_globvar_dict(), &globvars_var, VAR_DEF_SCOPE);
init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE); init_var_dict(vimvardict, &vimvars_var, VAR_SCOPE);
vimvardict.dv_lock = VAR_FIXED; vimvardict->dv_lock = VAR_FIXED;
hash_init(&compat_hashtab); hash_init(&compat_hashtab);
func_init(); func_init();
@@ -504,7 +507,7 @@ static void evalvars_clear(void)
hash_clear(&compat_hashtab); hash_clear(&compat_hashtab);
// global variables // global variables
vars_clear(&globvarht); vars_clear(get_globvar_ht());
// Script-local variables. Clear all the variables here. // Script-local variables. Clear all the variables here.
// The scriptvar_T is cleared later in free_scriptnames(), because a // The scriptvar_T is cleared later in free_scriptnames(), because a
@@ -535,200 +538,6 @@ void eval_clear(void)
#endif #endif
static lval_T *redir_lval = NULL;
static garray_T redir_ga; // Only valid when redir_lval is not NULL.
static char *redir_endp = NULL;
static char *redir_varname = NULL;
/// Start recording command output to a variable
///
/// @param append append to an existing variable
///
/// @return OK if successfully completed the setup. FAIL otherwise.
int var_redir_start(char *name, bool append)
{
// Catch a bad name early.
if (!eval_isnamec1(*name)) {
emsg(_(e_invarg));
return FAIL;
}
// Make a copy of the name, it is used in redir_lval until redir ends.
redir_varname = xstrdup(name);
redir_lval = xcalloc(1, sizeof(lval_T));
// The output is stored in growarray "redir_ga" until redirection ends.
ga_init(&redir_ga, (int)sizeof(char), 500);
// Parse the variable name (can be a dict or list entry).
redir_endp = get_lval(redir_varname, NULL, redir_lval, false, false,
0, FNE_CHECK_START);
if (redir_endp == NULL || redir_lval->ll_name == NULL
|| *redir_endp != NUL) {
clear_lval(redir_lval);
if (redir_endp != NULL && *redir_endp != NUL) {
// Trailing characters are present after the variable name
semsg(_(e_trailing_arg), redir_endp);
} else {
semsg(_(e_invarg2), name);
}
redir_endp = NULL; // don't store a value, only cleanup
var_redir_stop();
return FAIL;
}
// check if we can write to the variable: set it to or append an empty
// string
const int called_emsg_before = called_emsg;
did_emsg = false;
typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = "";
if (append) {
set_var_lval(redir_lval, redir_endp, &tv, true, false, ".");
} else {
set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
}
clear_lval(redir_lval);
if (called_emsg > called_emsg_before) {
redir_endp = NULL; // don't store a value, only cleanup
var_redir_stop();
return FAIL;
}
return OK;
}
/// Append "value[value_len]" to the variable set by var_redir_start().
/// The actual appending is postponed until redirection ends, because the value
/// appended may in fact be the string we write to, changing it may cause freed
/// memory to be used:
/// :redir => foo
/// :let foo
/// :redir END
void var_redir_str(const char *value, int value_len)
{
if (redir_lval == NULL) {
return;
}
int len;
if (value_len == -1) {
len = (int)strlen(value); // Append the entire string
} else {
len = value_len; // Append only "value_len" characters
}
ga_grow(&redir_ga, len);
memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, (size_t)len);
redir_ga.ga_len += len;
}
/// Stop redirecting command output to a variable.
/// Frees the allocated memory.
void var_redir_stop(void)
{
if (redir_lval != NULL) {
// If there was no error: assign the text to the variable.
if (redir_endp != NULL) {
ga_append(&redir_ga, NUL); // Append the trailing NUL.
typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = redir_ga.ga_data;
// Call get_lval() again, if it's inside a Dict or List it may
// have changed.
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, ".");
}
clear_lval(redir_lval);
}
// free the collected output
XFREE_CLEAR(redir_ga.ga_data);
XFREE_CLEAR(redir_lval);
}
XFREE_CLEAR(redir_varname);
}
int eval_charconvert(const char *const enc_from, const char *const enc_to,
const char *const fname_from, const char *const fname_to)
{
const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_CC_FROM, enc_from, -1);
set_vim_var_string(VV_CC_TO, enc_to, -1);
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
sctx_T *ctx = get_option_sctx(kOptCharconvert);
if (ctx != NULL) {
current_sctx = *ctx;
}
bool err = false;
if (eval_to_bool(p_ccv, &err, NULL, false, true)) {
err = true;
}
set_vim_var_string(VV_CC_FROM, NULL, -1);
set_vim_var_string(VV_CC_TO, NULL, -1);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
current_sctx = saved_sctx;
if (err) {
return FAIL;
}
return OK;
}
void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile)
{
const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
sctx_T *ctx = get_option_sctx(kOptDiffexpr);
if (ctx != NULL) {
current_sctx = *ctx;
}
// errors are ignored
typval_T *tv = eval_expr_ext(p_dex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_NEW, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
current_sctx = saved_sctx;
}
void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile)
{
const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_DIFF, difffile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
sctx_T *ctx = get_option_sctx(kOptPatchexpr);
if (ctx != NULL) {
current_sctx = *ctx;
}
// errors are ignored
typval_T *tv = eval_expr_ext(p_pex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_DIFF, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
current_sctx = saved_sctx;
}
void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip) void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
{ {
*evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE }; *evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE };
@@ -1098,7 +907,7 @@ typval_T *eval_expr(char *arg, exarg_T *eap)
return eval_expr_ext(arg, eap, false); return eval_expr_ext(arg, eap, false);
} }
static typval_T *eval_expr_ext(char *arg, exarg_T *eap, const bool use_simple_function) typval_T *eval_expr_ext(char *arg, exarg_T *eap, const bool use_simple_function)
{ {
typval_T *tv = xmalloc(sizeof(*tv)); typval_T *tv = xmalloc(sizeof(*tv));
evalarg_T evalarg; evalarg_T evalarg;
@@ -1157,78 +966,6 @@ void restore_vimvar(int idx, typval_T *save_tv)
} }
} }
/// Evaluate an expression to a list with suggestions.
/// For the "expr:" part of 'spellsuggest'.
///
/// @return NULL when there is an error.
list_T *eval_spell_expr(char *badword, char *expr)
{
typval_T save_val;
typval_T rettv;
list_T *list = NULL;
char *p = skipwhite(expr);
const sctx_T saved_sctx = current_sctx;
// Set "v:val" to the bad word.
prepare_vimvar(VV_VAL, &save_val);
set_vim_var_string(VV_VAL, badword, -1);
if (p_verbose == 0) {
emsg_off++;
}
sctx_T *ctx = get_option_sctx(kOptSpellsuggest);
if (ctx != NULL) {
current_sctx = *ctx;
}
int r = may_call_simple_func(p, &rettv);
if (r == NOTDONE) {
r = eval1(&p, &rettv, &EVALARG_EVALUATE);
}
if (r == OK) {
if (rettv.v_type != VAR_LIST) {
tv_clear(&rettv);
} else {
list = rettv.vval.v_list;
}
}
if (p_verbose == 0) {
emsg_off--;
}
tv_clear(get_vim_var_tv(VV_VAL));
restore_vimvar(VV_VAL, &save_val);
current_sctx = saved_sctx;
return list;
}
/// Get spell word from an entry from spellsuggest=expr:
///
/// Entry in question is supposed to be a list (to be checked by the caller)
/// with two items: a word and a score represented as an unsigned number
/// (whether it actually is unsigned is not checked).
///
/// Used to get the good word and score from the eval_spell_expr() result.
///
/// @param[in] list List to get values from.
/// @param[out] ret_word Suggested word. Not initialized if return value is
/// -1.
///
/// @return -1 in case of error, score otherwise.
int get_spellword(list_T *const list, const char **ret_word)
{
if (tv_list_len(list) != 2) {
emsg(_("E5700: Expression from 'spellsuggest' must yield lists with "
"exactly two values"));
return -1;
}
*ret_word = tv_list_find_str(list, 0);
if (*ret_word == NULL) {
return -1;
}
return (int)tv_list_find_nr(list, -1, NULL);
}
/// Call some Vim script function and return the result in "*rettv". /// Call some Vim script function and return the result in "*rettv".
/// Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] /// Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc]
/// should have type VAR_UNKNOWN. /// should have type VAR_UNKNOWN.
@@ -1499,7 +1236,7 @@ static glv_status_T get_lval_dict_item(lval_T *lp, char *name, char *key, int le
if (lp->ll_di == NULL) { if (lp->ll_di == NULL) {
// Can't add "v:" or "a:" variable. // Can't add "v:" or "a:" variable.
if (lp->ll_dict == &vimvardict if (lp->ll_dict == get_vimvar_dict()
|| &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) {
semsg(_(e_illvar), name); semsg(_(e_illvar), name);
return GLV_FAIL; return GLV_FAIL;
@@ -2277,18 +2014,6 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
xp->xp_pattern = arg; xp->xp_pattern = arg;
} }
/// Delete all "menutrans_" variables.
void del_menutrans_vars(void)
{
hash_lock(&globvarht);
HASHTAB_ITER(&globvarht, hi, {
if (strncmp(hi->hi_key, "menutrans_", 10) == 0) {
delete_var(&globvarht, hi);
}
});
hash_unlock(&globvarht);
}
/// Local string buffer for the next two functions to store a variable name /// Local string buffer for the next two functions to store a variable name
/// with its prefix. Allocated in cat_prefix_varname(), freed later in /// with its prefix. Allocated in cat_prefix_varname(), freed later in
/// get_user_var_name(). /// get_user_var_name().
@@ -2331,9 +2056,10 @@ char *get_user_var_name(expand_T *xp, int idx)
} }
// Global variables // Global variables
if (gdone < globvarht.ht_used) { hashtab_T *globvarht = get_globvar_ht();
if (gdone < globvarht->ht_used) {
if (gdone++ == 0) { if (gdone++ == 0) {
hi = globvarht.ht_array; hi = globvarht->ht_array;
} else { } else {
hi++; hi++;
} }
@@ -2566,7 +2292,7 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
/// If "arg" is a simple function call without arguments then call it and return /// If "arg" is a simple function call without arguments then call it and return
/// the result. Otherwise return NOTDONE. /// the result. Otherwise return NOTDONE.
static int may_call_simple_func(const char *arg, typval_T *rettv) int may_call_simple_func(const char *arg, typval_T *rettv)
{ {
const char *parens = strstr(arg, "()"); const char *parens = strstr(arg, "()");
int r = NOTDONE; int r = NOTDONE;
@@ -4803,7 +4529,7 @@ bool garbage_collect(bool testing)
} }
// global variables // global variables
ABORTING(set_ref_in_ht)(&globvarht, copyID, NULL); ABORTING(garbage_collect_globvars)(copyID);
// function-local variables // function-local variables
ABORTING(set_ref_in_call_stack)(copyID); ABORTING(set_ref_in_call_stack)(copyID);
@@ -6871,16 +6597,6 @@ typval_T *get_vim_var_tv(const VimVarIndex idx)
return &vimvars[idx].vv_tv; return &vimvars[idx].vv_tv;
} }
/// Set v:variable to tv.
///
/// @param[in] idx Index of variable to set.
/// @param[in] val Value to set to. Will be copied.
void set_vim_var_tv(const VimVarIndex idx, typval_T *const tv)
{
tv_clear(&vimvars[idx].vv_di.di_tv);
tv_copy(tv, &vimvars[idx].vv_di.di_tv);
}
/// Set the v:argv list. /// Set the v:argv list.
void set_argv_var(char **argv, int argc) void set_argv_var(char **argv, int argc)
{ {
@@ -7178,7 +6894,7 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
// worked find the variable again. Don't auto-load a script if it was // worked find the variable again. Don't auto-load a script if it was
// loaded already, otherwise it would be loaded every time when // loaded already, otherwise it would be loaded every time when
// checking if a function name is a Funcref variable. // checking if a function name is a Funcref variable.
if (ht == &globvarht && !no_autoload) { if (ht == get_globvar_ht() && !no_autoload) {
// Note: script_autoload() may make "hi" invalid. It must either // Note: script_autoload() may make "hi" invalid. It must either
// be obtained again or not used. // be obtained again or not used.
if (!script_autoload(varname, varname_len, false) || aborting()) { if (!script_autoload(varname, varname_len, false) || aborting()) {
@@ -7228,7 +6944,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
} }
if (funccal == NULL) { // global variable if (funccal == NULL) { // global variable
*d = &globvardict; *d = get_globvar_dict();
} else { // l: variable } else { // l: variable
*d = &funccal->fc_l_vars; *d = &funccal->fc_l_vars;
} }
@@ -7237,7 +6953,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
*varname = name + 2; *varname = name + 2;
if (*name == 'g') { // global variable if (*name == 'g') { // global variable
*d = &globvardict; *d = get_globvar_dict();
} else if (name_len > 2 } else if (name_len > 2
&& (memchr(name + 2, ':', name_len - 2) != NULL && (memchr(name + 2, ':', name_len - 2) != NULL
|| memchr(name + 2, AUTOLOAD_CHAR, name_len - 2) != NULL)) { || memchr(name + 2, AUTOLOAD_CHAR, name_len - 2) != NULL)) {
@@ -7252,7 +6968,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
} else if (*name == 't') { // tab page variable } else if (*name == 't') { // tab page variable
*d = curtab->tp_vars; *d = curtab->tp_vars;
} else if (*name == 'v') { // v: variable } else if (*name == 'v') { // v: variable
*d = &vimvardict; *d = get_vimvar_dict();
} else if (*name == 'a' && funccal != NULL) { // function argument } else if (*name == 'a' && funccal != NULL) { // function argument
*d = &funccal->fc_l_avars; *d = &funccal->fc_l_avars;
} else if (*name == 'l' && funccal != NULL) { // local variable } else if (*name == 'l' && funccal != NULL) { // local variable

View File

@@ -2427,7 +2427,7 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_
/// If the name is wrong give an error message and return true. /// If the name is wrong give an error message and return true.
int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name) int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name)
{ {
return (d == &globvardict || &d->dv_hashtab == get_funccal_local_ht()) return (d == get_globvar_dict() || &d->dv_hashtab == get_funccal_local_ht())
&& tv_is_func(*tv) && tv_is_func(*tv)
&& var_wrong_func_name(name, true); && var_wrong_func_name(name, true);
} }

View File

@@ -65,6 +65,19 @@ 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_missing_end_marker_str[] = N_("E990: Missing end marker '%s'");
static const char e_cannot_use_heredoc_here[] = N_("E991: Cannot use =<< here"); static const char e_cannot_use_heredoc_here[] = N_("E991: Cannot use =<< here");
static dict_T globvardict; // Dict with g: variables
/// g: value
#define globvarht globvardict.dv_hashtab
static dict_T vimvardict; // Dict with v: variables
/// v: hashtab
#define vimvarht vimvardict.dv_hashtab
int garbage_collect_globvars(int copyID)
{
return set_ref_in_ht(&globvarht, copyID, NULL);
}
bool garbage_collect_vimvars(int copyID) bool garbage_collect_vimvars(int copyID)
{ {
return set_ref_in_ht(&vimvarht, copyID, NULL); return set_ref_in_ht(&vimvarht, copyID, NULL);
@@ -94,6 +107,153 @@ void set_internal_string_var(const char *name, char *value) // NOLINT(readabili
set_var(name, strlen(name), &tv, true); set_var(name, strlen(name), &tv, true);
} }
int eval_charconvert(const char *const enc_from, const char *const enc_to,
const char *const fname_from, const char *const fname_to)
{
const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_CC_FROM, enc_from, -1);
set_vim_var_string(VV_CC_TO, enc_to, -1);
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
sctx_T *ctx = get_option_sctx(kOptCharconvert);
if (ctx != NULL) {
current_sctx = *ctx;
}
bool err = false;
if (eval_to_bool(p_ccv, &err, NULL, false, true)) {
err = true;
}
set_vim_var_string(VV_CC_FROM, NULL, -1);
set_vim_var_string(VV_CC_TO, NULL, -1);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
current_sctx = saved_sctx;
if (err) {
return FAIL;
}
return OK;
}
void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile)
{
const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
sctx_T *ctx = get_option_sctx(kOptDiffexpr);
if (ctx != NULL) {
current_sctx = *ctx;
}
// errors are ignored
typval_T *tv = eval_expr_ext(p_dex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_NEW, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
current_sctx = saved_sctx;
}
void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile)
{
const sctx_T saved_sctx = current_sctx;
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_DIFF, difffile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
sctx_T *ctx = get_option_sctx(kOptPatchexpr);
if (ctx != NULL) {
current_sctx = *ctx;
}
// errors are ignored
typval_T *tv = eval_expr_ext(p_pex, NULL, true);
tv_free(tv);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_DIFF, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
current_sctx = saved_sctx;
}
/// Evaluate an expression to a list with suggestions.
/// For the "expr:" part of 'spellsuggest'.
///
/// @return NULL when there is an error.
list_T *eval_spell_expr(char *badword, char *expr)
{
typval_T save_val;
typval_T rettv;
list_T *list = NULL;
char *p = skipwhite(expr);
const sctx_T saved_sctx = current_sctx;
// Set "v:val" to the bad word.
prepare_vimvar(VV_VAL, &save_val);
set_vim_var_string(VV_VAL, badword, -1);
if (p_verbose == 0) {
emsg_off++;
}
sctx_T *ctx = get_option_sctx(kOptSpellsuggest);
if (ctx != NULL) {
current_sctx = *ctx;
}
int r = may_call_simple_func(p, &rettv);
if (r == NOTDONE) {
r = eval1(&p, &rettv, &EVALARG_EVALUATE);
}
if (r == OK) {
if (rettv.v_type != VAR_LIST) {
tv_clear(&rettv);
} else {
list = rettv.vval.v_list;
}
}
if (p_verbose == 0) {
emsg_off--;
}
tv_clear(get_vim_var_tv(VV_VAL));
restore_vimvar(VV_VAL, &save_val);
current_sctx = saved_sctx;
return list;
}
/// Get spell word from an entry from spellsuggest=expr:
///
/// Entry in question is supposed to be a list (to be checked by the caller)
/// with two items: a word and a score represented as an unsigned number
/// (whether it actually is unsigned is not checked).
///
/// Used to get the good word and score from the eval_spell_expr() result.
///
/// @param[in] list List to get values from.
/// @param[out] ret_word Suggested word. Not initialized if return value is
/// -1.
///
/// @return -1 in case of error, score otherwise.
int get_spellword(list_T *const list, const char **ret_word)
{
if (tv_list_len(list) != 2) {
emsg(_("E5700: Expression from 'spellsuggest' must yield lists with "
"exactly two values"));
return -1;
}
*ret_word = tv_list_find_str(list, 0);
if (*ret_word == NULL) {
return -1;
}
return (int)tv_list_find_nr(list, -1, NULL);
}
/// List Vim variables. /// List Vim variables.
static void list_vim_vars(int *first) static void list_vim_vars(int *first)
{ {
@@ -1335,6 +1495,49 @@ static int do_lock_var(lval_T *lp, char *name_end FUNC_ATTR_UNUSED, exarg_T *eap
return ret; return ret;
} }
/// Delete all "menutrans_" variables.
void del_menutrans_vars(void)
{
hash_lock(&globvarht);
HASHTAB_ITER(&globvarht, hi, {
if (strncmp(hi->hi_key, "menutrans_", 10) == 0) {
delete_var(&globvarht, hi);
}
});
hash_unlock(&globvarht);
}
/// @return global variable dictionary
dict_T *get_globvar_dict(void)
FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
{
return &globvardict;
}
/// @return global variable hash table
hashtab_T *get_globvar_ht(void)
{
return &globvarht;
}
/// @return v: variable dictionary
dict_T *get_vimvar_dict(void)
FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET
{
return &vimvardict;
}
/// Set v:variable to tv.
///
/// @param[in] idx Index of variable to set.
/// @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);
}
/// Get number v: variable value. /// Get number v: variable value.
varnumber_T get_vim_var_nr(const VimVarIndex idx) FUNC_ATTR_PURE varnumber_T get_vim_var_nr(const VimVarIndex idx) FUNC_ATTR_PURE
{ {
@@ -1839,7 +2042,7 @@ void vars_clear_ext(hashtab_T *ht, bool free_val)
/// Delete a variable from hashtab "ht" at item "hi". /// Delete a variable from hashtab "ht" at item "hi".
/// Clear the variable value and free the dictitem. /// Clear the variable value and free the dictitem.
void delete_var(hashtab_T *ht, hashitem_T *hi) static void delete_var(hashtab_T *ht, hashitem_T *hi)
{ {
dictitem_T *di = TV_DICT_HI2DI(hi); dictitem_T *di = TV_DICT_HI2DI(hi);
@@ -2569,6 +2772,125 @@ bool var_exists(const char *var)
return n; return n;
} }
static lval_T *redir_lval = NULL;
static garray_T redir_ga; // Only valid when redir_lval is not NULL.
static char *redir_endp = NULL;
static char *redir_varname = NULL;
/// Start recording command output to a variable
///
/// @param append append to an existing variable
///
/// @return OK if successfully completed the setup. FAIL otherwise.
int var_redir_start(char *name, bool append)
{
// Catch a bad name early.
if (!eval_isnamec1(*name)) {
emsg(_(e_invarg));
return FAIL;
}
// Make a copy of the name, it is used in redir_lval until redir ends.
redir_varname = xstrdup(name);
redir_lval = xcalloc(1, sizeof(lval_T));
// The output is stored in growarray "redir_ga" until redirection ends.
ga_init(&redir_ga, (int)sizeof(char), 500);
// Parse the variable name (can be a dict or list entry).
redir_endp = get_lval(redir_varname, NULL, redir_lval, false, false,
0, FNE_CHECK_START);
if (redir_endp == NULL || redir_lval->ll_name == NULL
|| *redir_endp != NUL) {
clear_lval(redir_lval);
if (redir_endp != NULL && *redir_endp != NUL) {
// Trailing characters are present after the variable name
semsg(_(e_trailing_arg), redir_endp);
} else {
semsg(_(e_invarg2), name);
}
redir_endp = NULL; // don't store a value, only cleanup
var_redir_stop();
return FAIL;
}
// check if we can write to the variable: set it to or append an empty
// string
const int called_emsg_before = called_emsg;
did_emsg = false;
typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = "";
if (append) {
set_var_lval(redir_lval, redir_endp, &tv, true, false, ".");
} else {
set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
}
clear_lval(redir_lval);
if (called_emsg > called_emsg_before) {
redir_endp = NULL; // don't store a value, only cleanup
var_redir_stop();
return FAIL;
}
return OK;
}
/// Append "value[value_len]" to the variable set by var_redir_start().
/// The actual appending is postponed until redirection ends, because the value
/// appended may in fact be the string we write to, changing it may cause freed
/// memory to be used:
/// :redir => foo
/// :let foo
/// :redir END
void var_redir_str(const char *value, int value_len)
{
if (redir_lval == NULL) {
return;
}
int len;
if (value_len == -1) {
len = (int)strlen(value); // Append the entire string
} else {
len = value_len; // Append only "value_len" characters
}
ga_grow(&redir_ga, len);
memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, (size_t)len);
redir_ga.ga_len += len;
}
/// Stop redirecting command output to a variable.
/// Frees the allocated memory.
void var_redir_stop(void)
{
if (redir_lval != NULL) {
// If there was no error: assign the text to the variable.
if (redir_endp != NULL) {
ga_append(&redir_ga, NUL); // Append the trailing NUL.
typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = redir_ga.ga_data;
// Call get_lval() again, if it's inside a Dict or List it may
// have changed.
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, ".");
}
clear_lval(redir_lval);
}
// free the collected output
XFREE_CLEAR(redir_ga.ga_data);
XFREE_CLEAR(redir_lval);
}
XFREE_CLEAR(redir_varname);
}
/// "gettabvar()" function /// "gettabvar()" function
void f_gettabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) void f_gettabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{ {

View File

@@ -13,6 +13,3 @@
#define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars) #define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars)
#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab) #define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
/// v: hashtab
#define vimvarht vimvardict.dv_hashtab

View File

@@ -3374,7 +3374,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
} else if (colored_ccline->cmdfirstc == ':') { } else if (colored_ccline->cmdfirstc == ':') {
TRY_WRAP(&err, { TRY_WRAP(&err, {
err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s"); err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s");
dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), dgc_ret = tv_dict_get_callback(get_globvar_dict(), S_LEN("Nvim_color_cmdline"),
&color_cb); &color_cb);
}); });
can_free_cb = true; can_free_cb = true;

View File

@@ -533,7 +533,7 @@ static int put_view(FILE *fd, win_T *wp, tabpage_T *tp, bool add_edit, unsigned
static int store_session_globals(FILE *fd) static int store_session_globals(FILE *fd)
{ {
TV_DICT_ITER(&globvardict, this_var, { TV_DICT_ITER(get_globvar_dict(), this_var, {
if ((this_var->di_tv.v_type == VAR_NUMBER if ((this_var->di_tv.v_type == VAR_NUMBER
|| this_var->di_tv.v_type == VAR_STRING) || this_var->di_tv.v_type == VAR_STRING)
&& var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {

View File

@@ -178,10 +178,6 @@ EXTERN long emsg_assert_fails_lnum INIT( = 0);
EXTERN char *emsg_assert_fails_context INIT( = NULL); EXTERN char *emsg_assert_fails_context INIT( = NULL);
EXTERN bool did_endif INIT( = false); // just had ":endif" EXTERN bool did_endif INIT( = false); // just had ":endif"
EXTERN dict_T vimvardict; // Dict with v: variables
EXTERN dict_T globvardict; // Dict with g: variables
/// g: value
#define globvarht globvardict.dv_hashtab
EXTERN int did_emsg; // incremented by emsg() when a EXTERN int did_emsg; // incremented by emsg() when a
// message is displayed or thrown // message is displayed or thrown
EXTERN bool called_vim_beep; // set if vim_beep() is called EXTERN bool called_vim_beep; // set if vim_beep() is called

View File

@@ -327,9 +327,9 @@ static dict_T *nlua_get_var_scope(lua_State *lstate)
dict_T *dict = NULL; dict_T *dict = NULL;
Error err = ERROR_INIT; Error err = ERROR_INIT;
if (strequal(scope, "g")) { if (strequal(scope, "g")) {
dict = &globvardict; dict = get_globvar_dict();
} else if (strequal(scope, "v")) { } else if (strequal(scope, "v")) {
dict = &vimvardict; dict = get_vimvar_dict();
} else if (strequal(scope, "b")) { } else if (strequal(scope, "b")) {
buf_T *buf = find_buffer_by_handle(handle, &err); buf_T *buf = find_buffer_by_handle(handle, &err);
if (buf) { if (buf) {
@@ -410,7 +410,7 @@ int nlua_setvar(lua_State *lstate)
tv_dict_add(dict, di); tv_dict_add(dict, di);
} else { } else {
bool type_error = false; bool type_error = false;
if (dict == &vimvardict if (dict == get_vimvar_dict()
&& !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) { && !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) {
tv_clear(&tv); tv_clear(&tv);
if (type_error) { if (type_error) {
@@ -448,7 +448,7 @@ int nlua_getvar(lua_State *lstate)
const char *name = luaL_checklstring(lstate, 3, &len); const char *name = luaL_checklstring(lstate, 3, &len);
dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len); dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len);
if (di == NULL && dict == &globvardict) { // try to autoload script if (di == NULL && dict == get_globvar_dict()) { // try to autoload script
if (!script_autoload(name, len, false) || aborting()) { if (!script_autoload(name, len, false) || aborting()) {
return 0; // nil return 0; // nil
} }

View File

@@ -14,9 +14,9 @@
#include "nvim/cmdexpand_defs.h" #include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h" #include "nvim/cursor.h"
#include "nvim/errors.h" #include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds_defs.h" #include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h" #include "nvim/ex_docmd.h"
#include "nvim/garray.h" #include "nvim/garray.h"

View File

@@ -803,11 +803,12 @@ static const void *var_shada_iter(const void *const iter, const char **const nam
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2, 3)
{ {
const hashitem_T *hi; const hashitem_T *hi;
const hashitem_T *hifirst = globvarht.ht_array; hashtab_T *globvarht = get_globvar_ht();
const size_t hinum = (size_t)globvarht.ht_mask + 1; const hashitem_T *hifirst = globvarht->ht_array;
const size_t hinum = (size_t)globvarht->ht_mask + 1;
*name = NULL; *name = NULL;
if (iter == NULL) { if (iter == NULL) {
hi = globvarht.ht_array; hi = globvarht->ht_array;
while ((size_t)(hi - hifirst) < hinum while ((size_t)(hi - hifirst) < hinum
&& (HASHITEM_EMPTY(hi) && (HASHITEM_EMPTY(hi)
|| !(var_flavour(hi->hi_key) & flavour))) { || !(var_flavour(hi->hi_key) & flavour))) {

View File

@@ -15,9 +15,9 @@
#include "nvim/charset.h" #include "nvim/charset.h"
#include "nvim/cursor.h" #include "nvim/cursor.h"
#include "nvim/errors.h" #include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/fileio.h" #include "nvim/fileio.h"
#include "nvim/garray.h" #include "nvim/garray.h"
#include "nvim/garray_defs.h" #include "nvim/garray_defs.h"

View File

@@ -2358,7 +2358,7 @@ static char *get_config_string(char *key)
Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), NULL, &err); Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), NULL, &err);
api_clear_error(&err); api_clear_error(&err);
if (obj.type == kObjectTypeNil) { if (obj.type == kObjectTypeNil) {
obj = dict_get_value(&globvardict, cstr_as_string(key), NULL, &err); obj = dict_get_value(get_globvar_dict(), cstr_as_string(key), NULL, &err);
api_clear_error(&err); api_clear_error(&err);
} }
if (obj.type == kObjectTypeString) { if (obj.type == kObjectTypeString) {