refactor(api): use an arena for mappings

This commit is contained in:
bfredl
2024-02-17 20:31:21 +01:00
parent b12d193b4a
commit f25fcc68a3
16 changed files with 172 additions and 90 deletions

View File

@@ -294,7 +294,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
break; break;
case kCallbackFuncref: case kCallbackFuncref:
case kCallbackPartial: 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; break;
case kCallbackNone: case kCallbackNone:
abort(); abort();

View File

@@ -905,7 +905,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// @param[out] err Error details, if any /// @param[out] err Error details, if any
/// @returns Array of |maparg()|-like dictionaries describing mappings. /// @returns Array of |maparg()|-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle. /// 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) FUNC_API_SINCE(3)
{ {
buf_T *buf = find_buffer_by_handle(buffer, err); 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 (Array)ARRAY_DICT_INIT;
} }
return keymap_array(mode, buf); return keymap_array(mode, buf, arena);
} }
/// Sets a buffer-local |mapping| for the given mode. /// Sets a buffer-local |mapping| for the given mode.

View File

@@ -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)); rv = arena_array(arena, kv_size(marks));
for (size_t i = 0; i < kv_size(marks); i++) { 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); kv_destroy(marks);

View File

@@ -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) void api_free_object(Object value)
{ {
switch (value.type) { switch (value.type) {

View File

@@ -95,6 +95,8 @@
name.capacity = maxsize; \ name.capacity = maxsize; \
name.items = name##__items; \ name.items = name##__items; \
typedef kvec_withinit_t(Object, 16) ArrayBuilder;
#define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) #define cbuf_as_string(d, s) ((String) { .data = d, .size = s })
#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof("" s) - 1 }) #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof("" s) - 1 })

View File

@@ -1456,10 +1456,10 @@ Dictionary nvim_get_mode(Arena *arena)
/// @param mode Mode short-name ("n", "i", "v", ...) /// @param mode Mode short-name ("n", "i", "v", ...)
/// @returns Array of |maparg()|-like dictionaries describing mappings. /// @returns Array of |maparg()|-like dictionaries describing mappings.
/// The "buffer" key is always zero. /// 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) FUNC_API_SINCE(3)
{ {
return keymap_array(mode, NULL); return keymap_array(mode, NULL, arena);
} }
/// Sets a global |mapping| for the given mode. /// Sets a global |mapping| for the given mode.

View File

@@ -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 window Window handle, or 0 for current window
/// @param[out] err Error details, if any /// @param[out] err Error details, if any
/// @return (row, col) tuple /// @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) FUNC_API_SINCE(1)
{ {
Array rv = ARRAY_DICT_INIT; Array rv = ARRAY_DICT_INIT;
win_T *win = find_window_by_handle(window, err); win_T *win = find_window_by_handle(window, err);
if (win) { if (win) {
ADD(rv, INTEGER_OBJ(win->w_cursor.lnum)); rv = arena_array(arena, 2);
ADD(rv, INTEGER_OBJ(win->w_cursor.col)); ADD_C(rv, INTEGER_OBJ(win->w_cursor.lnum));
ADD_C(rv, INTEGER_OBJ(win->w_cursor.col));
} }
return rv; return rv;

View File

@@ -2429,7 +2429,7 @@ char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc)
case CALLABLE_EX: case CALLABLE_EX:
return xstrdup(acc.callable.cmd); return xstrdup(acc.callable.cmd);
case CALLABLE_CB: case CALLABLE_CB:
return callback_to_string(&acc.callable.cb); return callback_to_string(&acc.callable.cb, NULL);
case CALLABLE_NONE: case CALLABLE_NONE:
return "This is not possible"; return "This is not possible";
} }

View File

@@ -374,7 +374,7 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
goto end; goto end;
} }
object_to_vim(result, rettv, &err); object_to_vim_take_luaref(&result, rettv, true, &err);
end: end:
if (!handler.arena_return) { if (!handler.arena_return) {

View File

@@ -1805,10 +1805,10 @@ void callback_copy(Callback *dest, Callback *src)
} }
/// Generate a string description of a callback /// 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) { if (cb->type == kCallbackLua) {
return nlua_funcref_str(cb->data.luaref); return nlua_funcref_str(cb->data.luaref, arena);
} }
const size_t msglen = 100; const size_t msglen = 100;

View File

