diff options
author | bfredl <bjorn.linse@gmail.com> | 2024-02-18 18:04:28 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-18 18:04:28 +0100 |
commit | 93c911e52feadff72003f7e78257c00c3bbf1570 (patch) | |
tree | ff0e65d868b9e30ec4759f13e3c3809f8fd41697 | |
parent | d94adff48b1882f9078b0ae94b677be5d29e5fd2 (diff) | |
parent | 97531be1f766e6cad79e6360ae9cb827434cff3c (diff) | |
download | rneovim-93c911e52feadff72003f7e78257c00c3bbf1570.tar.gz rneovim-93c911e52feadff72003f7e78257c00c3bbf1570.tar.bz2 rneovim-93c911e52feadff72003f7e78257c00c3bbf1570.zip |
Merge pull request #27511 from bfredl/maparena
refactor(api): use arena for mappings, autocmd, channel info
-rw-r--r-- | src/nvim/api/autocmd.c | 217 | ||||
-rw-r--r-- | src/nvim/api/buffer.c | 4 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 2 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 9 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 2 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 2 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 18 | ||||
-rw-r--r-- | src/nvim/api/window.c | 7 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 2 | ||||
-rw-r--r-- | src/nvim/channel.c | 51 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 2 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 4 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 13 | ||||
-rw-r--r-- | src/nvim/main.c | 8 | ||||
-rw-r--r-- | src/nvim/mapping.c | 136 | ||||
-rw-r--r-- | src/nvim/message.c | 33 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 7 | ||||
-rw-r--r-- | src/nvim/os/process.c | 9 | ||||
-rw-r--r-- | src/nvim/strings.c | 41 | ||||
-rw-r--r-- | src/nvim/strings.h | 1 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 40 | ||||
-rw-r--r-- | src/nvim/usercmd.c | 2 |
22 files changed, 340 insertions, 270 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 90b7f3a5c6..7e33a77d24 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -23,6 +23,7 @@ #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" @@ -33,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 @@ -88,14 +89,14 @@ static int64_t next_autocmd_id = 1; /// 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; @@ -131,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, { @@ -139,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 { @@ -187,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)", @@ -196,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), { @@ -207,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), { @@ -250,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)); @@ -267,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 @@ -328,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 @@ -399,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; } @@ -469,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; } @@ -477,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; @@ -514,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; } @@ -555,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: @@ -564,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. @@ -599,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|. @@ -709,7 +698,7 @@ void nvim_del_augroup_by_name(String name, Error *err) /// - 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; @@ -717,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) { @@ -732,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; }); } @@ -752,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)) { @@ -779,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; @@ -790,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} @@ -843,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 257e5e6b05..ddca85945a 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -905,7 +905,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// @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); @@ -914,7 +914,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. diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 94f6059014..f6f7d332ec 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -346,7 +346,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e 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, arena))); + ADD_C(rv, ARRAY_OBJ(extmark_to_array(kv_A(marks, i), true, details, hl_name, arena))); } kv_destroy(marks); diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 1446683b0c..983f1a4fed 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -581,6 +581,15 @@ String arena_string(Arena *arena, String str) } } +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) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 395c5a9d1f..20bc889a0a 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -95,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 }) diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 140b520cb5..d5843caa96 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -1046,7 +1046,7 @@ static Array translate_firstarg(UI *ui, Array args, Arena *arena) 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; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1184eb7441..7a19ccf295 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1456,10 +1456,10 @@ Dictionary nvim_get_mode(Arena *arena) /// @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. @@ -1633,7 +1633,7 @@ 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(uint64_t channel_id, 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) { @@ -1644,17 +1644,17 @@ Dictionary nvim_get_chan_info(uint64_t channel_id, Integer chan, Error *err) assert(channel_id <= INT64_MAX); chan = (Integer)channel_id; } - return channel_info((uint64_t)chan); + 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. @@ -1891,7 +1891,7 @@ Array nvim_get_proc_children(Integer pid, Arena *arena, 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)); + 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; @@ -1903,7 +1903,7 @@ Array nvim_get_proc_children(Integer pid, Arena *arena, Error *err) } else { rvobj = arena_array(arena, proc_count); for (size_t i = 0; i < proc_count; i++) { - ADD(rvobj, INTEGER_OBJ(proc_list[i])); + ADD_C(rvobj, INTEGER_OBJ(proc_list[i])); } } @@ -1925,7 +1925,7 @@ Object nvim_get_proc(Integer pid, Arena *arena, Error *err) }); #ifdef MSWIN - rvobj = DICTIONARY_OBJ(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; } diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index ed1ad5b583..c41c5d4b07 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -77,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; diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 3b747e0920..3f93906942 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2429,7 +2429,7 @@ char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc) case CALLABLE_EX: return xstrdup(acc.callable.cmd); case CALLABLE_CB: - return callback_to_string(&acc.callable.cb); + return callback_to_string(&acc.callable.cb, NULL); case CALLABLE_NONE: return "This is not possible"; } diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 1dc1208696..ebeaffe5a1 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -238,7 +238,8 @@ void channel_create_event(Channel *chan, const char *ext_source) } assert(chan->id <= VARNUMBER_MAX); - Dictionary info = channel_info(chan->id); + Arena arena = ARENA_EMPTY; + Dictionary info = channel_info(chan->id, &arena); typval_T tv = TV_INITIAL_VALUE; // TODO(bfredl): do the conversion in one step. Also would be nice // to pretty print top level dict in defined order @@ -247,7 +248,7 @@ void channel_create_event(Channel *chan, const char *ext_source) char *str = encode_tv2json(&tv, NULL); ILOG("new channel %" PRIu64 " (%s) : %s", chan->id, source, str); xfree(str); - api_free_dictionary(info); + arena_mem_free(arena_finish(&arena)); #else (void)ext_source; @@ -876,7 +877,8 @@ static void set_info_event(void **argv) save_v_event_T save_v_event; dict_T *dict = get_v_event(&save_v_event); - Dictionary info = channel_info(chan->id); + Arena arena = ARENA_EMPTY; + Dictionary info = channel_info(chan->id, &arena); typval_T retval; object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); assert(retval.v_type == VAR_DICT); @@ -886,7 +888,7 @@ static void set_info_event(void **argv) apply_autocmds(event, NULL, NULL, false, curbuf); restore_v_event(dict, &save_v_event); - api_free_dictionary(info); + arena_mem_free(arena_finish(&arena)); channel_decref(chan); } @@ -898,15 +900,15 @@ bool channel_job_running(uint64_t id) && !process_is_stopped(&chan->stream.proc)); } -Dictionary channel_info(uint64_t id) +Dictionary channel_info(uint64_t id, Arena *arena) { Channel *chan = find_channel(id); if (!chan) { return (Dictionary)ARRAY_DICT_INIT; } - Dictionary info = ARRAY_DICT_INIT; - PUT(info, "id", INTEGER_OBJ((Integer)chan->id)); + Dictionary info = arena_dict(arena, 8); + PUT_C(info, "id", INTEGER_OBJ((Integer)chan->id)); const char *stream_desc, *mode_desc; switch (chan->streamtype) { @@ -914,18 +916,20 @@ Dictionary channel_info(uint64_t id) stream_desc = "job"; if (chan->stream.proc.type == kProcessTypePty) { const char *name = pty_process_tty_name(&chan->stream.pty); - PUT(info, "pty", CSTR_TO_OBJ(name)); + PUT_C(info, "pty", CSTR_TO_ARENA_OBJ(arena, name)); } - char **p = chan->stream.proc.argv; + char **args = chan->stream.proc.argv; Array argv = ARRAY_DICT_INIT; - if (p != NULL) { - while (*p != NULL) { - ADD(argv, CSTR_TO_OBJ(*p)); - p++; + if (args != NULL) { + size_t n; + for (n = 0; args[n] != NULL; n++) {} + argv = arena_array(arena, n); + for (size_t i = 0; i < n; i++) { + ADD_C(argv, CSTR_AS_OBJ(args[i])); } } - PUT(info, "argv", ARRAY_OBJ(argv)); + PUT_C(info, "argv", ARRAY_OBJ(argv)); break; } @@ -938,25 +942,25 @@ Dictionary channel_info(uint64_t id) break; case kChannelStreamInternal: - PUT(info, "internal", BOOLEAN_OBJ(true)); + PUT_C(info, "internal", BOOLEAN_OBJ(true)); FALLTHROUGH; case kChannelStreamSocket: stream_desc = "socket"; break; } - PUT(info, "stream", CSTR_TO_OBJ(stream_desc)); + PUT_C(info, "stream", CSTR_AS_OBJ(stream_desc)); if (chan->is_rpc) { mode_desc = "rpc"; - PUT(info, "client", DICTIONARY_OBJ(rpc_client_info(chan))); + PUT_C(info, "client", DICTIONARY_OBJ(chan->rpc.info)); } else if (chan->term) { mode_desc = "terminal"; - PUT(info, "buffer", BUFFER_OBJ(terminal_buf(chan->term))); + PUT_C(info, "buffer", BUFFER_OBJ(terminal_buf(chan->term))); } else { mode_desc = "bytes"; } - PUT(info, "mode", CSTR_TO_OBJ(mode_desc)); + PUT_C(info, "mode", CSTR_AS_OBJ(mode_desc)); return info; } @@ -969,21 +973,20 @@ static int int64_t_cmp(const void *pa, const void *pb) return a == b ? 0 : a > b ? 1 : -1; } -Array channel_all_info(void) +Array channel_all_info(Arena *arena) { // order the items in the array by channel number, for Determinismâ„¢ kvec_t(int64_t) ids = KV_INITIAL_VALUE; - kv_resize(ids, map_size(&channels)); + kv_fixsize_arena(arena, ids, map_size(&channels)); uint64_t id; map_foreach_key(&channels, id, { kv_push(ids, (int64_t)id); }); qsort(ids.items, ids.size, sizeof ids.items[0], int64_t_cmp); - Array ret = ARRAY_DICT_INIT; + Array ret = arena_array(arena, ids.size); for (size_t i = 0; i < ids.size; i++) { - ADD(ret, DICTIONARY_OBJ(channel_info((uint64_t)ids.items[i]))); + ADD_C(ret, DICTIONARY_OBJ(channel_info((uint64_t)ids.items[i], arena))); } - kv_destroy(ids); return ret; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8dbff8a7a6..e5af50c8f8 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -374,7 +374,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto end; } - object_to_vim(result, rettv, &err); + object_to_vim_take_luaref(&result, rettv, true, &err); end: if (!handler.arena_return) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index fb9e7ff9a8..9328f53dbd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1805,10 +1805,10 @@ void callback_copy(Callback *dest, Callback *src) } /// Generate a string description of a callback -char *callback_to_string(Callback *cb) +char *callback_to_string(Callback *cb, Arena *arena) { if (cb->type == kCallbackLua) { - return nlua_funcref_str(cb->data.luaref); + return nlua_funcref_str(cb->data.luaref, arena); } const size_t msglen = 100; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index db44288b16..58f329b76f 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -2327,11 +2327,9 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview) /// String representation of a Lua function reference /// /// @return Allocated string -char *nlua_funcref_str(LuaRef ref) +char *nlua_funcref_str(LuaRef ref, Arena *arena) { lua_State *const lstate = global_lstate; - StringBuilder str = KV_INITIAL_VALUE; - kv_resize(str, 16); if (!lua_checkstack(lstate, 1)) { goto plain; @@ -2345,14 +2343,13 @@ char *nlua_funcref_str(LuaRef ref) lua_Debug ar; if (lua_getinfo(lstate, ">S", &ar) && *ar.source == '@' && ar.linedefined >= 0) { char *src = home_replace_save(NULL, ar.source + 1); - kv_printf(str, "<Lua %d: %s:%d>", ref, src, ar.linedefined); + String str = arena_printf(arena, "<Lua %d: %s:%d>", ref, src, ar.linedefined); xfree(src); - return str.items; + return str.data; } -plain: - kv_printf(str, "<Lua %d>", ref); - return str.items; +plain: {} + return arena_printf(arena, "<Lua %d>", ref).data; } /// Execute the vim._defaults module to set up default mappings and autocommands diff --git a/src/nvim/main.c b/src/nvim/main.c index 1c4457ed9a..e18c561879 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -947,10 +947,10 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, Error err = ERROR_INIT; MAXSIZE_TEMP_ARRAY(a, 4); - ADD(a, INTEGER_OBJ((int)chan)); - ADD(a, CSTR_AS_OBJ(server_addr)); - ADD(a, CSTR_AS_OBJ(connect_error)); - ADD(a, ARRAY_OBJ(args)); + ADD_C(a, INTEGER_OBJ((int)chan)); + ADD_C(a, CSTR_AS_OBJ(server_addr)); + ADD_C(a, CSTR_AS_OBJ(connect_error)); + ADD_C(a, ARRAY_OBJ(args)); String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); Object o = nlua_exec(s, a, kRetObject, NULL, &err); kv_destroy(args); diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 963f65148d..43a4c10ea7 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -160,51 +160,47 @@ static void mapblock_free(mapblock_T **mpp) xfree(mp); } -/// Return characters to represent the map mode in an allocated string +/// put characters to represent the map mode in a string buffer /// -/// @return [allocated] NUL-terminated string with characters. -static char *map_mode_to_chars(int mode) - FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET +/// @param[out] buf must be at least 7 bytes (including NUL) +void map_mode_to_chars(int mode, char *buf) + FUNC_ATTR_NONNULL_ALL { - garray_T mapmode; - - ga_init(&mapmode, 1, 7); - + char *p = buf; if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE)) { - ga_append(&mapmode, '!'); // :map! + *p++ = '!'; // :map! } else if (mode & MODE_INSERT) { - ga_append(&mapmode, 'i'); // :imap + *p++ = 'i'; // :imap } else if (mode & MODE_LANGMAP) { - ga_append(&mapmode, 'l'); // :lmap + *p++ = 'l'; // :lmap } else if (mode & MODE_CMDLINE) { - ga_append(&mapmode, 'c'); // :cmap + *p++ = 'c'; // :cmap } else if ((mode & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING)) == (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING)) { - ga_append(&mapmode, ' '); // :map + *p++ = ' '; // :map } else { if (mode & MODE_NORMAL) { - ga_append(&mapmode, 'n'); // :nmap + *p++ = 'n'; // :nmap } if (mode & MODE_OP_PENDING) { - ga_append(&mapmode, 'o'); // :omap + *p++ = 'o'; // :omap } if (mode & MODE_TERMINAL) { - ga_append(&mapmode, 't'); // :tmap + *p++ = 't'; // :tmap } if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT)) { - ga_append(&mapmode, 'v'); // :vmap + *p++ = 'v'; // :vmap } else { if (mode & MODE_VISUAL) { - ga_append(&mapmode, 'x'); // :xmap + *p++ = 'x'; // :xmap } if (mode & MODE_SELECT) { - ga_append(&mapmode, 's'); // :smap + *p++ = 's'; // :smap } } } - ga_append(&mapmode, NUL); - return (char *)mapmode.ga_data; + *p = NUL; } /// @param local true for buffer-local map @@ -223,10 +219,10 @@ static void showmap(mapblock_T *mp, bool local) } } - char *const mapchars = map_mode_to_chars(mp->m_mode); + char mapchars[7]; + map_mode_to_chars(mp->m_mode, mapchars); msg_puts(mapchars); size_t len = strlen(mapchars); - xfree(mapchars); while (++len <= 3) { msg_putchar(' '); @@ -256,7 +252,7 @@ static void showmap(mapblock_T *mp, bool local) // Use false below if we only want things like <Up> to show up as such on // the rhs, and not M-x etc, true gets both -- webb if (mp->m_luaref != LUA_NOREF) { - char *str = nlua_funcref_str(mp->m_luaref); + char *str = nlua_funcref_str(mp->m_luaref, NULL); msg_puts_attr(str, HL_ATTR(HLF_8)); xfree(str); } else if (mp->m_str[0] == NUL) { @@ -2070,12 +2066,14 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @return A Dictionary. static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt, - const int buffer_value, const bool abbr, const bool compatible) + const int buffer_value, const bool abbr, const bool compatible, + Arena *arena) FUNC_ATTR_NONNULL_ARG(1) { - Dictionary dict = ARRAY_DICT_INIT; - char *const lhs = str2special_save(mp->m_keys, compatible, !compatible); - char *const mapmode = map_mode_to_chars(mp->m_mode); + Dictionary dict = arena_dict(arena, 19); + char *const lhs = str2special_arena(mp->m_keys, compatible, !compatible, arena); + char *mapmode = arena_alloc(arena, 7, false); + map_mode_to_chars(mp->m_mode, mapmode); int noremap_value; if (compatible) { @@ -2089,36 +2087,37 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs } if (mp->m_luaref != LUA_NOREF) { - PUT(dict, "callback", LUAREF_OBJ(api_new_luaref(mp->m_luaref))); + PUT_C(dict, "callback", LUAREF_OBJ(api_new_luaref(mp->m_luaref))); } else { - PUT(dict, "rhs", STRING_OBJ(compatible - ? cstr_to_string(mp->m_orig_str) - : cstr_as_string(str2special_save(mp->m_str, false, true)))); + String rhs = cstr_as_string(compatible + ? mp->m_orig_str + : str2special_arena(mp->m_str, false, true, arena)); + PUT_C(dict, "rhs", STRING_OBJ(rhs)); } if (mp->m_desc != NULL) { - PUT(dict, "desc", CSTR_TO_OBJ(mp->m_desc)); + PUT_C(dict, "desc", CSTR_AS_OBJ(mp->m_desc)); } - PUT(dict, "lhs", CSTR_AS_OBJ(lhs)); - PUT(dict, "lhsraw", CSTR_TO_OBJ(mp->m_keys)); + PUT_C(dict, "lhs", CSTR_AS_OBJ(lhs)); + PUT_C(dict, "lhsraw", CSTR_AS_OBJ(mp->m_keys)); if (lhsrawalt != NULL) { // Also add the value for the simplified entry. - PUT(dict, "lhsrawalt", CSTR_TO_OBJ(lhsrawalt)); - } - PUT(dict, "noremap", INTEGER_OBJ(noremap_value)); - PUT(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); - PUT(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0)); - PUT(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0)); - PUT(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid)); - PUT(dict, "scriptversion", INTEGER_OBJ(1)); - PUT(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum)); - PUT(dict, "buffer", INTEGER_OBJ(buffer_value)); - PUT(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0)); + PUT_C(dict, "lhsrawalt", CSTR_AS_OBJ(lhsrawalt)); + } + PUT_C(dict, "noremap", INTEGER_OBJ(noremap_value)); + PUT_C(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); + PUT_C(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0)); + PUT_C(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0)); + PUT_C(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid)); + PUT_C(dict, "scriptversion", INTEGER_OBJ(1)); + PUT_C(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum)); + PUT_C(dict, "buffer", INTEGER_OBJ(buffer_value)); + PUT_C(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0)); if (mp->m_replace_keycodes) { - PUT(dict, "replace_keycodes", INTEGER_OBJ(1)); + PUT_C(dict, "replace_keycodes", INTEGER_OBJ(1)); } - PUT(dict, "mode", CSTR_AS_OBJ(mapmode)); - PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); - PUT(dict, "mode_bits", INTEGER_OBJ(mp->m_mode)); + PUT_C(dict, "mode", CSTR_AS_OBJ(mapmode)); + PUT_C(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); + PUT_C(dict, "mode_bits", INTEGER_OBJ(mp->m_mode)); return dict; } @@ -2184,16 +2183,16 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) rettv->vval.v_string = str2special_save(rhs, false, false); } } else if (rhs_lua != LUA_NOREF) { - rettv->vval.v_string = nlua_funcref_str(mp->m_luaref); + rettv->vval.v_string = nlua_funcref_str(mp->m_luaref, NULL); } } else { // Return a dictionary. if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { - Dictionary dict = mapblock_fill_dict(mp, - did_simplify ? keys_simplified : NULL, - buffer_local, abbr, true); - object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL); - api_free_dictionary(dict); + Arena arena = ARENA_EMPTY; + Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_simplified : NULL, + buffer_local, abbr, true, &arena); + object_to_vim_take_luaref(&DICTIONARY_OBJ(dict), rettv, true, NULL); + arena_mem_free(arena_finish(&arena)); } else { // Return an empty dictionary. tv_dict_alloc_ret(rettv); @@ -2391,19 +2390,18 @@ void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *keys_buf = NULL; bool did_simplify = false; - char *lhs = str2special_save(mp->m_keys, true, false); + Arena arena = ARENA_EMPTY; + char *lhs = str2special_arena(mp->m_keys, true, false, &arena); replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify, p_cpo); - xfree(lhs); - Dictionary dict = mapblock_fill_dict(mp, - did_simplify ? keys_buf : NULL, - buffer_local, abbr, true); + Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_buf : NULL, + buffer_local, abbr, true, &arena); typval_T d = TV_INITIAL_VALUE; - object_to_vim(DICTIONARY_OBJ(dict), &d, NULL); + object_to_vim_take_luaref(&DICTIONARY_OBJ(dict), &d, true, NULL); assert(d.v_type == VAR_DICT); tv_list_append_dict(rettv->vval.v_list, d.vval.v_dict); - api_free_dictionary(dict); + arena_mem_free(arena_finish(&arena)); xfree(keys_buf); } } @@ -2805,9 +2803,10 @@ fail_and_free: /// @param mode The abbreviation for the mode /// @param buf The buffer to get the mapping array. NULL for global /// @returns Array of maparg()-like dictionaries describing mappings -ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf) +ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, Arena *arena) { - Array mappings = ARRAY_DICT_INIT; + ArrayBuilder mappings = KV_INITIAL_VALUE; + kvi_init(mappings); char *p = mode.size > 0 ? mode.data : "m"; bool forceit = *p == '!'; @@ -2833,12 +2832,11 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf) } // Check for correct mode if (int_mode & current_maphash->m_mode) { - ADD(mappings, - DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, - buffer_value, is_abbrev, false))); + kvi_push(mappings, DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value, + is_abbrev, false, arena))); } } } - return mappings; + return arena_take_arraybuilder(arena, &mappings); } diff --git a/src/nvim/message.c b/src/nvim/message.c index b4e1d61cc7..175e3b5d03 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1713,6 +1713,39 @@ char *str2special_save(const char *const str, const bool replace_spaces, const b return (char *)ga.ga_data; } +/// Convert string, replacing key codes with printables +/// +/// Used for lhs or rhs of mappings. +/// +/// @param[in] str String to convert. +/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used for +/// lhs of mapping and keytrans(), but not rhs. +/// @param[in] replace_lt Convert `<` into `<lt>`. +/// +/// @return [allocated] Converted string. +char *str2special_arena(const char *str, bool replace_spaces, bool replace_lt, Arena *arena) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_RET +{ + const char *p = str; + size_t len = 0; + while (*p) { + len += strlen(str2special(&p, replace_spaces, replace_lt)); + } + + char *buf = arena_alloc(arena, len + 1, false); + size_t pos = 0; + p = str; + while (*p) { + const char *s = str2special(&p, replace_spaces, replace_lt); + size_t s_len = strlen(s); + memcpy(buf + pos, s, s_len); + pos += s_len; + } + buf[pos] = NUL; + return buf; +} + /// Convert character, replacing key with printable representation. /// /// @param[in,out] sp String to convert. Is advanced to the next key code. diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index b411854387..1208c91760 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -682,12 +682,11 @@ static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler semsg("paste: %s", err->msg); api_clear_error(err); } else { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(err->type)); - ADD(args, CSTR_TO_OBJ(err->msg)); + MAXSIZE_TEMP_ARRAY(args, 2); + ADD_C(args, INTEGER_OBJ(err->type)); + ADD_C(args, CSTR_AS_OBJ(err->msg)); msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"), args, &pac); - api_free_array(args); } } else { msgpack_rpc_serialize_response(response_id, err, arg, &pac); diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 5263451488..e8d38d5b8a 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -230,7 +230,7 @@ int os_proc_children(int ppid, int **proc_list, size_t *proc_count) /// /// @param pid Process to inspect. /// @return Map of process properties, empty on error. -Dictionary os_proc_info(int pid) +Dictionary os_proc_info(int pid, Arena *arena) { Dictionary pinfo = ARRAY_DICT_INIT; PROCESSENTRY32 pe; @@ -258,9 +258,10 @@ Dictionary os_proc_info(int pid) CloseHandle(h); if (pe.th32ProcessID == (DWORD)pid) { - PUT(pinfo, "pid", INTEGER_OBJ(pid)); - PUT(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); - PUT(pinfo, "name", CSTR_TO_OBJ(pe.szExeFile)); + pinfo = arena_dict(arena, 3); + PUT_C(pinfo, "pid", INTEGER_OBJ(pid)); + PUT_C(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); + PUT_C(pinfo, "name", CSTR_TO_ARENA_OBJ(arena, pe.szExeFile)); } return pinfo; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index c16b1f4ad0..06622a86b7 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -2166,6 +2166,47 @@ int kv_do_printf(StringBuilder *str, const char *fmt, ...) return printed; } +String arena_printf(Arena *arena, const char *fmt, ...) + FUNC_ATTR_PRINTF(2, 3) +{ + size_t remaining = 0; + char *buf = NULL; + if (arena) { + if (!arena->cur_blk) { + alloc_block(arena); + } + + // happy case, we can fit the printed string in the rest of the current + // block (one pass): + remaining = arena->size - arena->pos; + buf = arena->cur_blk + arena->pos; + } + + va_list ap; + va_start(ap, fmt); + int printed = vsnprintf(buf, remaining, fmt, ap); + va_end(ap); + + if (printed < 0) { + return (String)STRING_INIT; + } + + // printed string didn't fit, allocate and try again + if ((size_t)printed >= remaining) { + buf = arena_alloc(arena, (size_t)printed + 1, false); + va_start(ap, fmt); + printed = vsnprintf(buf, (size_t)printed + 1, fmt, ap); + va_end(ap); + if (printed < 0) { + return (String)STRING_INIT; + } + } else { + arena->pos += (size_t)printed + 1; + } + + return cbuf_as_string(buf, (size_t)printed); +} + /// Reverse text into allocated memory. /// /// @return the allocated string. diff --git a/src/nvim/strings.h b/src/nvim/strings.h index 8478676f13..903559b062 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -5,6 +5,7 @@ #include "auto/config.h" #include "klib/kvec.h" +#include "nvim/api/private/defs.h" #include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/func_attr.h" #include "nvim/os/os_defs.h" diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 5565bd1055..7fae34d33f 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1434,28 +1434,28 @@ static void show_verbose_terminfo(TUIData *tui) abort(); } - Array chunks = ARRAY_DICT_INIT; - Array title = ARRAY_DICT_INIT; - ADD(title, CSTR_TO_OBJ("\n\n--- Terminal info --- {{{\n")); - ADD(title, CSTR_TO_OBJ("Title")); - ADD(chunks, ARRAY_OBJ(title)); - Array info = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(chunks, 3); + MAXSIZE_TEMP_ARRAY(title, 2); + ADD_C(title, CSTR_AS_OBJ("\n\n--- Terminal info --- {{{\n")); + ADD_C(title, CSTR_AS_OBJ("Title")); + ADD_C(chunks, ARRAY_OBJ(title)); + MAXSIZE_TEMP_ARRAY(info, 2); String str = terminfo_info_msg(ut, tui->term); - ADD(info, STRING_OBJ(str)); - ADD(chunks, ARRAY_OBJ(info)); - Array end_fold = ARRAY_DICT_INIT; - ADD(end_fold, CSTR_TO_OBJ("}}}\n")); - ADD(end_fold, CSTR_TO_OBJ("Title")); - ADD(chunks, ARRAY_OBJ(end_fold)); - - Array args = ARRAY_DICT_INIT; - ADD(args, ARRAY_OBJ(chunks)); - ADD(args, BOOLEAN_OBJ(true)); // history - Dictionary opts = ARRAY_DICT_INIT; - PUT(opts, "verbose", BOOLEAN_OBJ(true)); - ADD(args, DICTIONARY_OBJ(opts)); + ADD_C(info, STRING_OBJ(str)); + ADD_C(chunks, ARRAY_OBJ(info)); + MAXSIZE_TEMP_ARRAY(end_fold, 2); + ADD_C(end_fold, CSTR_AS_OBJ("}}}\n")); + ADD_C(end_fold, CSTR_AS_OBJ("Title")); + ADD_C(chunks, ARRAY_OBJ(end_fold)); + + MAXSIZE_TEMP_ARRAY(args, 3); + ADD_C(args, ARRAY_OBJ(chunks)); + ADD_C(args, BOOLEAN_OBJ(true)); // history + MAXSIZE_TEMP_DICT(opts, 1); + PUT_C(opts, "verbose", BOOLEAN_OBJ(true)); + ADD_C(args, DICTIONARY_OBJ(opts)); rpc_send_event(ui_client_channel_id, "nvim_echo", args); - api_free_array(args); + xfree(str.data); } void tui_suspend(TUIData *tui) diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c index 729e57cb2b..c4e04159ef 100644 --- a/src/nvim/usercmd.c +++ b/src/nvim/usercmd.c @@ -584,7 +584,7 @@ static void uc_list(char *name, size_t name_len) msg_outtrans(IObuff, 0); if (cmd->uc_luaref != LUA_NOREF) { - char *fn = nlua_funcref_str(cmd->uc_luaref); + char *fn = nlua_funcref_str(cmd->uc_luaref, NULL); msg_puts_attr(fn, HL_ATTR(HLF_8)); xfree(fn); // put the description on a new line |