From f25fcc68a34c2d51b0715fadc62cb50509de338b Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 17 Feb 2024 20:31:21 +0100 Subject: [PATCH] refactor(api): use an arena for mappings --- src/nvim/api/autocmd.c | 2 +- src/nvim/api/buffer.c | 4 +- src/nvim/api/extmark.c | 2 +- src/nvim/api/private/helpers.c | 9 +++ src/nvim/api/private/helpers.h | 2 + src/nvim/api/vim.c | 4 +- src/nvim/api/window.c | 7 +- src/nvim/autocmd.c | 2 +- src/nvim/eval/funcs.c | 2 +- src/nvim/eval/typval.c | 4 +- src/nvim/lua/executor.c | 13 ++-- src/nvim/mapping.c | 134 ++++++++++++++++----------------- src/nvim/message.c | 33 ++++++++ src/nvim/strings.c | 41 ++++++++++ src/nvim/strings.h | 1 + src/nvim/usercmd.c | 2 +- 16 files changed, 172 insertions(+), 90 deletions(-) diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 90b7f3a5c6..1b5d1927ad 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -294,7 +294,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) break; case kCallbackFuncref: case kCallbackPartial: - PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb))); + PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, NULL))); break; case kCallbackNone: abort(); 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..3cbf16fa72 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/vim.c b/src/nvim/api/vim.c index 1184eb7441..195da4366e 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. 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/eval/funcs.c b/src/nvim/eval/funcs.c index 28365e16df..f4253df63e 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, "", ref, src, ar.linedefined); + String str = arena_printf(arena, "", ref, src, ar.linedefined); xfree(src); - return str.items; + return str.data; } -plain: - kv_printf(str, "", ref); - return str.items; +plain: {} + return arena_printf(arena, "", ref).data; } /// Execute the vim._defaults module to set up default mappings and autocommands 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 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_C(dict, "lhsrawalt", CSTR_AS_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, "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 ``, normally used for +/// lhs of mapping and keytrans(), but not rhs. +/// @param[in] replace_lt Convert `<` into ``. +/// +/// @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/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/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