eval: Add special variables v:false, v:null, v:none

This commit is contained in:
ZyX
2016-01-31 01:25:00 +03:00
parent 18903bd9b8
commit d70a322c40
11 changed files with 441 additions and 116 deletions

View File

@@ -1418,6 +1418,13 @@ v:exception The value of the exception most recently caught and not
:endtry
< Output: "caught oops".
*v:false* *false-variable*
v:false Special value used to put "false" in JSON and msgpack. See
|jsonencode()|. This value is converted to "false" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators).
*v:fcs_reason* *fcs_reason-variable*
v:fcs_reason The reason why the |FileChangedShell| event was triggered.
Can be used in an autocommand to decide what to do and/or what
@@ -1557,6 +1564,20 @@ v:msgpack_types Dictionary containing msgpack types used by |msgpackparse()|
(not editable) empty lists. To check whether some list is one
of msgpack types, use |is| operator.
*v:null* *null-variable*
v:null Special value used to put "null" in JSON and NIL in msgpack.
See |jsonencode()|. This value is converted to "null" when
used as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators).
*v:none* *none-variable*
v:none Special value used to put an empty item in JSON. See
|jsonencode()|. This value is converted to "none" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to zero when used as a Number (e.g. in |expr5|
or |expr7| when used with numeric operators).
*v:oldfiles* *oldfiles-variable*
v:oldfiles List of file names that is loaded from the |shada| file on
startup. These are the files that Vim remembers marks for.
@@ -1722,6 +1743,13 @@ v:throwpoint The point where the exception most recently caught and not
:endtry
< Output: "Exception from test.vim, line 2"
*v:true* *true-variable*
v:true Special value used to put "true" in JSON and msgpack. See
|jsonencode()|. This value is converted to "true" when used
as a String (e.g. in |expr5| with string concatenation
operator) and to one when used as a Number (e.g. in |expr5| or
|expr7| when used with numeric operators).
*v:val* *val-variable*
v:val Value of the current item of a |List| or |Dictionary|. Only
valid while evaluating the expression used with |map()| and
@@ -4832,8 +4860,8 @@ msgpackdump({list}) {Nvim} *msgpackdump()*
(dictionary with zero items is represented by 0x80 byte in
messagepack).
Limitations: *E951* *E952*
1. |Funcref|s cannot be dumped.
Limitations: *E951* *E952* *E953*
1. |Funcref|s and |v:none| cannot be dumped.
2. Containers that reference themselves cannot be dumped.
3. Dictionary keys are always dumped as STR strings.
4. Other strings are always dumped as BIN strings.

View File

@@ -651,6 +651,22 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup)
}
switch (obj->v_type) {
case VAR_SPECIAL:
switch (obj->vval.v_special) {
case kSpecialVarTrue:
case kSpecialVarFalse: {
rv.type = kObjectTypeBoolean;
rv.data.boolean = (obj->vval.v_special == kSpecialVarTrue);
break;
}
case kSpecialVarNull:
case kSpecialVarNone: {
rv.type = kObjectTypeNil;
break;
}
}
break;
case VAR_STRING:
rv.type = kObjectTypeString;
rv.data.string = cstr_to_string((char *) obj->vval.v_string);
@@ -730,6 +746,10 @@ static Object vim_to_object_rec(typval_T *obj, PMap(ptr_t) *lookup)
}
}
break;
case VAR_UNKNOWN:
case VAR_FUNC:
break;
}
return rv;

View File