@@ -2327,11 +2327,9 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
/// String representation of a Lua function reference /// String representation of a Lua function reference
/// ///
/// @return Allocated string /// @return Allocated string
char *nlua_funcref_str(LuaRef ref) char *nlua_funcref_str(LuaRef ref, Arena *arena)
{ {
lua_State *const lstate = global_lstate; lua_State *const lstate = global_lstate;
StringBuilder str = KV_INITIAL_VALUE;
kv_resize(str, 16);
if (!lua_checkstack(lstate, 1)) { if (!lua_checkstack(lstate, 1)) {
goto plain; goto plain;
@@ -2345,14 +2343,13 @@ char *nlua_funcref_str(LuaRef ref)
lua_Debug ar; lua_Debug ar;
if (lua_getinfo(lstate, ">S", &ar) && *ar.source == '@' && ar.linedefined >= 0) { if (lua_getinfo(lstate, ">S", &ar) && *ar.source == '@' && ar.linedefined >= 0) {
char *src = home_replace_save(NULL, ar.source + 1); 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); xfree(src);
return str.items; return str.data;
} }
plain: plain: {}
kv_printf(str, "<Lua %d>", ref); return arena_printf(arena, "<Lua %d>", ref).data;
return str.items;
} }
/// Execute the vim._defaults module to set up default mappings and autocommands /// Execute the vim._defaults module to set up default mappings and autocommands

View File

@@ -160,51 +160,47 @@ static void mapblock_free(mapblock_T **mpp)
xfree(mp); 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. /// @param[out] buf must be at least 7 bytes (including NUL)
static char *map_mode_to_chars(int mode) void map_mode_to_chars(int mode, char *buf)
FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL
{ {
garray_T mapmode; char *p = buf;
ga_init(&mapmode, 1, 7);
if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE)) { if ((mode & (MODE_INSERT | MODE_CMDLINE)) == (MODE_INSERT | MODE_CMDLINE)) {
ga_append(&mapmode, '!'); // :map! *p++ = '!'; // :map!
} else if (mode & MODE_INSERT) { } else if (mode & MODE_INSERT) {
ga_append(&mapmode, 'i'); // :imap *p++ = 'i'; // :imap
} else if (mode & MODE_LANGMAP) { } else if (mode & MODE_LANGMAP) {
ga_append(&mapmode, 'l'); // :lmap *p++ = 'l'; // :lmap
} else if (mode & MODE_CMDLINE) { } 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)) } else if ((mode & (MODE_NORMAL | MODE_VISUAL | MODE_SELECT | MODE_OP_PENDING))
== (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 { } else {
if (mode & MODE_NORMAL) { if (mode & MODE_NORMAL) {
ga_append(&mapmode, 'n'); // :nmap *p++ = 'n'; // :nmap
} }
if (mode & MODE_OP_PENDING) { if (mode & MODE_OP_PENDING) {
ga_append(&mapmode, 'o'); // :omap *p++ = 'o'; // :omap
} }
if (mode & MODE_TERMINAL) { if (mode & MODE_TERMINAL) {
ga_append(&mapmode, 't'); // :tmap *p++ = 't'; // :tmap
} }
if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT)) { if ((mode & (MODE_VISUAL | MODE_SELECT)) == (MODE_VISUAL | MODE_SELECT)) {
ga_append(&mapmode, 'v'); // :vmap *p++ = 'v'; // :vmap
} else { } else {
if (mode & MODE_VISUAL) { if (mode & MODE_VISUAL) {
ga_append(&mapmode, 'x'); // :xmap *p++ = 'x'; // :xmap
} }
if (mode & MODE_SELECT) { if (mode & MODE_SELECT) {
ga_append(&mapmode, 's'); // :smap *p++ = 's'; // :smap
} }
} }
} }
ga_append(&mapmode, NUL); *p = NUL;
return (char *)mapmode.ga_data;
} }
/// @param local true for buffer-local map /// @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); msg_puts(mapchars);
size_t len = strlen(mapchars); size_t len = strlen(mapchars);
xfree(mapchars);
while (++len <= 3) { while (++len <= 3) {
msg_putchar(' '); 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 // 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 // the rhs, and not M-x etc, true gets both -- webb
if (mp->m_luaref != LUA_NOREF) { 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)); msg_puts_attr(str, HL_ATTR(HLF_8));
xfree(str); xfree(str);
} else if (mp->m_str[0] == NUL) { } 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. /// @return A Dictionary.
static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt, 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) FUNC_ATTR_NONNULL_ARG(1)
{ {
Dictionary dict = ARRAY_DICT_INIT; Dictionary dict = arena_dict(arena, 19);
char *const lhs = str2special_save(mp->m_keys, compatible, !compatible); char *const lhs = str2special_arena(mp->m_keys, compatible, !compatible, arena);
char *const mapmode = map_mode_to_chars(mp->m_mode); char *mapmode = arena_alloc(arena, 7, false);
map_mode_to_chars(mp->m_mode, mapmode);
int noremap_value; int noremap_value;
if (compatible) { 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) { 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 { } else {
PUT(dict, "rhs", STRING_OBJ(compatible String rhs = cstr_as_string(compatible
? cstr_to_string(mp->m_orig_str) ? mp->m_orig_str
: cstr_as_string(str2special_save(mp->m_str, false, true)))); : str2special_arena(mp->m_str, false, true, arena));
PUT_C(dict, "rhs", STRING_OBJ(rhs));
} }
if (mp->m_desc != NULL) { 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_C(dict, "lhs", CSTR_AS_OBJ(lhs));
PUT(dict, "lhsraw", CSTR_TO_OBJ(mp->m_keys)); PUT_C(dict, "lhsraw", CSTR_AS_OBJ(mp->m_keys));
if (lhsrawalt != NULL) { if (lhsrawalt != NULL) {
// Also add the value for the simplified entry. // 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_C(dict, "noremap", INTEGER_OBJ(noremap_value));
PUT(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0)); PUT_C(dict, "script", INTEGER_OBJ(mp->m_noremap == REMAP_SCRIPT ? 1 : 0));
PUT(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0)); PUT_C(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0));
PUT(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0)); PUT_C(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0));
PUT(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid)); PUT_C(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid));
PUT(dict, "scriptversion", INTEGER_OBJ(1)); PUT_C(dict, "scriptversion", INTEGER_OBJ(1));
PUT(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum)); PUT_C(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum));
PUT(dict, "buffer", INTEGER_OBJ(buffer_value)); PUT_C(dict, "buffer", INTEGER_OBJ(buffer_value));
PUT(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0)); PUT_C(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0));
if (mp->m_replace_keycodes) { 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_C(dict, "mode", CSTR_AS_OBJ(mapmode));
PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); PUT_C(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0));
PUT(dict, "mode_bits", INTEGER_OBJ(mp->m_mode)); PUT_C(dict, "mode_bits", INTEGER_OBJ(mp->m_mode));
return dict; 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); rettv->vval.v_string = str2special_save(rhs, false, false);
} }
} else if (rhs_lua != LUA_NOREF) { } 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 { } else {
// Return a dictionary. // Return a dictionary.
if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) {
Dictionary dict = mapblock_fill_dict(mp, Arena arena = ARENA_EMPTY;
did_simplify ? keys_simplified : NULL, Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_simplified : NULL,
buffer_local, abbr, true); buffer_local, abbr, true, &arena);
object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL); object_to_vim_take_luaref(&DICTIONARY_OBJ(dict), rettv, true, NULL);
api_free_dictionary(dict); arena_mem_free(arena_finish(&arena));
} else { } else {
// Return an empty dictionary. // Return an empty dictionary.
tv_dict_alloc_ret(rettv); tv_dict_alloc_ret(rettv);
@@ -2391,19 +2390,18 @@ void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
char *keys_buf = NULL; char *keys_buf = NULL;
bool did_simplify = false; 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, replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify,
p_cpo); p_cpo);
xfree(lhs);
Dictionary dict = mapblock_fill_dict(mp, Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_buf : NULL,
did_simplify ? keys_buf : NULL, buffer_local, abbr, true, &arena);
buffer_local, abbr, true);
typval_T d = TV_INITIAL_VALUE; 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); assert(d.v_type == VAR_DICT);
tv_list_append_dict(rettv->vval.v_list, d.vval.v_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); xfree(keys_buf);
} }
} }
@@ -2805,9 +2803,10 @@ fail_and_free:
/// @param mode The abbreviation for the mode /// @param mode The abbreviation for the mode
/// @param buf The buffer to get the mapping array. NULL for global /// @param buf The buffer to get the mapping array. NULL for global
/// @returns Array of maparg()-like dictionaries describing mappings /// @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"; char *p = mode.size > 0 ? mode.data : "m";
bool forceit = *p == '!'; bool forceit = *p == '!';
@@ -2833,12 +2832,11 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
} }
// Check for correct mode // Check for correct mode
if (int_mode & current_maphash->m_mode) { if (int_mode & current_maphash->m_mode) {
ADD(mappings, kvi_push(mappings, DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value,
DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, is_abbrev, false, arena)));
buffer_value, is_abbrev, false)));
} }
} }
} }
return mappings; return arena_take_arraybuilder(arena, &mappings);
} }

