aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/api')
-rw-r--r--src/nvim/api/autocmd.c252
-rw-r--r--src/nvim/api/buffer.c347
-rw-r--r--src/nvim/api/buffer.h3
-rw-r--r--src/nvim/api/command.c218
-rw-r--r--src/nvim/api/deprecated.c114
-rw-r--r--src/nvim/api/deprecated.h1
-rw-r--r--src/nvim/api/dispatch_deprecated.lua132
-rw-r--r--src/nvim/api/extmark.c360
-rw-r--r--src/nvim/api/keysets_defs.h76
-rw-r--r--src/nvim/api/options.c344
-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
-rw-r--r--src/nvim/api/tabpage.c54
-rw-r--r--src/nvim/api/ui.c499
-rw-r--r--src/nvim/api/ui.h19
-rw-r--r--src/nvim/api/ui_events.in.h2
-rw-r--r--src/nvim/api/vim.c600
-rw-r--r--src/nvim/api/vimscript.c285
-rw-r--r--src/nvim/api/win_config.c630
-rw-r--r--src/nvim/api/window.c45
24 files changed, 2248 insertions, 2194 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 08d9d8e117..d71bcc4bcf 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -14,13 +14,17 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
-#include "nvim/func_attr.h"
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/strings.h"
+#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -30,12 +34,12 @@
#define AUCMD_MAX_PATTERNS 256
// Copy string or array of strings into an empty array.
-// Get the event number, unless it is an error. Then goto `goto_name`.
-#define GET_ONE_EVENT(event_nr, event_str, goto_name) \
+// Get the event number, unless it is an error. Then do `or_else`.
+#define GET_ONE_EVENT(event_nr, event_str, or_else) \
event_T event_nr = \
event_name2nr_str(event_str.data.string); \
VALIDATE_S((event_nr < NUM_EVENTS), "event", event_str.data.string.data, { \
- goto goto_name; \
+ or_else; \
});
// ID for associating autocmds created via nvim_create_autocmd
@@ -71,7 +75,7 @@ static int64_t next_autocmd_id = 1;
/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands
/// |autocmd-buflocal|. Cannot be used with {pattern}
/// @return Array of autocommands matching the criteria, with each item
-/// containing the following fields:
+/// containing the following fields:
/// - id (number): the autocommand id (only when defined with the API).
/// - group (integer): the autocommand group id.
/// - group_name (string): the autocommand group name.
@@ -79,20 +83,20 @@ static int64_t next_autocmd_id = 1;
/// - event (string): the autocommand event.
/// - command (string): the autocommand command. Note: this will be empty if a callback is set.
/// - callback (function|string|nil): Lua function or name of a Vim script function
-/// which is executed when this autocommand is triggered.
+/// which is executed when this autocommand is triggered.
/// - once (boolean): whether the autocommand is only run once.
/// - pattern (string): the autocommand pattern.
-/// If the autocommand is buffer local |autocmd-buffer-local|:
+/// If the autocommand is buffer local |autocmd-buffer-local|:
/// - buflocal (boolean): true if the autocommand is buffer local.
/// - buffer (number): the buffer number.
-Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
+Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(9)
{
// TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... })
- Array autocmd_list = ARRAY_DICT_INIT;
+ ArrayBuilder autocmd_list = KV_INITIAL_VALUE;
+ kvi_init(autocmd_list);
char *pattern_filters[AUCMD_MAX_PATTERNS];
- char pattern_buflocal[BUFLOCAL_PAT_LEN];
Array buffers = ARRAY_DICT_INIT;
@@ -128,7 +132,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
Object v = opts->event;
if (v.type == kObjectTypeString) {
- GET_ONE_EVENT(event_nr, v, cleanup);
+ GET_ONE_EVENT(event_nr, v, goto cleanup);
event_set[event_nr] = true;
} else if (v.type == kObjectTypeArray) {
FOREACH_ITEM(v.data.array, event_v, {
@@ -136,7 +140,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
goto cleanup;
});
- GET_ONE_EVENT(event_nr, event_v, cleanup);
+ GET_ONE_EVENT(event_nr, event_v, goto cleanup);
event_set[event_nr] = true;
})
} else {
@@ -184,8 +188,9 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
goto cleanup;
}
- snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
- ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
+ String pat = arena_printf(arena, "<buffer=%d>", (int)buf->handle);
+ buffers = arena_array(arena, 1);
+ ADD_C(buffers, STRING_OBJ(pat));
} else if (opts->buffer.type == kObjectTypeArray) {
if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) {
api_set_error(err, kErrorTypeValidation, "Too many buffers (maximum of %d)",
@@ -193,6 +198,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
goto cleanup;
}
+ buffers = arena_array(arena, kv_size(opts->buffer.data.array));
FOREACH_ITEM(opts->buffer.data.array, bufnr, {
VALIDATE_EXP((bufnr.type == kObjectTypeInteger || bufnr.type == kObjectTypeBuffer),
"buffer", "Integer", api_typename(bufnr.type), {
@@ -204,8 +210,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
goto cleanup;
}
- snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
- ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
+ ADD_C(buffers, STRING_OBJ(arena_printf(arena, "<buffer=%d>", (int)buf->handle)));
});
} else if (HAS_KEY(opts, get_autocmds, buffer)) {
VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), {
@@ -247,6 +252,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
char *pat = pattern_filters[j];
int patlen = (int)strlen(pat);
+ char pattern_buflocal[BUFLOCAL_PAT_LEN];
if (aupat_is_buflocal(pat, patlen)) {
aupat_normalize_buflocal_pat(pattern_buflocal, pat, patlen,
aupat_get_buflocal_nr(pat, patlen));
@@ -264,51 +270,51 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
}
}
- Dictionary autocmd_info = ARRAY_DICT_INIT;
+ Dictionary autocmd_info = arena_dict(arena, 11);
if (ap->group != AUGROUP_DEFAULT) {
- PUT(autocmd_info, "group", INTEGER_OBJ(ap->group));
- PUT(autocmd_info, "group_name", CSTR_TO_OBJ(augroup_name(ap->group)));
+ PUT_C(autocmd_info, "group", INTEGER_OBJ(ap->group));
+ PUT_C(autocmd_info, "group_name", CSTR_AS_OBJ(augroup_name(ap->group)));
}
if (ac->id > 0) {
- PUT(autocmd_info, "id", INTEGER_OBJ(ac->id));
+ PUT_C(autocmd_info, "id", INTEGER_OBJ(ac->id));
}
if (ac->desc != NULL) {
- PUT(autocmd_info, "desc", CSTR_TO_OBJ(ac->desc));
+ PUT_C(autocmd_info, "desc", CSTR_AS_OBJ(ac->desc));
}
if (ac->exec.type == CALLABLE_CB) {
- PUT(autocmd_info, "command", STRING_OBJ(STRING_INIT));
+ PUT_C(autocmd_info, "command", STRING_OBJ(STRING_INIT));
Callback *cb = &ac->exec.callable.cb;
switch (cb->type) {
case kCallbackLua:
if (nlua_ref_is_function(cb->data.luaref)) {
- PUT(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref)));
+ PUT_C(autocmd_info, "callback", LUAREF_OBJ(api_new_luaref(cb->data.luaref)));
}
break;
case kCallbackFuncref:
case kCallbackPartial:
- PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb)));
+ PUT_C(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, arena)));
break;
case kCallbackNone:
abort();
}
} else {
- PUT(autocmd_info, "command", CSTR_TO_OBJ(ac->exec.callable.cmd));
+ PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->exec.callable.cmd));
}
- PUT(autocmd_info, "pattern", CSTR_TO_OBJ(ap->pat));
- PUT(autocmd_info, "event", CSTR_TO_OBJ(event_nr2name(event)));
- PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once));
+ PUT_C(autocmd_info, "pattern", CSTR_AS_OBJ(ap->pat));
+ PUT_C(autocmd_info, "event", CSTR_AS_OBJ(event_nr2name(event)));
+ PUT_C(autocmd_info, "once", BOOLEAN_OBJ(ac->once));
if (ap->buflocal_nr) {
- PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(true));
- PUT(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr));
+ PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(true));
+ PUT_C(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr));
} else {
- PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(false));
+ PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(false));
}
// TODO(sctx): It would be good to unify script_ctx to actually work with lua
@@ -325,16 +331,15 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
// Once we do that, we can put these into the autocmd_info, but I don't think it's
// useful to do that at this time.
//
- // PUT(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid));
- // PUT(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum));
+ // PUT_C(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid));
+ // PUT_C(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum));
- ADD(autocmd_list, DICTIONARY_OBJ(autocmd_info));
+ kvi_push(autocmd_list, DICTIONARY_OBJ(autocmd_info));
}
}
cleanup:
- api_free_array(buffers);
- return autocmd_list;
+ return arena_take_arraybuilder(arena, &autocmd_list);
}
/// Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript
@@ -375,15 +380,16 @@ cleanup:
/// |autocmd-buflocal|. Cannot be used with {pattern}.
/// - desc (string) optional: description (for documentation and troubleshooting).
/// - callback (function|string) optional: Lua function (or Vimscript function name, if
-/// string) called when the event(s) is triggered. Lua callback can return true to
-/// delete the autocommand, and receives a table argument with these keys:
+/// string) called when the event(s) is triggered. Lua callback can return a truthy
+/// value (not `false` or `nil`) to delete the autocommand. Receives a table argument
+/// with these keys:
/// - id: (number) autocommand id
/// - event: (string) name of the triggered event |autocmd-events|
/// - group: (number|nil) autocommand group id, if any
-/// - match: (string) expanded value of |<amatch>|
-/// - buf: (number) expanded value of |<abuf>|
-/// - file: (string) expanded value of |<afile>|
-/// - data: (any) arbitrary data passed from |nvim_exec_autocmds()|
+/// - match: (string) expanded value of [<amatch>]
+/// - buf: (number) expanded value of [<abuf>]
+/// - file: (string) expanded value of [<afile>]
+/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()]
/// - command (string) optional: Vim command to execute on event. Cannot be used with
/// {callback}
/// - once (boolean) optional: defaults to false. Run the autocommand
@@ -395,17 +401,16 @@ cleanup:
/// @see |autocommand|
/// @see |nvim_del_autocmd()|
Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts,
- Error *err)
+ Arena *arena, Error *err)
FUNC_API_SINCE(9)
{
int64_t autocmd_id = -1;
char *desc = NULL;
- Array patterns = ARRAY_DICT_INIT;
- Array event_array = ARRAY_DICT_INIT;
AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT;
Callback cb = CALLBACK_NONE;
- if (!unpack_string_or_array(&event_array, &event, "event", true, err)) {
+ Array event_array = unpack_string_or_array(event, "event", true, arena, err);
+ if (ERROR_SET(err)) {
goto cleanup;
}
@@ -428,7 +433,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
});
cb.type = kCallbackLua;
- cb.data.luaref = api_new_luaref(callback->data.luaref);
+ cb.data.luaref = callback->data.luaref;
+ callback->data.luaref = LUA_NOREF;
break;
case kObjectTypeString:
cb.type = kCallbackFuncref;
@@ -464,7 +470,9 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
});
- if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) {
+ Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "*",
+ arena, err);
+ if (ERROR_SET(err)) {
goto cleanup;
}
@@ -472,17 +480,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
desc = opts->desc.data;
}
- if (patterns.size == 0) {
- ADD(patterns, STATIC_CSTR_TO_OBJ("*"));
- }
-
VALIDATE_R((event_array.size > 0), "event", {
goto cleanup;
});
autocmd_id = next_autocmd_id++;
FOREACH_ITEM(event_array, event_str, {
- GET_ONE_EVENT(event_nr, event_str, cleanup);
+ GET_ONE_EVENT(event_nr, event_str, goto cleanup);
int retval;
@@ -509,8 +513,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
cleanup:
aucmd_exec_free(&aucmd);
- api_free_array(event_array);
- api_free_array(patterns);
return autocmd_id;
}
@@ -534,9 +536,9 @@ void nvim_del_autocmd(Integer id, Error *err)
/// @param opts Parameters
/// - event: (string|table)
/// Examples:
-/// - event: "pat1"
-/// - event: { "pat1" }
-/// - event: { "pat1", "pat2", "pat3" }
+/// - event: "pat1"
+/// - event: { "pat1" }
+/// - event: { "pat1", "pat2", "pat3" }
/// - pattern: (string|table)
/// - pattern or patterns to match exactly.
/// - For example, if you have `*.py` as that pattern for the autocmd,
@@ -550,7 +552,7 @@ void nvim_del_autocmd(Integer id, Error *err)
/// - group: (string|int) The augroup name or id.
/// - NOTE: If not passed, will only delete autocmds *not* in any group.
///
-void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
+void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(9)
{
// TODO(tjdevries): Future improvements:
@@ -559,33 +561,29 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
// - group: Allow passing "*" or true or something like that to force doing all
// autocmds, regardless of their group.
- Array patterns = ARRAY_DICT_INIT;
- Array event_array = ARRAY_DICT_INIT;
-
- if (!unpack_string_or_array(&event_array, &opts->event, "event", false, err)) {
- goto cleanup;
+ Array event_array = unpack_string_or_array(opts->event, "event", false, arena, err);
+ if (ERROR_SET(err)) {
+ return;
}
bool has_buffer = HAS_KEY(opts, clear_autocmds, buffer);
VALIDATE((!HAS_KEY(opts, clear_autocmds, pattern) || !has_buffer),
"%s", "Cannot use both 'pattern' and 'buffer'", {
- goto cleanup;
+ return;
});
int au_group = get_augroup_from_object(opts->group, err);
if (au_group == AUGROUP_ERROR) {
- goto cleanup;
- }
-
- if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) {
- goto cleanup;
+ return;
}
// When we create the autocmds, we want to say that they are all matched, so that's *
// but when we clear them, we want to say that we didn't pass a pattern, so that's NUL
- if (patterns.size == 0) {
- ADD(patterns, STATIC_CSTR_TO_OBJ(""));
+ Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "",
+ arena, err);
+ if (ERROR_SET(err)) {
+ return;
}
// If we didn't pass any events, that means clear all events.
@@ -594,26 +592,22 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
FOREACH_ITEM(patterns, pat_object, {
char *pat = pat_object.data.string.data;
if (!clear_autocmd(event, pat, au_group, err)) {
- goto cleanup;
+ return;
}
});
}
} else {
FOREACH_ITEM(event_array, event_str, {
- GET_ONE_EVENT(event_nr, event_str, cleanup);
+ GET_ONE_EVENT(event_nr, event_str, return );
FOREACH_ITEM(patterns, pat_object, {
char *pat = pat_object.data.string.data;
if (!clear_autocmd(event_nr, pat, au_group, err)) {
- goto cleanup;
+ return;
}
});
});
}
-
-cleanup:
- api_free_array(event_array);
- api_free_array(patterns);
}
/// Create or get an autocommand group |autocmd-groups|.
@@ -700,11 +694,11 @@ void nvim_del_augroup_by_name(String name, Error *err)
/// - buffer (integer) optional: buffer number |autocmd-buflocal|. Cannot be used with
/// {pattern}.
/// - modeline (bool) optional: defaults to true. Process the
-/// modeline after the autocommands |<nomodeline>|.
+/// modeline after the autocommands [<nomodeline>].
/// - data (any): arbitrary data to send to the autocommand callback. See
/// |nvim_create_autocmd()| for details.
/// @see |:doautocmd|
-void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
+void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(9)
{
int au_group = AUGROUP_ALL;
@@ -712,13 +706,11 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
buf_T *buf = curbuf;
- Array patterns = ARRAY_DICT_INIT;
- Array event_array = ARRAY_DICT_INIT;
-
Object *data = NULL;
- if (!unpack_string_or_array(&event_array, &event, "event", true, err)) {
- goto cleanup;
+ Array event_array = unpack_string_or_array(event, "event", true, arena, err);
+ if (ERROR_SET(err)) {
+ return;
}
switch (opts->group.type) {
@@ -727,19 +719,19 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
case kObjectTypeString:
au_group = augroup_find(opts->group.data.string.data);
VALIDATE_S((au_group != AUGROUP_ERROR), "group", opts->group.data.string.data, {
- goto cleanup;
+ return;
});
break;
case kObjectTypeInteger:
au_group = (int)opts->group.data.integer;
char *name = au_group == 0 ? NULL : augroup_name(au_group);
VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, {
- goto cleanup;
+ return;
});
break;
default:
VALIDATE_EXP(false, "group", "String or Integer", api_typename(opts->group.type), {
- goto cleanup;
+ return;
});
}
@@ -747,23 +739,21 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
if (HAS_KEY(opts, exec_autocmds, buffer)) {
VALIDATE((!HAS_KEY(opts, exec_autocmds, pattern)),
"%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", {
- goto cleanup;
+ return;
});
has_buffer = true;
buf = find_buffer_by_handle(opts->buffer, err);
if (ERROR_SET(err)) {
- goto cleanup;
+ return;
}
}
- if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) {
- goto cleanup;
- }
-
- if (patterns.size == 0) {
- ADD(patterns, STATIC_CSTR_TO_OBJ(""));
+ Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "",
+ arena, err);
+ if (ERROR_SET(err)) {
+ return;
}
if (HAS_KEY(opts, exec_autocmds, data)) {
@@ -774,7 +764,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
bool did_aucmd = false;
FOREACH_ITEM(event_array, event_str, {
- GET_ONE_EVENT(event_nr, event_str, cleanup)
+ GET_ONE_EVENT(event_nr, event_str, return )
FOREACH_ITEM(patterns, pat, {
char *fname = !has_buffer ? pat.data.string.data : NULL;
@@ -785,28 +775,26 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
if (did_aucmd && modeline) {
do_modelines(0);
}
-
-cleanup:
- api_free_array(event_array);
- api_free_array(patterns);
}
-static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err)
+static Array unpack_string_or_array(Object v, char *k, bool required, Arena *arena, Error *err)
{
- if (v->type == kObjectTypeString) {
- ADD(*array, copy_object(*v, NULL));
- } else if (v->type == kObjectTypeArray) {
- if (!check_string_array(v->data.array, k, true, err)) {
- return false;
+ if (v.type == kObjectTypeString) {
+ Array arr = arena_array(arena, 1);
+ ADD_C(arr, v);
+ return arr;
+ } else if (v.type == kObjectTypeArray) {
+ if (!check_string_array(v.data.array, k, true, err)) {
+ return (Array)ARRAY_DICT_INIT;
}
- *array = copy_array(v->data.array, NULL);
+ return v.data.array;
} else {
- VALIDATE_EXP(!required, k, "Array or String", api_typename(v->type), {
- return false;
+ VALIDATE_EXP(!required, k, "Array or String", api_typename(v.type), {
+ return (Array)ARRAY_DICT_INIT;
});
}
- return true;
+ return (Array)ARRAY_DICT_INIT;
}
// Returns AUGROUP_ERROR if there was a problem with {group}
@@ -838,55 +826,57 @@ static int get_augroup_from_object(Object group, Error *err)
}
}
-static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, bool has_buffer,
- Buffer buffer, Error *err)
+static Array get_patterns_from_pattern_or_buf(Object pattern, bool has_buffer, Buffer buffer,
+ char *fallback, Arena *arena, Error *err)
{
- const char pattern_buflocal[BUFLOCAL_PAT_LEN];
+ ArrayBuilder patterns = ARRAY_DICT_INIT;
+ kvi_init(patterns);
if (pattern.type != kObjectTypeNil) {
- Object *v = &pattern;
-
- if (v->type == kObjectTypeString) {
- const char *pat = v->data.string.data;
+ if (pattern.type == kObjectTypeString) {
+ const char *pat = pattern.data.string.data;
size_t patlen = aucmd_pattern_length(pat);
while (patlen) {
- ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen)));
+ kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen));
pat = aucmd_next_pattern(pat, patlen);
patlen = aucmd_pattern_length(pat);
}
- } else if (v->type == kObjectTypeArray) {
- if (!check_string_array(v->data.array, "pattern", true, err)) {
- return false;
+ } else if (pattern.type == kObjectTypeArray) {
+ if (!check_string_array(pattern.data.array, "pattern", true, err)) {
+ return (Array)ARRAY_DICT_INIT;
}
- Array array = v->data.array;
+ Array array = pattern.data.array;
FOREACH_ITEM(array, entry, {
const char *pat = entry.data.string.data;
size_t patlen = aucmd_pattern_length(pat);
while (patlen) {
- ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen)));
+ kvi_push(patterns, CBUF_TO_ARENA_OBJ(arena, pat, patlen));
pat = aucmd_next_pattern(pat, patlen);
patlen = aucmd_pattern_length(pat);
}
})
} else {
- VALIDATE_EXP(false, "pattern", "String or Table", api_typename(v->type), {
- return false;
+ VALIDATE_EXP(false, "pattern", "String or Table", api_typename(pattern.type), {
+ return (Array)ARRAY_DICT_INIT;
});
}
} else if (has_buffer) {
buf_T *buf = find_buffer_by_handle(buffer, err);
if (ERROR_SET(err)) {
- return false;
+ return (Array)ARRAY_DICT_INIT;
}
- snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
- ADD(*patterns, CSTR_TO_OBJ(pattern_buflocal));
+ kvi_push(patterns, STRING_OBJ(arena_printf(arena, "<buffer=%d>", (int)buf->handle)));
}
- return true;
+ if (kv_size(patterns) == 0 && fallback) {
+ kvi_push(patterns, CSTR_AS_OBJ(fallback));
+ }
+
+ return arena_take_arraybuilder(arena, &patterns);
}
static bool clear_autocmd(event_T event, char *pat, int au_group, Error *err)
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 0df231868d..7f195de959 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -12,10 +12,12 @@
#include "nvim/api/buffer.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
@@ -24,28 +26,32 @@
#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
#include "nvim/extmark.h"
-#include "nvim/func_attr.h"
+#include "nvim/extmark_defs.h"
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/mark_defs.h"
+#include "nvim/marktree_defs.h"
#include "nvim/memline.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/pos_defs.h"
#include "nvim/state_defs.h"
#include "nvim/types_defs.h"
#include "nvim/undo.h"
+#include "nvim/undo_defs.h"
#include "nvim/vim_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/buffer.c.generated.h"
#endif
-/// \defgroup api-buffer
-///
-/// \brief For more information on buffers, see |buffers|
+/// @brief <pre>help
+/// For more information on buffers, see |buffers|.
///
/// Unloaded Buffers: ~
///
@@ -57,6 +63,7 @@
///
/// You can use |nvim_buf_is_loaded()| or |nvim_buf_line_count()| to check
/// whether a buffer is loaded.
+/// </pre>
/// Returns the number of lines in the given buffer.
///
@@ -105,7 +112,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// Not for Lua callbacks.
/// @param opts Optional parameters.
/// - on_lines: Lua callback invoked on change.
-/// Return `true` to detach. Args:
+/// Return a truthy value (not `false` or `nil`) to detach. Args:
/// - the string "lines"
/// - buffer handle
/// - b:changedtick
@@ -118,8 +125,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// - on_bytes: Lua callback invoked on change.
/// This callback receives more granular information about the
/// change compared to on_lines.
-/// Return `true` to detach.
-/// Args:
+/// Return a truthy value (not `false` or `nil`) to detach. Args:
/// - the string "bytes"
/// - buffer handle
/// - b:changedtick
@@ -127,11 +133,13 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// - start column of the changed text
/// - byte offset of the changed text (from the start of
/// the buffer)
-/// - old end row of the changed text
+/// - old end row of the changed text (offset from start row)
/// - old end column of the changed text
+/// (if old end row = 0, offset from start column)
/// - old end byte length of the changed text
-/// - new end row of the changed text
+/// - new end row of the changed text (offset from start row)
/// - new end column of the changed text
+/// (if new end row = 0, offset from start column)
/// - new end byte length of the changed text
/// - on_changedtick: Lua callback invoked on changedtick
/// increment without text change. Args:
@@ -153,7 +161,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err)
/// @return False if attach failed (invalid parameter, or buffer isn't loaded);
/// otherwise True. TODO: LUA_API_NO_EVAL
Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer,
- DictionaryOf(LuaRef) opts, Error *err)
+ Dict(buf_attach) *opts, Error *err)
FUNC_API_SINCE(4)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -162,64 +170,40 @@ Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer,
return false;
}
- bool is_lua = (channel_id == LUA_INTERNAL_CALL);
BufUpdateCallbacks cb = BUF_UPDATE_CALLBACKS_INIT;
- struct {
- const char *name;
- LuaRef *dest;
- } cbs[] = {
- { "on_lines", &cb.on_lines },
- { "on_bytes", &cb.on_bytes },
- { "on_changedtick", &cb.on_changedtick },
- { "on_detach", &cb.on_detach },
- { "on_reload", &cb.on_reload },
- { NULL, NULL },
- };
-
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object *v = &opts.items[i].value;
- bool key_used = false;
- if (is_lua) {
- for (size_t j = 0; cbs[j].name; j++) {
- if (strequal(cbs[j].name, k.data)) {
- VALIDATE_T(cbs[j].name, kObjectTypeLuaRef, v->type, {
- goto error;
- });
- *(cbs[j].dest) = v->data.luaref;
- v->data.luaref = LUA_NOREF;
- key_used = true;
- break;
- }
- }
- if (key_used) {
- continue;
- } else if (strequal("utf_sizes", k.data)) {
- VALIDATE_T("utf_sizes", kObjectTypeBoolean, v->type, {
- goto error;
- });
- cb.utf_sizes = v->data.boolean;
- key_used = true;
- } else if (strequal("preview", k.data)) {
- VALIDATE_T("preview", kObjectTypeBoolean, v->type, {
- goto error;
- });
- cb.preview = v->data.boolean;
- key_used = true;
- }
+ if (channel_id == LUA_INTERNAL_CALL) {
+ if (HAS_KEY(opts, buf_attach, on_lines)) {
+ cb.on_lines = opts->on_lines;
+ opts->on_lines = LUA_NOREF;
}
- VALIDATE_S(key_used, "'opts' key", k.data, {
- goto error;
- });
+ if (HAS_KEY(opts, buf_attach, on_bytes)) {
+ cb.on_bytes = opts->on_bytes;
+ opts->on_bytes = LUA_NOREF;
+ }
+
+ if (HAS_KEY(opts, buf_attach, on_changedtick)) {
+ cb.on_changedtick = opts->on_changedtick;
+ opts->on_changedtick = LUA_NOREF;
+ }
+
+ if (HAS_KEY(opts, buf_attach, on_detach)) {
+ cb.on_detach = opts->on_detach;
+ opts->on_detach = LUA_NOREF;
+ }
+
+ if (HAS_KEY(opts, buf_attach, on_reload)) {
+ cb.on_reload = opts->on_reload;
+ opts->on_reload = LUA_NOREF;
+ }
+
+ cb.utf_sizes = opts->utf_sizes;
+
+ cb.preview = opts->preview;
}
return buf_updates_register(buf, channel_id, cb, send_buffer);
-
-error:
- buffer_update_callbacks_free(cb);
- return false;
}
/// Deactivates buffer-update events on the channel.
@@ -245,6 +229,7 @@ Boolean nvim_buf_detach(uint64_t channel_id, Buffer buffer, Error *err)
return true;
}
+/// @nodoc
void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *err)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -279,6 +264,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
Integer start,
Integer end,
Boolean strict_indexing,
+ Arena *arena,
lua_State *lstate,
Error *err)
FUNC_API_SINCE(1)
@@ -310,18 +296,10 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
size_t size = (size_t)(end - start);
- init_line_array(lstate, &rv, size);
-
- if (!buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv,
- lstate, err)) {
- goto end;
- }
+ init_line_array(lstate, &rv, size, arena);
-end:
- if (ERROR_SET(err)) {
- api_free_array(rv);
- rv.items = NULL;
- }
+ buf_collect_lines(buf, size, (linenr_T)start, 0, (channel_id != VIML_INTERNAL_CALL), &rv,
+ lstate, arena);
return rv;
}
@@ -348,7 +326,8 @@ end:
/// @param replacement Array of lines to use as replacement
/// @param[out] err Error details, if any
void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integer end,
- Boolean strict_indexing, ArrayOf(String) replacement, Error *err)
+ Boolean strict_indexing, ArrayOf(String) replacement, Arena *arena,
+ Error *err)
FUNC_API_SINCE(1)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
@@ -358,12 +337,10 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
return;
}
- // load buffer first if it's not loaded
- if (buf->b_ml.ml_mfp == NULL) {
- if (!buf_ensure_loaded(buf)) {
- api_set_error(err, kErrorTypeException, "Failed to load buffer");
- return;
- }
+ // Load buffer if necessary. #22670
+ if (!buf_ensure_loaded(buf)) {
+ api_set_error(err, kErrorTypeException, "Failed to load buffer");
+ return;
}
bool oob = false;
@@ -385,14 +362,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
size_t new_len = replacement.size;
size_t old_len = (size_t)(end - start);
ptrdiff_t extra = 0; // lines added to text, can be negative
- char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL;
+ char **lines = (new_len != 0) ? arena_alloc(arena, new_len * sizeof(char *), true) : NULL;
for (size_t i = 0; i < new_len; i++) {
const String l = replacement.items[i].data.string;
// Fill lines[i] with l's contents. Convert NULs to newlines as required by
// NL-used-for-NUL.
- lines[i] = xmemdupz(l.data, l.size);
+ lines[i] = arena_memdupz(arena, l.data, l.size);
memchrsub(lines[i], NUL, NL, l.size);
}
@@ -437,15 +414,12 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
goto end;
});
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) {
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to replace line");
goto end;
}
inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- // Mark lines that haven't been passed to the buffer as they need
- // to be freed later
- lines[i] = NULL;
}
// Now we may need to insert the remaining new old_len
@@ -463,9 +437,6 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- // Same as with replacing, but we also need to free lines
- xfree(lines[i]);
- lines[i] = NULL;
extra++;
}
@@ -488,11 +459,6 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
}
end:
- for (size_t i = 0; i < new_len; i++) {
- xfree(lines[i]);
- }
-
- xfree(lines);
try_end(err);
}
@@ -525,7 +491,8 @@ end:
/// @param replacement Array of lines to use as replacement
/// @param[out] err Error details, if any
void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, Integer start_col,
- Integer end_row, Integer end_col, ArrayOf(String) replacement, Error *err)
+ Integer end_row, Integer end_col, ArrayOf(String) replacement, Arena *arena,
+ Error *err)
FUNC_API_SINCE(7)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
@@ -540,12 +507,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
return;
}
- // load buffer first if it's not loaded
- if (buf->b_ml.ml_mfp == NULL) {
- if (!buf_ensure_loaded(buf)) {
- api_set_error(err, kErrorTypeException, "Failed to load buffer");
- return;
- }
+ // Load buffer if necessary. #22670
+ if (!buf_ensure_loaded(buf)) {
+ api_set_error(err, kErrorTypeException, "Failed to load buffer");
+ return;
}
bool oob = false;
@@ -562,33 +527,31 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
return;
});
- char *str_at_start = NULL;
- char *str_at_end = NULL;
-
- // Another call to ml_get_buf() may free the line, so make a copy.
- str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row));
+ // Another call to ml_get_buf() may free the lines, so we make copies
+ char *str_at_start = ml_get_buf(buf, (linenr_T)start_row);
size_t len_at_start = strlen(str_at_start);
+ str_at_start = arena_memdupz(arena, str_at_start, len_at_start);
start_col = start_col < 0 ? (int64_t)len_at_start + start_col + 1 : start_col;
VALIDATE_RANGE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col", {
- goto early_end;
+ return;
});
- // Another call to ml_get_buf() may free the line, so make a copy.
- str_at_end = xstrdup(ml_get_buf(buf, (linenr_T)end_row));
+ char *str_at_end = ml_get_buf(buf, (linenr_T)end_row);
size_t len_at_end = strlen(str_at_end);
+ str_at_end = arena_memdupz(arena, str_at_end, len_at_end);
end_col = end_col < 0 ? (int64_t)len_at_end + end_col + 1 : end_col;
VALIDATE_RANGE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col", {
- goto early_end;
+ return;
});
VALIDATE((start_row <= end_row && !(end_row == start_row && start_col > end_col)),
"%s", "'start' is higher than 'end'", {
- goto early_end;
+ return;
});
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
if (!check_string_array(replacement, "replacement string", disallow_nl, err)) {
- goto early_end;
+ return;
}
size_t new_len = replacement.size;
@@ -618,7 +581,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
if (replacement.size == 1) {
firstlen += last_part_len;
}
- char *first = xmallocz(firstlen);
+ char *first = arena_allocz(arena, firstlen);
char *last = NULL;
memcpy(first, str_at_start, (size_t)start_col);
memcpy(first + start_col, first_item.data, first_item.size);
@@ -626,13 +589,13 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
if (replacement.size == 1) {
memcpy(first + start_col + first_item.size, str_at_end + end_col, last_part_len);
} else {
- last = xmallocz(last_item.size + last_part_len);
+ last = arena_allocz(arena, last_item.size + last_part_len);
memcpy(last, last_item.data, last_item.size);
memchrsub(last, NUL, NL, last_item.size);
memcpy(last + last_item.size, str_at_end + end_col, last_part_len);
}
- char **lines = xcalloc(new_len, sizeof(char *));
+ char **lines = arena_alloc(arena, new_len * sizeof(char *), true);
lines[0] = first;
new_byte += (bcount_t)(first_item.size);
for (size_t i = 1; i < new_len - 1; i++) {
@@ -640,7 +603,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
// Fill lines[i] with l's contents. Convert NULs to newlines as required by
// NL-used-for-NUL.
- lines[i] = xmemdupz(l.data, l.size);
+ lines[i] = arena_memdupz(arena, l.data, l.size);
memchrsub(lines[i], NUL, NL, l.size);
new_byte += (bcount_t)(l.size) + 1;
}
@@ -692,13 +655,10 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
goto end;
});
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false) == FAIL) {
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
api_set_error(err, kErrorTypeException, "Failed to replace line");
goto end;
}
- // Mark lines that haven't been passed to the buffer as they need
- // to be freed later
- lines[i] = NULL;
}
// Now we may need to insert the remaining new old_len
@@ -714,9 +674,6 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
goto end;
}
- // Same as with replacing, but we also need to free lines
- xfree(lines[i]);
- lines[i] = NULL;
extra++;
}
@@ -749,15 +706,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
}
end:
- for (size_t i = 0; i < new_len; i++) {
- xfree(lines[i]);
- }
- xfree(lines);
try_end(err);
-
-early_end:
- xfree(str_at_start);
- xfree(str_at_end);
}
/// Gets a range from the buffer.
@@ -782,16 +731,12 @@ early_end:
ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
Integer start_row, Integer start_col,
Integer end_row, Integer end_col,
- Dictionary opts, lua_State *lstate,
- Error *err)
+ Dict(empty) *opts,
+ Arena *arena, lua_State *lstate, Error *err)
FUNC_API_SINCE(9)
{
Array rv = ARRAY_DICT_INIT;
- VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", {
- return rv;
- });
-
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@@ -822,44 +767,38 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
size_t size = (size_t)(end_row - start_row) + 1;
- init_line_array(lstate, &rv, size);
+ init_line_array(lstate, &rv, size, arena);
if (start_row == end_row) {
String line = buf_get_text(buf, start_row, start_col, end_col, err);
if (ERROR_SET(err)) {
goto end;
}
- push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl);
+ push_linestr(lstate, &rv, line.data, line.size, 0, replace_nl, arena);
return rv;
}
String str = buf_get_text(buf, start_row, start_col, MAXCOL - 1, err);
-
- push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl);
-
if (ERROR_SET(err)) {
goto end;
}
+ push_linestr(lstate, &rv, str.data, str.size, 0, replace_nl, arena);
+
if (size > 2) {
- if (!buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate,
- err)) {
- goto end;
- }
+ buf_collect_lines(buf, size - 2, (linenr_T)start_row + 1, 1, replace_nl, &rv, lstate, arena);
}
str = buf_get_text(buf, end_row, 0, end_col, err);
- push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl);
-
if (ERROR_SET(err)) {
goto end;
}
+ push_linestr(lstate, &rv, str.data, str.size, (int)(size - 1), replace_nl, arena);
+
end:
if (ERROR_SET(err)) {
- api_free_array(rv);
- rv.size = 0;
- rv.items = NULL;
+ return (Array)ARRAY_DICT_INIT;
}
return rv;
@@ -905,7 +844,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);
@@ -914,7 +853,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
@@ -937,12 +876,12 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// Gets a list of buffer-local |mapping| definitions.
///
-/// @param mode Mode short-name ("n", "i", "v", ...)
/// @param buffer Buffer handle, or 0 for current buffer
+/// @param mode Mode short-name ("n", "i", "v", ...)
/// @param[out] err Error details, if any
/// @returns Array of |maparg()|-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle.
-ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
+ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Arena *arena, Error *err)
FUNC_API_SINCE(3)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -951,7 +890,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
return (Array)ARRAY_DICT_INIT;
}
- return keymap_array(mode, buf);
+ return keymap_array(mode, buf, arena);
}
/// Sets a buffer-local |mapping| for the given mode.
@@ -993,7 +932,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
@@ -1010,7 +949,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
@@ -1018,7 +957,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Buffer name
-String nvim_buf_get_name(Buffer buffer, Arena *arena, Error *err)
+String nvim_buf_get_name(Buffer buffer, Error *err)
FUNC_API_SINCE(1)
{
String rv = STRING_INIT;
@@ -1082,7 +1021,7 @@ Boolean nvim_buf_is_loaded(Buffer buffer)
/// @param opts Optional parameters. Keys:
/// - force: Force deletion and ignore unsaved changes.
/// - unload: Unloaded only, do not delete. See |:bunload|
-void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err)
+void nvim_buf_delete(Buffer buffer, Dict(buf_delete) *opts, Error *err)
FUNC_API_SINCE(7)
FUNC_API_TEXTLOCK
{
@@ -1092,25 +1031,9 @@ void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err)
return;
}
- bool force = false;
- bool unload = false;
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object v = opts.items[i].value;
- if (strequal("force", k.data)) {
- force = api_object_to_bool(v, "force", false, err);
- } else if (strequal("unload", k.data)) {
- unload = api_object_to_bool(v, "unload", false, err);
- } else {
- VALIDATE_S(false, "'opts' key", k.data, {
- return;
- });
- }
- }
+ bool force = opts->force;
- if (ERROR_SET(err)) {
- return;
- }
+ bool unload = opts->unload;
int result = do_buffer(unload ? DOBUF_UNLOAD : DOBUF_WIPE,
DOBUF_FIRST,
@@ -1194,7 +1117,7 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err)
/// @return true if the mark was set, else false.
/// @see |nvim_buf_del_mark()|
/// @see |nvim_buf_get_mark()|
-Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, Dictionary opts,
+Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col, Dict(empty) *opts,
Error *err)
FUNC_API_SINCE(8)
{
@@ -1227,7 +1150,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;
@@ -1257,8 +1180,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;
}
@@ -1279,8 +1203,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
/// @param fun Function to call inside the buffer (currently Lua callable
/// only)
/// @param[out] err Error details, if any
-/// @return Return value of function. NB: will deepcopy Lua values
-/// currently, use upvalues to send Lua references in and out.
+/// @return Return value of function.
Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
FUNC_API_SINCE(7)
FUNC_API_LUA_ONLY
@@ -1294,35 +1217,35 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
aucmd_prepbuf(&aco, buf);
Array args = ARRAY_DICT_INIT;
- Object res = nlua_call_ref(fun, NULL, args, true, err);
+ Object res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
aucmd_restbuf(&aco);
try_end(err);
return res;
}
-Dictionary nvim__buf_stats(Buffer buffer, Error *err)
+/// @nodoc
+Dictionary nvim__buf_stats(Buffer buffer, Arena *arena, Error *err)
{
- Dictionary rv = ARRAY_DICT_INIT;
-
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
- return rv;
+ return (Dictionary)ARRAY_DICT_INIT;
}
+ Dictionary rv = arena_dict(arena, 7);
// Number of times the cached line was flushed.
// This should generally not increase while editing the same
// line in the same mode.
- PUT(rv, "flush_count", INTEGER_OBJ(buf->flush_count));
+ PUT_C(rv, "flush_count", INTEGER_OBJ(buf->flush_count));
// lnum of current line
- PUT(rv, "current_lnum", INTEGER_OBJ(buf->b_ml.ml_line_lnum));
+ PUT_C(rv, "current_lnum", INTEGER_OBJ(buf->b_ml.ml_line_lnum));
// whether the line has unflushed changes.
- PUT(rv, "line_dirty", BOOLEAN_OBJ(buf->b_ml.ml_flags & ML_LINE_DIRTY));
+ PUT_C(rv, "line_dirty", BOOLEAN_OBJ(buf->b_ml.ml_flags & ML_LINE_DIRTY));
// NB: this should be zero at any time API functions are called,
// this exists to debug issues
- PUT(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes));
- PUT(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2));
- PUT(rv, "virt_blocks", INTEGER_OBJ((Integer)buf->b_virt_line_blocks));
+ PUT_C(rv, "dirty_bytes", INTEGER_OBJ((Integer)buf->deleted_bytes));
+ PUT_C(rv, "dirty_bytes2", INTEGER_OBJ((Integer)buf->deleted_bytes2));
+ PUT_C(rv, "virt_blocks", INTEGER_OBJ((Integer)buf_meta_total(buf, kMTMetaLines)));
u_header_T *uhp = NULL;
if (buf->b_u_curhead != NULL) {
@@ -1331,7 +1254,7 @@ Dictionary nvim__buf_stats(Buffer buffer, Error *err)
uhp = buf->b_u_newhead;
}
if (uhp) {
- PUT(rv, "uhp_extmark_size", INTEGER_OBJ((Integer)kv_size(uhp->uh_extmark)));
+ PUT_C(rv, "uhp_extmark_size", INTEGER_OBJ((Integer)kv_size(uhp->uh_extmark)));
}
return rv;
@@ -1434,13 +1357,12 @@ static void fix_cursor_cols(win_T *win, linenr_T start_row, colnr_T start_col, l
/// @param lstate Lua state. When NULL the Array is initialized instead.
/// @param a Array to initialize
/// @param size Size of array
-static inline void init_line_array(lua_State *lstate, Array *a, size_t size)
+static inline void init_line_array(lua_State *lstate, Array *a, size_t size, Arena *arena)
{
if (lstate) {
lua_createtable(lstate, (int)size, 0);
} else {
- a->size = size;
- a->items = xcalloc(a->size, sizeof(Object));
+ *a = arena_array(arena, size);
}
}
@@ -1453,14 +1375,15 @@ static inline void init_line_array(lua_State *lstate, Array *a, size_t size)
/// @param a Array to push onto when not using Lua
/// @param s String to push
/// @param len Size of string
-/// @param idx 0-based index to place s
+/// @param idx 0-based index to place s (only used for Lua)
/// @param replace_nl Replace newlines ('\n') with null ('\0')
static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len, int idx,
- bool replace_nl)
+ bool replace_nl, Arena *arena)
{
if (lstate) {
// Vim represents NULs as NLs
if (s && replace_nl && strchr(s, '\n')) {
+ // TODO(bfredl): could manage scratch space in the arena, for the NUL case
char *tmp = xmemdupz(s, len);
strchrsub(tmp, '\n', '\0');
lua_pushlstring(lstate, tmp, len);
@@ -1471,15 +1394,15 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len,
lua_rawseti(lstate, -2, idx + 1);
} else {
String str = STRING_INIT;
- if (s) {
- str = cbuf_to_string(s, len);
+ if (len > 0) {
+ str = CBUF_TO_ARENA_STR(arena, s, len);
if (replace_nl) {
// Vim represents NULs as NLs, but this may confuse clients.
strchrsub(str.data, '\n', '\0');
}
}
- a->items[idx] = STRING_OBJ(str);
+ ADD_C(*a, STRING_OBJ(str));
}
}
@@ -1490,27 +1413,17 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len,
/// @param n Number of lines to collect
/// @param replace_nl Replace newlines ("\n") with NUL
/// @param start Line number to start from
-/// @param start_idx First index to push to
+/// @param start_idx First index to push to (only used for Lua)
/// @param[out] l If not NULL, Lines are copied here
/// @param[out] lstate If not NULL, Lines are pushed into a table onto the stack
/// @param err[out] Error, if any
/// @return true unless `err` was set
-bool buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl,
- Array *l, lua_State *lstate, Error *err)
+void buf_collect_lines(buf_T *buf, size_t n, linenr_T start, int start_idx, bool replace_nl,
+ Array *l, lua_State *lstate, Arena *arena)
{
for (size_t i = 0; i < n; i++) {
linenr_T lnum = start + (linenr_T)i;
-
- if (lnum >= MAXLNUM) {
- if (err != NULL) {
- api_set_error(err, kErrorTypeValidation, "Line index is too high");
- }
- return false;
- }
-
char *bufstr = ml_get_buf(buf, lnum);
- push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl);
+ push_linestr(lstate, l, bufstr, strlen(bufstr), start_idx + (int)i, replace_nl, arena);
}
-
- return true;
}
diff --git a/src/nvim/api/buffer.h b/src/nvim/api/buffer.h
index f3971c1d30..fe2d104058 100644
--- a/src/nvim/api/buffer.h
+++ b/src/nvim/api/buffer.h
@@ -5,7 +5,8 @@
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
#include "nvim/api/private/defs.h" // IWYU pragma: keep
-#include "nvim/buffer_defs.h" // IWYU pragma: keep
+#include "nvim/pos_defs.h" // IWYU pragma: keep
+#include "nvim/types_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/buffer.h.generated.h"
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 2a57ce9a19..779e216c74 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -13,13 +13,14 @@
#include "nvim/api/private/validate.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/cmdexpand_defs.h"
-#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.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/lua/executor.h"
#include "nvim/macros_defs.h"
@@ -47,16 +48,16 @@
/// @param[out] err Error details, if any.
/// @return Dictionary containing command information, with these keys:
/// - cmd: (string) Command name.
-/// - range: (array) (optional) Command range (|<line1>| |<line2>|).
+/// - range: (array) (optional) Command range ([<line1>] [<line2>]).
/// Omitted if command doesn't accept a range.
/// Otherwise, has no elements if no range was specified, one element if
/// only a single range item was specified, or two elements if both range
/// items were specified.
-/// - count: (number) (optional) Command |<count>|.
+/// - count: (number) (optional) Command [<count>].
/// Omitted if command cannot take a count.
-/// - reg: (string) (optional) Command |<register>|.
+/// - reg: (string) (optional) Command [<register>].
/// Omitted if command cannot take a register.
-/// - bang: (boolean) Whether command contains a |<bang>| (!) modifier.
+/// - bang: (boolean) Whether command contains a [<bang>] (!) modifier.
/// - args: (array) Command arguments.
/// - addr: (string) Value of |:command-addr|. Uses short name or "line" for -addr=lines.
/// - nargs: (string) Value of |:command-nargs|.
@@ -66,7 +67,7 @@
/// - file: (boolean) The command expands filenames. Which means characters such as "%",
/// "#" and wildcards are expanded.
/// - bar: (boolean) The "|" character is treated as a command separator and the double
-/// quote character (\") is treated as the start of a comment.
+/// quote character (") is treated as the start of a comment.
/// - mods: (dictionary) |:command-modifiers|.
/// - filter: (dictionary) |:filter|.
/// - pattern: (string) Filter pattern. Empty string if there is no filter.
@@ -95,19 +96,15 @@
/// - "belowright": |:belowright|.
/// - "topleft": |:topleft|.
/// - "botright": |:botright|.
-Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
+Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(10) FUNC_API_FAST
{
- Dictionary result = ARRAY_DICT_INIT;
-
- VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", {
- return result;
- });
+ Dict(cmd) result = KEYDICT_INIT;
// Parse command line
exarg_T ea;
CmdParseInfo cmdinfo;
- char *cmdline = string_to_cstr(str);
+ char *cmdline = arena_memdupz(arena, str.data, str.size);
const char *errormsg = NULL;
if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) {
@@ -127,22 +124,23 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
// otherwise split arguments by whitespace.
if (ea.argt & EX_NOSPC) {
if (*ea.arg != NUL) {
- ADD(args, STRING_OBJ(cstrn_to_string(ea.arg, length)));
+ args = arena_array(arena, 1);
+ ADD_C(args, STRING_OBJ(cstrn_as_string(ea.arg, length)));
}
} else {
size_t end = 0;
size_t len = 0;
- char *buf = xcalloc(length, sizeof(char));
+ char *buf = arena_alloc(arena, length + 1, false);
bool done = false;
+ args = arena_array(arena, uc_nargs_upper_bound(ea.arg, length));
while (!done) {
done = uc_split_args_iter(ea.arg, length, &end, buf, &len);
if (len > 0) {
- ADD(args, STRING_OBJ(cstrn_to_string(buf, len)));
+ ADD_C(args, STRING_OBJ(cstrn_as_string(buf, len)));
+ buf += len + 1;
}
}
-
- xfree(buf);
}
ucmd_T *cmd = NULL;
@@ -152,40 +150,32 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
cmd = USER_CMD_GA(&curbuf->b_ucmds, ea.useridx);
}
- if (cmd != NULL) {
- PUT(result, "cmd", CSTR_TO_OBJ(cmd->uc_name));
- } else {
- PUT(result, "cmd", CSTR_TO_OBJ(get_command_name(NULL, ea.cmdidx)));
- }
+ char *name = (cmd != NULL ? cmd->uc_name : get_command_name(NULL, ea.cmdidx));
+ PUT_KEY(result, cmd, cmd, cstr_as_string(name));
if (ea.argt & EX_RANGE) {
- Array range = ARRAY_DICT_INIT;
+ Array range = arena_array(arena, 2);
if (ea.addr_count > 0) {
if (ea.addr_count > 1) {
- ADD(range, INTEGER_OBJ(ea.line1));
+ ADD_C(range, INTEGER_OBJ(ea.line1));
}
- ADD(range, INTEGER_OBJ(ea.line2));
+ ADD_C(range, INTEGER_OBJ(ea.line2));
}
- PUT(result, "range", ARRAY_OBJ(range));
+ PUT_KEY(result, cmd, range, range);
}
if (ea.argt & EX_COUNT) {
- if (ea.addr_count > 0) {
- PUT(result, "count", INTEGER_OBJ(ea.line2));
- } else if (cmd != NULL) {
- PUT(result, "count", INTEGER_OBJ(cmd->uc_def));
- } else {
- PUT(result, "count", INTEGER_OBJ(0));
- }
+ Integer count = ea.addr_count > 0 ? ea.line2 : (cmd != NULL ? cmd->uc_def : 0);
+ PUT_KEY(result, cmd, count, count);
}
if (ea.argt & EX_REGSTR) {
char reg[2] = { (char)ea.regname, NUL };
- PUT(result, "reg", CSTR_TO_OBJ(reg));
+ PUT_KEY(result, cmd, reg, CSTR_TO_ARENA_STR(arena, reg));
}
- PUT(result, "bang", BOOLEAN_OBJ(ea.forceit));
- PUT(result, "args", ARRAY_OBJ(args));
+ PUT_KEY(result, cmd, bang, ea.forceit);
+ PUT_KEY(result, cmd, args, args);
char nargs[2];
if (ea.argt & EX_EXTRA) {
@@ -204,9 +194,9 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
nargs[0] = '0';
}
nargs[1] = '\0';
- PUT(result, "nargs", CSTR_TO_OBJ(nargs));
+ PUT_KEY(result, cmd, nargs, CSTR_TO_ARENA_OBJ(arena, nargs));
- const char *addr;
+ char *addr;
switch (ea.addr_type) {
case ADDR_LINES:
addr = "line";
@@ -236,38 +226,37 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
addr = "?";
break;
}
- PUT(result, "addr", CSTR_TO_OBJ(addr));
- PUT(result, "nextcmd", CSTR_TO_OBJ(ea.nextcmd));
-
- Dictionary mods = ARRAY_DICT_INIT;
-
- Dictionary filter = ARRAY_DICT_INIT;
- PUT(filter, "pattern", cmdinfo.cmdmod.cmod_filter_pat
- ? CSTR_TO_OBJ(cmdinfo.cmdmod.cmod_filter_pat)
- : STATIC_CSTR_TO_OBJ(""));
- PUT(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force));
- PUT(mods, "filter", DICTIONARY_OBJ(filter));
-
- PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT));
- PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT));
- PUT(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT));
- PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX));
- PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD));
- PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab - 1));
- PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.cmdmod.cmod_verbose - 1));
- PUT(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_BROWSE));
- PUT(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_CONFIRM));
- PUT(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_HIDE));
- PUT(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPALT));
- PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPJUMPS));
- PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPMARKS));
- PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPPATTERNS));
- PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_LOCKMARKS));
- PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOSWAPFILE));
- PUT(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_VERT));
- PUT(mods, "horizontal", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_HOR));
-
- const char *split;
+ PUT_KEY(result, cmd, addr, CSTR_AS_OBJ(addr));
+ PUT_KEY(result, cmd, nextcmd, CSTR_AS_OBJ(ea.nextcmd));
+
+ // TODO(bfredl): nested keydict would be nice..
+ Dictionary mods = arena_dict(arena, 20);
+
+ Dictionary filter = arena_dict(arena, 2);
+ PUT_C(filter, "pattern", CSTR_TO_ARENA_OBJ(arena, cmdinfo.cmdmod.cmod_filter_pat));
+ PUT_C(filter, "force", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_filter_force));
+ PUT_C(mods, "filter", DICTIONARY_OBJ(filter));
+
+ PUT_C(mods, "silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SILENT));
+ PUT_C(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT));
+ PUT_C(mods, "unsilent", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_UNSILENT));
+ PUT_C(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX));
+ PUT_C(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOAUTOCMD));
+ PUT_C(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.cmod_tab - 1));
+ PUT_C(mods, "verbose", INTEGER_OBJ(cmdinfo.cmdmod.cmod_verbose - 1));
+ PUT_C(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_BROWSE));
+ PUT_C(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_CONFIRM));
+ PUT_C(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_HIDE));
+ PUT_C(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPALT));
+ PUT_C(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPJUMPS));
+ PUT_C(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPMARKS));
+ PUT_C(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_KEEPPATTERNS));
+ PUT_C(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_LOCKMARKS));
+ PUT_C(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_flags & CMOD_NOSWAPFILE));
+ PUT_C(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_VERT));
+ PUT_C(mods, "horizontal", BOOLEAN_OBJ(cmdinfo.cmdmod.cmod_split & WSP_HOR));
+
+ char *split;
if (cmdinfo.cmdmod.cmod_split & WSP_BOT) {
split = "botright";
} else if (cmdinfo.cmdmod.cmod_split & WSP_TOP) {
@@ -279,18 +268,17 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
} else {
split = "";
}
- PUT(mods, "split", CSTR_TO_OBJ(split));
+ PUT_C(mods, "split", CSTR_AS_OBJ(split));
- PUT(result, "mods", DICTIONARY_OBJ(mods));
+ PUT_KEY(result, cmd, mods, mods);
- Dictionary magic = ARRAY_DICT_INIT;
- PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
- PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
- PUT(result, "magic", DICTIONARY_OBJ(magic));
+ Dictionary magic = arena_dict(arena, 2);
+ PUT_C(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
+ PUT_C(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
+ PUT_KEY(result, cmd, magic, magic);
undo_cmdmod(&cmdinfo.cmdmod);
end:
- xfree(cmdline);
return result;
}
@@ -317,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;
@@ -355,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);
@@ -364,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;
@@ -395,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), {
@@ -424,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;
@@ -526,7 +514,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
VALIDATE_MOD((!ea.forceit || (ea.argt & EX_BANG)), "bang", cmd->cmd.data);
if (HAS_KEY(cmd, cmd, magic)) {
- Dict(cmd_magic) magic[1] = { 0 };
+ Dict(cmd_magic) magic[1] = KEYDICT_INIT;
if (!api_dict_to_keydict(magic, KeyDict_cmd_magic_get_field, cmd->magic, err)) {
goto end;
}
@@ -544,13 +532,13 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
}
if (HAS_KEY(cmd, cmd, mods)) {
- Dict(cmd_mods) mods[1] = { 0 };
+ Dict(cmd_mods) mods[1] = KEYDICT_INIT;
if (!api_dict_to_keydict(mods, KeyDict_cmd_mods_get_field, cmd->mods, err)) {
goto end;
}
if (HAS_KEY(mods, cmd_mods, filter)) {
- Dict(cmd_mods_filter) filter[1] = { 0 };
+ Dict(cmd_mods_filter) filter[1] = KEYDICT_INIT;
if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field,
mods->filter, err)) {
@@ -678,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);
@@ -871,17 +853,17 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
/// from Lua, the command can also be a Lua function. The function is called with a
/// single table argument that contains the following keys:
/// - name: (string) Command name
-/// - args: (string) The args passed to the command, if any |<args>|
+/// - args: (string) The args passed to the command, if any [<args>]
/// - fargs: (table) The args split by unescaped whitespace (when more than one
-/// argument is allowed), if any |<f-args>|
+/// argument is allowed), if any [<f-args>]
/// - nargs: (string) Number of arguments |:command-nargs|
-/// - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>|
-/// - line1: (number) The starting line of the command range |<line1>|
-/// - line2: (number) The final line of the command range |<line2>|
-/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>|
-/// - count: (number) Any count supplied |<count>|
-/// - reg: (string) The optional register, if specified |<reg>|
-/// - mods: (string) Command modifiers, if any |<mods>|
+/// - bang: (boolean) "true" if the command was executed with a ! modifier [<bang>]
+/// - line1: (number) The starting line of the command range [<line1>]
+/// - line2: (number) The final line of the command range [<line2>]
+/// - range: (number) The number of items in the command range: 0, 1, or 2 [<range>]
+/// - count: (number) Any count supplied [<count>]
+/// - reg: (string) The optional register, if specified [<reg>]
+/// - mods: (string) Command modifiers, if any [<mods>]
/// - smods: (table) Command modifiers in a structured format. Has the same
/// structure as the "mods" key of |nvim_parse_cmd()|.
/// @param opts Optional |command-attributes|.
@@ -1115,7 +1097,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
if (opts->complete.type == kObjectTypeLuaRef) {
context = EXPAND_USER_LUA;
- compl_luaref = api_new_luaref(opts->complete.data.luaref);
+ compl_luaref = opts->complete.data.luaref;
+ opts->complete.data.luaref = LUA_NOREF;
} else if (opts->complete.type == kObjectTypeString) {
VALIDATE_S(OK == parse_compl_arg(opts->complete.data.string.data,
(int)opts->complete.data.string.size, &context, &argt,
@@ -1135,7 +1118,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
});
argt |= EX_PREVIEW;
- preview_luaref = api_new_luaref(opts->preview.data.luaref);
+ preview_luaref = opts->preview.data.luaref;
+ opts->preview.data.luaref = LUA_NOREF;
}
switch (command.type) {
@@ -1182,10 +1166,10 @@ err:
/// @param[out] err Error details, if any.
///
/// @returns Map of maps describing commands.
-Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err)
+Dictionary nvim_get_commands(Dict(get_commands) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(4)
{
- return nvim_buf_get_commands(-1, opts, err);
+ return nvim_buf_get_commands(-1, opts, arena, err);
}
/// Gets a map of buffer-local |user-commands|.
@@ -1195,7 +1179,7 @@ Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err)
/// @param[out] err Error details, if any.
///
/// @returns Map of maps describing commands.
-Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err)
+Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(4)
{
bool global = (buffer == -1);
@@ -1208,12 +1192,12 @@ Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error
api_set_error(err, kErrorTypeValidation, "builtin=true not implemented");
return (Dictionary)ARRAY_DICT_INIT;
}
- return commands_array(NULL);
+ return commands_array(NULL, arena);
}
buf_T *buf = find_buffer_by_handle(buffer, err);
if (opts->builtin || !buf) {
return (Dictionary)ARRAY_DICT_INIT;
}
- return commands_array(buf);
+ return commands_array(buf, arena);
}
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index 2ec11236d7..6254e9fbd8 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -6,22 +6,24 @@
#include "nvim/api/deprecated.h"
#include "nvim/api/extmark.h"
#include "nvim/api/keysets_defs.h"
-#include "nvim/api/options.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/vimscript.h"
#include "nvim/buffer_defs.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_defs.h"
#include "nvim/extmark.h"
-#include "nvim/func_attr.h"
#include "nvim/globals.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/pos_defs.h"
+#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/deprecated.c.generated.h"
@@ -30,8 +32,8 @@
/// @deprecated Use nvim_exec2() instead.
/// @see nvim_exec2
String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
- FUNC_API_SINCE(7)
- FUNC_API_DEPRECATED_SINCE(11)
+ FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(11)
+ FUNC_API_RET_ALLOC
{
Dict(exec_opts) opts = { .output = output };
return exec_impl(channel_id, src, &opts, err);
@@ -40,8 +42,8 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
/// @deprecated
/// @see nvim_exec2
String nvim_command_output(uint64_t channel_id, String command, Error *err)
- FUNC_API_SINCE(1)
- FUNC_API_DEPRECATED_SINCE(7)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(7)
+ FUNC_API_RET_ALLOC
{
Dict(exec_opts) opts = { .output = true };
return exec_impl(channel_id, command, &opts, err);
@@ -49,18 +51,17 @@ String nvim_command_output(uint64_t channel_id, String command, Error *err)
/// @deprecated Use nvim_exec_lua() instead.
/// @see nvim_exec_lua
-Object nvim_execute_lua(String code, Array args, Error *err)
+Object nvim_execute_lua(String code, Array args, Arena *arena, Error *err)
FUNC_API_SINCE(3)
FUNC_API_DEPRECATED_SINCE(7)
FUNC_API_REMOTE_ONLY
{
- return nlua_exec(code, args, err);
+ return nlua_exec(code, args, kRetObject, arena, err);
}
/// Gets the buffer number
///
-/// @deprecated The buffer number now is equal to the object id,
-/// so there is no need to use this function.
+/// @deprecated The buffer number now is equal to the object id
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
@@ -98,8 +99,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start,
/// Set the virtual text (annotation) for a buffer line.
///
-/// @deprecated use nvim_buf_set_extmark to use full virtual text
-/// functionality.
+/// @deprecated use nvim_buf_set_extmark to use full virtual text functionality.
///
/// The text will be placed after the buffer text. Virtual text will never
/// cause reflow, rather virtual text will be truncated at the end of the screen
@@ -117,7 +117,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start,
/// virtual text, the allocated id is then returned.
///
/// @param buffer Buffer handle, or 0 for current buffer
-/// @param ns_id Namespace to use or 0 to create a namespace,
+/// @param src_id Namespace to use or 0 to create a namespace,
/// or -1 for a ungrouped annotation
/// @param line Line to annotate with virtual text (zero-indexed)
/// @param chunks A list of [text, hl_group] arrays, each representing a
@@ -127,7 +127,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start,
/// @param[out] err Error details, if any
/// @return The ns_id that was used
Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, Array chunks,
- Dictionary opts, Error *err)
+ Dict(empty) *opts, Error *err)
FUNC_API_SINCE(5)
FUNC_API_DEPRECATED_SINCE(8)
{
@@ -141,11 +141,6 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
return 0;
}
- if (opts.size > 0) {
- api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
- return 0;
- }
-
uint32_t ns_id = src2ns(&src_id);
int width;
@@ -172,7 +167,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A
DecorInline decor = { .ext = true, .data.ext.vt = vt, .data.ext.sh_idx = DECOR_ID_INVALID };
extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, 0, true,
- false, false, false, NULL);
+ false, false, false, false, NULL);
return src_id;
}
@@ -228,12 +223,12 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Arena *arena, Error *er
/// the end of the buffer.
/// @param lines Array of lines
/// @param[out] err Error details, if any
-void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *err)
+void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Arena *arena, Error *err)
FUNC_API_DEPRECATED_SINCE(1)
{
// "lnum" will be the index of the line after inserting,
// no matter if it is negative or not
- nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, err);
+ nvim_buf_set_lines(0, buffer, lnum, lnum, true, lines, arena, err);
}
/// Gets a buffer line
@@ -248,20 +243,18 @@ void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *er
/// @param index Line index
/// @param[out] err Error details, if any
/// @return Line string
-String buffer_get_line(Buffer buffer, Integer index, Error *err)
+String buffer_get_line(Buffer buffer, Integer index, Arena *arena, Error *err)
FUNC_API_DEPRECATED_SINCE(1)
{
String rv = { .size = 0 };
index = convert_index(index);
- Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, NULL, err);
+ Array slice = nvim_buf_get_lines(0, buffer, index, index + 1, true, arena, NULL, err);
if (!ERROR_SET(err) && slice.size) {
rv = slice.items[0].data.string;
}
- xfree(slice.items);
-
return rv;
}
@@ -277,13 +270,13 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
/// @param index Line index
/// @param line Contents of the new line
/// @param[out] err Error details, if any
-void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
+void buffer_set_line(Buffer buffer, Integer index, String line, Arena *arena, Error *err)
FUNC_API_DEPRECATED_SINCE(1)
{
Object l = STRING_OBJ(line);
Array array = { .items = &l, .size = 1 };
index = convert_index(index);
- nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err);
+ nvim_buf_set_lines(0, buffer, index, index + 1, true, array, arena, err);
}
/// Deletes a buffer line
@@ -296,12 +289,12 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err)
/// @param buffer buffer handle
/// @param index line index
/// @param[out] err Error details, if any
-void buffer_del_line(Buffer buffer, Integer index, Error *err)
+void buffer_del_line(Buffer buffer, Integer index, Arena *arena, Error *err)
FUNC_API_DEPRECATED_SINCE(1)
{
Array array = ARRAY_DICT_INIT;
index = convert_index(index);
- nvim_buf_set_lines(0, buffer, index, index + 1, true, array, err);
+ nvim_buf_set_lines(0, buffer, index, index + 1, true, array, arena, err);
}
/// Retrieves a line range from the buffer
@@ -322,12 +315,13 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
Integer end,
Boolean include_start,
Boolean include_end,
+ Arena *arena,
Error *err)
FUNC_API_DEPRECATED_SINCE(1)
{
start = convert_index(start) + !include_start;
end = convert_index(end) + include_end;
- return nvim_buf_get_lines(0, buffer, start, end, false, NULL, err);
+ return nvim_buf_get_lines(0, buffer, start, end, false, arena, NULL, err);
}
/// Replaces a line range on the buffer
@@ -346,12 +340,13 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer,
// array will delete the line range)
/// @param[out] err Error details, if any
void buffer_set_line_slice(Buffer buffer, Integer start, Integer end, Boolean include_start,
- Boolean include_end, ArrayOf(String) replacement, Error *err)
+ Boolean include_end, ArrayOf(String) replacement, Arena *arena,
+ Error *err)
FUNC_API_DEPRECATED_SINCE(1)
{
start = convert_index(start) + !include_start;
end = convert_index(end) + include_end;
- nvim_buf_set_lines(0, buffer, start, end, false, replacement, err);
+ nvim_buf_set_lines(0, buffer, start, end, false, replacement, arena, err);
}
/// Sets a buffer-scoped (b:) variable
@@ -366,7 +361,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);
@@ -375,7 +370,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
@@ -386,7 +381,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);
@@ -395,7 +390,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
@@ -410,7 +405,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);
@@ -419,7 +414,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
@@ -430,7 +425,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);
@@ -439,7 +434,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
@@ -454,7 +449,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);
@@ -463,7 +458,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
@@ -474,7 +469,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);
@@ -483,7 +478,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
@@ -491,18 +486,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)
@@ -517,11 +512,11 @@ static int64_t convert_index(int64_t index)
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option Information
-Dictionary nvim_get_option_info(String name, Error *err)
+Dictionary nvim_get_option_info(String name, Arena *arena, Error *err)
FUNC_API_SINCE(7)
FUNC_API_DEPRECATED_SINCE(11)
{
- return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, err);
+ return get_vimoption(name, OPT_GLOBAL, curbuf, curwin, arena, err);
}
/// Sets the global value of an option.
@@ -544,7 +539,7 @@ void nvim_set_option(uint64_t channel_id, String name, Object value, Error *err)
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value (global)
-Object nvim_get_option(String name, Arena *arena, Error *err)
+Object nvim_get_option(String name, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(11)
{
@@ -558,7 +553,7 @@ Object nvim_get_option(String name, Arena *arena, Error *err)
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
-Object nvim_buf_get_option(Buffer buffer, String name, Arena *arena, Error *err)
+Object nvim_buf_get_option(Buffer buffer, String name, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(11)
{
@@ -600,7 +595,7 @@ void nvim_buf_set_option(uint64_t channel_id, Buffer buffer, String name, Object
/// @param name Option name
/// @param[out] err Error details, if any
/// @return Option value
-Object nvim_win_get_option(Window window, String name, Arena *arena, Error *err)
+Object nvim_win_get_option(Window window, String name, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(11)
{
@@ -649,7 +644,7 @@ static Object get_option_from(void *from, OptReqScope req_scope, String name, Er
return (Object)OBJECT_INIT;
});
- OptVal value = get_option_value_strict(name.data, req_scope, from, err);
+ OptVal value = get_option_value_strict(find_option(name.data), req_scope, from, err);
if (ERROR_SET(err)) {
return (Object)OBJECT_INIT;
}
@@ -675,8 +670,8 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope,
return;
});
- int flags = get_option_attrs(name.data);
- VALIDATE_S(flags != 0, "option name", name.data, {
+ OptIndex opt_idx = find_option(name.data);
+ VALIDATE_S(opt_idx != kOptInvalid, "option name", name.data, {
return;
});
@@ -691,13 +686,14 @@ static void set_option_to(uint64_t channel_id, void *to, OptReqScope req_scope,
return;
});
+ int attrs = get_option_attrs(opt_idx);
// For global-win-local options -> setlocal
// For win-local options -> setglobal and setlocal (opt_flags == 0)
- const int opt_flags = (req_scope == kOptReqWin && !(flags & SOPT_GLOBAL))
+ const int opt_flags = (req_scope == kOptReqWin && !(attrs & SOPT_GLOBAL))
? 0
: (req_scope == kOptReqGlobal) ? OPT_GLOBAL : OPT_LOCAL;
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, optval, opt_flags, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err);
});
}
diff --git a/src/nvim/api/deprecated.h b/src/nvim/api/deprecated.h
index e20d8304e0..c879794bb3 100644
--- a/src/nvim/api/deprecated.h
+++ b/src/nvim/api/deprecated.h
@@ -2,6 +2,7 @@
#include <stdint.h> // IWYU pragma: keep
+#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/api/dispatch_deprecated.lua b/src/nvim/api/dispatch_deprecated.lua
index 5650a77ac0..7a92789f79 100644
--- a/src/nvim/api/dispatch_deprecated.lua
+++ b/src/nvim/api/dispatch_deprecated.lua
@@ -1,69 +1,69 @@
local deprecated_aliases = {
- nvim_buf_add_highlight="buffer_add_highlight",
- nvim_buf_clear_highlight="buffer_clear_highlight",
- nvim_buf_get_lines="buffer_get_lines",
- nvim_buf_get_mark="buffer_get_mark",
- nvim_buf_get_name="buffer_get_name",
- nvim_buf_get_number="buffer_get_number",
- nvim_buf_get_option="buffer_get_option",
- nvim_buf_get_var="buffer_get_var",
- nvim_buf_is_valid="buffer_is_valid",
- nvim_buf_line_count="buffer_line_count",
- nvim_buf_set_lines="buffer_set_lines",
- nvim_buf_set_name="buffer_set_name",
- nvim_buf_set_option="buffer_set_option",
- nvim_call_function="vim_call_function",
- nvim_command="vim_command",
- nvim_command_output="vim_command_output",
- nvim_del_current_line="vim_del_current_line",
- nvim_err_write="vim_err_write",
- nvim_err_writeln="vim_report_error",
- nvim_eval="vim_eval",
- nvim_feedkeys="vim_feedkeys",
- nvim_get_api_info="vim_get_api_info",
- nvim_get_color_by_name="vim_name_to_color",
- nvim_get_color_map="vim_get_color_map",
- nvim_get_current_buf="vim_get_current_buffer",
- nvim_get_current_line="vim_get_current_line",
- nvim_get_current_tabpage="vim_get_current_tabpage",
- nvim_get_current_win="vim_get_current_window",
- nvim_get_option="vim_get_option",
- nvim_get_var="vim_get_var",
- nvim_get_vvar="vim_get_vvar",
- nvim_input="vim_input",
- nvim_list_bufs="vim_get_buffers",
- nvim_list_runtime_paths="vim_list_runtime_paths",
- nvim_list_tabpages="vim_get_tabpages",
- nvim_list_wins="vim_get_windows",
- nvim_out_write="vim_out_write",
- nvim_replace_termcodes="vim_replace_termcodes",
- nvim_set_current_buf="vim_set_current_buffer",
- nvim_set_current_dir="vim_change_directory",
- nvim_set_current_line="vim_set_current_line",
- nvim_set_current_tabpage="vim_set_current_tabpage",
- nvim_set_current_win="vim_set_current_window",
- nvim_set_option="vim_set_option",
- nvim_strwidth="vim_strwidth",
- nvim_subscribe="vim_subscribe",
- nvim_tabpage_get_var="tabpage_get_var",
- nvim_tabpage_get_win="tabpage_get_window",
- nvim_tabpage_is_valid="tabpage_is_valid",
- nvim_tabpage_list_wins="tabpage_get_windows",
- nvim_ui_detach="ui_detach",
- nvim_ui_try_resize="ui_try_resize",
- nvim_unsubscribe="vim_unsubscribe",
- nvim_win_get_buf="window_get_buffer",
- nvim_win_get_cursor="window_get_cursor",
- nvim_win_get_height="window_get_height",
- nvim_win_get_option="window_get_option",
- nvim_win_get_position="window_get_position",
- nvim_win_get_tabpage="window_get_tabpage",
- nvim_win_get_var="window_get_var",
- nvim_win_get_width="window_get_width",
- nvim_win_is_valid="window_is_valid",
- nvim_win_set_cursor="window_set_cursor",
- nvim_win_set_height="window_set_height",
- nvim_win_set_option="window_set_option",
- nvim_win_set_width="window_set_width",
+ nvim_buf_add_highlight = 'buffer_add_highlight',
+ nvim_buf_clear_highlight = 'buffer_clear_highlight',
+ nvim_buf_get_lines = 'buffer_get_lines',
+ nvim_buf_get_mark = 'buffer_get_mark',
+ nvim_buf_get_name = 'buffer_get_name',
+ nvim_buf_get_number = 'buffer_get_number',
+ nvim_buf_get_option = 'buffer_get_option',
+ nvim_buf_get_var = 'buffer_get_var',
+ nvim_buf_is_valid = 'buffer_is_valid',
+ nvim_buf_line_count = 'buffer_line_count',
+ nvim_buf_set_lines = 'buffer_set_lines',
+ nvim_buf_set_name = 'buffer_set_name',
+ nvim_buf_set_option = 'buffer_set_option',
+ nvim_call_function = 'vim_call_function',
+ nvim_command = 'vim_command',
+ nvim_command_output = 'vim_command_output',
+ nvim_del_current_line = 'vim_del_current_line',
+ nvim_err_write = 'vim_err_write',
+ nvim_err_writeln = 'vim_report_error',
+ nvim_eval = 'vim_eval',
+ nvim_feedkeys = 'vim_feedkeys',
+ nvim_get_api_info = 'vim_get_api_info',
+ nvim_get_color_by_name = 'vim_name_to_color',
+ nvim_get_color_map = 'vim_get_color_map',
+ nvim_get_current_buf = 'vim_get_current_buffer',
+ nvim_get_current_line = 'vim_get_current_line',
+ nvim_get_current_tabpage = 'vim_get_current_tabpage',
+ nvim_get_current_win = 'vim_get_current_window',
+ nvim_get_option = 'vim_get_option',
+ nvim_get_var = 'vim_get_var',
+ nvim_get_vvar = 'vim_get_vvar',
+ nvim_input = 'vim_input',
+ nvim_list_bufs = 'vim_get_buffers',
+ nvim_list_runtime_paths = 'vim_list_runtime_paths',
+ nvim_list_tabpages = 'vim_get_tabpages',
+ nvim_list_wins = 'vim_get_windows',
+ nvim_out_write = 'vim_out_write',
+ nvim_replace_termcodes = 'vim_replace_termcodes',
+ nvim_set_current_buf = 'vim_set_current_buffer',
+ nvim_set_current_dir = 'vim_change_directory',
+ nvim_set_current_line = 'vim_set_current_line',
+ nvim_set_current_tabpage = 'vim_set_current_tabpage',
+ nvim_set_current_win = 'vim_set_current_window',
+ nvim_set_option = 'vim_set_option',
+ nvim_strwidth = 'vim_strwidth',
+ nvim_subscribe = 'vim_subscribe',
+ nvim_tabpage_get_var = 'tabpage_get_var',
+ nvim_tabpage_get_win = 'tabpage_get_window',
+ nvim_tabpage_is_valid = 'tabpage_is_valid',
+ nvim_tabpage_list_wins = 'tabpage_get_windows',
+ nvim_ui_detach = 'ui_detach',
+ nvim_ui_try_resize = 'ui_try_resize',
+ nvim_unsubscribe = 'vim_unsubscribe',
+ nvim_win_get_buf = 'window_get_buffer',
+ nvim_win_get_cursor = 'window_get_cursor',
+ nvim_win_get_height = 'window_get_height',
+ nvim_win_get_option = 'window_get_option',
+ nvim_win_get_position = 'window_get_position',
+ nvim_win_get_tabpage = 'window_get_tabpage',
+ nvim_win_get_var = 'window_get_var',
+ nvim_win_get_width = 'window_get_width',
+ nvim_win_is_valid = 'window_is_valid',
+ nvim_win_set_cursor = 'window_set_cursor',
+ nvim_win_set_height = 'window_set_height',
+ nvim_win_set_option = 'window_set_option',
+ nvim_win_set_width = 'window_set_width',
}
return deprecated_aliases
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index d71498d6ed..1b03a97edb 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -14,16 +14,19 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_defs.h"
#include "nvim/decoration_provider.h"
#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
-#include "nvim/func_attr.h"
#include "nvim/grid.h"
#include "nvim/highlight_group.h"
+#include "nvim/map_defs.h"
#include "nvim/marktree.h"
+#include "nvim/marktree_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/move.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
@@ -40,7 +43,7 @@ void api_extmark_free_all_mem(void)
map_destroy(String, &namespace_ids);
}
-/// Creates a new namespace or gets an existing one. \*namespace\*
+/// Creates a new namespace or gets an existing one. [namespace]()
///
/// Namespaces are used for buffer highlights and virtual text, see
/// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|.
@@ -69,15 +72,15 @@ Integer nvim_create_namespace(String name)
/// Gets existing, non-anonymous |namespace|s.
///
/// @return dict that maps from names to namespace ids.
-Dictionary nvim_get_namespaces(void)
+Dictionary nvim_get_namespaces(Arena *arena)
FUNC_API_SINCE(5)
{
- Dictionary retval = ARRAY_DICT_INIT;
+ Dictionary retval = arena_dict(arena, map_size(&namespace_ids));
String name;
handle_T id;
map_foreach(&namespace_ids, name, id, {
- PUT(retval, name.data, INTEGER_OBJ(id));
+ PUT_C(retval, name.data, INTEGER_OBJ(id));
})
return retval;
@@ -104,73 +107,85 @@ bool ns_initialized(uint32_t ns)
return ns < (uint32_t)next_namespace_id;
}
-Array virt_text_to_array(VirtText vt, bool hl_name)
+Array virt_text_to_array(VirtText vt, bool hl_name, Arena *arena)
{
- Array chunks = ARRAY_DICT_INIT;
- Array hl_array = ARRAY_DICT_INIT;
+ Array chunks = arena_array(arena, kv_size(vt));
for (size_t i = 0; i < kv_size(vt); i++) {
- char *text = kv_A(vt, i).text;
- int hl_id = kv_A(vt, i).hl_id;
- if (text == NULL) {
+ size_t j = i;
+ for (; j < kv_size(vt); j++) {
+ if (kv_A(vt, j).text != NULL) {
+ break;
+ }
+ }
+
+ Array hl_array = arena_array(arena, i < j ? j - i + 1 : 0);
+ for (; i < j; i++) {
+ int hl_id = kv_A(vt, i).hl_id;
if (hl_id > 0) {
- ADD(hl_array, hl_group_name(hl_id, hl_name));
+ ADD_C(hl_array, hl_group_name(hl_id, hl_name));
}
- continue;
}
- Array chunk = ARRAY_DICT_INIT;
- ADD(chunk, CSTR_TO_OBJ(text));
+
+ char *text = kv_A(vt, i).text;
+ int hl_id = kv_A(vt, i).hl_id;
+ Array chunk = arena_array(arena, 2);
+ ADD_C(chunk, CSTR_AS_OBJ(text));
if (hl_array.size > 0) {
if (hl_id > 0) {
- ADD(hl_array, hl_group_name(hl_id, hl_name));
+ ADD_C(hl_array, hl_group_name(hl_id, hl_name));
}
- ADD(chunk, ARRAY_OBJ(hl_array));
- hl_array = (Array)ARRAY_DICT_INIT;
+ ADD_C(chunk, ARRAY_OBJ(hl_array));
} else if (hl_id > 0) {
- ADD(chunk, hl_group_name(hl_id, hl_name));
+ ADD_C(chunk, hl_group_name(hl_id, hl_name));
}
- ADD(chunks, ARRAY_OBJ(chunk));
+ ADD_C(chunks, ARRAY_OBJ(chunk));
}
- assert(hl_array.size == 0);
return chunks;
}
-static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name)
+static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_name, Arena *arena)
{
MTKey start = extmark.start;
- Array rv = ARRAY_DICT_INIT;
+ Array rv = arena_array(arena, 4);
if (id) {
- ADD(rv, INTEGER_OBJ((Integer)start.id));
+ ADD_C(rv, INTEGER_OBJ((Integer)start.id));
}
- ADD(rv, INTEGER_OBJ(start.pos.row));
- ADD(rv, INTEGER_OBJ(start.pos.col));
+ ADD_C(rv, INTEGER_OBJ(start.pos.row));
+ ADD_C(rv, INTEGER_OBJ(start.pos.col));
if (add_dict) {
- Dictionary dict = ARRAY_DICT_INIT;
+ // TODO(bfredl): coding the size like this is a bit fragile.
+ // We want ArrayOf(Dict(set_extmark)) as the return type..
+ Dictionary dict = arena_dict(arena, ARRAY_SIZE(set_extmark_table));
- PUT(dict, "ns_id", INTEGER_OBJ((Integer)start.ns));
+ PUT_C(dict, "ns_id", INTEGER_OBJ((Integer)start.ns));
- PUT(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start)));
+ PUT_C(dict, "right_gravity", BOOLEAN_OBJ(mt_right(start)));
- if (extmark.end_pos.row >= 0) {
- PUT(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row));
- PUT(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col));
- PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity));
+ if (mt_paired(start)) {
+ PUT_C(dict, "end_row", INTEGER_OBJ(extmark.end_pos.row));
+ PUT_C(dict, "end_col", INTEGER_OBJ(extmark.end_pos.col));
+ PUT_C(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity));
}
if (mt_no_undo(start)) {
- PUT(dict, "undo_restore", BOOLEAN_OBJ(false));
+ PUT_C(dict, "undo_restore", BOOLEAN_OBJ(false));
}
if (mt_invalidate(start)) {
- PUT(dict, "invalidate", BOOLEAN_OBJ(true));
+ PUT_C(dict, "invalidate", BOOLEAN_OBJ(true));
}
if (mt_invalid(start)) {
- PUT(dict, "invalid", BOOLEAN_OBJ(true));
+ PUT_C(dict, "invalid", BOOLEAN_OBJ(true));
+ }
+
+ if (mt_scoped(start)) {
+ PUT_C(dict, "scoped", BOOLEAN_OBJ(true));
}
- decor_to_dict_legacy(&dict, mt_decor(start), hl_name);
+ decor_to_dict_legacy(&dict, mt_decor(start), hl_name, arena);
- ADD(rv, DICTIONARY_OBJ(dict));
+ ADD_C(rv, DICTIONARY_OBJ(dict));
}
return rv;
@@ -188,8 +203,8 @@ static Array extmark_to_array(MTPair extmark, bool id, bool add_dict, bool hl_na
/// @return 0-indexed (row, col) tuple or empty list () if extmark id was
/// absent
ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
- Integer id, Dictionary opts,
- Error *err)
+ Integer id, Dict(get_extmark) *opts,
+ Arena *arena, Error *err)
FUNC_API_SINCE(7)
{
Array rv = ARRAY_DICT_INIT;
@@ -204,37 +219,19 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return rv;
});
- bool details = false;
- bool hl_name = true;
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object *v = &opts.items[i].value;
- if (strequal("details", k.data)) {
- details = api_object_to_bool(*v, "details", false, err);
- if (ERROR_SET(err)) {
- return rv;
- }
- } else if (strequal("hl_name", k.data)) {
- hl_name = api_object_to_bool(*v, "hl_name", false, err);
- if (ERROR_SET(err)) {
- return rv;
- }
- } else {
- VALIDATE_S(false, "'opts' key", k.data, {
- return rv;
- });
- }
- }
+ bool details = opts->details;
+
+ bool hl_name = GET_BOOL_OR_TRUE(opts, get_extmark, hl_name);
MTPair extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
if (extmark.start.pos.row < 0) {
return rv;
}
- return extmark_to_array(extmark, false, details, hl_name);
+ return extmark_to_array(extmark, false, details, hl_name, arena);
}
-/// Gets |extmarks| (including |signs|) in "traversal order" from a |charwise|
-/// region defined by buffer positions (inclusive, 0-indexed |api-indexing|).
+/// Gets |extmarks| in "traversal order" from a |charwise| region defined by
+/// buffer positions (inclusive, 0-indexed |api-indexing|).
///
/// Region can be given as (row,col) tuples, or valid extmark ids (whose
/// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
@@ -252,6 +249,10 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// the `overlap` option might be useful. Otherwise only the start position
/// of an extmark will be considered.
///
+/// Note: legacy signs placed through the |:sign| commands are implemented
+/// as extmarks and will show up here. Their details array will contain a
+/// `sign_name` field.
+///
/// Example:
///
/// ```lua
@@ -283,9 +284,9 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// their start position is less than `start`
/// - type: Filter marks by type: "highlight", "sign", "virt_text" and "virt_lines"
/// @param[out] err Error details, if any
-/// @return List of [extmark_id, row, col] tuples in "traversal order".
+/// @return List of `[extmark_id, row, col]` tuples in "traversal order".
Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object end,
- Dict(get_extmarks) *opts, Error *err)
+ Dict(get_extmarks) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(7)
{
Array rv = ARRAY_DICT_INIT;
@@ -349,8 +350,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row,
u_col, (int64_t)limit, reverse, type, opts->overlap);
+ rv = arena_array(arena, kv_size(marks));
for (size_t i = 0; i < kv_size(marks); i++) {
- ADD(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name)));
+ ADD_C(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena)));
}
kv_destroy(marks);
@@ -388,7 +390,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// of the screen line (just like for diff and
/// cursorline highlight).
/// - virt_text : virtual text to link to this mark.
-/// A list of [text, highlight] tuples, each representing a
+/// A list of `[text, highlight]` tuples, each representing a
/// text chunk with specified highlight. `highlight` element
/// can either be a single highlight group, or an array of
/// multiple highlight groups that will be stacked
@@ -410,6 +412,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// text is selected or hidden because of
/// scrolling with 'nowrap' or 'smoothscroll'.
/// Currently only affects "overlay" virt_text.
+/// - virt_text_repeat_linebreak : repeat the virtual text on
+/// wrapped lines.
/// - hl_mode : control how highlights are combined with the
/// highlights of the text. Currently only affects
/// virt_text highlights, but might affect `hl_group`
@@ -421,7 +425,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
///
/// - virt_lines : virtual lines to add next to this mark
/// This should be an array over lines, where each line in
-/// turn is an array over [text, highlight] tuples. In
+/// turn is an array over `[text, highlight]` tuples. In
/// general, buffer and window options do not affect the
/// display of the text. In particular 'wrap'
/// and 'linebreak' options do not take effect, so
@@ -450,35 +454,28 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// if text around the mark was deleted and then restored by undo.
/// Defaults to true.
/// - invalidate : boolean that indicates whether to hide the
-/// extmark if the entirety of its range is deleted. If
+/// extmark if the entirety of its range is deleted. For
+/// hidden marks, an "invalid" key is added to the "details"
+/// array of |nvim_buf_get_extmarks()| and family. If
/// "undo_restore" is false, the extmark is deleted instead.
-/// - priority: a priority value for the highlight group or sign
-/// attribute. For example treesitter highlighting uses a
-/// value of 100.
+/// - priority: a priority value for the highlight group, sign
+/// attribute or virtual text. For virtual text, item with
+/// highest priority is drawn last. For example treesitter
+/// highlighting uses a value of 100.
/// - strict: boolean that indicates extmark should not be placed
/// if the line or column value is past the end of the
/// buffer or end of the line respectively. Defaults to true.
/// - sign_text: string of length 1-2 used to display in the
/// sign column.
-/// Note: ranges are unsupported and decorations are only
-/// applied to start_row
/// - sign_hl_group: name of the highlight group used to
/// highlight the sign column text.
-/// Note: ranges are unsupported and decorations are only
-/// applied to start_row
/// - number_hl_group: name of the highlight group used to
/// highlight the number column.
-/// Note: ranges are unsupported and decorations are only
-/// applied to start_row
/// - line_hl_group: name of the highlight group used to
/// highlight the whole line.
-/// Note: ranges are unsupported and decorations are only
-/// applied to start_row
/// - cursorline_hl_group: name of the highlight group used to
-/// highlight the line when the cursor is on the same line
-/// as the mark and 'cursorline' is enabled.
-/// Note: ranges are unsupported and decorations are only
-/// applied to start_row
+/// highlight the sign column text when the cursor is on
+/// the same line as the mark and 'cursorline' is enabled.
/// - conceal: string which should be either empty or a single
/// character. Enable concealing similar to |:syn-conceal|.
/// When a character is supplied it is used as |:syn-cchar|.
@@ -490,6 +487,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// by a UI. When set, the UI will receive win_extmark events.
/// Note: the mark is positioned by virt_text attributes. Can be
/// used together with virt_text.
+/// - url: A URL to associate with this extmark. In the TUI, the OSC 8 control
+/// sequence is used to generate a clickable hyperlink to this URL.
+/// - scoped: boolean that indicates that the extmark should only be
+/// displayed in the namespace scope. (experimental)
///
/// @param[out] err Error details, if any
/// @return Id of the created/updated extmark
@@ -503,6 +504,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT;
DecorVirtText virt_text = DECOR_VIRT_TEXT_INIT;
DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT;
+ char *url = NULL;
bool has_hl = false;
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -556,36 +558,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = (int)val;
}
- // uncrustify:off
+ hl.hl_id = (int)opts->hl_group;
+ has_hl = hl.hl_id > 0;
+ sign.hl_id = (int)opts->sign_hl_group;
+ sign.cursorline_hl_id = (int)opts->cursorline_hl_group;
+ sign.number_hl_id = (int)opts->number_hl_group;
+ sign.line_hl_id = (int)opts->line_hl_group;
- // TODO(bfredl): keyset type alias for hl_group? (nil|int|string)
- struct {
- const char *name;
- Object *opt;
- int *dest;
- } hls[] = {
- { "hl_group" , &opts->hl_group , &hl.hl_id },
- { "sign_hl_group" , &opts->sign_hl_group , &sign.hl_id },
- { "number_hl_group" , &opts->number_hl_group , &sign.number_hl_id },
- { "line_hl_group" , &opts->line_hl_group , &sign.line_hl_id },
- { "cursorline_hl_group", &opts->cursorline_hl_group, &sign.cursorline_hl_id },
- { NULL, NULL, NULL },
- };
-
- // uncrustify:on
-
- for (int j = 0; hls[j].name && hls[j].dest; j++) {
- if (hls[j].opt->type != kObjectTypeNil) {
- if (j > 0) {
- sign.flags |= kSHIsSign;
- } else {
- has_hl = true;
- }
- *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
- if (ERROR_SET(err)) {
- goto error;
- }
- }
+ if (sign.hl_id || sign.cursorline_hl_id || sign.number_hl_id || sign.line_hl_id) {
+ sign.flags |= kSHIsSign;
}
if (HAS_KEY(opts, set_extmark, conceal)) {
@@ -632,7 +613,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
hl.flags |= opts->hl_eol ? kSHHlEol : 0;
- virt_text.flags |= opts->virt_text_hide ? kVTHide : 0;
+ virt_text.flags |= ((opts->virt_text_hide ? kVTHide : 0)
+ | (opts->virt_text_repeat_linebreak ? kVTRepeatLinebreak : 0));
if (HAS_KEY(opts, set_extmark, hl_mode)) {
String str = opts->hl_mode;
@@ -684,9 +666,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
if (HAS_KEY(opts, set_extmark, sign_text)) {
- sign.text.ptr = NULL;
- VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data),
- "sign_text", "", {
+ sign.text[0] = 0;
+ VALIDATE_S(init_sign_text(NULL, sign.text, opts->sign_text.data), "sign_text", "", {
goto error;
});
sign.flags |= kSHIsSign;
@@ -708,6 +689,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
has_hl = true;
}
+ if (HAS_KEY(opts, set_extmark, url)) {
+ url = string_to_cstr(opts->url);
+ }
+
if (opts->ui_watched) {
hl.flags |= kSHUIWatched;
if (virt_text.pos == kVPosOverlay) {
@@ -764,6 +749,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) {
+ if (opts->scoped) {
+ api_set_error(err, kErrorTypeException, "not yet implemented");
+ goto error;
+ }
+
int r = (int)line;
int c = (int)col;
if (line2 == -1) {
@@ -771,15 +761,32 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = c;
}
+ DecorPriority subpriority = DECOR_PRIORITY_BASE;
+ if (HAS_KEY(opts, set_extmark, _subpriority)) {
+ VALIDATE_RANGE((opts->_subpriority >= 0 && opts->_subpriority <= UINT16_MAX),
+ "_subpriority", {
+ goto error;
+ });
+ subpriority = (DecorPriority)opts->_subpriority;
+ }
+
if (kv_size(virt_text.data.virt_text)) {
- decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true);
+ decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_text, NULL), true,
+ subpriority);
}
if (kv_size(virt_lines.data.virt_lines)) {
- decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true);
+ decor_range_add_virt(&decor_state, r, c, line2, col2, decor_put_vt(virt_lines, NULL), true,
+ subpriority);
+ }
+ if (url != NULL) {
+ DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT;
+ sh.url = url;
+ decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, 0, 0, subpriority);
}
if (has_hl) {
DecorSignHighlight sh = decor_sh_from_inline(hl);
- decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id);
+ decor_range_add_sh(&decor_state, r, c, line2, col2, &sh, true, (uint32_t)ns_id, id,
+ subpriority);
}
} else {
if (opts->ephemeral) {
@@ -802,9 +809,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
uint32_t decor_indexed = DECOR_ID_INVALID;
+ if (url != NULL) {
+ DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT;
+ sh.url = url;
+ sh.next = decor_indexed;
+ decor_indexed = decor_put_sh(sh);
+ }
if (sign.flags & kSHIsSign) {
+ sign.next = decor_indexed;
decor_indexed = decor_put_sh(sign);
- if (sign.text.ptr != NULL) {
+ if (sign.text[0]) {
decor_flags |= MT_FLAG_DECOR_SIGNTEXT;
}
if (sign.number_hl_id || sign.line_hl_id || sign.cursorline_hl_id) {
@@ -832,7 +846,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
decor, decor_flags, right_gravity, opts->end_right_gravity,
!GET_BOOL_OR_TRUE(opts, set_extmark, undo_restore),
- opts->invalidate, err);
+ opts->invalidate, opts->scoped, err);
if (ERROR_SET(err)) {
decor_free(decor);
return 0;
@@ -844,6 +858,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
error:
clear_virttext(&virt_text.data.virt_text);
clear_virtlines(&virt_lines.data.virt_lines);
+ if (url != NULL) {
+ xfree(url);
+ }
+
return 0;
}
@@ -954,7 +972,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
decor.data.hl.hl_id = hl_id;
extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
- decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
+ decor, MT_FLAG_DECOR_HL, true, false, false, false, false, NULL);
return ns_id;
}
@@ -1022,19 +1040,27 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// @param ns_id Namespace id from |nvim_create_namespace()|
/// @param opts Table of callbacks:
/// - on_start: called first on each screen redraw
+/// ```
/// ["start", tick]
+/// ```
/// - on_buf: called for each buffer being redrawn (before
-/// window callbacks)
+/// window callbacks)
+/// ```
/// ["buf", bufnr, tick]
-/// - on_win: called when starting to redraw a
-/// specific window. botline_guess is an approximation
-/// that does not exceed the last line number.
-/// ["win", winid, bufnr, topline, botline_guess]
+/// ```
+/// - on_win: called when starting to redraw a specific window.
+/// ```
+/// ["win", winid, bufnr, topline, botline]
+/// ```
/// - on_line: called for each buffer line being redrawn.
/// (The interaction with fold lines is subject to change)
-/// ["win", winid, bufnr, row]
+/// ```
+/// ["line", winid, bufnr, row]
+/// ```
/// - on_end: called at the end of a redraw cycle
+/// ```
/// ["end", tick]
+/// ```
void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *opts, Error *err)
FUNC_API_SINCE(7) FUNC_API_LUA_ONLY
{
@@ -1070,7 +1096,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
*v = LUA_NOREF;
}
- p->active = true;
+ p->state = kDecorProviderActive;
p->hl_valid++;
p->hl_cached = false;
}
@@ -1189,8 +1215,9 @@ free_exit:
return virt_text;
}
+/// @nodoc
String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error *err)
- FUNC_API_SINCE(7)
+ FUNC_API_SINCE(7) FUNC_API_RET_ALLOC
{
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@@ -1199,3 +1226,72 @@ String nvim__buf_debug_extmarks(Buffer buffer, Boolean keys, Boolean dot, Error
return mt_inspect(buf->b_marktree, keys, dot);
}
+
+/// Adds the namespace scope to the window.
+///
+/// @param window Window handle, or 0 for current window
+/// @param ns_id the namespace to add
+/// @return true if the namespace was added, else false
+Boolean nvim_win_add_ns(Window window, Integer ns_id, Error *err)
+ FUNC_API_SINCE(12)
+{
+ win_T *win = find_window_by_handle(window, err);
+ if (!win) {
+ return false;
+ }
+
+ VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
+ return false;
+ });
+
+ set_put(uint32_t, &win->w_ns_set, (uint32_t)ns_id);
+
+ changed_window_setting_win(win);
+
+ return true;
+}
+
+/// Gets all the namespaces scopes associated with a window.
+///
+/// @param window Window handle, or 0 for current window
+/// @return a list of namespaces ids
+ArrayOf(Integer) nvim_win_get_ns(Window window, Arena *arena, Error *err)
+ FUNC_API_SINCE(12)
+{
+ win_T *win = find_window_by_handle(window, err);
+ if (!win) {
+ return (Array)ARRAY_DICT_INIT;
+ }
+
+ Array rv = arena_array(arena, set_size(&win->w_ns_set));
+ uint32_t i;
+ set_foreach(&win->w_ns_set, i, {
+ ADD_C(rv, INTEGER_OBJ((Integer)(i)));
+ });
+
+ return rv;
+}
+
+/// Removes the namespace scope from the window.
+///
+/// @param window Window handle, or 0 for current window
+/// @param ns_id the namespace to remove
+/// @return true if the namespace was removed, else false
+Boolean nvim_win_remove_ns(Window window, Integer ns_id, Error *err)
+ FUNC_API_SINCE(12)
+{
+ win_T *win = find_window_by_handle(window, err);
+ if (!win) {
+ return false;
+ }
+
+ if (!set_has(uint32_t, &win->w_ns_set, (uint32_t)ns_id)) {
+ return false;
+ }
+
+ set_del(uint32_t, &win->w_ns_set, (uint32_t)ns_id);
+
+ changed_window_setting_win(win);
+
+ return true;
+}
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index e59eda5686..fe91d9760d 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -3,6 +3,10 @@
#include "nvim/api/private/defs.h"
typedef struct {
+ OptionalKeys is_set__empty_;
+} Dict(empty);
+
+typedef struct {
OptionalKeys is_set__context_;
Array types;
} Dict(context);
@@ -24,11 +28,12 @@ typedef struct {
Integer end_line;
Integer end_row;
Integer end_col;
- Object hl_group;
+ HLGroupID hl_group;
Array virt_text;
String virt_text_pos;
Integer virt_text_win_col;
Boolean virt_text_hide;
+ Boolean virt_text_repeat_linebreak;
Boolean hl_eol;
String hl_mode;
Boolean invalidate;
@@ -41,17 +46,27 @@ typedef struct {
Boolean virt_lines_leftcol;
Boolean strict;
String sign_text;
- Object sign_hl_group;
- Object number_hl_group;
- Object line_hl_group;
- Object cursorline_hl_group;
+ HLGroupID sign_hl_group;
+ HLGroupID number_hl_group;
+ HLGroupID line_hl_group;
+ HLGroupID cursorline_hl_group;
String conceal;
Boolean spell;
Boolean ui_watched;
Boolean undo_restore;
+ String url;
+ Boolean scoped;
+
+ Integer _subpriority;
} Dict(set_extmark);
typedef struct {
+ OptionalKeys is_set__get_extmark_;
+ Boolean details;
+ Boolean hl_name;
+} Dict(get_extmark);
+
+typedef struct {
OptionalKeys is_set__get_extmarks_;
Integer limit;
Boolean details;
@@ -94,17 +109,19 @@ typedef struct {
} Dict(user_command);
typedef struct {
- OptionalKeys is_set__float_config_;
+ OptionalKeys is_set__win_config_;
Float row;
Float col;
Integer width;
Integer height;
String anchor;
String relative;
+ String split;
Window win;
Array bufpos;
Boolean external;
Boolean focusable;
+ Boolean vertical;
Integer zindex;
Object border;
Object title;
@@ -115,7 +132,7 @@ typedef struct {
Boolean noautocmd;
Boolean fixed;
Boolean hide;
-} Dict(float_config);
+} Dict(win_config);
typedef struct {
Boolean is_lua;
@@ -172,6 +189,7 @@ typedef struct {
Boolean fg_indexed;
Boolean bg_indexed;
Boolean force;
+ String url;
} Dict(highlight);
typedef struct {
@@ -313,3 +331,47 @@ typedef struct {
typedef struct {
Boolean output;
} Dict(exec_opts);
+
+typedef struct {
+ OptionalKeys is_set__buf_attach_;
+ LuaRef on_lines;
+ LuaRef on_bytes;
+ LuaRef on_changedtick;
+ LuaRef on_detach;
+ LuaRef on_reload;
+ Boolean utf_sizes;
+ Boolean preview;
+} Dict(buf_attach);
+
+typedef struct {
+ OptionalKeys is_set__buf_delete_;
+ Boolean force;
+ Boolean unload;
+} Dict(buf_delete);
+
+typedef struct {
+ OptionalKeys is_set__open_term_;
+ LuaRef on_input;
+ Boolean force_crlf;
+} Dict(open_term);
+
+typedef struct {
+ OptionalKeys is_set__complete_set_;
+ String info;
+} Dict(complete_set);
+
+typedef struct {
+ OptionalKeys is_set__xdl_diff_;
+ LuaRef on_hunk;
+ String result_type;
+ String algorithm;
+ Integer ctxlen;
+ Integer interhunkctxlen;
+ Object linematch;
+ Boolean ignore_whitespace;
+ Boolean ignore_whitespace_change;
+ Boolean ignore_whitespace_change_at_eol;
+ Boolean ignore_cr_at_eol;
+ Boolean ignore_blank_lines;
+ Boolean indent_heuristic;
+} Dict(xdl_diff);
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index c012a69c7b..d9bc0ccc92 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -9,24 +9,22 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
-#include "nvim/eval/window.h"
-#include "nvim/func_attr.h"
+#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
-#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/option.h"
-#include "nvim/option_vars.h"
+#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
-#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/options.c.generated.h"
#endif
-static int validate_option_value_args(Dict(option) *opts, char *name, int *scope,
- OptReqScope *req_scope, void **from, char **filetype,
- Error *err)
+static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp,
+ int *scope, OptReqScope *req_scope, void **from,
+ char **filetype, Error *err)
{
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
if (HAS_KEY_X(opts, scope)) {
@@ -80,7 +78,8 @@ static int validate_option_value_args(Dict(option) *opts, char *name, int *scope
return FAIL;
});
- int flags = get_option_attrs(name);
+ *opt_idxp = find_option(name);
+ int flags = get_option_attrs(*opt_idxp);
if (flags == 0) {
// hidden or unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
@@ -120,10 +119,10 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err)
aucmd_prepbuf(aco, ftbuf);
TRY_WRAP(err, {
- set_option_value("bufhidden", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL);
- set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL);
- set_option_value("swapfile", BOOLEAN_OPTVAL(false), OPT_LOCAL);
- set_option_value("modeline", BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline'
+ set_option_value(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL);
+ set_option_value(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL);
+ set_option_value(kOptSwapfile, BOOLEAN_OPTVAL(false), OPT_LOCAL);
+ set_option_value(kOptModeline, BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline'
ftbuf->b_p_ft = xstrdup(filetype);
do_filetype_autocmd(ftbuf, false);
@@ -151,25 +150,24 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err)
/// @param[out] err Error details, if any
/// @return Option value
Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
- FUNC_API_SINCE(9)
+ FUNC_API_SINCE(9) FUNC_API_RET_ALLOC
{
- Object rv = OBJECT_INIT;
- OptVal value = NIL_OPTVAL;
-
+ OptIndex opt_idx = 0;
int scope = 0;
OptReqScope req_scope = kOptReqGlobal;
void *from = NULL;
char *filetype = NULL;
- if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &from, &filetype, err)) {
- goto err;
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, &filetype,
+ err)) {
+ return (Object)OBJECT_INIT;
}
aco_save_T aco;
buf_T *ftbuf = do_ft_buf(filetype, &aco, err);
if (ERROR_SET(err)) {
- goto err;
+ return (Object)OBJECT_INIT;
}
if (ftbuf != NULL) {
@@ -177,8 +175,8 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
from = ftbuf;
}
- bool hidden;
- value = get_option_value_for(name.data, NULL, scope, &hidden, req_scope, from, err);
+ OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err);
+ bool hidden = is_option_hidden(opt_idx);
if (ftbuf != NULL) {
// restore curwin/curbuf and a few other things
@@ -199,7 +197,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
return optval_as_object(value);
err:
optval_free(value);
- return rv;
+ return (Object)OBJECT_INIT;
}
/// Sets the value of an option. The behavior of this function matches that of
@@ -220,10 +218,11 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
Error *err)
FUNC_API_SINCE(9)
{
+ OptIndex opt_idx = 0;
int scope = 0;
OptReqScope req_scope = kOptReqGlobal;
void *to = NULL;
- if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &to, NULL, err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) {
return;
}
@@ -234,7 +233,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
//
// Then force scope to local since we don't want to change the global option
if (req_scope == kOptReqWin && scope == 0) {
- int flags = get_option_attrs(name.data);
+ int flags = get_option_attrs(opt_idx);
if (flags & SOPT_GLOBAL) {
scope = OPT_LOCAL;
}
@@ -252,7 +251,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
});
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, optval, scope, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, scope, req_scope, to, err);
});
}
@@ -264,30 +263,30 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
/// @see |nvim_get_commands()|
///
/// @return dictionary of all options
-Dictionary nvim_get_all_options_info(Error *err)
+Dictionary nvim_get_all_options_info(Arena *arena, Error *err)
FUNC_API_SINCE(7)
{
- return get_all_vimoptions();
+ return get_all_vimoptions(arena);
}
/// Gets the option information for one option from arbitrary buffer or window
///
/// Resulting dictionary has keys:
-/// - name: Name of the option (like 'filetype')
-/// - shortname: Shortened name of the option (like 'ft')
-/// - type: type of option ("string", "number" or "boolean")
-/// - default: The default value for the option
-/// - was_set: Whether the option was set.
+/// - name: Name of the option (like 'filetype')
+/// - shortname: Shortened name of the option (like 'ft')
+/// - type: type of option ("string", "number" or "boolean")
+/// - default: The default value for the option
+/// - was_set: Whether the option was set.
///
-/// - last_set_sid: Last set script id (if any)
-/// - last_set_linenr: line number where option was set
-/// - last_set_chan: Channel where option was set (0 for local)
+/// - last_set_sid: Last set script id (if any)
+/// - last_set_linenr: line number where option was set
+/// - last_set_chan: Channel where option was set (0 for local)
///
-/// - scope: one of "global", "win", or "buf"
-/// - global_local: whether win or buf option has a global value
+/// - scope: one of "global", "win", or "buf"
+/// - global_local: whether win or buf option has a global value
///
-/// - commalist: List of comma separated values
-/// - flaglist: List of single char flags
+/// - commalist: List of comma separated values
+/// - flaglist: List of single char flags
///
/// When {scope} is not provided, the last set information applies to the local
/// value in the current buffer or window if it is available, otherwise the
@@ -303,275 +302,20 @@ Dictionary nvim_get_all_options_info(Error *err)
/// Implies {scope} is "local".
/// @param[out] err Error details, if any
/// @return Option Information
-Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err)
+Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(11)
{
+ OptIndex opt_idx = 0;
int scope = 0;
OptReqScope req_scope = kOptReqGlobal;
void *from = NULL;
- if (!validate_option_value_args(opts, name.data, &scope, &req_scope, &from, NULL, err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL,
+ err)) {
return (Dictionary)ARRAY_DICT_INIT;
}
buf_T *buf = (req_scope == kOptReqBuf) ? (buf_T *)from : curbuf;
win_T *win = (req_scope == kOptReqWin) ? (win_T *)from : curwin;
- return get_vimoption(name, scope, buf, win, err);
-}
-
-/// Switch current context to get/set option value for window/buffer.
-///
-/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-/// @param[in] from Target buffer/window.
-/// @param[out] err Error message, if any.
-///
-/// @return true if context was switched, false otherwise.
-static bool switch_option_context(void *const ctx, OptReqScope req_scope, void *const from,
- Error *err)
-{
- switch (req_scope) {
- case kOptReqWin: {
- win_T *const win = (win_T *)from;
- switchwin_T *const switchwin = (switchwin_T *)ctx;
-
- if (win == curwin) {
- return false;
- }
-
- if (switch_win_noblock(switchwin, win, win_find_tabpage(win), true)
- == FAIL) {
- restore_win_noblock(switchwin, true);
-
- if (try_end(err)) {
- return false;
- }
- api_set_error(err, kErrorTypeException, "Problem while switching windows");
- return false;
- }
- return true;
- }
- case kOptReqBuf: {
- buf_T *const buf = (buf_T *)from;
- aco_save_T *const aco = (aco_save_T *)ctx;
-
- if (buf == curbuf) {
- return false;
- }
- aucmd_prepbuf(aco, buf);
- return true;
- }
- case kOptReqGlobal:
- return false;
- }
- UNREACHABLE;
-}
-
-/// Restore context after getting/setting option for window/buffer. See switch_option_context() for
-/// params.
-static void restore_option_context(void *const ctx, OptReqScope req_scope)
-{
- switch (req_scope) {
- case kOptReqWin:
- restore_win_noblock((switchwin_T *)ctx, true);
- break;
- case kOptReqBuf:
- aucmd_restbuf((aco_save_T *)ctx);
- break;
- case kOptReqGlobal:
- break;
- }
-}
-
-/// Get attributes for an option.
-///
-/// @param name Option name.
-///
-/// @return Option attributes.
-/// 0 for hidden or unknown option.
-/// See SOPT_* in option_defs.h for other flags.
-int get_option_attrs(char *name)
-{
- int opt_idx = findoption(name);
-
- if (opt_idx < 0) {
- return 0;
- }
-
- vimoption_T *opt = get_option(opt_idx);
-
- if (is_tty_option(opt->fullname)) {
- return SOPT_STRING | SOPT_GLOBAL;
- }
-
- // Hidden option
- if (opt->var == NULL) {
- return 0;
- }
-
- int attrs = 0;
-
- if (opt->flags & P_BOOL) {
- attrs |= SOPT_BOOL;
- } else if (opt->flags & P_NUM) {
- attrs |= SOPT_NUM;
- } else if (opt->flags & P_STRING) {
- attrs |= SOPT_STRING;
- }
-
- if (opt->indir == PV_NONE || (opt->indir & PV_BOTH)) {
- attrs |= SOPT_GLOBAL;
- }
- if (opt->indir & PV_WIN) {
- attrs |= SOPT_WIN;
- } else if (opt->indir & PV_BUF) {
- attrs |= SOPT_BUF;
- }
-
- return attrs;
-}
-
-/// Check if option has a value in the requested scope.
-///
-/// @param name Option name.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-///
-/// @return true if option has a value in the requested scope, false otherwise.
-static bool option_has_scope(char *name, OptReqScope req_scope)
-{
- int opt_idx = findoption(name);
-
- if (opt_idx < 0) {
- return false;
- }
-
- vimoption_T *opt = get_option(opt_idx);
-
- // Hidden option.
- if (opt->var == NULL) {
- return false;
- }
- // TTY option.
- if (is_tty_option(opt->fullname)) {
- return req_scope == kOptReqGlobal;
- }
-
- switch (req_scope) {
- case kOptReqGlobal:
- return opt->var != VAR_WIN;
- case kOptReqBuf:
- return opt->indir & PV_BUF;
- case kOptReqWin:
- return opt->indir & PV_WIN;
- }
- UNREACHABLE;
-}
-
-/// Get the option value in the requested scope.
-///
-/// @param name Option name.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-/// @param[in] from Pointer to buffer or window for local option value.
-/// @param[out] err Error message, if any.
-///
-/// @return Option value in the requested scope. Returns a Nil option value if option is not found,
-/// hidden or if it isn't present in the requested scope. (i.e. has no global, window-local or
-/// buffer-local value depending on opt_scope).
-OptVal get_option_value_strict(char *name, OptReqScope req_scope, void *from, Error *err)
-{
- OptVal retv = NIL_OPTVAL;
-
- if (!option_has_scope(name, req_scope)) {
- return retv;
- }
- if (get_tty_option(name, &retv.data.string.data)) {
- retv.type = kOptValTypeString;
- return retv;
- }
-
- int opt_idx = findoption(name);
- assert(opt_idx != 0); // option_has_scope() already verifies if option name is valid.
-
- vimoption_T *opt = get_option(opt_idx);
- switchwin_T switchwin;
- aco_save_T aco;
- void *ctx = req_scope == kOptReqWin ? (void *)&switchwin
- : (req_scope == kOptReqBuf ? (void *)&aco : NULL);
- bool switched = switch_option_context(ctx, req_scope, from, err);
- if (ERROR_SET(err)) {
- return retv;
- }
-
- char *varp = get_varp_scope(opt, req_scope == kOptReqGlobal ? OPT_GLOBAL : OPT_LOCAL);
- retv = optval_from_varp(opt_idx, varp);
-
- if (switched) {
- restore_option_context(ctx, req_scope);
- }
-
- return retv;
-}
-
-/// Get option value for buffer / window.
-///
-/// @param[in] name Option name.
-/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL).
-/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
-/// @param[out] hidden Whether option is hidden.
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-/// @param[in] from Target buffer/window.
-/// @param[out] err Error message, if any.
-///
-/// @return Option value. Must be freed by caller.
-OptVal get_option_value_for(const char *const name, uint32_t *flagsp, int scope, bool *hidden,
- const OptReqScope req_scope, void *const from, Error *err)
-{
- switchwin_T switchwin;
- aco_save_T aco;
- void *ctx = req_scope == kOptReqWin ? (void *)&switchwin
- : (req_scope == kOptReqBuf ? (void *)&aco : NULL);
-
- bool switched = switch_option_context(ctx, req_scope, from, err);
- if (ERROR_SET(err)) {
- return NIL_OPTVAL;
- }
-
- OptVal retv = get_option_value(name, flagsp, scope, hidden);
-
- if (switched) {
- restore_option_context(ctx, req_scope);
- }
-
- return retv;
-}
-
-/// Set option value for buffer / window.
-///
-/// @param[in] name Option name.
-/// @param[in] value Option value.
-/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// @param req_scope Requested option scope. See OptReqScope in option.h.
-/// @param[in] from Target buffer/window.
-/// @param[out] err Error message, if any.
-void set_option_value_for(const char *const name, OptVal value, const int opt_flags,
- const OptReqScope req_scope, void *const from, Error *err)
-{
- switchwin_T switchwin;
- aco_save_T aco;
- void *ctx = req_scope == kOptReqWin ? (void *)&switchwin
- : (req_scope == kOptReqBuf ? (void *)&aco : NULL);
-
- bool switched = switch_option_context(ctx, req_scope, from, err);
- if (ERROR_SET(err)) {
- return;
- }
-
- const char *const errmsg = set_option_value(name, value, opt_flags);
- if (errmsg) {
- api_set_error(err, kErrorTypeException, "%s", errmsg);
- }
-
- if (switched) {
- restore_option_context(ctx, req_scope);
- }
+ return get_vimoption(name, scope, buf, win, arena, err);
}
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"
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index c854a22477..040abb1e3f 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -6,17 +6,20 @@
#include "nvim/api/tabpage.h"
#include "nvim/api/vim.h"
#include "nvim/buffer_defs.h"
-#include "nvim/func_attr.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/window.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/tabpage.c.generated.h"
+#endif
+
/// Gets the windows in a tabpage
///
/// @param tabpage Tabpage handle, or 0 for current tabpage
/// @param[out] err Error details, if any
/// @return List of windows in `tabpage`
-ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err)
+ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
Array rv = ARRAY_DICT_INIT;
@@ -26,15 +29,15 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err)
return rv;
}
+ size_t n = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, tab) {
- rv.size++;
+ n++;
}
- rv.items = xmalloc(sizeof(Object) * rv.size);
- size_t i = 0;
+ rv = arena_array(arena, n);
FOR_ALL_WINDOWS_IN_TAB(wp, tab) {
- rv.items[i++] = WINDOW_OBJ(wp->handle);
+ ADD_C(rv, WINDOW_OBJ(wp->handle));
}
return rv;
@@ -46,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);
@@ -55,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
@@ -73,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
@@ -90,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
@@ -119,6 +122,37 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err)
abort();
}
+/// Sets the current window in a tabpage
+///
+/// @param tabpage Tabpage handle, or 0 for current tabpage
+/// @param win Window handle, must already belong to {tabpage}
+/// @param[out] err Error details, if any
+void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err)
+ FUNC_API_SINCE(12)
+{
+ tabpage_T *tp = find_tab_by_handle(tabpage, err);
+ if (!tp) {
+ return;
+ }
+
+ win_T *wp = find_window_by_handle(win, err);
+ if (!wp) {
+ return;
+ }
+
+ if (!tabpage_win_valid(tp, wp)) {
+ api_set_error(err, kErrorTypeException, "Window does not belong to tabpage %d", tp->handle);
+ return;
+ }
+
+ if (tp == curtab) {
+ win_enter(wp, true);
+ } else if (tp->tp_curwin != wp) {
+ tp->tp_prevwin = tp->tp_curwin;
+ tp->tp_curwin = wp;
+ }
+}
+
/// Gets the tabpage number
///
/// @param tabpage Tabpage handle, or 0 for current tabpage
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 836a68546c..692e3f95fc 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -12,27 +12,34 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/ui.h"
+#include "nvim/assert_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/channel.h"
+#include "nvim/channel_defs.h"
#include "nvim/eval.h"
+#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
+#include "nvim/event/multiqueue.h"
#include "nvim/event/wstream.h"
-#include "nvim/func_attr.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/helpers.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
+#include "nvim/msgpack_rpc/packer.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf))
+#define BUF_POS(ui) ((size_t)((ui)->packer.ptr - (ui)->packer.startptr))
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/ui.c.generated.h"
@@ -41,55 +48,6 @@
static PMap(uint64_t) connected_uis = MAP_INIT;
-#define mpack_w(b, byte) *(*b)++ = (char)(byte);
-static void mpack_w2(char **b, uint32_t v)
-{
- *(*b)++ = (char)((v >> 8) & 0xff);
- *(*b)++ = (char)(v & 0xff);
-}
-
-static void mpack_w4(char **b, uint32_t v)
-{
- *(*b)++ = (char)((v >> 24) & 0xff);
- *(*b)++ = (char)((v >> 16) & 0xff);
- *(*b)++ = (char)((v >> 8) & 0xff);
- *(*b)++ = (char)(v & 0xff);
-}
-
-static void mpack_uint(char **buf, uint32_t val)
-{
- if (val > 0xffff) {
- mpack_w(buf, 0xce);
- mpack_w4(buf, val);
- } else if (val > 0xff) {
- mpack_w(buf, 0xcd);
- mpack_w2(buf, val);
- } else if (val > 0x7f) {
- mpack_w(buf, 0xcc);
- mpack_w(buf, val);
- } else {
- mpack_w(buf, val);
- }
-}
-
-static void mpack_bool(char **buf, bool val)
-{
- mpack_w(buf, 0xc2 | (val ? 1 : 0));
-}
-
-static void mpack_array(char **buf, uint32_t len)
-{
- if (len < 0x10) {
- mpack_w(buf, 0x90 | len);
- } else if (len < 0x10000) {
- mpack_w(buf, 0xdc);
- mpack_w2(buf, len);
- } else {
- mpack_w(buf, 0xdd);
- mpack_w4(buf, len);
- }
-}
-
static char *mpack_array_dyn16(char **buf)
{
mpack_w(buf, 0xdc);
@@ -98,30 +56,44 @@ static char *mpack_array_dyn16(char **buf)
return pos;
}
-static void mpack_str(char **buf, const char *str)
+static void mpack_str_small(char **buf, const char *str, size_t len)
{
- assert(sizeof(schar_T) - 1 < 0x20);
- size_t len = strlen(str);
+ assert(len < 0x20);
mpack_w(buf, 0xa0 | len);
memcpy(*buf, str, len);
*buf += len;
}
+static void remote_ui_destroy(RemoteUI *ui)
+ FUNC_ATTR_NONNULL_ALL
+{
+ kv_destroy(ui->call_buf);
+ xfree(ui->packer.startptr);
+ XFREE_CLEAR(ui->term_name);
+ xfree(ui);
+}
+
void remote_ui_disconnect(uint64_t channel_id)
{
- UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
+ RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
if (!ui) {
return;
}
- UIData *data = ui->data;
- kv_destroy(data->call_buf);
pmap_del(uint64_t)(&connected_uis, channel_id, NULL);
ui_detach_impl(ui, channel_id);
+ remote_ui_destroy(ui);
+}
- // Destroy `ui`.
- XFREE_CLEAR(ui->term_name);
- xfree(ui);
+#ifdef EXITFREE
+void remote_ui_free_all_mem(void)
+{
+ RemoteUI *ui;
+ map_foreach_value(&connected_uis, ui, {
+ remote_ui_destroy(ui);
+ });
+ map_destroy(uint64_t, &connected_uis);
}
+#endif
/// Wait until ui has connected on stdio channel if only_stdio
/// is true, otherwise any channel.
@@ -145,7 +117,7 @@ void remote_ui_wait_for_attach(bool only_stdio)
/// Activates UI events on the channel.
///
-/// Entry point of all UI clients. Allows |\-\-embed| to continue startup.
+/// Entry point of all UI clients. Allows |--embed| to continue startup.
/// Implies that the client is ready to show the UI. Adds the client to the
/// list of UIs. |nvim_list_uis()|
///
@@ -173,7 +145,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
"Expected width > 0 and height > 0");
return;
}
- UI *ui = xcalloc(1, sizeof(UI));
+ RemoteUI *ui = xcalloc(1, sizeof(RemoteUI));
ui->width = (int)width;
ui->height = (int)height;
ui->pum_row = -1.0;
@@ -200,22 +172,26 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
ui->ui_ext[kUICmdline] = true;
}
- UIData *data = ui->data;
- data->channel_id = channel_id;
- data->cur_event = NULL;
- data->hl_id = 0;
- data->client_col = -1;
- data->nevents_pos = NULL;
- data->nevents = 0;
- data->flushed_events = false;
- data->ncalls_pos = NULL;
- data->ncalls = 0;
- data->ncells_pending = 0;
- data->buf_wptr = data->buf;
- data->temp_buf = NULL;
- data->wildmenu_active = false;
- data->call_buf = (Array)ARRAY_DICT_INIT;
- kv_ensure_space(data->call_buf, 16);
+ ui->channel_id = channel_id;
+ ui->cur_event = NULL;
+ ui->hl_id = 0;
+ ui->client_col = -1;
+ ui->nevents_pos = NULL;
+ ui->nevents = 0;
+ ui->flushed_events = false;
+ ui->ncalls_pos = NULL;
+ ui->ncalls = 0;
+ ui->ncells_pending = 0;
+ ui->packer = (PackerBuffer) {
+ .startptr = NULL,
+ .ptr = NULL,
+ .endptr = NULL,
+ .packer_flush = ui_flush_callback,
+ .anydata = ui,
+ };
+ ui->wildmenu_active = false;
+ ui->call_buf = (Array)ARRAY_DICT_INIT;
+ kv_ensure_space(ui->call_buf, 16);
pmap_put(uint64_t)(&connected_uis, channel_id, ui);
ui_attach_impl(ui, channel_id);
@@ -227,10 +203,9 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
void ui_attach(uint64_t channel_id, Integer width, Integer height, Boolean enable_rgb, Error *err)
FUNC_API_DEPRECATED_SINCE(1)
{
- Dictionary opts = ARRAY_DICT_INIT;
- PUT(opts, "rgb", BOOLEAN_OBJ(enable_rgb));
+ MAXSIZE_TEMP_DICT(opts, 1);
+ PUT_C(opts, "rgb", BOOLEAN_OBJ(enable_rgb));
nvim_ui_attach(channel_id, width, height, opts, err);
- api_free_dictionary(opts);
}
/// Tells the nvim server if focus was gained or lost by the GUI
@@ -268,7 +243,7 @@ void nvim_ui_detach(uint64_t channel_id, Error *err)
}
// TODO(bfredl): use me to detach a specific ui from the server
-void remote_ui_stop(UI *ui)
+void remote_ui_stop(RemoteUI *ui)
{
}
@@ -287,7 +262,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Erro
return;
}
- UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
+ RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
ui->width = (int)width;
ui->height = (int)height;
ui_refresh();
@@ -301,12 +276,12 @@ void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *e
"UI not attached to channel: %" PRId64, channel_id);
return;
}
- UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
+ RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
ui_set_option(ui, false, name, value, error);
}
-static void ui_set_option(UI *ui, bool init, String name, Object value, Error *err)
+static void ui_set_option(RemoteUI *ui, bool init, String name, Object value, Error *err)
{
if (strequal(name.data, "override")) {
VALIDATE_T("override", kObjectTypeBoolean, value.type, {
@@ -435,7 +410,7 @@ void nvim_ui_try_resize_grid(uint64_t channel_id, Integer grid, Integer width, I
}
/// Tells Nvim the number of elements displaying in the popupmenu, to decide
-/// <PageUp> and <PageDown> movement.
+/// [<PageUp>] and [<PageDown>] movement.
///
/// @param channel_id
/// @param height Popupmenu height, must be greater than zero.
@@ -454,7 +429,7 @@ void nvim_ui_pum_set_height(uint64_t channel_id, Integer height, Error *err)
return;
}
- UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
+ RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
if (!ui->ui_ext[kUIPopupmenu]) {
api_set_error(err, kErrorTypeValidation,
"It must support the ext_popupmenu option");
@@ -490,7 +465,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
return;
}
- UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
+ RemoteUI *ui = pmap_get(uint64_t)(&connected_uis, channel_id);
if (!ui->ui_ext[kUIPopupmenu]) {
api_set_error(err, kErrorTypeValidation,
"UI must support the ext_popupmenu option");
@@ -522,7 +497,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
///
/// @param channel_id
/// @param event Event name
-/// @param payload Event payload
+/// @param value Event payload
/// @param[out] err Error details, if any.
void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *err)
FUNC_API_SINCE(12) FUNC_API_REMOTE_ONLY
@@ -539,126 +514,75 @@ void nvim_ui_term_event(uint64_t channel_id, String event, Object value, Error *
}
}
-static void flush_event(UIData *data)
+static void flush_event(RemoteUI *ui)
{
- if (data->cur_event) {
- mpack_w2(&data->ncalls_pos, data->ncalls);
- data->cur_event = NULL;
- }
- if (!data->nevents_pos) {
- assert(BUF_POS(data) == 0);
- char **buf = &data->buf_wptr;
- // [2, "redraw", [...]]
- mpack_array(buf, 3);
- mpack_uint(buf, 2);
- mpack_str(buf, "redraw");
- data->nevents_pos = mpack_array_dyn16(buf);
+ if (ui->cur_event) {
+ mpack_w2(&ui->ncalls_pos, 1 + ui->ncalls);
+ ui->cur_event = NULL;
+ ui->ncalls_pos = NULL;
+ ui->ncalls = 0;
}
}
-static inline int write_cb(void *vdata, const char *buf, size_t len)
+static void ui_alloc_buf(RemoteUI *ui)
{
- UIData *data = (UIData *)vdata;
- if (!buf) {
- return 0;
- }
-
- data->pack_totlen += len;
- if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) {
- data->buf_overflow = true;
- return 0;
- }
-
- memcpy(data->buf_wptr, buf, len);
- data->buf_wptr += len;
-
- return 0;
+ ui->packer.startptr = alloc_block();
+ ui->packer.ptr = ui->packer.startptr;
+ ui->packer.endptr = ui->packer.startptr + UI_BUF_SIZE;
}
-static bool prepare_call(UI *ui, const char *name)
+static void prepare_call(RemoteUI *ui, const char *name)
{
- UIData *data = ui->data;
+ if (ui->packer.startptr && BUF_POS(ui) > UI_BUF_SIZE - EVENT_BUF_SIZE) {
+ ui_flush_buf(ui);
+ }
- if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) {
- remote_ui_flush_buf(ui);
+ if (ui->packer.startptr == NULL) {
+ ui_alloc_buf(ui);
}
// To optimize data transfer(especially for "grid_line"), we bundle adjacent
// calls to same method together, so only add a new call entry if the last
// method call is different from "name"
- if (!data->cur_event || !strequal(data->cur_event, name)) {
- flush_event(data);
- data->cur_event = name;
- char **buf = &data->buf_wptr;
- data->ncalls_pos = mpack_array_dyn16(buf);
- mpack_str(buf, name);
- data->nevents++;
- data->ncalls = 1;
- return true;
+ if (!ui->cur_event || !strequal(ui->cur_event, name)) {
+ char **buf = &ui->packer.ptr;
+ if (!ui->nevents_pos) {
+ // [2, "redraw", [...]]
+ mpack_array(buf, 3);
+ mpack_uint(buf, 2);
+ mpack_str_small(buf, S_LEN("redraw"));
+ ui->nevents_pos = mpack_array_dyn16(buf);
+ assert(ui->cur_event == NULL);
+ }
+ flush_event(ui);
+ ui->cur_event = name;
+ ui->ncalls_pos = mpack_array_dyn16(buf);
+ mpack_str_small(buf, name, strlen(name));
+ ui->nevents++;
+ ui->ncalls = 1;
+ } else {
+ ui->ncalls++;
}
-
- return false;
}
-/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush().
-static void push_call(UI *ui, const char *name, Array args)
+/// Pushes data into RemoteUI, to be consumed later by remote_ui_flush().
+static void push_call(RemoteUI *ui, const char *name, Array args)
{
- UIData *data = ui->data;
- bool pending = data->nevents_pos;
- char *buf_pos_save = data->buf_wptr;
-
- bool new_event = prepare_call(ui, name);
-
- msgpack_packer pac;
- data->pack_totlen = 0;
- data->buf_overflow = false;
- msgpack_packer_init(&pac, data, write_cb);
- msgpack_rpc_from_array(args, &pac);
- if (data->buf_overflow) {
- data->buf_wptr = buf_pos_save;
- if (new_event) {
- data->cur_event = NULL;
- data->nevents--;
- }
- if (pending) {
- remote_ui_flush_buf(ui);
- }
+ prepare_call(ui, name);
+ mpack_object_array(args, &ui->packer);
+}
- if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) {
- // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set)
- data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen);
- data->buf_wptr = data->temp_buf;
- char **buf = &data->buf_wptr;
- mpack_array(buf, 3);
- mpack_uint(buf, 2);
- mpack_str(buf, "redraw");
- mpack_array(buf, 1);
- mpack_array(buf, 2);
- mpack_str(buf, name);
- } else {
- prepare_call(ui, name);
- }
- data->pack_totlen = 0;
- data->buf_overflow = false;
- msgpack_rpc_from_array(args, &pac);
-
- if (data->temp_buf) {
- size_t size = (size_t)(data->buf_wptr - data->temp_buf);
- WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree);
- rpc_write_raw(data->channel_id, buf);
- data->temp_buf = NULL;
- data->buf_wptr = data->buf;
- data->nevents_pos = NULL;
- }
- }
- data->ncalls++;
+static void ui_flush_callback(PackerBuffer *packer)
+{
+ RemoteUI *ui = packer->anydata;
+ ui_flush_buf(ui);
+ ui_alloc_buf(ui);
}
-void remote_ui_grid_clear(UI *ui, Integer grid)
+void remote_ui_grid_clear(RemoteUI *ui, Integer grid)
{
- UIData *data = ui->data;
- Array args = data->call_buf;
+ Array args = ui->call_buf;
if (ui->ui_ext[kUILinegrid]) {
ADD_C(args, INTEGER_OBJ(grid));
}
@@ -666,14 +590,13 @@ void remote_ui_grid_clear(UI *ui, Integer grid)
push_call(ui, name, args);
}
-void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height)
+void remote_ui_grid_resize(RemoteUI *ui, Integer grid, Integer width, Integer height)
{
- UIData *data = ui->data;
- Array args = data->call_buf;
+ Array args = ui->call_buf;
if (ui->ui_ext[kUILinegrid]) {
ADD_C(args, INTEGER_OBJ(grid));
} else {
- data->client_col = -1; // force cursor update
+ ui->client_col = -1; // force cursor update
}
ADD_C(args, INTEGER_OBJ(width));
ADD_C(args, INTEGER_OBJ(height));
@@ -681,12 +604,11 @@ void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height)
push_call(ui, name, args);
}
-void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left,
+void remote_ui_grid_scroll(RemoteUI *ui, Integer grid, Integer top, Integer bot, Integer left,
Integer right, Integer rows, Integer cols)
{
- UIData *data = ui->data;
if (ui->ui_ext[kUILinegrid]) {
- Array args = data->call_buf;
+ Array args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(grid));
ADD_C(args, INTEGER_OBJ(top));
ADD_C(args, INTEGER_OBJ(bot));
@@ -696,20 +618,20 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ
ADD_C(args, INTEGER_OBJ(cols));
push_call(ui, "grid_scroll", args);
} else {
- Array args = data->call_buf;
+ Array args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(top));
ADD_C(args, INTEGER_OBJ(bot - 1));
ADD_C(args, INTEGER_OBJ(left));
ADD_C(args, INTEGER_OBJ(right - 1));
push_call(ui, "set_scroll_region", args);
- args = data->call_buf;
+ args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(rows));
push_call(ui, "scroll", args);
// some clients have "clear" being affected by scroll region,
// so reset it.
- args = data->call_buf;
+ args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(0));
ADD_C(args, INTEGER_OBJ(ui->height - 1));
ADD_C(args, INTEGER_OBJ(0));
@@ -718,14 +640,13 @@ void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integ
}
}
-void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
+void remote_ui_default_colors_set(RemoteUI *ui, Integer rgb_fg, Integer rgb_bg, Integer rgb_sp,
Integer cterm_fg, Integer cterm_bg)
{
if (!ui->ui_ext[kUITermColors]) {
HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp);
}
- UIData *data = ui->data;
- Array args = data->call_buf;
+ Array args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(rgb_fg));
ADD_C(args, INTEGER_OBJ(rgb_bg));
ADD_C(args, INTEGER_OBJ(rgb_sp));
@@ -735,34 +656,41 @@ void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, Intege
// Deprecated
if (!ui->ui_ext[kUILinegrid]) {
- args = data->call_buf;
+ args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1));
push_call(ui, "update_fg", args);
- args = data->call_buf;
+ args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1));
push_call(ui, "update_bg", args);
- args = data->call_buf;
+ args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1));
push_call(ui, "update_sp", args);
}
}
-void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
+void remote_ui_hl_attr_define(RemoteUI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
Array info)
{
if (!ui->ui_ext[kUILinegrid]) {
return;
}
- UIData *data = ui->data;
- Array args = data->call_buf;
+ Array args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(id));
MAXSIZE_TEMP_DICT(rgb, HLATTRS_DICT_SIZE);
MAXSIZE_TEMP_DICT(cterm, HLATTRS_DICT_SIZE);
hlattrs2dict(&rgb, NULL, rgb_attrs, true, false);
hlattrs2dict(&cterm, NULL, rgb_attrs, false, false);
+
+ // URLs are not added in hlattrs2dict since they are used only by UIs and not by the highlight
+ // system. So we add them here.
+ if (rgb_attrs.url >= 0) {
+ const char *url = hl_get_url((uint32_t)rgb_attrs.url);
+ PUT_C(rgb, "url", CSTR_AS_OBJ(url));
+ }
+
ADD_C(args, DICTIONARY_OBJ(rgb));
ADD_C(args, DICTIONARY_OBJ(cterm));
@@ -775,15 +703,14 @@ void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cte
push_call(ui, "hl_attr_define", args);
}
-void remote_ui_highlight_set(UI *ui, int id)
+void remote_ui_highlight_set(RemoteUI *ui, int id)
{
- UIData *data = ui->data;
- Array args = data->call_buf;
+ Array args = ui->call_buf;
- if (data->hl_id == id) {
+ if (ui->hl_id == id) {
return;
}
- data->hl_id = id;
+ ui->hl_id = id;
MAXSIZE_TEMP_DICT(dict, HLATTRS_DICT_SIZE);
hlattrs2dict(&dict, NULL, syn_attr2entry(id), ui->rgb, false);
ADD_C(args, DICTIONARY_OBJ(dict));
@@ -791,57 +718,55 @@ void remote_ui_highlight_set(UI *ui, int id)
}
/// "true" cursor used only for input focus
-void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col)
+void remote_ui_grid_cursor_goto(RemoteUI *ui, Integer grid, Integer row, Integer col)
{
if (ui->ui_ext[kUILinegrid]) {
- UIData *data = ui->data;
- Array args = data->call_buf;
+ Array args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(grid));
ADD_C(args, INTEGER_OBJ(row));
ADD_C(args, INTEGER_OBJ(col));
push_call(ui, "grid_cursor_goto", args);
} else {
- UIData *data = ui->data;
- data->cursor_row = row;
- data->cursor_col = col;
+ ui->cursor_row = row;
+ ui->cursor_col = col;
remote_ui_cursor_goto(ui, row, col);
}
}
/// emulated cursor used both for drawing and for input focus
-void remote_ui_cursor_goto(UI *ui, Integer row, Integer col)
+void remote_ui_cursor_goto(RemoteUI *ui, Integer row, Integer col)
{
- UIData *data = ui->data;
- if (data->client_row == row && data->client_col == col) {
+ if (ui->client_row == row && ui->client_col == col) {
return;
}
- data->client_row = row;
- data->client_col = col;
- Array args = data->call_buf;
+ ui->client_row = row;
+ ui->client_col = col;
+ Array args = ui->call_buf;
ADD_C(args, INTEGER_OBJ(row));
ADD_C(args, INTEGER_OBJ(col));
push_call(ui, "cursor_goto", args);
}
-void remote_ui_put(UI *ui, const char *cell)
+void remote_ui_put(RemoteUI *ui, const char *cell)
{
- UIData *data = ui->data;
- data->client_col++;
- Array args = data->call_buf;
- ADD_C(args, CSTR_AS_OBJ((char *)cell));
+ ui->client_col++;
+ Array args = ui->call_buf;
+ ADD_C(args, CSTR_AS_OBJ(cell));
push_call(ui, "put", args);
}
-void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol,
+void remote_ui_raw_line(RemoteUI *ui, Integer grid, Integer row, Integer startcol, Integer endcol,
Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk,
const sattr_T *attrs)
{
- UIData *data = ui->data;
+ // If MAX_SCHAR_SIZE is made larger, we need to refactor implementation below
+ // to not only use FIXSTR (only up to 0x20 bytes)
+ STATIC_ASSERT(MAX_SCHAR_SIZE - 1 < 0x20, "SCHAR doesn't fit in fixstr");
+
if (ui->ui_ext[kUILinegrid]) {
prepare_call(ui, "grid_line");
- data->ncalls++;
- char **buf = &data->buf_wptr;
+ char **buf = &ui->packer.ptr;
mpack_array(buf, 5);
mpack_uint(buf, (uint32_t)grid);
mpack_uint(buf, (uint32_t)row);
@@ -856,7 +781,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
for (size_t i = 0; i < ncells; i++) {
repeat++;
if (i == ncells - 1 || attrs[i] != attrs[i + 1] || chunk[i] != chunk[i + 1]) {
- if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) {
+ if (UI_BUF_SIZE - BUF_POS(ui) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5) + 1) {
// close to overflowing the redraw buffer. finish this event,
// flush, and start a new "grid_line" event at the current position.
// For simplicity leave place for the final "clear" element
@@ -865,10 +790,9 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
// We only ever set the wrap field on the final "grid_line" event for the line.
mpack_bool(buf, false);
- remote_ui_flush_buf(ui);
+ ui_flush_buf(ui);
prepare_call(ui, "grid_line");
- data->ncalls++;
mpack_array(buf, 5);
mpack_uint(buf, (uint32_t)grid);
mpack_uint(buf, (uint32_t)row);
@@ -880,16 +804,16 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1);
nelem++;
mpack_array(buf, csize);
- char sc_buf[MAX_SCHAR_SIZE];
- schar_get(sc_buf, chunk[i]);
- mpack_str(buf, sc_buf);
+ char *size_byte = (*buf)++;
+ size_t len = schar_get_adv(buf, chunk[i]);
+ *size_byte = (char)(0xa0 | len);
if (csize >= 2) {
mpack_uint(buf, (uint32_t)attrs[i]);
if (csize >= 3) {
mpack_uint(buf, repeat);
}
}
- data->ncells_pending += MIN(repeat, 2);
+ ui->ncells_pending += MIN(repeat, 2);
last_hl = attrs[i];
repeat = 0;
was_space = chunk[i] == schar_from_ascii(' ');
@@ -899,18 +823,18 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
// no more cells to clear, so there is no ambiguity about what to clear.
if (endcol < clearcol || was_space) {
nelem++;
- data->ncells_pending += 1;
+ ui->ncells_pending += 1;
mpack_array(buf, 3);
- mpack_str(buf, " ");
+ mpack_str_small(buf, S_LEN(" "));
mpack_uint(buf, (uint32_t)clearattr);
mpack_uint(buf, (uint32_t)(clearcol - endcol));
}
mpack_w2(&lenpos, nelem);
mpack_bool(buf, flags & kLineFlagWrap);
- if (data->ncells_pending > 500) {
+ if (ui->ncells_pending > 500) {
// pass off cells to UI to let it start processing them
- remote_ui_flush_buf(ui);
+ ui_flush_buf(ui);
}
} else {
for (int i = 0; i < endcol - startcol; i++) {
@@ -920,7 +844,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
schar_get(sc_buf, chunk[i]);
remote_ui_put(ui, sc_buf);
if (utf_ambiguous_width(utf_ptr2char(sc_buf))) {
- data->client_col = -1; // force cursor update
+ ui->client_col = -1; // force cursor update
}
}
if (endcol < clearcol) {
@@ -944,49 +868,47 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
///
/// This might happen multiple times before the actual ui_flush, if the
/// total redraw size is large!
-void remote_ui_flush_buf(UI *ui)
+static void ui_flush_buf(RemoteUI *ui)
{
- UIData *data = ui->data;
- if (!data->nevents_pos) {
+ if (!ui->packer.startptr || !BUF_POS(ui)) {
return;
}
- if (data->cur_event) {
- flush_event(data);
+
+ flush_event(ui);
+ if (ui->nevents_pos != NULL) {
+ mpack_w2(&ui->nevents_pos, ui->nevents);
+ ui->nevents = 0;
+ ui->nevents_pos = NULL;
}
- mpack_w2(&data->nevents_pos, data->nevents);
- data->nevents = 0;
- data->nevents_pos = NULL;
-
- // TODO(bfredl): elide copy by a length one free-list like the arena
- size_t size = BUF_POS(data);
- WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree);
- rpc_write_raw(data->channel_id, buf);
- data->buf_wptr = data->buf;
- // we have sent events to the client, but possibly not yet the final "flush"
- // event.
- data->flushed_events = true;
-
- data->ncells_pending = 0;
+
+ WBuffer *buf = wstream_new_buffer(ui->packer.startptr, BUF_POS(ui), 1, free_block);
+ rpc_write_raw(ui->channel_id, buf);
+
+ ui->packer.startptr = NULL;
+ ui->packer.ptr = NULL;
+
+ // we have sent events to the client, but possibly not yet the final "flush" event.
+ ui->flushed_events = true;
+ ui->ncells_pending = 0;
}
/// An intentional flush (vsync) when Nvim is finished redrawing the screen
///
/// Clients can know this happened by a final "flush" event at the end of the
/// "redraw" batch.
-void remote_ui_flush(UI *ui)
+void remote_ui_flush(RemoteUI *ui)
{
- UIData *data = ui->data;
- if (data->nevents > 0 || data->flushed_events) {
+ if (ui->nevents > 0 || ui->flushed_events) {
if (!ui->ui_ext[kUILinegrid]) {
- remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col);
+ remote_ui_cursor_goto(ui, ui->cursor_row, ui->cursor_col);
}
push_call(ui, "flush", (Array)ARRAY_DICT_INIT);
- remote_ui_flush_buf(ui);
- data->flushed_events = false;
+ ui_flush_buf(ui);
+ ui->flushed_events = false;
}
}
-static Array translate_contents(UI *ui, Array contents, Arena *arena)
+static Array translate_contents(RemoteUI *ui, Array contents, Arena *arena)
{
Array new_contents = arena_array(arena, contents.size);
for (size_t i = 0; i < contents.size; i++) {
@@ -996,32 +918,31 @@ 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;
}
-static Array translate_firstarg(UI *ui, Array args, Arena *arena)
+static Array translate_firstarg(RemoteUI *ui, Array args, Arena *arena)
{
Array new_args = arena_array(arena, args.size);
Array contents = args.items[0].data.array;
ADD_C(new_args, ARRAY_OBJ(translate_contents(ui, contents, arena)));
for (size_t i = 1; i < args.size; i++) {
- ADD(new_args, args.items[i]);
+ ADD_C(new_args, args.items[i]);
}
return new_args;
}
-void remote_ui_event(UI *ui, char *name, Array args)
+void remote_ui_event(RemoteUI *ui, char *name, Array args)
{
Arena arena = ARENA_EMPTY;
- UIData *data = ui->data;
if (!ui->ui_ext[kUILinegrid]) {
// the representation of highlights in cmdline changed, translate back
// never consumes args
@@ -1030,7 +951,7 @@ void remote_ui_event(UI *ui, char *name, Array args)
push_call(ui, name, new_args);
goto free_ret;
} else if (strequal(name, "cmdline_block_show")) {
- Array new_args = data->call_buf;
+ Array new_args = ui->call_buf;
Array block = args.items[0].data.array;
Array new_block = arena_array(&arena, block.size);
for (size_t i = 0; i < block.size; i++) {
@@ -1049,10 +970,10 @@ void remote_ui_event(UI *ui, char *name, Array args)
// Back-compat: translate popupmenu_xx to legacy wildmenu_xx.
if (ui->ui_ext[kUIWildmenu]) {
if (strequal(name, "popupmenu_show")) {
- data->wildmenu_active = (args.items[4].data.integer == -1)
- || !ui->ui_ext[kUIPopupmenu];
- if (data->wildmenu_active) {
- Array new_args = data->call_buf;
+ ui->wildmenu_active = (args.items[4].data.integer == -1)
+ || !ui->ui_ext[kUIPopupmenu];
+ if (ui->wildmenu_active) {
+ Array new_args = ui->call_buf;
Array items = args.items[0].data.array;
Array new_items = arena_array(&arena, items.size);
for (size_t i = 0; i < items.size; i++) {
@@ -1061,18 +982,18 @@ void remote_ui_event(UI *ui, char *name, Array args)
ADD_C(new_args, ARRAY_OBJ(new_items));
push_call(ui, "wildmenu_show", new_args);
if (args.items[1].data.integer != -1) {
- Array new_args2 = data->call_buf;
+ Array new_args2 = ui->call_buf;
ADD_C(new_args2, args.items[1]);
push_call(ui, "wildmenu_select", new_args2);
}
goto free_ret;
}
} else if (strequal(name, "popupmenu_select")) {
- if (data->wildmenu_active) {
+ if (ui->wildmenu_active) {
name = "wildmenu_select";
}
} else if (strequal(name, "popupmenu_hide")) {
- if (data->wildmenu_active) {
+ if (ui->wildmenu_active) {
name = "wildmenu_hide";
}
}
@@ -1084,9 +1005,3 @@ void remote_ui_event(UI *ui, char *name, Array args)
free_ret:
arena_mem_free(arena_finish(&arena));
}
-
-void remote_ui_inspect(UI *ui, Dictionary *info)
-{
- UIData *data = ui->data;
- PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id));
-}
diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h
index 26a91d0dbc..cdccc27ba4 100644
--- a/src/nvim/api/ui.h
+++ b/src/nvim/api/ui.h
@@ -4,11 +4,24 @@
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/highlight_defs.h" // IWYU pragma: keep
-#include "nvim/map_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
-#include "nvim/ui.h"
+#include "nvim/ui_defs.h" // IWYU pragma: keep
+
+/// Keep in sync with UIExtension in ui_defs.h
+EXTERN const char *ui_ext_names[] INIT( = {
+ "ext_cmdline",
+ "ext_popupmenu",
+ "ext_tabline",
+ "ext_wildmenu",
+ "ext_messages",
+ "ext_linegrid",
+ "ext_multigrid",
+ "ext_hlstate",
+ "ext_termcolors",
+ "_debug_float",
+});
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/ui.h.generated.h"
-# include "ui_events_remote.h.generated.h" // IWYU pragma: export
+# include "ui_events_remote.h.generated.h"
#endif
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index bda0c72423..c2f02c34f8 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -39,6 +39,8 @@ void screenshot(String path)
FUNC_API_SINCE(7);
void option_set(String name, Object value)
FUNC_API_SINCE(4);
+void chdir(String path)
+ FUNC_API_SINCE(12);
// Stop event is not exported as such, represented by EOF in the msgpack stream.
void stop(void)
FUNC_API_NOEXPORT;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index d631b10af9..84a2f24dbc 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -21,21 +21,26 @@
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
+#include "nvim/channel_defs.h"
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fold.h"
-#include "nvim/func_attr.h"
#include "nvim/getchar.h"
+#include "nvim/getchar_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
#include "nvim/log.h"
@@ -43,25 +48,33 @@
#include "nvim/macros_defs.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
+#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
+#include "nvim/message_defs.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
+#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/process.h"
#include "nvim/popupmenu.h"
#include "nvim/pos_defs.h"
#include "nvim/runtime.h"
-#include "nvim/sign.h"
+#include "nvim/sign_defs.h"
#include "nvim/state.h"
+#include "nvim/state_defs.h"
#include "nvim/statusline.h"
+#include "nvim/statusline_defs.h"
#include "nvim/strings.h"
#include "nvim/terminal.h"
#include "nvim/types_defs.h"
@@ -110,7 +123,7 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E
///
/// @note Unlike the `:highlight` command which can update a highlight group,
/// this function completely replaces the definition. For example:
-/// ``nvim_set_hl(0, 'Visual', {})`` will clear the highlight group
+/// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group
/// 'Visual'.
///
/// @note The fg and bg keys also accept the string values `"fg"` or `"bg"`
@@ -128,9 +141,9 @@ Dictionary nvim_get_hl(Integer ns_id, Dict(get_highlight) *opts, Arena *arena, E
/// |nvim_set_hl_ns()| or |nvim_win_set_hl_ns()| to activate them.
/// @param name Highlight group name, e.g. "ErrorMsg"
/// @param val Highlight definition map, accepts the following keys:
-/// - fg (or foreground): color name or "#RRGGBB", see note.
-/// - bg (or background): color name or "#RRGGBB", see note.
-/// - sp (or special): color name or "#RRGGBB"
+/// - fg: color name or "#RRGGBB", see note.
+/// - bg: color name or "#RRGGBB", see note.
+/// - sp: color name or "#RRGGBB"
/// - blend: integer between 0 and 100
/// - bold: boolean
/// - standout: boolean
@@ -163,6 +176,12 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err)
});
int link_id = -1;
+ // Setting URLs directly through highlight attributes is not supported
+ if (HAS_KEY(val, highlight, url)) {
+ api_free_string(val->url);
+ val->url = NULL_STRING;
+ }
+
HlAttrs attrs = dict2hlattrs(val, true, &link_id, err);
if (!ERROR_SET(err)) {
ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val);
@@ -230,7 +249,7 @@ void nvim_set_hl_ns_fast(Integer ns_id, Error *err)
///
/// On execution error: does not fail, but updates v:errmsg.
///
-/// To input sequences like <C-o> use |nvim_replace_termcodes()| (typically
+/// To input sequences like [<C-o>] use |nvim_replace_termcodes()| (typically
/// with escape_ks=false) to replace |keycodes|, then pass the result to
/// nvim_feedkeys().
///
@@ -318,11 +337,11 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks)
///
/// On execution error: does not fail, but updates v:errmsg.
///
-/// @note |keycodes| like <CR> are translated, so "<" is special.
-/// To input a literal "<", send <LT>.
+/// @note |keycodes| like [<CR>] are translated, so "<" is special.
+/// To input a literal "<", send [<LT>].
///
/// @note For mouse events use |nvim_input_mouse()|. The pseudokey form
-/// "<LeftMouse><col,row>" is deprecated since |api-level| 6.
+/// `<LeftMouse><col,row>` is deprecated since |api-level| 6.
///
/// @param keys to be typed
/// @return Number of bytes actually written (can be fewer than
@@ -343,9 +362,10 @@ Integer nvim_input(String keys)
/// by calling it multiple times in a loop: the intermediate mouse
/// positions will be ignored. It should be used to implement real-time
/// mouse input in a GUI. The deprecated pseudokey form
-/// ("<LeftMouse><col,row>") of |nvim_input()| has the same limitation.
+/// (`<LeftMouse><col,row>`) of |nvim_input()| has the same limitation.
///
-/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move".
+/// @param button Mouse button: one of "left", "right", "middle", "wheel", "move",
+/// "x1", "x2".
/// @param action For ordinary buttons, one of "press", "drag", "release".
/// For the wheel, one of "up", "down", "left", "right". Ignored for "move".
/// @param modifier String of modifiers each represented by a single char.
@@ -376,6 +396,10 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri
code = KE_RIGHTMOUSE;
} else if (strequal(button.data, "wheel")) {
code = KE_MOUSEDOWN;
+ } else if (strequal(button.data, "x1")) {
+ code = KE_X1MOUSE;
+ } else if (strequal(button.data, "x2")) {
+ code = KE_X2MOUSE;
} else if (strequal(button.data, "move")) {
code = KE_MOUSEMOVE;
} else {
@@ -427,17 +451,17 @@ error:
"invalid button or action");
}
-/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
+/// Replaces terminal codes and |keycodes| ([<CR>], [<Esc>], ...) in a string with
/// the internal representation.
///
/// @param str String to be converted.
/// @param from_part Legacy Vim parameter. Usually true.
-/// @param do_lt Also translate <lt>. Ignored if `special` is false.
-/// @param special Replace |keycodes|, e.g. <CR> becomes a "\r" char.
+/// @param do_lt Also translate [<lt>]. Ignored if `special` is false.
+/// @param special Replace |keycodes|, e.g. [<CR>] becomes a "\r" char.
/// @see replace_termcodes
/// @see cpoptions
String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special)
- FUNC_API_SINCE(1)
+ FUNC_API_SINCE(1) FUNC_API_RET_ALLOC
{
if (str.size == 0) {
// Empty string
@@ -456,7 +480,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
}
char *ptr = NULL;
- replace_termcodes(str.data, str.size, &ptr, 0, flags, NULL, CPO_TO_CPO_FLAGS);
+ replace_termcodes(str.data, str.size, &ptr, 0, flags, NULL, p_cpo);
return cstr_as_string(ptr);
}
@@ -472,11 +496,12 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
/// or executing the Lua code.
///
/// @return Return value of Lua code if present or NIL.
-Object nvim_exec_lua(String code, Array args, Error *err)
+Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err)
FUNC_API_SINCE(7)
FUNC_API_REMOTE_ONLY
{
- return nlua_exec(code, args, err);
+ // TODO(bfredl): convert directly from msgpack to lua and then back again
+ return nlua_exec(code, args, kRetObject, arena, err);
}
/// Notify the user with a message
@@ -488,7 +513,7 @@ Object nvim_exec_lua(String code, Array args, Error *err)
/// @param log_level The log level
/// @param opts Reserved for future use.
/// @param[out] err Error details, if any
-Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
+Object nvim_notify(String msg, Integer log_level, Dictionary opts, Arena *arena, Error *err)
FUNC_API_SINCE(7)
{
MAXSIZE_TEMP_ARRAY(args, 3);
@@ -496,11 +521,11 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
ADD_C(args, INTEGER_OBJ(log_level));
ADD_C(args, DICTIONARY_OBJ(opts));
- return NLUA_EXEC_STATIC("return vim.notify(...)", args, err);
+ return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
}
/// Calculates the number of display cells occupied by `text`.
-/// Control characters including <Tab> count as one cell.
+/// Control characters including [<Tab>] count as one cell.
///
/// @param text Some text
/// @param[out] err Error details, if any
@@ -518,17 +543,23 @@ Integer nvim_strwidth(String text, Error *err)
/// Gets the paths contained in |runtime-search-path|.
///
/// @return List of paths
-ArrayOf(String) nvim_list_runtime_paths(Error *err)
+ArrayOf(String) nvim_list_runtime_paths(Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
- return nvim_get_runtime_file(NULL_STRING, true, err);
+ return nvim_get_runtime_file(NULL_STRING, true, arena, err);
}
-Array nvim__runtime_inspect(void)
+/// @nodoc
+Array nvim__runtime_inspect(Arena *arena)
{
- return runtime_inspect();
+ return runtime_inspect(arena);
}
+typedef struct {
+ ArrayBuilder rv;
+ Arena *arena;
+} RuntimeCookie;
+
/// Find files in runtime directories
///
/// "name" can contain wildcards. For example
@@ -541,25 +572,27 @@ Array nvim__runtime_inspect(void)
/// @param name pattern of files to search for
/// @param all whether to return all matches or only the first
/// @return list of absolute paths to the found files
-ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err)
+ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Arena *arena, Error *err)
FUNC_API_SINCE(7)
FUNC_API_FAST
{
- Array rv = ARRAY_DICT_INIT;
+ RuntimeCookie cookie = { .rv = ARRAY_DICT_INIT, .arena = arena, };
+ kvi_init(cookie.rv);
int flags = DIP_DIRFILE | (all ? DIP_ALL : 0);
TRY_WRAP(err, {
- do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &rv);
+ do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
});
- return rv;
+ return arena_take_arraybuilder(arena, &cookie.rv);
}
-static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *cookie)
+static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *c)
{
- Array *rv = (Array *)cookie;
+ RuntimeCookie *cookie = (RuntimeCookie *)c;
for (int i = 0; i < num_fnames; i++) {
- ADD(*rv, CSTR_TO_OBJ(fnames[i]));
+ // TODO(bfredl): consider memory management of gen_expand_wildcards() itself
+ kvi_push(cookie->rv, CSTR_TO_ARENA_OBJ(cookie->arena, fnames[i]));
if (!all) {
return true;
}
@@ -568,7 +601,9 @@ static bool find_runtime_cb(int num_fnames, char **fnames, bool all, void *cooki
return num_fnames > 0;
}
+/// @nodoc
String nvim__get_lib_dir(void)
+ FUNC_API_RET_ALLOC
{
return cstr_as_string(get_lib_dir());
}
@@ -579,7 +614,8 @@ String nvim__get_lib_dir(void)
/// @param all whether to return all matches or only the first
/// @param opts is_lua: only search Lua subdirs
/// @return list of absolute paths to the found files
-ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err)
+ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Arena *arena,
+ Error *err)
FUNC_API_SINCE(8)
FUNC_API_FAST
{
@@ -589,12 +625,12 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E
return (Array)ARRAY_DICT_INIT;
}
- ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all);
+ ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all, arena);
if (opts->do_source) {
for (size_t i = 0; i < res.size; i++) {
String name = res.items[i].data.string;
- (void)do_source(name.data, false, DOSO_NONE, NULL);
+ do_source(name.data, false, DOSO_NONE, NULL);
}
}
@@ -632,31 +668,31 @@ void nvim_set_current_dir(String dir, Error *err)
///
/// @param[out] err Error details, if any
/// @return Current line string
-String nvim_get_current_line(Error *err)
+String nvim_get_current_line(Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
- return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
+ return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, arena, err);
}
/// Sets the current line.
///
/// @param line Line contents
/// @param[out] err Error details, if any
-void nvim_set_current_line(String line, Error *err)
+void nvim_set_current_line(String line, Arena *arena, Error *err)
FUNC_API_SINCE(1)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
- buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err);
+ buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, arena, err);
}
/// Deletes the current line.
///
/// @param[out] err Error details, if any
-void nvim_del_current_line(Error *err)
+void nvim_del_current_line(Arena *arena, Error *err)
FUNC_API_SINCE(1)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
- buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err);
+ buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, arena, err);
}
/// Gets a global (g:) variable.
@@ -664,7 +700,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);
@@ -678,7 +714,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.
@@ -689,7 +725,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.
@@ -699,7 +735,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.
@@ -707,10 +743,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.
@@ -721,12 +757,12 @@ 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.
///
-/// @param chunks A list of [text, hl_group] arrays, each representing a
+/// @param chunks A list of `[text, hl_group]` arrays, each representing a
/// text chunk with specified highlight. `hl_group` element
/// can be omitted for no highlight.
/// @param history if true, add to |message-history|.
@@ -799,20 +835,19 @@ void nvim_err_writeln(String str)
/// Use |nvim_buf_is_loaded()| to check if a buffer is loaded.
///
/// @return List of buffer handles
-ArrayOf(Buffer) nvim_list_bufs(void)
+ArrayOf(Buffer) nvim_list_bufs(Arena *arena)
FUNC_API_SINCE(1)
{
- Array rv = ARRAY_DICT_INIT;
+ size_t n = 0;
FOR_ALL_BUFFERS(b) {
- rv.size++;
+ n++;
}
- rv.items = xmalloc(sizeof(Object) * rv.size);
- size_t i = 0;
+ Array rv = arena_array(arena, n);
FOR_ALL_BUFFERS(b) {
- rv.items[i++] = BUFFER_OBJ(b->handle);
+ ADD_C(rv, BUFFER_OBJ(b->handle));
}
return rv;
@@ -854,20 +889,19 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
/// Gets the current list of window handles.
///
/// @return List of window handles
-ArrayOf(Window) nvim_list_wins(void)
+ArrayOf(Window) nvim_list_wins(Arena *arena)
FUNC_API_SINCE(1)
{
- Array rv = ARRAY_DICT_INIT;
+ size_t n = 0;
FOR_ALL_TAB_WINDOWS(tp, wp) {
- rv.size++;
+ n++;
}
- rv.items = xmalloc(sizeof(Object) * rv.size);
- size_t i = 0;
+ Array rv = arena_array(arena, n);
FOR_ALL_TAB_WINDOWS(tp, wp) {
- rv.items[i++] = WINDOW_OBJ(wp->handle);
+ ADD_C(rv, WINDOW_OBJ(wp->handle));
}
return rv;
@@ -949,8 +983,8 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
if (scratch) {
- set_string_option_direct_in_buf(buf, "bufhidden", -1, "hide", OPT_LOCAL, 0);
- set_string_option_direct_in_buf(buf, "buftype", -1, "nofile", OPT_LOCAL, 0);
+ set_string_option_direct_in_buf(buf, kOptBufhidden, "hide", OPT_LOCAL, 0);
+ set_string_option_direct_in_buf(buf, kOptBuftype, "nofile", OPT_LOCAL, 0);
assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
buf->b_p_swf = false;
buf->b_p_ml = false;
@@ -985,10 +1019,11 @@ fail:
/// master end. For instance, a carriage return is sent
/// as a "\r", not as a "\n". |textlock| applies. It is possible
/// to call |nvim_chan_send()| directly in the callback however.
-/// ["input", term, bufnr, data]
+/// `["input", term, bufnr, data]`
+/// - force_crlf: (boolean, default true) Convert "\n" to "\r\n".
/// @param[out] err Error details, if any
/// @return Channel id, or 0 on error
-Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
+Integer nvim_open_term(Buffer buffer, Dict(open_term) *opts, Error *err)
FUNC_API_SINCE(7)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
@@ -997,39 +1032,31 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
return 0;
}
- if (cmdwin_type != 0 && buf == curbuf) {
+ if (buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return 0;
}
LuaRef cb = LUA_NOREF;
- for (size_t i = 0; i < opts.size; i++) {
- String k = opts.items[i].key;
- Object *v = &opts.items[i].value;
- if (strequal("on_input", k.data)) {
- VALIDATE_T("on_input", kObjectTypeLuaRef, v->type, {
- return 0;
- });
- cb = v->data.luaref;
- v->data.luaref = LUA_NOREF;
- break;
- } else {
- VALIDATE_S(false, "'opts' key", k.data, {});
- }
+ if (HAS_KEY(opts, open_term, on_input)) {
+ cb = opts->on_input;
+ opts->on_input = LUA_NOREF;
}
- TerminalOptions topts;
Channel *chan = channel_alloc(kChannelStreamInternal);
chan->stream.internal.cb = cb;
chan->stream.internal.closed = false;
- topts.data = chan;
- // NB: overridden in terminal_check_size if a window is already
- // displaying the buffer
- topts.width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0);
- topts.height = (uint16_t)curwin->w_height_inner;
- topts.write_cb = term_write;
- topts.resize_cb = term_resize;
- topts.close_cb = term_close;
+ TerminalOptions topts = {
+ .data = chan,
+ // NB: overridden in terminal_check_size if a window is already
+ // displaying the buffer
+ .width = (uint16_t)MAX(curwin->w_width_inner - win_col_off(curwin), 0),
+ .height = (uint16_t)curwin->w_height_inner,
+ .write_cb = term_write,
+ .resize_cb = term_resize,
+ .close_cb = term_close,
+ .force_crlf = GET_BOOL_OR_TRUE(opts, open_term, force_crlf),
+ };
channel_incref(chan);
terminal_open(&chan->term, buf, topts);
if (chan->term != NULL) {
@@ -1039,7 +1066,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
return (Integer)chan->id;
}
-static void term_write(char *buf, size_t size, void *data) // NOLINT(readability-non-const-parameter)
+static void term_write(const char *buf, size_t size, void *data)
{
Channel *chan = data;
LuaRef cb = chan->stream.internal.cb;
@@ -1049,9 +1076,9 @@ static void term_write(char *buf, size_t size, void *data) // NOLINT(readabilit
MAXSIZE_TEMP_ARRAY(args, 3);
ADD_C(args, INTEGER_OBJ((Integer)chan->id));
ADD_C(args, BUFFER_OBJ(terminal_buf(chan->term)));
- ADD_C(args, STRING_OBJ(((String){ .data = buf, .size = size })));
+ ADD_C(args, STRING_OBJ(((String){ .data = (char *)buf, .size = size })));
textlock++;
- nlua_call_ref(cb, "input", args, false, NULL);
+ nlua_call_ref(cb, "input", args, kRetNilBool, NULL, NULL);
textlock--;
}
@@ -1098,20 +1125,19 @@ void nvim_chan_send(Integer chan, String data, Error *err)
/// Gets the current list of tabpage handles.
///
/// @return List of tabpage handles
-ArrayOf(Tabpage) nvim_list_tabpages(void)
+ArrayOf(Tabpage) nvim_list_tabpages(Arena *arena)
FUNC_API_SINCE(1)
{
- Array rv = ARRAY_DICT_INIT;
+ size_t n = 0;
FOR_ALL_TABS(tp) {
- rv.size++;
+ n++;
}
- rv.items = xmalloc(sizeof(Object) * rv.size);
- size_t i = 0;
+ Array rv = arena_array(arena, n);
FOR_ALL_TABS(tp) {
- rv.items[i++] = TABPAGE_OBJ(tp->handle);
+ ADD_C(rv, TABPAGE_OBJ(tp->handle));
}
return rv;
@@ -1172,7 +1198,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
/// @return
/// - true: Client may continue pasting.
/// - false: Client must cancel the paste.
-Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
+Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error *err)
FUNC_API_SINCE(6)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
@@ -1182,19 +1208,17 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
VALIDATE_INT((phase >= -1 && phase <= 3), "phase", phase, {
return false;
});
- Array args = ARRAY_DICT_INIT;
- Object rv = OBJECT_INIT;
if (phase == -1 || phase == 1) { // Start of paste-stream.
draining = false;
} else if (draining) {
// Skip remaining chunks. Report error only once per "stream".
goto theend;
}
- Array lines = string_to_array(data, crlf);
- ADD(args, ARRAY_OBJ(lines));
- ADD(args, INTEGER_OBJ(phase));
- rv = nvim_exec_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args,
- err);
+ Array lines = string_to_array(data, crlf, arena);
+ MAXSIZE_TEMP_ARRAY(args, 2);
+ ADD_C(args, ARRAY_OBJ(lines));
+ ADD_C(args, INTEGER_OBJ(phase));
+ Object rv = NLUA_EXEC_STATIC("return vim.paste(...)", args, kRetNilBool, arena, err);
if (ERROR_SET(err)) {
draining = true;
goto theend;
@@ -1221,8 +1245,6 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
AppendCharToRedobuff(ESC); // Dot-repeat.
}
theend:
- api_free_object(rv);
- api_free_array(args);
if (cancel || phase == -1 || phase == 3) { // End of paste-stream.
draining = false;
}
@@ -1243,24 +1265,27 @@ theend:
/// @param after If true insert after cursor (like |p|), or before (like |P|).
/// @param follow If true place cursor at end of inserted text.
/// @param[out] err Error details, if any
-void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Error *err)
+void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow, Arena *arena,
+ Error *err)
FUNC_API_SINCE(6)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
- yankreg_T *reg = xcalloc(1, sizeof(yankreg_T));
+ yankreg_T reg[1] = { 0 };
VALIDATE_S((prepare_yankreg_from_object(reg, type, lines.size)), "type", type.data, {
- goto cleanup;
+ return;
});
if (lines.size == 0) {
- goto cleanup; // Nothing to do.
+ return; // Nothing to do.
}
+ reg->y_array = arena_alloc(arena, lines.size * sizeof(uint8_t *), true);
+ reg->y_size = lines.size;
for (size_t i = 0; i < lines.size; i++) {
VALIDATE_T("line", kObjectTypeString, lines.items[i].type, {
- goto cleanup;
+ return;
});
String line = lines.items[i].data.string;
- reg->y_array[i] = xmemdupz(line.data, line.size);
+ reg->y_array[i] = arena_memdupz(arena, line.data, line.size);
memchrsub(reg->y_array[i], NUL, NL, line.size);
}
@@ -1273,10 +1298,6 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow,
msg_silent--;
VIsual_active = VIsual_was_active;
});
-
-cleanup:
- free_register(reg);
- xfree(reg);
}
/// Subscribes to event broadcasts.
@@ -1334,14 +1355,13 @@ Integer nvim_get_color_by_name(String name)
/// (e.g. 65535).
///
/// @return Map of color names and RGB values.
-Dictionary nvim_get_color_map(void)
+Dictionary nvim_get_color_map(Arena *arena)
FUNC_API_SINCE(1)
{
- Dictionary colors = ARRAY_DICT_INIT;
+ Dictionary colors = arena_dict(arena, ARRAY_SIZE(color_name_table));
for (int i = 0; color_name_table[i].name != NULL; i++) {
- PUT(colors, color_name_table[i].name,
- INTEGER_OBJ(color_name_table[i].color));
+ PUT_C(colors, color_name_table[i].name, INTEGER_OBJ(color_name_table[i].color));
}
return colors;
}
@@ -1354,7 +1374,7 @@ Dictionary nvim_get_color_map(void)
/// @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;
@@ -1390,7 +1410,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;
}
@@ -1421,16 +1441,16 @@ Object nvim_load_context(Dictionary dict, Error *err)
/// "blocking" is true if Nvim is waiting for input.
///
/// @returns Dictionary { "mode": String, "blocking": Boolean }
-Dictionary nvim_get_mode(void)
+Dictionary nvim_get_mode(Arena *arena)
FUNC_API_SINCE(2) FUNC_API_FAST
{
- Dictionary rv = ARRAY_DICT_INIT;
- char modestr[MODE_MAX_LENGTH];
+ Dictionary rv = arena_dict(arena, 2);
+ char *modestr = arena_alloc(arena, MODE_MAX_LENGTH, false);
get_mode(modestr);
bool blocked = input_blocking();
- PUT(rv, "mode", CSTR_TO_OBJ(modestr));
- PUT(rv, "blocking", BOOLEAN_OBJ(blocked));
+ PUT_C(rv, "mode", CSTR_AS_OBJ(modestr));
+ PUT_C(rv, "blocking", BOOLEAN_OBJ(blocked));
return rv;
}
@@ -1440,10 +1460,10 @@ Dictionary nvim_get_mode(void)
/// @param mode Mode short-name ("n", "i", "v", ...)
/// @returns Array of |maparg()|-like dictionaries describing mappings.
/// The "buffer" key is always zero.
-ArrayOf(Dictionary) nvim_get_keymap(String mode)
+ArrayOf(Dictionary) nvim_get_keymap(String mode, Arena *arena)
FUNC_API_SINCE(3)
{
- return keymap_array(mode, NULL);
+ return keymap_array(mode, NULL, arena);
}
/// Sets a global |mapping| for the given mode.
@@ -1451,7 +1471,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// To set a buffer-local mapping, use |nvim_buf_set_keymap()|.
///
/// Unlike |:map|, leading/trailing whitespace is accepted as part of the {lhs} or {rhs}.
-/// Empty {rhs} is |<Nop>|. |keycodes| are replaced as usual.
+/// Empty {rhs} is [<Nop>]. |keycodes| are replaced as usual.
///
/// Example:
///
@@ -1471,7 +1491,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// "ia", "ca" or "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively
/// @param lhs Left-hand-side |{lhs}| of the mapping.
/// @param rhs Right-hand-side |{rhs}| of the mapping.
-/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except |<buffer>|,
+/// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except [<buffer>],
/// values are booleans (default false). Also:
/// - "noremap" disables |recursive_mapping|, like |:noremap|
/// - "desc" human-readable description.
@@ -1501,7 +1521,7 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err)
/// Returns a 2-tuple (Array), where item 0 is the current channel id and item
/// 1 is the |api-metadata| map (Dictionary).
///
-/// @returns 2-tuple [{channel-id}, {api-metadata}]
+/// @returns 2-tuple `[{channel-id}, {api-metadata}]`
Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY
{
@@ -1509,7 +1529,7 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
assert(channel_id <= INT64_MAX);
ADD_C(rv, INTEGER_OBJ((int64_t)channel_id));
- ADD_C(rv, DICTIONARY_OBJ(api_metadata()));
+ ADD_C(rv, api_metadata());
return rv;
}
@@ -1529,14 +1549,14 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
/// @param channel_id
/// @param name Short name for the connected client
/// @param version Dictionary describing the version, with these
-/// (optional) keys:
+/// (optional) keys:
/// - "major" major version (defaults to 0 if not set, for no release yet)
/// - "minor" minor version
/// - "patch" patch number
/// - "prerelease" string describing a prerelease, like "dev" or "beta1"
/// - "commit" hash or similar identifier of commit
/// @param type Must be one of the following values. Client libraries should
-/// default to "remote" unless overridden by the user.
+/// default to "remote" unless overridden by the user.
/// - "remote" remote client connected "Nvim flavored" MessagePack-RPC (responses
/// must be in reverse order of requests). |msgpack-rpc|
/// - "msgpack-rpc" remote client connected to Nvim via fully MessagePack-RPC
@@ -1547,12 +1567,12 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
/// - "host" plugin host, typically started by nvim
/// - "plugin" single plugin, started by nvim
/// @param methods Builtin methods in the client. For a host, this does not
-/// include plugin methods which will be discovered later.
-/// The key should be the method name, the values are dicts with
-/// these (optional) keys (more keys may be added in future
-/// versions of Nvim, thus unknown keys are ignored. Clients
-/// must only use keys defined in this or later versions of
-/// Nvim):
+/// include plugin methods which will be discovered later.
+/// The key should be the method name, the values are dicts with
+/// these (optional) keys (more keys may be added in future
+/// versions of Nvim, thus unknown keys are ignored. Clients
+/// must only use keys defined in this or later versions of
+/// Nvim):
/// - "async" if true, send as a notification. If false or unspecified,
/// use a blocking request
/// - "nargs" Number of arguments. Could be a single integer or an array
@@ -1567,13 +1587,12 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
///
/// @param[out] err Error details, if any
void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version, String type,
- Dictionary methods, Dictionary attributes, Error *err)
+ Dictionary methods, Dictionary attributes, Arena *arena, Error *err)
FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
{
- Dictionary info = ARRAY_DICT_INIT;
- PUT(info, "name", copy_object(STRING_OBJ(name), NULL));
+ MAXSIZE_TEMP_DICT(info, 5);
+ PUT_C(info, "name", STRING_OBJ(name));
- version = copy_dictionary(version, NULL);
bool has_major = false;
for (size_t i = 0; i < version.size; i++) {
if (strequal(version.items[i].key.data, "major")) {
@@ -1582,19 +1601,26 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
}
}
if (!has_major) {
- PUT(version, "major", INTEGER_OBJ(0));
+ Dictionary v = arena_dict(arena, version.size + 1);
+ if (version.size) {
+ memcpy(v.items, version.items, version.size * sizeof(v.items[0]));
+ v.size = version.size;
+ }
+ PUT_C(v, "major", INTEGER_OBJ(0));
+ version = v;
}
- PUT(info, "version", DICTIONARY_OBJ(version));
+ PUT_C(info, "version", DICTIONARY_OBJ(version));
- PUT(info, "type", copy_object(STRING_OBJ(type), NULL));
- PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods, NULL)));
- PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes, NULL)));
+ PUT_C(info, "type", STRING_OBJ(type));
+ PUT_C(info, "methods", DICTIONARY_OBJ(methods));
+ PUT_C(info, "attributes", DICTIONARY_OBJ(attributes));
- rpc_set_client_info(channel_id, info);
+ rpc_set_client_info(channel_id, copy_dictionary(info, NULL));
}
/// Gets information about a channel.
///
+/// @param chan channel_id, or 0 for current channel
/// @returns Dictionary describing a channel, with these keys:
/// - "id" Channel id.
/// - "argv" (optional) Job arguments list.
@@ -1616,23 +1642,28 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
/// the RPC channel), if provided by it via
/// |nvim_set_client_info()|.
///
-Dictionary nvim_get_chan_info(Integer chan, Error *err)
+Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err)
FUNC_API_SINCE(4)
{
if (chan < 0) {
return (Dictionary)ARRAY_DICT_INIT;
}
- return channel_info((uint64_t)chan);
+
+ if (chan == 0 && !is_internal_call(channel_id)) {
+ assert(channel_id <= INT64_MAX);
+ chan = (Integer)channel_id;
+ }
+ return channel_info((uint64_t)chan, arena);
}
/// Get information about all open channels.
///
/// @returns Array of Dictionaries, each describing a channel with
/// the format specified at |nvim_get_chan_info()|.
-Array nvim_list_chans(void)
+Array nvim_list_chans(Arena *arena)
FUNC_API_SINCE(4)
{
- return channel_all_info();
+ return channel_all_info(arena);
}
/// Calls many API methods atomically.
@@ -1698,7 +1729,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er
// directly here. But `result` might become invalid when next api function
// is called in the loop.
ADD_C(results, copy_object(result, arena));
- if (!handler.arena_return) {
+ if (handler.ret_alloc) {
api_free_object(result);
}
}
@@ -1778,9 +1809,9 @@ static void write_msg(String message, bool to_err, bool writeln)
/// @param[in] obj Object to return.
///
/// @return its argument.
-Object nvim__id(Object obj)
+Object nvim__id(Object obj, Arena *arena)
{
- return copy_object(obj, NULL);
+ return copy_object(obj, arena);
}
/// Returns array given as argument.
@@ -1791,9 +1822,9 @@ Object nvim__id(Object obj)
/// @param[in] arr Array to return.
///
/// @return its argument.
-Array nvim__id_array(Array arr)
+Array nvim__id_array(Array arr, Arena *arena)
{
- return copy_array(arr, NULL);
+ return copy_array(arr, arena);
}
/// Returns dictionary given as argument.
@@ -1804,9 +1835,9 @@ Array nvim__id_array(Array arr)
/// @param[in] dct Dictionary to return.
///
/// @return its argument.
-Dictionary nvim__id_dictionary(Dictionary dct)
+Dictionary nvim__id_dictionary(Dictionary dct, Arena *arena)
{
- return copy_dictionary(dct, NULL);
+ return copy_dictionary(dct, arena);
}
/// Returns floating-point value given as argument.
@@ -1825,14 +1856,14 @@ Float nvim__id_float(Float flt)
/// Gets internal stats.
///
/// @return Map of various internal stats.
-Dictionary nvim__stats(void)
-{
- Dictionary rv = ARRAY_DICT_INIT;
- PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync));
- PUT(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip));
- PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count()));
- PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
- PUT(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count));
+Dictionary nvim__stats(Arena *arena)
+{
+ Dictionary rv = arena_dict(arena, 5);
+ PUT_C(rv, "fsync", INTEGER_OBJ(g_stats.fsync));
+ PUT_C(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip));
+ PUT_C(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count()));
+ PUT_C(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
+ PUT_C(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count));
return rv;
}
@@ -1844,16 +1875,16 @@ Dictionary nvim__stats(void)
/// - "rgb" true if the UI uses RGB colors (false implies |cterm-colors|)
/// - "ext_..." Requested UI extensions, see |ui-option|
/// - "chan" |channel-id| of remote UI
-Array nvim_list_uis(void)
+Array nvim_list_uis(Arena *arena)
FUNC_API_SINCE(4)
{
- return ui_array();
+ return ui_array(arena);
}
/// Gets the immediate children of process `pid`.
///
/// @return Array of child process ids, empty if process not found.
-Array nvim_get_proc_children(Integer pid, Error *err)
+Array nvim_get_proc_children(Integer pid, Arena *arena, Error *err)
FUNC_API_SINCE(4)
{
Array rvobj = ARRAY_DICT_INIT;
@@ -1869,8 +1900,8 @@ Array nvim_get_proc_children(Integer pid, Error *err)
// syscall failed (possibly because of kernel options), try shelling out.
DLOG("fallback to vim._os_proc_children()");
MAXSIZE_TEMP_ARRAY(a, 1);
- ADD(a, INTEGER_OBJ(pid));
- Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, err);
+ ADD_C(a, INTEGER_OBJ(pid));
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, kRetObject, arena, err);
if (o.type == kObjectTypeArray) {
rvobj = o.data.array;
} else if (!ERROR_SET(err)) {
@@ -1878,11 +1909,11 @@ Array nvim_get_proc_children(Integer pid, Error *err)
"Failed to get process children. pid=%" PRId64 " error=%d",
pid, rv);
}
- goto end;
- }
-
- for (size_t i = 0; i < proc_count; i++) {
- ADD(rvobj, INTEGER_OBJ(proc_list[i]));
+ } else {
+ rvobj = arena_array(arena, proc_count);
+ for (size_t i = 0; i < proc_count; i++) {
+ ADD_C(rvobj, INTEGER_OBJ(proc_list[i]));
+ }
}
end:
@@ -1893,19 +1924,17 @@ end:
/// Gets info describing process `pid`.
///
/// @return Map of process properties, or NIL if process not found.
-Object nvim_get_proc(Integer pid, Error *err)
+Object nvim_get_proc(Integer pid, Arena *arena, Error *err)
FUNC_API_SINCE(4)
{
- Object rvobj = OBJECT_INIT;
- rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT;
- rvobj.type = kObjectTypeDictionary;
+ Object rvobj = NIL;
VALIDATE_INT((pid > 0 && pid <= INT_MAX), "pid", pid, {
return NIL;
});
#ifdef MSWIN
- rvobj.data.dictionary = os_proc_info((int)pid);
+ rvobj = DICTIONARY_OBJ(os_proc_info((int)pid, arena));
if (rvobj.data.dictionary.size == 0) { // Process not found.
return NIL;
}
@@ -1913,11 +1942,11 @@ Object nvim_get_proc(Integer pid, Error *err)
// Cross-platform process info APIs are miserable, so use `ps` instead.
MAXSIZE_TEMP_ARRAY(a, 1);
ADD(a, INTEGER_OBJ(pid));
- Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, err);
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, kRetObject, arena, err);
if (o.type == kObjectTypeArray && o.data.array.size == 0) {
return NIL; // Process not found.
} else if (o.type == kObjectTypeDictionary) {
- rvobj.data.dictionary = o.data.dictionary;
+ rvobj = o;
} else if (!ERROR_SET(err)) {
api_set_error(err, kErrorTypeException,
"Failed to get process info. pid=%" PRId64, pid);
@@ -1931,7 +1960,7 @@ Object nvim_get_proc(Integer pid, Error *err)
/// If neither |ins-completion| nor |cmdline-completion| popup menu is active
/// this API call is silently ignored.
/// Useful for an external UI using |ui-popupmenu| to control the popup menu with the mouse.
-/// Can also be used in a mapping; use <Cmd> |:map-cmd| or a Lua mapping to ensure the mapping
+/// Can also be used in a mapping; use [<Cmd>] |:map-cmd| or a Lua mapping to ensure the mapping
/// doesn't end completion mode.
///
/// @param item Index (zero-based) of the item to select. Value of -1 selects nothing
@@ -1941,14 +1970,10 @@ Object nvim_get_proc(Integer pid, Error *err)
/// @param finish Finish the completion and dismiss the popup menu. Implies {insert}.
/// @param opts Optional parameters. Reserved for future use.
/// @param[out] err Error details, if any
-void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dictionary opts,
+void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Dict(empty) *opts,
Error *err)
FUNC_API_SINCE(6)
{
- VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", {
- return;
- });
-
if (finish) {
insert = true;
}
@@ -1956,7 +1981,7 @@ void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Di
pum_ext_select_item((int)item, insert, finish);
}
-/// NB: if your UI doesn't use hlstate, this will not return hlstate first time
+/// NB: if your UI doesn't use hlstate, this will not return hlstate first time.
Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, Error *err)
{
Array ret = ARRAY_DICT_INIT;
@@ -1987,11 +2012,12 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E
ADD_C(ret, DICTIONARY_OBJ(hl_get_attr_by_id(attr, true, arena, err)));
// will not work first time
if (!highlight_use_hlstate()) {
- ADD_C(ret, ARRAY_OBJ(hl_inspect(attr)));
+ ADD_C(ret, ARRAY_OBJ(hl_inspect(attr, arena)));
}
return ret;
}
+/// @nodoc
void nvim__screenshot(String path)
FUNC_API_FAST
{
@@ -2006,10 +2032,11 @@ void nvim__invalidate_glyph_cache(void)
must_redraw = UPD_CLEAR;
}
-Object nvim__unpack(String str, Error *err)
+/// @nodoc
+Object nvim__unpack(String str, Arena *arena, Error *err)
FUNC_API_FAST
{
- return unpack(str.data, str.size, err);
+ return unpack(str.data, str.size, arena, err);
}
/// Deletes an uppercase/file named mark. See |mark-motions|.
@@ -2049,7 +2076,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, Dictionary 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;
@@ -2097,10 +2124,11 @@ Array nvim_get_mark(String name, Dictionary 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);
@@ -2132,15 +2160,14 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// |Dictionary| with these keys:
/// - start: (number) Byte index (0-based) of first character that uses the highlight.
/// - group: (string) Name of highlight group.
-Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *err)
+Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(8) FUNC_API_FAST
{
Dictionary result = ARRAY_DICT_INIT;
int maxwidth;
- int fillchar = 0;
+ schar_T fillchar = 0;
int statuscol_lnum = 0;
- Window window = 0;
if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) {
const char *const errmsg = check_stl_option(str.data);
@@ -2149,16 +2176,17 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
});
}
- if (HAS_KEY(opts, eval_statusline, winid)) {
- window = opts->winid;
- }
+ Window window = opts->winid;
+
if (HAS_KEY(opts, eval_statusline, fillchar)) {
VALIDATE_EXP((*opts->fillchar.data != 0
- && ((size_t)utf_ptr2len(opts->fillchar.data) == opts->fillchar.size)),
+ && ((size_t)utfc_ptr2len(opts->fillchar.data) == opts->fillchar.size)),
"fillchar", "single character", NULL, {
return result;
});
- fillchar = utf_ptr2char(opts->fillchar.data);
+ int c;
+ fillchar = utfc_ptr2schar(opts->fillchar.data, &c);
+ // TODO(bfredl): actually check c is single width
}
int use_bools = (int)opts->use_winbar + (int)opts->use_tabline;
@@ -2186,50 +2214,44 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
statuscol_T statuscol = { 0 };
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
- if (opts->use_tabline) {
- fillchar = ' ';
- } else {
- if (fillchar == 0) {
- if (opts->use_winbar) {
- fillchar = wp->w_p_fcs_chars.wbr;
- } else {
- int attr;
- fillchar = fillchar_status(&attr, wp);
+ if (statuscol_lnum) {
+ int line_id = 0;
+ int cul_id = 0;
+ int num_id = 0;
+ linenr_T lnum = statuscol_lnum;
+ decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id);
+
+ statuscol.sattrs = sattrs;
+ statuscol.foldinfo = fold_info(wp, lnum);
+ wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
+
+ if (wp->w_p_cul) {
+ if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) {
+ wp->w_cursorline = statuscol.foldinfo.fi_lnum;
}
+ statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR);
}
- if (statuscol_lnum) {
- int line_id = 0;
- int cul_id = 0;
- int num_id = 0;
- linenr_T lnum = statuscol_lnum;
- wp->w_scwidth = win_signcol_count(wp);
- decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id);
-
- statuscol.sattrs = sattrs;
- statuscol.foldinfo = fold_info(wp, lnum);
- wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
-
- if (wp->w_p_cul) {
- if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) {
- wp->w_cursorline = statuscol.foldinfo.fi_lnum;
- }
- statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR);
- }
- statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0;
- if (num_id) {
- stc_hl_id = num_id;
- } else if (statuscol.use_cul) {
- stc_hl_id = HLF_CLN + 1;
- } else if (wp->w_p_rnu) {
- stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1;
- } else {
- stc_hl_id = HLF_N + 1;
- }
+ statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0;
+ if (num_id) {
+ stc_hl_id = num_id;
+ } else if (statuscol.use_cul) {
+ stc_hl_id = HLF_CLN + 1;
+ } else if (wp->w_p_rnu) {
+ stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB) + 1;
+ } else {
+ stc_hl_id = HLF_N + 1;
+ }
- set_vim_var_nr(VV_LNUM, lnum);
- set_vim_var_nr(VV_RELNUM, labs(get_cursor_rel_lnum(wp, lnum)));
- set_vim_var_nr(VV_VIRTNUM, 0);
+ set_vim_var_nr(VV_LNUM, lnum);
+ set_vim_var_nr(VV_RELNUM, labs(get_cursor_rel_lnum(wp, lnum)));
+ set_vim_var_nr(VV_VIRTNUM, 0);
+ } else if (fillchar == 0 && !opts->use_tabline) {
+ if (opts->use_winbar) {
+ fillchar = wp->w_p_fcs_chars.wbr;
+ } else {
+ int attr;
+ fillchar = fillchar_status(&attr, wp);
}
}
@@ -2242,70 +2264,66 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
&& global_stl_height() > 0)) ? Columns : wp->w_width;
}
- char buf[MAXPATHL];
+ result = arena_dict(arena, 3);
+ char *buf = arena_alloc(arena, MAXPATHL, false);
stl_hlrec_t *hltab;
+ size_t hltab_len = 0;
// Temporarily reset 'cursorbind' to prevent side effects from moving the cursor away and back.
int p_crb_save = wp->w_p_crb;
wp->w_p_crb = false;
- int width = build_stl_str_hl(wp,
- buf,
- sizeof(buf),
- str.data,
- NULL,
- 0,
- fillchar,
- maxwidth,
- opts->highlights ? &hltab : NULL,
- NULL,
+ int width = build_stl_str_hl(wp, buf, MAXPATHL, str.data, -1, 0, fillchar, maxwidth,
+ opts->highlights ? &hltab : NULL, &hltab_len, NULL,
statuscol_lnum ? &statuscol : NULL);
- PUT(result, "width", INTEGER_OBJ(width));
+ PUT_C(result, "width", INTEGER_OBJ(width));
// Restore original value of 'cursorbind'
wp->w_p_crb = p_crb_save;
if (opts->highlights) {
- Array hl_values = ARRAY_DICT_INIT;
- const char *grpname;
+ Array hl_values = arena_array(arena, hltab_len + 1);
char user_group[15]; // strlen("User") + strlen("2147483647") + NUL
// If first character doesn't have a defined highlight,
// add the default highlight at the beginning of the highlight list
if (hltab->start == NULL || (hltab->start - buf) != 0) {
- Dictionary hl_info = ARRAY_DICT_INIT;
- grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id);
+ Dictionary hl_info = arena_dict(arena, 2);
+ const char *grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp,
+ opts->use_winbar, stc_hl_id);
- PUT(hl_info, "start", INTEGER_OBJ(0));
- PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
+ PUT_C(hl_info, "start", INTEGER_OBJ(0));
+ PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
- ADD(hl_values, DICTIONARY_OBJ(hl_info));
+ ADD_C(hl_values, DICTIONARY_OBJ(hl_info));
}
for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) {
- Dictionary hl_info = ARRAY_DICT_INIT;
+ Dictionary hl_info = arena_dict(arena, 2);
- PUT(hl_info, "start", INTEGER_OBJ(sp->start - buf));
+ PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf));
+ const char *grpname;
if (sp->userhl == 0) {
grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id);
} else if (sp->userhl < 0) {
grpname = syn_id2name(-sp->userhl);
} else {
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
- grpname = user_group;
+ grpname = arena_memdupz(arena, user_group, strlen(user_group));
}
- PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
- ADD(hl_values, DICTIONARY_OBJ(hl_info));
+ PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
+ ADD_C(hl_values, DICTIONARY_OBJ(hl_info));
}
- PUT(result, "highlights", ARRAY_OBJ(hl_values));
+ PUT_C(result, "highlights", ARRAY_OBJ(hl_values));
}
- PUT(result, "str", CSTR_TO_OBJ(buf));
+ PUT_C(result, "str", CSTR_AS_OBJ(buf));
return result;
}
+/// @nodoc
void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
FUNC_API_REMOTE_ONLY
{
@@ -2313,3 +2331,29 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
// if we fork nvim processes as async workers
ELOG("async error on channel %" PRId64 ": %s", channel_id, data.size ? data.data : "");
}
+
+/// Set info for the completion candidate index.
+/// if the info was shown in a window, then the
+/// window and buffer ids are returned for further
+/// customization. If the text was not shown, an
+/// empty dict is returned.
+///
+/// @param index the completion candidate index
+/// @param opts Optional parameters.
+/// - info: (string) info text.
+/// @return Dictionary containing these keys:
+/// - winid: (number) floating window id
+/// - bufnr: (number) buffer id in floating window
+Dictionary nvim_complete_set(Integer index, Dict(complete_set) *opts, Arena *arena)
+ FUNC_API_SINCE(12)
+{
+ Dictionary rv = arena_dict(arena, 2);
+ if (HAS_KEY(opts, complete_set, info)) {
+ win_T *wp = pum_set_info((int)index, opts->info.data);
+ if (wp) {
+ PUT_C(rv, "winid", WINDOW_OBJ(wp->handle));
+ PUT_C(rv, "bufnr", BUFFER_OBJ(wp->w_buffer->handle));
+ }
+ }
+ return rv;
+}
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index c75bf21572..477cbe2428 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -11,18 +11,21 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/vimscript.h"
#include "nvim/ascii_defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_docmd.h"
-#include "nvim/func_attr.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
#include "nvim/runtime.h"
#include "nvim/vim_defs.h"
#include "nvim/viml/parser/expressions.h"
#include "nvim/viml/parser/parser.h"
+#include "nvim/viml/parser/parser_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vimscript.c.generated.h"
@@ -48,7 +51,7 @@
/// @return Dictionary containing information about execution, with these keys:
/// - output: (string|nil) Output if `opts.output` is true.
Dictionary nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err)
- FUNC_API_SINCE(11)
+ FUNC_API_SINCE(11) FUNC_API_RET_ALLOC
{
Dictionary result = ARRAY_DICT_INIT;
@@ -145,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
@@ -176,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);
}
}
@@ -193,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;
@@ -208,9 +211,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
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;
- }
+ object_to_vim(args.items[i], &vim_args[i], err);
}
// Initialize `force_abort` and `suppress_errthrow` at the top level.
@@ -233,18 +234,17 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
TRY_WRAP(err, {
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
// (see above) to capture abort-causing non-exception errors.
- (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size,
- vim_args, &funcexe);
+ call_func(fn.data, (int)fn.size, &rettv, (int)args.size,
+ vim_args, &funcexe);
});
if (!ERROR_SET(err)) {
- rv = vim_to_object(&rettv);
+ rv = vim_to_object(&rettv, arena, false);
}
tv_clear(&rettv);
recursive--;
-free_vim_args:
while (i > 0) {
tv_clear(&vim_args[--i]);
}
@@ -260,10 +260,10 @@ free_vim_args:
/// @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;
@@ -298,9 +298,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
mustfree = true;
break;
case kObjectTypeDictionary:
- if (!object_to_vim(dict, &rettv, err)) {
- goto end;
- }
+ object_to_vim(dict, &rettv, err);
break;
default:
api_set_error(err, kErrorTypeValidation,
@@ -339,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);
@@ -353,9 +351,7 @@ typedef struct {
Object *ret_node_p;
} ExprASTConvStackItem;
-/// @cond DOXYGEN_NOT_A_FUNCTION
typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
-/// @endcond
/// Parse a Vimscript expression.
///
@@ -369,8 +365,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// - "l" when needing to start parsing with lvalues for
/// ":let" or ":for".
/// Common flag sets:
-/// - "m" to parse like for ":echo".
-/// - "E" to parse like for "<C-r>=".
+/// - "m" to parse like for `":echo"`.
+/// - "E" to parse like for `"<C-r>="`.
/// - empty string for ":call".
/// - "lm" to parse for ":let".
/// @param[in] highlight If true, return value will also include "highlight"
@@ -389,12 +385,12 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// - "arg": String, error message argument.
/// - "len": Amount of bytes successfully parsed. With flags equal to ""
/// that should be equal to the length of expr string.
-/// (“Successfully parsed” here means “participated in AST
-/// creation”, not “till the first error”.)
+/// ("Successfully parsed" here means "participated in AST
+/// creation", not "till the first error".)
/// - "ast": AST, either nil or a dictionary with these keys:
/// - "type": node type, one of the value names from ExprASTNodeType
/// stringified without "kExprNode" prefix.
-/// - "start": a pair [line, column] describing where node is "started"
+/// - "start": a pair `[line, column]` describing where node is "started"
/// where "line" is always 0 (will not be 0 if you will be
/// using this API on e.g. ":let", but that is not
/// present yet). Both elements are Integers.
@@ -431,7 +427,8 @@ typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack;
/// - "svalue": String, value for "SingleQuotedString" and
/// "DoubleQuotedString" nodes.
/// @param[out] err Error details, if any
-Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Error *err)
+Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, Arena *arena,
+ Error *err)
FUNC_API_SINCE(4) FUNC_API_FAST
{
int pflags = 0;
@@ -473,82 +470,40 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
+ (size_t)(east.err.msg != NULL) // "error"
+ (size_t)highlight // "highlight"
+ 0);
- Dictionary ret = {
- .items = xmalloc(ret_size * sizeof(ret.items[0])),
- .size = 0,
- .capacity = ret_size,
- };
- ret.items[ret.size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("ast"),
- .value = NIL,
- };
- ret.items[ret.size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("len"),
- .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1
- ? parser_lines[0].size
- : pstate.pos.col)),
- };
+
+ Dictionary ret = arena_dict(arena, ret_size);
+ PUT_C(ret, "len", INTEGER_OBJ((Integer)(pstate.pos.line == 1
+ ? parser_lines[0].size
+ : pstate.pos.col)));
if (east.err.msg != NULL) {
- Dictionary err_dict = {
- .items = xmalloc(2 * sizeof(err_dict.items[0])),
- .size = 2,
- .capacity = 2,
- };
- err_dict.items[0] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("message"),
- .value = CSTR_TO_OBJ(east.err.msg),
- };
- if (east.err.arg == NULL) {
- err_dict.items[1] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("arg"),
- .value = STRING_OBJ(STRING_INIT),
- };
- } else {
- err_dict.items[1] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("arg"),
- .value = STRING_OBJ(((String) {
- .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len),
- .size = (size_t)east.err.arg_len,
- })),
- };
- }
- ret.items[ret.size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("error"),
- .value = DICTIONARY_OBJ(err_dict),
- };
+ Dictionary err_dict = arena_dict(arena, 2);
+ PUT_C(err_dict, "message", CSTR_TO_ARENA_OBJ(arena, east.err.msg));
+ PUT_C(err_dict, "arg", CBUF_TO_ARENA_OBJ(arena, east.err.arg, (size_t)east.err.arg_len));
+ PUT_C(ret, "error", DICTIONARY_OBJ(err_dict));
}
if (highlight) {
- Array hl = (Array) {
- .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])),
- .capacity = kv_size(colors),
- .size = kv_size(colors),
- };
+ Array hl = arena_array(arena, kv_size(colors));
for (size_t i = 0; i < kv_size(colors); i++) {
const ParserHighlightChunk chunk = kv_A(colors, i);
- Array chunk_arr = (Array) {
- .items = xmalloc(4 * sizeof(chunk_arr.items[0])),
- .capacity = 4,
- .size = 4,
- };
- chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line);
- chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col);
- chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col);
- chunk_arr.items[3] = CSTR_TO_OBJ(chunk.group);
- hl.items[i] = ARRAY_OBJ(chunk_arr);
+ Array chunk_arr = arena_array(arena, 4);
+ ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.line));
+ ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.start.col));
+ ADD_C(chunk_arr, INTEGER_OBJ((Integer)chunk.end_col));
+ ADD_C(chunk_arr, CSTR_AS_OBJ(chunk.group));
+
+ ADD_C(hl, ARRAY_OBJ(chunk_arr));
}
- ret.items[ret.size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("highlight"),
- .value = ARRAY_OBJ(hl),
- };
+ PUT_C(ret, "highlight", ARRAY_OBJ(hl));
}
kvi_destroy(colors);
// Walk over the AST, freeing nodes in process.
ExprASTConvStack ast_conv_stack;
kvi_init(ast_conv_stack);
+ Object ast = NIL;
kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
.node_p = &east.root,
- .ret_node_p = &ret.items[0].value,
+ .ret_node_p = &ast,
}));
while (kv_size(ast_conv_stack)) {
ExprASTConvStackItem cur_item = kv_last(ast_conv_stack);
@@ -575,28 +530,17 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
|| node->type == kExprNodeSingleQuotedString) // "svalue"
+ (node->type == kExprNodeAssignment) // "augmentation"
+ 0);
- Dictionary ret_node = {
- .items = xmalloc(items_size * sizeof(ret_node.items[0])),
- .capacity = items_size,
- .size = 0,
- };
+ Dictionary ret_node = arena_dict(arena, items_size);
*cur_item.ret_node_p = DICTIONARY_OBJ(ret_node);
}
Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary;
if (node->children != NULL) {
const size_t num_children = 1 + (node->children->next != NULL);
- Array children_array = {
- .items = xmalloc(num_children * sizeof(children_array.items[0])),
- .capacity = num_children,
- .size = num_children,
- };
+ Array children_array = arena_array(arena, num_children);
for (size_t i = 0; i < num_children; i++) {
- children_array.items[i] = NIL;
+ ADD_C(children_array, NIL);
}
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("children"),
- .value = ARRAY_OBJ(children_array),
- };
+ PUT_C(*ret_node, "children", ARRAY_OBJ(children_array));
kvi_push(ast_conv_stack, ((ExprASTConvStackItem) {
.node_p = &node->children,
.ret_node_p = &children_array.items[0],
@@ -608,126 +552,60 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
}));
} else {
kv_drop(ast_conv_stack, 1);
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("type"),
- .value = CSTR_TO_OBJ(east_node_type_tab[node->type]),
- };
- Array start_array = {
- .items = xmalloc(2 * sizeof(start_array.items[0])),
- .capacity = 2,
- .size = 2,
- };
- start_array.items[0] = INTEGER_OBJ((Integer)node->start.line);
- start_array.items[1] = INTEGER_OBJ((Integer)node->start.col);
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("start"),
- .value = ARRAY_OBJ(start_array),
- };
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("len"),
- .value = INTEGER_OBJ((Integer)node->len),
- };
+ PUT_C(*ret_node, "type", CSTR_AS_OBJ(east_node_type_tab[node->type]));
+ Array start_array = arena_array(arena, 2);
+ ADD_C(start_array, INTEGER_OBJ((Integer)node->start.line));
+ ADD_C(start_array, INTEGER_OBJ((Integer)node->start.col));
+ PUT_C(*ret_node, "start", ARRAY_OBJ(start_array));
+
+ PUT_C(*ret_node, "len", INTEGER_OBJ((Integer)node->len));
switch (node->type) {
case kExprNodeDoubleQuotedString:
- case kExprNodeSingleQuotedString:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("svalue"),
- .value = STRING_OBJ(((String) {
- .data = node->data.str.value,
- .size = node->data.str.size,
- })),
- };
+ case kExprNodeSingleQuotedString: {
+ Object str = CBUF_TO_ARENA_OBJ(arena, node->data.str.value, node->data.str.size);
+ PUT_C(*ret_node, "svalue", str);
+ xfree(node->data.str.value);
break;
+ }
case kExprNodeOption:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("scope"),
- .value = INTEGER_OBJ(node->data.opt.scope),
- };
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("ident"),
- .value = STRING_OBJ(((String) {
- .data = xmemdupz(node->data.opt.ident,
- node->data.opt.ident_len),
- .size = node->data.opt.ident_len,
- })),
- };
+ PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.opt.scope));
+ PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.opt.ident,
+ node->data.opt.ident_len));
break;
case kExprNodePlainIdentifier:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("scope"),
- .value = INTEGER_OBJ(node->data.var.scope),
- };
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("ident"),
- .value = STRING_OBJ(((String) {
- .data = xmemdupz(node->data.var.ident,
- node->data.var.ident_len),
- .size = node->data.var.ident_len,
- })),
- };
+ PUT_C(*ret_node, "scope", INTEGER_OBJ(node->data.var.scope));
+ PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident,
+ node->data.var.ident_len));
break;
case kExprNodePlainKey:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("ident"),
- .value = STRING_OBJ(((String) {
- .data = xmemdupz(node->data.var.ident,
- node->data.var.ident_len),
- .size = node->data.var.ident_len,
- })),
- };
+ PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.var.ident,
+ node->data.var.ident_len));
break;
case kExprNodeEnvironment:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("ident"),
- .value = STRING_OBJ(((String) {
- .data = xmemdupz(node->data.env.ident,
- node->data.env.ident_len),
- .size = node->data.env.ident_len,
- })),
- };
+ PUT_C(*ret_node, "ident", CBUF_TO_ARENA_OBJ(arena, node->data.env.ident,
+ node->data.env.ident_len));
break;
case kExprNodeRegister:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("name"),
- .value = INTEGER_OBJ(node->data.reg.name),
- };
+ PUT_C(*ret_node, "name", INTEGER_OBJ(node->data.reg.name));
break;
case kExprNodeComparison:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("cmp_type"),
- .value = CSTR_TO_OBJ(eltkn_cmp_type_tab[node->data.cmp.type]),
- };
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("ccs_strategy"),
- .value = CSTR_TO_OBJ(ccs_tab[node->data.cmp.ccs]),
- };
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("invert"),
- .value = BOOLEAN_OBJ(node->data.cmp.inv),
- };
+ PUT_C(*ret_node, "cmp_type", CSTR_AS_OBJ(eltkn_cmp_type_tab[node->data.cmp.type]));
+ PUT_C(*ret_node, "ccs_strategy", CSTR_AS_OBJ(ccs_tab[node->data.cmp.ccs]));
+ PUT_C(*ret_node, "invert", BOOLEAN_OBJ(node->data.cmp.inv));
break;
case kExprNodeFloat:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("fvalue"),
- .value = FLOAT_OBJ(node->data.flt.value),
- };
+ PUT_C(*ret_node, "fvalue", FLOAT_OBJ(node->data.flt.value));
break;
case kExprNodeInteger:
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("ivalue"),
- .value = INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX
- ? API_INTEGER_MAX
- : (Integer)node->data.num.value)),
- };
+ PUT_C(*ret_node, "ivalue", INTEGER_OBJ((Integer)(node->data.num.value > API_INTEGER_MAX
+ ? API_INTEGER_MAX
+ : (Integer)node->data.num.value)));
break;
case kExprNodeAssignment: {
const ExprAssignmentType asgn_type = node->data.ass.type;
- ret_node->items[ret_node->size++] = (KeyValuePair) {
- .key = STATIC_CSTR_TO_STRING("augmentation"),
- .value = STRING_OBJ(asgn_type == kExprAsgnPlain
- ? (String)STRING_INIT
- : cstr_to_string(expr_asgn_type_tab[asgn_type])),
- };
+ String str = (asgn_type == kExprAsgnPlain
+ ? (String)STRING_INIT : cstr_as_string(expr_asgn_type_tab[asgn_type]));
+ PUT_C(*ret_node, "augmentation", STRING_OBJ(str));
break;
}
case kExprNodeMissing:
@@ -768,6 +646,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
}
}
kvi_destroy(ast_conv_stack);
+ PUT_C(ret, "ast", ast);
assert(ret.size == ret.capacity);
// Should be a no-op actually, leaving it in case non-nodes will need to be
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 4e23717dc6..543c7b8113 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -7,24 +7,33 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/tabpage.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_defs.h"
#include "nvim/drawscreen.h"
-#include "nvim/func_attr.h"
+#include "nvim/eval/window.h"
+#include "nvim/extmark_defs.h"
#include "nvim/globals.h"
-#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/option.h"
+#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
+#include "nvim/types_defs.h"
#include "nvim/ui.h"
+#include "nvim/ui_compositor.h"
+#include "nvim/ui_defs.h"
+#include "nvim/vim_defs.h"
#include "nvim/window.h"
#include "nvim/winfloat.h"
@@ -32,9 +41,9 @@
# include "api/win_config.c.generated.h"
#endif
-/// Open a new window.
+/// Opens a new split window, or a floating window if `relative` is specified,
+/// or an external window (managed by the UI) if `external` is specified.
///
-/// Currently this is used to open floating and external windows.
/// Floats are windows that are drawn above the split layout, at some anchor
/// position in some other window. Floats can be drawn internally or by external
/// GUI with the |ui-multigrid| extension. External windows are only supported
@@ -42,8 +51,17 @@
///
/// For a general overview of floats, see |api-floatwin|.
///
-/// Exactly one of `external` and `relative` must be specified. The `width` and
-/// `height` of the new window must be specified.
+/// The `width` and `height` of the new window must be specified when opening
+/// a floating window, but are optional for normal windows.
+///
+/// If `relative` and `external` are omitted, a normal "split" window is created.
+/// The `win` property determines which window will be split. If no `win` is
+/// provided or `win == 0`, a window will be created adjacent to the current window.
+/// If -1 is provided, a top-level split will be created. `vertical` and `split` are
+/// only valid for normal windows, and are used to control split direction. For `vertical`,
+/// the exact direction is determined by |'splitright'| and |'splitbelow'|.
+/// Split windows cannot have `bufpos`/`row`/`col`/`border`/`title`/`footer`
+/// properties.
///
/// With relative=editor (row=0,col=0) refers to the top-left corner of the
/// screen-grid and (row=Lines-1,col=Columns-1) refers to the bottom-right
@@ -68,6 +86,14 @@
/// ```lua
/// vim.api.nvim_open_win(0, false,
/// {relative='win', width=12, height=3, bufpos={100,10}})
+/// ```
+///
+/// Example (Lua): vertical split left of the current window
+///
+/// ```lua
+/// vim.api.nvim_open_win(0, false, {
+/// split = 'left',
+/// win = 0
/// })
/// ```
///
@@ -80,7 +106,8 @@
/// - "win" Window given by the `win` field, or current window.
/// - "cursor" Cursor position in current window.
/// - "mouse" Mouse position
-/// - win: |window-ID| for relative="win".
+/// - win: |window-ID| window to split, or relative window when creating a
+/// float (relative="win").
/// - anchor: Decides which corner of the float to place at (row,col):
/// - "NW" northwest (default)
/// - "NE" northeast
@@ -89,12 +116,12 @@
/// - width: Window width (in character cells). Minimum of 1.
/// - height: Window height (in character cells). Minimum of 1.
/// - bufpos: Places float relative to buffer text (only when
-/// relative="win"). Takes a tuple of zero-indexed [line, column].
-/// `row` and `col` if given are applied relative to this
-/// position, else they default to:
-/// - `row=1` and `col=0` if `anchor` is "NW" or "NE"
-/// - `row=0` and `col=0` if `anchor` is "SW" or "SE"
-/// (thus like a tooltip near the buffer text).
+/// relative="win"). Takes a tuple of zero-indexed `[line, column]`.
+/// `row` and `col` if given are applied relative to this
+/// position, else they default to:
+/// - `row=1` and `col=0` if `anchor` is "NW" or "NE"
+/// - `row=0` and `col=0` if `anchor` is "SW" or "SE"
+/// (thus like a tooltip near the buffer text).
/// - row: Row position in units of "screen cell height", may be fractional.
/// - col: Column position in units of "screen cell width", may be
/// fractional.
@@ -126,7 +153,7 @@
/// 'fillchars' to a space char, and clearing the
/// |hl-EndOfBuffer| region in 'winhighlight'.
/// - border: Style of (optional) window border. This can either be a string
-/// or an array. The string values are
+/// or an array. The string values are
/// - "none": No border (default).
/// - "single": A single line box.
/// - "double": A double line box.
@@ -134,21 +161,31 @@
/// - "solid": Adds padding by a single whitespace cell.
/// - "shadow": A drop shadow effect by blending with the background.
/// - If it is an array, it should have a length of eight or any divisor of
-/// eight. The array will specify the eight chars building up the border
-/// in a clockwise fashion starting with the top-left corner. As an
-/// example, the double box style could be specified as
+/// eight. The array will specify the eight chars building up the border
+/// in a clockwise fashion starting with the top-left corner. As an
+/// example, the double box style could be specified as:
+/// ```
/// [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ].
-/// If the number of chars are less than eight, they will be repeated. Thus
-/// an ASCII border could be specified as
+/// ```
+/// If the number of chars are less than eight, they will be repeated. Thus
+/// an ASCII border could be specified as
+/// ```
/// [ "/", "-", \"\\\\\", "|" ],
-/// or all chars the same as
+/// ```
+/// or all chars the same as
+/// ```
/// [ "x" ].
+/// ```
/// An empty string can be used to turn off a specific border, for instance,
+/// ```
/// [ "", "", "", ">", "", "", "", "<" ]
+/// ```
/// will only make vertical borders but not horizontal ones.
/// By default, `FloatBorder` highlight is used, which links to `WinSeparator`
/// when not defined. It could also be specified by character:
+/// ```
/// [ ["+", "MyCorner"], ["x", "MyBorder"] ].
+/// ```
/// - title: Title (optional) in window border, string or list.
/// List should consist of `[text, highlight]` tuples.
/// If string, the default highlight group is `FloatTitle`.
@@ -167,43 +204,90 @@
/// - fixed: If true when anchor is NW or SW, the float window
/// would be kept fixed even if the window would be truncated.
/// - hide: If true the floating window will be hidden.
+/// - vertical: Split vertically |:vertical|.
+/// - split: Split direction: "left", "right", "above", "below".
///
/// @param[out] err Error details, if any
///
/// @return Window handle, or 0 on error
-Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err)
- FUNC_API_SINCE(6)
- FUNC_API_TEXTLOCK_ALLOW_CMDWIN
+Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Error *err)
+ FUNC_API_SINCE(6) FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
+#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key)
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
return 0;
}
- if (cmdwin_type != 0 && (enter || buf == curbuf)) {
+ if ((cmdwin_type != 0 && enter) || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return 0;
}
- FloatConfig fconfig = FLOAT_CONFIG_INIT;
+ WinConfig fconfig = WIN_CONFIG_INIT;
if (!parse_float_config(config, &fconfig, false, true, err)) {
return 0;
}
- win_T *wp = win_new_float(NULL, false, fconfig, err);
+
+ bool is_split = HAS_KEY_X(config, split) || HAS_KEY_X(config, vertical);
+
+ win_T *wp = NULL;
+ tabpage_T *tp = curtab;
+ if (is_split) {
+ win_T *parent = NULL;
+ if (config->win != -1) {
+ parent = find_window_by_handle(fconfig.window, err);
+ if (!parent) {
+ // find_window_by_handle has already set the error
+ return 0;
+ } else if (parent->w_floating) {
+ api_set_error(err, kErrorTypeException, "Cannot split a floating window");
+ return 0;
+ }
+ }
+
+ if (HAS_KEY_X(config, vertical) && !HAS_KEY_X(config, split)) {
+ if (config->vertical) {
+ fconfig.split = p_spr ? kWinSplitRight : kWinSplitLeft;
+ } else {
+ fconfig.split = p_sb ? kWinSplitBelow : kWinSplitAbove;
+ }
+ }
+ int flags = win_split_flags(fconfig.split, parent == NULL) | WSP_NOENTER;
+
+ if (parent == NULL) {
+ wp = win_split_ins(0, flags, NULL, 0);
+ } else {
+ tp = win_find_tabpage(parent);
+ switchwin_T switchwin;
+ // `parent` is valid in `tp`, so switch_win should not fail.
+ const int result = switch_win(&switchwin, parent, tp, true);
+ (void)result;
+ assert(result == OK);
+ wp = win_split_ins(0, flags, NULL, 0);
+ restore_win(&switchwin, true);
+ }
+ if (wp) {
+ wp->w_config = fconfig;
+ }
+ } else {
+ wp = win_new_float(NULL, false, fconfig, err);
+ }
if (!wp) {
+ api_set_error(err, kErrorTypeException, "Failed to create window");
return 0;
}
+ switchwin_T switchwin;
+ if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
+ apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
+ }
+ restore_win_noblock(&switchwin, true);
if (enter) {
- win_enter(wp, false);
+ goto_tabpage_win(tp, wp);
}
- // autocmds in win_enter or win_set_buf below may close the window
- if (win_valid(wp) && buffer > 0) {
- Boolean noautocmd = !enter || fconfig.noautocmd;
- win_set_buf(wp, buf, noautocmd, err);
- if (!fconfig.noautocmd) {
- apply_autocmds(EVENT_WINNEW, NULL, NULL, false, buf);
- }
+ if (win_valid_any_tab(wp) && buf != wp->w_buffer) {
+ win_set_buf(wp, buf, !enter || fconfig.noautocmd, err);
}
- if (!win_valid(wp)) {
+ if (!win_valid_any_tab(wp)) {
api_set_error(err, kErrorTypeException, "Window was closed immediately");
return 0;
}
@@ -213,6 +297,37 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
didset_window_options(wp, true);
}
return wp->handle;
+#undef HAS_KEY_X
+}
+
+static WinSplit win_split_dir(win_T *win)
+{
+ if (win->w_frame == NULL || win->w_frame->fr_parent == NULL) {
+ return kWinSplitLeft;
+ }
+
+ char layout = win->w_frame->fr_parent->fr_layout;
+ if (layout == FR_COL) {
+ return win->w_frame->fr_next ? kWinSplitAbove : kWinSplitBelow;
+ } else {
+ return win->w_frame->fr_next ? kWinSplitLeft : kWinSplitRight;
+ }
+}
+
+static int win_split_flags(WinSplit split, bool toplevel)
+{
+ int flags = 0;
+ if (split == kWinSplitAbove || split == kWinSplitBelow) {
+ flags |= WSP_HOR;
+ } else {
+ flags |= WSP_VERT;
+ }
+ if (split == kWinSplitAbove || split == kWinSplitLeft) {
+ flags |= toplevel ? WSP_TOP : WSP_ABOVE;
+ } else {
+ flags |= toplevel ? WSP_BOT : WSP_BELOW;
+ }
+ return flags;
}
/// Configures window layout. Currently only for floating and external windows
@@ -227,61 +342,231 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
/// @param config Map defining the window configuration,
/// see |nvim_open_win()|
/// @param[out] err Error details, if any
-void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
+void nvim_win_set_config(Window window, Dict(win_config) *config, Error *err)
FUNC_API_SINCE(6)
{
+#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key)
win_T *win = find_window_by_handle(window, err);
if (!win) {
return;
}
- bool new_float = !win->w_floating;
+ tabpage_T *win_tp = win_find_tabpage(win);
+ bool was_split = !win->w_floating;
+ bool has_split = HAS_KEY_X(config, split);
+ bool has_vertical = HAS_KEY_X(config, vertical);
// reuse old values, if not overridden
- FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
+ WinConfig fconfig = win->w_config;
- if (!parse_float_config(config, &fconfig, !new_float, false, err)) {
+ bool to_split = config->relative.size == 0
+ && !(HAS_KEY_X(config, external) ? config->external : fconfig.external)
+ && (has_split || has_vertical || was_split);
+
+ if (!parse_float_config(config, &fconfig, !was_split || to_split, false, err)) {
return;
}
- if (new_float) {
+ if (was_split && !to_split) {
if (!win_new_float(win, false, fconfig, err)) {
return;
}
redraw_later(win, UPD_NOT_VALID);
+ } else if (to_split) {
+ win_T *parent = NULL;
+ if (config->win != -1) {
+ parent = find_window_by_handle(fconfig.window, err);
+ if (!parent) {
+ return;
+ } else if (parent->w_floating) {
+ api_set_error(err, kErrorTypeException, "Cannot split a floating window");
+ return;
+ }
+ }
+
+ WinSplit old_split = win_split_dir(win);
+ if (has_vertical && !has_split) {
+ if (config->vertical) {
+ if (old_split == kWinSplitRight || p_spr) {
+ fconfig.split = kWinSplitRight;
+ } else {
+ fconfig.split = kWinSplitLeft;
+ }
+ } else {
+ if (old_split == kWinSplitBelow || p_sb) {
+ fconfig.split = kWinSplitBelow;
+ } else {
+ fconfig.split = kWinSplitAbove;
+ }
+ }
+ }
+ win->w_config = fconfig;
+
+ // If there's no "vertical" or "split" set, or if "split" is unchanged,
+ // then we can just change the size of the window.
+ if ((!has_vertical && !has_split)
+ || (was_split && !HAS_KEY_X(config, win) && old_split == fconfig.split)) {
+ if (HAS_KEY_X(config, width)) {
+ win_setwidth_win(fconfig.width, win);
+ }
+ if (HAS_KEY_X(config, height)) {
+ win_setheight_win(fconfig.height, win);
+ }
+ redraw_later(win, UPD_NOT_VALID);
+ return;
+ }
+
+ if (was_split) {
+ win_T *new_curwin = NULL;
+
+ // If the window is the last in the tabpage or `fconfig.win` is
+ // a handle to itself, we can't split it.
+ if (win->w_frame->fr_parent == NULL) {
+ // FIXME(willothy): if the window is the last in the tabpage but there is another tabpage
+ // and the target window is in that other tabpage, should we move the window to that
+ // tabpage and close the previous one, or just error?
+ api_set_error(err, kErrorTypeValidation, "Cannot move last window");
+ return;
+ } else if (parent != NULL && parent->handle == win->handle) {
+ int n_frames = 0;
+ for (frame_T *fr = win->w_frame->fr_parent->fr_child; fr != NULL; fr = fr->fr_next) {
+ n_frames++;
+ }
+
+ win_T *neighbor = NULL;
+
+ if (n_frames > 2) {
+ // There are three or more windows in the frame, we need to split a neighboring window.
+ frame_T *frame = win->w_frame->fr_parent;
+
+ if (frame->fr_parent) {
+ // ┌──────────────┐
+ // │ A │
+ // ├────┬────┬────┤
+ // │ B │ C │ D │
+ // └────┴────┴────┘
+ // ||
+ // \/
+ // ┌───────────────────┐
+ // │ A │
+ // ├─────────┬─────────┤
+ // │ │ C │
+ // │ B ├─────────┤
+ // │ │ D │
+ // └─────────┴─────────┘
+ if (fconfig.split == kWinSplitAbove || fconfig.split == kWinSplitLeft) {
+ neighbor = win->w_next;
+ } else {
+ neighbor = win->w_prev;
+ }
+ }
+ // If the frame doesn't have a parent, the old frame
+ // was the root frame and we need to create a top-level split.
+ int dir;
+ new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
+ } else if (n_frames == 2) {
+ // There are two windows in the frame, we can just rotate it.
+ int dir;
+ neighbor = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
+ new_curwin = neighbor;
+ } else {
+ // There is only one window in the frame, we can't split it.
+ api_set_error(err, kErrorTypeValidation, "Cannot split window into itself");
+ return;
+ }
+ // Set the parent to whatever the correct
+ // neighbor window was determined to be.
+ parent = neighbor;
+ } else {
+ int dir;
+ new_curwin = winframe_remove(win, &dir, win_tp == curtab ? NULL : win_tp);
+ }
+ // move to neighboring window if we're moving the current window to a new tabpage
+ if (curwin == win && parent != NULL && new_curwin != NULL
+ && win_tp != win_find_tabpage(parent)) {
+ win_enter(new_curwin, true);
+ }
+ win_remove(win, win_tp == curtab ? NULL : win_tp);
+ } else {
+ win_remove(win, win_tp == curtab ? NULL : win_tp);
+ ui_comp_remove_grid(&win->w_grid_alloc);
+ if (win->w_config.external) {
+ for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) {
+ if (tp == curtab) {
+ continue;
+ }
+ if (tp->tp_curwin == win) {
+ tp->tp_curwin = tp->tp_firstwin;
+ }
+ }
+ }
+ win->w_pos_changed = true;
+ }
+
+ int flags = win_split_flags(fconfig.split, parent == NULL);
+
+ if (parent == NULL) {
+ if (!win_split_ins(0, flags, win, 0)) {
+ // TODO(willothy): What should this error message say?
+ api_set_error(err, kErrorTypeException, "Failed to split window");
+ return;
+ }
+ } else {
+ win_execute_T args;
+
+ tabpage_T *tp = win_find_tabpage(parent);
+ if (!win_execute_before(&args, parent, tp)) {
+ // TODO(willothy): how should we handle this / what should the message be?
+ api_set_error(err, kErrorTypeException, "Failed to switch to tabpage %d", tp->handle);
+ win_execute_after(&args);
+ return;
+ }
+ // This should return the same ptr to `win`, but we check for
+ // NULL to detect errors.
+ win_T *res = win_split_ins(0, flags, win, 0);
+ win_execute_after(&args);
+ if (!res) {
+ // TODO(willothy): What should this error message say?
+ api_set_error(err, kErrorTypeException, "Failed to split window");
+ return;
+ }
+ }
+ if (HAS_KEY_X(config, width)) {
+ win_setwidth_win(fconfig.width, win);
+ }
+ if (HAS_KEY_X(config, height)) {
+ win_setheight_win(fconfig.height, win);
+ }
+ redraw_later(win, UPD_NOT_VALID);
+ return;
} else {
win_config_float(win, fconfig);
win->w_pos_changed = true;
}
- if (HAS_KEY(config, float_config, style)) {
+ if (HAS_KEY_X(config, style)) {
if (fconfig.style == kWinStyleMinimal) {
win_set_minimal_style(win);
didset_window_options(win, true);
}
}
+#undef HAS_KEY_X
}
-static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig,
- BorderTextType bordertext_type)
+#define PUT_KEY_X(d, key, value) PUT_KEY(d, win_config, key, value)
+static void config_put_bordertext(Dict(win_config) *config, WinConfig *fconfig,
+ BorderTextType bordertext_type, Arena *arena)
{
VirtText vt;
AlignTextPos align;
- char *field_name;
- char *field_pos_name;
switch (bordertext_type) {
case kBorderTextTitle:
vt = fconfig->title_chunks;
align = fconfig->title_pos;
- field_name = "title";
- field_pos_name = "title_pos";
break;
case kBorderTextFooter:
vt = fconfig->footer_chunks;
align = fconfig->footer_pos;
- field_name = "footer";
- field_pos_name = "footer_pos";
break;
}
- Array bordertext = virt_text_to_array(vt, true);
- PUT(config, field_name, ARRAY_OBJ(bordertext));
+ Array bordertext = virt_text_to_array(vt, true, arena);
char *pos;
switch (align) {
@@ -295,9 +580,16 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig,
pos = "right";
break;
}
- PUT(config, field_pos_name, CSTR_TO_OBJ(pos));
- return config;
+ switch (bordertext_type) {
+ case kBorderTextTitle:
+ PUT_KEY_X(*config, title, ARRAY_OBJ(bordertext));
+ PUT_KEY_X(*config, title_pos, cstr_as_string(pos));
+ break;
+ case kBorderTextFooter:
+ PUT_KEY_X(*config, footer, ARRAY_OBJ(bordertext));
+ PUT_KEY_X(*config, footer_pos, cstr_as_string(pos));
+ }
}
/// Gets window configuration.
@@ -309,70 +601,80 @@ static Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig,
/// @param window Window handle, or 0 for current window
/// @param[out] err Error details, if any
/// @return Map defining the window configuration, see |nvim_open_win()|
-Dictionary nvim_win_get_config(Window window, Error *err)
+Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
FUNC_API_SINCE(6)
{
- Dictionary rv = ARRAY_DICT_INIT;
+ /// Keep in sync with FloatRelative in buffer_defs.h
+ static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" };
+
+ /// Keep in sync with WinSplit in buffer_defs.h
+ static const char *const win_split_str[] = { "left", "right", "above", "below" };
+
+ Dict(win_config) rv = KEYDICT_INIT;
win_T *wp = find_window_by_handle(window, err);
if (!wp) {
return rv;
}
- FloatConfig *config = &wp->w_float_config;
+ WinConfig *config = &wp->w_config;
- PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable));
- PUT(rv, "external", BOOLEAN_OBJ(config->external));
- PUT(rv, "hide", BOOLEAN_OBJ(config->hide));
+ PUT_KEY_X(rv, focusable, config->focusable);
+ PUT_KEY_X(rv, external, config->external);
+ PUT_KEY_X(rv, hide, config->hide);
if (wp->w_floating) {
- PUT(rv, "width", INTEGER_OBJ(config->width));
- PUT(rv, "height", INTEGER_OBJ(config->height));
+ PUT_KEY_X(rv, width, config->width);
+ PUT_KEY_X(rv, height, config->height);
if (!config->external) {
if (config->relative == kFloatRelativeWindow) {
- PUT(rv, "win", INTEGER_OBJ(config->window));
+ PUT_KEY_X(rv, win, config->window);
if (config->bufpos.lnum >= 0) {
- Array pos = ARRAY_DICT_INIT;
- ADD(pos, INTEGER_OBJ(config->bufpos.lnum));
- ADD(pos, INTEGER_OBJ(config->bufpos.col));
- PUT(rv, "bufpos", ARRAY_OBJ(pos));
+ Array pos = arena_array(arena, 2);
+ ADD_C(pos, INTEGER_OBJ(config->bufpos.lnum));
+ ADD_C(pos, INTEGER_OBJ(config->bufpos.col));
+ PUT_KEY_X(rv, bufpos, pos);
}
}
- PUT(rv, "anchor", CSTR_TO_OBJ(float_anchor_str[config->anchor]));
- PUT(rv, "row", FLOAT_OBJ(config->row));
- PUT(rv, "col", FLOAT_OBJ(config->col));
- PUT(rv, "zindex", INTEGER_OBJ(config->zindex));
+ PUT_KEY_X(rv, anchor, cstr_as_string(float_anchor_str[config->anchor]));
+ PUT_KEY_X(rv, row, config->row);
+ PUT_KEY_X(rv, col, config->col);
+ PUT_KEY_X(rv, zindex, config->zindex);
}
if (config->border) {
- Array border = ARRAY_DICT_INIT;
+ Array border = arena_array(arena, 8);
for (size_t i = 0; i < 8; i++) {
- Array tuple = ARRAY_DICT_INIT;
-
- String s = cstrn_to_string(config->border_chars[i], MAX_SCHAR_SIZE);
+ String s = cstrn_as_string(config->border_chars[i], MAX_SCHAR_SIZE);
int hi_id = config->border_hl_ids[i];
char *hi_name = syn_id2name(hi_id);
if (hi_name[0]) {
- ADD(tuple, STRING_OBJ(s));
- ADD(tuple, CSTR_TO_OBJ(hi_name));
- ADD(border, ARRAY_OBJ(tuple));
+ Array tuple = arena_array(arena, 2);
+ ADD_C(tuple, STRING_OBJ(s));
+ ADD_C(tuple, CSTR_AS_OBJ(hi_name));
+ ADD_C(border, ARRAY_OBJ(tuple));
} else {
- ADD(border, STRING_OBJ(s));
+ ADD_C(border, STRING_OBJ(s));
}
}
- PUT(rv, "border", ARRAY_OBJ(border));
+ PUT_KEY_X(rv, border, ARRAY_OBJ(border));
if (config->title) {
- rv = config_put_bordertext(rv, config, kBorderTextTitle);
+ config_put_bordertext(&rv, config, kBorderTextTitle, arena);
}
if (config->footer) {
- rv = config_put_bordertext(rv, config, kBorderTextFooter);
+ config_put_bordertext(&rv, config, kBorderTextFooter, arena);
}
}
+ } else if (!config->external) {
+ PUT_KEY_X(rv, width, wp->w_width);
+ PUT_KEY_X(rv, height, wp->w_height);
+ WinSplit split = win_split_dir(wp);
+ PUT_KEY_X(rv, split, cstr_as_string(win_split_str[split]));
}
const char *rel = (wp->w_floating && !config->external
? float_relative_str[config->relative] : "");
- PUT(rv, "relative", CSTR_TO_OBJ(rel));
+ PUT_KEY_X(rv, relative, cstr_as_string(rel));
return rv;
}
@@ -414,10 +716,26 @@ static bool parse_float_relative(String relative, FloatRelative *out)
return true;
}
+static bool parse_config_split(String split, WinSplit *out)
+{
+ char *str = split.data;
+ if (striequal(str, "left")) {
+ *out = kWinSplitLeft;
+ } else if (striequal(str, "right")) {
+ *out = kWinSplitRight;
+ } else if (striequal(str, "above")) {
+ *out = kWinSplitAbove;
+ } else if (striequal(str, "below")) {
+ *out = kWinSplitBelow;
+ } else {
+ return false;
+ }
+ return true;
+}
+
static bool parse_float_bufpos(Array bufpos, lpos_T *out)
{
- if (bufpos.size != 2
- || bufpos.items[0].type != kObjectTypeInteger
+ if (bufpos.size != 2 || bufpos.items[0].type != kObjectTypeInteger
|| bufpos.items[1].type != kObjectTypeInteger) {
return false;
}
@@ -426,21 +744,39 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
return true;
}
-static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
- FloatConfig *fconfig, Error *err)
+static void parse_bordertext(Object bordertext, BorderTextType bordertext_type, WinConfig *fconfig,
+ Error *err)
{
+ if (bordertext.type != kObjectTypeString && bordertext.type != kObjectTypeArray) {
+ api_set_error(err, kErrorTypeValidation, "title/footer must be string or array");
+ return;
+ }
+
+ if (bordertext.type == kObjectTypeArray && bordertext.data.array.size == 0) {
+ api_set_error(err, kErrorTypeValidation, "title/footer cannot be an empty array");
+ return;
+ }
+
bool *is_present;
VirtText *chunks;
int *width;
int default_hl_id;
switch (bordertext_type) {
case kBorderTextTitle:
+ if (fconfig->title) {
+ clear_virttext(&fconfig->title_chunks);
+ }
+
is_present = &fconfig->title;
chunks = &fconfig->title_chunks;
width = &fconfig->title_width;
default_hl_id = syn_check_group(S_LEN("FloatTitle"));
break;
case kBorderTextFooter:
+ if (fconfig->footer) {
+ clear_virttext(&fconfig->footer_chunks);
+ }
+
is_present = &fconfig->footer;
chunks = &fconfig->footer_chunks;
width = &fconfig->footer_width;
@@ -460,16 +796,6 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
return;
}
- if (bordertext.type != kObjectTypeArray) {
- api_set_error(err, kErrorTypeValidation, "title must be string or array");
- return;
- }
-
- if (bordertext.data.array.size == 0) {
- api_set_error(err, kErrorTypeValidation, "title cannot be an empty array");
- return;
- }
-
*width = 0;
*chunks = parse_virt_text(bordertext.data.array, err, width);
@@ -477,7 +803,7 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
}
static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type,
- FloatConfig *fconfig, Error *err)
+ WinConfig *fconfig, Error *err)
{
AlignTextPos *align;
switch (bordertext_type) {
@@ -516,7 +842,7 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex
return true;
}
-static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
+static void parse_border_style(Object style, WinConfig *fconfig, Error *err)
{
struct {
const char *name;
@@ -531,7 +857,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
{ NULL, { { NUL } }, false },
};
- char (*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars;
+ char(*chars)[MAX_SCHAR_SIZE] = fconfig->border_chars;
int *hl_ids = fconfig->border_hl_ids;
fconfig->border = true;
@@ -540,8 +866,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
Array arr = style.data.array;
size_t size = arr.size;
if (!size || size > 8 || (size & (size - 1))) {
- api_set_error(err, kErrorTypeValidation,
- "invalid number of border chars");
+ api_set_error(err, kErrorTypeValidation, "invalid number of border chars");
return;
}
for (size_t i = 0; i < size; i++) {
@@ -571,10 +896,8 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
api_set_error(err, kErrorTypeValidation, "invalid border char");
return;
}
- if (string.size
- && mb_string2cells_len(string.data, string.size) > 1) {
- api_set_error(err, kErrorTypeValidation,
- "border chars must be one cell");
+ if (string.size && mb_string2cells_len(string.data, string.size) > 1) {
+ api_set_error(err, kErrorTypeValidation, "border chars must be one cell");
return;
}
size_t len = MIN(string.size, sizeof(*chars) - 1);
@@ -593,8 +916,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
|| (chars[1][0] && chars[3][0] && !chars[2][0])
|| (chars[3][0] && chars[5][0] && !chars[4][0])
|| (chars[5][0] && chars[7][0] && !chars[6][0])) {
- api_set_error(err, kErrorTypeValidation,
- "corner between used edges must be specified");
+ api_set_error(err, kErrorTypeValidation, "corner between used edges must be specified");
}
} else if (style.type == kObjectTypeString) {
String str = style.data.string;
@@ -621,26 +943,24 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
return;
}
}
- api_set_error(err, kErrorTypeValidation,
- "invalid border style \"%s\"", str.data);
+ api_set_error(err, kErrorTypeValidation, "invalid border style \"%s\"", str.data);
}
}
-static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf,
+static bool parse_float_config(Dict(win_config) *config, WinConfig *fconfig, bool reconf,
bool new_win, Error *err)
{
-#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key)
- bool has_relative = false, relative_is_win = false;
- // ignore empty string, to match nvim_win_get_config
- if (HAS_KEY_X(config, relative) && config->relative.size > 0) {
+#define HAS_KEY_X(d, key) HAS_KEY(d, win_config, key)
+ bool has_relative = false, relative_is_win = false, is_split = false;
+ if (config->relative.size > 0) {
if (!parse_float_relative(config->relative, &fconfig->relative)) {
api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key");
return false;
}
- if (!(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) && !HAS_KEY_X(config, bufpos)) {
- api_set_error(err, kErrorTypeValidation,
- "'relative' requires 'row'/'col' or 'bufpos'");
+ if (config->relative.size > 0 && !(HAS_KEY_X(config, row) && HAS_KEY_X(config, col))
+ && !HAS_KEY_X(config, bufpos)) {
+ api_set_error(err, kErrorTypeValidation, "'relative' requires 'row'/'col' or 'bufpos'");
return false;
}
@@ -650,6 +970,32 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
relative_is_win = true;
fconfig->bufpos.lnum = -1;
}
+ } else if (!config->external) {
+ if (HAS_KEY_X(config, vertical) || HAS_KEY_X(config, split)) {
+ is_split = true;
+ } else if (new_win) {
+ api_set_error(err, kErrorTypeValidation,
+ "Must specify 'relative' or 'external' when creating a float");
+ return false;
+ }
+ }
+
+ if (HAS_KEY_X(config, vertical)) {
+ if (!is_split) {
+ api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'vertical'");
+ return false;
+ }
+ }
+
+ if (HAS_KEY_X(config, split)) {
+ if (!is_split) {
+ api_set_error(err, kErrorTypeValidation, "floating windows cannot have 'split'");
+ return false;
+ }
+ if (!parse_config_split(config->split, &fconfig->split)) {
+ api_set_error(err, kErrorTypeValidation, "Invalid value of 'split' key");
+ return false;
+ }
}
if (HAS_KEY_X(config, anchor)) {
@@ -660,7 +1006,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
}
if (HAS_KEY_X(config, row)) {
- if (!has_relative) {
+ if (!has_relative || is_split) {
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'");
return false;
}
@@ -668,7 +1014,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
}
if (HAS_KEY_X(config, col)) {
- if (!has_relative) {
+ if (!has_relative || is_split) {
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'");
return false;
}
@@ -676,7 +1022,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
}
if (HAS_KEY_X(config, bufpos)) {
- if (!has_relative) {
+ if (!has_relative || is_split) {
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'");
return false;
} else {
@@ -701,7 +1047,7 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer");
return false;
}
- } else if (!reconf) {
+ } else if (!reconf && !is_split) {
api_set_error(err, kErrorTypeValidation, "Must specify 'width'");
return false;
}
@@ -713,21 +1059,22 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer");
return false;
}
- } else if (!reconf) {
+ } else if (!reconf && !is_split) {
api_set_error(err, kErrorTypeValidation, "Must specify 'height'");
return false;
}
- if (relative_is_win) {
+ if (relative_is_win || is_split) {
fconfig->window = curwin->handle;
if (HAS_KEY_X(config, win)) {
if (config->win > 0) {
fconfig->window = config->win;
}
}
- } else {
+ } else if (has_relative) {
if (HAS_KEY_X(config, win)) {
- api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'");
+ api_set_error(err, kErrorTypeValidation,
+ "'win' key is only valid with relative='win' and relative=''");
return false;
}
}
@@ -740,23 +1087,20 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
return false;
}
if (fconfig->external && !ui_has(kUIMultigrid)) {
- api_set_error(err, kErrorTypeValidation,
- "UI doesn't support external windows");
+ api_set_error(err, kErrorTypeValidation, "UI doesn't support external windows");
return false;
}
}
- if (!reconf && (!has_relative && !fconfig->external)) {
- api_set_error(err, kErrorTypeValidation,
- "One of 'relative' and 'external' must be used");
- return false;
- }
-
if (HAS_KEY_X(config, focusable)) {
fconfig->focusable = config->focusable;
}
if (HAS_KEY_X(config, zindex)) {
+ if (is_split) {
+ api_set_error(err, kErrorTypeValidation, "non-float cannot have 'zindex'");
+ return false;
+ }
if (config->zindex > 0) {
fconfig->zindex = (int)config->zindex;
} else {
@@ -766,16 +1110,16 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
}
if (HAS_KEY_X(config, title)) {
+ if (is_split) {
+ api_set_error(err, kErrorTypeValidation, "non-float cannot have 'title'");
+ return false;
+ }
// title only work with border
if (!HAS_KEY_X(config, border) && !fconfig->border) {
api_set_error(err, kErrorTypeException, "title requires border to be set");
return false;
}
- if (fconfig->title) {
- clear_virttext(&fconfig->title_chunks);
- }
-
parse_bordertext(config->title, kBorderTextTitle, fconfig, err);
if (ERROR_SET(err)) {
return false;
@@ -793,16 +1137,16 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
}
if (HAS_KEY_X(config, footer)) {
+ if (is_split) {
+ api_set_error(err, kErrorTypeValidation, "non-float cannot have 'footer'");
+ return false;
+ }
// footer only work with border
if (!HAS_KEY_X(config, border) && !fconfig->border) {
api_set_error(err, kErrorTypeException, "footer requires border to be set");
return false;
}
- if (fconfig->footer) {
- clear_virttext(&fconfig->footer_chunks);
- }
-
parse_bordertext(config->footer, kBorderTextFooter, fconfig, err);
if (ERROR_SET(err)) {
return false;
@@ -820,6 +1164,10 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
}
if (HAS_KEY_X(config, border)) {
+ if (is_split) {
+ api_set_error(err, kErrorTypeValidation, "non-float cannot have 'border'");
+ return false;
+ }
parse_border_style(config->border, fconfig, err);
if (ERROR_SET(err)) {
return false;
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index de5b40940f..ed51eedf1b 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -15,11 +15,10 @@
#include "nvim/drawscreen.h"
#include "nvim/eval/window.h"
#include "nvim/ex_docmd.h"
-#include "nvim/func_attr.h"
-#include "nvim/gettext.h"
+#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
-#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/plines.h"
@@ -27,6 +26,10 @@
#include "nvim/types_defs.h"
#include "nvim/window.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "api/window.c.generated.h"
+#endif
+
/// Gets the current buffer in a window
///
/// @param window Window handle, or 0 for current window
@@ -58,7 +61,7 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
if (!win || !buf) {
return;
}
- if (cmdwin_type != 0 && (win == curwin || win == cmdwin_old_curwin || buf == curbuf)) {
+ if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return;
}
@@ -74,15 +77,16 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
/// @param window Window handle, or 0 for current window
/// @param[out] err Error details, if any
/// @return (row, col) tuple
-ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err)
+ArrayOf(Integer, 2) nvim_win_get_cursor(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_cursor.lnum));
- ADD(rv, INTEGER_OBJ(win->w_cursor.col));
+ rv = arena_array(arena, 2);
+ ADD_C(rv, INTEGER_OBJ(win->w_cursor.lnum));
+ ADD_C(rv, INTEGER_OBJ(win->w_cursor.col));
}
return rv;
@@ -234,7 +238,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);
@@ -243,7 +247,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
@@ -261,7 +265,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
@@ -278,7 +282,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.
@@ -286,15 +290,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;
@@ -418,8 +423,7 @@ void nvim_win_close(Window window, Boolean force, Error *err)
/// @param fun Function to call inside the window (currently Lua callable
/// only)
/// @param[out] err Error details, if any
-/// @return Return value of function. NB: will deepcopy Lua values
-/// currently, use upvalues to send Lua references in and out.
+/// @return Return value of function.
Object nvim_win_call(Window window, LuaRef fun, Error *err)
FUNC_API_SINCE(7)
FUNC_API_LUA_ONLY
@@ -432,10 +436,12 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
try_start();
Object res = OBJECT_INIT;
- WIN_EXECUTE(win, tabpage, {
+ win_execute_T win_execute_args;
+ if (win_execute_before(&win_execute_args, win, tabpage)) {
Array args = ARRAY_DICT_INIT;
- res = nlua_call_ref(fun, NULL, args, true, err);
- });
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+ }
+ win_execute_after(&win_execute_args);
try_end(err);
return res;
}
@@ -446,6 +452,7 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
///
/// This takes precedence over the 'winhighlight' option.
///
+/// @param window
/// @param ns_id the namespace to use
/// @param[out] err Error details, if any
void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err)