refactor(api): use arena for autocmds

This commit is contained in:
bfredl 2024-02-17 20:33:58 +01:00
parent 146333ca12
commit 97531be1f7
2 changed files with 102 additions and 117 deletions

View File

@ -23,6 +23,7 @@
#include "nvim/globals.h" #include "nvim/globals.h"
#include "nvim/lua/executor.h" #include "nvim/lua/executor.h"
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h" #include "nvim/types_defs.h"
#include "nvim/vim_defs.h" #include "nvim/vim_defs.h"
@ -33,12 +34,12 @@
#define AUCMD_MAX_PATTERNS 256 #define AUCMD_MAX_PATTERNS 256
// Copy string or array of strings into an empty array. // Copy string or array of strings into an empty array.
// Get the event number, unless it is an error. Then goto `goto_name`. // Get the event number, unless it is an error. Then do `or_else`.
#define GET_ONE_EVENT(event_nr, event_str, goto_name) \ #define GET_ONE_EVENT(event_nr, event_str, or_else) \
event_T event_nr = \ event_T event_nr = \
event_name2nr_str(event_str.data.string); \ event_name2nr_str(event_str.data.string); \
VALIDATE_S((event_nr < NUM_EVENTS), "event", event_str.data.string.data, { \ 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 // 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|: /// If the autocommand is buffer local |autocmd-buffer-local|:
/// - buflocal (boolean): true if the autocommand is buffer local. /// - buflocal (boolean): true if the autocommand is buffer local.
/// - buffer (number): the buffer number. /// - 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) FUNC_API_SINCE(9)
{ {
// TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... }) // 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_filters[AUCMD_MAX_PATTERNS];
char pattern_buflocal[BUFLOCAL_PAT_LEN];
Array buffers = ARRAY_DICT_INIT; Array buffers = ARRAY_DICT_INIT;
@ -131,7 +132,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
Object v = opts->event; Object v = opts->event;
if (v.type == kObjectTypeString) { if (v.type == kObjectTypeString) {
GET_ONE_EVENT(event_nr, v, cleanup); GET_ONE_EVENT(event_nr, v, goto cleanup);
event_set[event_nr] = true; event_set[event_nr] = true;
} else if (v.type == kObjectTypeArray) { } else if (v.type == kObjectTypeArray) {
FOREACH_ITEM(v.data.array, event_v, { FOREACH_ITEM(v.data.array, event_v, {
@ -139,7 +140,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
goto cleanup; goto cleanup;
}); });
GET_ONE_EVENT(event_nr, event_v, cleanup); GET_ONE_EVENT(event_nr, event_v, goto cleanup);
event_set[event_nr] = true; event_set[event_nr] = true;
}) })
} else { } else {
@ -187,8 +188,9 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
goto cleanup; goto cleanup;
} }
snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); String pat = arena_printf(arena, "<buffer=%d>", (int)buf->handle);
ADD(buffers, CSTR_TO_OBJ(pattern_buflocal)); buffers = arena_array(arena, 1);
ADD_C(buffers, STRING_OBJ(pat));
} else if (opts->buffer.type == kObjectTypeArray) { } else if (opts->buffer.type == kObjectTypeArray) {
if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) { if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) {
api_set_error(err, kErrorTypeValidation, "Too many buffers (maximum of %d)", 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; goto cleanup;
} }
buffers = arena_array(arena, kv_size(opts->buffer.data.array));
FOREACH_ITEM(opts->buffer.data.array, bufnr, { FOREACH_ITEM(opts->buffer.data.array, bufnr, {
VALIDATE_EXP((bufnr.type == kObjectTypeInteger || bufnr.type == kObjectTypeBuffer), VALIDATE_EXP((bufnr.type == kObjectTypeInteger || bufnr.type == kObjectTypeBuffer),
"buffer", "Integer", api_typename(bufnr.type), { "buffer", "Integer", api_typename(bufnr.type), {
@ -207,8 +210,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
goto cleanup; goto cleanup;
} }
snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); ADD_C(buffers, STRING_OBJ(arena_printf(arena, "<buffer=%d>", (int)buf->handle)));
ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
}); });
} else if (HAS_KEY(opts, get_autocmds, buffer)) { } else if (HAS_KEY(opts, get_autocmds, buffer)) {
VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), { 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]; char *pat = pattern_filters[j];
int patlen = (int)strlen(pat); int patlen = (int)strlen(pat);
char pattern_buflocal[BUFLOCAL_PAT_LEN];
if (aupat_is_buflocal(pat, patlen)) { if (aupat_is_buflocal(pat, patlen)) {
aupat_normalize_buflocal_pat(pattern_buflocal, pat, patlen, aupat_normalize_buflocal_pat(pattern_buflocal, pat, patlen,
aupat_get_buflocal_nr(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) { if (ap->group != AUGROUP_DEFAULT) {
PUT(autocmd_info, "group", INTEGER_OBJ(ap->group)); PUT_C(autocmd_info, "group", INTEGER_OBJ(ap->group));
PUT(autocmd_info, "group_name", CSTR_TO_OBJ(augroup_name(ap->group))); PUT_C(autocmd_info, "group_name", CSTR_AS_OBJ(augroup_name(ap->group)));
} }
if (ac->id > 0) { 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) { 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) { 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; Callback *cb = &ac->exec.callable.cb;
switch (cb->type) { switch (cb->type) {
case kCallbackLua: case kCallbackLua:
if (nlua_ref_is_function(cb->data.luaref)) { 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; break;
case kCallbackFuncref: case kCallbackFuncref:
case kCallbackPartial: case kCallbackPartial:
PUT(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, NULL))); PUT_C(autocmd_info, "callback", CSTR_AS_OBJ(callback_to_string(cb, arena)));
break; break;
case kCallbackNone: case kCallbackNone:
abort(); abort();
} }
} else { } 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_C(autocmd_info, "pattern", CSTR_AS_OBJ(ap->pat));
PUT(autocmd_info, "event", CSTR_TO_OBJ(event_nr2name(event))); PUT_C(autocmd_info, "event", CSTR_AS_OBJ(event_nr2name(event)));
PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); PUT_C(autocmd_info, "once", BOOLEAN_OBJ(ac->once));
if (ap->buflocal_nr) { if (ap->buflocal_nr) {
PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(true)); PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(true));
PUT(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr)); PUT_C(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr));
} else { } 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 // 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 // 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. // useful to do that at this time.
// //
// PUT(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid)); // PUT_C(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, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum));
ADD(autocmd_list, DICTIONARY_OBJ(autocmd_info)); kvi_push(autocmd_list, DICTIONARY_OBJ(autocmd_info));
} }
} }
cleanup: cleanup:
api_free_array(buffers); return arena_take_arraybuilder(arena, &autocmd_list);
return autocmd_list;
} }
/// Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript /// Creates an |autocommand| event handler, defined by `callback` (Lua function or Vimscript
@ -399,17 +401,16 @@ cleanup:
/// @see |autocommand| /// @see |autocommand|
/// @see |nvim_del_autocmd()| /// @see |nvim_del_autocmd()|
Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts, Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts,
Error *err) Arena *arena, Error *err)
FUNC_API_SINCE(9) FUNC_API_SINCE(9)
{ {
int64_t autocmd_id = -1; int64_t autocmd_id = -1;
char *desc = NULL; char *desc = NULL;
Array patterns = ARRAY_DICT_INIT;
Array event_array = ARRAY_DICT_INIT;
AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT; AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT;
Callback cb = CALLBACK_NONE; 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; goto cleanup;
} }
@ -469,7 +470,9 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup; 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; goto cleanup;
} }
@ -477,17 +480,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
desc = opts->desc.data; desc = opts->desc.data;
} }
if (patterns.size == 0) {
ADD(patterns, STATIC_CSTR_TO_OBJ("*"));
}
VALIDATE_R((event_array.size > 0), "event", { VALIDATE_R((event_array.size > 0), "event", {
goto cleanup; goto cleanup;
}); });
autocmd_id = next_autocmd_id++; autocmd_id = next_autocmd_id++;
FOREACH_ITEM(event_array, event_str, { 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; int retval;
@ -514,8 +513,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
cleanup: cleanup:
aucmd_exec_free(&aucmd); aucmd_exec_free(&aucmd);
api_free_array(event_array);
api_free_array(patterns);
return autocmd_id; return autocmd_id;
} }
@ -555,7 +552,7 @@ void nvim_del_autocmd(Integer id, Error *err)
/// - group: (string|int) The augroup name or id. /// - group: (string|int) The augroup name or id.
/// - NOTE: If not passed, will only delete autocmds *not* in any group. /// - 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) FUNC_API_SINCE(9)
{ {
// TODO(tjdevries): Future improvements: // 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 // - group: Allow passing "*" or true or something like that to force doing all
// autocmds, regardless of their group. // autocmds, regardless of their group.
Array patterns = ARRAY_DICT_INIT; Array event_array = unpack_string_or_array(opts->event, "event", false, arena, err);
Array event_array = ARRAY_DICT_INIT; if (ERROR_SET(err)) {
return;
if (!unpack_string_or_array(&event_array, &opts->event, "event", false, err)) {
goto cleanup;
} }
bool has_buffer = HAS_KEY(opts, clear_autocmds, buffer); bool has_buffer = HAS_KEY(opts, clear_autocmds, buffer);
VALIDATE((!HAS_KEY(opts, clear_autocmds, pattern) || !has_buffer), VALIDATE((!HAS_KEY(opts, clear_autocmds, pattern) || !has_buffer),
"%s", "Cannot use both 'pattern' and 'buffer'", { "%s", "Cannot use both 'pattern' and 'buffer'", {
goto cleanup; return;
}); });
int au_group = get_augroup_from_object(opts->group, err); int au_group = get_augroup_from_object(opts->group, err);
if (au_group == AUGROUP_ERROR) { if (au_group == AUGROUP_ERROR) {
goto cleanup; return;
}
if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) {
goto cleanup;
} }
// When we create the autocmds, we want to say that they are all matched, so that's * // 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 // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL
if (patterns.size == 0) { Array patterns = get_patterns_from_pattern_or_buf(opts->pattern, has_buffer, opts->buffer, "",
ADD(patterns, STATIC_CSTR_TO_OBJ("")); arena, err);
if (ERROR_SET(err)) {
return;
} }
// If we didn't pass any events, that means clear all events. // 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, { FOREACH_ITEM(patterns, pat_object, {
char *pat = pat_object.data.string.data; char *pat = pat_object.data.string.data;
if (!clear_autocmd(event, pat, au_group, err)) { if (!clear_autocmd(event, pat, au_group, err)) {
goto cleanup; return;
} }
}); });
} }
} else { } else {
FOREACH_ITEM(event_array, event_str, { 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, { FOREACH_ITEM(patterns, pat_object, {
char *pat = pat_object.data.string.data; char *pat = pat_object.data.string.data;
if (!clear_autocmd(event_nr, pat, au_group, err)) { 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|. /// 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 /// - data (any): arbitrary data to send to the autocommand callback. See
/// |nvim_create_autocmd()| for details. /// |nvim_create_autocmd()| for details.
/// @see |:doautocmd| /// @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) FUNC_API_SINCE(9)
{ {
int au_group = AUGROUP_ALL; 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; buf_T *buf = curbuf;
Array patterns = ARRAY_DICT_INIT;
Array event_array = ARRAY_DICT_INIT;
Object *data = NULL; Object *data = NULL;
if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { Array event_array = unpack_string_or_array(event, "event", true, arena, err);
goto cleanup; if (ERROR_SET(err)) {
return;
} }
switch (opts->group.type) { switch (opts->group.type) {
@ -732,19 +719,19 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
case kObjectTypeString: case kObjectTypeString:
au_group = augroup_find(opts->group.data.string.data); au_group = augroup_find(opts->group.data.string.data);
VALIDATE_S((au_group != AUGROUP_ERROR), "group", opts->group.data.string.data, { VALIDATE_S((au_group != AUGROUP_ERROR), "group", opts->group.data.string.data, {
goto cleanup; return;
}); });
break; break;
case kObjectTypeInteger: case kObjectTypeInteger:
au_group = (int)opts->group.data.integer; au_group = (int)opts->group.data.integer;
char *name = au_group == 0 ? NULL : augroup_name(au_group); char *name = au_group == 0 ? NULL : augroup_name(au_group);
VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, { VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, {
goto cleanup; return;
}); });
break; break;
default: default:
VALIDATE_EXP(false, "group", "String or Integer", api_typename(opts->group.type), { 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)) { if (HAS_KEY(opts, exec_autocmds, buffer)) {
VALIDATE((!HAS_KEY(opts, exec_autocmds, pattern)), VALIDATE((!HAS_KEY(opts, exec_autocmds, pattern)),
"%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", { "%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", {
goto cleanup; return;
}); });
has_buffer = true; has_buffer = true;
buf = find_buffer_by_handle(opts->buffer, err); buf = find_buffer_by_handle(opts->buffer, err);
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
goto cleanup; return;
} }
} }
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, "",
goto cleanup; arena, err);
} if (ERROR_SET(err)) {
return;
if (patterns.size == 0) {
ADD(patterns, STATIC_CSTR_TO_OBJ(""));
} }
if (HAS_KEY(opts, exec_autocmds, data)) { 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; bool did_aucmd = false;
FOREACH_ITEM(event_array, event_str, { 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, { FOREACH_ITEM(patterns, pat, {
char *fname = !has_buffer ? pat.data.string.data : NULL; 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) { if (did_aucmd && modeline) {
do_modelines(0); 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) { if (v.type == kObjectTypeString) {
ADD(*array, copy_object(*v, NULL)); Array arr = arena_array(arena, 1);
} else if (v->type == kObjectTypeArray) { ADD_C(arr, v);
if (!check_string_array(v->data.array, k, true, err)) { return arr;
return false; } 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 { } else {
VALIDATE_EXP(!required, k, "Array or String", api_typename(v->type), { VALIDATE_EXP(!required, k, "Array or String", api_typename(v.type), {
return false; return (Array)ARRAY_DICT_INIT;
}); });
} }
return true; return (Array)ARRAY_DICT_INIT;
} }
// Returns AUGROUP_ERROR if there was a problem with {group} // 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, static Array get_patterns_from_pattern_or_buf(Object pattern, bool has_buffer, Buffer buffer,
Buffer buffer, Error *err) 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) { if (pattern.type != kObjectTypeNil) {
Object *v = &pattern; if (pattern.type == kObjectTypeString) {
const char *pat = pattern.data.string.data;
if (v->type == kObjectTypeString) {
const char *pat = v->data.string.data;
size_t patlen = aucmd_pattern_length(pat); size_t patlen = aucmd_pattern_length(pat);
while (patlen) { 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); pat = aucmd_next_pattern(pat, patlen);
patlen = aucmd_pattern_length(pat); patlen = aucmd_pattern_length(pat);
} }
} else if (v->type == kObjectTypeArray) { } else if (pattern.type == kObjectTypeArray) {
if (!check_string_array(v->data.array, "pattern", true, err)) { if (!check_string_array(pattern.data.array, "pattern", true, err)) {
return false; return (Array)ARRAY_DICT_INIT;
} }
Array array = v->data.array; Array array = pattern.data.array;
FOREACH_ITEM(array, entry, { FOREACH_ITEM(array, entry, {
const char *pat = entry.data.string.data; const char *pat = entry.data.string.data;
size_t patlen = aucmd_pattern_length(pat); size_t patlen = aucmd_pattern_length(pat);
while (patlen) { 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); pat = aucmd_next_pattern(pat, patlen);
patlen = aucmd_pattern_length(pat); patlen = aucmd_pattern_length(pat);
} }
}) })
} else { } else {
VALIDATE_EXP(false, "pattern", "String or Table", api_typename(v->type), { VALIDATE_EXP(false, "pattern", "String or Table", api_typename(pattern.type), {
return false; return (Array)ARRAY_DICT_INIT;
}); });
} }
} else if (has_buffer) { } else if (has_buffer) {
buf_T *buf = find_buffer_by_handle(buffer, err); buf_T *buf = find_buffer_by_handle(buffer, err);
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
return false; return (Array)ARRAY_DICT_INIT;
} }
snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); kvi_push(patterns, STRING_OBJ(arena_printf(arena, "<buffer=%d>", (int)buf->handle)));
ADD(*patterns, CSTR_TO_OBJ(pattern_buflocal));
} }
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) static bool clear_autocmd(event_T event, char *pat, int au_group, Error *err)

View File

@ -585,7 +585,7 @@ Array arena_take_arraybuilder(Arena *arena, ArrayBuilder *arr)
{ {
Array ret = arena_array(arena, kv_size(*arr)); Array ret = arena_array(arena, kv_size(*arr));
ret.size = kv_size(*arr); ret.size = kv_size(*arr);
memcpy((ret).items, (*arr).items, sizeof((ret).items[0]) * (ret).size); memcpy(ret.items, arr->items, sizeof(ret.items[0]) * ret.size);
kvi_destroy(*arr); kvi_destroy(*arr);
return ret; return ret;
} }