diff options
| author | Björn Linse <bjorn.linse@gmail.com> | 2021-10-30 00:05:02 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-30 00:05:02 +0200 |
| commit | ac358bfb2f644b79ab1f6f17dddcec1bf32f8d97 (patch) | |
| tree | e9162ebddb4a272b18e3b33f27df7667bafe1215 | |
| parent | 615e03c33e73b1771e8d567cddfb362534e61673 (diff) | |
| parent | a60beeb34f811d2a9722887ee1a05e63e1dfa2b4 (diff) | |
| download | rneovim-ac358bfb2f644b79ab1f6f17dddcec1bf32f8d97.tar.gz rneovim-ac358bfb2f644b79ab1f6f17dddcec1bf32f8d97.tar.bz2 rneovim-ac358bfb2f644b79ab1f6f17dddcec1bf32f8d97.zip | |
Merge pull request #16154 from bfredl/neoscript
refactor(api): break out Vim script functions to its own file
| -rwxr-xr-x | scripts/gen_vimdoc.py | 2 | ||||
| -rw-r--r-- | src/nvim/api/deprecated.c | 1 | ||||
| -rw-r--r-- | src/nvim/api/private/converter.c | 348 | ||||
| -rw-r--r-- | src/nvim/api/private/converter.h | 11 | ||||
| -rw-r--r-- | src/nvim/api/private/dispatch.c | 1 | ||||
| -rw-r--r-- | src/nvim/api/private/helpers.c | 332 | ||||
| -rw-r--r-- | src/nvim/api/private/helpers.h | 2 | ||||
| -rw-r--r-- | src/nvim/api/vim.c | 715 | ||||
| -rw-r--r-- | src/nvim/api/vimscript.c | 733 | ||||
| -rw-r--r-- | src/nvim/api/vimscript.h | 9 | ||||
| -rw-r--r-- | src/nvim/channel.c | 1 | ||||
| -rw-r--r-- | src/nvim/context.c | 2 | ||||
| -rw-r--r-- | src/nvim/eval/funcs.c | 1 | ||||
| -rw-r--r-- | test/unit/api/private_helpers_spec.lua | 2 |
14 files changed, 1112 insertions, 1048 deletions
diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 813b79bae9..cb80bd05c6 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -89,7 +89,9 @@ CONFIG = { # Section ordering. 'section_order': [ 'vim.c', + 'vimscript.c', 'buffer.c', + 'extmark.c', 'window.c', 'win_config.c', 'tabpage.c', diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 815dd70a52..76b699800e 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -12,6 +12,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" +#include "nvim/api/vimscript.h" #include "nvim/extmark.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c new file mode 100644 index 0000000000..0d054c7958 --- /dev/null +++ b/src/nvim/api/private/converter.c @@ -0,0 +1,348 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include <assert.h> +#include <stddef.h> +#include <stdlib.h> + +#include "nvim/assert.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/api/private/converter.h" +#include "nvim/eval/typval.h" + +/// Helper structure for vim_to_object +typedef struct { + kvec_withinit_t(Object, 2) stack; ///< Object stack. +} EncodedData; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/private/converter.c.generated.h" +#endif + +#define TYPVAL_ENCODE_ALLOW_SPECIALS false + +#define TYPVAL_ENCODE_CONV_NIL(tv) \ + kvi_push(edata->stack, NIL) + +#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ + kvi_push(edata->stack, BOOLEAN_OBJ((Boolean)(num))) + +#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ + kvi_push(edata->stack, INTEGER_OBJ((Integer)(num))) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER + +#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ + kvi_push(edata->stack, FLOAT_OBJ((Float)(flt))) + +#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ + do { \ + const size_t len_ = (size_t)(len); \ + const char *const str_ = (const char *)(str); \ + assert(len_ == 0 || str_ != NULL); \ + kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_?str_:""), len_))); \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING + +#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ + TYPVAL_ENCODE_CONV_NIL(tv) + +#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ + do { \ + const size_t len_ = (size_t)(len); \ + const blob_T *const blob_ = (blob); \ + kvi_push(edata->stack, STRING_OBJ(((String) { \ + .data = len_ != 0 ? xmemdup(blob_->bv_ga.ga_data, len_) : NULL, \ + .size = len_ \ + }))); \ + } while (0) + +#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_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(tv) \ + kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ + kvi_push(edata->stack, \ + DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) + +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)), + }))); +} + +#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 +{ + Object item = kv_pop(edata->stack); + 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; +} + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ + typval_encode_between_list_items(edata) + +static inline void typval_encode_list_end(EncodedData *const edata) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + typval_encode_between_list_items(edata); +#ifndef NDEBUG + const Object *const list = &kv_last(edata->stack); + assert(list->data.array.size == list->data.array.capacity); +#endif +} + +#define TYPVAL_ENCODE_CONV_LIST_END(tv) \ + typval_encode_list_end(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)), + }))); +} + +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ + typval_encode_dict_start(edata, (size_t)(len)) + +#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 +{ + Object key = kv_pop(edata->stack); + Object *const dict = &kv_last(edata->stack); + assert(dict->type == kObjectTypeDictionary); + assert(dict->data.dictionary.size < dict->data.dictionary.capacity); + if (key.type == kObjectTypeString) { + 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__"); + } +} + +#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) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + Object val = kv_pop(edata->stack); + Object *const dict = &kv_last(edata->stack); + assert(dict->type == kObjectTypeDictionary); + assert(dict->data.dictionary.size < dict->data.dictionary.capacity); + dict->data.dictionary.items[dict->data.dictionary.size++].value = val; +} + +#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) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + typval_encode_between_dict_items(edata); +#ifndef NDEBUG + const Object *const dict = &kv_last(edata->stack); + assert(dict->data.dictionary.size == dict->data.dictionary.capacity); +#endif +} + +#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(val) + +#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_BLOB +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#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_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_RECURSE +#undef TYPVAL_ENCODE_ALLOW_SPECIALS + +/// Convert a vim object to an `Object` instance, recursively expanding +/// Arrays/Dictionaries. +/// +/// @param obj The source object +/// @return The converted value +Object vim_to_object(typval_T *obj) +{ + EncodedData edata; + kvi_init(edata.stack); + 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); + kvi_destroy(edata.stack); + return ret; +} + +/// Converts from type Object to a VimL value. +/// +/// @param obj Object to convert from. +/// @param tv Conversion result is placed here. On failure member v_type is +/// set to VAR_UNKNOWN (no allocation was made for this variable). +/// returns true if conversion is successful, otherwise false. +bool object_to_vim(Object obj, typval_T *tv, Error *err) +{ + tv->v_type = VAR_UNKNOWN; + tv->v_lock = VAR_UNLOCKED; + + switch (obj.type) { + case kObjectTypeNil: + tv->v_type = VAR_SPECIAL; + tv->vval.v_special = kSpecialVarNull; + break; + + case kObjectTypeBoolean: + tv->v_type = VAR_BOOL; + 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), + "Integer size must be <= VimL number size"); + tv->v_type = VAR_NUMBER; + tv->vval.v_number = (varnumber_T)obj.data.integer; + break; + + case kObjectTypeFloat: + tv->v_type = VAR_FLOAT; + tv->vval.v_float = obj.data.floating; + break; + + case kObjectTypeString: + tv->v_type = VAR_STRING; + 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); + } + break; + + case kObjectTypeArray: { + 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]; + typval_T li_tv; + + if (!object_to_vim(item, &li_tv, err)) { + tv_list_free(list); + return false; + } + + tv_list_append_owned_tv(list, li_tv); + } + tv_list_ref(list); + + tv->v_type = VAR_LIST; + tv->vval.v_list = list; + break; + } + + 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; + } + + 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; + } + + tv_dict_add(dict, di); + } + dict->dv_refcount++; + + tv->v_type = VAR_DICT; + tv->vval.v_dict = dict; + break; + } + default: + abort(); + } + + return true; +} diff --git a/src/nvim/api/private/converter.h b/src/nvim/api/private/converter.h new file mode 100644 index 0000000000..80ee640295 --- /dev/null +++ b/src/nvim/api/private/converter.h @@ -0,0 +1,11 @@ +#ifndef NVIM_API_PRIVATE_CONVERTER_H +#define NVIM_API_PRIVATE_CONVERTER_H + +#include "nvim/api/private/defs.h" +#include "nvim/eval/typval.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/private/converter.h.generated.h" +#endif + +#endif // NVIM_API_PRIVATE_CONVERTER_H diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 79af9bf176..8ab7743e01 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -15,6 +15,7 @@ #include "nvim/api/tabpage.h" #include "nvim/api/ui.h" #include "nvim/api/vim.h" +#include "nvim/api/vimscript.h" #include "nvim/api/win_config.h" #include "nvim/api/window.h" #include "nvim/log.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 745681c3f8..6b3f4a29d7 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -28,6 +28,7 @@ #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/api/private/converter.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/option_defs.h" @@ -37,11 +38,6 @@ #include "nvim/vim.h" #include "nvim/window.h" -/// Helper structure for vim_to_object -typedef struct { - kvec_withinit_t(Object, 2) stack; ///< Object stack. -} EncodedData; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/funcs_metadata.generated.h" # include "api/private/helpers.c.generated.h" @@ -415,226 +411,6 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object current_sctx = save_current_sctx; } -#define TYPVAL_ENCODE_ALLOW_SPECIALS false - -#define TYPVAL_ENCODE_CONV_NIL(tv) \ - kvi_push(edata->stack, NIL) - -#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - kvi_push(edata->stack, BOOLEAN_OBJ((Boolean)(num))) - -#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - kvi_push(edata->stack, INTEGER_OBJ((Integer)(num))) - -#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER - -#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - kvi_push(edata->stack, FLOAT_OBJ((Float)(flt))) - -#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \ - do { \ - const size_t len_ = (size_t)(len); \ - const char *const str_ = (const char *)(str); \ - assert(len_ == 0 || str_ != NULL); \ - kvi_push(edata->stack, STRING_OBJ(cbuf_to_string((len_?str_:""), len_))); \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING - -#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ - TYPVAL_ENCODE_CONV_NIL(tv) - -#define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \ - do { \ - const size_t len_ = (size_t)(len); \ - const blob_T *const blob_ = (blob); \ - kvi_push(edata->stack, STRING_OBJ(((String) { \ - .data = len_ != 0 ? xmemdup(blob_->bv_ga.ga_data, len_) : NULL, \ - .size = len_ \ - }))); \ - } while (0) - -#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_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(tv) \ - kvi_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) - -#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - kvi_push(edata->stack, \ - DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 }))) - -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)), - }))); -} - -#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 -{ - Object item = kv_pop(edata->stack); - 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; -} - -#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \ - typval_encode_between_list_items(edata) - -static inline void typval_encode_list_end(EncodedData *const edata) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL -{ - typval_encode_between_list_items(edata); -#ifndef NDEBUG - const Object *const list = &kv_last(edata->stack); - assert(list->data.array.size == list->data.array.capacity); -#endif -} - -#define TYPVAL_ENCODE_CONV_LIST_END(tv) \ - typval_encode_list_end(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)), - }))); -} - -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ - typval_encode_dict_start(edata, (size_t)(len)) - -#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 -{ - Object key = kv_pop(edata->stack); - Object *const dict = &kv_last(edata->stack); - assert(dict->type == kObjectTypeDictionary); - assert(dict->data.dictionary.size < dict->data.dictionary.capacity); - if (key.type == kObjectTypeString) { - 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__"); - } -} - -#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) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL -{ - Object val = kv_pop(edata->stack); - Object *const dict = &kv_last(edata->stack); - assert(dict->type == kObjectTypeDictionary); - assert(dict->data.dictionary.size < dict->data.dictionary.capacity); - dict->data.dictionary.items[dict->data.dictionary.size++].value = val; -} - -#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) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL -{ - typval_encode_between_dict_items(edata); -#ifndef NDEBUG - const Object *const dict = &kv_last(edata->stack); - assert(dict->data.dictionary.size == dict->data.dictionary.capacity); -#endif -} - -#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(val) - -#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_BLOB -#undef TYPVAL_ENCODE_CONV_NUMBER -#undef TYPVAL_ENCODE_CONV_FLOAT -#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_SPECIAL_DICT_KEY_CHECK -#undef TYPVAL_ENCODE_CONV_LIST_END -#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_RECURSE -#undef TYPVAL_ENCODE_ALLOW_SPECIALS - -/// Convert a vim object to an `Object` instance, recursively expanding -/// Arrays/Dictionaries. -/// -/// @param obj The source object -/// @return The converted value -Object vim_to_object(typval_T *obj) -{ - EncodedData edata; - kvi_init(edata.stack); - 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); - kvi_destroy(edata.stack); - return ret; -} buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { @@ -966,112 +742,6 @@ bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, Arr return true; } -/// Converts from type Object to a VimL value. -/// -/// @param obj Object to convert from. -/// @param tv Conversion result is placed here. On failure member v_type is -/// set to VAR_UNKNOWN (no allocation was made for this variable). -/// returns true if conversion is successful, otherwise false. -bool object_to_vim(Object obj, typval_T *tv, Error *err) -{ - tv->v_type = VAR_UNKNOWN; - tv->v_lock = VAR_UNLOCKED; - - switch (obj.type) { - case kObjectTypeNil: - tv->v_type = VAR_SPECIAL; - tv->vval.v_special = kSpecialVarNull; - break; - - case kObjectTypeBoolean: - tv->v_type = VAR_BOOL; - 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), - "Integer size must be <= VimL number size"); - tv->v_type = VAR_NUMBER; - tv->vval.v_number = (varnumber_T)obj.data.integer; - break; - - case kObjectTypeFloat: - tv->v_type = VAR_FLOAT; - tv->vval.v_float = obj.data.floating; - break; - - case kObjectTypeString: - tv->v_type = VAR_STRING; - 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); - } - break; - - case kObjectTypeArray: { - 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]; - typval_T li_tv; - - if (!object_to_vim(item, &li_tv, err)) { - tv_list_free(list); - return false; - } - - tv_list_append_owned_tv(list, li_tv); - } - tv_list_ref(list); - - tv->v_type = VAR_LIST; - tv->vval.v_list = list; - break; - } - - 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; - } - - 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; - } - - tv_dict_add(dict, di); - } - dict->dv_refcount++; - - tv->v_type = VAR_DICT; - tv->vval.v_dict = dict; - break; - } - default: - abort(); - } - - return true; -} void api_free_string(String value) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 08d2c8d90c..6d0aec9c90 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -1,8 +1,6 @@ #ifndef NVIM_API_PRIVATE_HELPERS_H #define NVIM_API_PRIVATE_HELPERS_H -#include <stdbool.h> - #include "nvim/api/private/defs.h" #include "nvim/decoration.h" #include "nvim/ex_eval.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 7f442c8e52..3ca6189ddd 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -10,6 +10,7 @@ #include "nvim/api/buffer.h" #include "nvim/api/deprecated.h" +#include "nvim/api/private/converter.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" @@ -59,84 +60,6 @@ # include "api/vim.c.generated.h" #endif -/// Executes Vimscript (multiline block of Ex-commands), like anonymous -/// |:source|. -/// -/// Unlike |nvim_command()| this function supports heredocs, script-scope (s:), -/// etc. -/// -/// On execution error: fails with VimL error, does not update v:errmsg. -/// -/// @see |execute()| -/// @see |nvim_command()| -/// -/// @param src Vimscript code -/// @param output Capture and return all (non-error, non-shell |:!|) output -/// @param[out] err Error details (Vim error), if any -/// @return Output (non-error, non-shell |:!|) if `output` is true, -/// else empty string. -String nvim_exec(String src, Boolean output, Error *err) - FUNC_API_SINCE(7) -{ - const int save_msg_silent = msg_silent; - garray_T *const save_capture_ga = capture_ga; - garray_T capture_local; - if (output) { - ga_init(&capture_local, 1, 80); - capture_ga = &capture_local; - } - - try_start(); - if (output) { - msg_silent++; - } - do_source_str(src.data, "nvim_exec()"); - if (output) { - capture_ga = save_capture_ga; - msg_silent = save_msg_silent; - } - try_end(err); - - if (ERROR_SET(err)) { - goto theend; - } - - if (output && capture_local.ga_len > 1) { - String s = (String){ - .data = capture_local.ga_data, - .size = (size_t)capture_local.ga_len, - }; - // redir usually (except :echon) prepends a newline. - if (s.data[0] == '\n') { - memmove(s.data, s.data + 1, s.size - 1); - s.data[s.size - 1] = '\0'; - s.size = s.size - 1; - } - return s; // Caller will free the memory. - } -theend: - if (output) { - ga_clear(&capture_local); - } - return (String)STRING_INIT; -} - -/// Executes an ex-command. -/// -/// On execution error: fails with VimL error, does not update v:errmsg. -/// -/// @see |nvim_exec()| -/// -/// @param command Ex-command string -/// @param[out] err Error details (Vim error), if any -void nvim_command(String command, Error *err) - FUNC_API_SINCE(1) -{ - try_start(); - do_cmdline_cmd(command.data); - try_end(err); -} - /// Gets a highlight definition by name. /// /// @param name Highlight group name @@ -477,51 +400,6 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool return cstr_as_string(ptr); } -/// Evaluates a VimL |expression|. -/// Dictionaries and Lists are recursively expanded. -/// -/// On execution error: fails with VimL error, does not update v:errmsg. -/// -/// @param expr VimL expression string -/// @param[out] err Error details, if any -/// @return Evaluation result or expanded object -Object nvim_eval(String expr, Error *err) - FUNC_API_SINCE(1) -{ - static int recursive = 0; // recursion depth - Object rv = OBJECT_INIT; - - TRY_WRAP({ - // Initialize `force_abort` and `suppress_errthrow` at the top level. - if (!recursive) { - force_abort = false; - suppress_errthrow = false; - current_exception = NULL; - // `did_emsg` is set by emsg(), which cancels execution. - did_emsg = false; - } - recursive++; - try_start(); - - typval_T rettv; - int ok = eval0((char_u *)expr.data, &rettv, NULL, true); - - if (!try_end(err)) { - if (ok == FAIL) { - // Should never happen, try_end() should get the error. #8371 - api_set_error(err, kErrorTypeException, - "Failed to evaluate expression: '%.*s'", 256, expr.data); - } else { - rv = vim_to_object(&rettv); - } - } - - tv_clear(&rettv); - recursive--; - }); - - return rv; -} /// Execute Lua code. Parameters (if any) are available as `...` inside the /// chunk. The chunk can return a value. @@ -562,164 +440,6 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err); } -/// Calls a VimL function. -/// -/// @param fn Function name -/// @param args Function arguments -/// @param self `self` dict, or NULL for non-dict functions -/// @param[out] err Error details, if any -/// @return Result of the function call -static Object _call_function(String fn, Array args, dict_T *self, Error *err) -{ - static int recursive = 0; // recursion depth - Object rv = OBJECT_INIT; - - if (args.size > MAX_FUNC_ARGS) { - api_set_error(err, kErrorTypeValidation, - "Function called with too many arguments"); - return rv; - } - - // Convert the arguments in args from Object to typval_T values - typval_T vim_args[MAX_FUNC_ARGS + 1]; - size_t i = 0; // also used for freeing the variables - for (; i < args.size; i++) { - if (!object_to_vim(args.items[i], &vim_args[i], err)) { - goto free_vim_args; - } |