mirror of
https://github.com/neovim/neovim.git
synced 2025-09-15 07:48:18 +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) {
|
switch (obj.type) {
|
||||||
case kObjectTypeNil:
|
case kObjectTypeNil:
|
||||||
tv->v_type = VAR_NUMBER;
|
tv->v_type = VAR_SPECIAL;
|
||||||
tv->vval.v_number = 0;
|
tv->vval.v_special = kSpecialVarNull;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kObjectTypeBoolean:
|
case kObjectTypeBoolean:
|
||||||
tv->v_type = VAR_NUMBER;
|
tv->v_type = VAR_SPECIAL;
|
||||||
tv->vval.v_number = obj.data.boolean;
|
tv->vval.v_special = obj.data.boolean? kSpecialVarTrue: kSpecialVarFalse;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kObjectTypeBuffer:
|
case kObjectTypeBuffer:
|
||||||
|
@@ -343,6 +343,24 @@ static int name##_convert_one_value(firstargtype firstargname, \
|
|||||||
})); \
|
})); \
|
||||||
break; \
|
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: { \
|
case VAR_DICT: { \
|
||||||
if (tv->vval.v_dict == NULL \
|
if (tv->vval.v_dict == NULL \
|
||||||
|| tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
|
|| tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
|
||||||
@@ -369,14 +387,14 @@ static int name##_convert_one_value(firstargtype firstargname, \
|
|||||||
} \
|
} \
|
||||||
switch ((MessagePackType) i) { \
|
switch ((MessagePackType) i) { \
|
||||||
case kMPNil: { \
|
case kMPNil: { \
|
||||||
CONV_SPECIAL_NIL(); \
|
CONV_NIL(); \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
case kMPBoolean: { \
|
case kMPBoolean: { \
|
||||||
if (val_di->di_tv.v_type != VAR_NUMBER) { \
|
if (val_di->di_tv.v_type != VAR_NUMBER) { \
|
||||||
goto name##_convert_one_value_regular_dict; \
|
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; \
|
break; \
|
||||||
} \
|
} \
|
||||||
case kMPInteger: { \
|
case kMPInteger: { \
|
||||||
@@ -698,9 +716,14 @@ encode_vim_to_##name##_error_ret: \
|
|||||||
#define CONV_EMPTY_DICT() \
|
#define CONV_EMPTY_DICT() \
|
||||||
ga_concat(gap, "{}")
|
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)
|
#define CONV_UNSIGNED_NUMBER(num)
|
||||||
|
|
||||||
@@ -804,12 +827,12 @@ DEFINE_VIML_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
|||||||
#undef CONV_ALLOW_SPECIAL
|
#undef CONV_ALLOW_SPECIAL
|
||||||
#define CONV_ALLOW_SPECIAL true
|
#define CONV_ALLOW_SPECIAL true
|
||||||
|
|
||||||
#undef CONV_SPECIAL_NIL
|
#undef CONV_NIL
|
||||||
#define CONV_SPECIAL_NIL() \
|
#define CONV_NIL() \
|
||||||
ga_concat(gap, "null")
|
ga_concat(gap, "null")
|
||||||
|
|
||||||
#undef CONV_SPECIAL_BOOL
|
#undef CONV_BOOL
|
||||||
#define CONV_SPECIAL_BOOL(num) \
|
#define CONV_BOOL(num) \
|
||||||
ga_concat(gap, ((num)? "true": "false"))
|
ga_concat(gap, ((num)? "true": "false"))
|
||||||
|
|
||||||
#undef CONV_UNSIGNED_NUMBER
|
#undef CONV_UNSIGNED_NUMBER
|
||||||
@@ -1046,6 +1069,9 @@ static inline bool check_json_key(const typval_T *const tv)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#undef CONV_NONE
|
||||||
|
#define CONV_NONE()
|
||||||
|
|
||||||
DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
||||||
|
|
||||||
#undef CONV_STRING
|
#undef CONV_STRING
|
||||||
@@ -1057,8 +1083,9 @@ DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
|||||||
#undef CONV_EMPTY_LIST
|
#undef CONV_EMPTY_LIST
|
||||||
#undef CONV_LIST_START
|
#undef CONV_LIST_START
|
||||||
#undef CONV_EMPTY_DICT
|
#undef CONV_EMPTY_DICT
|
||||||
#undef CONV_SPECIAL_NIL
|
#undef CONV_NIL
|
||||||
#undef CONV_SPECIAL_BOOL
|
#undef CONV_BOOL
|
||||||
|
#undef CONV_NONE
|
||||||
#undef CONV_UNSIGNED_NUMBER
|
#undef CONV_UNSIGNED_NUMBER
|
||||||
#undef CONV_DICT_START
|
#undef CONV_DICT_START
|
||||||
#undef CONV_DICT_END
|
#undef CONV_DICT_END
|
||||||
@@ -1191,10 +1218,14 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
#define CONV_EMPTY_DICT() \
|
#define CONV_EMPTY_DICT() \
|
||||||
msgpack_pack_map(packer, 0)
|
msgpack_pack_map(packer, 0)
|
||||||
|
|
||||||
#define CONV_SPECIAL_NIL() \
|
#define CONV_NIL() \
|
||||||
msgpack_pack_nil(packer)
|
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 { \
|
do { \
|
||||||
if ((num)) { \
|
if ((num)) { \
|
||||||
msgpack_pack_true(packer); \
|
msgpack_pack_true(packer); \
|
||||||
@@ -1239,8 +1270,8 @@ DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
|
|||||||
#undef CONV_EMPTY_LIST
|
#undef CONV_EMPTY_LIST
|
||||||
#undef CONV_LIST_START
|
#undef CONV_LIST_START
|
||||||
#undef CONV_EMPTY_DICT
|
#undef CONV_EMPTY_DICT
|
||||||
#undef CONV_SPECIAL_NIL
|
#undef CONV_NIL
|
||||||
#undef CONV_SPECIAL_BOOL
|
#undef CONV_BOOL
|
||||||
#undef CONV_UNSIGNED_NUMBER
|
#undef CONV_UNSIGNED_NUMBER
|
||||||
#undef CONV_DICT_START
|
#undef CONV_DICT_START
|
||||||
#undef CONV_DICT_END
|
#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
|
n = argvars[0].vval.v_dict == NULL
|
||||||
|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
|
|| argvars[0].vval.v_dict->dv_hashtab.ht_used == 0;
|
||||||
break;
|
break;
|
||||||
|
case VAR_SPECIAL:
|
||||||
|
n = argvars[0].vval.v_special != kSpecialVarTrue;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
EMSG2(_(e_intern2), "f_empty()");
|
EMSG2(_(e_intern2), "f_empty()");
|
||||||
n = 0;
|
n = 0;
|
||||||
@@ -17571,7 +17574,7 @@ handle_subscript (
|
|||||||
void free_tv(typval_T *varp)
|
void free_tv(typval_T *varp)
|
||||||
{
|
{
|
||||||
if (varp != NULL) {
|
if (varp != NULL) {
|
||||||
switch (varp->v_type) {
|
switch ((VarType) varp->v_type) {
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
func_unref(varp->vval.v_string);
|
func_unref(varp->vval.v_string);
|
||||||
/*FALLTHROUGH*/
|
/*FALLTHROUGH*/
|
||||||
@@ -17584,6 +17587,7 @@ void free_tv(typval_T *varp)
|
|||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
dict_unref(varp->vval.v_dict);
|
dict_unref(varp->vval.v_dict);
|
||||||
break;
|
break;
|
||||||
|
case VAR_SPECIAL:
|
||||||
case VAR_NUMBER:
|
case VAR_NUMBER:
|
||||||
case VAR_FLOAT:
|
case VAR_FLOAT:
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
|
@@ -16,38 +16,55 @@ typedef double float_T;
|
|||||||
typedef struct listvar_S list_T;
|
typedef struct listvar_S list_T;
|
||||||
typedef struct dictvar_S dict_T;
|
typedef struct dictvar_S dict_T;
|
||||||
|
|
||||||
/*
|
/// Special variable values
|
||||||
* Structure to hold an internal variable without a name.
|
typedef enum {
|
||||||
*/
|
kSpecialVarNull, ///< v:null
|
||||||
|
kSpecialVarNone, ///< v:none
|
||||||
|
kSpecialVarFalse, ///< v:false
|
||||||
|
kSpecialVarTrue, ///< v:true
|
||||||
|
} SpecialVarValue;
|
||||||
|
|
||||||
|
/// Structure that holds an internal variable value
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */
|
VarType v_type; ///< Variable type.
|
||||||
char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */
|
VarLockStatus v_lock; ///< Variable lock status.
|
||||||
union {
|
union {
|
||||||
varnumber_T v_number; /* number value */
|
varnumber_T v_number; ///< Number, for VAR_NUMBER.
|
||||||
float_T v_float; /* floating number value */
|
SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
|
||||||
char_u *v_string; /* string value (can be NULL!) */
|
float_T v_float; ///< Floating-point number, for VAR_FLOAT.
|
||||||
list_T *v_list; /* list value (can be NULL!) */
|
char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
|
||||||
dict_T *v_dict; /* dict value (can be NULL!) */
|
list_T *v_list; ///< List for VAR_LIST, can be NULL.
|
||||||
} vval;
|
dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
|
||||||
|
} vval; ///< Actual value.
|
||||||
} typval_T;
|
} typval_T;
|
||||||
|
|
||||||
/* Values for "v_type". */
|
/// VimL variable types, for use in typval_T.v_type
|
||||||
#define VAR_UNKNOWN 0
|
///
|
||||||
#define VAR_NUMBER 1 /* "v_number" is used */
|
/// @warning Numbers are part of the user API (returned by type()), so they must
|
||||||
#define VAR_STRING 2 /* "v_string" is used */
|
/// not be changed.
|
||||||
#define VAR_FUNC 3 /* "v_string" is function name */
|
typedef enum {
|
||||||
#define VAR_LIST 4 /* "v_list" is used */
|
VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
|
||||||
#define VAR_DICT 5 /* "v_dict" is used */
|
VAR_NUMBER = 1, ///< Number, .v_number is used.
|
||||||
#define VAR_FLOAT 6 /* "v_float" 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". */
|
/* Values for "dv_scope". */
|
||||||
#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */
|
#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */
|
||||||
#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not
|
#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not
|
||||||
allowed to mask existing functions */
|
allowed to mask existing functions */
|
||||||
|
|
||||||
/* Values for "v_lock". */
|
/// Variable lock status for typval_T.v_lock
|
||||||
#define VAR_LOCKED 1 /* locked with lock(), can use unlock() */
|
typedef enum {
|
||||||
#define VAR_FIXED 2 /* locked forever */
|
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.
|
* Structure to hold an item of a list: an internal variable without a name.
|
||||||
|
@@ -181,20 +181,20 @@ static int included_patches[] = {
|
|||||||
// 1184 NA
|
// 1184 NA
|
||||||
// 1183 NA
|
// 1183 NA
|
||||||
// 1182 NA
|
// 1182 NA
|
||||||
// 1181,
|
1181,
|
||||||
1180,
|
1180,
|
||||||
// 1179,
|
// 1179,
|
||||||
// 1178,
|
1178,
|
||||||
// 1177 NA
|
// 1177 NA
|
||||||
// 1176 NA
|
// 1176 NA
|
||||||
// 1175 NA
|
// 1175 NA
|
||||||
// 1174 NA
|
// 1174 NA
|
||||||
// 1173,
|
1173,
|
||||||
// 1172 NA
|
// 1172 NA
|
||||||
// 1171 NA
|
// 1171 NA
|
||||||
// 1170 NA
|
// 1170 NA
|
||||||
// 1169 NA
|
// 1169 NA
|
||||||
// 1168,
|
1168,
|
||||||
// 1167,
|
// 1167,
|
||||||
// 1166,
|
// 1166,
|
||||||
// 1165 NA
|
// 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