View File

@@ -1713,6 +1713,39 @@ char *str2special_save(const char *const str, const bool replace_spaces, const b
return (char *)ga.ga_data; 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. /// Convert character, replacing key with printable representation.
/// ///
/// @param[in,out] sp String to convert. Is advanced to the next key code. /// @param[in,out] sp String to convert. Is advanced to the next key code.

View File

@@ -2166,6 +2166,47 @@ int kv_do_printf(StringBuilder *str, const char *fmt, ...)
return printed; 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. /// Reverse text into allocated memory.
/// ///
/// @return the allocated string. /// @return the allocated string.

View File

@@ -5,6 +5,7 @@
#include "auto/config.h" #include "auto/config.h"
#include "klib/kvec.h" #include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep #include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/func_attr.h" #include "nvim/func_attr.h"
#include "nvim/os/os_defs.h" #include "nvim/os/os_defs.h"

View File

@@ -584,7 +584,7 @@ static void uc_list(char *name, size_t name_len)
msg_outtrans(IObuff, 0); msg_outtrans(IObuff, 0);
if (cmd->uc_luaref != LUA_NOREF) { 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)); msg_puts_attr(fn, HL_ATTR(HLF_8));
xfree(fn); xfree(fn);
// put the description on a new line // put the description on a new line