@@ -8,10 +8,12 @@
#include <msgpack.h>
#include <inttypes.h>
#include <assert.h>
#include "nvim/encode.h"
#include "nvim/buffer_defs.h" // vimconv_T
#include "nvim/eval.h"
#include "nvim/eval_defs.h"
#include "nvim/garray.h"
#include "nvim/mbyte.h"
#include "nvim/message.h"
@@ -53,6 +55,13 @@ typedef struct {
/// Stack used to convert VimL values to messagepack.
typedef kvec_t(MPConvStackVal) MPConvStack;
const char *const encode_special_var_names[] = {
[kSpecialVarNull] = "null",
[kSpecialVarNone] = "none",
[kSpecialVarTrue] = "true",
[kSpecialVarFalse] = "false",
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "encode.c.generated.h"
#endif
@@ -355,7 +364,7 @@ static int name##_convert_one_value(firstargtype firstargname, \
break; \
} \
case kSpecialVarNone: { \
CONV_NONE(); \
CONV_NONE_VAL(); \
break; \
} \
} \
@@ -558,8 +567,7 @@ scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
const char *const objname) \
FUNC_ATTR_WARN_UNUSED_RESULT \
{ \
current_copyID += COPYID_INC; \
const int copyID = current_copyID; \
const int copyID = get_copyID(); \
MPConvStack mpstack; \
kv_init(mpstack); \
if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
@@ -717,13 +725,13 @@ encode_vim_to_##name##_error_ret: \
ga_concat(gap, "{}")
#define CONV_NIL() \
ga_append(gap, "v:null")
ga_concat(gap, "v:null")
#define CONV_BOOL(num) \
ga_append(gap, ((num)? "v:true": "v:false"))
ga_concat(gap, ((num)? "v:true": "v:false"))
#define CONV_NONE() \
ga_append(gap, "v:none")
#define CONV_NONE_VAL() \
ga_concat(gap, "v:none")
#define CONV_UNSIGNED_NUMBER(num)
@@ -1069,8 +1077,8 @@ static inline bool check_json_key(const typval_T *const tv)
} \
} while (0)
#undef CONV_NONE
#define CONV_NONE()
#undef CONV_NONE_VAL
#define CONV_NONE_VAL()
DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
@@ -1085,7 +1093,7 @@ DEFINE_VIML_CONV_FUNCTIONS(static, json, garray_T *const, gap)
#undef CONV_EMPTY_DICT
#undef CONV_NIL
#undef CONV_BOOL
#undef CONV_NONE
#undef CONV_NONE_VAL
#undef CONV_UNSIGNED_NUMBER
#undef CONV_DICT_START
#undef CONV_DICT_END
@@ -1221,7 +1229,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define CONV_NIL() \
msgpack_pack_nil(packer)
#define CONV_NONE() \
#define CONV_NONE_VAL() \
return conv_error(_("E953: Attempt to convert v:none in %s, %s"), \
mpstack, objname)
@@ -1272,6 +1280,7 @@ DEFINE_VIML_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
#undef CONV_EMPTY_DICT
#undef CONV_NIL
#undef CONV_BOOL
#undef CONV_NONE_VAL
#undef CONV_UNSIGNED_NUMBER
#undef CONV_DICT_START
#undef CONV_DICT_END

View File

@@ -51,6 +51,9 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
};
}
/// Array mapping values from SpecialVarValue enum to names
extern const char *const encode_special_var_names[];
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "encode.h.generated.h"
#endif

View File

