diff options
author | ZyX <kp-pav@yandex.ru> | 2016-01-31 01:25:00 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2016-04-18 02:44:03 +0300 |
commit | d70a322c40e849f98ad573d2a37dc680c5616b26 (patch) | |
tree | abe3c021dd0d7e9ae0f0f7a6b23d4ec8f548f930 | |
parent | 18903bd9b88ec960cb36b1ddd2b5062aad4bac2e (diff) | |
download | rneovim-d70a322c40e849f98ad573d2a37dc680c5616b26.tar.gz rneovim-d70a322c40e849f98ad573d2a37dc680c5616b26.tar.bz2 rneovim-d70a322c40e849f98ad573d2a37dc680c5616b26.zip |
eval: Add special variables v:false, v:null, v:none
-rw-r--r-- | runtime/doc/eval.txt | 32 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 20 | ||||
-rw-r--r-- | src/nvim/encode.c | 31 | ||||
-rw-r--r-- | src/nvim/encode.h | 3 | ||||
-rw-r--r-- | src/nvim/eval.c | 209 | ||||
-rw-r--r-- | src/nvim/eval.h | 12 | ||||
-rw-r--r-- | src/nvim/eval_defs.h | 47 | ||||
-rw-r--r-- | src/nvim/version.c | 14 | ||||
-rw-r--r-- | test/functional/eval/msgpack_functions_spec.lua | 26 | ||||
-rw-r--r-- | test/functional/eval/special_vars_spec.lua | 156 | ||||
-rw-r--r-- | test/functional/eval/string_spec.lua | 7 |
11 files changed, 441 insertions, 116 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2d08afff4f..572cf4c03f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -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. diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index c770618ce4..a8082655fd 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -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; diff --git a/src/nvim/encode.c b/src/nvim/encode.c index 6fdbe67ec8..c80e9783e0 100644 --- a/src/nvim/encode.c +++ b/src/nvim/encode.c @@ -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 diff --git a/src/nvim/encode.h b/src/nvim/encode.h index 799850aab9..5b81ed84dc 100644 --- a/src/nvim/encode.h +++ b/src/nvim/encode.h @@ -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 diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 114368d621..819b3059e2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -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); } - diff --git a/src/nvim/eval.h b/src/nvim/eval.h index e8b5964775..89aa263434 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -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 diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index 56833f97d8..bcd9e80f9a 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -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. */ diff --git a/src/nvim/version.c b/src/nvim/version.c index 8f45620570..9ccd53f76b 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -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, diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index fc0aad7902..0c91471656 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -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) diff --git a/test/functional/eval/special_vars_spec.lua b/test/functional/eval/special_vars_spec.lua index 7adc529c88..c7df847946 100644 --- a/test/functional/eval/special_vars_spec.lua +++ b/test/functional/eval/special_vars_spec.lua @@ -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 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(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) diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua index f7f5dca70a..fe79708910 100644 --- a/test/functional/eval/string_spec.lua +++ b/test/functional/eval/string_spec.lua @@ -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)) |