aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/clint.py7
-rw-r--r--src/nvim/api/private/helpers.c78
-rw-r--r--src/nvim/eval.c240
-rw-r--r--src/nvim/eval/encode.c323
-rw-r--r--src/nvim/eval/typval_encode.c.h802
-rw-r--r--src/nvim/eval/typval_encode.h594
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/fileio.c123
-rw-r--r--src/nvim/globals.h8
-rw-r--r--src/nvim/hashtab.c12
-rw-r--r--src/nvim/hashtab.h9
-rw-r--r--src/nvim/memory.c49
-rw-r--r--src/nvim/memory.h34
-rw-r--r--src/nvim/os/time.c39
-rw-r--r--src/nvim/path.c47
-rw-r--r--src/nvim/testdir/test_autocmd.vim31
-rw-r--r--src/nvim/testdir/test_usercommands.vim64
-rw-r--r--src/nvim/version.c7
18 files changed, 1585 insertions, 886 deletions
diff --git a/src/clint.py b/src/clint.py
index 0470f824fa..76982144fb 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -3005,9 +3005,10 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
include = match.group(2)
is_system = (match.group(1) == '<')
if include in include_state:
- error(filename, linenum, 'build/include', 4,
- '"%s" already included at %s:%s' %
- (include, filename, include_state[include]))
+ if is_system or not include.endswith('.c.h'):
+ error(filename, linenum, 'build/include', 4,
+ '"%s" already included at %s:%s' %
+ (include, filename, include_state[include]))
else:
include_state[include] = linenum
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index b004cfc7a1..7daa4d7207 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -19,7 +19,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/version.h"
-#include "nvim/eval/typval_encode.h"
#include "nvim/lib/kvec.h"
/// Helper structure for vim_to_object
@@ -327,21 +326,21 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
kv_push(edata->stack, NIL)
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
kv_push(edata->stack, BOOLEAN_OBJ((Boolean)(num)))
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
kv_push(edata->stack, INTEGER_OBJ((Integer)(num)))
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
kv_push(edata->stack, FLOATING_OBJ((Float)(flt)))
-#define TYPVAL_ENCODE_CONV_STRING(str, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
do { \
const size_t len_ = (size_t)(len); \
const char *const str_ = (const char *)(str); \
@@ -354,19 +353,23 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
-#define TYPVAL_ENCODE_CONV_EXT_STRING(str, len, type) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
+ TYPVAL_ENCODE_CONV_NIL(tv)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+ do { \
+ TYPVAL_ENCODE_CONV_NIL(tv); \
+ goto typval_encode_stop_converting_one_item; \
+ } while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
kv_push(edata->stack, \
DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 })))
@@ -381,9 +384,11 @@ static inline void typval_encode_list_start(EncodedData *const edata,
})));
}
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
typval_encode_list_start(edata, (size_t)(len))
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
+
static inline void typval_encode_between_list_items(EncodedData *const edata)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
{
@@ -394,7 +399,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata)
list->data.array.items[list->data.array.size++] = item;
}
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
typval_encode_between_list_items(edata)
static inline void typval_encode_list_end(EncodedData *const edata)
@@ -407,7 +412,7 @@ static inline void typval_encode_list_end(EncodedData *const edata)
#endif
}
-#define TYPVAL_ENCODE_CONV_LIST_END() \
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
typval_encode_list_end(edata)
static inline void typval_encode_dict_start(EncodedData *const edata,
@@ -421,10 +426,12 @@ static inline void typval_encode_dict_start(EncodedData *const edata,
})));
}
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
typval_encode_dict_start(edata, (size_t)(len))
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
+
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
static inline void typval_encode_after_key(EncodedData *const edata)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
@@ -443,7 +450,7 @@ static inline void typval_encode_after_key(EncodedData *const edata)
}
}
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
typval_encode_after_key(edata)
static inline void typval_encode_between_dict_items(EncodedData *const edata)
@@ -456,7 +463,7 @@ static inline void typval_encode_between_dict_items(EncodedData *const edata)
dict->data.dictionary.items[dict->data.dictionary.size++].value = val;
}
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
typval_encode_between_dict_items(edata)
static inline void typval_encode_dict_end(EncodedData *const edata)
@@ -469,34 +476,44 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
#endif
}
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
typval_encode_dict_end(edata)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
TYPVAL_ENCODE_CONV_NIL()
-// object_convert_one_value()
-// encode_vim_to_object()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME object
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE EncodedData *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME edata
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
@@ -510,7 +527,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
Object vim_to_object(typval_T *obj)
{
EncodedData edata = { .stack = KV_INITIAL_VALUE };
- encode_vim_to_object(&edata, obj, "vim_to_object argument");
+ const int evo_ret = encode_vim_to_object(&edata, obj,
+ "vim_to_object argument");
+ (void)evo_ret;
+ assert(evo_ret == OK);
Object ret = kv_A(edata.stack, 0);
assert(kv_size(edata.stack) == 1);
kv_destroy(edata.stack);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 2ca621ad22..a046b2a288 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6445,7 +6445,7 @@ void dict_free(dict_T *d) {
*/
dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET
{
- dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(key));
+ dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1);
#ifndef __clang_analyzer__
STRCPY(di->di_key, key);
#endif
@@ -9251,7 +9251,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
dict_T *d = NULL;
typval_T save_val;
typval_T save_key;
- int rem;
+ int rem = false;
int todo;
char_u *ermsg = (char_u *)(map ? "map()" : "filter()");
char_u *arg_errmsg = (char_u *)(map ? N_("map() argument")
@@ -19042,124 +19042,205 @@ void free_tv(typval_T *varp)
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
tv->vval.v_special = kSpecialVarFalse; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_BOOL(ignored) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
+ TYPVAL_ENCODE_CONV_NIL(tv)
-#define TYPVAL_ENCODE_CONV_NUMBER(ignored) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
- (void)ignored; \
+ (void)num; \
tv->vval.v_number = 0; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(ignored) \
- assert(false)
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
-#define TYPVAL_ENCODE_CONV_FLOAT(ignored) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
tv->vval.v_float = 0; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_STRING(str, ignored) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
- xfree(str); \
+ xfree(buf); \
tv->vval.v_string = NULL; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(ignored1, ignored2)
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(ignored1, ignored2, ignored3)
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+static inline int _nothing_conv_func_start(typval_T *const tv,
+ char_u *const fun)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
+{
+ tv->v_lock = VAR_UNLOCKED;
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt_ = tv->vval.v_partial;
+ if (pt_ != NULL && pt_->pt_refcount > 1) {
+ pt_->pt_refcount--;
+ tv->vval.v_partial = NULL;
+ return OK;
+ }
+ } else {
+ func_unref(fun);
+ if (fun != empty_string) {
+ xfree(fun);
+ }
+ tv->vval.v_string = NULL;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- func_unref(fun); \
- if (fun != empty_string) { \
- xfree(fun); \
+ if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \
+ return OK; \
} \
- tv->vval.v_string = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
- do { \
- partial_unref(pt); \
- pt = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
+{
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt = tv->vval.v_partial;
+ if (pt == NULL) {
+ return;
+ }
+ // Dictionary should already be freed by the time.
+ // If it was not freed then it is a part of the reference cycle.
+ assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID);
+ pt->pt_dict = NULL;
+ // As well as all arguments.
+ pt->pt_argc = 0;
+ assert(pt->pt_refcount <= 1);
+ partial_unref(pt);
+ tv->vval.v_partial = NULL;
+ assert(tv->v_lock == VAR_UNLOCKED);
+ }
+}
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
do { \
list_unref(tv->vval.v_list); \
tv->vval.v_list = NULL; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
do { \
- dict_unref(tv->vval.v_dict); \
- tv->vval.v_dict = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
-
-#define TYPVAL_ENCODE_CONV_LIST_START(ignored) \
- do { \
- if (tv->vval.v_list->lv_refcount > 1) { \
- tv->vval.v_list->lv_refcount--; \
- tv->vval.v_list = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- return OK; \
+ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
+ dict_unref((dict_T *)dict); \
+ *((dict_T **)&dict) = NULL; \
+ if (tv != NULL) { \
+ ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
-
-#define TYPVAL_ENCODE_CONV_LIST_END() \
- do { \
- typval_T *const cur_tv = cur_mpsv->tv; \
- assert(cur_tv->v_type == VAR_LIST); \
- list_unref(cur_tv->vval.v_list); \
- cur_tv->vval.v_list = NULL; \
- cur_tv->v_lock = VAR_UNLOCKED; \
- } while (0)
+static inline int _nothing_conv_real_list_after_start(
+ typval_T *const tv, MPConvStackVal *const mpsv)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ assert(tv != NULL);
+ tv->v_lock = VAR_UNLOCKED;
+ if (tv->vval.v_list->lv_refcount > 1) {
+ tv->vval.v_list->lv_refcount--;
+ tv->vval.v_list = NULL;
+ mpsv->data.l.li = NULL;
+ return OK;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len)
-#define TYPVAL_ENCODE_CONV_DICT_START(ignored) \
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \
do { \
- if (tv->vval.v_dict->dv_refcount > 1) { \
- tv->vval.v_dict->dv_refcount--; \
- tv->vval.v_dict = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- return OK; \
+ if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \
+ goto typval_encode_stop_converting_one_item; \
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(ignored1, ignored2)
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
+static inline void _nothing_conv_list_end(typval_T *const tv)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (tv == NULL) {
+ return;
+ }
+ assert(tv->v_type == VAR_LIST);
+ list_T *const list = tv->vval.v_list;
+ list_unref(list);
+ tv->vval.v_list = NULL;
+}
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv)
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+static inline int _nothing_conv_real_dict_after_start(
+ typval_T *const tv, dict_T **const dictp, const void *const nodictvar,
+ MPConvStackVal *const mpsv)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (tv != NULL) {
+ tv->v_lock = VAR_UNLOCKED;
+ }
+ if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) {
+ (*dictp)->dv_refcount--;
+ *dictp = NULL;
+ mpsv->data.d.todo = 0;
+ return OK;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len)
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \
do { \
- typval_T *const cur_tv = cur_mpsv->tv; \
- assert(cur_tv->v_type == VAR_DICT); \
- dict_unref(cur_tv->vval.v_dict); \
- cur_tv->vval.v_dict = NULL; \
- cur_tv->v_lock = VAR_UNLOCKED; \
+ if (_nothing_conv_real_dict_after_start( \
+ tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \
+ &mpsv) != NOTDONE) { \
+ goto typval_encode_stop_converting_one_item; \
+ } \
} while (0)
-#define TYPVAL_ENCODE_CONV_RECURSE(ignored1, ignored2)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict)
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
-// nothing_convert_one_value()
-// encode_vim_to_nothing()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
+static inline void _nothing_conv_dict_end(typval_T *const tv,
+ dict_T **const dictp,
+ const void *const nodictvar)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if ((const void *)dictp != nodictvar) {
+ dict_unref(*dictp);
+ *dictp = NULL;
+ }
+}
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
+ _nothing_conv_dict_end(tv, (dict_T **)&dict, \
+ (void *)&TYPVAL_ENCODE_NODICT_VAR)
+
+#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type)
+
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME nothing
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
#undef TYPVAL_ENCODE_CONV_NIL
@@ -19170,15 +19251,19 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_DICT_START
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_DICT_END
@@ -19190,7 +19275,9 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
void clear_tv(typval_T *varp)
{
if (varp != NULL && varp->v_type != VAR_UNKNOWN) {
- encode_vim_to_nothing(varp, varp, "clear_tv argument");
+ const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument");
+ (void)evn_ret;
+ assert(evn_ret == OK);
}
}
@@ -21578,9 +21665,12 @@ void func_unref(char_u *name)
fp = find_func(name);
if (fp == NULL) {
#ifdef EXITFREE
- if (!entered_free_all_mem) // NOLINT(readability/braces)
-#endif
+ if (!entered_free_all_mem) {
EMSG2(_(e_intern2), "func_unref()");
+ }
+#else
+ EMSG2(_(e_intern2), "func_unref()");
+#endif
} else {
user_func_unref(fp);
}
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 5af4893975..ee66b7cf09 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -108,9 +108,12 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
{
garray_T msg_ga;
ga_init(&msg_ga, (int)sizeof(char), 80);
- char *const key_msg = _("key %s");
- char *const key_pair_msg = _("key %s at index %i from special map");
- char *const idx_msg = _("index %i");
+ const char *const key_msg = _("key %s");
+ const char *const key_pair_msg = _("key %s at index %i from special map");
+ const char *const idx_msg = _("index %i");
+ const char *const partial_arg_msg = _("partial");
+ const char *const partial_arg_i_msg = _("argument %i");
+ const char *const partial_self_msg = _("partial self dictionary");
for (size_t i = 0; i < kv_size(*mpstack); i++) {
if (i != 0) {
ga_concat(&msg_ga, ", ");
@@ -154,6 +157,29 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
}
break;
}
+ case kMPConvPartial: {
+ switch (v.data.p.stage) {
+ case kMPConvPartialArgs: {
+ assert(false);
+ break;
+ }
+ case kMPConvPartialSelf: {
+ ga_concat(&msg_ga, partial_arg_msg);
+ break;
+ }
+ case kMPConvPartialEnd: {
+ ga_concat(&msg_ga, partial_self_msg);
+ break;
+ }
+ }
+ break;
+ }
+ case kMPConvPartialList: {
+ const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1;
+ vim_snprintf((char *)IObuff, IOSIZE, partial_arg_i_msg, idx);
+ ga_concat(&msg_ga, IObuff);
+ break;
+ }
}
}
EMSG3(msg, objname, (kv_size(*mpstack) == 0
@@ -254,7 +280,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
: OK);
}
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
const char *const buf_ = (const char *) buf; \
if (buf == NULL) { \
@@ -273,19 +299,19 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
- TYPVAL_ENCODE_CONV_STRING(buf, len)
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
+ TYPVAL_ENCODE_CONV_STRING(tv, buf, len)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type)
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
ga_concat(gap, numbuf); \
} while (0)
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
switch (fpclassify(flt_)) { \
@@ -308,103 +334,75 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- ga_concat(gap, "function("); \
- TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \
- ga_append(gap, ')'); \
+ const char *const fun_ = (const char *)(fun); \
+ if (fun_ == NULL) { \
+ EMSG2(_(e_intern2), "string(): NULL function name"); \
+ ga_concat(gap, "function(NULL"); \
+ } else { \
+ ga_concat(gap, "function("); \
+ TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \
+ }\
} while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \
do { \
- int i; \
- ga_concat(gap, "function("); \
- if (pt->pt_name != NULL) { \
- size_t len; \
- char_u *p; \
- len = 3; \
- len += STRLEN(pt->pt_name); \
- for (p = pt->pt_name; *p != NUL; mb_ptr_adv(p)) { \
- if (*p == '\'') { \
- len++; \
- } \
- } \
- char_u *r, *s; \
- s = r = xmalloc(len); \
- if (r != NULL) { \
- *r++ = '\''; \
- for (p = pt->pt_name; *p != NUL; ) { \
- if (*p == '\'') { \
- *r++ = '\''; \
- } \
- MB_COPY_CHAR(p, r); \
- } \
- *r++ = '\''; \
- *r++ = NUL; \
- } \
- ga_concat(gap, s); \
- xfree(s); \
- } \
- if (pt->pt_argc > 0) { \
- ga_concat(gap, ", ["); \
- for (i = 0; i < pt->pt_argc; i++) { \
- if (i > 0) { \
- ga_concat(gap, ", "); \
- } \
- char *tofree = encode_tv2string(&pt->pt_argv[i], NULL); \
- ga_concat(gap, tofree); \
- xfree(tofree); \
- } \
- ga_append(gap, ']'); \
- } \
- if (pt->pt_dict != NULL) { \
- typval_T dtv; \
- ga_concat(gap, ", "); \
- dtv.v_type = VAR_DICT; \
- dtv.vval.v_dict = pt->pt_dict; \
- char *tofree = encode_tv2string(&dtv, NULL); \
- ga_concat(gap, tofree); \
- xfree(tofree); \
- } \
- ga_append(gap, ')'); \
+ if (len != 0) { \
+ ga_concat(gap, ", "); \
+ } \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \
+ do { \
+ if ((ptrdiff_t)len != -1) { \
+ ga_concat(gap, ", "); \
+ } \
} while (0)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv) \
+ ga_append(gap, ')')
+
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
ga_concat(gap, "[]")
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
ga_append(gap, '[')
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
ga_concat(gap, "{}")
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "v:null")
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
ga_concat(gap, ((num)? "v:true": "v:false"))
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num)
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
ga_append(gap, '{')
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
+
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
ga_append(gap, '}')
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
ga_concat(gap, ": ")
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
ga_concat(gap, ", ")
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
-#define TYPVAL_ENCODE_CONV_LIST_END() \
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
ga_append(gap, ']')
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, NULL)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
do { \
@@ -437,9 +435,15 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-// string_convert_one_value()
-// encode_vim_to_string()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME string
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_RECURSE
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
@@ -469,9 +473,15 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
return OK; \
} while (0)
-// echo_convert_one_value()
-// encode_vim_to_echo()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE
+#define TYPVAL_ENCODE_NAME echo
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_RECURSE
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
@@ -489,15 +499,15 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
#undef TYPVAL_ENCODE_CONV_NIL
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "null")
#undef TYPVAL_ENCODE_CONV_BOOL
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
ga_concat(gap, ((num)? "true": "false"))
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
do { \
char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
@@ -505,7 +515,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
} while (0)
#undef TYPVAL_ENCODE_CONV_FLOAT
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
switch (fpclassify(flt_)) { \
@@ -694,7 +704,7 @@ static inline int convert_to_json_string(garray_T *const gap,
}
#undef TYPVAL_ENCODE_CONV_STRING
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
return FAIL; \
@@ -702,25 +712,19 @@ static inline int convert_to_json_string(garray_T *const gap,
} while (0)
#undef TYPVAL_ENCODE_CONV_EXT_STRING
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
xfree(buf); \
EMSG(_("E474: Unable to convert EXT string to JSON")); \
return FAIL; \
} while (0)
-#undef TYPVAL_ENCODE_CONV_FUNC
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
return conv_error(_("E474: Error while dumping %s, %s: " \
"attempt to dump function reference"), \
mpstack, objname)
-#undef TYPVAL_ENCODE_CONV_PARTIAL
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
- return conv_error(_("E474: Error while dumping %s, %s: " \
- "attempt to dump partial"), \
- mpstack, objname)
-
/// Check whether given key can be used in json_encode()
///
/// @param[in] tv Key to check.
@@ -759,8 +763,8 @@ bool encode_check_json_key(const typval_T *const tv)
return true;
}
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key) \
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) \
do { \
if (!encode_check_json_key(&key)) { \
EMSG(_("E474: Invalid key in special dictionary")); \
@@ -768,28 +772,38 @@ bool encode_check_json_key(const typval_T *const tv)
} \
} while (0)
-// json_convert_one_value()
-// encode_vim_to_json()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME json
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
@@ -807,7 +821,10 @@ char *encode_tv2string(typval_T *tv, size_t *len)
{
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- encode_vim_to_string(&ga, tv, "encode_tv2string() argument");
+ const int evs_ret = encode_vim_to_string(&ga, tv,
+ "encode_tv2string() argument");
+ (void)evs_ret;
+ assert(evs_ret == OK);
did_echo_string_emsg = false;
if (len != NULL) {
*len = (size_t) ga.ga_len;
@@ -833,7 +850,9 @@ char *encode_tv2echo(typval_T *tv, size_t *len)
ga_concat(&ga, tv->vval.v_string);
}
} else {
- encode_vim_to_echo(&ga, tv, ":echo argument");
+ const int eve_ret = encode_vim_to_echo(&ga, tv, ":echo argument");
+ (void)eve_ret;
+ assert(eve_ret == OK);
}
if (len != NULL) {
*len = (size_t) ga.ga_len;
@@ -854,16 +873,19 @@ char *encode_tv2json(typval_T *tv, size_t *len)
{
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ const int evj_ret = encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ if (!evj_ret) {
+ ga_clear(&ga);
+ }
did_echo_string_emsg = false;
if (len != NULL) {
- *len = (size_t) ga.ga_len;
+ *len = (size_t)ga.ga_len;
}
ga_append(&ga, '\0');
- return (char *) ga.ga_data;
+ return (char *)ga.ga_data;
}
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
if (buf == NULL) { \
msgpack_pack_bin(packer, 0); \
@@ -874,7 +896,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
do { \
if (buf == NULL) { \
msgpack_pack_str(packer, 0); \
@@ -885,7 +907,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
if (buf == NULL) { \
msgpack_pack_ext(packer, 0, (int8_t) type); \
@@ -896,35 +918,36 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
- msgpack_pack_int64(packer, (int64_t) (num))
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
+ msgpack_pack_int64(packer, (int64_t)(num))
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
- msgpack_pack_double(packer, (double) (flt))
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
+ msgpack_pack_double(packer, (double)(flt))
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
return conv_error(_("E5004: Error while dumping %s, %s: " \
"attempt to dump function reference"), \
mpstack, objname)
-#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
- return conv_error(_("E5004: Error while dumping %s, %s: " \
- "attempt to dump partial"), \
- mpstack, objname)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
msgpack_pack_array(packer, 0)
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
- msgpack_pack_array(packer, (size_t) (len))
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
+ msgpack_pack_array(packer, (size_t)(len))
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
msgpack_pack_map(packer, 0)
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
msgpack_pack_nil(packer)
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
do { \
if ((num)) { \
msgpack_pack_true(packer); \
@@ -933,23 +956,25 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
msgpack_pack_uint64(packer, (num))
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
- msgpack_pack_map(packer, (size_t) (len))
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
+ msgpack_pack_map(packer, (size_t)(len))
+
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
-#define TYPVAL_ENCODE_CONV_DICT_END()
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
-#define TYPVAL_ENCODE_CONV_LIST_END()
+#define TYPVAL_ENCODE_CONV_LIST_END(tv)
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
return conv_error(_("E5005: Unable to dump %s: " \
@@ -958,28 +983,38 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
-// msgpack_convert_one_value()
-// encode_vim_to_msgpack()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
+#define TYPVAL_ENCODE_SCOPE
+#define TYPVAL_ENCODE_NAME msgpack
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE msgpack_packer *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME packer
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_NIL
#undef TYPVAL_ENCODE_CONV_BOOL
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
new file mode 100644
index 0000000000..365eb2dd77
--- /dev/null
+++ b/src/nvim/eval/typval_encode.c.h
@@ -0,0 +1,802 @@
+/// @file eval/typval_encode.c.h
+///
+/// Contains set of macros used to convert (possibly recursive) typval_T into
+/// something else. For these macros to work the following macros must be
+/// defined:
+
+/// @def TYPVAL_ENCODE_CONV_NIL
+/// @brief Macros used to convert NIL value
+///
+/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
+/// is false) and `v:null`.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to special dictionary.
+
+/// @def TYPVAL_ENCODE_CONV_BOOL
+/// @brief Macros used to convert boolean value
+///
+/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
+/// is false) and `v:true`/`v:false`.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param num Boolean value to convert. Value is an expression which
+/// evaluates to some integer.
+
+/// @def TYPVAL_ENCODE_CONV_NUMBER
+/// @brief Macros used to convert integer
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param num Integer to convert, must accept both varnumber_T and int64_t.
+
+/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
+/// @brief Macros used to convert unsigned integer
+///
+/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
+/// defined.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. Points
+/// to a special dictionary.
+/// @param num Integer to convert, must accept uint64_t.
+
+/// @def TYPVAL_ENCODE_CONV_FLOAT
+/// @brief Macros used to convert floating-point number
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param flt Number to convert, must accept float_T.
+
+/// @def TYPVAL_ENCODE_CONV_STRING
+/// @brief Macros used to convert plain string
+///
+/// Is used to convert VAR_STRING objects as well as BIN strings represented as
+/// special dictionary.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+
+/// @def TYPVAL_ENCODE_CONV_STR_STRING
+/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
+///
+/// Is used to convert dictionary keys and STR strings represented as special
+/// dictionaries.
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+
+/// @def TYPVAL_ENCODE_CONV_EXT_STRING
+/// @brief Macros used to convert EXT string
+///
+/// Is used to convert EXT strings represented as special dictionaries. Never
+/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
+/// defined.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. Points
+/// to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+/// @param type EXT type.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_START
+/// @brief Macros used when starting to convert a funcref or a partial
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @param fun Function name. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+/// @brief Macros used before starting to convert partial arguments
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @param len Number of arguments. Zero for absent arguments or when
+/// converting a funcref.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+/// @brief Macros used before starting to convert self dictionary
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @param len Number of arguments. May be zero for empty dictionary or -1 for
+/// missing self dictionary, also when converting function
+/// reference.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_END
+/// @brief Macros used after converting a funcref or a partial
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
+/// @brief Macros used to convert an empty list
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
+/// @brief Macros used to convert an empty dictionary
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_LIST_START
+/// @brief Macros used before starting to convert non-empty list
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param len List length. Is an expression which evaluates to an integer.
+
+/// @def TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
+/// @brief Macros used after pushing list onto the stack
+///
+/// Only used for real list_T* lists, not for special dictionaries or partial
+/// arguments.
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param mpsv Pushed MPConvStackVal value.
+
+/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
+/// @brief Macros used after finishing converting non-last list item
+///
+/// @param tv Pointer to typval where list is stored. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_LIST_END
+/// @brief Macros used after converting non-empty list
+///
+/// @param tv Pointer to typval where list is stored. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_DICT_START
+/// @brief Macros used before starting to convert non-empty dictionary
+///
+/// Only used for real dict_T* dictionaries, not for special dictionaries. Also
+/// used for partial self dictionary.
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+/// @param len Dictionary length. Is an expression which evaluates to an
+/// integer.
+
+/// @def TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
+/// @brief Macros used after pushing dictionary onto the stack
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL.
+/// May not point to a special dictionary.
+/// @param dict Converted dictionary, lvalue.
+/// @param mpsv Pushed MPConvStackVal value.
+
+/// @def TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
+/// @brief Macros used to check special dictionary key
+///
+/// @param label Label for goto in case check was not successfull.
+/// @param key typval_T key to check.
+
+/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
+/// @brief Macros used after finishing converting dictionary key
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
+/// @brief Macros used after finishing converting non-last dictionary value
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_DICT_END
+/// @brief Macros used after converting non-empty dictionary
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_RECURSE
+/// @brief Macros used when self-containing container is detected
+///
+/// @param val Container for which this situation was detected.
+/// @param conv_type Type of the stack entry, @see MPConvStackValType.
+
+/// @def TYPVAL_ENCODE_ALLOW_SPECIALS
+/// @brief Macros that specifies whether special dictionaries are special
+///
+/// Must be something that evaluates to boolean, most likely `true` or `false`.
+/// If it is false then special dictionaries are not treated specially.
+
+/// @def TYPVAL_ENCODE_SCOPE
+/// @brief Scope of the main function: either nothing or `static`
+
+/// @def TYPVAL_ENCODE_NAME
+/// @brief Name of the target converter
+///
+/// After including this file it will define function
+/// `encode_vim_to_{TYPVAL_ENCODE_NAME}` with scope #TYPVAL_ENCODE_SCOPE and
+/// static functions `_typval_encode_{TYPVAL_ENCODE_NAME}_convert_one_value` and
+/// `_typval_encode_{TYPVAL_ENCODE_NAME}_check_self_reference`.
+
+/// @def TYPVAL_ENCODE_FIRST_ARG_TYPE
+/// @brief Type of the first argument, which will be used to return the results
+///
+/// Is expected to be a pointer type.
+
+/// @def TYPVAL_ENCODE_FIRST_ARG_NAME
+/// @brief Name of the first argument
+///
+/// This name will only be used by one of the above macros which are defined by
+/// the caller. Functions defined here do not use first argument directly.
+#ifndef NVIM_EVAL_TYPVAL_ENCODE_C_H
+#define NVIM_EVAL_TYPVAL_ENCODE_C_H
+#undef NVIM_EVAL_TYPVAL_ENCODE_C_H
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "nvim/lib/kvec.h"
+#include "nvim/eval_defs.h"
+#include "nvim/eval/encode.h"
+#include "nvim/func_attr.h"
+#include "nvim/eval/typval_encode.h"
+
+/// Dummy variable used because some macros need lvalue
+///
+/// Must not be written to, if needed one must check that address of the
+/// macros argument is (not) equal to `&TYPVAL_ENCODE_NODICT_VAR`.
+const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL;
+
+static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ void *const val, int *const val_copyID,
+ const MPConvStack *const mpstack, const int copyID,
+ const MPConvStackValType conv_type,
+ const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT
+ REAL_FATTR_ALWAYS_INLINE;
+
+/// Function for checking whether container references itself
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument.
+/// @param[in,out] val Container to check.
+/// @param val_copyID Pointer to the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+/// @param[in] mpstack Stack with values to convert. Read-only, used for error
+/// reporting.
+/// @param[in] copyID CopyID used by the caller.
+/// @param[in] conv_type Type of the conversion, @see MPConvStackValType.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return NOTDONE in case of success, what to return in case of failure.
+static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ void *const val, int *const val_copyID,
+ const MPConvStack *const mpstack, const int copyID,
+ const MPConvStackValType conv_type,
+ const char *const objname)
+{
+ if (*val_copyID == copyID) {
+ TYPVAL_ENCODE_CONV_RECURSE(val, conv_type);
+ return OK;
+ }
+ *val_copyID = copyID;
+ return NOTDONE;
+}
+
+static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
+ typval_T *const tv, const int copyID,
+ const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Convert single value
+///
+/// Only scalar values are converted immediately, everything else is pushed onto
+/// the stack.
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
+/// includer. Only meaningful to macros
+/// defined by the includer.
+/// @param mpstack Stack with values to convert. Values which are not
+/// converted completely by this function (i.e.
+/// non-scalars) are pushed here.
+/// @param cur_mpsv Currently converted value from stack.
+/// @param tv Converted value.
+/// @param[in] copyID CopyID.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return OK in case of success, FAIL in case of failure.
+static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
+ typval_T *const tv, const int copyID,
+ const char *const objname)
+{
+ switch (tv->v_type) {
+ case VAR_STRING: {
+ TYPVAL_ENCODE_CONV_STRING(tv, tv->vval.v_string, tv_strlen(tv));
+ break;
+ }
+ case VAR_NUMBER: {
+ TYPVAL_ENCODE_CONV_NUMBER(tv, tv->vval.v_number);
+ break;
+ }
+ case VAR_FLOAT: {
+ TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float);
+ break;
+ }
+ case VAR_FUNC: {
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string);
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
+ TYPVAL_ENCODE_CONV_FUNC_END(tv);
+ break;
+ }
+ case VAR_PARTIAL: {
+ partial_T *const pt = tv->vval.v_partial;
+ (void)pt;
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : pt->pt_name));
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .type = kMPConvPartial,
+ .tv = tv,
+ .data = {
+ .p = {
+ .stage = kMPConvPartialArgs,
+ .pt = tv->vval.v_partial,
+ },
+ },
+ }));
+ break;
+ }
+ case VAR_LIST: {
+ if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
+ break;
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
+ kMPConvList);
+ TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .type = kMPConvList,
+ .tv = tv,
+ .data = {
+ .l = {
+ .list = tv->vval.v_list,
+ .li = tv->vval.v_list->lv_first,
+ },
+ },
+ }));
+ TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack));
+ break;
+ }
+ case VAR_SPECIAL: {
+ switch (tv->vval.v_special) {
+ case kSpecialVarNull: {
+ TYPVAL_ENCODE_CONV_NIL(tv);
+ break;
+ }
+ case kSpecialVarTrue:
+ case kSpecialVarFalse: {
+ TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_special == kSpecialVarTrue);
+ break;
+ }
+ }
+ break;
+ }
+ case VAR_DICT: {
+ if (tv->vval.v_dict == NULL
+ || tv->vval.v_dict->dv_hashtab.ht_used == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, tv->vval.v_dict);
+ break;
+ }
+ const dictitem_T *type_di;
+ const dictitem_T *val_di;
+ if (TYPVAL_ENCODE_ALLOW_SPECIALS
+ && tv->vval.v_dict->dv_hashtab.ht_used == 2
+ && (type_di = dict_find((dict_T *)tv->vval.v_dict,
+ (char_u *)"_TYPE", -1)) != NULL
+ && type_di->di_tv.v_type == VAR_LIST
+ && (val_di = dict_find((dict_T *)tv->vval.v_dict,
+ (char_u *)"_VAL", -1)) != NULL) {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) {
+ if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(eval_msgpack_type_lists)) {
+ goto _convert_one_value_regular_dict;
+ }
+ switch ((MessagePackType)i) {
+ case kMPNil: {
+ TYPVAL_ENCODE_CONV_NIL(tv);
+ break;
+ }
+ case kMPBoolean: {
+ if (val_di->di_tv.v_type != VAR_NUMBER) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_BOOL(tv, val_di->di_tv.vval.v_number);
+ break;
+ }
+ case kMPInteger: {
+ const list_T *val_list;
+ varnumber_T sign;
+ varnumber_T highest_bits;
+ varnumber_T high_bits;
+ varnumber_T low_bits;
+ // List of 4 integers; first is signed (should be 1 or -1, but
+ // this is not checked), second is unsigned and have at most
+ // one (sign is -1) or two (sign is 1) non-zero bits (number of
+ // bits is not checked), other unsigned and have at most 31
+ // non-zero bits (number of bits is not checked).
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 4
+ || val_list->lv_first->li_tv.v_type != VAR_NUMBER
+ || (sign = val_list->lv_first->li_tv.vval.v_number) == 0
+ || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
+ || (highest_bits =
+ val_list->lv_first->li_next->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
+ || (high_bits =
+ val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_tv.v_type != VAR_NUMBER
+ || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
+ goto _convert_one_value_regular_dict;
+ }
+ uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
+ | (uint64_t)(((uint64_t)high_bits) << 31)
+ | (uint64_t)low_bits);
+ if (sign > 0) {
+ TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number);
+ } else {
+ TYPVAL_ENCODE_CONV_NUMBER(tv, -number);
+ }
+ break;
+ }
+ case kMPFloat: {
+ if (val_di->di_tv.v_type != VAR_FLOAT) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_FLOAT(tv, val_di->di_tv.vval.v_float);
+ break;
+ }
+ case kMPString:
+ case kMPBinary: {
+ const bool is_string = ((MessagePackType)i == kMPString);
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len,
+ &buf)) {
+ goto _convert_one_value_regular_dict;
+ }
+ if (is_string) {
+ TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len);
+ } else {
+ TYPVAL_ENCODE_CONV_STRING(tv, buf, len);
+ }
+ xfree(buf);
+ break;
+ }
+ case kMPArray: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
+ lv_copyID, copyID,
+ kMPConvList);
+ TYPVAL_ENCODE_CONV_LIST_START(tv,
+ val_di->di_tv.vval.v_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvList,
+ .data = {
+ .l = {
+ .list = val_di->di_tv.vval.v_list,
+ .li = val_di->di_tv.vval.v_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPMap: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ list_T *const val_list = val_di->di_tv.vval.v_list;
+ if (val_list == NULL || val_list->lv_len == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
+ break;
+ }
+ for (const listitem_T *li = val_list->lv_first; li != NULL;
+ li = li->li_next) {
+ if (li->li_tv.v_type != VAR_LIST
+ || li->li_tv.vval.v_list->lv_len != 2) {
+ goto _convert_one_value_regular_dict;
+ }
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
+ kMPConvPairs);
+ TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
+ val_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvPairs,
+ .data = {
+ .l = {
+ .list = val_list,
+ .li = val_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPExt: {
+ const list_T *val_list;
+ varnumber_T type;
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 2
+ || (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
+ || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
+ || type < INT8_MIN
+ || (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
+ goto _convert_one_value_regular_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
+ &len, &buf)) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type);
+ xfree(buf);
+ break;
+ }
+ }
+ break;
+ }
+_convert_one_value_regular_dict:
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
+ kMPConvDict);
+ TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
+ tv->vval.v_dict->dv_hashtab.ht_used);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvDict,
+ .data = {
+ .d = {
+ .dict = tv->vval.v_dict,
+ .dictp = &tv->vval.v_dict,
+ .hi = tv->vval.v_dict->dv_hashtab.ht_array,
+ .todo = tv->vval.v_dict->dv_hashtab.ht_used,
+ },
+ },
+ }));
+ TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict,
+ _mp_last(*mpstack));
+ break;
+ }
+ case VAR_UNKNOWN: {
+ EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
+ return FAIL;
+ }
+ }
+typval_encode_stop_converting_one_item:
+ return OK;
+ // Prevent “unused label” warnings.
+ goto typval_encode_stop_converting_one_item;
+}
+
+TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ typval_T *const tv, const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Convert the whole typval
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
+/// includer. Only meaningful to macros
+/// defined by the includer.
+/// @param top_tv Converted value.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return OK in case of success, FAIL in case of failure.
+TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ typval_T *const top_tv, const char *const objname)
+{
+ const int copyID = get_copyID();
+ MPConvStack mpstack;
+ _mp_init(mpstack);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ NULL,
+ top_tv, copyID, objname)
+ == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+/// Label common for this and convert_one_value functions, used for escaping
+/// from macros like TYPVAL_ENCODE_CONV_DICT_START.
+typval_encode_stop_converting_one_item:
+ while (_mp_size(mpstack)) {
+ MPConvStackVal *cur_mpsv = &_mp_last(mpstack);
+ typval_T *tv = NULL;
+ switch (cur_mpsv->type) {
+ case kMPConvDict: {
+ if (!cur_mpsv->data.d.todo) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.d.dict->dv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp);
+ continue;
+ } else if (cur_mpsv->data.d.todo
+ != cur_mpsv->data.d.dict->dv_hashtab.ht_used) {
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv,
+ *cur_mpsv->data.d.dictp);
+ }
+ while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) {
+ cur_mpsv->data.d.hi++;
+ }
+ dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi);
+ cur_mpsv->data.d.todo--;
+ cur_mpsv->data.d.hi++;
+ TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0],
+ strlen((char *)&di->di_key[0]));
+ TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
+ *cur_mpsv->data.d.dictp);
+ tv = &di->di_tv;
+ break;
+ }
+ case kMPConvList: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
+ continue;
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv);
+ }
+ tv = &cur_mpsv->data.l.li->li_tv;
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ break;
+ }
+ case kMPConvPairs: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
+ continue;
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(
+ cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
+ }
+ const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
+ TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(
+ encode_vim_to__error_ret, kv_pair->lv_first->li_tv);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME,
+ &mpstack, cur_mpsv,
+ &kv_pair->lv_first->li_tv,
+ copyID,
+ objname) == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+ TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
+ TYPVAL_ENCODE_NODICT_VAR);
+ tv = &kv_pair->lv_last->li_tv;
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ break;
+ }
+ case kMPConvPartial: {
+ partial_T *const pt = cur_mpsv->data.p.pt;
+ tv = cur_mpsv->tv;
+ switch (cur_mpsv->data.p.stage) {
+ case kMPConvPartialArgs: {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv,
+ pt == NULL ? 0 : pt->pt_argc);
+ cur_mpsv->data.p.stage = kMPConvPartialSelf;
+ if (pt != NULL && pt->pt_argc > 0) {
+ TYPVAL_ENCODE_CONV_LIST_START(NULL, pt->pt_argc);
+ _mp_push(mpstack, ((MPConvStackVal) {
+ .type = kMPConvPartialList,
+ .tv = NULL,
+ .data = {
+ .a = {
+ .arg = pt->pt_argv,
+ .argv = pt->pt_argv,
+ .todo = (size_t)pt->pt_argc,
+ },
+ },
+ }));
+ }
+ break;
+ }
+ case kMPConvPartialSelf: {
+ cur_mpsv->data.p.stage = kMPConvPartialEnd;
+ dict_T *const dict = pt == NULL ? NULL : pt->pt_dict;
+ if (dict != NULL) {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, dict->dv_hashtab.ht_used);
+ if (dict->dv_hashtab.ht_used == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict);
+ continue;
+ }
+ const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_NAME,
+ dict, &dict->dv_copyID, &mpstack, copyID, kMPConvDict,
+ objname);
+ if (te_csr_ret != NOTDONE) {
+ if (te_csr_ret == FAIL) {
+ goto encode_vim_to__error_ret;
+ } else {
+ continue;
+ }
+ }
+ TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict,
+ dict->dv_hashtab.ht_used);
+ _mp_push(mpstack, ((MPConvStackVal) {
+ .type = kMPConvDict,
+ .tv = NULL,
+ .data = {
+ .d = {
+ .dict = dict,
+ .dictp = &pt->pt_dict,
+ .hi = dict->dv_hashtab.ht_array,
+ .todo = dict->dv_hashtab.ht_used,
+ },
+ },
+ }));
+ TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict,
+ _mp_last(mpstack));
+ } else {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
+ }
+ break;
+ }
+ case kMPConvPartialEnd: {
+ TYPVAL_ENCODE_CONV_FUNC_END(tv);
+ (void)_mp_pop(mpstack);
+ break;
+ }
+ }
+ continue;
+ }
+ case kMPConvPartialList: {
+ if (!cur_mpsv->data.a.todo) {
+ (void)_mp_pop(mpstack);
+ TYPVAL_ENCODE_CONV_LIST_END(NULL);
+ continue;
+ } else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) {
+ TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(NULL);
+ }
+ tv = cur_mpsv->data.a.arg++;
+ cur_mpsv->data.a.todo--;
+ break;
+ }
+ }
+ assert(tv != NULL);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ cur_mpsv, tv, copyID, objname)
+ == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+ }
+ _mp_destroy(mpstack);
+ return OK;
+encode_vim_to__error_ret:
+ _mp_destroy(mpstack);
+ return FAIL;
+ // Prevent “unused label” warnings.
+ goto typval_encode_stop_converting_one_item;
+}
+#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index b79158b30c..ba325b8f55 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -1,161 +1,35 @@
-/// @file eval/typval_convert.h
+/// @file eval/typval_encode.h
///
-/// Contains set of macros used to convert (possibly recursive) typval_T into
-/// something else. For these macros to work the following macros must be
-/// defined:
-
-/// @def TYPVAL_ENCODE_CONV_NIL
-/// @brief Macros used to convert NIL value
-///
-/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
-/// is false) and `v:null`. Accepts no arguments, but still must be
-/// a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_BOOL
-/// @brief Macros used to convert boolean value
-///
-/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
-/// is false) and `v:true`/`v:false`.
-///
-/// @param num Boolean value to convert. Value is an expression which
-/// evaluates to some integer.
-
-/// @def TYPVAL_ENCODE_CONV_NUMBER
-/// @brief Macros used to convert integer
-///
-/// @param num Integer to convert, must accept both varnumber_T and int64_t.
-
-/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
-/// @brief Macros used to convert unsigned integer
-///
-/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
-/// defined.
-///
-/// @param num Integer to convert, must accept uint64_t.
-
-/// @def TYPVAL_ENCODE_CONV_FLOAT
-/// @brief Macros used to convert floating-point number
-///
-/// @param flt Number to convert, must accept float_T.
-
-/// @def TYPVAL_ENCODE_CONV_STRING
-/// @brief Macros used to convert plain string
-///
-/// Is used to convert VAR_STRING objects as well as BIN strings represented as
-/// special dictionary.
-///
-/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
-/// @param len String length.
-
-/// @def TYPVAL_ENCODE_CONV_STR_STRING
-/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
-///
-/// Is used to convert dictionary keys and STR strings represented as special
-/// dictionaries.
-
-/// @def TYPVAL_ENCODE_CONV_EXT_STRING
-/// @brief Macros used to convert EXT string
-///
-/// Is used to convert EXT strings represented as special dictionaries. Never
-/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
-/// defined.
-///
-/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
-/// @param len String length.
-/// @param type EXT type.
-
-/// @def TYPVAL_ENCODE_CONV_FUNC
-/// @brief Macros used to convert a function reference
-///
-/// @param fun Function name.
-
-/// @def TYPVAL_ENCODE_CONV_PARTIAL
-/// @brief Macros used to convert a partial
-///
-/// @param pt Partial name.
-
-/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
-/// @brief Macros used to convert an empty list
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
-/// @brief Macros used to convert an empty dictionary
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_START
-/// @brief Macros used before starting to convert non-empty list
-///
-/// @param len List length. Is an expression which evaluates to an integer.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
-/// @brief Macros used after finishing converting non-last list item
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_END
-/// @brief Macros used after converting non-empty list
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_START
-/// @brief Macros used before starting to convert non-empty dictionary
-///
-/// @param len Dictionary length. Is an expression which evaluates to an
-/// integer.
-
-/// @def TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
-/// @brief Macros used to check special dictionary key
-///
-/// @param label Label for goto in case check was not successfull.
-/// @param key typval_T key to check.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
-/// @brief Macros used after finishing converting dictionary key
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-/// @brief Macros used after finishing converting non-last dictionary value
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_END
-/// @brief Macros used after converting non-empty dictionary
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_RECURSE
-/// @brief Macros used when self-containing container is detected
-///
-/// @param val Container for which this situation was detected.
-/// @param conv_type Type of the stack entry, @see MPConvStackValType.
-
-/// @def TYPVAL_ENCODE_ALLOW_SPECIALS
-/// @brief Macros that specifies whether special dictionaries are special
-///
-/// Must be something that evaluates to boolean, most likely `true` or `false`.
-/// If it is false then special dictionaries are not treated specially.
+/// Contains common definitions for eval/typval_encode.c.h. Most of time should
+/// not be included directly.
#ifndef NVIM_EVAL_TYPVAL_ENCODE_H
#define NVIM_EVAL_TYPVAL_ENCODE_H
#include <stddef.h>
#include <inttypes.h>
+#include <string.h>
#include <assert.h>
#include "nvim/lib/kvec.h"
#include "nvim/eval_defs.h"
-#include "nvim/eval/encode.h"
#include "nvim/func_attr.h"
/// Type of the stack entry
typedef enum {
- kMPConvDict, ///< Convert dict_T *dictionary.
- kMPConvList, ///< Convert list_T *list.
+ kMPConvDict, ///< Convert dict_T *dictionary.
+ kMPConvList, ///< Convert list_T *list.
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
+ kMPConvPartial, ///< Convert partial_T* partial.
+ kMPConvPartialList, ///< Convert argc/argv pair coming from a partial.
} MPConvStackValType;
+/// Stage at which partial is being converted
+typedef enum {
+ kMPConvPartialArgs, ///< About to convert arguments.
+ kMPConvPartialSelf, ///< About to convert self dictionary.
+ kMPConvPartialEnd, ///< Already converted everything.
+} MPConvPartialStage;
+
/// Structure representing current VimL to messagepack conversion state
typedef struct {
MPConvStackValType type; ///< Type of the stack entry.
@@ -163,6 +37,9 @@ typedef struct {
union {
struct {
dict_T *dict; ///< Currently converted dictionary.
+ dict_T **dictp; ///< Location where that dictionary is stored.
+ ///< Normally it is &.tv->vval.v_dict, but not when
+ ///< converting partials.
hashitem_T *hi; ///< Currently converted dictionary item.
size_t todo; ///< Amount of items left to process.
} d; ///< State of dictionary conversion.
@@ -170,6 +47,15 @@ typedef struct {
list_T *list; ///< Currently converted list.
listitem_T *li; ///< Currently converted list item.
} l; ///< State of list or generic mapping conversion.
+ struct {
+ MPConvPartialStage stage; ///< Stage at which partial is being converted.
+ partial_T *pt; ///< Currently converted partial.
+ } p; ///< State of partial conversion.
+ struct {
+ typval_T *arg; ///< Currently converted argument.
+ typval_T *argv; ///< Start of the argument list.
+ size_t todo; ///< Number of items left to process.
+ } a; ///< State of list or generic mapping conversion.
} data; ///< Data to convert.
} MPConvStackVal;
@@ -184,21 +70,9 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
#define _mp_pop kv_pop
#define _mp_last kv_last
-/// Code for checking whether container references itself
-///
-/// @param[in,out] val Container to check.
-/// @param copyID_attr Name of the container attribute that holds copyID.
-/// After checking whether value of this attribute is
-/// copyID (variable) it is set to copyID.
-/// @param conv_type Type of the conversion, @see MPConvStackValType.
-#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
- do { \
- if ((val)->copyID_attr == copyID) { \
- TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \
- return OK; \
- } \
- (val)->copyID_attr = copyID; \
- } while (0)
+static inline size_t tv_strlen(const typval_T *const tv)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT
+ REAL_FATTR_NONNULL_ALL;
/// Length of the string stored in typval_T
///
@@ -208,8 +82,6 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
/// @return Length of the string stored in typval_T, including 0 for NULL
/// string.
static inline size_t tv_strlen(const typval_T *const tv)
- FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
- FUNC_ATTR_NONNULL_ALL
{
assert(tv->v_type == VAR_STRING);
return (tv->vval.v_string == NULL
@@ -217,363 +89,57 @@ static inline size_t tv_strlen(const typval_T *const tv)
: strlen((char *) tv->vval.v_string));
}
-/// Define functions to convert a VimL value:
-/// `{name}_convert_one_value(...)`
-/// `encode_vim_to_{name}(...)`
+/// Code for checking whether container references itself
///
-/// @param scope Scope of the main function: either nothing or `static`.
-/// @param name Name of the target converter.
-/// @param firstargtype Type of the first argument. It will be used to return
-/// the results.
-/// @param firstargname Name of the first argument.
-#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \
- firstargname) \
-/* Returns OK or FAIL */ \
-static int name##_convert_one_value(firstargtype firstargname, \
- MPConvStack *const mpstack, \
- typval_T *const tv, \
- const int copyID, \
- const char *const objname) \
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- switch (tv->v_type) { \
- case VAR_STRING: { \
- TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv)); \
- break; \
- } \
- case VAR_NUMBER: { \
- TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number); \
- break; \
- } \
- case VAR_FLOAT: { \
- TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float); \
- break; \
- } \
- case VAR_FUNC: { \
- TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \
- break; \
- } \
- case VAR_PARTIAL: { \
- TYPVAL_ENCODE_CONV_PARTIAL(tv->vval.v_partial); \
- break; \
- } \
- case VAR_LIST: { \
- if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
- break; \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, \
- kMPConvList); \
- TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .type = kMPConvList, \
- .tv = tv, \
- .data = { \
- .l = { \
- .list = tv->vval.v_list, \
- .li = tv->vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_SPECIAL: { \
- switch (tv->vval.v_special) { \
- case kSpecialVarNull: { \
- TYPVAL_ENCODE_CONV_NIL(); \
- break; \
- } \
- case kSpecialVarTrue: \
- case kSpecialVarFalse: { \
- TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \
- break; \
- } \
- } \
- break; \
- } \
- case VAR_DICT: { \
- if (tv->vval.v_dict == NULL \
- || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
- break; \
- } \
- const dictitem_T *type_di; \
- const dictitem_T *val_di; \
- if (TYPVAL_ENCODE_ALLOW_SPECIALS \
- && tv->vval.v_dict->dv_hashtab.ht_used == 2 \
- && (type_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_TYPE", -1)) != NULL \
- && type_di->di_tv.v_type == VAR_LIST \
- && (val_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_VAL", -1)) != NULL) { \
- size_t i; \
- for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \
- if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \
- break; \
- } \
- } \
- if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- switch ((MessagePackType) i) { \
- case kMPNil: { \
- TYPVAL_ENCODE_CONV_NIL(); \
- break; \
- } \
- case kMPBoolean: { \
- if (val_di->di_tv.v_type != VAR_NUMBER) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_BOOL(val_di->di_tv.vval.v_number); \
- break; \
- } \
- case kMPInteger: { \
- const list_T *val_list; \
- varnumber_T sign; \
- varnumber_T highest_bits; \
- varnumber_T high_bits; \
- varnumber_T low_bits; \
- /* List of 4 integers; first is signed (should be 1 or -1, but */ \
- /* this is not checked), second is unsigned and have at most */ \
- /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
- /* bits is not checked), other unsigned and have at most 31 */ \
- /* non-zero bits (number of bits is not checked).*/ \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 4 \
- || val_list->lv_first->li_tv.v_type != VAR_NUMBER \
- || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
- || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
- || (highest_bits = \
- val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
- || (high_bits = \
- val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_tv.v_type != VAR_NUMBER \
- || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
- | (uint64_t) (((uint64_t) high_bits) << 31) \
- | (uint64_t) low_bits); \
- if (sign > 0) { \
- TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(number); \
- } else { \
- TYPVAL_ENCODE_CONV_NUMBER(-number); \
- } \
- break; \
- } \
- case kMPFloat: { \
- if (val_di->di_tv.v_type != VAR_FLOAT) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_FLOAT(val_di->di_tv.vval.v_float); \
- break; \
- } \
- case kMPString: \
- case kMPBinary: { \
- const bool is_string = ((MessagePackType) i == kMPString); \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \
- &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- if (is_string) { \
- TYPVAL_ENCODE_CONV_STR_STRING(buf, len); \
- } else { \
- TYPVAL_ENCODE_CONV_STRING(buf, len); \
- } \
- xfree(buf); \
- break; \
- } \
- case kMPArray: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, \
- lv_copyID, kMPConvList); \
- TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvList, \
- .data = { \
- .l = { \
- .list = val_di->di_tv.vval.v_list, \
- .li = val_di->di_tv.vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPMap: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- list_T *const val_list = val_di->di_tv.vval.v_list; \
- if (val_list == NULL || val_list->lv_len == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
- break; \
- } \
- for (const listitem_T *li = val_list->lv_first; li != NULL; \
- li = li->li_next) { \
- if (li->li_tv.v_type != VAR_LIST \
- || li->li_tv.vval.v_list->lv_len != 2) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, \
- kMPConvPairs); \
- TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvPairs, \
- .data = { \
- .l = { \
- .list = val_list, \
- .li = val_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPExt: { \
- const list_T *val_list; \
- varnumber_T type; \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 2 \
- || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
- || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
- || type < INT8_MIN \
- || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
- &len, &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type); \
- xfree(buf); \
- break; \
- } \
- } \
- break; \
- } \
-name##_convert_one_value_regular_dict: \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, \
- kMPConvDict); \
- TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvDict, \
- .data = { \
- .d = { \
- .dict = tv->vval.v_dict, \
- .hi = tv->vval.v_dict->dv_hashtab.ht_array, \
- .todo = tv->vval.v_dict->dv_hashtab.ht_used, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_UNKNOWN: { \
- EMSG2(_(e_intern2), #name "_convert_one_value()"); \
- return FAIL; \
- } \
- } \
- return OK; \
-} \
-\
-scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
- const char *const objname) \
- FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- const int copyID = get_copyID(); \
- MPConvStack mpstack; \
- _mp_init(mpstack); \
- if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
- == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- while (_mp_size(mpstack)) { \
- MPConvStackVal *cur_mpsv = &_mp_last(mpstack); \
- typval_T *cur_tv = NULL; \
- switch (cur_mpsv->type) { \
- case kMPConvDict: { \
- if (!cur_mpsv->data.d.todo) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_DICT_END(); \
- continue; \
- } else if (cur_mpsv->data.d.todo \
- != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \
- } \
- while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \
- cur_mpsv->data.d.hi++; \
- } \
- dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \
- cur_mpsv->data.d.todo--; \
- cur_mpsv->data.d.hi++; \
- TYPVAL_ENCODE_CONV_STR_STRING(&di->di_key[0], \
- strlen((char *) &di->di_key[0])); \
- TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \
- cur_tv = &di->di_tv; \
- break; \
- } \
- case kMPConvList: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_LIST_END(); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(); \
- } \
- cur_tv = &cur_mpsv->data.l.li->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
- } \
- case kMPConvPairs: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_DICT_END(); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \
- } \
- const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
- TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK( \
- encode_vim_to_##name##_error_ret, kv_pair->lv_first->li_tv); \
- if (name##_convert_one_value(firstargname, &mpstack, \
- &kv_pair->lv_first->li_tv, copyID, \
- objname) == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \
- cur_tv = &kv_pair->lv_last->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
+/// @param[in,out] val Container to check.
+/// @param copyID_attr Name of the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+/// @param[in] copyID CopyID used by the caller.
+/// @param conv_type Type of the conversion, @see MPConvStackValType.
+#define _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val, copyID_attr, copyID, \
+ conv_type) \
+ do { \
+ const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( \
+ TYPVAL_ENCODE_FIRST_ARG_NAME, \
+ (val), &(val)->copyID_attr, mpstack, copyID, conv_type, objname); \
+ if (te_csr_ret != NOTDONE) { \
+ return te_csr_ret; \
} \
- } \
- assert(cur_tv != NULL); \
- if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
- objname) == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- } \
- _mp_destroy(mpstack); \
- return OK; \
-encode_vim_to_##name##_error_ret: \
- _mp_destroy(mpstack); \
- return FAIL; \
-}
+ } while (0)
+
+#define _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) \
+ pref##name##suf
+#define _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, name, suf) \
+ _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf)
+
+/// Construct function name, possibly using macros
+///
+/// Is used to expand macros that may appear in arguments.
+///
+/// @note Expands all arguments, even if only one is needed.
+///
+/// @param[in] pref Prefix.
+/// @param[in] suf Suffix.
+///
+/// @return Concat: pref + #TYPVAL_ENCODE_NAME + suf.
+#define _TYPVAL_ENCODE_FUNC_NAME(pref, suf) \
+ _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, TYPVAL_ENCODE_NAME, suf)
+
+/// Self reference checker function name
+#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE \
+ _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _check_self_reference)
+
+/// Entry point function name
+#define _TYPVAL_ENCODE_ENCODE \
+ _TYPVAL_ENCODE_FUNC_NAME(encode_vim_to_, )
+
+/// Name of the …convert_one_value function
+#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \
+ _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _convert_one_value)
+
+/// Name of the dummy const dict_T *const variable
+#define TYPVAL_ENCODE_NODICT_VAR \
+ _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _nodict_var)
#endif // NVIM_EVAL_TYPVAL_ENCODE_H
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 79a0b5849f..eccece7ac7 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1374,7 +1374,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
}
continue;
}
- if (!checkforcmd(&ea.cmd, "noswapfile", 6)) {
+ if (!checkforcmd(&ea.cmd, "noswapfile", 3)) {
break;
}
cmdmod.noswapfile = true;
@@ -1711,7 +1711,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
xfree(p);
// If the autocommands did something and didn't cause an error, try
// finding the command again.
- p = (ret && !aborting()) ? find_command(&ea, NULL) : NULL;
+ p = (ret && !aborting()) ? find_command(&ea, NULL) : ea.cmd;
}
if (p == NULL) {
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 529b48adbd..bbfa56dfbf 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -5652,13 +5652,14 @@ static event_T event_name2nr(char_u *start, char_u **end)
int i;
int len;
- /* the event name ends with end of line, a blank or a comma */
- for (p = start; *p && !ascii_iswhite(*p) && *p != ','; ++p)
- ;
- for (i = 0; event_names[i].name != NULL; ++i) {
- len = (int) event_names[i].len;
- if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0)
+ // the event name ends with end of line, '|', a blank or a comma */
+ for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) {
+ }
+ for (i = 0; event_names[i].name != NULL; i++) {
+ len = (int)event_names[i].len;
+ if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) {
break;
+ }
}
if (*p == ',')
++p;
@@ -5700,7 +5701,7 @@ find_end_event (
}
pat = arg + 1;
} else {
- for (pat = arg; *pat && !ascii_iswhite(*pat); pat = p) {
+ for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) {
if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS) {
if (have_group)
EMSG2(_("E216: No such event: %s"), pat);
@@ -5815,8 +5816,9 @@ void au_event_restore(char_u *old_ei)
*
* Mostly a {group} argument can optionally appear before <event>.
*/
-void do_autocmd(char_u *arg, int forceit)
+void do_autocmd(char_u *arg_in, int forceit)
{
+ char_u *arg = arg_in;
char_u *pat;
char_u *envpat = NULL;
char_u *cmd;
@@ -5825,10 +5827,13 @@ void do_autocmd(char_u *arg, int forceit)
int nested = FALSE;
int group;
- /*
- * Check for a legal group name. If not, use AUGROUP_ALL.
- */
- group = au_get_grouparg(&arg);
+ if (*arg == '|') {
+ arg = (char_u *)"";
+ group = AUGROUP_ALL; // no argument, use all groups
+ } else {
+ // Check for a legal group name. If not, use AUGROUP_ALL.
+ group = au_get_grouparg(&arg);
+ }
/*
* Scan over the events.
@@ -5838,50 +5843,54 @@ void do_autocmd(char_u *arg, int forceit)
if (pat == NULL)
return;
- /*
- * Scan over the pattern. Put a NUL at the end.
- */
pat = skipwhite(pat);
- cmd = pat;
- while (*cmd && (!ascii_iswhite(*cmd) || cmd[-1] == '\\'))
- cmd++;
- if (*cmd)
- *cmd++ = NUL;
-
- /* Expand environment variables in the pattern. Set 'shellslash', we want
- * forward slashes here. */
- if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) {
+ if (*pat == '|') {
+ pat = (char_u *)"";
+ cmd = (char_u *)"";
+ } else {
+ // Scan over the pattern. Put a NUL at the end.
+ cmd = pat;
+ while (*cmd && (!ascii_iswhite(*cmd) || cmd[-1] == '\\')) {
+ cmd++;
+ }
+ if (*cmd) {
+ *cmd++ = NUL;
+ }
+
+ // Expand environment variables in the pattern. Set 'shellslash', we want
+ // forward slashes here.
+ if (vim_strchr(pat, '$') != NULL || vim_strchr(pat, '~') != NULL) {
#ifdef BACKSLASH_IN_FILENAME
- int p_ssl_save = p_ssl;
+ int p_ssl_save = p_ssl;
- p_ssl = TRUE;
+ p_ssl = true;
#endif
- envpat = expand_env_save(pat);
+ envpat = expand_env_save(pat);
#ifdef BACKSLASH_IN_FILENAME
- p_ssl = p_ssl_save;
+ p_ssl = p_ssl_save;
#endif
- if (envpat != NULL)
- pat = envpat;
- }
+ if (envpat != NULL) {
+ pat = envpat;
+ }
+ }
- /*
- * Check for "nested" flag.
- */
- cmd = skipwhite(cmd);
- if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) {
- nested = TRUE;
- cmd = skipwhite(cmd + 6);
- }
+ // Check for "nested" flag.
+ cmd = skipwhite(cmd);
+ if (*cmd != NUL && STRNCMP(cmd, "nested", 6) == 0
+ && ascii_iswhite(cmd[6])) {
+ nested = true;
+ cmd = skipwhite(cmd + 6);
+ }
- /*
- * Find the start of the commands.
- * Expand <sfile> in it.
- */
- if (*cmd != NUL) {
- cmd = expand_sfile(cmd);
- if (cmd == NULL) /* some error */
- return;
- need_free = TRUE;
+ // Find the start of the commands.
+ // Expand <sfile> in it.
+ if (*cmd != NUL) {
+ cmd = expand_sfile(cmd);
+ if (cmd == NULL) { // some error
+ return;
+ }
+ need_free = true;
+ }
}
/*
@@ -5895,16 +5904,17 @@ void do_autocmd(char_u *arg, int forceit)
/*
* Loop over the events.
*/
- last_event = (event_T)-1; /* for listing the event name */
- last_group = AUGROUP_ERROR; /* for listing the group name */
- if (*arg == '*' || *arg == NUL) {
+ last_event = (event_T)-1; // for listing the event name
+ last_group = AUGROUP_ERROR; // for listing the group name
+ if (*arg == '*' || *arg == NUL || *arg == '|') {
for (event = (event_T)0; (int)event < (int)NUM_EVENTS;
- event = (event_T)((int)event + 1))
- if (do_autocmd_event(event, pat,
- nested, cmd, forceit, group) == FAIL)
+ event = (event_T)((int)event + 1)) {
+ if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
break;
+ }
+ }
} else {
- while (*arg && !ascii_iswhite(*arg)) {
+ while (*arg && *arg != '|' && !ascii_iswhite(*arg)) {
event_T event = event_name2nr(arg, &arg);
assert(event < NUM_EVENTS);
if (do_autocmd_event(event, pat, nested, cmd, forceit, group) == FAIL) {
@@ -5931,7 +5941,8 @@ static int au_get_grouparg(char_u **argp)
char_u *arg = *argp;
int group = AUGROUP_ALL;
- p = skiptowhite(arg);
+ for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) {
+ }
if (p > arg) {
group_name = vim_strnsave(arg, (int)(p - arg));
group = au_find_group(group_name);
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 872ff8d5b8..5ee04ad982 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -409,10 +409,6 @@ EXTERN struct caller_scope {
} provider_caller_scope;
EXTERN int provider_call_nesting INIT(= 0);
-/* Magic number used for hashitem "hi_key" value indicating a deleted item.
- * Only the address is used. */
-EXTERN char_u hash_removed;
-
EXTERN int t_colors INIT(= 256); // int value of T_CCO
@@ -636,10 +632,6 @@ EXTERN int exiting INIT(= FALSE);
/* TRUE when planning to exit Vim. Might
* still keep on running if there is a changed
* buffer. */
-#if defined(EXITFREE)
-// true when in or after free_all_mem()
-EXTERN bool entered_free_all_mem INIT(= false);
-#endif
// volatile because it is used in signal handler deathtrap().
EXTERN volatile int full_screen INIT(= false);
// TRUE when doing full-screen output
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index fa4077f22f..376f33e23e 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -36,6 +36,8 @@
# include "hashtab.c.generated.h"
#endif
+char hash_removed;
+
/// Initialize an empty hash table.
void hash_init(hashtab_T *ht)
{
@@ -380,3 +382,13 @@ hash_T hash_hash(char_u *key)
return hash;
}
+
+/// Function to get HI_KEY_REMOVED value
+///
+/// Used for testing because luajit ffi does not allow getting addresses of
+/// globals.
+const char_u *_hash_key_removed(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return HI_KEY_REMOVED;
+}
diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h
index 7233d8c47c..0da2b13f2e 100644
--- a/src/nvim/hashtab.h
+++ b/src/nvim/hashtab.h
@@ -5,14 +5,19 @@
#include "nvim/types.h"
+/// Magic number used for hashitem "hi_key" value indicating a deleted item
+///
+/// Only the address is used.
+extern char hash_removed;
+
/// Type for hash number (hash calculation result).
typedef size_t hash_T;
/// The address of "hash_removed" is used as a magic number
/// for hi_key to indicate a removed item.
-#define HI_KEY_REMOVED &hash_removed
+#define HI_KEY_REMOVED ((char_u *)&hash_removed)
#define HASHITEM_EMPTY(hi) ((hi)->hi_key == NULL \
- || (hi)->hi_key == &hash_removed)
+ || (hi)->hi_key == (char_u *)&hash_removed)
/// A hastable item.
///
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 1884d55999..92ead873ae 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -17,16 +17,41 @@
// Force je_ prefix on jemalloc functions.
# define JEMALLOC_NO_DEMANGLE
# include <jemalloc/jemalloc.h>
-# define malloc(size) je_malloc(size)
-# define calloc(count, size) je_calloc(count, size)
-# define realloc(ptr, size) je_realloc(ptr, size)
-# define free(ptr) je_free(ptr)
+#endif
+
+#ifdef UNIT_TESTING
+# define malloc(size) mem_malloc(size)
+# define calloc(count, size) mem_calloc(count, size)
+# define realloc(ptr, size) mem_realloc(ptr, size)
+# define free(ptr) mem_free(ptr)
+# ifdef HAVE_JEMALLOC
+MemMalloc mem_malloc = &je_malloc;
+MemFree mem_free = &je_free;
+MemCalloc mem_calloc = &je_calloc;
+MemRealloc mem_realloc = &je_realloc;
+# else
+MemMalloc mem_malloc = &malloc;
+MemFree mem_free = &free;
+MemCalloc mem_calloc = &calloc;
+MemRealloc mem_realloc = &realloc;
+# endif
+#else
+# ifdef HAVE_JEMALLOC
+# define malloc(size) je_malloc(size)
+# define calloc(count, size) je_calloc(count, size)
+# define realloc(ptr, size) je_realloc(ptr, size)
+# define free(ptr) je_free(ptr)
+# endif
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "memory.c.generated.h"
#endif
+#ifdef EXITFREE
+bool entered_free_all_mem = false;
+#endif
+
/// Try to free memory. Used when trying to recover from out of memory errors.
/// @see {xmalloc}
void try_to_free_memory(void)
@@ -353,15 +378,15 @@ char *xstpncpy(char *restrict dst, const char *restrict src, size_t maxlen)
size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size)
FUNC_ATTR_NONNULL_ALL
{
- size_t ret = strlen(src);
+ size_t ret = strlen(src);
- if (size) {
- size_t len = (ret >= size) ? size - 1 : ret;
- memcpy(dst, src, len);
- dst[len] = '\0';
- }
+ if (size) {
+ size_t len = (ret >= size) ? size - 1 : ret;
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ }
- return ret;
+ return ret;
}
/// strdup() wrapper
@@ -371,6 +396,7 @@ size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size)
/// @return pointer to a copy of the string
char *xstrdup(const char *str)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
+ FUNC_ATTR_NONNULL_ALL
{
return xmemdupz(str, strlen(str));
}
@@ -401,6 +427,7 @@ void *xmemrchr(const void *src, uint8_t c, size_t len)
/// @return pointer to a copy of the string
char *xstrndup(const char *str, size_t len)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
+ FUNC_ATTR_NONNULL_ALL
{
char *p = memchr(str, '\0', len);
return xmemdupz(str, p ? (size_t)(p - str) : len);
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index 62cc78360c..250ac3e08f 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -1,9 +1,41 @@
#ifndef NVIM_MEMORY_H
#define NVIM_MEMORY_H
+#include <stdbool.h> // for bool
#include <stdint.h> // for uint8_t
#include <stddef.h> // for size_t
-#include <time.h> // for time_t
+#include <time.h> // for time_t
+
+/// `malloc()` function signature
+typedef void *(*MemMalloc)(size_t);
+
+/// `free()` function signature
+typedef void (*MemFree)(void *);
+
+/// `calloc()` function signature
+typedef void *(*MemCalloc)(size_t, size_t);
+
+/// `realloc()` function signature
+typedef void *(*MemRealloc)(void *, size_t);
+
+#ifdef UNIT_TESTING
+/// When unit testing: pointer to the `malloc()` function, may be altered
+extern MemMalloc mem_malloc;
+
+/// When unit testing: pointer to the `free()` function, may be altered
+extern MemFree mem_free;
+
+/// When unit testing: pointer to the `calloc()` function, may be altered
+extern MemCalloc mem_calloc;
+
+/// When unit testing: pointer to the `realloc()` function, may be altered
+extern MemRealloc mem_realloc;
+#endif
+
+#ifdef EXITFREE
+/// Indicates that free_all_mem function was or is running
+extern bool entered_free_all_mem;
+#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "memory.h.generated.h"
diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c
index 2205ad0958..8ce2ecf4f4 100644
--- a/src/nvim/os/time.c
+++ b/src/nvim/os/time.c
@@ -7,6 +7,7 @@
#include <uv.h>
#include "nvim/os/time.h"
+#include "nvim/os/input.h"
#include "nvim/event/loop.h"
#include "nvim/vim.h"
#include "nvim/main.h"
@@ -34,10 +35,10 @@ uint64_t os_hrtime(void)
return uv_hrtime();
}
-/// Sleeps for a certain amount of milliseconds
+/// Sleeps for a certain amount of milliseconds.
///
/// @param milliseconds Number of milliseconds to sleep
-/// @param ignoreinput If true, allow a SIGINT to interrupt us
+/// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt.
void os_delay(uint64_t milliseconds, bool ignoreinput)
{
if (ignoreinput) {
@@ -46,26 +47,42 @@ void os_delay(uint64_t milliseconds, bool ignoreinput)
}
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int);
} else {
- os_microdelay(milliseconds * 1000);
+ os_microdelay(milliseconds * 1000u, ignoreinput);
}
}
-/// Sleeps for a certain amount of microseconds
+/// Sleeps for a certain amount of microseconds.
///
-/// @param microseconds Number of microseconds to sleep
-void os_microdelay(uint64_t microseconds)
+/// @param ms Number of microseconds to sleep.
+/// @param ignoreinput If true, ignore all input (including SIGINT/CTRL-C).
+/// If false, waiting is aborted on any input.
+void os_microdelay(uint64_t ms, bool ignoreinput)
{
- uint64_t elapsed = 0;
- uint64_t ns = microseconds * 1000; // convert to nanoseconds
+ uint64_t elapsed = 0u;
uint64_t base = uv_hrtime();
+ // Convert microseconds to nanoseconds, or UINT64_MAX on overflow.
+ const uint64_t ns = (ms < UINT64_MAX / 1000u) ? ms * 1000u : UINT64_MAX;
uv_mutex_lock(&delay_mutex);
while (elapsed < ns) {
- if (uv_cond_timedwait(&delay_cond, &delay_mutex, ns - elapsed)
- == UV_ETIMEDOUT)
+ // If ignoring input, we simply wait the full delay.
+ // Else we check for input in ~100ms intervals.
+ const uint64_t ns_delta = ignoreinput
+ ? ns - elapsed
+ : MIN(ns - elapsed, 100000000u); // 100ms
+
+ const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta);
+ if (0 != rv && UV_ETIMEDOUT != rv) {
+ assert(false);
+ break;
+ } // Else: Timeout proceeded normally.
+
+ if (!ignoreinput && os_char_avail()) {
break;
- uint64_t now = uv_hrtime();
+ }
+
+ const uint64_t now = uv_hrtime();
elapsed += now - base;
base = now;
}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 6149e1ab99..3d1def8dd4 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -417,15 +417,11 @@ char *FullName_save(char *fname, bool force)
}
char *buf = xmalloc(MAXPATHL);
- char *new_fname = NULL;
- if (vim_FullName(fname, buf, MAXPATHL, force) != FAIL) {
- new_fname = xstrdup(buf);
- } else {
- new_fname = xstrdup(fname);
+ if (vim_FullName(fname, buf, MAXPATHL, force) == FAIL) {
+ xfree(buf);
+ return xstrdup(fname);
}
- xfree(buf);
-
- return new_fname;
+ return buf;
}
/// Saves the absolute path.
@@ -1649,30 +1645,37 @@ bool vim_isAbsName(char_u *name)
/// Save absolute file name to "buf[len]".
///
-/// @param fname is the filename to evaluate
-/// @param[out] buf is the buffer for returning the absolute path for `fname`
-/// @param len is the length of `buf`
-/// @param force is a flag to force expanding even if the path is absolute
+/// @param fname filename to evaluate
+/// @param[out] buf contains `fname` absolute path, or:
+/// - truncated `fname` if longer than `len`
+/// - unmodified `fname` if absolute path fails or is a URL
+/// @param len length of `buf`
+/// @param force flag to force expanding even if the path is absolute
///
/// @return FAIL for failure, OK otherwise
int vim_FullName(const char *fname, char *buf, size_t len, bool force)
FUNC_ATTR_NONNULL_ARG(2)
{
- int retval = OK;
- int url;
-
*buf = NUL;
- if (fname == NULL)
+ if (fname == NULL) {
return FAIL;
+ }
- url = path_with_url(fname);
- if (!url)
- retval = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force);
- if (url || retval == FAIL) {
- /* something failed; use the file name (truncate when too long) */
+ if (strlen(fname) > (len - 1)) {
+ xstrlcpy(buf, fname, len); // truncate
+ return FAIL;
+ }
+
+ if (path_with_url(fname)) {
xstrlcpy(buf, fname, len);
+ return OK;
}
- return retval;
+
+ int rv = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force);
+ if (rv == FAIL) {
+ xstrlcpy(buf, fname, len); // something failed; use the filename
+ }
+ return rv;
}
/// Get the full resolved path for `fname`
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1dceb70cd4..5675697dc4 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -19,6 +19,7 @@ if has('timers')
call timer_start(100, 'ExitInsertMode')
call feedkeys('a', 'x!')
call assert_equal(1, g:triggered)
+ au! CursorHoldI
endfunc
func Test_cursorhold_insert_ctrl_x()
@@ -29,6 +30,7 @@ if has('timers')
" CursorHoldI does not trigger after CTRL-X
call feedkeys("a\<C-X>", 'x!')
call assert_equal(0, g:triggered)
+ au! CursorHoldI
endfunc
endif
@@ -58,5 +60,34 @@ function Test_bufunload()
bwipeout
call assert_equal(["bufunload", "bufdelete", "bufwipeout"], s:li)
+ au! test_bufunload_group
augroup! test_bufunload_group
endfunc
+
+func s:AddAnAutocmd()
+ augroup vimBarTest
+ au BufReadCmd * echo 'hello'
+ augroup END
+ call assert_equal(3, len(split(execute('au vimBarTest'), "\n")))
+endfunc
+
+func Test_early_bar()
+ " test that a bar is recognized before the {event}
+ call s:AddAnAutocmd()
+ augroup vimBarTest | au! | augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ call s:AddAnAutocmd()
+ augroup vimBarTest| au!| augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ " test that a bar is recognized after the {event}
+ call s:AddAnAutocmd()
+ augroup vimBarTest| au!BufReadCmd| augroup END
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+
+ " test that a bar is recognized after the {group}
+ call s:AddAnAutocmd()
+ au! vimBarTest|echo 'hello'
+ call assert_equal(1, len(split(execute('au vimBarTest'), "\n")))
+endfunc
diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim
index f593d16dbf..d0864ec64c 100644
--- a/src/nvim/testdir/test_usercommands.vim
+++ b/src/nvim/testdir/test_usercommands.vim
@@ -8,31 +8,57 @@ function Test_cmdmods()
MyCmd
aboveleft MyCmd
+ abo MyCmd
belowright MyCmd
+ bel MyCmd
botright MyCmd
+ bo MyCmd
browse MyCmd
+ bro MyCmd
confirm MyCmd
+ conf MyCmd
hide MyCmd
+ hid MyCmd
keepalt MyCmd
+ keepa MyCmd
keepjumps MyCmd
+ keepj MyCmd
keepmarks MyCmd
+ kee MyCmd
keeppatterns MyCmd
+ keepp MyCmd
+ leftabove MyCmd " results in :aboveleft
+ lefta MyCmd
lockmarks MyCmd
+ loc MyCmd
+ " noautocmd MyCmd
noswapfile MyCmd
+ nos MyCmd
+ rightbelow MyCmd " results in :belowright
+ rightb MyCmd
+ " sandbox MyCmd
silent MyCmd
+ sil MyCmd
tab MyCmd
topleft MyCmd
+ to MyCmd
+ " unsilent MyCmd
verbose MyCmd
+ verb MyCmd
vertical MyCmd
+ vert MyCmd
aboveleft belowright botright browse confirm hide keepalt keepjumps
\ keepmarks keeppatterns lockmarks noswapfile silent tab
\ topleft verbose vertical MyCmd
- call assert_equal(' aboveleft belowright botright browse confirm ' .
- \ 'hide keepalt keepjumps keepmarks keeppatterns lockmarks ' .
- \ 'noswapfile silent tab topleft verbose vertical aboveleft ' .
- \ 'belowright botright browse confirm hide keepalt keepjumps ' .
+ call assert_equal(' aboveleft aboveleft belowright belowright botright ' .
+ \ 'botright browse browse confirm confirm hide hide ' .
+ \ 'keepalt keepalt keepjumps keepjumps keepmarks keepmarks ' .
+ \ 'keeppatterns keeppatterns aboveleft aboveleft lockmarks lockmarks noswapfile ' .
+ \ 'noswapfile belowright belowright silent silent tab topleft topleft verbose verbose ' .
+ \ 'vertical vertical ' .
+ \ 'aboveleft belowright botright browse confirm hide keepalt keepjumps ' .
\ 'keepmarks keeppatterns lockmarks noswapfile silent tab topleft ' .
\ 'verbose vertical ', g:mods)
@@ -46,3 +72,33 @@ function Test_cmdmods()
delcommand MyQCmd
unlet g:mods
endfunction
+
+func Test_Ambiguous()
+ command Doit let g:didit = 'yes'
+ command Dothat let g:didthat = 'also'
+ call assert_fails('Do', 'E464:')
+ Doit
+ call assert_equal('yes', g:didit)
+ Dothat
+ call assert_equal('also', g:didthat)
+ unlet g:didit
+ unlet g:didthat
+
+ delcommand Doit
+ Do
+ call assert_equal('also', g:didthat)
+ delcommand Dothat
+endfunc
+
+func Test_CmdUndefined()
+ call assert_fails('Doit', 'E492:')
+ au CmdUndefined Doit :command Doit let g:didit = 'yes'
+ Doit
+ call assert_equal('yes', g:didit)
+ delcommand Doit
+
+ call assert_fails('Dothat', 'E492:')
+ au CmdUndefined * let g:didnot = 'yes'
+ call assert_fails('Dothat', 'E492:')
+ call assert_equal('yes', g:didnot)
+endfunc
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 6914cd9409..19062be730 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -313,7 +313,7 @@ static int included_patches[] = {
// 2130 NA
// 2129 NA
2128,
- // 2127,
+ 2127,
2126,
// 2125 NA
2124,
@@ -330,14 +330,14 @@ static int included_patches[] = {
// 2113,
2112,
// 2111,
- // 2110,
+ 2110,
2109,
// 2108 NA
// 2107,
// 2106,
// 2105 NA
// 2104,
- // 2103,
+ 2103,
// 2102 NA
// 2101,
// 2100,
@@ -689,7 +689,6 @@ static int included_patches[] = {
1755,
1754,
1753,
- // 1753,
1752,
1751,
// 1750 NA