@@ -178,8 +178,6 @@ static dictitem_T globvars_var; /* variable used for g: */
*/
static hashtab_T compat_hashtab;
int current_copyID = 0;
hashtab_T func_hashtab;
/*
@@ -366,11 +364,16 @@ static struct vimvar {
{ VV_NAME("errors", VAR_LIST), 0 },
{ VV_NAME("msgpack_types", VAR_DICT), VV_RO },
{ VV_NAME("event", VAR_DICT), VV_RO },
{ VV_NAME("false", VAR_SPECIAL), VV_RO },
{ VV_NAME("true", VAR_SPECIAL), VV_RO },
{ VV_NAME("null", VAR_SPECIAL), VV_RO },
{ VV_NAME("none", VAR_SPECIAL), VV_RO },
};
/* shorthand */
#define vv_type vv_di.di_tv.v_type
#define vv_nr vv_di.di_tv.vval.v_number
#define vv_special vv_di.di_tv.vval.v_special
#define vv_float vv_di.di_tv.vval.v_float
#define vv_str vv_di.di_tv.vval.v_string
#define vv_list vv_di.di_tv.vval.v_list
@@ -506,7 +509,13 @@ void eval_init(void)
set_vim_var_list(VV_ERRORS, list_alloc());
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
set_reg_var(0); /* default for v:register is not 0 but '"' */
set_vim_var_special(VV_FALSE, kSpecialVarFalse);
set_vim_var_special(VV_TRUE, kSpecialVarTrue);
set_vim_var_special(VV_NONE, kSpecialVarNone);
set_vim_var_special(VV_NULL, kSpecialVarNull);
set_reg_var(0); // default for v:register is not 0 but '"'
}
#if defined(EXITFREE)
@@ -2368,11 +2377,12 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
char_u numbuf[NUMBUFLEN];
char_u *s;
/* Can't do anything with a Funcref or a Dict on the right. */
// Can't do anything with a Funcref, a Dict or special value on the right.
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) {
switch (tv1->v_type) {
case VAR_DICT:
case VAR_FUNC:
case VAR_SPECIAL:
break;
case VAR_LIST:
@@ -2440,6 +2450,9 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
tv1->vval.v_float -= f;
}
return OK;
case VAR_UNKNOWN:
assert(false);
}
}
@@ -3077,6 +3090,15 @@ static void item_lock(typval_T *tv, int deep, int lock)
}
}
}
break;
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_STRING:
case VAR_FUNC:
case VAR_SPECIAL:
break;
case VAR_UNKNOWN:
assert(false);
}
--recurse;
}
@@ -4306,6 +4328,11 @@ eval_index (
if (verbose)
EMSG(_(e_float_as_string));
return FAIL;
} else if (rettv->v_type == VAR_SPECIAL) {
if (verbose) {
EMSG(_("E15: Cannot index a special value"));
}
return FAIL;
}
init_tv(&var1);
@@ -4496,6 +4523,11 @@ eval_index (
*rettv = var1;
}
break;
case VAR_FUNC:
case VAR_FLOAT:
case VAR_UNKNOWN:
case VAR_SPECIAL:
assert(false);
}
}
@@ -5040,6 +5072,12 @@ tv_equal (
s1 = get_tv_string_buf(tv1, buf1);
s2 = get_tv_string_buf(tv2, buf2);
return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0;
case VAR_SPECIAL:
return tv1->vval.v_special == tv2->vval.v_special;
case VAR_UNKNOWN:
break;
}
EMSG2(_(e_intern2), "tv_equal()");
@@ -5505,6 +5543,22 @@ static int list_join(garray_T *const gap, list_T *const l,
return retval;
}
/// Get next (unique) copy ID
///
/// Used for traversing nested structures e.g. when serializing them or garbage
/// collecting.
int get_copyID(void)
FUNC_ATTR_WARN_UNUSED_RESULT
{
// CopyID for recursively traversing lists and dicts
//
// This value is needed to avoid endless recursiveness. Last bit is used for
// previous_funccal and normally ignored when comparing.
static int current_copyID = 0;
current_copyID += COPYID_INC;
return current_copyID;
}
/*
* Garbage collection for lists and dictionaries.
*
@@ -5540,8 +5594,7 @@ bool garbage_collect(void)
// We advance by two because we add one for items referenced through
// previous_funccal.
current_copyID += COPYID_INC;
int copyID = current_copyID;
const int copyID = get_copyID();
// 1. Go through all accessible variables and mark all lists and dicts
// with copyID.
@@ -5886,6 +5939,15 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
}
break;
}
case VAR_FUNC:
case VAR_UNKNOWN:
case VAR_SPECIAL:
case VAR_FLOAT:
case VAR_NUMBER:
case VAR_STRING: {
break;
}
}
return abort;
}
@@ -8260,9 +8322,8 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv)
if (noref < 0 || noref > 1)
EMSG(_(e_invarg));
else {
current_copyID += COPYID_INC;
var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0
? current_copyID
? get_copyID()
: 0));
}
}
@@ -8477,7 +8538,7 @@ static void f_empty(typval_T *argvars, typval_T *rettv)
case VAR_SPECIAL:
n = argvars[0].vval.v_special != kSpecialVarTrue;
break;
default:
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "f_empty()");
n = 0;
}
@@ -16386,13 +16447,28 @@ static void f_type(typval_T *argvars, typval_T *rettv)
int n;
switch (argvars[0].v_type) {
case VAR_NUMBER: n = 0; break;
case VAR_STRING: n = 1; break;
case VAR_FUNC: n = 2; break;
case VAR_LIST: n = 3; break;
case VAR_DICT: n = 4; break;
case VAR_FLOAT: n = 5; break;
default: EMSG2(_(e_intern2), "f_type()"); n = 0; break;
case VAR_NUMBER: n = 0; break;
case VAR_STRING: n = 1; break;
case VAR_FUNC: n = 2; break;
case VAR_LIST: n = 3; break;
case VAR_DICT: n = 4; break;
case VAR_FLOAT: n = 5; break;
case VAR_SPECIAL: {
switch (argvars[0].vval.v_special) {
case kSpecialVarTrue:
case kSpecialVarFalse: {
n = 6;
break;
}
case kSpecialVarNone:
case kSpecialVarNull: {
n = 7;
break;
}
}
break;
}
case VAR_UNKNOWN: EMSG2(_(e_intern2), "f_type()"); n = 0; break;
}
rettv->vval.v_number = n;
}
@@ -17226,6 +17302,12 @@ void set_vim_var_nr(int idx, long val)
vimvars[idx].vv_nr = val;
}
/// Set special v: variable to "val"
void set_vim_var_special(const int idx, const SpecialVarValue val)
{
vimvars[idx].vv_special = val;
}
/*
* Get number v: variable value.
*/
@@ -17574,7 +17656,7 @@ handle_subscript (
void free_tv(typval_T *varp)
{
if (varp != NULL) {
switch ((VarType) varp->v_type) {
switch (varp->v_type) {
case VAR_FUNC:
func_unref(varp->vval.v_string);
/*FALLTHROUGH*/
@@ -17592,9 +17674,6 @@ void free_tv(typval_T *varp)
case VAR_FLOAT:
case VAR_UNKNOWN:
break;
default:
EMSG2(_(e_intern2), "free_tv()");
break;
}
xfree(varp);
}
@@ -17632,10 +17711,11 @@ void clear_tv(typval_T *varp)
case VAR_FLOAT:
varp->vval.v_float = 0.0;
break;
case VAR_SPECIAL:
varp->vval.v_special = kSpecialVarFalse;
break;
case VAR_UNKNOWN:
break;
default:
EMSG2(_(e_intern2), "clear_tv()");
}
varp->v_lock = 0;
}
@@ -17690,7 +17770,19 @@ long get_tv_number_chk(typval_T *varp, int *denote)
case VAR_DICT:
EMSG(_("E728: Using a Dictionary as a Number"));
break;
default:
case VAR_SPECIAL:
switch (varp->vval.v_special) {
case kSpecialVarTrue: {
return 1;
}
case kSpecialVarFalse:
case kSpecialVarNone:
case kSpecialVarNull: {
return 0;
}
}
break;
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "get_tv_number()");
break;
}
@@ -17796,7 +17888,10 @@ static char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf)
if (varp->vval.v_string != NULL)
return varp->vval.v_string;
return (char_u *)"";
default:
case VAR_SPECIAL:
STRCPY(buf, encode_special_var_names[varp->vval.v_special]);
return buf;
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "get_tv_string_buf()");
break;
}
@@ -18345,42 +18440,34 @@ void copy_tv(typval_T *from, typval_T *to)
{
to->v_type = from->v_type;
to->v_lock = 0;
memmove(&to->vval, &from->vval, sizeof(to->vval));
switch (from->v_type) {
case VAR_NUMBER:
to->vval.v_number = from->vval.v_number;
break;
case VAR_FLOAT:
to->vval.v_float = from->vval.v_float;
break;
case VAR_STRING:
case VAR_FUNC:
if (from->vval.v_string == NULL)
to->vval.v_string = NULL;
else {
to->vval.v_string = vim_strsave(from->vval.v_string);
if (from->v_type == VAR_FUNC)
func_ref(to->vval.v_string);
}
break;
case VAR_LIST:
if (from->vval.v_list == NULL)
to->vval.v_list = NULL;
else {
to->vval.v_list = from->vval.v_list;
++to->vval.v_list->lv_refcount;
}
break;
case VAR_DICT:
if (from->vval.v_dict == NULL)
to->vval.v_dict = NULL;
else {
to->vval.v_dict = from->vval.v_dict;
++to->vval.v_dict->dv_refcount;
}
break;
default:
EMSG2(_(e_intern2), "copy_tv()");
break;
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_SPECIAL:
break;
case VAR_STRING:
case VAR_FUNC:
if (from->vval.v_string != NULL) {
to->vval.v_string = vim_strsave(from->vval.v_string);
if (from->v_type == VAR_FUNC) {
func_ref(to->vval.v_string);
}
}
break;
case VAR_LIST:
if (from->vval.v_list != NULL) {
to->vval.v_list->lv_refcount++;
}
break;
case VAR_DICT:
if (from->vval.v_dict != NULL) {
to->vval.v_dict->dv_refcount++;
}
break;
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "copy_tv()");
break;
}
}
@@ -18420,6 +18507,7 @@ int var_item_copy(const vimconv_T *const conv,
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_FUNC:
case VAR_SPECIAL:
copy_tv(from, to);
break;
case VAR_STRING:
@@ -18466,7 +18554,7 @@ int var_item_copy(const vimconv_T *const conv,
if (to->vval.v_dict == NULL)
ret = FAIL;
break;
default:
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "var_item_copy()");
ret = FAIL;
}
@@ -21705,4 +21793,3 @@ static bool is_watched(dict_T *d)
{
return d && !QUEUE_EMPTY(&d->watchers);
}

