aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api/private
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api/private')
-rw-r--r--src/nvim/api/private/converter.c121
-rw-r--r--src/nvim/api/private/defs.h10
-rw-r--r--src/nvim/api/private/dispatch.h16
-rw-r--r--src/nvim/api/private/helpers.c282
-rw-r--r--src/nvim/api/private/helpers.h30
-rw-r--r--src/nvim/api/private/validate.h2
6 files changed, 243 insertions, 218 deletions
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 90023171e5..a70ef1e50b 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -11,7 +11,6 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/func_attr.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
#include "nvim/types_defs.h"
@@ -20,6 +19,8 @@
/// Helper structure for vim_to_object
typedef struct {
kvec_withinit_t(Object, 2) stack; ///< Object stack.
+ Arena *arena; ///< arena where objects will be allocated
+ bool reuse_strdata;
} EncodedData;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -42,12 +43,21 @@ typedef struct {
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
kvi_push(edata->stack, FLOAT_OBJ((Float)(flt)))
+static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t len)
+{
+ if (edata->reuse_strdata) {
+ return STRING_OBJ(cbuf_as_string((char *)(len ? data : ""), len));
+ } else {
+ return CBUF_TO_ARENA_OBJ(edata->arena, data, len);
+ }
+}
+
#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
do { \
const size_t len_ = (size_t)(len); \
const char *const str_ = (str); \
assert(len_ == 0 || str_ != NULL); \
- kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_ ? str_ : ""), len_))); \
+ kvi_push(edata->stack, typval_cbuf_to_obj(edata, str_, len_)); \
} while (0)
#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
@@ -59,10 +69,7 @@ typedef struct {
do { \
const size_t len_ = (size_t)(len); \
const blob_T *const blob_ = (blob); \
- kvi_push(edata->stack, STRING_OBJ(((String) { \
- .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \
- .size = len_ \
- }))); \
+ kvi_push(edata->stack, typval_cbuf_to_obj(edata, len_ ? blob_->bv_ga.ga_data : "", len_)); \
} while (0)
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
@@ -91,11 +98,7 @@ typedef struct {
static inline void typval_encode_list_start(EncodedData *const edata, const size_t len)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
{
- kvi_push(edata->stack, ARRAY_OBJ(((Array) {
- .capacity = len,
- .size = 0,
- .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.array.items)),
- })));
+ kvi_push(edata->stack, ARRAY_OBJ(arena_array(edata->arena, len)));
}
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
@@ -110,7 +113,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata)
Object *const list = &kv_last(edata->stack);
assert(list->type == kObjectTypeArray);
assert(list->data.array.size < list->data.array.capacity);
- list->data.array.items[list->data.array.size++] = item;
+ ADD_C(list->data.array, item);
}
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
@@ -132,11 +135,7 @@ static inline void typval_encode_list_end(EncodedData *const edata)
static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
{
- kvi_push(edata->stack, DICTIONARY_OBJ(((Dictionary) {
- .capacity = len,
- .size = 0,
- .items = xmalloc(len * sizeof(*((Object)OBJECT_INIT).data.dictionary.items)),
- })));
+ kvi_push(edata->stack, DICTIONARY_OBJ(arena_dict(edata->arena, len)));
}
#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
@@ -157,9 +156,8 @@ static inline void typval_encode_after_key(EncodedData *const edata)
dict->data.dictionary.items[dict->data.dictionary.size].key
= key.data.string;
} else {
- api_free_object(key);
dict->data.dictionary.items[dict->data.dictionary.size].key
- = STATIC_CSTR_TO_STRING("__INVALID_KEY__");
+ = STATIC_CSTR_AS_STRING("__INVALID_KEY__");
}
}
@@ -234,17 +232,22 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
#undef TYPVAL_ENCODE_CONV_RECURSE
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
-/// Convert a vim object to an `Object` instance, recursively expanding
+/// Convert a vim object to an `Object` instance, recursively converting
/// Arrays/Dictionaries.
///
/// @param obj The source object
+/// @param arena if NULL, use direct allocation
+/// @param reuse_strdata when true, don't copy string data to Arena but reference
+/// typval strings directly. takes no effect when arena is
+/// NULL
/// @return The converted value
-Object vim_to_object(typval_T *obj)
+Object vim_to_object(typval_T *obj, Arena *arena, bool reuse_strdata)
{
EncodedData edata;
kvi_init(edata.stack);
- const int evo_ret = encode_vim_to_object(&edata, obj,
- "vim_to_object argument");
+ edata.arena = arena;
+ edata.reuse_strdata = reuse_strdata;
+ 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);
@@ -259,14 +262,20 @@ Object vim_to_object(typval_T *obj)
/// @param tv Conversion result is placed here. On failure member v_type is
/// set to VAR_UNKNOWN (no allocation was made for this variable).
/// @param err Error object.
+void object_to_vim(Object obj, typval_T *tv, Error *err)
+{
+ object_to_vim_take_luaref(&obj, tv, false, err);
+}
+
+/// same as object_to_vim but consumes all luarefs (nested) in `obj`
///
-/// @returns true if conversion is successful, otherwise false.
-bool object_to_vim(Object obj, typval_T *tv, Error *err)
+/// useful when `obj` is allocated on an arena
+void object_to_vim_take_luaref(Object *obj, typval_T *tv, bool take_luaref, Error *err)
{
tv->v_type = VAR_UNKNOWN;
tv->v_lock = VAR_UNLOCKED;
- switch (obj.type) {
+ switch (obj->type) {
case kObjectTypeNil:
tv->v_type = VAR_SPECIAL;
tv->vval.v_special = kSpecialVarNull;
@@ -274,46 +283,40 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
case kObjectTypeBoolean:
tv->v_type = VAR_BOOL;
- tv->vval.v_bool = obj.data.boolean ? kBoolVarTrue : kBoolVarFalse;
+ tv->vval.v_bool = obj->data.boolean ? kBoolVarTrue : kBoolVarFalse;
break;
case kObjectTypeBuffer:
case kObjectTypeWindow:
case kObjectTypeTabpage:
case kObjectTypeInteger:
- STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T),
+ STATIC_ASSERT(sizeof(obj->data.integer) <= sizeof(varnumber_T),
"Integer size must be <= Vimscript number size");
tv->v_type = VAR_NUMBER;
- tv->vval.v_number = (varnumber_T)obj.data.integer;
+ tv->vval.v_number = (varnumber_T)obj->data.integer;
break;
case kObjectTypeFloat:
tv->v_type = VAR_FLOAT;
- tv->vval.v_float = obj.data.floating;
+ tv->vval.v_float = obj->data.floating;
break;
case kObjectTypeString:
tv->v_type = VAR_STRING;
- if (obj.data.string.data == NULL) {
+ if (obj->data.string.data == NULL) {
tv->vval.v_string = NULL;
} else {
- tv->vval.v_string = xmemdupz(obj.data.string.data,
- obj.data.string.size);
+ tv->vval.v_string = xmemdupz(obj->data.string.data,
+ obj->data.string.size);
}
break;
case kObjectTypeArray: {
- list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size);
+ list_T *const list = tv_list_alloc((ptrdiff_t)obj->data.array.size);
- for (uint32_t i = 0; i < obj.data.array.size; i++) {
- Object item = obj.data.array.items[i];
+ for (uint32_t i = 0; i < obj->data.array.size; i++) {
typval_T li_tv;
-
- if (!object_to_vim(item, &li_tv, err)) {
- tv_list_free(list);
- return false;
- }
-
+ object_to_vim_take_luaref(&obj->data.array.items[i], &li_tv, take_luaref, err);
tv_list_append_owned_tv(list, li_tv);
}
tv_list_ref(list);
@@ -326,27 +329,11 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
case kObjectTypeDictionary: {
dict_T *const dict = tv_dict_alloc();
- for (uint32_t i = 0; i < obj.data.dictionary.size; i++) {
- KeyValuePair item = obj.data.dictionary.items[i];
- String key = item.key;
-
- if (key.size == 0) {
- api_set_error(err, kErrorTypeValidation,
- "Empty dictionary keys aren't allowed");
- // cleanup
- tv_dict_free(dict);
- return false;
- }
-
+ for (uint32_t i = 0; i < obj->data.dictionary.size; i++) {
+ KeyValuePair *item = &obj->data.dictionary.items[i];
+ String key = item->key;
dictitem_T *const di = tv_dict_item_alloc(key.data);
-
- if (!object_to_vim(item.value, &di->di_tv, err)) {
- // cleanup
- tv_dict_item_free(di);
- tv_dict_free(dict);
- return false;
- }
-
+ object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err);
tv_dict_add(dict, di);
}
dict->dv_refcount++;
@@ -357,12 +344,16 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
}
case kObjectTypeLuaRef: {
- char *name = register_luafunc(api_new_luaref(obj.data.luaref));
+ LuaRef ref = obj->data.luaref;
+ if (take_luaref) {
+ obj->data.luaref = LUA_NOREF;
+ } else {
+ ref = api_new_luaref(ref);
+ }
+ char *name = register_luafunc(ref);
tv->v_type = VAR_FUNC;
tv->vval.v_string = xstrdup(name);
break;
}
}
-
- return true;
}
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 25c8377518..ca088d7a55 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -105,6 +105,14 @@ typedef enum {
kObjectTypeTabpage,
} ObjectType;
+/// Value by which objects represented as EXT type are shifted
+///
+/// Subtracted when packing, added when unpacking. Used to allow moving
+/// buffer/window/tabpage block inside ObjectType enum. This block yet cannot be
+/// split or reordered.
+#define EXT_OBJECT_TYPE_SHIFT kObjectTypeBuffer
+#define EXT_OBJECT_TYPE_MAX (kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT)
+
struct object {
ObjectType type;
union {
@@ -124,6 +132,7 @@ struct key_value_pair {
};
typedef uint64_t OptionalKeys;
+typedef Integer HLGroupID;
// this is the prefix of all keysets with optional keys
typedef struct {
@@ -135,6 +144,7 @@ typedef struct {
size_t ptr_off;
ObjectType type; // kObjectTypeNil == untyped
int opt_index;
+ bool is_hlgroup;
} KeySetLink;
typedef KeySetLink *(*FieldHashfn)(const char *str, size_t len);
diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h
index 6a2c9eaf54..288f368fba 100644
--- a/src/nvim/api/private/dispatch.h
+++ b/src/nvim/api/private/dispatch.h
@@ -3,7 +3,7 @@
#include <stdbool.h>
#include <stdint.h>
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/memory_defs.h"
#include "nvim/types_defs.h"
@@ -14,18 +14,18 @@ typedef Object (*ApiDispatchWrapper)(uint64_t channel_id, Array args, Arena *are
struct MsgpackRpcRequestHandler {
const char *name;
ApiDispatchWrapper fn;
- bool fast; // Function is safe to be executed immediately while running the
- // uv loop (the loop is run very frequently due to breakcheck).
- // If "fast" is false, the function is deferred, i e the call will
- // be put in the event queue, for safe handling later.
- bool arena_return; // return value is allocated in the arena (or statically)
- // and should not be freed as such.
+ bool fast; ///< Function is safe to be executed immediately while running the
+ ///< uv loop (the loop is run very frequently due to breakcheck).
+ ///< If "fast" is false, the function is deferred, i e the call will
+ ///< be put in the event queue, for safe handling later.
+ bool ret_alloc; ///< return value is allocated and should be freed using api_free_object
+ ///< otherwise it uses arena and/or static memory
};
extern const MsgpackRpcRequestHandler method_handlers[];
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/dispatch.h.generated.h"
-# include "api/private/dispatch_wrappers.h.generated.h" // IWYU pragma: export
+# include "api/private/dispatch_wrappers.h.generated.h"
# include "keysets_defs.generated.h"
#endif
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index be39836a5b..1cd98aa0c4 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -17,11 +17,9 @@
#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_eval.h"
-#include "nvim/func_attr.h"
-#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/globals.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
@@ -29,17 +27,18 @@
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
-#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
+#include "nvim/ui_defs.h"
#include "nvim/version.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/private/funcs_metadata.generated.h"
+# include "api/private/api_metadata.generated.h"
# include "api/private/helpers.c.generated.h"
-# include "api/private/ui_events_metadata.generated.h"
#endif
/// Start block that may cause Vimscript exceptions while evaluating another code
@@ -140,7 +139,7 @@ bool try_end(Error *err)
api_set_error(err, kErrorTypeException, "Keyboard interrupt");
got_int = false;
} else if (msg_list != NULL && *msg_list != NULL) {
- int should_free;
+ bool should_free;
char *msg = get_exception_string(*msg_list,
ET_ERROR,
NULL,
@@ -151,7 +150,7 @@ bool try_end(Error *err)
if (should_free) {
xfree(msg);
}
- } else if (did_throw) {
+ } else if (did_throw || need_rethrow) {
if (*current_exception->throw_name != NUL) {
if (current_exception->throw_lnum != 0) {
api_set_error(err, kErrorTypeException, "%s, line %" PRIdLINENR ": %s",
@@ -175,7 +174,7 @@ bool try_end(Error *err)
/// @param dict The vimscript dict
/// @param key The key
/// @param[out] err Details of an error that may have occurred
-Object dict_get_value(dict_T *dict, String key, Error *err)
+Object dict_get_value(dict_T *dict, String key, Arena *arena, Error *err)
{
dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size);
@@ -184,7 +183,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
return (Object)OBJECT_INIT;
}
- return vim_to_object(&di->di_tv);
+ return vim_to_object(&di->di_tv, arena, true);
}
dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err)
@@ -221,7 +220,8 @@ dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err)
/// @param retval If true the old value will be converted and returned.
/// @param[out] err Details of an error that may have occurred
/// @return The old value if `retval` is true and the key was present, else NIL
-Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Error *err)
+Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retval, Arena *arena,
+ Error *err)
{
Object rv = OBJECT_INIT;
dictitem_T *di = dict_check_writable(dict, key, del, err);
@@ -244,7 +244,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
}
// Return the old value
if (retval) {
- rv = vim_to_object(&di->di_tv);
+ rv = vim_to_object(&di->di_tv, arena, false);
}
// Delete the entry
tv_dict_item_remove(dict, di);
@@ -254,9 +254,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
typval_T tv;
// Convert the object to a vimscript type in the temporary variable
- if (!object_to_vim(value, &tv, err)) {
- return rv;
- }
+ object_to_vim(value, &tv, err);
typval_T oldtv = TV_INITIAL_VALUE;
@@ -267,7 +265,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, bool retva
} else {
// Return the old value
if (retval) {
- rv = vim_to_object(&di->di_tv);
+ rv = vim_to_object(&di->di_tv, arena, false);
}
bool type_error = false;
if (dict == &vimvardict
@@ -427,12 +425,12 @@ String cstrn_as_string(char *str, size_t maxsize)
/// @param str the C string to use
/// @return The resulting String, or an empty String if
/// str was NULL
-String cstr_as_string(char *str) FUNC_ATTR_PURE
+String cstr_as_string(const char *str) FUNC_ATTR_PURE
{
if (str == NULL) {
return (String)STRING_INIT;
}
- return (String){ .data = str, .size = strlen(str) };
+ return (String){ .data = (char *)str, .size = strlen(str) };
}
/// Return the owned memory of a ga as a String
@@ -456,9 +454,10 @@ String ga_take_string(garray_T *ga)
/// @param input Binary string
/// @param crlf Also break lines at CR and CRLF.
/// @return [allocated] String array
-Array string_to_array(const String input, bool crlf)
+Array string_to_array(const String input, bool crlf, Arena *arena)
{
- Array ret = ARRAY_DICT_INIT;
+ ArrayBuilder ret = ARRAY_DICT_INIT;
+ kvi_init(ret);
for (size_t i = 0; i < input.size; i++) {
const char *start = input.data + i;
const char *end = start;
@@ -473,20 +472,17 @@ Array string_to_array(const String input, bool crlf)
if (crlf && *end == CAR && i + 1 < input.size && *(end + 1) == NL) {
i += 1; // Advance past CRLF.
}
- String s = {
- .size = line_len,
- .data = xmemdupz(start, line_len),
- };
+ String s = CBUF_TO_ARENA_STR(arena, start, line_len);
memchrsub(s.data, NUL, NL, line_len);
- ADD(ret, STRING_OBJ(s));
+ kvi_push(ret, STRING_OBJ(s));
// If line ends at end-of-buffer, add empty final item.
// This is "readfile()-style", see also ":help channel-lines".
if (i + 1 == input.size && (*end == NL || (crlf && *end == CAR))) {
- ADD(ret, STRING_OBJ(STRING_INIT));
+ kvi_push(ret, STRING_OBJ(STRING_INIT));
}
}
- return ret;
+ return arena_take_arraybuilder(arena, &ret);
}
/// Normalizes 0-based indexes to buffer line numbers.
@@ -578,10 +574,19 @@ String arena_string(Arena *arena, String str)
if (str.size) {
return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size);
} else {
- return (String)STRING_INIT;
+ return (String){ .data = arena ? "" : xstrdup(""), .size = 0 };
}
}
+Array arena_take_arraybuilder(Arena *arena, ArrayBuilder *arr)
+{
+ Array ret = arena_array(arena, kv_size(*arr));
+ ret.size = kv_size(*arr);
+ memcpy(ret.items, arr->items, sizeof(ret.items[0]) * ret.size);
+ kvi_destroy(*arr);
+ return ret;
+}
+
void api_free_object(Object value)
{
switch (value.type) {
@@ -642,102 +647,30 @@ void api_clear_error(Error *value)
value->type = kErrorTypeNone;
}
-/// @returns a shared value. caller must not modify it!
-Dictionary api_metadata(void)
-{
- static Dictionary metadata = ARRAY_DICT_INIT;
-
- if (!metadata.size) {
- PUT(metadata, "version", DICTIONARY_OBJ(version_dict()));
- init_function_metadata(&metadata);
- init_ui_event_metadata(&metadata);
- init_error_type_metadata(&metadata);
- init_type_metadata(&metadata);
- }
-
- return metadata;
-}
+// initialized once, never freed
+static ArenaMem mem_for_metadata = NULL;
-static void init_function_metadata(Dictionary *metadata)
+/// @returns a shared value. caller must not modify it!
+Object api_metadata(void)
{
- msgpack_unpacked unpacked;
- msgpack_unpacked_init(&unpacked);
- if (msgpack_unpack_next(&unpacked,
- (const char *)funcs_metadata,
- sizeof(funcs_metadata),
- NULL) != MSGPACK_UNPACK_SUCCESS) {
- abort();
- }
- Object functions;
- msgpack_rpc_to_object(&unpacked.data, &functions);
- msgpack_unpacked_destroy(&unpacked);
- PUT(*metadata, "functions", functions);
-}
+ static Object metadata = OBJECT_INIT;
-static void init_ui_event_metadata(Dictionary *metadata)
-{
- msgpack_unpacked unpacked;
- msgpack_unpacked_init(&unpacked);
- if (msgpack_unpack_next(&unpacked,
- (const char *)ui_events_metadata,
- sizeof(ui_events_metadata),
- NULL) != MSGPACK_UNPACK_SUCCESS) {
- abort();
- }
- Object ui_events;
- msgpack_rpc_to_object(&unpacked.data, &ui_events);
- msgpack_unpacked_destroy(&unpacked);
- PUT(*metadata, "ui_events", ui_events);
- Array ui_options = ARRAY_DICT_INIT;
- ADD(ui_options, CSTR_TO_OBJ("rgb"));
- for (UIExtension i = 0; i < kUIExtCount; i++) {
- if (ui_ext_names[i][0] != '_') {
- ADD(ui_options, CSTR_TO_OBJ(ui_ext_names[i]));
+ if (metadata.type == kObjectTypeNil) {
+ Arena arena = ARENA_EMPTY;
+ Error err = ERROR_INIT;
+ metadata = unpack((char *)packed_api_metadata, sizeof(packed_api_metadata), &arena, &err);
+ if (ERROR_SET(&err) || metadata.type != kObjectTypeDictionary) {
+ abort();
}
+ mem_for_metadata = arena_finish(&arena);
}
- PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options));
-}
-
-static void init_error_type_metadata(Dictionary *metadata)
-{
- Dictionary types = ARRAY_DICT_INIT;
-
- Dictionary exception_metadata = ARRAY_DICT_INIT;
- PUT(exception_metadata, "id", INTEGER_OBJ(kErrorTypeException));
-
- Dictionary validation_metadata = ARRAY_DICT_INIT;
- PUT(validation_metadata, "id", INTEGER_OBJ(kErrorTypeValidation));
- PUT(types, "Exception", DICTIONARY_OBJ(exception_metadata));
- PUT(types, "Validation", DICTIONARY_OBJ(validation_metadata));
-
- PUT(*metadata, "error_types", DICTIONARY_OBJ(types));
+ return metadata;
}
-static void init_type_metadata(Dictionary *metadata)
+String api_metadata_raw(void)
{
- Dictionary types = ARRAY_DICT_INIT;
-
- Dictionary buffer_metadata = ARRAY_DICT_INIT;
- PUT(buffer_metadata, "id",
- INTEGER_OBJ(kObjectTypeBuffer - EXT_OBJECT_TYPE_SHIFT));
- PUT(buffer_metadata, "prefix", CSTR_TO_OBJ("nvim_buf_"));
-
- Dictionary window_metadata = ARRAY_DICT_INIT;
- PUT(window_metadata, "id",
- INTEGER_OBJ(kObjectTypeWindow - EXT_OBJECT_TYPE_SHIFT));
- PUT(window_metadata, "prefix", CSTR_TO_OBJ("nvim_win_"));
-
- Dictionary tabpage_metadata = ARRAY_DICT_INIT;
- PUT(tabpage_metadata, "id",
- INTEGER_OBJ(kObjectTypeTabpage - EXT_OBJECT_TYPE_SHIFT));
- PUT(tabpage_metadata, "prefix", CSTR_TO_OBJ("nvim_tabpage_"));
-
- PUT(types, "Buffer", DICTIONARY_OBJ(buffer_metadata));
- PUT(types, "Window", DICTIONARY_OBJ(window_metadata));
- PUT(types, "Tabpage", DICTIONARY_OBJ(tabpage_metadata));
-
- PUT(*metadata, "types", DICTIONARY_OBJ(types));
+ return cbuf_as_string((char *)packed_api_metadata, sizeof(packed_api_metadata));
}
// all the copy_[object] functions allow arena=NULL,
@@ -938,13 +871,26 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error
char *mem = ((char *)retval + field->ptr_off);
Object *value = &dict.items[i].value;
+
if (field->type == kObjectTypeNil) {
*(Object *)mem = *value;
} else if (field->type == kObjectTypeInteger) {
- VALIDATE_T(field->str, kObjectTypeInteger, value->type, {
- return false;
- });
- *(Integer *)mem = value->data.integer;
+ if (field->is_hlgroup) {
+ int hl_id = 0;
+ if (value->type != kObjectTypeNil) {
+ hl_id = object_to_hl_id(*value, k.data, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
+ }
+ *(Integer *)mem = hl_id;
+ } else {
+ VALIDATE_T(field->str, kObjectTypeInteger, value->type, {
+ return false;
+ });
+
+ *(Integer *)mem = value->data.integer;
+ }
} else if (field->type == kObjectTypeFloat) {
Float *val = (Float *)mem;
if (value->type == kObjectTypeInteger) {
@@ -1003,24 +949,104 @@ bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error
return true;
}
-void api_free_keydict(void *dict, KeySetLink *table)
+Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size, Arena *arena)
+{
+ Dictionary rv = arena_dict(arena, max_size);
+ for (size_t i = 0; table[i].str; i++) {
+ KeySetLink *field = &table[i];
+ bool is_set = true;
+ if (field->opt_index >= 0) {
+ OptKeySet *ks = (OptKeySet *)value;
+ is_set = ks->is_set_ & (1ULL << field->opt_index);
+ }
+
+ if (!is_set) {
+ continue;
+ }
+
+ char *mem = ((char *)value + field->ptr_off);
+ Object val = NIL;
+
+ if (field->type == kObjectTypeNil) {
+ val = *(Object *)mem;
+ } else if (field->type == kObjectTypeInteger) {
+ val = INTEGER_OBJ(*(Integer *)mem);
+ } else if (field->type == kObjectTypeFloat) {
+ val = FLOAT_OBJ(*(Float *)mem);
+ } else if (field->type == kObjectTypeBoolean) {
+ val = BOOLEAN_OBJ(*(Boolean *)mem);
+ } else if (field->type == kObjectTypeString) {
+ val = STRING_OBJ(*(String *)mem);
+ } else if (field->type == kObjectTypeArray) {
+ val = ARRAY_OBJ(*(Array *)mem);
+ } else if (field->type == kObjectTypeDictionary) {
+ val = DICTIONARY_OBJ(*(Dictionary *)mem);
+ } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow
+ || field->type == kObjectTypeTabpage) {
+ val.data.integer = *(handle_T *)mem;
+ val.type = field->type;
+ } else if (field->type == kObjectTypeLuaRef) {
+ // do nothing
+ } else {
+ abort();
+ }
+
+ PUT_C(rv, field->str, val);
+ }
+
+ return rv;
+}
+
+void api_luarefs_free_object(Object value)
+{
+ // TODO(bfredl): this is more complicated than it needs to be.
+ // we should be able to lock down more specifically where luarefs can be
+ switch (value.type) {
+ case kObjectTypeLuaRef:
+ api_free_luaref(value.data.luaref);
+ break;
+
+ case kObjectTypeArray:
+ api_luarefs_free_array(value.data.array);
+ break;
+
+ case kObjectTypeDictionary:
+ api_luarefs_free_dict(value.data.dictionary);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void api_luarefs_free_keydict(void *dict, KeySetLink *table)
{
for (size_t i = 0; table[i].str; i++) {
char *mem = ((char *)dict + table[i].ptr_off);
if (table[i].type == kObjectTypeNil) {
- api_free_object(*(Object *)mem);
- } else if (table[i].type == kObjectTypeString) {
- api_free_string(*(String *)mem);
- } else if (table[i].type == kObjectTypeArray) {
- api_free_array(*(Array *)mem);
- } else if (table[i].type == kObjectTypeDictionary) {
- api_free_dictionary(*(Dictionary *)mem);
+ api_luarefs_free_object(*(Object *)mem);
} else if (table[i].type == kObjectTypeLuaRef) {
api_free_luaref(*(LuaRef *)mem);
+ } else if (table[i].type == kObjectTypeDictionary) {
+ api_luarefs_free_dict(*(Dictionary *)mem);
}
}
}
+void api_luarefs_free_array(Array value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_luarefs_free_object(value.items[i]);
+ }
+}
+
+void api_luarefs_free_dict(Dictionary value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_luarefs_free_object(value.items[i].value);
+ }
+}
+
/// Set a named mark
/// buffer and mark name must be validated already
/// @param buffer Buffer to set the mark on
@@ -1048,7 +1074,7 @@ bool set_mark(buf_T *buf, String name, Integer line, Integer col, Error *err)
}
}
assert(INT32_MIN <= line && line <= INT32_MAX);
- pos_T pos = { (linenr_T)line, (int)col, (int)col };
+ pos_T pos = { (linenr_T)line, (int)col, 0 };
res = setmark_pos(*name.data, &pos, buf->handle, NULL);
if (!res) {
if (deleting) {
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index e61dd5f992..7eda8ffaf6 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -2,21 +2,15 @@
#include <stdbool.h>
#include <stddef.h>
-#include <stdint.h>
#include "klib/kvec.h"
-#include "nvim/api/private/defs.h"
-#include "nvim/api/private/dispatch.h"
-#include "nvim/decoration.h"
-#include "nvim/eval/typval_defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
+#include "nvim/buffer_defs.h" // IWYU pragma: keep
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/ex_eval_defs.h"
-#include "nvim/getchar.h"
-#include "nvim/gettext.h"
-#include "nvim/globals.h"
#include "nvim/macros_defs.h"
#include "nvim/map_defs.h"
-#include "nvim/memory.h"
-#include "nvim/message.h"
+#include "nvim/message_defs.h" // IWYU pragma: keep
#define OBJECT_OBJ(o) o
@@ -38,6 +32,10 @@
#define CSTR_AS_OBJ(s) STRING_OBJ(cstr_as_string(s))
#define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s))
+#define CSTR_TO_ARENA_STR(arena, s) arena_string(arena, cstr_as_string(s))
+#define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(CSTR_TO_ARENA_STR(arena, s))
+#define CBUF_TO_ARENA_STR(arena, s, len) arena_string(arena, cbuf_as_string((char *)(s), len))
+#define CBUF_TO_ARENA_OBJ(arena, s, len) STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len))
#define BUFFER_OBJ(s) ((Object) { \
.type = kObjectTypeBuffer, \
@@ -76,6 +74,9 @@
#define PUT_C(dict, k, v) \
kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v }))
+#define PUT_KEY(d, typ, key, v) \
+ do { (d).is_set__##typ##_ |= (1 << KEYSET_OPTIDX_##typ##__##key); (d).key = v; } while (0)
+
#define ADD(array, item) \
kv_push(array, item)
@@ -94,6 +95,8 @@
name.capacity = maxsize; \
name.items = name##__items; \
+typedef kvec_withinit_t(Object, 16) ArrayBuilder;
+
#define cbuf_as_string(d, s) ((String) { .data = d, .size = s })
#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof("" s) - 1 })
@@ -120,12 +123,7 @@
#define api_init_array = ARRAY_DICT_INIT
#define api_init_dictionary = ARRAY_DICT_INIT
-#define api_free_boolean(value)
-#define api_free_integer(value)
-#define api_free_float(value)
-#define api_free_buffer(value)
-#define api_free_window(value)
-#define api_free_tabpage(value)
+#define KEYDICT_INIT { 0 }
EXTERN PMap(int) buffer_handles INIT( = MAP_INIT);
EXTERN PMap(int) window_handles INIT( = MAP_INIT);
diff --git a/src/nvim/api/private/validate.h b/src/nvim/api/private/validate.h
index d1c977cd6e..2c1d1a241d 100644
--- a/src/nvim/api/private/validate.h
+++ b/src/nvim/api/private/validate.h
@@ -3,7 +3,7 @@
#include <stdbool.h>
#include <stddef.h>
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/api/private/helpers.h"
#include "nvim/assert_defs.h"
#include "nvim/macros_defs.h"