diff options
Diffstat (limited to 'src/nvim/api/autocmd.c')
-rw-r--r-- | src/nvim/api/autocmd.c | 283 |
1 files changed, 110 insertions, 173 deletions
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index a2cb297b15..36d3e04f54 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -12,6 +12,7 @@ #include "nvim/api/autocmd.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/private/validate.h" #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" @@ -107,25 +108,24 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) break; case kObjectTypeString: group = augroup_find(opts->group.data.string.data); - if (group < 0) { - api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + VALIDATE_S((group >= 0), "group", "", { goto cleanup; - } + }); break; case kObjectTypeInteger: group = (int)opts->group.data.integer; char *name = augroup_name(group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + VALIDATE_S(augroup_exists(name), "group", "", { goto cleanup; - } + }); break; default: - api_set_error(err, kErrorTypeValidation, "group must be a string or an integer."); - goto cleanup; + VALIDATE_S(false, "group (must be string or integer)", "", { + goto cleanup; + }); } - if (opts->event.type != kObjectTypeNil) { + if (HAS_KEY(opts->event)) { check_event = true; Object v = opts->event; @@ -134,57 +134,50 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) event_set[event_nr] = true; } else if (v.type == kObjectTypeArray) { FOREACH_ITEM(v.data.array, event_v, { - if (event_v.type != kObjectTypeString) { - api_set_error(err, - kErrorTypeValidation, - "Every event must be a string in 'event'"); + VALIDATE_T("event item", kObjectTypeString, event_v.type, { goto cleanup; - } + }); GET_ONE_EVENT(event_nr, event_v, cleanup); event_set[event_nr] = true; }) } else { - api_set_error(err, - kErrorTypeValidation, - "Not a valid 'event' value. Must be a string or an array"); - goto cleanup; + VALIDATE_S(false, "event (must be String or Array)", "", { + goto cleanup; + }); } } - if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { - api_set_error(err, kErrorTypeValidation, - "Cannot use both 'pattern' and 'buffer'"); + VALIDATE((!HAS_KEY(opts->pattern) || !HAS_KEY(opts->buffer)), + "%s", "Cannot use both 'pattern' and 'buffer'", { goto cleanup; - } + }); int pattern_filter_count = 0; - if (opts->pattern.type != kObjectTypeNil) { + if (HAS_KEY(opts->pattern)) { Object v = opts->pattern; if (v.type == kObjectTypeString) { pattern_filters[pattern_filter_count] = v.data.string.data; pattern_filter_count += 1; } else if (v.type == kObjectTypeArray) { if (v.data.array.size > AUCMD_MAX_PATTERNS) { - api_set_error(err, kErrorTypeValidation, - "Too many patterns. Please limit yourself to %d or fewer", + api_set_error(err, kErrorTypeValidation, "Too many patterns (maximum of %d)", AUCMD_MAX_PATTERNS); goto cleanup; } FOREACH_ITEM(v.data.array, item, { - if (item.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'pattern': must be a string"); + VALIDATE_T("pattern", kObjectTypeString, item.type, { goto cleanup; - } + }); pattern_filters[pattern_filter_count] = item.data.string.data; pattern_filter_count += 1; }); } else { - api_set_error(err, kErrorTypeValidation, - "Not a valid 'pattern' value. Must be a string or an array"); - goto cleanup; + VALIDATE_EXP(false, "pattern", "String or Array", api_typename(v.type), { + goto cleanup; + }); } } @@ -194,34 +187,33 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) goto cleanup; } - snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal)); + snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); + ADD(buffers, CSTR_TO_OBJ(pattern_buflocal)); } else if (opts->buffer.type == kObjectTypeArray) { if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) { - api_set_error(err, - kErrorTypeValidation, - "Too many buffers. Please limit yourself to %d or fewer", AUCMD_MAX_PATTERNS); + api_set_error(err, kErrorTypeValidation, "Too many buffers (maximum of %d)", + AUCMD_MAX_PATTERNS); goto cleanup; } FOREACH_ITEM(opts->buffer.data.array, bufnr, { - if (bufnr.type != kObjectTypeInteger && bufnr.type != kObjectTypeBuffer) { - api_set_error(err, kErrorTypeValidation, "Invalid value for 'buffer': must be an integer"); + VALIDATE_EXP((bufnr.type == kObjectTypeInteger || bufnr.type == kObjectTypeBuffer), + "buffer", "Integer", api_typename(bufnr.type), { goto cleanup; - } + }); buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err); if (ERROR_SET(err)) { goto cleanup; } - snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal)); + snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); + ADD(buffers, CSTR_TO_OBJ(pattern_buflocal)); + }); + } else if (HAS_KEY(opts->buffer)) { + VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), { + goto cleanup; }); - } else if (opts->buffer.type != kObjectTypeNil) { - api_set_error(err, kErrorTypeValidation, - "Invalid value for 'buffer': must be an integer or array of integers"); - goto cleanup; } FOREACH_ITEM(buffers, bufnr, { @@ -319,7 +311,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) PUT(autocmd_info, "pattern", - STRING_OBJ(cstr_to_string((char *)ap->pat))); + STRING_OBJ(cstr_to_string(ap->pat))); PUT(autocmd_info, "event", @@ -421,10 +413,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc { 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; @@ -432,30 +422,23 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc goto cleanup; } - if (opts->callback.type != kObjectTypeNil && opts->command.type != kObjectTypeNil) { - api_set_error(err, kErrorTypeValidation, "specify either 'callback' or 'command', not both"); + VALIDATE((!HAS_KEY(opts->callback) || !HAS_KEY(opts->command)), + "%s", "Cannot use both 'callback' and 'command'", { goto cleanup; - } else if (opts->callback.type != kObjectTypeNil) { - // TODO(tjdevries): It's possible we could accept callable tables, - // but we don't do that many other places, so for the moment let's - // not do that. + }); + + if (HAS_KEY(opts->callback)) { + // NOTE: We could accept callable tables, but that isn't common in the API. Object *callback = &opts->callback; switch (callback->type) { case kObjectTypeLuaRef: - if (callback->data.luaref == LUA_NOREF) { - api_set_error(err, - kErrorTypeValidation, - "must pass an actual value"); + VALIDATE_S((callback->data.luaref != LUA_NOREF), "callback", "<no value>", { goto cleanup; - } - - if (!nlua_ref_is_function(callback->data.luaref)) { - api_set_error(err, - kErrorTypeValidation, - "must pass a function for callback"); + }); + VALIDATE_S(nlua_ref_is_function(callback->data.luaref), "callback", "<not a function>", { goto cleanup; - } + }); cb.type = kCallbackLua; cb.data.luaref = api_new_luaref(callback->data.luaref); @@ -465,28 +448,25 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc cb.data.funcref = string_to_cstr(callback->data.string); break; default: - api_set_error(err, - kErrorTypeException, - "'callback' must be a lua function or name of vim function"); - goto cleanup; + VALIDATE_EXP(false, "callback", "Lua function or Vim function name", + api_typename(callback->type), { + goto cleanup; + }); } aucmd.type = CALLABLE_CB; aucmd.callable.cb = cb; - } else if (opts->command.type != kObjectTypeNil) { + } else if (HAS_KEY(opts->command)) { Object *command = &opts->command; - if (command->type == kObjectTypeString) { - aucmd.type = CALLABLE_EX; - aucmd.callable.cmd = string_to_cstr(command->data.string); - } else { - api_set_error(err, - kErrorTypeValidation, - "'command' must be a string"); + VALIDATE_T("command", kObjectTypeString, command->type, { goto cleanup; - } + }); + aucmd.type = CALLABLE_EX; + aucmd.callable.cmd = string_to_cstr(command->data.string); } else { - api_set_error(err, kErrorTypeValidation, "must pass one of: 'command', 'callback'"); - goto cleanup; + VALIDATE(false, "%s", "Required: 'command' or 'callback'", { + goto cleanup; + }); } bool is_once = api_object_to_bool(opts->once, "once", false, err); @@ -501,25 +481,20 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc goto cleanup; } - if (opts->desc.type != kObjectTypeNil) { - if (opts->desc.type == kObjectTypeString) { - desc = opts->desc.data.string.data; - } else { - api_set_error(err, - kErrorTypeValidation, - "'desc' must be a string"); + if (HAS_KEY(opts->desc)) { + VALIDATE_T("desc", kObjectTypeString, opts->desc.type, { goto cleanup; - } + }); + desc = opts->desc.data.string.data; } if (patterns.size == 0) { ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*"))); } - if (event_array.size == 0) { - api_set_error(err, kErrorTypeValidation, "'event' is a required key"); + VALIDATE_R((event_array.size > 0), "event", { goto cleanup; - } + }); autocmd_id = next_autocmd_id++; FOREACH_ITEM(event_array, event_str, { @@ -564,10 +539,9 @@ cleanup: void nvim_del_autocmd(Integer id, Error *err) FUNC_API_SINCE(9) { - if (id <= 0) { - api_set_error(err, kErrorTypeException, "Invalid autocmd id"); + VALIDATE_INT((id > 0), "autocmd id", id, { return; - } + }); if (!autocmd_delete_id(id)) { api_set_error(err, kErrorTypeException, "Failed to delete autocmd"); } @@ -610,11 +584,10 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err) goto cleanup; } - if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { - api_set_error(err, kErrorTypeValidation, - "Cannot use both 'pattern' and 'buffer'"); + VALIDATE((!HAS_KEY(opts->pattern) || !HAS_KEY(opts->buffer)), + "%s", "Cannot use both 'pattern' and 'buffer'", { goto cleanup; - } + }); int au_group = get_augroup_from_object(opts->group, err); if (au_group == AUGROUP_ERROR) { @@ -772,32 +745,29 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) break; case kObjectTypeString: au_group = augroup_find(opts->group.data.string.data); - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeValidation, - "invalid augroup: %s", opts->group.data.string.data); + VALIDATE_S((au_group != AUGROUP_ERROR), "group", opts->group.data.string.data, { goto cleanup; - } + }); break; case kObjectTypeInteger: au_group = (int)opts->group.data.integer; char *name = augroup_name(au_group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, { goto cleanup; - } + }); break; default: - api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); - goto cleanup; + VALIDATE_EXP(false, "group", "String or Integer", api_typename(opts->group.type), { + goto cleanup; + }); } - if (opts->buffer.type != kObjectTypeNil) { + if (HAS_KEY(opts->buffer)) { Object buf_obj = opts->buffer; - if (buf_obj.type != kObjectTypeInteger && buf_obj.type != kObjectTypeBuffer) { - api_set_error(err, kErrorTypeException, "invalid buffer: %d", buf_obj.type); + VALIDATE_EXP((buf_obj.type == kObjectTypeInteger || buf_obj.type == kObjectTypeBuffer), + "buffer", "Integer", api_typename(buf_obj.type), { goto cleanup; - } + }); buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err); @@ -814,7 +784,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); } - if (opts->data.type != kObjectTypeNil) { + if (HAS_KEY(opts->data)) { data = &opts->data; } @@ -825,7 +795,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err) GET_ONE_EVENT(event_nr, event_str, cleanup) FOREACH_ITEM(patterns, pat, { - char *fname = opts->buffer.type == kObjectTypeNil ? pat.data.string.data : NULL; + char *fname = !HAS_KEY(opts->buffer) ? pat.data.string.data : NULL; did_aucmd |= apply_autocmds_group(event_nr, fname, NULL, true, au_group, buf, NULL, data); }) @@ -840,45 +810,19 @@ cleanup: api_free_array(patterns); } -static bool check_autocmd_string_array(Array arr, char *k, Error *err) -{ - FOREACH_ITEM(arr, entry, { - if (entry.type != kObjectTypeString) { - api_set_error(err, - kErrorTypeValidation, - "All entries in '%s' must be strings", - k); - return false; - } - - // Disallow newlines in the middle of the line. - const String l = entry.data.string; - if (memchr(l.data, NL, l.size)) { - api_set_error(err, kErrorTypeValidation, - "String cannot contain newlines"); - return false; - } - }) - return true; -} - static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err) { if (v->type == kObjectTypeString) { ADD(*array, copy_object(*v, NULL)); } else if (v->type == kObjectTypeArray) { - if (!check_autocmd_string_array(v->data.array, k, err)) { + if (!check_string_array(v->data.array, k, true, err)) { return false; } *array = copy_array(v->data.array, NULL); } else { - if (required) { - api_set_error(err, - kErrorTypeValidation, - "'%s' must be an array or a string.", - k); + VALIDATE_EXP(!required, k, "Array or String", api_typename(v->type), { return false; - } + }); } return true; @@ -894,27 +838,22 @@ static int get_augroup_from_object(Object group, Error *err) return AUGROUP_DEFAULT; case kObjectTypeString: au_group = augroup_find(group.data.string.data); - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeValidation, - "invalid augroup: %s", group.data.string.data); - + VALIDATE_S((au_group != AUGROUP_ERROR), "group", group.data.string.data, { return AUGROUP_ERROR; - } + }); return au_group; case kObjectTypeInteger: au_group = (int)group.data.integer; char *name = augroup_name(au_group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, { return AUGROUP_ERROR; - } - + }); return au_group; default: - api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); - return AUGROUP_ERROR; + VALIDATE_EXP(false, "group", "String or Integer", api_typename(group.type), { + return AUGROUP_ERROR; + }); } } @@ -923,24 +862,25 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob { const char pattern_buflocal[BUFLOCAL_PAT_LEN]; - if (pattern.type != kObjectTypeNil && buffer.type != kObjectTypeNil) { - api_set_error(err, kErrorTypeValidation, - "cannot pass both: 'pattern' and 'buffer' for the same autocmd"); + VALIDATE((!HAS_KEY(pattern) || !HAS_KEY(buffer)), + "%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", { return false; - } else if (pattern.type != kObjectTypeNil) { + }); + + if (HAS_KEY(pattern)) { Object *v = &pattern; if (v->type == kObjectTypeString) { char *pat = v->data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { - ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen))); pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } } else if (v->type == kObjectTypeArray) { - if (!check_autocmd_string_array(v->data.array, "pattern", err)) { + if (!check_string_array(v->data.array, "pattern", true, err)) { return false; } @@ -949,25 +889,22 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob char *pat = entry.data.string.data; size_t patlen = aucmd_pattern_length(pat); while (patlen) { - ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + ADD(*patterns, STRING_OBJ(cbuf_to_string(pat, patlen))); pat = aucmd_next_pattern(pat, patlen); patlen = aucmd_pattern_length(pat); } }) } else { - api_set_error(err, - kErrorTypeValidation, - "'pattern' must be a string or table"); - return false; + VALIDATE_EXP(false, "pattern", "String or Table", api_typename(v->type), { + return false; + }); } - } else if (buffer.type != kObjectTypeNil) { - if (buffer.type != kObjectTypeInteger && buffer.type != kObjectTypeBuffer) { - api_set_error(err, - kErrorTypeValidation, - "'buffer' must be an integer"); + } else if (HAS_KEY(buffer)) { + VALIDATE_EXP((buffer.type == kObjectTypeInteger || buffer.type == kObjectTypeBuffer), + "buffer", "Integer", api_typename(buffer.type), { return false; - } + }); buf_T *buf = find_buffer_by_handle((Buffer)buffer.data.integer, err); if (ERROR_SET(err)) { @@ -975,7 +912,7 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob } snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); - ADD(*patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal))); + ADD(*patterns, STRING_OBJ(cstr_to_string(pattern_buflocal))); } return true; |