View File

@@ -15,12 +15,6 @@
// All user-defined functions are found in this hashtable.
extern hashtab_T func_hashtab;
/// CopyID for recursively traversing lists and dicts
///
/// This value is needed to avoid endless recursiveness. Last bit is used for
/// previous_funccal and normally ignored when comparing.
extern int current_copyID;
// Structure to hold info for a user function.
typedef struct ufunc ufunc_T;
@@ -127,7 +121,11 @@ enum {
VV_ERRORS,
VV_MSGPACK_TYPES,
VV_EVENT,
VV_LEN, // number of v: vars
VV_FALSE,
VV_TRUE,
VV_NULL,
VV_NONE,
VV_LEN, ///< Number of v: variables
};
/// All recognized msgpack types

View File

@@ -18,12 +18,32 @@ typedef struct dictvar_S dict_T;
/// Special variable values
typedef enum {
kSpecialVarNull, ///< v:null
kSpecialVarNone, ///< v:none
kSpecialVarFalse, ///< v:false
kSpecialVarTrue, ///< v:true
kSpecialVarNone, ///< v:none
kSpecialVarNull, ///< v:null
} SpecialVarValue;
/// 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;
/// VimL variable types, for use in typval_T.v_type
typedef enum {
VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
VAR_NUMBER, ///< Number, .v_number is used.
VAR_STRING, ///< String, .v_string is used.
VAR_FUNC, ///< Function referene, .v_string is used for function name.
VAR_LIST, ///< List, .v_list is used.
VAR_DICT, ///< Dictionary, .v_dict is used.
VAR_FLOAT, ///< Floating-point value, .v_float is used.
VAR_SPECIAL, ///< Special value (true, false, null, none), .v_special
///< is used.
} VarType;
/// Structure that holds an internal variable value
typedef struct {
VarType v_type; ///< Variable type.
@@ -38,34 +58,11 @@ typedef struct {
} vval; ///< Actual value.
} typval_T;
/// 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 */
/// 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.
*/

