mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #27460 from bfredl/merarena
refactor(eval): use arena when converting typvals to Object
This commit is contained in:
commit
c6f7419420
@ -869,7 +869,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err)
|
||||
/// @param name Variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Variable value
|
||||
Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
|
||||
Object nvim_buf_get_var(Buffer buffer, String name, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
@ -878,7 +878,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err)
|
||||
return (Object)OBJECT_INIT;
|
||||
}
|
||||
|
||||
return dict_get_value(buf->b_vars, name, err);
|
||||
return dict_get_value(buf->b_vars, name, arena, err);
|
||||
}
|
||||
|
||||
/// Gets a changed tick of a buffer
|
||||
@ -957,7 +957,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err)
|
||||
return;
|
||||
}
|
||||
|
||||
dict_set_var(buf->b_vars, name, value, false, false, err);
|
||||
dict_set_var(buf->b_vars, name, value, false, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Removes a buffer-scoped (b:) variable
|
||||
@ -974,7 +974,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
|
||||
return;
|
||||
}
|
||||
|
||||
dict_set_var(buf->b_vars, name, NIL, true, false, err);
|
||||
dict_set_var(buf->b_vars, name, NIL, true, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Gets the full file name for the buffer
|
||||
@ -1175,7 +1175,7 @@ Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col,
|
||||
/// uppercase/file mark set in another buffer.
|
||||
/// @see |nvim_buf_set_mark()|
|
||||
/// @see |nvim_buf_del_mark()|
|
||||
ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
|
||||
ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
@ -1205,8 +1205,9 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
|
||||
pos = fm->mark;
|
||||
}
|
||||
|
||||
ADD(rv, INTEGER_OBJ(pos.lnum));
|
||||
ADD(rv, INTEGER_OBJ(pos.col));
|
||||
rv = arena_array(arena, 2);
|
||||
ADD_C(rv, INTEGER_OBJ(pos.lnum));
|
||||
ADD_C(rv, INTEGER_OBJ(pos.col));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -305,7 +305,7 @@ end:
|
||||
/// - output: (boolean, default false) Whether to return command output.
|
||||
/// @param[out] err Error details, if any.
|
||||
/// @return Command output (non-error, non-shell |:!|) if `output` is true, else empty string.
|
||||
String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error *err)
|
||||
String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(10)
|
||||
{
|
||||
exarg_T ea;
|
||||
@ -343,7 +343,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
goto end;
|
||||
});
|
||||
|
||||
cmdname = string_to_cstr(cmd->cmd);
|
||||
cmdname = arena_string(arena, cmd->cmd).data;
|
||||
ea.cmd = cmdname;
|
||||
|
||||
char *p = find_ex_command(&ea, NULL);
|
||||
@ -352,9 +352,8 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
// autocommands defined, trigger the matching autocommands.
|
||||
if (p != NULL && ea.cmdidx == CMD_SIZE && ASCII_ISUPPER(*ea.cmd)
|
||||
&& has_event(EVENT_CMDUNDEFINED)) {
|
||||
p = xstrdup(cmdname);
|
||||
p = arena_string(arena, cmd->cmd).data;
|
||||
int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL);
|
||||
xfree(p);
|
||||
// If the autocommands did something and didn't cause an error, try
|
||||
// finding the command again.
|
||||
p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
|
||||
@ -383,28 +382,31 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
if (HAS_KEY(cmd, cmd, args)) {
|
||||
// Process all arguments. Convert non-String arguments to String and check if String arguments
|
||||
// have non-whitespace characters.
|
||||
args = arena_array(arena, cmd->args.size);
|
||||
for (size_t i = 0; i < cmd->args.size; i++) {
|
||||
Object elem = cmd->args.items[i];
|
||||
char *data_str;
|
||||
|
||||
switch (elem.type) {
|
||||
case kObjectTypeBoolean:
|
||||
data_str = xcalloc(2, sizeof(char));
|
||||
data_str = arena_alloc(arena, 2, false);
|
||||
data_str[0] = elem.data.boolean ? '1' : '0';
|
||||
data_str[1] = '\0';
|
||||
ADD_C(args, CSTR_AS_OBJ(data_str));
|
||||
break;
|
||||
case kObjectTypeBuffer:
|
||||
case kObjectTypeWindow:
|
||||
case kObjectTypeTabpage:
|
||||
case kObjectTypeInteger:
|
||||
data_str = xcalloc(NUMBUFLEN, sizeof(char));
|
||||
data_str = arena_alloc(arena, NUMBUFLEN, false);
|
||||
snprintf(data_str, NUMBUFLEN, "%" PRId64, elem.data.integer);
|
||||
ADD_C(args, CSTR_AS_OBJ(data_str));
|
||||
break;
|
||||
case kObjectTypeString:
|
||||
VALIDATE_EXP(!string_iswhite(elem.data.string), "command arg", "non-whitespace", NULL, {
|
||||
goto end;
|
||||
});
|
||||
data_str = string_to_cstr(elem.data.string);
|
||||
ADD_C(args, elem);
|
||||
break;
|
||||
default:
|
||||
VALIDATE_EXP(false, "command arg", "valid type", api_typename(elem.type), {
|
||||
@ -412,8 +414,6 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
ADD(args, CSTR_AS_OBJ(data_str));
|
||||
}
|
||||
|
||||
bool argc_valid;
|
||||
@ -666,26 +666,20 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
}
|
||||
|
||||
if (opts->output && capture_local.ga_len > 1) {
|
||||
retv = (String){
|
||||
.data = capture_local.ga_data,
|
||||
.size = (size_t)capture_local.ga_len,
|
||||
};
|
||||
// TODO(bfredl): if there are more cases like this we might want custom xfree-list in arena
|
||||
retv = CBUF_TO_ARENA_STR(arena, capture_local.ga_data, (size_t)capture_local.ga_len);
|
||||
// redir usually (except :echon) prepends a newline.
|
||||
if (retv.data[0] == '\n') {
|
||||
memmove(retv.data, retv.data + 1, retv.size - 1);
|
||||
retv.data[retv.size - 1] = '\0';
|
||||
retv.size = retv.size - 1;
|
||||
retv.data++;
|
||||
retv.size--;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
clear_ga:
|
||||
if (opts->output) {
|
||||
ga_clear(&capture_local);
|
||||
}
|
||||
end:
|
||||
api_free_array(args);
|
||||
xfree(cmdline);
|
||||
xfree(cmdname);
|
||||
xfree(ea.args);
|
||||
xfree(ea.arglens);
|
||||
|
||||
|
@ -362,7 +362,7 @@ void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean in
|
||||
///
|
||||
/// @warning It may return nil if there was no previous value
|
||||
/// or if previous value was `v:null`.
|
||||
Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
|
||||
Object buffer_set_var(Buffer buffer, String name, Object value, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
@ -371,7 +371,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
|
||||
return NIL;
|
||||
}
|
||||
|
||||
return dict_set_var(buf->b_vars, name, value, false, true, err);
|
||||
return dict_set_var(buf->b_vars, name, value, false, true, arena, err);
|
||||
}
|
||||
|
||||
/// Removes a buffer-scoped (b:) variable
|
||||
@ -382,7 +382,7 @@ Object buffer_set_var(Buffer buffer, String name, Object value, Error *err)
|
||||
/// @param name Variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Old value
|
||||
Object buffer_del_var(Buffer buffer, String name, Error *err)
|
||||
Object buffer_del_var(Buffer buffer, String name, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||
@ -391,7 +391,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err)
|
||||
return NIL;
|
||||
}
|
||||
|
||||
return dict_set_var(buf->b_vars, name, NIL, true, true, err);
|
||||
return dict_set_var(buf->b_vars, name, NIL, true, true, arena, err);
|
||||
}
|
||||
|
||||
/// Sets a window-scoped (w:) variable
|
||||
@ -406,7 +406,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err)
|
||||
///
|
||||
/// @warning It may return nil if there was no previous value
|
||||
/// or if previous value was `v:null`.
|
||||
Object window_set_var(Window window, String name, Object value, Error *err)
|
||||
Object window_set_var(Window window, String name, Object value, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
win_T *win = find_window_by_handle(window, err);
|
||||
@ -415,7 +415,7 @@ Object window_set_var(Window window, String name, Object value, Error *err)
|
||||
return NIL;
|
||||
}
|
||||
|
||||
return dict_set_var(win->w_vars, name, value, false, true, err);
|
||||
return dict_set_var(win->w_vars, name, value, false, true, arena, err);
|
||||
}
|
||||
|
||||
/// Removes a window-scoped (w:) variable
|
||||
@ -426,7 +426,7 @@ Object window_set_var(Window window, String name, Object value, Error *err)
|
||||
/// @param name variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Old value
|
||||
Object window_del_var(Window window, String name, Error *err)
|
||||
Object window_del_var(Window window, String name, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
win_T *win = find_window_by_handle(window, err);
|
||||
@ -435,7 +435,7 @@ Object window_del_var(Window window, String name, Error *err)
|
||||
return NIL;
|
||||
}
|
||||
|
||||
return dict_set_var(win->w_vars, name, NIL, true, true, err);
|
||||
return dict_set_var(win->w_vars, name, NIL, true, true, arena, err);
|
||||
}
|
||||
|
||||
/// Sets a tab-scoped (t:) variable
|
||||
@ -450,7 +450,7 @@ Object window_del_var(Window window, String name, Error *err)
|
||||
///
|
||||
/// @warning It may return nil if there was no previous value
|
||||
/// or if previous value was `v:null`.
|
||||
Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
|
||||
Object tabpage_set_var(Tabpage tabpage, String name, Object value, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
tabpage_T *tab = find_tab_by_handle(tabpage, err);
|
||||
@ -459,7 +459,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
|
||||
return NIL;
|
||||
}
|
||||
|
||||
return dict_set_var(tab->tp_vars, name, value, false, true, err);
|
||||
return dict_set_var(tab->tp_vars, name, value, false, true, arena, err);
|
||||
}
|
||||
|
||||
/// Removes a tab-scoped (t:) variable
|
||||
@ -470,7 +470,7 @@ Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err)
|
||||
/// @param name Variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Old value
|
||||
Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
|
||||
Object tabpage_del_var(Tabpage tabpage, String name, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
tabpage_T *tab = find_tab_by_handle(tabpage, err);
|
||||
@ -479,7 +479,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
|
||||
return NIL;
|
||||
}
|
||||
|
||||
return dict_set_var(tab->tp_vars, name, NIL, true, true, err);
|
||||
return dict_set_var(tab->tp_vars, name, NIL, true, true, arena, err);
|
||||
}
|
||||
|
||||
/// @deprecated
|
||||
@ -487,18 +487,18 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err)
|
||||
/// @warning May return nil if there was no previous value
|
||||
/// OR if previous value was `v:null`.
|
||||
/// @return Old value or nil if there was no previous value.
|
||||
Object vim_set_var(String name, Object value, Error *err)
|
||||
Object vim_set_var(String name, Object value, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
return dict_set_var(&globvardict, name, value, false, true, err);
|
||||
return dict_set_var(&globvardict, name, value, false, true, arena, err);
|
||||
}
|
||||
|
||||
/// @deprecated
|
||||
/// @see nvim_del_var
|
||||
Object vim_del_var(String name, Error *err)
|
||||
Object vim_del_var(String name, Arena *arena, Error *err)
|
||||
FUNC_API_DEPRECATED_SINCE(1)
|
||||
{
|
||||
return dict_set_var(&globvardict, name, NIL, true, true, err);
|
||||
return dict_set_var(&globvardict, name, NIL, true, true, arena, err);
|
||||
}
|
||||
|
||||
static int64_t convert_index(int64_t index)
|
||||
|
@ -19,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
|
||||
@ -41,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
|
||||
@ -58,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) \
|
||||
@ -90,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) \
|
||||
@ -109,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) \
|
||||
@ -131,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) \
|
||||
@ -156,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__");
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,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,11 +263,19 @@ Object vim_to_object(typval_T *obj)
|
||||
/// 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`
|
||||
///
|
||||
/// 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;
|
||||
@ -271,41 +283,40 @@ void 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;
|
||||
object_to_vim(item, &li_tv, err);
|
||||
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);
|
||||
@ -318,11 +329,11 @@ void 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;
|
||||
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);
|
||||
object_to_vim(item.value, &di->di_tv, err);
|
||||
object_to_vim_take_luaref(&item->value, &di->di_tv, take_luaref, err);
|
||||
tv_dict_add(dict, di);
|
||||
}
|
||||
dict->dv_refcount++;
|
||||
@ -333,7 +344,13 @@ void 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;
|
||||
|
@ -175,7 +175,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 +184,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 +221,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 +245,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);
|
||||
@ -265,7 +266,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
|
||||
|
@ -35,6 +35,7 @@
|
||||
#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, \
|
||||
|
@ -49,7 +49,7 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err)
|
||||
/// @param name Variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Variable value
|
||||
Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err)
|
||||
Object nvim_tabpage_get_var(Tabpage tabpage, String name, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
tabpage_T *tab = find_tab_by_handle(tabpage, err);
|
||||
@ -58,7 +58,7 @@ Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err)
|
||||
return (Object)OBJECT_INIT;
|
||||
}
|
||||
|
||||
return dict_get_value(tab->tp_vars, name, err);
|
||||
return dict_get_value(tab->tp_vars, name, arena, err);
|
||||
}
|
||||
|
||||
/// Sets a tab-scoped (t:) variable
|
||||
@ -76,7 +76,7 @@ void nvim_tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err
|
||||
return;
|
||||
}
|
||||
|
||||
dict_set_var(tab->tp_vars, name, value, false, false, err);
|
||||
dict_set_var(tab->tp_vars, name, value, false, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Removes a tab-scoped (t:) variable
|
||||
@ -93,7 +93,7 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err)
|
||||
return;
|
||||
}
|
||||
|
||||
dict_set_var(tab->tp_vars, name, NIL, true, false, err);
|
||||
dict_set_var(tab->tp_vars, name, NIL, true, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Gets the current window in a tabpage
|
||||
|
@ -1029,12 +1029,12 @@ static Array translate_contents(UI *ui, Array contents, Arena *arena)
|
||||
if (attr) {
|
||||
Dictionary rgb_attrs = arena_dict(arena, HLATTRS_DICT_SIZE);
|
||||
hlattrs2dict(&rgb_attrs, NULL, syn_attr2entry(attr), ui->rgb, false);
|
||||
ADD(new_item, DICTIONARY_OBJ(rgb_attrs));
|
||||
ADD_C(new_item, DICTIONARY_OBJ(rgb_attrs));
|
||||
} else {
|
||||
ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
|
||||
ADD_C(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
|
||||
}
|
||||
ADD(new_item, item.items[1]);
|
||||
ADD(new_contents, ARRAY_OBJ(new_item));
|
||||
ADD_C(new_item, item.items[1]);
|
||||
ADD_C(new_contents, ARRAY_OBJ(new_item));
|
||||
}
|
||||
return new_contents;
|
||||
}
|
||||
|
@ -690,7 +690,7 @@ void nvim_del_current_line(Error *err)
|
||||
/// @param name Variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Variable value
|
||||
Object nvim_get_var(String name, Error *err)
|
||||
Object nvim_get_var(String name, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size);
|
||||
@ -704,7 +704,7 @@ Object nvim_get_var(String name, Error *err)
|
||||
VALIDATE((di != NULL), "Key not found: %s", name.data, {
|
||||
return (Object)OBJECT_INIT;
|
||||
});
|
||||
return vim_to_object(&di->di_tv);
|
||||
return vim_to_object(&di->di_tv, arena, true);
|
||||
}
|
||||
|
||||
/// Sets a global (g:) variable.
|
||||
@ -715,7 +715,7 @@ Object nvim_get_var(String name, Error *err)
|
||||
void nvim_set_var(String name, Object value, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
dict_set_var(&globvardict, name, value, false, false, err);
|
||||
dict_set_var(&globvardict, name, value, false, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Removes a global (g:) variable.
|
||||
@ -725,7 +725,7 @@ void nvim_set_var(String name, Object value, Error *err)
|
||||
void nvim_del_var(String name, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
dict_set_var(&globvardict, name, NIL, true, false, err);
|
||||
dict_set_var(&globvardict, name, NIL, true, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Gets a v: variable.
|
||||
@ -733,10 +733,10 @@ void nvim_del_var(String name, Error *err)
|
||||
/// @param name Variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Variable value
|
||||
Object nvim_get_vvar(String name, Error *err)
|
||||
Object nvim_get_vvar(String name, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
return dict_get_value(&vimvardict, name, err);
|
||||
return dict_get_value(&vimvardict, name, arena, err);
|
||||
}
|
||||
|
||||
/// Sets a v: variable, if it is not readonly.
|
||||
@ -747,7 +747,7 @@ Object nvim_get_vvar(String name, Error *err)
|
||||
void nvim_set_vvar(String name, Object value, Error *err)
|
||||
FUNC_API_SINCE(6)
|
||||
{
|
||||
dict_set_var(&vimvardict, name, value, false, false, err);
|
||||
dict_set_var(&vimvardict, name, value, false, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Echo a message.
|
||||
@ -1370,7 +1370,7 @@ Dictionary nvim_get_color_map(Arena *arena)
|
||||
/// @param[out] err Error details, if any
|
||||
///
|
||||
/// @return map of global |context|.
|
||||
Dictionary nvim_get_context(Dict(context) *opts, Error *err)
|
||||
Dictionary nvim_get_context(Dict(context) *opts, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(6)
|
||||
{
|
||||
Array types = ARRAY_DICT_INIT;
|
||||
@ -1406,7 +1406,7 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err)
|
||||
|
||||
Context ctx = CONTEXT_INIT;
|
||||
ctx_save(&ctx, int_types);
|
||||
Dictionary dict = ctx_to_dict(&ctx);
|
||||
Dictionary dict = ctx_to_dict(&ctx, arena);
|
||||
ctx_free(&ctx);
|
||||
return dict;
|
||||
}
|
||||
@ -2065,7 +2065,7 @@ Boolean nvim_del_mark(String name, Error *err)
|
||||
/// not set.
|
||||
/// @see |nvim_buf_set_mark()|
|
||||
/// @see |nvim_del_mark()|
|
||||
Array nvim_get_mark(String name, Dict(empty) *opts, Error *err)
|
||||
Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(8)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
@ -2113,10 +2113,11 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Error *err)
|
||||
col = pos.col;
|
||||
}
|
||||
|
||||
ADD(rv, INTEGER_OBJ(row));
|
||||
ADD(rv, INTEGER_OBJ(col));
|
||||
ADD(rv, INTEGER_OBJ(bufnr));
|
||||
ADD(rv, CSTR_TO_OBJ(filename));
|
||||
rv = arena_array(arena, 4);
|
||||
ADD_C(rv, INTEGER_OBJ(row));
|
||||
ADD_C(rv, INTEGER_OBJ(col));
|
||||
ADD_C(rv, INTEGER_OBJ(bufnr));
|
||||
ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, filename));
|
||||
|
||||
if (allocated) {
|
||||
xfree(filename);
|
||||
|
@ -148,7 +148,7 @@ void nvim_command(String command, Error *err)
|
||||
/// @param expr Vimscript expression string
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Evaluation result or expanded object
|
||||
Object nvim_eval(String expr, Error *err)
|
||||
Object nvim_eval(String expr, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
static int recursive = 0; // recursion depth
|
||||
@ -179,7 +179,7 @@ Object nvim_eval(String expr, Error *err)
|
||||
api_set_error(err, kErrorTypeException,
|
||||
"Failed to evaluate expression: '%.*s'", 256, expr.data);
|
||||
} else {
|
||||
rv = vim_to_object(&rettv);
|
||||
rv = vim_to_object(&rettv, arena, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ Object nvim_eval(String expr, Error *err)
|
||||
/// @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 Object _call_function(String fn, Array args, dict_T *self, Arena *arena, Error *err)
|
||||
{
|
||||
static int recursive = 0; // recursion depth
|
||||
Object rv = OBJECT_INIT;
|
||||
@ -239,7 +239,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
|
||||
});
|
||||
|
||||
if (!ERROR_SET(err)) {
|
||||
rv = vim_to_object(&rettv);
|
||||
rv = vim_to_object(&rettv, arena, false);
|
||||
}
|
||||
|
||||
tv_clear(&rettv);
|
||||
@ -260,10 +260,10 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
|
||||
/// @param args Function arguments packed in an Array
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Result of the function call
|
||||
Object nvim_call_function(String fn, Array args, Error *err)
|
||||
Object nvim_call_function(String fn, Array args, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
return _call_function(fn, args, NULL, err);
|
||||
return _call_function(fn, args, NULL, arena, err);
|
||||
}
|
||||
|
||||
/// Calls a Vimscript |Dictionary-function| with the given arguments.
|
||||
@ -275,7 +275,7 @@ Object nvim_call_function(String fn, Array args, Error *err)
|
||||
/// @param args Function arguments packed in an Array
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Result of the function call
|
||||
Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
|
||||
Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(4)
|
||||
{
|
||||
Object rv = OBJECT_INIT;
|
||||
@ -337,7 +337,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
|
||||
goto end;
|
||||
}
|
||||
|
||||
rv = _call_function(fn, args, self_dict, err);
|
||||
rv = _call_function(fn, args, self_dict, arena, err);
|
||||
end:
|
||||
if (mustfree) {
|
||||
tv_clear(&rettv);
|
||||
|
@ -237,7 +237,7 @@ void nvim_win_set_width(Window window, Integer width, Error *err)
|
||||
/// @param name Variable name
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return Variable value
|
||||
Object nvim_win_get_var(Window window, String name, Error *err)
|
||||
Object nvim_win_get_var(Window window, String name, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
win_T *win = find_window_by_handle(window, err);
|
||||
@ -246,7 +246,7 @@ Object nvim_win_get_var(Window window, String name, Error *err)
|
||||
return (Object)OBJECT_INIT;
|
||||
}
|
||||
|
||||
return dict_get_value(win->w_vars, name, err);
|
||||
return dict_get_value(win->w_vars, name, arena, err);
|
||||
}
|
||||
|
||||
/// Sets a window-scoped (w:) variable
|
||||
@ -264,7 +264,7 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err)
|
||||
return;
|
||||
}
|
||||
|
||||
dict_set_var(win->w_vars, name, value, false, false, err);
|
||||
dict_set_var(win->w_vars, name, value, false, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Removes a window-scoped (w:) variable
|
||||
@ -281,7 +281,7 @@ void nvim_win_del_var(Window window, String name, Error *err)
|
||||
return;
|
||||
}
|
||||
|
||||
dict_set_var(win->w_vars, name, NIL, true, false, err);
|
||||
dict_set_var(win->w_vars, name, NIL, true, false, NULL, err);
|
||||
}
|
||||
|
||||
/// Gets the window position in display cells. First position is zero.
|
||||
@ -289,15 +289,16 @@ void nvim_win_del_var(Window window, String name, Error *err)
|
||||
/// @param window Window handle, or 0 for current window
|
||||
/// @param[out] err Error details, if any
|
||||
/// @return (row, col) tuple with the window position
|
||||
ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err)
|
||||
ArrayOf(Integer, 2) nvim_win_get_position(Window window, Arena *arena, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
win_T *win = find_window_by_handle(window, err);
|
||||
|
||||
if (win) {
|
||||
ADD(rv, INTEGER_OBJ(win->w_winrow));
|
||||
ADD(rv, INTEGER_OBJ(win->w_wincol));
|
||||
rv = arena_array(arena, 2);
|
||||
ADD_C(rv, INTEGER_OBJ(win->w_winrow));
|
||||
ADD_C(rv, INTEGER_OBJ(win->w_wincol));
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -296,7 +296,7 @@ static inline void ctx_restore_funcs(Context *ctx)
|
||||
/// @param[in] sbuf msgpack_sbuffer to convert.
|
||||
///
|
||||
/// @return readfile()-style array representation of "sbuf".
|
||||
static inline Array sbuf_to_array(msgpack_sbuffer sbuf)
|
||||
static inline Array sbuf_to_array(msgpack_sbuffer sbuf, Arena *arena)
|
||||
{
|
||||
list_T *const list = tv_list_alloc(kListLenMayKnow);
|
||||
tv_list_append_string(list, "", 0);
|
||||
@ -310,7 +310,7 @@ static inline Array sbuf_to_array(msgpack_sbuffer sbuf)
|
||||
.vval.v_list = list
|
||||
};
|
||||
|
||||
Array array = vim_to_object(&list_tv).data.array;
|
||||
Array array = vim_to_object(&list_tv, arena, false).data.array;
|
||||
tv_clear(&list_tv);
|
||||
return array;
|
||||
}
|
||||
@ -346,18 +346,18 @@ static inline msgpack_sbuffer array_to_sbuf(Array array, Error *err)
|
||||
/// @param[in] ctx Context to convert.
|
||||
///
|
||||
/// @return Dictionary representing "ctx".
|
||||
Dictionary ctx_to_dict(Context *ctx)
|
||||
Dictionary ctx_to_dict(Context *ctx, Arena *arena)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
assert(ctx != NULL);
|
||||
|
||||
Dictionary rv = ARRAY_DICT_INIT;
|
||||
Dictionary rv = arena_dict(arena, 5);
|
||||
|
||||
PUT(rv, "regs", ARRAY_OBJ(sbuf_to_array(ctx->regs)));
|
||||
PUT(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps)));
|
||||
PUT(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs)));
|
||||
PUT(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars)));
|
||||
PUT(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, NULL)));
|
||||
PUT_C(rv, "regs", ARRAY_OBJ(sbuf_to_array(ctx->regs, arena)));
|
||||
PUT_C(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps, arena)));
|
||||
PUT_C(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs, arena)));
|
||||
PUT_C(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars, arena)));
|
||||
PUT_C(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, arena)));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -1355,7 +1355,7 @@ Object eval_foldtext(win_T *wp)
|
||||
retval = STRING_OBJ(NULL_STRING);
|
||||
} else {
|
||||
if (tv.v_type == VAR_LIST) {
|
||||
retval = vim_to_object(&tv);
|
||||
retval = vim_to_object(&tv, NULL, false);
|
||||
} else {
|
||||
retval = STRING_OBJ(cstr_to_string(tv_get_string(&tv)));
|
||||
}
|
||||
|
@ -359,15 +359,15 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|
||||
MsgpackRpcRequestHandler handler = *fptr.api_handler;
|
||||
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS);
|
||||
Arena arena = ARENA_EMPTY;
|
||||
|
||||
for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) {
|
||||
ADD(args, vim_to_object(tv));
|
||||
ADD_C(args, vim_to_object(tv, &arena, false));
|
||||
}
|
||||
|
||||
Error err = ERROR_INIT;
|
||||
Arena res_arena = ARENA_EMPTY;
|
||||
Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err);
|
||||
Object result = handler.fn(VIML_INTERNAL_CALL, args, &arena, &err);
|
||||
|
||||
if (ERROR_SET(&err)) {
|
||||
semsg_multiline(e_api_error, err.msg);
|
||||
@ -377,12 +377,10 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
object_to_vim(result, rettv, &err);
|
||||
|
||||
end:
|
||||
api_free_array(args);
|
||||
if (handler.arena_return) {
|
||||
arena_mem_free(arena_finish(&res_arena));
|
||||
} else {
|
||||
if (!handler.arena_return) {
|
||||
api_free_object(result);
|
||||
}
|
||||
arena_mem_free(arena_finish(&arena));
|
||||
api_clear_error(&err);
|
||||
}
|
||||
|
||||
@ -1037,10 +1035,11 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary ctx_dict = ctx_to_dict(ctx);
|
||||
Arena arena = ARENA_EMPTY;
|
||||
Dictionary ctx_dict = ctx_to_dict(ctx, &arena);
|
||||
Error err = ERROR_INIT;
|
||||
object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err);
|
||||
api_free_dictionary(ctx_dict);
|
||||
arena_mem_free(arena_finish(&arena));
|
||||
api_clear_error(&err);
|
||||
}
|
||||
|
||||
@ -1108,7 +1107,8 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
const int save_did_emsg = did_emsg;
|
||||
did_emsg = false;
|
||||
|
||||
Dictionary dict = vim_to_object(&argvars[0]).data.dictionary;
|
||||
Arena arena = ARENA_EMPTY;
|
||||
Dictionary dict = vim_to_object(&argvars[0], &arena, true).data.dictionary;
|
||||
Context tmp = CONTEXT_INIT;
|
||||
Error err = ERROR_INIT;
|
||||
ctx_from_dict(dict, &tmp, &err);
|
||||
@ -1121,7 +1121,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
*ctx = tmp;
|
||||
}
|
||||
|
||||
api_free_dictionary(dict);
|
||||
arena_mem_free(arena_finish(&arena));
|
||||
api_clear_error(&err);
|
||||
did_emsg = save_did_emsg;
|
||||
}
|
||||
@ -6883,16 +6883,17 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
return;
|
||||
}
|
||||
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS);
|
||||
Arena arena = ARENA_EMPTY;
|
||||
|
||||
for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) {
|
||||
ADD(args, vim_to_object(tv));
|
||||
ADD_C(args, vim_to_object(tv, &arena, true));
|
||||
}
|
||||
|
||||
bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number,
|
||||
tv_get_string(&argvars[1]), args);
|
||||
|
||||
api_free_array(args);
|
||||
arena_mem_free(arena_finish(&arena));
|
||||
|
||||
if (!ok) {
|
||||
semsg(_(e_invarg2), "Channel doesn't exist");
|
||||
@ -6922,10 +6923,11 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
return;
|
||||
}
|
||||
|
||||
Array args = ARRAY_DICT_INIT;
|
||||
MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS);
|
||||
Arena arena = ARENA_EMPTY;
|
||||
|
||||
for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) {
|
||||
ADD(args, vim_to_object(tv));
|
||||
ADD_C(args, vim_to_object(tv, &arena, true));
|
||||
}
|
||||
|
||||
sctx_T save_current_sctx;
|
||||
@ -6961,7 +6963,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
|
||||
ArenaMem res_mem = NULL;
|
||||
Object result = rpc_send_call(chan_id, method, args, &res_mem, &err);
|
||||
api_free_array(args);
|
||||
arena_mem_free(arena_finish(&arena));
|
||||
|
||||
if (l_provider_call_nesting) {
|
||||
current_sctx = save_current_sctx;
|
||||
@ -8868,10 +8870,10 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
Error err = ERROR_INIT;
|
||||
// deprecated: use 'channel' buffer option
|
||||
dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
|
||||
INTEGER_OBJ((Integer)chan->id), false, false, &err);
|
||||
INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err);
|
||||
api_clear_error(&err);
|
||||
dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
|
||||
INTEGER_OBJ(pid), false, false, &err);
|
||||
INTEGER_OBJ(pid), false, false, NULL, &err);
|
||||
api_clear_error(&err);
|
||||
|
||||
channel_incref(chan);
|
||||
|
@ -845,15 +845,17 @@ exit_0:
|
||||
write_shifted_output(' %s ret = %s(%s);\n', fn.return_type, fn.name, cparams)
|
||||
|
||||
local ret_type = real_type(fn.return_type)
|
||||
local ret_mode = (ret_type == 'Object') and '&' or ''
|
||||
if fn.has_lua_imp then
|
||||
-- only push onto the Lua stack if we haven't already
|
||||
write_shifted_output(string.format(
|
||||
[[
|
||||
if (lua_gettop(lstate) == 0) {
|
||||
nlua_push_%s(lstate, ret, true);
|
||||
nlua_push_%s(lstate, %sret, true);
|
||||
}
|
||||
]],
|
||||
return_type
|
||||
return_type,
|
||||
ret_mode
|
||||
))
|
||||
elseif string.match(ret_type, '^KeyDict_') then
|
||||
write_shifted_output(
|
||||
@ -862,7 +864,12 @@ exit_0:
|
||||
)
|
||||
else
|
||||
local special = (fn.since ~= nil and fn.since < 11)
|
||||
write_shifted_output(' nlua_push_%s(lstate, ret, %s);\n', return_type, tostring(special))
|
||||
write_shifted_output(
|
||||
' nlua_push_%s(lstate, %sret, %s);\n',
|
||||
return_type,
|
||||
ret_mode,
|
||||
tostring(special)
|
||||
)
|
||||
end
|
||||
|
||||
-- NOTE: we currently assume err_throw needs nothing from arena
|
||||
|
@ -719,7 +719,7 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special
|
||||
}
|
||||
for (size_t i = 0; i < dict.size; i++) {
|
||||
nlua_push_String(lstate, dict.items[i].key, special);
|
||||
nlua_push_Object(lstate, dict.items[i].value, special);
|
||||
nlua_push_Object(lstate, &dict.items[i].value, special);
|
||||
lua_rawset(lstate, -3);
|
||||
}
|
||||
}
|
||||
@ -732,7 +732,7 @@ void nlua_push_Array(lua_State *lstate, const Array array, bool special)
|
||||
{
|
||||
lua_createtable(lstate, (int)array.size, 0);
|
||||
for (size_t i = 0; i < array.size; i++) {
|
||||
nlua_push_Object(lstate, array.items[i], special);
|
||||
nlua_push_Object(lstate, &array.items[i], special);
|
||||
lua_rawseti(lstate, -2, (int)i + 1);
|
||||
}
|
||||
}
|
||||
@ -753,10 +753,10 @@ GENERATE_INDEX_FUNCTION(Tabpage)
|
||||
/// Convert given Object to lua value
|
||||
///
|
||||
/// Leaves converted value on top of the stack.
|
||||
void nlua_push_Object(lua_State *lstate, const Object obj, bool special)
|
||||
void nlua_push_Object(lua_State *lstate, Object *obj, bool special)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
switch (obj.type) {
|
||||
switch (obj->type) {
|
||||
case kObjectTypeNil:
|
||||
if (special) {
|
||||
lua_pushnil(lstate);
|
||||
@ -765,12 +765,14 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special)
|
||||
}
|
||||
break;
|
||||
case kObjectTypeLuaRef: {
|
||||
nlua_pushref(lstate, obj.data.luaref);
|
||||
nlua_pushref(lstate, obj->data.luaref);
|
||||
api_free_luaref(obj->data.luaref);
|
||||
obj->data.luaref = LUA_NOREF;
|
||||
break;
|
||||
}
|
||||
#define ADD_TYPE(type, data_key) \
|
||||
case kObjectType##type: { \
|
||||
nlua_push_##type(lstate, obj.data.data_key, special); \
|
||||
nlua_push_##type(lstate, obj->data.data_key, special); \
|
||||
break; \
|
||||
}
|
||||
ADD_TYPE(Boolean, boolean)
|
||||
@ -782,7 +784,7 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special)
|
||||
#undef ADD_TYPE
|
||||
#define ADD_REMOTE_TYPE(type) \
|
||||
case kObjectType##type: { \
|
||||
nlua_push_##type(lstate, (type)obj.data.integer, special); \
|
||||
nlua_push_##type(lstate, (type)obj->data.integer, special); \
|
||||
break; \
|
||||
}
|
||||
ADD_REMOTE_TYPE(Buffer)
|
||||
@ -1378,7 +1380,7 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table)
|
||||
|
||||
lua_pushstring(L, field->str);
|
||||
if (field->type == kObjectTypeNil) {
|
||||
nlua_push_Object(L, *(Object *)mem, false);
|
||||
nlua_push_Object(L, (Object *)mem, false);
|
||||
} else if (field->type == kObjectTypeInteger || field->type == kObjectTypeBuffer
|
||||
|| field->type == kObjectTypeWindow || field->type == kObjectTypeTabpage) {
|
||||
lua_pushinteger(L, *(Integer *)mem);
|
||||
|
@ -1260,7 +1260,7 @@ static int nlua_rpc(lua_State *lstate, bool request)
|
||||
ArenaMem res_mem = NULL;
|
||||
Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
|
||||
if (!ERROR_SET(&err)) {
|
||||
nlua_push_Object(lstate, result, false);
|
||||
nlua_push_Object(lstate, &result, false);
|
||||
arena_mem_free(res_mem);
|
||||
}
|
||||
} else {
|
||||
@ -1563,7 +1563,7 @@ Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *are
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < args.size; i++) {
|
||||
nlua_push_Object(lstate, args.items[i], false);
|
||||
nlua_push_Object(lstate, &args.items[i], false);
|
||||
}
|
||||
|
||||
if (nlua_pcall(lstate, (int)args.size, 1)) {
|
||||
@ -1610,7 +1610,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode,
|
||||
nargs++;
|
||||
}
|
||||
for (size_t i = 0; i < args.size; i++) {
|
||||
nlua_push_Object(lstate, args.items[i], false);
|
||||
nlua_push_Object(lstate, &args.items[i], false);
|
||||
}
|
||||
|
||||
if (nlua_pcall(lstate, nargs, 1)) {
|
||||
|
@ -1102,7 +1102,7 @@ static void command_line_scan(mparm_T *parmp)
|
||||
}
|
||||
|
||||
Object md = DICTIONARY_OBJ(api_metadata());
|
||||
msgpack_rpc_from_object(md, p);
|
||||
msgpack_rpc_from_object(&md, p);
|
||||
|
||||
msgpack_packer_free(p);
|
||||
const int ff_ret = file_flush(&fp);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "nvim/eval.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/eval/typval_defs.h"
|
||||
#include "nvim/eval/userfunc.h"
|
||||
#include "nvim/ex_cmds_defs.h"
|
||||
#include "nvim/ex_session.h"
|
||||
#include "nvim/garray.h"
|
||||
@ -2305,13 +2306,13 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||
LuaRef rhs_lua = LUA_NOREF;
|
||||
dictitem_T *callback_di = tv_dict_find(d, S_LEN("callback"));
|
||||
if (callback_di != NULL) {
|
||||
Object callback_obj = vim_to_object(&callback_di->di_tv);
|
||||
if (callback_obj.type == kObjectTypeLuaRef && callback_obj.data.luaref != LUA_NOREF) {
|
||||
rhs_lua = callback_obj.data.luaref;
|
||||
orig_rhs = "";
|
||||
callback_obj.data.luaref = LUA_NOREF;
|
||||
if (callback_di->di_tv.v_type == VAR_FUNC) {
|
||||
ufunc_T *fp = find_func(callback_di->di_tv.vval.v_string);
|
||||
if (fp != NULL && (fp->uf_flags & FC_LUAREF)) {
|
||||
rhs_lua = api_new_luaref(fp->uf_luaref);
|
||||
orig_rhs = "";
|
||||
}
|
||||
}
|
||||
api_free_object(callback_obj);
|
||||
}
|
||||
if (lhs == NULL || lhsraw == NULL || orig_rhs == NULL) {
|
||||
emsg(_(e_entries_missing_in_mapset_dict_argument));
|
||||
|
@ -449,7 +449,7 @@ static void request_event(void **argv)
|
||||
e->type,
|
||||
e->request_id,
|
||||
&error,
|
||||
result,
|
||||
&result,
|
||||
&out_buffer));
|
||||
}
|
||||
if (!handler.arena_return) {
|
||||
@ -538,7 +538,7 @@ static void send_error(Channel *chan, MsgpackRpcRequestHandler handler, MessageT
|
||||
type,
|
||||
id,
|
||||
&e,
|
||||
NIL,
|
||||
&NIL,
|
||||
&out_buffer));
|
||||
api_clear_error(&e);
|
||||
}
|
||||
@ -669,7 +669,7 @@ static WBuffer *serialize_request(uint64_t channel_id, uint32_t request_id, cons
|
||||
}
|
||||
|
||||
static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler handler,
|
||||
MessageType type, uint32_t response_id, Error *err, Object arg,
|
||||
MessageType type, uint32_t response_id, Error *err, Object *arg,
|
||||
msgpack_sbuffer *sbuffer)
|
||||
{
|
||||
msgpack_packer pac;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "msgpack/pack.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/assert_defs.h"
|
||||
#include "nvim/lua/executor.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/msgpack_rpc/helpers.h"
|
||||
#include "nvim/types_defs.h"
|
||||
@ -309,34 +310,40 @@ static void msgpack_rpc_from_handle(ObjectType type, Integer o, msgpack_packer *
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const Object *aobj;
|
||||
Object *aobj;
|
||||
bool container;
|
||||
size_t idx;
|
||||
} APIToMPObjectStackItem;
|
||||
|
||||
/// Convert type used by Nvim API to msgpack type.
|
||||
///
|
||||
/// consumes (frees) any luaref inside `result`, even though they are not used
|
||||
/// (just represented as NIL)
|
||||
///
|
||||
/// @param[in] result Object to convert.
|
||||
/// @param[out] res Structure that defines where conversion results are saved.
|
||||
///
|
||||
/// @return true in case of success, false otherwise.
|
||||
void msgpack_rpc_from_object(const Object result, msgpack_packer *const res)
|
||||
void msgpack_rpc_from_object(Object *result, msgpack_packer *const res)
|
||||
FUNC_ATTR_NONNULL_ARG(2)
|
||||
{
|
||||
kvec_withinit_t(APIToMPObjectStackItem, 2) stack = KV_INITIAL_VALUE;
|
||||
kvi_init(stack);
|
||||
kvi_push(stack, ((APIToMPObjectStackItem) { &result, false, 0 }));
|
||||
kvi_push(stack, ((APIToMPObjectStackItem) { result, false, 0 }));
|
||||
while (kv_size(stack)) {
|
||||
APIToMPObjectStackItem cur = kv_last(stack);
|
||||
STATIC_ASSERT(kObjectTypeWindow == kObjectTypeBuffer + 1
|
||||
&& kObjectTypeTabpage == kObjectTypeWindow + 1,
|
||||
"Buffer, window and tabpage enum items are in order");
|
||||
switch (cur.aobj->type) {
|
||||
case kObjectTypeNil:
|
||||
case kObjectTypeLuaRef:
|
||||
// TODO(bfredl): could also be an error. Though kObjectTypeLuaRef
|
||||
// should only appear when the caller has opted in to handle references,
|
||||
// see nlua_pop_Object.
|
||||
api_free_luaref(cur.aobj->data.luaref);
|
||||
cur.aobj->data.luaref = LUA_NOREF;
|
||||
FALLTHROUGH;
|
||||
case kObjectTypeNil:
|
||||
msgpack_pack_nil(res);
|
||||
break;
|
||||
case kObjectTypeBoolean:
|
||||
@ -415,7 +422,7 @@ void msgpack_rpc_from_array(Array result, msgpack_packer *res)
|
||||
msgpack_pack_array(res, result.size);
|
||||
|
||||
for (size_t i = 0; i < result.size; i++) {
|
||||
msgpack_rpc_from_object(result.items[i], res);
|
||||
msgpack_rpc_from_object(&result.items[i], res);
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,7 +433,7 @@ void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res)
|
||||
|
||||
for (size_t i = 0; i < result.size; i++) {
|
||||
msgpack_rpc_from_string(result.items[i].key, res);
|
||||
msgpack_rpc_from_object(result.items[i].value, res);
|
||||
msgpack_rpc_from_object(&result.items[i].value, res);
|
||||
}
|
||||
}
|
||||
|
||||
@ -447,9 +454,9 @@ void msgpack_rpc_serialize_request(uint32_t request_id, const String method, Arr
|
||||
}
|
||||
|
||||
/// Serializes a msgpack-rpc response
|
||||
void msgpack_rpc_serialize_response(uint32_t response_id, Error *err, Object arg,
|
||||
void msgpack_rpc_serialize_response(uint32_t response_id, Error *err, Object *arg,
|
||||
msgpack_packer *pac)
|
||||
FUNC_ATTR_NONNULL_ARG(2, 4)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
msgpack_pack_array(pac, 4);
|
||||
msgpack_pack_int(pac, 1);
|
||||
|
@ -343,7 +343,6 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
|
||||
if (name) {
|
||||
int dummy;
|
||||
RgbValue color_val = name_to_color(name, &dummy);
|
||||
xfree(name);
|
||||
|
||||
if (color_val != -1) {
|
||||
VTermColor color;
|
||||
@ -1003,6 +1002,7 @@ static void buf_set_term_title(buf_T *buf, const char *title, size_t len)
|
||||
STRING_OBJ(((String){ .data = (char *)title, .size = len })),
|
||||
false,
|
||||
false,
|
||||
NULL,
|
||||
&err);
|
||||
api_clear_error(&err);
|
||||
status_redraw_buf(buf);
|
||||
@ -1883,10 +1883,10 @@ static char *get_config_string(char *key)
|
||||
{
|
||||
Error err = ERROR_INIT;
|
||||
// Only called from terminal_open where curbuf->terminal is the context.
|
||||
Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), &err);
|
||||
Object obj = dict_get_value(curbuf->b_vars, cstr_as_string(key), NULL, &err);
|
||||
api_clear_error(&err);
|
||||
if (obj.type == kObjectTypeNil) {
|
||||
obj = dict_get_value(&globvardict, cstr_as_string(key), &err);
|
||||
obj = dict_get_value(&globvardict, cstr_as_string(key), NULL, &err);
|
||||
api_clear_error(&err);
|
||||
}
|
||||
if (obj.type == kObjectTypeString) {
|
||||
|
@ -22,7 +22,7 @@ local api = cimport('./src/nvim/api/private/helpers.h', './src/nvim/api/private/
|
||||
|
||||
describe('vim_to_object', function()
|
||||
local vim_to_object = function(l)
|
||||
return obj2lua(api.vim_to_object(lua2typvalt(l)))
|
||||
return obj2lua(api.vim_to_object(lua2typvalt(l), nil, false))
|
||||
end
|
||||
|
||||
local different_output_test = function(name, input, output)
|
||||
@ -92,13 +92,13 @@ describe('vim_to_object', function()
|
||||
itp('outputs empty list for NULL list', function()
|
||||
local tt = typvalt('VAR_LIST', { v_list = NULL })
|
||||
eq(nil, tt.vval.v_list)
|
||||
eq({ [type_key] = list_type }, obj2lua(api.vim_to_object(tt)))
|
||||
eq({ [type_key] = list_type }, obj2lua(api.vim_to_object(tt, nil, false)))
|
||||
end)
|
||||
|
||||
itp('outputs empty dict for NULL dict', function()
|
||||
local tt = typvalt('VAR_DICT', { v_dict = NULL })
|
||||
eq(nil, tt.vval.v_dict)
|
||||
eq({}, obj2lua(api.vim_to_object(tt)))
|
||||
eq({}, obj2lua(api.vim_to_object(tt, nil, false)))
|
||||
end)
|
||||
|
||||
itp('regression: partials in a list', function()
|
||||
@ -113,6 +113,6 @@ describe('vim_to_object', function()
|
||||
}
|
||||
local list = lua2typvalt(llist)
|
||||
eq(llist, typvalt2lua(list))
|
||||
eq({ nil_value, {} }, obj2lua(api.vim_to_object(list)))
|
||||
eq({ nil_value, {} }, obj2lua(api.vim_to_object(list, nil, false)))
|
||||
end)
|
||||
end)
|
||||
|
Loading…
Reference in New Issue
Block a user