mirror of
https://github.com/neovim/neovim.git
synced 2025-10-07 02:16:31 +00:00
Merge pull request #36035 from janlazo/vim-8.1.1957
vim-patch:8.1.1957,8.2.0200
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
#include "nvim/buffer_defs.h"
|
||||
#include "nvim/decoration.h"
|
||||
#include "nvim/decoration_defs.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/extmark.h"
|
||||
#include "nvim/globals.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)
|
||||
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
|
||||
@@ -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)
|
||||
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)
|
||||
|
@@ -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);
|
||||
}
|
||||
bool type_error = false;
|
||||
if (dict == &vimvardict
|
||||
if (dict == get_vimvar_dict()
|
||||
&& !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) {
|
||||
tv_clear(&tv);
|
||||
if (type_error) {
|
||||
|
@@ -691,13 +691,13 @@ void nvim_del_current_line(Arena *arena, Error *err)
|
||||
Object nvim_get_var(String name, Arena *arena, Error *err)
|
||||
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
|
||||
bool found = script_autoload(name.data, name.size, false) && !aborting();
|
||||
VALIDATE(found, "Key not found: %s", name.data, {
|
||||
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, {
|
||||
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)
|
||||
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.
|
||||
@@ -723,7 +723,7 @@ void nvim_set_var(String name, Object value, Error *err)
|
||||
void nvim_del_var(String name, Error *err)
|
||||
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.
|
||||
@@ -734,7 +734,7 @@ void nvim_del_var(String name, Error *err)
|
||||
Object nvim_get_vvar(String name, Arena *arena, Error *err)
|
||||
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.
|
||||
@@ -745,7 +745,7 @@ Object nvim_get_vvar(String name, Arena *arena, Error *err)
|
||||
void nvim_set_vvar(String name, Object value, Error *err)
|
||||
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".
|
||||
|
@@ -19,8 +19,8 @@
|
||||
#include "nvim/change.h"
|
||||
#include "nvim/drawscreen.h"
|
||||
#include "nvim/errors.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_eval.h"
|
||||
|
@@ -28,8 +28,8 @@
|
||||
#include "nvim/diff.h"
|
||||
#include "nvim/drawscreen.h"
|
||||
#include "nvim/errors.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/ex_cmds.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
|
320
src/nvim/eval.c
320
src/nvim/eval.c
@@ -292,6 +292,8 @@ static struct vimvar {
|
||||
#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;
|
||||
|
||||
@@ -392,11 +394,12 @@ 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(&globvardict, &globvars_var, VAR_DEF_SCOPE);
|
||||
init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
|
||||
vimvardict.dv_lock = VAR_FIXED;
|
||||
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);
|
||||
func_init();
|
||||
|
||||
@@ -504,7 +507,7 @@ static void evalvars_clear(void)
|
||||
hash_clear(&compat_hashtab);
|
||||
|
||||
// global variables
|
||||
vars_clear(&globvarht);
|
||||
vars_clear(get_globvar_ht());
|
||||
|
||||
// Script-local variables. Clear all the variables here.
|
||||
// The scriptvar_T is cleared later in free_scriptnames(), because a
|
||||
@@ -535,200 +538,6 @@ void eval_clear(void)
|
||||
|
||||
#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)
|
||||
{
|
||||
*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);
|
||||
}
|
||||
|
||||
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));
|
||||
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".
|
||||
/// Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc]
|
||||
/// 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) {
|
||||
// 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()) {
|
||||
semsg(_(e_illvar), name);
|
||||
return GLV_FAIL;
|
||||
@@ -2277,18 +2014,6 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
|
||||
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
|
||||
/// with its prefix. Allocated in cat_prefix_varname(), freed later in
|
||||
/// get_user_var_name().
|
||||
@@ -2331,9 +2056,10 @@ char *get_user_var_name(expand_T *xp, int idx)
|
||||
}
|
||||
|
||||
// Global variables
|
||||
if (gdone < globvarht.ht_used) {
|
||||
hashtab_T *globvarht = get_globvar_ht();
|
||||
if (gdone < globvarht->ht_used) {
|
||||
if (gdone++ == 0) {
|
||||
hi = globvarht.ht_array;
|
||||
hi = globvarht->ht_array;
|
||||
} else {
|
||||
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
|
||||
/// 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, "()");
|
||||
int r = NOTDONE;
|
||||
@@ -4803,7 +4529,7 @@ bool garbage_collect(bool testing)
|
||||
}
|
||||
|
||||
// global variables
|
||||
ABORTING(set_ref_in_ht)(&globvarht, copyID, NULL);
|
||||
ABORTING(garbage_collect_globvars)(copyID);
|
||||
|
||||
// function-local variables
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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
|
||||
// loaded already, otherwise it would be loaded every time when
|
||||
// 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
|
||||
// be obtained again or not used.
|
||||
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
|
||||
*d = &globvardict;
|
||||
*d = get_globvar_dict();
|
||||
} else { // l: variable
|
||||
*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;
|
||||
if (*name == 'g') { // global variable
|
||||
*d = &globvardict;
|
||||
*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)) {
|
||||
@@ -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
|
||||
*d = curtab->tp_vars;
|
||||
} else if (*name == 'v') { // v: variable
|
||||
*d = &vimvardict;
|
||||
*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
|
||||
|
@@ -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.
|
||||
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)
|
||||
&& var_wrong_func_name(name, true);
|
||||
}
|
||||
|
@@ -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_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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
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".
|
||||
/// 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);
|
||||
|
||||
@@ -2569,6 +2772,125 @@ bool var_exists(const char *var)
|
||||
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
|
||||
void f_gettabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
{
|
||||
|
@@ -13,6 +13,3 @@
|
||||
|
||||
#define SCRIPT_SV(id) (SCRIPT_ITEM(id)->sn_vars)
|
||||
#define SCRIPT_VARS(id) (SCRIPT_SV(id)->sv_dict.dv_hashtab)
|
||||
|
||||
/// v: hashtab
|
||||
#define vimvarht vimvardict.dv_hashtab
|
||||
|
@@ -3374,7 +3374,7 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
|
||||
} else if (colored_ccline->cmdfirstc == ':') {
|
||||
TRY_WRAP(&err, {
|
||||
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);
|
||||
});
|
||||
can_free_cb = true;
|
||||
|
@@ -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)
|
||||
{
|
||||
TV_DICT_ITER(&globvardict, this_var, {
|
||||
TV_DICT_ITER(get_globvar_dict(), this_var, {
|
||||
if ((this_var->di_tv.v_type == VAR_NUMBER
|
||||
|| this_var->di_tv.v_type == VAR_STRING)
|
||||
&& var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) {
|
||||
|
@@ -178,10 +178,6 @@ EXTERN long emsg_assert_fails_lnum INIT( = 0);
|
||||
EXTERN char *emsg_assert_fails_context INIT( = NULL);
|
||||
|
||||
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
|
||||
// message is displayed or thrown
|
||||
EXTERN bool called_vim_beep; // set if vim_beep() is called
|
||||
|
@@ -327,9 +327,9 @@ static dict_T *nlua_get_var_scope(lua_State *lstate)
|
||||
dict_T *dict = NULL;
|
||||
Error err = ERROR_INIT;
|
||||
if (strequal(scope, "g")) {
|
||||
dict = &globvardict;
|
||||
dict = get_globvar_dict();
|
||||
} else if (strequal(scope, "v")) {
|
||||
dict = &vimvardict;
|
||||
dict = get_vimvar_dict();
|
||||
} else if (strequal(scope, "b")) {
|
||||
buf_T *buf = find_buffer_by_handle(handle, &err);
|
||||
if (buf) {
|
||||
@@ -410,7 +410,7 @@ int nlua_setvar(lua_State *lstate)
|
||||
tv_dict_add(dict, di);
|
||||
} else {
|
||||
bool type_error = false;
|
||||
if (dict == &vimvardict
|
||||
if (dict == get_vimvar_dict()
|
||||
&& !before_set_vvar(key.data, di, &tv, true, watched, &type_error)) {
|
||||
tv_clear(&tv);
|
||||
if (type_error) {
|
||||
@@ -448,7 +448,7 @@ int nlua_getvar(lua_State *lstate)
|
||||
const char *name = luaL_checklstring(lstate, 3, &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()) {
|
||||
return 0; // nil
|
||||
}
|
||||
|
@@ -14,9 +14,9 @@
|
||||
#include "nvim/cmdexpand_defs.h"
|
||||
#include "nvim/cursor.h"
|
||||
#include "nvim/errors.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_docmd.h"
|
||||
#include "nvim/garray.h"
|
||||
|
@@ -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)
|
||||
{
|
||||
const hashitem_T *hi;
|
||||
const hashitem_T *hifirst = globvarht.ht_array;
|
||||
const size_t hinum = (size_t)globvarht.ht_mask + 1;
|
||||
hashtab_T *globvarht = get_globvar_ht();
|
||||
const hashitem_T *hifirst = globvarht->ht_array;
|
||||
const size_t hinum = (size_t)globvarht->ht_mask + 1;
|
||||
*name = NULL;
|
||||
if (iter == NULL) {
|
||||
hi = globvarht.ht_array;
|
||||
hi = globvarht->ht_array;
|
||||
while ((size_t)(hi - hifirst) < hinum
|
||||
&& (HASHITEM_EMPTY(hi)
|
||||
|| !(var_flavour(hi->hi_key) & flavour))) {
|
||||
|
@@ -15,9 +15,9 @@
|
||||
#include "nvim/charset.h"
|
||||
#include "nvim/cursor.h"
|
||||
#include "nvim/errors.h"
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/eval/vars.h"
|
||||
#include "nvim/fileio.h"
|
||||
#include "nvim/garray.h"
|
||||
#include "nvim/garray_defs.h"
|
||||
|
@@ -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);
|
||||
api_clear_error(&err);
|
||||
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);
|
||||
}
|
||||
if (obj.type == kObjectTypeString) {
|
||||
|
Reference in New Issue
Block a user