View File

@@ -195,18 +195,18 @@ static int included_patches[] = {
// 1170 NA
// 1169 NA
1168,
// 1167,
// 1166,
1167,
1166,
// 1165 NA
// 1164,
// 1163,
1164,
1163,
// 1162 NA
// 1161,
// 1160,
1160,
// 1159 NA
// 1158 NA
// 1157,
// 1156,
1157,
// 1156 NA
// 1155 NA
// 1154,
// 1153,

View File

@@ -1,5 +1,6 @@
local helpers = require('test.functional.helpers')
local clear = helpers.clear
local funcs = helpers.funcs
local eval, eq = helpers.eval, helpers.eq
local execute = helpers.execute
local nvim = helpers.nvim
@@ -517,6 +518,19 @@ describe('msgpackdump() function', function()
eq({'\129\128\128'}, eval('msgpackdump([todump])'))
end)
it('can dump v:true', function()
eq({'\195'}, funcs.msgpackdump({true}))
end)
it('can dump v:false', function()
eq({'\194'}, funcs.msgpackdump({false}))
end)
it('can v:null', function()
execute('let todump = v:null')
eq({'\192'}, eval('msgpackdump([todump])'))
end)
it('can dump special ext mapping', function()
execute('let todump = {"_TYPE": v:msgpack_types.ext, "_VAL": [5, ["",""]]}')
eq({'\212\005', ''}, eval('msgpackdump([todump])'))
@@ -620,6 +634,11 @@ describe('msgpackdump() function', function()
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump v:none', function()
eq('Vim(call):E953: Attempt to convert v:none in msgpackdump() argument, index 0, itself',
exc_exec('call msgpackdump([v:none])'))
end)
it('fails when called with no arguments', function()
eq('Vim(call):E119: Not enough arguments for function: msgpackdump',
exc_exec('call msgpackdump()'))
@@ -654,4 +673,11 @@ describe('msgpackdump() function', function()
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
exc_exec('call msgpackdump(0.0)'))
end)
it('fails to dump special value', function()
for _, val in ipairs({'v:true', 'v:false', 'v:null', 'v:none'}) do
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
exc_exec('call msgpackdump(' .. val .. ')'))
end
end)
end)

