mirror of
https://github.com/neovim/neovim.git
synced 2025-09-14 23:38:17 +00:00
eval: Add special variable type
This commit is contained in:
@@ -397,13 +397,13 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
|
||||
|
||||
switch (obj.type) {
|
||||
case kObjectTypeNil:
|
||||
tv->v_type = VAR_NUMBER;
|
||||
tv->vval.v_number = 0;
|
||||
tv->v_type = VAR_SPECIAL;
|
||||
tv->vval.v_special = kSpecialVarNull;
|
||||
break;
|
||||
|
||||
case kObjectTypeBoolean:
|
||||
tv->v_type = VAR_NUMBER;
|
||||
tv->vval.v_number = obj.data.boolean;
|
||||
tv->v_type = VAR_SPECIAL;
|
||||
tv->vval.v_special = obj.data.boolean? kSpecialVarTrue: kSpecialVarFalse;
|
||||
break;
|
||||
|
||||
case kObjectTypeBuffer:
|
||||
|
@@ -343,6 +343,24 @@ static int name##_convert_one_value(firstargtype firstargname, \
|
||||
})); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_SPECIAL: { \
|
||||
switch (tv->vval.v_special) { \
|
||||
case kSpecialVarNull: { \
|
||||
CONV_NIL(); \
|
||||
break; \
|
||||
} \
|
||||
case kSpecialVarTrue: \
|
||||
case kSpecialVarFalse: { \
|
||||
CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \
|
||||
break; \
|
||||
} \
|
||||
case kSpecialVarNone: { \
|
||||
CONV_NONE(); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case VAR_DICT: { \
|
||||
if (tv->vval.v_dict == NULL \
|
||||
|| tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
|
||||
@@ -369,14 +387,14 @@ static int name##_convert_one_value(firstargtype firstargname, \
|
||||
} \
|
||||
switch ((MessagePackType) i) { \
|
||||
case kMPNil: { \
|
||||
CONV_SPECIAL_NIL(); \
|
||||
CONV_NIL(); \
|
||||
break; \
|
||||
} \
|
||||
case kMPBoolean: { \
|
||||
if (val_di->di_tv.v_type != VAR_NUMBER) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
CONV_SPECIAL_BOOL(val_di->di_tv.vval.v_number); \
|
||||
CONV_BOOL(val_di->di_tv.vval.v_number); \
|
||||
break; \
|
||||
} \
|
||||
case kMPInteger: { \
|
||||
@@ -698,9 +716,14 @@ encode_vim_to_##name##_error_ret: \
|
||||
#define CONV_EMPTY_DICT() \
|
||||
ga_concat(gap, "{}")
|
||||
|
||||
#define CONV_SPECIAL_NIL()
|
||||
#define CONV_NIL() \
|
||||
ga_append(gap, "v:null")
|
||||
|
||||
#define CONV_SPECIAL_BOOL(num)
|
||||
#define CONV_BOOL(num) \
|
||||
ga_append(gap, ((num)? "v:true": "v:false"))
|
||||
|
||||
#define CONV_NONE() \
|
||||
ga_append(gap, "v:none")
|
||||
|
||||
#define CONV_UNSIGNED_NUMBER(num)
|
||||
|
||||
@@ -804,12 +827,12 @@ DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
||||
#undef CONV_ALLOW_SPECIAL
|
||||
#define CONV_ALLOW_SPECIAL true
|
||||
|
||||
#undef CONV_SPECIAL_NIL
|
||||
#define CONV_SPECIAL_NIL() \
|
||||
#undef CONV_NIL
|
||||
#define CONV_NIL() \
|
||||
ga_concat(gap, "null")
|
||||
|
||||
#undef CONV_SPECIAL_BOOL
|
||||
#define CONV_SPECIAL_BOOL(num) \
|
||||
#undef CONV_BOOL
|
||||
#define CONV_BOOL(num) \
|
||||
ga_concat(gap, ((num)? "true": "false"))
|
||||
|
||||
#undef CONV_UNSIGNED_NUMBER
|
||||
@@ -1046,6 +1069,9 @@ static inline bool check_json_key(const typval_T *const tv)
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#undef CONV_NONE
|
||||
#define CONV_NONE()
|
||||
|
||||
DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
||||
|
||||
#undef CONV_STRING
|
||||
@@ -1057,8 +1083,9 @@ DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
||||
#undef CONV_EMPTY_LIST
|
||||
#undef CONV_LIST_START
|
||||
#undef CONV_EMPTY_DICT
|
||||
#undef CONV_SPECIAL_NIL
|
||||
#undef CONV_SPECIAL_BOOL
|
||||
#undef CONV_NIL
|
||||
#undef CONV_BOOL
|
||||
#undef CONV_NONE
|
||||
#undef CONV_UNSIGNED_NUMBER
|
||||
#undef CONV_DICT_START
|
||||
#undef CONV_DICT_END
|
||||
@@ -1191,10 +1218,14 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
||||
#define CONV_EMPTY_DICT() \
|
||||
msgpack_pack_map(packer, 0)
|
||||
|
||||
#define CONV_SPECIAL_NIL() \
|
||||
#define CONV_NIL() \
|
||||
msgpack_pack_nil(packer)
|
||||
|
||||
#define CONV_SPECIAL_BOOL(num) \
|
||||
#define CONV_NONE() \
|
||||
return conv_error(_("E953: Attempt to convert v:none in %s, %s"), \
|
||||
mpstack, objname)
|
||||
|
||||
#define CONV_BOOL(num) \
|
||||
do { \
|
||||
if ((num)) { \
|
||||
msgpack_pack_true(packer); \
|
||||
@@ -1239,8 +1270,8 @@ DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
|
||||
#undef CONV_EMPTY_LIST
|
||||
#undef CONV_LIST_START
|
||||
#undef CONV_EMPTY_DICT
|
||||
#undef CONV_SPECIAL_NIL
|
||||
#undef CONV_SPECIAL_BOOL
|
||||
#undef CONV_NIL
|
||||
#undef CONV_BOOL
|
||||
#undef CONV_UNSIGNED_NUMBER
|
||||
#undef CONV_DICT_START
|
||||
#undef CONV_DICT_END
|
||||
|
@@ -8474,6 +8474,9 @@ static void f_empty(typval_T *argvars, typval_T *rettv)
|
||||
n = argvars[0].vval.v_dict == NULL
|
||||
|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
|
||||
break;
|
||||
case VAR_SPECIAL:
|
||||
n = argvars[0].vval.v_special != kSpecialVarTrue;
|
||||
break;
|
||||
default:
|
||||
EMSG2(_(e_intern2), "f_empty()");
|
||||
n = 0;
|
||||
@@ -17571,7 +17574,7 @@ handle_subscript (
|
||||
void free_tv(typval_T *varp)
|
||||
{
|
||||
if (varp != NULL) {
|
||||
switch (varp->v_type) {
|
||||
switch ((VarType) varp->v_type) {
|
||||
case VAR_FUNC:
|
||||
func_unref(varp->vval.v_string);
|
||||
/*FALLTHROUGH*/
|
||||
@@ -17584,6 +17587,7 @@ void free_tv(typval_T *varp)
|
||||
case VAR_DICT:
|
||||
dict_unref(varp->vval.v_dict);
|
||||
break;
|
||||
case VAR_SPECIAL:
|
||||
case VAR_NUMBER:
|
||||
case VAR_FLOAT:
|
||||
case VAR_UNKNOWN:
|
||||
|
@@ -16,38 +16,55 @@ typedef double float_T;
|
||||
typedef struct listvar_S list_T;
|
||||
typedef struct dictvar_S dict_T;
|
||||
|
||||
/*
|
||||
* Structure to hold an internal variable without a name.
|
||||
*/
|
||||
/// Special variable values
|
||||
typedef enum {
|
||||
kSpecialVarNull, ///< v:null
|
||||
kSpecialVarNone, ///< v:none
|
||||
kSpecialVarFalse, ///< v:false
|
||||
kSpecialVarTrue, ///< v:true
|
||||
} SpecialVarValue;
|
||||
|
||||
/// Structure that holds an internal variable value
|
||||
typedef struct {
|
||||
char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */
|
||||
char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */
|
||||
VarType v_type; ///< Variable type.
|
||||
VarLockStatus v_lock; ///< Variable lock status.
|
||||
union {
|
||||
varnumber_T v_number; /* number value */
|
||||
float_T v_float; /* floating number value */
|
||||
char_u *v_string; /* string value (can be NULL!) */
|
||||
list_T *v_list; /* list value (can be NULL!) */
|
||||
dict_T *v_dict; /* dict value (can be NULL!) */
|
||||
} vval;
|
||||
varnumber_T v_number; ///< Number, for VAR_NUMBER.
|
||||
SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
|
||||
float_T v_float; ///< Floating-point number, for VAR_FLOAT.
|
||||
char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
|
||||
list_T *v_list; ///< List for VAR_LIST, can be NULL.
|
||||
dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
|
||||
} vval; ///< Actual value.
|
||||
} typval_T;
|
||||
|
||||
/* Values for "v_type". */
|
||||
#define VAR_UNKNOWN 0
|
||||
#define VAR_NUMBER 1 /* "v_number" is used */
|
||||
#define VAR_STRING 2 /* "v_string" is used */
|
||||
#define VAR_FUNC 3 /* "v_string" is function name */
|
||||
#define VAR_LIST 4 /* "v_list" is used */
|
||||
#define VAR_DICT 5 /* "v_dict" is used */
|
||||
#define VAR_FLOAT 6 /* "v_float" is used */
|
||||
/// VimL variable types, for use in typval_T.v_type
|
||||
///
|
||||
/// @warning Numbers are part of the user API (returned by type()), so they must
|
||||
/// not be changed.
|
||||
typedef enum {
|
||||
VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
|
||||
VAR_NUMBER = 1, ///< Number, .v_number is used.
|
||||
VAR_STRING = 2, ///< String, .v_string is used.
|
||||
VAR_FUNC = 3, ///< Function referene, .v_string is used for function name.
|
||||
VAR_LIST = 4, ///< List, .v_list is used.
|
||||
VAR_DICT = 5, ///< Dictionary, .v_dict is used.
|
||||
VAR_FLOAT = 6, ///< Floating-point value, .v_float is used.
|
||||
VAR_SPECIAL = 7, ///< Special value (true, false, null, none), .v_special
|
||||
///< is used.
|
||||
} VarType;
|
||||
|
||||
/* Values for "dv_scope". */
|
||||
#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */
|
||||
#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not
|
||||
allowed to mask existing functions */
|
||||
|
||||
/* Values for "v_lock". */
|
||||
#define VAR_LOCKED 1 /* locked with lock(), can use unlock() */
|
||||
#define VAR_FIXED 2 /* locked forever */
|
||||
/// Variable lock status for typval_T.v_lock
|
||||
typedef enum {
|
||||
VAR_UNLOCKED = 0, ///< Not locked.
|
||||
VAR_LOCKED, ///< User lock, can be unlocked.
|
||||
VAR_FIXED, ///< Locked forever.
|
||||
} VarLockStatus;
|
||||
|
||||
/*
|
||||
* Structure to hold an item of a list: an internal variable without a name.
|
||||
|
@@ -181,20 +181,20 @@ static int included_patches[] = {
|
||||
// 1184 NA
|
||||
// 1183 NA
|
||||
// 1182 NA
|
||||
// 1181,
|
||||
1181,
|
||||
1180,
|
||||
// 1179,
|
||||
// 1178,
|
||||
1178,
|
||||
// 1177 NA
|
||||
// 1176 NA
|
||||
// 1175 NA
|
||||
// 1174 NA
|
||||
// 1173,
|
||||
1173,
|
||||
// 1172 NA
|
||||
// 1171 NA
|
||||
// 1170 NA
|
||||
// 1169 NA
|
||||
// 1168,
|
||||
1168,
|
||||
// 1167,
|
||||
// 1166,
|
||||
// 1165 NA
|
||||
|
36
test/functional/eval/special_vars_spec.lua
Normal file
36
test/functional/eval/special_vars_spec.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
local helpers = require('test.functional.helpers')
|
||||
local execute = helpers.execute
|
||||
local funcs = helpers.funcs
|
||||
local clear = helpers.clear
|
||||
local eval = helpers.eval
|
||||
|
||||
describe('Special values', function()
|
||||
before_each(clear)
|
||||
|
||||
it('do not cause error when freed', function()
|
||||
execute([[
|
||||
function Test()
|
||||
try
|
||||
return v:true
|
||||
finally
|
||||
return 'something else'
|
||||
endtry
|
||||
endfunction
|
||||
]])
|
||||
eq(true, funcs.Test())
|
||||
end)
|
||||
|
||||
it('work with empty()', function()
|
||||
eq(0, funcs.empty(true))
|
||||
eq(1, funcs.empty(false))
|
||||
eq(1, funcs.empty(nil))
|
||||
eq(1, eval('empty(v:none)'))
|
||||
end)
|
||||
|
||||
it('can be stringified and eval’ed back', function()
|
||||
eq(true, funcs.eval(funcs.string(true)))
|
||||
eq(false, funcs.eval(funcs.string(false)))
|
||||
eq(nil, funcs.eval(funcs.string(nil)))
|
||||
eq(1, eval('eval(string(v:none)) is# v:none'))
|
||||
end)
|
||||
end)
|
Reference in New Issue
Block a user