View File

@@ -1,8 +1,12 @@
local helpers = require('test.functional.helpers')
local exc_exec = helpers.exc_exec
local execute = helpers.execute
local meths = helpers.meths
local funcs = helpers.funcs
local meths = helpers.meths
local clear = helpers.clear
local eval = helpers.eval
local eq = helpers.eq
describe('Special values', function()
before_each(clear)
@@ -17,20 +21,166 @@ describe('Special values', function()
endtry
endfunction
]])
eq(true, funcs.Test())
eq(0, exc_exec('call 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:null)'))
eq(1, eval('empty(v:none)'))
end)
it('can be stringified and evaled back', function()
eq(true, funcs.eval(funcs.string(true)))
eq(false, funcs.eval(funcs.string(false)))
eq(nil, funcs.eval(funcs.string(nil)))
eq(nil, eval('eval(string(v:null))'))
eq(1, eval('eval(string(v:none)) is# v:none'))
end)
it('work with is/isnot properly', function()
eq(1, eval('v:none is v:none'))
eq(0, eval('v:none is v:null'))
eq(0, eval('v:none is v:true'))
eq(0, eval('v:none is v:false'))
eq(1, eval('v:null is v:null'))
eq(0, eval('v:null is v:true'))
eq(0, eval('v:null is v:false'))
eq(1, eval('v:true is v:true'))
eq(0, eval('v:true is v:false'))
eq(1, eval('v:false is v:false'))
eq(0, eval('v:none is 0'))
eq(0, eval('v:null is 0'))
eq(0, eval('v:true is 0'))
eq(0, eval('v:false is 0'))
eq(0, eval('v:none is 1'))
eq(0, eval('v:null is 1'))
eq(0, eval('v:true is 1'))
eq(0, eval('v:false is 1'))
eq(0, eval('v:none is ""'))
eq(0, eval('v:null is ""'))
eq(0, eval('v:true is ""'))
eq(0, eval('v:false is ""'))
eq(0, eval('v:none is "none"'))
eq(0, eval('v:null is "null"'))
eq(0, eval('v:true is "true"'))
eq(0, eval('v:false is "false"'))
eq(0, eval('v:none is []'))
eq(0, eval('v:null is []'))
eq(0, eval('v:true is []'))
eq(0, eval('v:false is []'))
eq(0, eval('v:none isnot v:none'))
eq(1, eval('v:none isnot v:null'))
eq(1, eval('v:none isnot v:true'))
eq(1, eval('v:none isnot v:false'))
eq(0, eval('v:null isnot v:null'))
eq(1, eval('v:null isnot v:true'))
eq(1, eval('v:null isnot v:false'))
eq(0, eval('v:true isnot v:true'))
eq(1, eval('v:true isnot v:false'))
eq(0, eval('v:false isnot v:false'))
eq(1, eval('v:none isnot 0'))
eq(1, eval('v:null isnot 0'))
eq(1, eval('v:true isnot 0'))
eq(1, eval('v:false isnot 0'))
eq(1, eval('v:none isnot 1'))
eq(1, eval('v:null isnot 1'))
eq(1, eval('v:true isnot 1'))
eq(1, eval('v:false isnot 1'))
eq(1, eval('v:none isnot ""'))
eq(1, eval('v:null isnot ""'))
eq(1, eval('v:true isnot ""'))
eq(1, eval('v:false isnot ""'))
eq(1, eval('v:none isnot "none"'))
eq(1, eval('v:null isnot "null"'))
eq(1, eval('v:true isnot "true"'))
eq(1, eval('v:false isnot "false"'))
eq(1, eval('v:none isnot []'))
eq(1, eval('v:null isnot []'))
eq(1, eval('v:true isnot []'))
eq(1, eval('v:false isnot []'))
end)
it('work with +/-/* properly', function()
eq(1, eval('0 + v:true'))
eq(0, eval('0 + v:none'))
eq(0, eval('0 + v:null'))
eq(0, eval('0 + v:false'))
eq(-1, eval('0 - v:true'))
eq( 0, eval('0 - v:none'))
eq( 0, eval('0 - v:null'))
eq( 0, eval('0 - v:false'))
eq(1, eval('1 * v:true'))
eq(0, eval('1 * v:none'))
eq(0, eval('1 * v:null'))
eq(0, eval('1 * v:false'))
end)
it('does not work with +=/-=/.=', function()
meths.set_var('true', true)
meths.set_var('false', false)
execute('let none = v:none')
execute('let null = v:null')
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let true += 1'))
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let false += 1'))
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let none += 1'))
eq('Vim(let):E734: Wrong variable type for +=', exc_exec('let null += 1'))
eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let true -= 1'))
eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let false -= 1'))
eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let none -= 1'))
eq('Vim(let):E734: Wrong variable type for -=', exc_exec('let null -= 1'))
eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let true .= 1'))
eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let false .= 1'))
eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let none .= 1'))
eq('Vim(let):E734: Wrong variable type for .=', exc_exec('let null .= 1'))
end)
it('work with . (concat) properly', function()
eq("true", eval('"" . v:true'))
eq("none", eval('"" . v:none'))
eq("null", eval('"" . v:null'))
eq("false", eval('"" . v:false'))
end)
it('work with type()', function()
eq(6, funcs.type(true))
eq(6, funcs.type(false))
eq(7, eval('type(v:null)'))
eq(7, eval('type(v:none)'))
end)
it('work with copy() and deepcopy()', function()
eq(true, funcs.deepcopy(true))
eq(false, funcs.deepcopy(false))
eq(nil, eval('deepcopy(v:null)'))
eq(nil, eval('deepcopy(v:none)'))
eq(true, funcs.copy(true))
eq(false, funcs.copy(false))
eq(nil, eval('copy(v:null)'))
eq(nil, eval('copy(v:none)'))
end)
it('fails in index', function()
eq('Vim(echo):E15: Cannot index a special value', exc_exec('echo v:true[0]'))
eq('Vim(echo):E15: Cannot index a special value', exc_exec('echo v:false[0]'))
eq('Vim(echo):E15: Cannot index a special value', exc_exec('echo v:none[0]'))
eq('Vim(echo):E15: Cannot index a special value', exc_exec('echo v:null[0]'))
end)
end)

View File

@@ -28,6 +28,13 @@ describe('string() function', function()
eq('0.0', eval('string(0.0)'))
end)
it('dumps special v: values', function()
eq('v:true', eval('string(v:true)'))
eq('v:false', eval('string(v:false)'))
eq('v:none', eval('string(v:none)'))
eq('v:null', eval('string(v:null)'))
end)
it('dumps values with at most six digits after the decimal point',
function()
eq('1.234568e-20', funcs.string(1.23456789123456789123456789e-020))