mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
refactor(api): VALIDATE macros #22187
Problem: - API validation involves too much boilerplate. - API validation errors are not consistently worded. Solution: Introduce some macros. Currently these are clumsy, but they at least help with consistency and avoid some nesting.
This commit is contained in:
parent
5396808267
commit
46a87a5d2b
@ -12,6 +12,7 @@
|
|||||||
#include "nvim/api/autocmd.h"
|
#include "nvim/api/autocmd.h"
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/buffer.h"
|
#include "nvim/buffer.h"
|
||||||
@ -107,22 +108,21 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
|||||||
break;
|
break;
|
||||||
case kObjectTypeString:
|
case kObjectTypeString:
|
||||||
group = augroup_find(opts->group.data.string.data);
|
group = augroup_find(opts->group.data.string.data);
|
||||||
if (group < 0) {
|
VALIDATE_S((group >= 0), "group", "", {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid augroup passed.");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
break;
|
break;
|
||||||
case kObjectTypeInteger:
|
case kObjectTypeInteger:
|
||||||
group = (int)opts->group.data.integer;
|
group = (int)opts->group.data.integer;
|
||||||
char *name = augroup_name(group);
|
char *name = augroup_name(group);
|
||||||
if (!augroup_exists(name)) {
|
VALIDATE_S(augroup_exists(name), "group", "", {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid augroup passed.");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
api_set_error(err, kErrorTypeValidation, "group must be a string or an integer.");
|
VALIDATE_S(false, "group (must be string or integer)", "", {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->event.type != kObjectTypeNil) {
|
if (opts->event.type != kObjectTypeNil) {
|
||||||
@ -134,29 +134,24 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
|||||||
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, {
|
||||||
if (event_v.type != kObjectTypeString) {
|
VALIDATE_T("event item", kObjectTypeString, event_v.type, {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"Every event must be a string in 'event'");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
|
||||||
GET_ONE_EVENT(event_nr, event_v, cleanup);
|
GET_ONE_EVENT(event_nr, event_v, cleanup);
|
||||||
event_set[event_nr] = true;
|
event_set[event_nr] = true;
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err,
|
VALIDATE_S(false, "event (must be String or Array)", "", {
|
||||||
kErrorTypeValidation,
|
goto cleanup;
|
||||||
"Not a valid 'event' value. Must be a string or an array");
|
});
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) {
|
VALIDATE((opts->pattern.type == kObjectTypeNil || opts->buffer.type == kObjectTypeNil),
|
||||||
api_set_error(err, kErrorTypeValidation,
|
"Cannot use both 'pattern' and 'buffer'", {
|
||||||
"Cannot use both 'pattern' and 'buffer'");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
|
||||||
int pattern_filter_count = 0;
|
int pattern_filter_count = 0;
|
||||||
if (opts->pattern.type != kObjectTypeNil) {
|
if (opts->pattern.type != kObjectTypeNil) {
|
||||||
@ -166,25 +161,23 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
|||||||
pattern_filter_count += 1;
|
pattern_filter_count += 1;
|
||||||
} else if (v.type == kObjectTypeArray) {
|
} else if (v.type == kObjectTypeArray) {
|
||||||
if (v.data.array.size > AUCMD_MAX_PATTERNS) {
|
if (v.data.array.size > AUCMD_MAX_PATTERNS) {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
api_set_error(err, kErrorTypeValidation, "Too many patterns (maximum of %d)",
|
||||||
"Too many patterns. Please limit yourself to %d or fewer",
|
|
||||||
AUCMD_MAX_PATTERNS);
|
AUCMD_MAX_PATTERNS);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
FOREACH_ITEM(v.data.array, item, {
|
FOREACH_ITEM(v.data.array, item, {
|
||||||
if (item.type != kObjectTypeString) {
|
VALIDATE_T("pattern", kObjectTypeString, item.type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'pattern': must be a string");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
|
||||||
pattern_filters[pattern_filter_count] = item.data.string.data;
|
pattern_filters[pattern_filter_count] = item.data.string.data;
|
||||||
pattern_filter_count += 1;
|
pattern_filter_count += 1;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
VALIDATE_EXP(false, "pattern", "String or Array", api_typename(v.type), {
|
||||||
"Not a valid 'pattern' value. Must be a string or an array");
|
goto cleanup;
|
||||||
goto cleanup;
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,17 +191,16 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
|||||||
ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
|
ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
|
||||||
} 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,
|
api_set_error(err, kErrorTypeValidation, "Too many buffers (maximum of %d)",
|
||||||
kErrorTypeValidation,
|
AUCMD_MAX_PATTERNS);
|
||||||
"Too many buffers. Please limit yourself to %d or fewer", AUCMD_MAX_PATTERNS);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
FOREACH_ITEM(opts->buffer.data.array, bufnr, {
|
FOREACH_ITEM(opts->buffer.data.array, bufnr, {
|
||||||
if (bufnr.type != kObjectTypeInteger && bufnr.type != kObjectTypeBuffer) {
|
VALIDATE_EXP((bufnr.type == kObjectTypeInteger || bufnr.type == kObjectTypeBuffer),
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'buffer': must be an integer");
|
"buffer", "Integer", api_typename(bufnr.type), {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
|
||||||
buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err);
|
buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err);
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
@ -219,9 +211,9 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
|||||||
ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
|
ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
|
||||||
});
|
});
|
||||||
} else if (opts->buffer.type != kObjectTypeNil) {
|
} else if (opts->buffer.type != kObjectTypeNil) {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), {
|
||||||
"Invalid value for 'buffer': must be an integer or array of integers");
|
goto cleanup;
|
||||||
goto cleanup;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
FOREACH_ITEM(buffers, bufnr, {
|
FOREACH_ITEM(buffers, bufnr, {
|
||||||
@ -432,30 +424,23 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->callback.type != kObjectTypeNil && opts->command.type != kObjectTypeNil) {
|
VALIDATE((opts->callback.type == kObjectTypeNil || opts->command.type == kObjectTypeNil),
|
||||||
api_set_error(err, kErrorTypeValidation, "specify either 'callback' or 'command', not both");
|
"Cannot use both 'callback' and 'command'", {
|
||||||
goto cleanup;
|
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
|
if (opts->callback.type != kObjectTypeNil) {
|
||||||
// not do that.
|
// NOTE: We could accept callable tables, but that isn't common in the API.
|
||||||
|
|
||||||
Object *callback = &opts->callback;
|
Object *callback = &opts->callback;
|
||||||
switch (callback->type) {
|
switch (callback->type) {
|
||||||
case kObjectTypeLuaRef:
|
case kObjectTypeLuaRef:
|
||||||
if (callback->data.luaref == LUA_NOREF) {
|
VALIDATE_S((callback->data.luaref != LUA_NOREF), "callback", "<no value>", {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"must pass an actual value");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
VALIDATE_S(nlua_ref_is_function(callback->data.luaref), "callback", "<not a function>", {
|
||||||
if (!nlua_ref_is_function(callback->data.luaref)) {
|
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"must pass a function for callback");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
|
||||||
cb.type = kCallbackLua;
|
cb.type = kCallbackLua;
|
||||||
cb.data.luaref = api_new_luaref(callback->data.luaref);
|
cb.data.luaref = api_new_luaref(callback->data.luaref);
|
||||||
@ -465,28 +450,25 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
|||||||
cb.data.funcref = string_to_cstr(callback->data.string);
|
cb.data.funcref = string_to_cstr(callback->data.string);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
api_set_error(err,
|
VALIDATE_EXP(false, "callback", "Lua function or Vim function name",
|
||||||
kErrorTypeException,
|
api_typename(callback->type), {
|
||||||
"'callback' must be a lua function or name of vim function");
|
goto cleanup;
|
||||||
goto cleanup;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
aucmd.type = CALLABLE_CB;
|
aucmd.type = CALLABLE_CB;
|
||||||
aucmd.callable.cb = cb;
|
aucmd.callable.cb = cb;
|
||||||
} else if (opts->command.type != kObjectTypeNil) {
|
} else if (opts->command.type != kObjectTypeNil) {
|
||||||
Object *command = &opts->command;
|
Object *command = &opts->command;
|
||||||
if (command->type == kObjectTypeString) {
|
VALIDATE_T("command", kObjectTypeString, command->type, {
|
||||||
aucmd.type = CALLABLE_EX;
|
|
||||||
aucmd.callable.cmd = string_to_cstr(command->data.string);
|
|
||||||
} else {
|
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"'command' must be a string");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
aucmd.type = CALLABLE_EX;
|
||||||
|
aucmd.callable.cmd = string_to_cstr(command->data.string);
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "must pass one of: 'command', 'callback'");
|
VALIDATE_S(false, "'command' or 'callback' is required", "", {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_once = api_object_to_bool(opts->once, "once", false, err);
|
bool is_once = api_object_to_bool(opts->once, "once", false, err);
|
||||||
@ -502,24 +484,19 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opts->desc.type != kObjectTypeNil) {
|
if (opts->desc.type != kObjectTypeNil) {
|
||||||
if (opts->desc.type == kObjectTypeString) {
|
VALIDATE_T("desc", kObjectTypeString, opts->desc.type, {
|
||||||
desc = opts->desc.data.string.data;
|
|
||||||
} else {
|
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"'desc' must be a string");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
desc = opts->desc.data.string.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patterns.size == 0) {
|
if (patterns.size == 0) {
|
||||||
ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*")));
|
ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_array.size == 0) {
|
VALIDATE_R((event_array.size != 0), "event", {
|
||||||
api_set_error(err, kErrorTypeValidation, "'event' is a required key");
|
|
||||||
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, {
|
||||||
@ -564,10 +541,9 @@ cleanup:
|
|||||||
void nvim_del_autocmd(Integer id, Error *err)
|
void nvim_del_autocmd(Integer id, Error *err)
|
||||||
FUNC_API_SINCE(9)
|
FUNC_API_SINCE(9)
|
||||||
{
|
{
|
||||||
if (id <= 0) {
|
VALIDATE_INT((id > 0), "autocmd id", id, {
|
||||||
api_set_error(err, kErrorTypeException, "Invalid autocmd id");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
if (!autocmd_delete_id(id)) {
|
if (!autocmd_delete_id(id)) {
|
||||||
api_set_error(err, kErrorTypeException, "Failed to delete autocmd");
|
api_set_error(err, kErrorTypeException, "Failed to delete autocmd");
|
||||||
}
|
}
|
||||||
@ -610,11 +586,10 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) {
|
VALIDATE((opts->pattern.type == kObjectTypeNil || opts->buffer.type == kObjectTypeNil),
|
||||||
api_set_error(err, kErrorTypeValidation,
|
"Cannot use both 'pattern' and 'buffer'", {
|
||||||
"Cannot use both 'pattern' and 'buffer'");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
|
||||||
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) {
|
||||||
@ -772,32 +747,29 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
|
|||||||
break;
|
break;
|
||||||
case kObjectTypeString:
|
case kObjectTypeString:
|
||||||
au_group = augroup_find(opts->group.data.string.data);
|
au_group = augroup_find(opts->group.data.string.data);
|
||||||
if (au_group == AUGROUP_ERROR) {
|
VALIDATE_S((au_group != AUGROUP_ERROR), "group", opts->group.data.string.data, {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"invalid augroup: %s", opts->group.data.string.data);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
break;
|
break;
|
||||||
case kObjectTypeInteger:
|
case kObjectTypeInteger:
|
||||||
au_group = (int)opts->group.data.integer;
|
au_group = (int)opts->group.data.integer;
|
||||||
char *name = augroup_name(au_group);
|
char *name = augroup_name(au_group);
|
||||||
if (!augroup_exists(name)) {
|
VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer.");
|
VALIDATE_EXP(false, "group", "String or Integer", api_typename(opts->group.type), {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->buffer.type != kObjectTypeNil) {
|
if (opts->buffer.type != kObjectTypeNil) {
|
||||||
Object buf_obj = opts->buffer;
|
Object buf_obj = opts->buffer;
|
||||||
if (buf_obj.type != kObjectTypeInteger && buf_obj.type != kObjectTypeBuffer) {
|
VALIDATE_EXP((buf_obj.type == kObjectTypeInteger || buf_obj.type == kObjectTypeBuffer),
|
||||||
api_set_error(err, kErrorTypeException, "invalid buffer: %d", buf_obj.type);
|
"buffer", "Integer", api_typename(buf_obj.type), {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
|
|
||||||
buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err);
|
buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err);
|
||||||
|
|
||||||
@ -844,18 +816,15 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err)
|
|||||||
{
|
{
|
||||||
FOREACH_ITEM(arr, entry, {
|
FOREACH_ITEM(arr, entry, {
|
||||||
if (entry.type != kObjectTypeString) {
|
if (entry.type != kObjectTypeString) {
|
||||||
api_set_error(err,
|
api_set_error(err, kErrorTypeValidation, "Invalid '%s' item type: expected String, got %s",
|
||||||
kErrorTypeValidation,
|
k, api_typename(entry.type));
|
||||||
"All entries in '%s' must be strings",
|
|
||||||
k);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disallow newlines in the middle of the line.
|
// Disallow newlines in the middle of the line.
|
||||||
const String l = entry.data.string;
|
const String l = entry.data.string;
|
||||||
if (memchr(l.data, NL, l.size)) {
|
if (memchr(l.data, NL, l.size)) {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
api_set_error(err, kErrorTypeValidation, "'%s' item cannot contain newlines", k);
|
||||||
"String cannot contain newlines");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -873,10 +842,9 @@ static bool unpack_string_or_array(Array *array, Object *v, char *k, bool requir
|
|||||||
*array = copy_array(v->data.array, NULL);
|
*array = copy_array(v->data.array, NULL);
|
||||||
} else {
|
} else {
|
||||||
if (required) {
|
if (required) {
|
||||||
api_set_error(err,
|
api_set_error(err, kErrorTypeValidation,
|
||||||
kErrorTypeValidation,
|
"Invalid '%s' type: expected Array or String, got %s",
|
||||||
"'%s' must be an array or a string.",
|
k, api_typename(v->type));
|
||||||
k);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -894,27 +862,22 @@ static int get_augroup_from_object(Object group, Error *err)
|
|||||||
return AUGROUP_DEFAULT;
|
return AUGROUP_DEFAULT;
|
||||||
case kObjectTypeString:
|
case kObjectTypeString:
|
||||||
au_group = augroup_find(group.data.string.data);
|
au_group = augroup_find(group.data.string.data);
|
||||||
if (au_group == AUGROUP_ERROR) {
|
VALIDATE_S((au_group != AUGROUP_ERROR), "group", group.data.string.data, {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"invalid augroup: %s", group.data.string.data);
|
|
||||||
|
|
||||||
return AUGROUP_ERROR;
|
return AUGROUP_ERROR;
|
||||||
}
|
});
|
||||||
|
|
||||||
return au_group;
|
return au_group;
|
||||||
case kObjectTypeInteger:
|
case kObjectTypeInteger:
|
||||||
au_group = (int)group.data.integer;
|
au_group = (int)group.data.integer;
|
||||||
char *name = augroup_name(au_group);
|
char *name = augroup_name(au_group);
|
||||||
if (!augroup_exists(name)) {
|
VALIDATE_INT(augroup_exists(name), "group", (int64_t)au_group, {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group);
|
|
||||||
return AUGROUP_ERROR;
|
return AUGROUP_ERROR;
|
||||||
}
|
});
|
||||||
|
|
||||||
return au_group;
|
return au_group;
|
||||||
default:
|
default:
|
||||||
api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer.");
|
VALIDATE_EXP(false, "group", "String or Integer", api_typename(group.type), {
|
||||||
return AUGROUP_ERROR;
|
return AUGROUP_ERROR;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -923,11 +886,12 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob
|
|||||||
{
|
{
|
||||||
const char pattern_buflocal[BUFLOCAL_PAT_LEN];
|
const char pattern_buflocal[BUFLOCAL_PAT_LEN];
|
||||||
|
|
||||||
if (pattern.type != kObjectTypeNil && buffer.type != kObjectTypeNil) {
|
VALIDATE((pattern.type == kObjectTypeNil || buffer.type == kObjectTypeNil),
|
||||||
api_set_error(err, kErrorTypeValidation,
|
"Cannot use both 'pattern' and 'buffer' for the same autocmd", {
|
||||||
"cannot pass both: 'pattern' and 'buffer' for the same autocmd");
|
|
||||||
return false;
|
return false;
|
||||||
} else if (pattern.type != kObjectTypeNil) {
|
});
|
||||||
|
|
||||||
|
if (pattern.type != kObjectTypeNil) {
|
||||||
Object *v = &pattern;
|
Object *v = &pattern;
|
||||||
|
|
||||||
if (v->type == kObjectTypeString) {
|
if (v->type == kObjectTypeString) {
|
||||||
@ -956,18 +920,15 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err,
|
VALIDATE_EXP(false, "pattern", "String or Table", api_typename(v->type), {
|
||||||
kErrorTypeValidation,
|
return false;
|
||||||
"'pattern' must be a string or table");
|
});
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else if (buffer.type != kObjectTypeNil) {
|
} else if (buffer.type != kObjectTypeNil) {
|
||||||
if (buffer.type != kObjectTypeInteger && buffer.type != kObjectTypeBuffer) {
|
VALIDATE_EXP((buffer.type == kObjectTypeInteger || buffer.type == kObjectTypeBuffer),
|
||||||
api_set_error(err,
|
"buffer", "Integer", api_typename(buffer.type), {
|
||||||
kErrorTypeValidation,
|
|
||||||
"'buffer' must be an integer");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
|
|
||||||
buf_T *buf = find_buffer_by_handle((Buffer)buffer.data.integer, err);
|
buf_T *buf = find_buffer_by_handle((Buffer)buffer.data.integer, err);
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "nvim/api/buffer.h"
|
#include "nvim/api/buffer.h"
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/buffer.h"
|
#include "nvim/buffer.h"
|
||||||
@ -179,11 +180,9 @@ Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer,
|
|||||||
if (is_lua) {
|
if (is_lua) {
|
||||||
for (size_t j = 0; cbs[j].name; j++) {
|
for (size_t j = 0; cbs[j].name; j++) {
|
||||||
if (strequal(cbs[j].name, k.data)) {
|
if (strequal(cbs[j].name, k.data)) {
|
||||||
if (v->type != kObjectTypeLuaRef) {
|
VALIDATE_T(cbs[j].name, kObjectTypeLuaRef, v->type, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"%s is not a function", cbs[j].name);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
*(cbs[j].dest) = v->data.luaref;
|
*(cbs[j].dest) = v->data.luaref;
|
||||||
v->data.luaref = LUA_NOREF;
|
v->data.luaref = LUA_NOREF;
|
||||||
key_used = true;
|
key_used = true;
|
||||||
@ -194,26 +193,23 @@ Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer,
|
|||||||
if (key_used) {
|
if (key_used) {
|
||||||
continue;
|
continue;
|
||||||
} else if (strequal("utf_sizes", k.data)) {
|
} else if (strequal("utf_sizes", k.data)) {
|
||||||
if (v->type != kObjectTypeBoolean) {
|
VALIDATE_T("utf_sizes", kObjectTypeBoolean, v->type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "utf_sizes must be boolean");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
cb.utf_sizes = v->data.boolean;
|
cb.utf_sizes = v->data.boolean;
|
||||||
key_used = true;
|
key_used = true;
|
||||||
} else if (strequal("preview", k.data)) {
|
} else if (strequal("preview", k.data)) {
|
||||||
if (v->type != kObjectTypeBoolean) {
|
VALIDATE_T("preview", kObjectTypeBoolean, v->type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "preview must be boolean");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
cb.preview = v->data.boolean;
|
cb.preview = v->data.boolean;
|
||||||
key_used = true;
|
key_used = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key_used) {
|
VALIDATE_S(key_used, "key", k.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf_updates_register(buf, channel_id, cb, send_buffer);
|
return buf_updates_register(buf, channel_id, cb, send_buffer);
|
||||||
@ -297,10 +293,9 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
|
|||||||
start = normalize_index(buf, start, true, &oob);
|
start = normalize_index(buf, start, true, &oob);
|
||||||
end = normalize_index(buf, end, true, &oob);
|
end = normalize_index(buf, end, true, &oob);
|
||||||
|
|
||||||
if (strict_indexing && oob) {
|
VALIDATE((!strict_indexing || !oob), "Index out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (start >= end) {
|
if (start >= end) {
|
||||||
// Return 0-length array
|
// Return 0-length array
|
||||||
@ -328,20 +323,15 @@ end:
|
|||||||
static bool check_string_array(Array arr, bool disallow_nl, Error *err)
|
static bool check_string_array(Array arr, bool disallow_nl, Error *err)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < arr.size; i++) {
|
for (size_t i = 0; i < arr.size; i++) {
|
||||||
if (arr.items[i].type != kObjectTypeString) {
|
VALIDATE_T("replacement item", kObjectTypeString, arr.items[i].type, {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"All items in the replacement array must be strings");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
// Disallow newlines in the middle of the line.
|
// Disallow newlines in the middle of the line.
|
||||||
if (disallow_nl) {
|
if (disallow_nl) {
|
||||||
const String l = arr.items[i].data.string;
|
const String l = arr.items[i].data.string;
|
||||||
if (memchr(l.data, NL, l.size)) {
|
VALIDATE(!memchr(l.data, NL, l.size), "String cannot contain newlines", {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"String cannot contain newlines");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -383,17 +373,12 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
|||||||
start = normalize_index(buf, start, true, &oob);
|
start = normalize_index(buf, start, true, &oob);
|
||||||
end = normalize_index(buf, end, true, &oob);
|
end = normalize_index(buf, end, true, &oob);
|
||||||
|
|
||||||
if (strict_indexing && oob) {
|
VALIDATE((!strict_indexing || !oob), "Index out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
VALIDATE((start <= end), "\"start\" is higher than \"end\"", {
|
||||||
if (start > end) {
|
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"Argument \"start\" is higher than \"end\"");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
||||||
if (!check_string_array(replacement, disallow_nl, err)) {
|
if (!check_string_array(replacement, disallow_nl, err)) {
|
||||||
@ -453,10 +438,9 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
|||||||
for (size_t i = 0; i < to_replace; i++) {
|
for (size_t i = 0; i < to_replace; i++) {
|
||||||
int64_t lnum = start + (int64_t)i;
|
int64_t lnum = start + (int64_t)i;
|
||||||
|
|
||||||
if (lnum >= MAXLNUM) {
|
VALIDATE(lnum < MAXLNUM, "Index value is too high", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
||||||
api_set_error(err, kErrorTypeException, "Failed to replace line");
|
api_set_error(err, kErrorTypeException, "Failed to replace line");
|
||||||
@ -473,10 +457,9 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
|||||||
for (size_t i = to_replace; i < new_len; i++) {
|
for (size_t i = to_replace; i < new_len; i++) {
|
||||||
int64_t lnum = start + (int64_t)i - 1;
|
int64_t lnum = start + (int64_t)i - 1;
|
||||||
|
|
||||||
if (lnum >= MAXLNUM) {
|
VALIDATE(lnum < MAXLNUM, "Index value is too high", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||||
api_set_error(err, kErrorTypeException, "Failed to insert line");
|
api_set_error(err, kErrorTypeException, "Failed to insert line");
|
||||||
@ -563,16 +546,14 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
|||||||
// check range is ordered and everything!
|
// check range is ordered and everything!
|
||||||
// start_row, end_row within buffer len (except add text past the end?)
|
// start_row, end_row within buffer len (except add text past the end?)
|
||||||
start_row = normalize_index(buf, start_row, false, &oob);
|
start_row = normalize_index(buf, start_row, false, &oob);
|
||||||
if (oob) {
|
VALIDATE((!oob), "start_row out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "start_row out of bounds");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
end_row = normalize_index(buf, end_row, false, &oob);
|
end_row = normalize_index(buf, end_row, false, &oob);
|
||||||
if (oob) {
|
VALIDATE((!oob), "end_row out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "end_row out of bounds");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
char *str_at_start = NULL;
|
char *str_at_start = NULL;
|
||||||
char *str_at_end = NULL;
|
char *str_at_end = NULL;
|
||||||
@ -580,23 +561,21 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
|||||||
// Another call to ml_get_buf() may free the line, so make a copy.
|
// Another call to ml_get_buf() may free the line, so make a copy.
|
||||||
str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row, false));
|
str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row, false));
|
||||||
size_t len_at_start = strlen(str_at_start);
|
size_t len_at_start = strlen(str_at_start);
|
||||||
if (start_col < 0 || (size_t)start_col > len_at_start) {
|
VALIDATE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "start_col out of bounds");
|
|
||||||
goto early_end;
|
goto early_end;
|
||||||
}
|
});
|
||||||
|
|
||||||
// Another call to ml_get_buf() may free the line, so make a copy.
|
// Another call to ml_get_buf() may free the line, so make a copy.
|
||||||
str_at_end = xstrdup(ml_get_buf(buf, (linenr_T)end_row, false));
|
str_at_end = xstrdup(ml_get_buf(buf, (linenr_T)end_row, false));
|
||||||
size_t len_at_end = strlen(str_at_end);
|
size_t len_at_end = strlen(str_at_end);
|
||||||
if (end_col < 0 || (size_t)end_col > len_at_end) {
|
VALIDATE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "end_col out of bounds");
|
|
||||||
goto early_end;
|
goto early_end;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (start_row > end_row || (end_row == start_row && start_col > end_col)) {
|
VALIDATE((start_row <= end_row && !(end_row == start_row && start_col > end_col)),
|
||||||
api_set_error(err, kErrorTypeValidation, "start is higher than end");
|
"start is higher than end", {
|
||||||
goto early_end;
|
goto early_end;
|
||||||
}
|
});
|
||||||
|
|
||||||
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
||||||
if (!check_string_array(replacement, disallow_nl, err)) {
|
if (!check_string_array(replacement, disallow_nl, err)) {
|
||||||
@ -702,10 +681,9 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
|||||||
for (size_t i = 0; i < to_replace; i++) {
|
for (size_t i = 0; i < to_replace; i++) {
|
||||||
int64_t lnum = start_row + (int64_t)i;
|
int64_t lnum = start_row + (int64_t)i;
|
||||||
|
|
||||||
if (lnum >= MAXLNUM) {
|
VALIDATE((lnum < MAXLNUM), "Index value is too high", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
||||||
api_set_error(err, kErrorTypeException, "Failed to replace line");
|
api_set_error(err, kErrorTypeException, "Failed to replace line");
|
||||||
@ -720,10 +698,9 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
|||||||
for (size_t i = to_replace; i < new_len; i++) {
|
for (size_t i = to_replace; i < new_len; i++) {
|
||||||
int64_t lnum = start_row + (int64_t)i - 1;
|
int64_t lnum = start_row + (int64_t)i - 1;
|
||||||
|
|
||||||
if (lnum >= MAXLNUM) {
|
VALIDATE((lnum < MAXLNUM), "Index value is too high", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||||
api_set_error(err, kErrorTypeException, "Failed to insert line");
|
api_set_error(err, kErrorTypeException, "Failed to insert line");
|
||||||
@ -800,10 +777,9 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
|
|||||||
{
|
{
|
||||||
Array rv = ARRAY_DICT_INIT;
|
Array rv = ARRAY_DICT_INIT;
|
||||||
|
|
||||||
if (opts.size > 0) {
|
VALIDATE((opts.size == 0), "opts dict isn't empty", {
|
||||||
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
buf_T *buf = find_buffer_by_handle(buffer, err);
|
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||||
|
|
||||||
@ -820,18 +796,16 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
|
|||||||
start_row = normalize_index(buf, start_row, false, &oob);
|
start_row = normalize_index(buf, start_row, false, &oob);
|
||||||
end_row = normalize_index(buf, end_row, false, &oob);
|
end_row = normalize_index(buf, end_row, false, &oob);
|
||||||
|
|
||||||
if (oob) {
|
VALIDATE((!oob), "Index out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
// nvim_buf_get_lines doesn't care if the start row is greater than the end
|
// nvim_buf_get_lines doesn't care if the start row is greater than the end
|
||||||
// row (it will just return an empty array), but nvim_buf_get_text does in
|
// row (it will just return an empty array), but nvim_buf_get_text does in
|
||||||
// order to maintain symmetry with nvim_buf_set_text.
|
// order to maintain symmetry with nvim_buf_set_text.
|
||||||
if (start_row > end_row) {
|
VALIDATE((start_row <= end_row), "start is higher than end", {
|
||||||
api_set_error(err, kErrorTypeValidation, "start is higher than end");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
bool replace_nl = (channel_id != VIML_INTERNAL_CALL);
|
bool replace_nl = (channel_id != VIML_INTERNAL_CALL);
|
||||||
|
|
||||||
@ -907,10 +881,9 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < 0 || index > buf->b_ml.ml_line_count) {
|
VALIDATE((index >= 0 && index <= buf->b_ml.ml_line_count), "Index out of bounds", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
});
|
||||||
|
|
||||||
return ml_find_line_or_offset(buf, (int)index + 1, NULL, true);
|
return ml_find_line_or_offset(buf, (int)index + 1, NULL, true);
|
||||||
}
|
}
|
||||||
@ -1118,8 +1091,9 @@ void nvim_buf_delete(Buffer buffer, Dictionary opts, Error *err)
|
|||||||
} else if (strequal("unload", k.data)) {
|
} else if (strequal("unload", k.data)) {
|
||||||
unload = api_object_to_bool(v, "unload", false, err);
|
unload = api_object_to_bool(v, "unload", false, err);
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
VALIDATE_S(false, "key", k.data, {
|
||||||
return;
|
return;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1174,20 +1148,16 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.size != 1) {
|
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Mark name must be a single character");
|
|
||||||
return res;
|
return res;
|
||||||
}
|
});
|
||||||
|
|
||||||
fmark_T *fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, *name.data);
|
fmark_T *fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, *name.data);
|
||||||
|
|
||||||
// fm is NULL when there's no mark with the given name
|
// fm is NULL when there's no mark with the given name
|
||||||
if (fm == NULL) {
|
VALIDATE_S((fm != NULL), "mark name", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid mark name: '%c'",
|
|
||||||
*name.data);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
});
|
||||||
|
|
||||||
// mark.lnum is 0 when the mark is not valid in the buffer, or is not set.
|
// mark.lnum is 0 when the mark is not valid in the buffer, or is not set.
|
||||||
if (fm->mark.lnum != 0 && fm->fnum == buf->handle) {
|
if (fm->mark.lnum != 0 && fm->fnum == buf->handle) {
|
||||||
@ -1224,11 +1194,9 @@ Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.size != 1) {
|
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Mark name must be a single character");
|
|
||||||
return res;
|
return res;
|
||||||
}
|
});
|
||||||
|
|
||||||
res = set_mark(buf, name, line, col, err);
|
res = set_mark(buf, name, line, col, err);
|
||||||
|
|
||||||
@ -1257,21 +1225,18 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.size != 1) {
|
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Mark name must be a single character");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
fmark_T *fm;
|
fmark_T *fm;
|
||||||
pos_T pos;
|
pos_T pos;
|
||||||
char mark = *name.data;
|
char mark = *name.data;
|
||||||
|
|
||||||
fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, mark);
|
fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, mark);
|
||||||
if (fm == NULL) {
|
VALIDATE_S((fm != NULL), "mark name", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid mark name");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
// (0, 0) uppercase/file mark set in another buffer.
|
// (0, 0) uppercase/file mark set in another buffer.
|
||||||
if (fm->fnum != buf->handle) {
|
if (fm->fnum != buf->handle) {
|
||||||
pos.lnum = 0;
|
pos.lnum = 0;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "nvim/api/command.h"
|
#include "nvim/api/command.h"
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/buffer_defs.h"
|
#include "nvim/buffer_defs.h"
|
||||||
@ -99,10 +100,9 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
|
|||||||
{
|
{
|
||||||
Dictionary result = ARRAY_DICT_INIT;
|
Dictionary result = ARRAY_DICT_INIT;
|
||||||
|
|
||||||
if (opts.size > 0) {
|
VALIDATE((opts.size == 0), "opts dict isn't empty", {
|
||||||
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
|
||||||
return result;
|
return result;
|
||||||
}
|
});
|
||||||
|
|
||||||
// Parse command line
|
// Parse command line
|
||||||
exarg_T ea;
|
exarg_T ea;
|
||||||
@ -998,7 +998,7 @@ void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
|
api_set_error(err, kErrorTypeException, "Invalid command (not found): %s", name.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_user_command(String name, Object command, Dict(user_command) *opts, int flags,
|
void create_user_command(String name, Object command, Dict(user_command) *opts, int flags,
|
||||||
@ -1014,20 +1014,17 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
|||||||
LuaRef compl_luaref = LUA_NOREF;
|
LuaRef compl_luaref = LUA_NOREF;
|
||||||
LuaRef preview_luaref = LUA_NOREF;
|
LuaRef preview_luaref = LUA_NOREF;
|
||||||
|
|
||||||
if (!uc_validate_name(name.data)) {
|
VALIDATE_S(uc_validate_name(name.data), "command name", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid command name");
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
});
|
||||||
|
VALIDATE_S(!mb_islower(name.data[0]), "command name (must begin with an uppercase letter)",
|
||||||
if (mb_islower(name.data[0])) {
|
name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter");
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
});
|
||||||
|
VALIDATE((!HAS_KEY(opts->range) || !HAS_KEY(opts->count)),
|
||||||
if (HAS_KEY(opts->range) && HAS_KEY(opts->count)) {
|
"Cannot use both 'range' and 'count'", {
|
||||||
api_set_error(err, kErrorTypeValidation, "'range' and 'count' are mutually exclusive");
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (opts->nargs.type == kObjectTypeInteger) {
|
if (opts->nargs.type == kObjectTypeInteger) {
|
||||||
switch (opts->nargs.data.integer) {
|
switch (opts->nargs.data.integer) {
|
||||||
@ -1038,14 +1035,14 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
|||||||
argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
|
argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
VALIDATE_INT(false, "nargs", (int64_t)opts->nargs.data.integer, {
|
||||||
goto err;
|
goto err;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (opts->nargs.type == kObjectTypeString) {
|
} else if (opts->nargs.type == kObjectTypeString) {
|
||||||
if (opts->nargs.data.string.size > 1) {
|
VALIDATE_S((opts->nargs.data.string.size <= 1), "nargs", opts->nargs.data.string.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
});
|
||||||
|
|
||||||
switch (opts->nargs.data.string.data[0]) {
|
switch (opts->nargs.data.string.data[0]) {
|
||||||
case '*':
|
case '*':
|
||||||
@ -1058,18 +1055,19 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
|||||||
argt |= EX_EXTRA | EX_NEEDARG;
|
argt |= EX_EXTRA | EX_NEEDARG;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
VALIDATE_S(false, "nargs", opts->nargs.data.string.data, {
|
||||||
goto err;
|
goto err;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->nargs)) {
|
} else if (HAS_KEY(opts->nargs)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
VALIDATE_S(false, "nargs", "", {
|
||||||
goto err;
|
goto err;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HAS_KEY(opts->complete) && !argt) {
|
VALIDATE((!HAS_KEY(opts->complete) || argt), "'complete' used without 'nargs'", {
|
||||||
api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'");
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (opts->range.type == kObjectTypeBoolean) {
|
if (opts->range.type == kObjectTypeBoolean) {
|
||||||
if (opts->range.data.boolean) {
|
if (opts->range.data.boolean) {
|
||||||
@ -1077,13 +1075,12 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
|||||||
addr_type_arg = ADDR_LINES;
|
addr_type_arg = ADDR_LINES;
|
||||||
}
|
}
|
||||||
} else if (opts->range.type == kObjectTypeString) {
|
} else if (opts->range.type == kObjectTypeString) {
|
||||||
if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) {
|
VALIDATE_S((opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1),
|
||||||
argt |= EX_RANGE | EX_DFLALL;
|
"range", "", {
|
||||||
addr_type_arg = ADDR_LINES;
|
|
||||||
} else {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
});
|
||||||
|
argt |= EX_RANGE | EX_DFLALL;
|
||||||
|
addr_type_arg = ADDR_LINES;
|
||||||
} else if (opts->range.type == kObjectTypeInteger) {
|
} else if (opts->range.type == kObjectTypeInteger) {
|
||||||
argt |= EX_RANGE | EX_ZEROR;
|
argt |= EX_RANGE | EX_ZEROR;
|
||||||
def = opts->range.data.integer;
|
def = opts->range.data.integer;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "nvim/api/extmark.h"
|
#include "nvim/api/extmark.h"
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/buffer_defs.h"
|
#include "nvim/buffer_defs.h"
|
||||||
#include "nvim/charset.h"
|
#include "nvim/charset.h"
|
||||||
#include "nvim/decoration.h"
|
#include "nvim/decoration.h"
|
||||||
@ -218,10 +219,9 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ns_initialized((uint32_t)ns_id)) {
|
VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
bool details = false;
|
bool details = false;
|
||||||
for (size_t i = 0; i < opts.size; i++) {
|
for (size_t i = 0; i < opts.size; i++) {
|
||||||
@ -233,12 +233,14 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
|
|||||||
} else if (v->type == kObjectTypeInteger) {
|
} else if (v->type == kObjectTypeInteger) {
|
||||||
details = v->data.integer;
|
details = v->data.integer;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "details is not an boolean");
|
VALIDATE_EXP(false, "details", "Boolean or Integer", api_typename(v->type), {
|
||||||
return rv;
|
return rv;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
VALIDATE_S(false, "key", k.data, {
|
||||||
return rv;
|
return rv;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,10 +303,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ns_initialized((uint32_t)ns_id)) {
|
VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
Integer limit = -1;
|
Integer limit = -1;
|
||||||
bool details = false;
|
bool details = false;
|
||||||
@ -313,10 +314,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
|||||||
String k = opts.items[i].key;
|
String k = opts.items[i].key;
|
||||||
Object *v = &opts.items[i].value;
|
Object *v = &opts.items[i].value;
|
||||||
if (strequal("limit", k.data)) {
|
if (strequal("limit", k.data)) {
|
||||||
if (v->type != kObjectTypeInteger) {
|
VALIDATE_T("limit", kObjectTypeInteger, v->type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "limit is not an integer");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
limit = v->data.integer;
|
limit = v->data.integer;
|
||||||
} else if (strequal("details", k.data)) {
|
} else if (strequal("details", k.data)) {
|
||||||
if (v->type == kObjectTypeBoolean) {
|
if (v->type == kObjectTypeBoolean) {
|
||||||
@ -324,12 +324,14 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
|||||||
} else if (v->type == kObjectTypeInteger) {
|
} else if (v->type == kObjectTypeInteger) {
|
||||||
details = v->data.integer;
|
details = v->data.integer;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "details is not an boolean");
|
VALIDATE_EXP(false, "details", "Boolean or Integer", api_typename(v->type), {
|
||||||
return rv;
|
return rv;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
VALIDATE_S(false, "key", k.data, {
|
||||||
return rv;
|
return rv;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,27 +503,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ns_initialized((uint32_t)ns_id)) {
|
VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
|
|
||||||
uint32_t id = 0;
|
uint32_t id = 0;
|
||||||
if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) {
|
if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) {
|
||||||
id = (uint32_t)opts->id.data.integer;
|
id = (uint32_t)opts->id.data.integer;
|
||||||
} else if (HAS_KEY(opts->id)) {
|
} else if (HAS_KEY(opts->id)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "id is not a positive integer");
|
VALIDATE_S(false, "id (must be positive integer)", "", {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int line2 = -1;
|
int line2 = -1;
|
||||||
|
|
||||||
// For backward compatibility we support "end_line" as an alias for "end_row"
|
// For backward compatibility we support "end_line" as an alias for "end_row"
|
||||||
if (HAS_KEY(opts->end_line)) {
|
if (HAS_KEY(opts->end_line)) {
|
||||||
if (HAS_KEY(opts->end_row)) {
|
VALIDATE(!HAS_KEY(opts->end_row), "cannot use both end_row and end_line", {
|
||||||
api_set_error(err, kErrorTypeValidation, "cannot use both end_row and end_line");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
opts->end_row = opts->end_line;
|
opts->end_row = opts->end_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,29 +537,28 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
|
|
||||||
if (opts->end_row.type == kObjectTypeInteger) {
|
if (opts->end_row.type == kObjectTypeInteger) {
|
||||||
Integer val = opts->end_row.data.integer;
|
Integer val = opts->end_row.data.integer;
|
||||||
if (val < 0 || (val > buf->b_ml.ml_line_count && strict)) {
|
VALIDATE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)),
|
||||||
api_set_error(err, kErrorTypeValidation, "end_row value outside range");
|
"end_row value outside range", {
|
||||||
goto error;
|
goto error;
|
||||||
} else {
|
});
|
||||||
line2 = (int)val;
|
line2 = (int)val;
|
||||||
}
|
|
||||||
} else if (HAS_KEY(opts->end_row)) {
|
} else if (HAS_KEY(opts->end_row)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "end_row is not an integer");
|
VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
colnr_T col2 = -1;
|
colnr_T col2 = -1;
|
||||||
if (opts->end_col.type == kObjectTypeInteger) {
|
if (opts->end_col.type == kObjectTypeInteger) {
|
||||||
Integer val = opts->end_col.data.integer;
|
Integer val = opts->end_col.data.integer;
|
||||||
if (val < 0 || val > MAXCOL) {
|
VALIDATE((val >= 0 && val <= MAXCOL), "end_col value outside range", {
|
||||||
api_set_error(err, kErrorTypeValidation, "end_col value outside range");
|
|
||||||
goto error;
|
goto error;
|
||||||
} else {
|
});
|
||||||
col2 = (int)val;
|
col2 = (int)val;
|
||||||
}
|
|
||||||
} else if (HAS_KEY(opts->end_col)) {
|
} else if (HAS_KEY(opts->end_col)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "end_col is not an integer");
|
VALIDATE_T("end_col", kObjectTypeInteger, opts->end_col.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// uncrustify:off
|
// uncrustify:off
|
||||||
@ -596,8 +596,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
}
|
}
|
||||||
has_decor = true;
|
has_decor = true;
|
||||||
} else if (HAS_KEY(opts->conceal)) {
|
} else if (HAS_KEY(opts->conceal)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "conceal is not a String");
|
VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->virt_text.type == kObjectTypeArray) {
|
if (opts->virt_text.type == kObjectTypeArray) {
|
||||||
@ -608,8 +609,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->virt_text)) {
|
} else if (HAS_KEY(opts->virt_text)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "virt_text is not an Array");
|
VALIDATE_T("virt_text", kObjectTypeArray, opts->virt_text.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->virt_text_pos.type == kObjectTypeString) {
|
if (opts->virt_text_pos.type == kObjectTypeString) {
|
||||||
@ -621,21 +623,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
} else if (strequal("right_align", str.data)) {
|
} else if (strequal("right_align", str.data)) {
|
||||||
decor.virt_text_pos = kVTRightAlign;
|
decor.virt_text_pos = kVTRightAlign;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "virt_text_pos: invalid value");
|
VALIDATE_S(false, "virt_text_pos", "", {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->virt_text_pos)) {
|
} else if (HAS_KEY(opts->virt_text_pos)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "virt_text_pos is not a String");
|
VALIDATE_T("virt_text_pos", kObjectTypeString, opts->virt_text_pos.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->virt_text_win_col.type == kObjectTypeInteger) {
|
if (opts->virt_text_win_col.type == kObjectTypeInteger) {
|
||||||
decor.col = (int)opts->virt_text_win_col.data.integer;
|
decor.col = (int)opts->virt_text_win_col.data.integer;
|
||||||
decor.virt_text_pos = kVTWinCol;
|
decor.virt_text_pos = kVTWinCol;
|
||||||
} else if (HAS_KEY(opts->virt_text_win_col)) {
|
} else if (HAS_KEY(opts->virt_text_win_col)) {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
VALIDATE_T("virt_text_win_col", kObjectTypeInteger, opts->virt_text_win_col.type, {
|
||||||
"virt_text_win_col is not a Number of the correct size");
|
goto error;
|
||||||
goto error;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
|
OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
|
||||||
@ -650,13 +654,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
} else if (strequal("blend", str.data)) {
|
} else if (strequal("blend", str.data)) {
|
||||||
decor.hl_mode = kHlModeBlend;
|
decor.hl_mode = kHlModeBlend;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
VALIDATE_S(false, "virt_text_pos", "", {
|
||||||
"virt_text_pos: invalid value");
|
goto error;
|
||||||
goto error;
|
});
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->hl_mode)) {
|
} else if (HAS_KEY(opts->hl_mode)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "hl_mode is not a String");
|
VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool virt_lines_leftcol = false;
|
bool virt_lines_leftcol = false;
|
||||||
@ -665,10 +670,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
if (opts->virt_lines.type == kObjectTypeArray) {
|
if (opts->virt_lines.type == kObjectTypeArray) {
|
||||||
Array a = opts->virt_lines.data.array;
|
Array a = opts->virt_lines.data.array;
|
||||||
for (size_t j = 0; j < a.size; j++) {
|
for (size_t j = 0; j < a.size; j++) {
|
||||||
if (a.items[j].type != kObjectTypeArray) {
|
VALIDATE_T("virt_text_line", kObjectTypeArray, a.items[j].type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "virt_text_line item is not an Array");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
int dummig;
|
int dummig;
|
||||||
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
|
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
|
||||||
kv_push(decor.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol }));
|
kv_push(decor.virt_lines, ((struct virt_line){ jtem, virt_lines_leftcol }));
|
||||||
@ -678,8 +682,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
has_decor = true;
|
has_decor = true;
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->virt_lines)) {
|
} else if (HAS_KEY(opts->virt_lines)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array");
|
VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false);
|
OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false);
|
||||||
@ -687,26 +692,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
if (opts->priority.type == kObjectTypeInteger) {
|
if (opts->priority.type == kObjectTypeInteger) {
|
||||||
Integer val = opts->priority.data.integer;
|
Integer val = opts->priority.data.integer;
|
||||||
|
|
||||||
if (val < 0 || val > UINT16_MAX) {
|
VALIDATE_S((val >= 0 && val <= UINT16_MAX), "priority", "(out of range)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "priority is not a valid value");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
decor.priority = (DecorPriority)val;
|
decor.priority = (DecorPriority)val;
|
||||||
} else if (HAS_KEY(opts->priority)) {
|
} else if (HAS_KEY(opts->priority)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "priority is not a Number of the correct size");
|
VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->sign_text.type == kObjectTypeString) {
|
if (opts->sign_text.type == kObjectTypeString) {
|
||||||
if (!init_sign_text(&decor.sign_text,
|
VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data.string.data),
|
||||||
opts->sign_text.data.string.data)) {
|
"sign_text", "", {
|
||||||
api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
has_decor = true;
|
has_decor = true;
|
||||||
} else if (HAS_KEY(opts->sign_text)) {
|
} else if (HAS_KEY(opts->sign_text)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "sign_text is not a String");
|
VALIDATE_T("sign_text", kObjectTypeString, opts->sign_text.type, {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool right_gravity = true;
|
bool right_gravity = true;
|
||||||
@ -714,11 +719,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
|
|
||||||
// Only error out if they try to set end_right_gravity without
|
// Only error out if they try to set end_right_gravity without
|
||||||
// setting end_col or end_row
|
// setting end_col or end_row
|
||||||
if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) {
|
VALIDATE(!(line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)),
|
||||||
api_set_error(err, kErrorTypeValidation,
|
"cannot set end_right_gravity without setting end_row or end_col", {
|
||||||
"cannot set end_right_gravity without setting end_row or end_col");
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
|
|
||||||
bool end_right_gravity = false;
|
bool end_right_gravity = false;
|
||||||
OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false);
|
OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false);
|
||||||
@ -742,16 +746,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
has_decor = true;
|
has_decor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line < 0) {
|
VALIDATE_S((line >= 0), "line", "(out of range)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "line value outside range");
|
|
||||||
goto error;
|
goto error;
|
||||||
} else if (line > buf->b_ml.ml_line_count) {
|
});
|
||||||
if (strict) {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "line value outside range");
|
if (line > buf->b_ml.ml_line_count) {
|
||||||
|
VALIDATE_S(!strict, "line", "(out of range)", {
|
||||||
goto error;
|
goto error;
|
||||||
} else {
|
});
|
||||||
line = buf->b_ml.ml_line_count;
|
line = buf->b_ml.ml_line_count;
|
||||||
}
|
|
||||||
} else if (line < buf->b_ml.ml_line_count) {
|
} else if (line < buf->b_ml.ml_line_count) {
|
||||||
len = ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1, false));
|
len = ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1, false));
|
||||||
}
|
}
|
||||||
@ -759,15 +762,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
if (col == -1) {
|
if (col == -1) {
|
||||||
col = (Integer)len;
|
col = (Integer)len;
|
||||||
} else if (col > (Integer)len) {
|
} else if (col > (Integer)len) {
|
||||||
if (strict) {
|
VALIDATE_S(!strict, "col", "(out of range)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "col value outside range");
|
|
||||||
goto error;
|
goto error;
|
||||||
} else {
|
});
|
||||||
col = (Integer)len;
|
col = (Integer)len;
|
||||||
}
|
|
||||||
} else if (col < -1) {
|
} else if (col < -1) {
|
||||||
api_set_error(err, kErrorTypeValidation, "col value outside range");
|
VALIDATE_S(false, "col", "(out of range)", {
|
||||||
goto error;
|
goto error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (col2 >= 0) {
|
if (col2 >= 0) {
|
||||||
@ -781,12 +783,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
|||||||
line2 = (int)line;
|
line2 = (int)line;
|
||||||
}
|
}
|
||||||
if (col2 > (Integer)len) {
|
if (col2 > (Integer)len) {
|
||||||
if (strict) {
|
VALIDATE_S(!strict, "end_col", "(out of range)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "end_col value outside range");
|
|
||||||
goto error;
|
goto error;
|
||||||
} else {
|
});
|
||||||
col2 = (int)len;
|
col2 = (int)len;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (line2 >= 0) {
|
} else if (line2 >= 0) {
|
||||||
col2 = 0;
|
col2 = 0;
|
||||||
@ -829,10 +829,9 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
|
|||||||
if (!buf) {
|
if (!buf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ns_initialized((uint32_t)ns_id)) {
|
VALIDATE_INT(ns_initialized((uint32_t)ns_id), "ns_id", ns_id, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
|
|
||||||
return extmark_del(buf, (uint32_t)ns_id, (uint32_t)id);
|
return extmark_del(buf, (uint32_t)ns_id, (uint32_t)id);
|
||||||
}
|
}
|
||||||
@ -887,14 +886,13 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line < 0 || line >= MAXLNUM) {
|
VALIDATE_S((line >= 0 && line < MAXLNUM), "line number", "(out of range)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Line number outside range");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
});
|
||||||
if (col_start < 0 || col_start > MAXCOL) {
|
VALIDATE_S((col_start >= 0 && col_start <= MAXCOL), "column", "(out of range)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Column value outside range");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (col_end < 0 || col_end > MAXCOL) {
|
if (col_end < 0 || col_end > MAXCOL) {
|
||||||
col_end = MAXCOL;
|
col_end = MAXCOL;
|
||||||
}
|
}
|
||||||
@ -950,10 +948,10 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line_start < 0 || line_start >= MAXLNUM) {
|
VALIDATE_S((line_start >= 0 && line_start < MAXLNUM), "line number", "(out of range)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Line number outside range");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (line_end < 0 || line_end > MAXLNUM) {
|
if (line_end < 0 || line_end > MAXLNUM) {
|
||||||
line_end = MAXLNUM;
|
line_end = MAXLNUM;
|
||||||
}
|
}
|
||||||
@ -1034,11 +1032,10 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v->type != kObjectTypeLuaRef) {
|
VALIDATE_T(cbs[i].name, kObjectTypeLuaRef, v->type, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"%s is not a function", cbs[i].name);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
});
|
||||||
|
|
||||||
*(cbs[i].dest) = v->data.luaref;
|
*(cbs[i].dest) = v->data.luaref;
|
||||||
v->data.luaref = LUA_NOREF;
|
v->data.luaref = LUA_NOREF;
|
||||||
}
|
}
|
||||||
@ -1075,39 +1072,39 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in
|
|||||||
*col = MAXCOL;
|
*col = MAXCOL;
|
||||||
return true;
|
return true;
|
||||||
} else if (id < 0) {
|
} else if (id < 0) {
|
||||||
api_set_error(err, kErrorTypeValidation, "Mark id must be positive");
|
VALIDATE_INT(false, "mark id", id, {
|
||||||
return false;
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
|
ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
|
||||||
if (extmark.row >= 0) {
|
|
||||||
*row = extmark.row;
|
VALIDATE_INT((extmark.row >= 0), "mark id (not found)", id, {
|
||||||
*col = extmark.col;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "No mark with requested id");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
|
*row = extmark.row;
|
||||||
|
*col = extmark.col;
|
||||||
|
return true;
|
||||||
|
|
||||||
// Check if it is a position
|
// Check if it is a position
|
||||||
} else if (obj.type == kObjectTypeArray) {
|
} else if (obj.type == kObjectTypeArray) {
|
||||||
Array pos = obj.data.array;
|
Array pos = obj.data.array;
|
||||||
if (pos.size != 2
|
VALIDATE((pos.size == 2
|
||||||
|| pos.items[0].type != kObjectTypeInteger
|
&& pos.items[0].type == kObjectTypeInteger
|
||||||
|| pos.items[1].type != kObjectTypeInteger) {
|
&& pos.items[1].type == kObjectTypeInteger),
|
||||||
api_set_error(err, kErrorTypeValidation,
|
"Invalid position: expected 2 Integer items", {
|
||||||
"Position must have 2 integer elements");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
|
|
||||||
Integer pos_row = pos.items[0].data.integer;
|
Integer pos_row = pos.items[0].data.integer;
|
||||||
Integer pos_col = pos.items[1].data.integer;
|
Integer pos_col = pos.items[1].data.integer;
|
||||||
*row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
|
*row = (int)(pos_row >= 0 ? pos_row : MAXLNUM);
|
||||||
*col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
|
*col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
VALIDATE(false, "Invalid position: expected mark id Integer or 2-item Array", {
|
||||||
"Position must be a mark id Integer or position Array");
|
return false;
|
||||||
return false;
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// adapted from sign.c:sign_define_init_text.
|
// adapted from sign.c:sign_define_init_text.
|
||||||
@ -1151,17 +1148,14 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
|
|||||||
VirtText virt_text = KV_INITIAL_VALUE;
|
VirtText virt_text = KV_INITIAL_VALUE;
|
||||||
int w = 0;
|
int w = 0;
|
||||||
for (size_t i = 0; i < chunks.size; i++) {
|
for (size_t i = 0; i < chunks.size; i++) {
|
||||||
if (chunks.items[i].type != kObjectTypeArray) {
|
VALIDATE_T("chunk", kObjectTypeArray, chunks.items[i].type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Chunk is not an array");
|
|
||||||
goto free_exit;
|
goto free_exit;
|
||||||
}
|
});
|
||||||
Array chunk = chunks.items[i].data.array;
|
Array chunk = chunks.items[i].data.array;
|
||||||
if (chunk.size == 0 || chunk.size > 2
|
VALIDATE((chunk.size > 0 && chunk.size <= 2 && chunk.items[0].type == kObjectTypeString),
|
||||||
|| chunk.items[0].type != kObjectTypeString) {
|
"Invalid chunk: expected Array with 1 or 2 Strings", {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Chunk is not an array with one or two strings");
|
|
||||||
goto free_exit;
|
goto free_exit;
|
||||||
}
|
});
|
||||||
|
|
||||||
String str = chunk.items[0].data.string;
|
String str = chunk.items[0].data.string;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "nvim/api/options.h"
|
#include "nvim/api/options.h"
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/buffer_defs.h"
|
#include "nvim/buffer_defs.h"
|
||||||
#include "nvim/eval/window.h"
|
#include "nvim/eval/window.h"
|
||||||
@ -31,11 +32,14 @@ static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_t
|
|||||||
} else if (!strcmp(opts->scope.data.string.data, "global")) {
|
} else if (!strcmp(opts->scope.data.string.data, "global")) {
|
||||||
*scope = OPT_GLOBAL;
|
*scope = OPT_GLOBAL;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'");
|
VALIDATE(false, "Invalid scope (expected 'local' or 'global')", {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->scope)) {
|
} else if (HAS_KEY(opts->scope)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid value for key: scope");
|
VALIDATE_T("scope", kObjectTypeString, opts->scope.type, {
|
||||||
|
return FAIL;
|
||||||
|
});
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +52,9 @@ static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_t
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->win)) {
|
} else if (HAS_KEY(opts->win)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid value for key: win");
|
VALIDATE_T("win", kObjectTypeInteger, opts->win.type, {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->buf.type == kObjectTypeInteger) {
|
if (opts->buf.type == kObjectTypeInteger) {
|
||||||
@ -60,19 +65,17 @@ static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_t
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
} else if (HAS_KEY(opts->buf)) {
|
} else if (HAS_KEY(opts->buf)) {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid value for key: buf");
|
VALIDATE_T("buf", kObjectTypeInteger, opts->buf.type, {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) {
|
VALIDATE((!HAS_KEY(opts->scope) || !HAS_KEY(opts->buf)), "cannot use both 'scope' and 'buf'", {
|
||||||
api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together");
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
});
|
||||||
|
VALIDATE((!HAS_KEY(opts->win) || !HAS_KEY(opts->buf)), "cannot use both 'buf' and 'win'", {
|
||||||
if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together");
|
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
});
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@ -132,8 +135,9 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data);
|
VALIDATE_S(false, "option", name.data, {
|
||||||
return rv;
|
return rv;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
@ -193,8 +197,9 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error
|
|||||||
scope |= OPT_CLEAR;
|
scope |= OPT_CLEAR;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid value for option");
|
VALIDATE_EXP(false, "option type", "Integer, Boolean, or String", api_typename(value.type), {
|
||||||
return;
|
return;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
access_option_value_for(name.data, &numval, &stringval, scope, opt_type, to, false, err);
|
access_option_value_for(name.data, &numval, &stringval, scope, opt_type, to, false, err);
|
||||||
@ -351,21 +356,18 @@ static Object get_option_from(void *from, int type, String name, Error *err)
|
|||||||
{
|
{
|
||||||
Object rv = OBJECT_INIT;
|
Object rv = OBJECT_INIT;
|
||||||
|
|
||||||
if (name.size == 0) {
|
VALIDATE_S(name.size > 0, "option name", "<empty>", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Empty option name");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
// Return values
|
// Return values
|
||||||
int64_t numval;
|
int64_t numval;
|
||||||
char *stringval = NULL;
|
char *stringval = NULL;
|
||||||
int flags = get_option_value_strict(name.data, &numval, &stringval, type, from);
|
|
||||||
|
|
||||||
if (!flags) {
|
int flags = get_option_value_strict(name.data, &numval, &stringval, type, from);
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
|
VALIDATE_S(flags != 0, "option name", name.data, {
|
||||||
name.data);
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (flags & SOPT_BOOL) {
|
if (flags & SOPT_BOOL) {
|
||||||
rv.type = kObjectTypeBoolean;
|
rv.type = kObjectTypeBoolean;
|
||||||
@ -374,20 +376,15 @@ static Object get_option_from(void *from, int type, String name, Error *err)
|
|||||||
rv.type = kObjectTypeInteger;
|
rv.type = kObjectTypeInteger;
|
||||||
rv.data.integer = numval;
|
rv.data.integer = numval;
|
||||||
} else if (flags & SOPT_STRING) {
|
} else if (flags & SOPT_STRING) {
|
||||||
if (stringval) {
|
if (!stringval) {
|
||||||
rv.type = kObjectTypeString;
|
api_set_error(err, kErrorTypeException, "Failed to get option '%s'", name.data);
|
||||||
rv.data.string.data = stringval;
|
return rv;
|
||||||
rv.data.string.size = strlen(stringval);
|
|
||||||
} else {
|
|
||||||
api_set_error(err, kErrorTypeException,
|
|
||||||
"Failed to get value for option '%s'",
|
|
||||||
name.data);
|
|
||||||
}
|
}
|
||||||
|
rv.type = kObjectTypeString;
|
||||||
|
rv.data.string.data = stringval;
|
||||||
|
rv.data.string.size = strlen(stringval);
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err,
|
api_set_error(err, kErrorTypeException, "Unknown type for option '%s'", name.data);
|
||||||
kErrorTypeException,
|
|
||||||
"Unknown type for option '%s'",
|
|
||||||
name.data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
@ -402,29 +399,22 @@ static Object get_option_from(void *from, int type, String name, Error *err)
|
|||||||
/// @param[out] err Details of an error that may have occurred
|
/// @param[out] err Details of an error that may have occurred
|
||||||
void set_option_to(uint64_t channel_id, void *to, int type, String name, Object value, Error *err)
|
void set_option_to(uint64_t channel_id, void *to, int type, String name, Object value, Error *err)
|
||||||
{
|
{
|
||||||
if (name.size == 0) {
|
VALIDATE_S(name.size > 0, "option name", "<empty>", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Empty option name");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
int flags = get_option_value_strict(name.data, NULL, NULL, type, to);
|
int flags = get_option_value_strict(name.data, NULL, NULL, type, to);
|
||||||
|
VALIDATE_S(flags != 0, "option name", name.data, {
|
||||||
if (flags == 0) {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'",
|
|
||||||
name.data);
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (value.type == kObjectTypeNil) {
|
if (value.type == kObjectTypeNil) {
|
||||||
if (type == SREQ_GLOBAL) {
|
if (type == SREQ_GLOBAL) {
|
||||||
api_set_error(err, kErrorTypeException, "Cannot unset option '%s'",
|
api_set_error(err, kErrorTypeException, "Cannot unset option '%s'", name.data);
|
||||||
name.data);
|
|
||||||
return;
|
return;
|
||||||
} else if (!(flags & SOPT_GLOBAL)) {
|
} else if (!(flags & SOPT_GLOBAL)) {
|
||||||
api_set_error(err,
|
api_set_error(err, kErrorTypeException,
|
||||||
kErrorTypeException,
|
"Cannot unset option '%s' because it doesn't have a global value",
|
||||||
"Cannot unset option '%s' "
|
|
||||||
"because it doesn't have a global value",
|
|
||||||
name.data);
|
name.data);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@ -437,39 +427,23 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object
|
|||||||
char *stringval = NULL;
|
char *stringval = NULL;
|
||||||
|
|
||||||
if (flags & SOPT_BOOL) {
|
if (flags & SOPT_BOOL) {
|
||||||
if (value.type != kObjectTypeBoolean) {
|
VALIDATE_FMT(value.type == kObjectTypeBoolean, "Option '%s' value must be Boolean", name.data, {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"Option '%s' requires a Boolean value",
|
|
||||||
name.data);
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
numval = value.data.boolean;
|
numval = value.data.boolean;
|
||||||
} else if (flags & SOPT_NUM) {
|
} else if (flags & SOPT_NUM) {
|
||||||
if (value.type != kObjectTypeInteger) {
|
VALIDATE_FMT(value.type == kObjectTypeInteger, "Option '%s' value must be Integer", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Option '%s' requires an integer value",
|
|
||||||
name.data);
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
VALIDATE_FMT((value.data.integer <= INT_MAX && value.data.integer >= INT_MIN),
|
||||||
if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) {
|
"Option '%s' value is out of range", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Value for option '%s' is out of range",
|
|
||||||
name.data);
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
numval = (int)value.data.integer;
|
numval = (int)value.data.integer;
|
||||||
} else {
|
} else {
|
||||||
if (value.type != kObjectTypeString) {
|
VALIDATE_FMT(value.type == kObjectTypeString, "Option '%s' value must be String", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Option '%s' requires a string value",
|
|
||||||
name.data);
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
stringval = value.data.string.data;
|
stringval = value.data.string.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,6 +827,36 @@ int object_to_hl_id(Object obj, const char *what, Error *err)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *api_typename(ObjectType t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case kObjectTypeNil:
|
||||||
|
return "nil";
|
||||||
|
case kObjectTypeBoolean:
|
||||||
|
return "Boolean";
|
||||||
|
case kObjectTypeInteger:
|
||||||
|
return "Integer";
|
||||||
|
case kObjectTypeFloat:
|
||||||
|
return "Float";
|
||||||
|
case kObjectTypeString:
|
||||||
|
return "String";
|
||||||
|
case kObjectTypeArray:
|
||||||
|
return "Array";
|
||||||
|
case kObjectTypeDictionary:
|
||||||
|
return "Dict";
|
||||||
|
case kObjectTypeLuaRef:
|
||||||
|
return "Function";
|
||||||
|
case kObjectTypeBuffer:
|
||||||
|
return "Buffer";
|
||||||
|
case kObjectTypeWindow:
|
||||||
|
return "Window";
|
||||||
|
case kObjectTypeTabpage:
|
||||||
|
return "Tabpage";
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HlMessage parse_hl_msg(Array chunks, Error *err)
|
HlMessage parse_hl_msg(Array chunks, Error *err)
|
||||||
{
|
{
|
||||||
HlMessage hl_msg = KV_INITIAL_VALUE;
|
HlMessage hl_msg = KV_INITIAL_VALUE;
|
||||||
|
69
src/nvim/api/private/validate.h
Normal file
69
src/nvim/api/private/validate.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef NVIM_API_PRIVATE_VALIDATE_H
|
||||||
|
#define NVIM_API_PRIVATE_VALIDATE_H
|
||||||
|
|
||||||
|
#include "nvim/api/private/defs.h"
|
||||||
|
#include "nvim/api/private/helpers.h"
|
||||||
|
|
||||||
|
#define VALIDATE_INT(cond, name, val_, code) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid " name ": %" PRId64, val_); \
|
||||||
|
code; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VALIDATE_S(cond, name, val_, code) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
if (strequal(val_, "")) { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid " name); \
|
||||||
|
} else { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid " name ": '%s'", val_); \
|
||||||
|
} \
|
||||||
|
code; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VALIDATE_R(cond, name, code) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, "'" name "' is required"); \
|
||||||
|
code; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VALIDATE_EXP(cond, name, expected, actual, code) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid " name ": expected %s, got %s", \
|
||||||
|
expected, actual); \
|
||||||
|
code; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VALIDATE_T(name, expected_t, actual_t, code) \
|
||||||
|
do { \
|
||||||
|
if (expected_t != actual_t) { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid %s: expected %s, got %s", \
|
||||||
|
name, api_typename(expected_t), api_typename(actual_t)); \
|
||||||
|
code; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VALIDATE(cond, msg_, code) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, "%s", msg_); \
|
||||||
|
code; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define VALIDATE_FMT(cond, fmt_, msg_, code) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
api_set_error(err, kErrorTypeValidation, fmt_, msg_); \
|
||||||
|
code; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif // NVIM_API_PRIVATE_VALIDATE_H
|
@ -18,6 +18,7 @@
|
|||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/dispatch.h"
|
#include "nvim/api/private/dispatch.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/api/vim.h"
|
#include "nvim/api/vim.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
@ -193,10 +194,9 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err)
|
|||||||
void nvim_set_hl_ns(Integer ns_id, Error *err)
|
void nvim_set_hl_ns(Integer ns_id, Error *err)
|
||||||
FUNC_API_SINCE(10)
|
FUNC_API_SINCE(10)
|
||||||
{
|
{
|
||||||
if (ns_id < 0) {
|
VALIDATE_INT((ns_id >= 0), "namespace", ns_id, {
|
||||||
api_set_error(err, kErrorTypeValidation, "no such namespace");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
ns_hl_global = (NS)ns_id;
|
ns_hl_global = (NS)ns_id;
|
||||||
hl_check_ns();
|
hl_check_ns();
|
||||||
@ -500,10 +500,9 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
|
|||||||
Integer nvim_strwidth(String text, Error *err)
|
Integer nvim_strwidth(String text, Error *err)
|
||||||
FUNC_API_SINCE(1)
|
FUNC_API_SINCE(1)
|
||||||
{
|
{
|
||||||
if (text.size > INT_MAX) {
|
VALIDATE((text.size <= INT_MAX), "text length (too long)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "String is too long");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
});
|
||||||
|
|
||||||
return (Integer)mb_string2cells(text.data);
|
return (Integer)mb_string2cells(text.data);
|
||||||
}
|
}
|
||||||
@ -575,9 +574,7 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E
|
|||||||
{
|
{
|
||||||
bool is_lua = api_object_to_bool(opts->is_lua, "is_lua", false, err);
|
bool is_lua = api_object_to_bool(opts->is_lua, "is_lua", false, err);
|
||||||
bool source = api_object_to_bool(opts->do_source, "do_source", false, err);
|
bool source = api_object_to_bool(opts->do_source, "do_source", false, err);
|
||||||
if (source && !nlua_is_deferred_safe()) {
|
VALIDATE((!source || nlua_is_deferred_safe()), "'do_source' used in fast callback", {});
|
||||||
api_set_error(err, kErrorTypeValidation, "'do_source' cannot be used in fast callback");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
return (Array)ARRAY_DICT_INIT;
|
return (Array)ARRAY_DICT_INIT;
|
||||||
@ -602,10 +599,9 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E
|
|||||||
void nvim_set_current_dir(String dir, Error *err)
|
void nvim_set_current_dir(String dir, Error *err)
|
||||||
FUNC_API_SINCE(1)
|
FUNC_API_SINCE(1)
|
||||||
{
|
{
|
||||||
if (dir.size >= MAXPATHL) {
|
VALIDATE((dir.size < MAXPATHL), "directory name (too long)", {
|
||||||
api_set_error(err, kErrorTypeValidation, "Directory name is too long");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
char string[MAXPATHL];
|
char string[MAXPATHL];
|
||||||
memcpy(string, dir.data, dir.size);
|
memcpy(string, dir.data, dir.size);
|
||||||
@ -664,16 +660,14 @@ Object nvim_get_var(String name, Error *err)
|
|||||||
{
|
{
|
||||||
dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size);
|
dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size);
|
||||||
if (di == NULL) { // try to autoload script
|
if (di == NULL) { // try to autoload script
|
||||||
if (!script_autoload(name.data, name.size, false) || aborting()) {
|
VALIDATE_S((script_autoload(name.data, name.size, false) && !aborting()), "key", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Key not found: %s", name.data);
|
|
||||||
return (Object)OBJECT_INIT;
|
return (Object)OBJECT_INIT;
|
||||||
}
|
});
|
||||||
di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size);
|
di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size);
|
||||||
}
|
}
|
||||||
if (di == NULL) {
|
VALIDATE_S((di != NULL), "key (not found)", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Key not found: %s", name.data);
|
|
||||||
return (Object)OBJECT_INIT;
|
return (Object)OBJECT_INIT;
|
||||||
}
|
});
|
||||||
return vim_to_object(&di->di_tv);
|
return vim_to_object(&di->di_tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -991,16 +985,14 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err)
|
|||||||
String k = opts.items[i].key;
|
String k = opts.items[i].key;
|
||||||
Object *v = &opts.items[i].value;
|
Object *v = &opts.items[i].value;
|
||||||
if (strequal("on_input", k.data)) {
|
if (strequal("on_input", k.data)) {
|
||||||
if (v->type != kObjectTypeLuaRef) {
|
VALIDATE_T("on_input", kObjectTypeLuaRef, v->type, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"%s is not a function", "on_input");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
});
|
||||||
cb = v->data.luaref;
|
cb = v->data.luaref;
|
||||||
v->data.luaref = LUA_NOREF;
|
v->data.luaref = LUA_NOREF;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
VALIDATE_S(false, "key", k.data, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1075,9 +1067,7 @@ void nvim_chan_send(Integer chan, String data, Error *err)
|
|||||||
|
|
||||||
channel_send((uint64_t)chan, data.data, data.size,
|
channel_send((uint64_t)chan, data.data, data.size,
|
||||||
false, &error);
|
false, &error);
|
||||||
if (error) {
|
VALIDATE(!error, error, {});
|
||||||
api_set_error(err, kErrorTypeValidation, "%s", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current list of tabpage handles.
|
/// Gets the current list of tabpage handles.
|
||||||
@ -1164,10 +1154,9 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
|
|||||||
static bool draining = false;
|
static bool draining = false;
|
||||||
bool cancel = false;
|
bool cancel = false;
|
||||||
|
|
||||||
if (phase < -1 || phase > 3) {
|
VALIDATE_INT((phase >= -1 && phase <= 3), "phase", phase, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid phase: %" PRId64, phase);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
});
|
||||||
Array args = ARRAY_DICT_INIT;
|
Array args = ARRAY_DICT_INIT;
|
||||||
Object rv = OBJECT_INIT;
|
Object rv = OBJECT_INIT;
|
||||||
if (phase == -1 || phase == 1) { // Start of paste-stream.
|
if (phase == -1 || phase == 1) { // Start of paste-stream.
|
||||||
@ -1234,20 +1223,17 @@ void nvim_put(ArrayOf(String) lines, String type, Boolean after, Boolean follow,
|
|||||||
FUNC_API_CHECK_TEXTLOCK
|
FUNC_API_CHECK_TEXTLOCK
|
||||||
{
|
{
|
||||||
yankreg_T *reg = xcalloc(1, sizeof(yankreg_T));
|
yankreg_T *reg = xcalloc(1, sizeof(yankreg_T));
|
||||||
if (!prepare_yankreg_from_object(reg, type, lines.size)) {
|
VALIDATE_S((prepare_yankreg_from_object(reg, type, lines.size)), "type", type.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "Invalid type: '%s'", type.data);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
if (lines.size == 0) {
|
if (lines.size == 0) {
|
||||||
goto cleanup; // Nothing to do.
|
goto cleanup; // Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < lines.size; i++) {
|
for (size_t i = 0; i < lines.size; i++) {
|
||||||
if (lines.items[i].type != kObjectTypeString) {
|
VALIDATE_T("line", kObjectTypeString, lines.items[i].type, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Invalid lines (expected array of strings)");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
});
|
||||||
String line = lines.items[i].data.string;
|
String line = lines.items[i].data.string;
|
||||||
reg->y_array[i] = xmemdupz(line.data, line.size);
|
reg->y_array[i] = xmemdupz(line.data, line.size);
|
||||||
memchrsub(reg->y_array[i], NUL, NL, line.size);
|
memchrsub(reg->y_array[i], NUL, NL, line.size);
|
||||||
@ -1351,8 +1337,9 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err)
|
|||||||
if (opts->types.type == kObjectTypeArray) {
|
if (opts->types.type == kObjectTypeArray) {
|
||||||
types = opts->types.data.array;
|
types = opts->types.data.array;
|
||||||
} else if (opts->types.type != kObjectTypeNil) {
|
} else if (opts->types.type != kObjectTypeNil) {
|
||||||
api_set_error(err, kErrorTypeValidation, "invalid value for key: types");
|
VALIDATE_T("types", kObjectTypeArray, opts->types.type, {
|
||||||
return (Dictionary)ARRAY_DICT_INIT;
|
return (Dictionary)ARRAY_DICT_INIT;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int int_types = types.size > 0 ? 0 : kCtxAll;
|
int int_types = types.size > 0 ? 0 : kCtxAll;
|
||||||
@ -1373,8 +1360,9 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err)
|
|||||||
} else if (strequal(s, "funcs")) {
|
} else if (strequal(s, "funcs")) {
|
||||||
int_types |= kCtxFuncs;
|
int_types |= kCtxFuncs;
|
||||||
} else {
|
} else {
|
||||||
api_set_error(err, kErrorTypeValidation, "unexpected type: %s", s);
|
VALIDATE_S(false, "type", s, {
|
||||||
return (Dictionary)ARRAY_DICT_INIT;
|
return (Dictionary)ARRAY_DICT_INIT;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1651,34 +1639,20 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er
|
|||||||
|
|
||||||
size_t i; // also used for freeing the variables
|
size_t i; // also used for freeing the variables
|
||||||
for (i = 0; i < calls.size; i++) {
|
for (i = 0; i < calls.size; i++) {
|
||||||
if (calls.items[i].type != kObjectTypeArray) {
|
VALIDATE_T("calls item", kObjectTypeArray, calls.items[i].type, {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"Items in calls array must be arrays");
|
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
});
|
||||||
Array call = calls.items[i].data.array;
|
Array call = calls.items[i].data.array;
|
||||||
if (call.size != 2) {
|
VALIDATE((call.size == 2), "Items in calls array must be arrays of size 2", {
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"Items in calls array must be arrays of size 2");
|
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
});
|
||||||
|
VALIDATE_T("name", kObjectTypeString, call.items[0].type, {
|
||||||
if (call.items[0].type != kObjectTypeString) {
|
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"Name must be String");
|
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
});
|
||||||
String name = call.items[0].data.string;
|
String name = call.items[0].data.string;
|
||||||
|
VALIDATE_T("args", kObjectTypeArray, call.items[1].type, {
|
||||||
if (call.items[1].type != kObjectTypeArray) {
|
|
||||||
api_set_error(err,
|
|
||||||
kErrorTypeValidation,
|
|
||||||
"Args must be Array");
|
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
});
|
||||||
Array args = call.items[1].data.array;
|
Array args = call.items[1].data.array;
|
||||||
|
|
||||||
MsgpackRpcRequestHandler handler =
|
MsgpackRpcRequestHandler handler =
|
||||||
@ -1937,10 +1911,9 @@ void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Di
|
|||||||
Error *err)
|
Error *err)
|
||||||
FUNC_API_SINCE(6)
|
FUNC_API_SINCE(6)
|
||||||
{
|
{
|
||||||
if (opts.size > 0) {
|
VALIDATE((opts.size == 0), "opts dict isn't empty", {
|
||||||
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (finish) {
|
if (finish) {
|
||||||
insert = true;
|
insert = true;
|
||||||
@ -1961,13 +1934,10 @@ Array nvim__inspect_cell(Integer grid, Integer row, Integer col, Arena *arena, E
|
|||||||
g = &pum_grid;
|
g = &pum_grid;
|
||||||
} else if (grid > 1) {
|
} else if (grid > 1) {
|
||||||
win_T *wp = get_win_by_grid_handle((handle_T)grid);
|
win_T *wp = get_win_by_grid_handle((handle_T)grid);
|
||||||
if (wp != NULL && wp->w_grid_alloc.chars != NULL) {
|
VALIDATE_INT((wp != NULL && wp->w_grid_alloc.chars != NULL), "grid handle", grid, {
|
||||||
g = &wp->w_grid_alloc;
|
|
||||||
} else {
|
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"No grid with the given handle");
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
});
|
||||||
|
g = &wp->w_grid_alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row < 0 || row >= g->rows
|
if (row < 0 || row >= g->rows
|
||||||
@ -2009,20 +1979,16 @@ Boolean nvim_del_mark(String name, Error *err)
|
|||||||
FUNC_API_SINCE(8)
|
FUNC_API_SINCE(8)
|
||||||
{
|
{
|
||||||
bool res = false;
|
bool res = false;
|
||||||
if (name.size != 1) {
|
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Mark name must be a single character");
|
|
||||||
return res;
|
return res;
|
||||||
}
|
});
|
||||||
// Only allow file/uppercase marks
|
// Only allow file/uppercase marks
|
||||||
// TODO(muniter): Refactor this ASCII_ISUPPER macro to a proper function
|
// TODO(muniter): Refactor this ASCII_ISUPPER macro to a proper function
|
||||||
if (ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data)) {
|
VALIDATE_S((ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data)),
|
||||||
res = set_mark(NULL, name, 0, 0, err);
|
"mark name (must be file/uppercase)", name.data, {
|
||||||
} else {
|
return res;
|
||||||
api_set_error(err, kErrorTypeValidation,
|
});
|
||||||
"Only file/uppercase marks allowed, invalid mark name: '%c'",
|
res = set_mark(NULL, name, 0, 0, err);
|
||||||
*name.data);
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2043,16 +2009,13 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
|
|||||||
{
|
{
|
||||||
Array rv = ARRAY_DICT_INIT;
|
Array rv = ARRAY_DICT_INIT;
|
||||||
|
|
||||||
if (name.size != 1) {
|
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation,
|
|
||||||
"Mark name must be a single character");
|
|
||||||
return rv;
|
return rv;
|
||||||
} else if (!(ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data))) {
|
});
|
||||||
api_set_error(err, kErrorTypeValidation,
|
VALIDATE_S((ASCII_ISUPPER(*name.data) || ascii_isdigit(*name.data)),
|
||||||
"Only file/uppercase marks allowed, invalid mark name: '%c'",
|
"mark name (must be file/uppercase)", name.data, {
|
||||||
*name.data);
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
});
|
||||||
|
|
||||||
xfmark_T *mark = mark_get_global(false, *name.data); // false avoids loading the mark buffer
|
xfmark_T *mark = mark_get_global(false, *name.data); // false avoids loading the mark buffer
|
||||||
pos_T pos = mark->fmark.mark;
|
pos_T pos = mark->fmark.mark;
|
||||||
@ -2137,27 +2100,28 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
|||||||
|
|
||||||
if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) {
|
if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) {
|
||||||
const char *const errmsg = check_stl_option(str.data);
|
const char *const errmsg = check_stl_option(str.data);
|
||||||
if (errmsg) {
|
VALIDATE(!errmsg, errmsg, {
|
||||||
api_set_error(err, kErrorTypeValidation, "%s", errmsg);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HAS_KEY(opts->winid)) {
|
if (HAS_KEY(opts->winid)) {
|
||||||
if (opts->winid.type != kObjectTypeInteger) {
|
VALIDATE_T("winid", kObjectTypeInteger, opts->winid.type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "winid must be an integer");
|
|
||||||
return result;
|
return result;
|
||||||
}
|
});
|
||||||
|
|
||||||
window = (Window)opts->winid.data.integer;
|
window = (Window)opts->winid.data.integer;
|
||||||
}
|
}
|
||||||
if (HAS_KEY(opts->fillchar)) {
|
if (HAS_KEY(opts->fillchar)) {
|
||||||
if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size == 0
|
VALIDATE_T("fillchar", kObjectTypeString, opts->fillchar.type, {
|
||||||
|| ((size_t)utf_ptr2len(opts->fillchar.data.string.data)
|
|
||||||
!= opts->fillchar.data.string.size)) {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "fillchar must be a single character");
|
|
||||||
return result;
|
return result;
|
||||||
}
|
});
|
||||||
|
VALIDATE((opts->fillchar.data.string.size != 0
|
||||||
|
&& ((size_t)utf_ptr2len(opts->fillchar.data.string.data)
|
||||||
|
== opts->fillchar.data.string.size)),
|
||||||
|
"Invalid fillchar (not a single character)", {
|
||||||
|
return result;
|
||||||
|
});
|
||||||
fillchar = utf_ptr2char(opts->fillchar.data.string.data);
|
fillchar = utf_ptr2char(opts->fillchar.data.string.data);
|
||||||
}
|
}
|
||||||
if (HAS_KEY(opts->highlights)) {
|
if (HAS_KEY(opts->highlights)) {
|
||||||
@ -2211,10 +2175,9 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (HAS_KEY(opts->maxwidth)) {
|
if (HAS_KEY(opts->maxwidth)) {
|
||||||
if (opts->maxwidth.type != kObjectTypeInteger) {
|
VALIDATE_T("maxwidth", kObjectTypeInteger, opts->maxwidth.type, {
|
||||||
api_set_error(err, kErrorTypeValidation, "maxwidth must be an integer");
|
|
||||||
return result;
|
return result;
|
||||||
}
|
});
|
||||||
|
|
||||||
maxwidth = (int)opts->maxwidth.data.integer;
|
maxwidth = (int)opts->maxwidth.data.integer;
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "nvim/api/extmark.h"
|
#include "nvim/api/extmark.h"
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/buffer.h"
|
#include "nvim/buffer.h"
|
||||||
@ -5624,10 +5625,9 @@ long get_sidescrolloff_value(win_T *wp)
|
|||||||
Dictionary get_vimoption(String name, Error *err)
|
Dictionary get_vimoption(String name, Error *err)
|
||||||
{
|
{
|
||||||
int opt_idx = findoption_len((const char *)name.data, name.size);
|
int opt_idx = findoption_len((const char *)name.data, name.size);
|
||||||
if (opt_idx < 0) {
|
VALIDATE_S(opt_idx >= 0, "option (not found)", name.data, {
|
||||||
api_set_error(err, kErrorTypeValidation, "no such option: '%s'", name.data);
|
|
||||||
return (Dictionary)ARRAY_DICT_INIT;
|
return (Dictionary)ARRAY_DICT_INIT;
|
||||||
}
|
});
|
||||||
return vimoption2dict(&options[opt_idx]);
|
return vimoption2dict(&options[opt_idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "klib/kvec.h"
|
#include "klib/kvec.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/api/private/validate.h"
|
||||||
#include "nvim/api/ui.h"
|
#include "nvim/api/ui.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
@ -608,7 +609,7 @@ Array ui_array(void)
|
|||||||
return all_uis;
|
return all_uis;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
|
void ui_grid_resize(handle_T grid_handle, int width, int height, Error *err)
|
||||||
{
|
{
|
||||||
if (grid_handle == DEFAULT_GRID_HANDLE) {
|
if (grid_handle == DEFAULT_GRID_HANDLE) {
|
||||||
screen_resize(width, height);
|
screen_resize(width, height);
|
||||||
@ -616,11 +617,9 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
win_T *wp = get_win_by_grid_handle(grid_handle);
|
win_T *wp = get_win_by_grid_handle(grid_handle);
|
||||||
if (wp == NULL) {
|
VALIDATE_INT((wp != NULL), "window handle", (int64_t)grid_handle, {
|
||||||
api_set_error(error, kErrorTypeValidation,
|
|
||||||
"No window with the given handle");
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (wp->w_floating) {
|
if (wp->w_floating) {
|
||||||
if (width != wp->w_width || height != wp->w_height) {
|
if (width != wp->w_width || height != wp->w_height) {
|
||||||
|
@ -21,7 +21,7 @@ describe('autocmd api', function()
|
|||||||
callback = "NotAllowed",
|
callback = "NotAllowed",
|
||||||
})
|
})
|
||||||
|
|
||||||
eq("specify either 'callback' or 'command', not both", rv)
|
eq("Cannot use both 'callback' and 'command'", rv)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('doesnt leak when you use ++once', function()
|
it('doesnt leak when you use ++once', function()
|
||||||
@ -66,7 +66,7 @@ describe('autocmd api', function()
|
|||||||
pattern = "*.py",
|
pattern = "*.py",
|
||||||
})
|
})
|
||||||
|
|
||||||
eq("cannot pass both: 'pattern' and 'buffer' for the same autocmd", rv)
|
eq("Cannot use both 'pattern' and 'buffer' for the same autocmd", rv)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('does not allow passing invalid buffers', function()
|
it('does not allow passing invalid buffers', function()
|
||||||
@ -407,8 +407,8 @@ describe('autocmd api', function()
|
|||||||
pattern = "<buffer=2>",
|
pattern = "<buffer=2>",
|
||||||
}}, aus)
|
}}, aus)
|
||||||
|
|
||||||
eq("Invalid value for 'buffer': must be an integer or array of integers", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" }))
|
eq("Invalid buffer: expected Integer or Array, got String", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" }))
|
||||||
eq("Invalid value for 'buffer': must be an integer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } }))
|
eq("Invalid buffer: expected Integer, got String", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } }))
|
||||||
eq("Invalid buffer id: 42", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { 42 } }))
|
eq("Invalid buffer id: 42", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { 42 } }))
|
||||||
|
|
||||||
local bufs = {}
|
local bufs = {}
|
||||||
@ -416,7 +416,7 @@ describe('autocmd api', function()
|
|||||||
table.insert(bufs, meths.create_buf(true, false))
|
table.insert(bufs, meths.create_buf(true, false))
|
||||||
end
|
end
|
||||||
|
|
||||||
eq("Too many buffers. Please limit yourself to 256 or fewer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs }))
|
eq("Too many buffers (maximum of 256)", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs }))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('should return autocmds when group is specified by id', function()
|
it('should return autocmds when group is specified by id', function()
|
||||||
@ -578,7 +578,7 @@ describe('autocmd api', function()
|
|||||||
]], {}))
|
]], {}))
|
||||||
|
|
||||||
eq(false, success)
|
eq(false, success)
|
||||||
matches('invalid augroup: NotDefined', code)
|
matches("Invalid group: 'NotDefined'", code)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('raises error for undefined augroup id', function()
|
it('raises error for undefined augroup id', function()
|
||||||
@ -596,7 +596,7 @@ describe('autocmd api', function()
|
|||||||
]], {}))
|
]], {}))
|
||||||
|
|
||||||
eq(false, success)
|
eq(false, success)
|
||||||
matches('invalid augroup: 1', code)
|
matches('Invalid group: 1', code)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('raises error for invalid group type', function()
|
it('raises error for invalid group type', function()
|
||||||
@ -611,7 +611,7 @@ describe('autocmd api', function()
|
|||||||
]], {}))
|
]], {}))
|
||||||
|
|
||||||
eq(false, success)
|
eq(false, success)
|
||||||
matches("'group' must be a string or an integer", code)
|
matches("Invalid group: expected String or Integer, got Boolean", code)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('raises error for invalid pattern array', function()
|
it('raises error for invalid pattern array', function()
|
||||||
@ -625,7 +625,7 @@ describe('autocmd api', function()
|
|||||||
]], {}))
|
]], {}))
|
||||||
|
|
||||||
eq(false, success)
|
eq(false, success)
|
||||||
matches("All entries in 'pattern' must be strings", code)
|
matches("Invalid 'pattern' item type: expected String, got Array", code)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -762,7 +762,7 @@ describe('API: buffer events:', function()
|
|||||||
it('returns a proper error on nonempty options dict', function()
|
it('returns a proper error on nonempty options dict', function()
|
||||||
clear()
|
clear()
|
||||||
local b = editoriginal(false)
|
local b = editoriginal(false)
|
||||||
eq("unexpected key: builtin", pcall_err(buffer, 'attach', b, false, {builtin="asfd"}))
|
eq("Invalid key: 'builtin'", pcall_err(buffer, 'attach', b, false, {builtin="asfd"}))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('nvim_buf_attach returns response after delay #8634', function()
|
it('nvim_buf_attach returns response after delay #8634', function()
|
||||||
|
@ -543,7 +543,7 @@ describe('nvim_create_user_command', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('does not allow invalid command names', function()
|
it('does not allow invalid command names', function()
|
||||||
matches("'name' must begin with an uppercase letter", pcall_err(exec_lua, [[
|
matches("Invalid command name %(must begin with an uppercase letter%): 'test'", pcall_err(exec_lua, [[
|
||||||
vim.api.nvim_create_user_command('test', 'echo "hi"', {})
|
vim.api.nvim_create_user_command('test', 'echo "hi"', {})
|
||||||
]]))
|
]]))
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ describe('API/extmarks', function()
|
|||||||
end_col = 0,
|
end_col = 0,
|
||||||
end_row = 1
|
end_row = 1
|
||||||
})
|
})
|
||||||
eq("end_col value outside range",
|
eq("Invalid end_col: '(out of range)'",
|
||||||
pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }))
|
pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@ -1344,10 +1344,10 @@ describe('API/extmarks', function()
|
|||||||
|
|
||||||
it('throws consistent error codes', function()
|
it('throws consistent error codes', function()
|
||||||
local ns_invalid = ns2 + 1
|
local ns_invalid = ns2 + 1
|
||||||
eq("Invalid ns_id", pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2]))
|
eq("Invalid ns_id: 3", pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2]))
|
||||||
eq("Invalid ns_id", pcall_err(curbufmeths.del_extmark, ns_invalid, marks[1]))
|
eq("Invalid ns_id: 3", pcall_err(curbufmeths.del_extmark, ns_invalid, marks[1]))
|
||||||
eq("Invalid ns_id", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
|
eq("Invalid ns_id: 3", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
|
||||||
eq("Invalid ns_id", pcall_err(get_extmark_by_id, ns_invalid, marks[1]))
|
eq("Invalid ns_id: 3", pcall_err(get_extmark_by_id, ns_invalid, marks[1]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('when col = line-length, set the mark on eol', function()
|
it('when col = line-length, set the mark on eol', function()
|
||||||
@ -1362,13 +1362,13 @@ describe('API/extmarks', function()
|
|||||||
|
|
||||||
it('when col = line-length, set the mark on eol', function()
|
it('when col = line-length, set the mark on eol', function()
|
||||||
local invalid_col = init_text:len() + 1
|
local invalid_col = init_text:len() + 1
|
||||||
eq("col value outside range", pcall_err(set_extmark, ns, marks[1], 0, invalid_col))
|
eq("Invalid col: '(out of range)'", pcall_err(set_extmark, ns, marks[1], 0, invalid_col))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails when line > line_count', function()
|
it('fails when line > line_count', function()
|
||||||
local invalid_col = init_text:len() + 1
|
local invalid_col = init_text:len() + 1
|
||||||
local invalid_lnum = 3
|
local invalid_lnum = 3
|
||||||
eq('line value outside range', pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col))
|
eq("Invalid line: '(out of range)'", pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col))
|
||||||
eq({}, get_extmark_by_id(ns, marks[1]))
|
eq({}, get_extmark_by_id(ns, marks[1]))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -1155,7 +1155,7 @@ describe('API', function()
|
|||||||
|
|
||||||
describe('nvim_put', function()
|
describe('nvim_put', function()
|
||||||
it('validates args', function()
|
it('validates args', function()
|
||||||
eq('Invalid lines (expected array of strings)',
|
eq("Invalid line: expected String, got Integer",
|
||||||
pcall_err(request, 'nvim_put', {42}, 'l', false, false))
|
pcall_err(request, 'nvim_put', {42}, 'l', false, false))
|
||||||
eq("Invalid type: 'x'",
|
eq("Invalid type: 'x'",
|
||||||
pcall_err(request, 'nvim_put', {'foo'}, 'x', false, false))
|
pcall_err(request, 'nvim_put', {'foo'}, 'x', false, false))
|
||||||
@ -1410,6 +1410,15 @@ describe('API', function()
|
|||||||
ok(not nvim('get_option_value', 'equalalways', {}))
|
ok(not nvim('get_option_value', 'equalalways', {}))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('validation', function()
|
||||||
|
eq("Invalid scope (expected 'local' or 'global')",
|
||||||
|
pcall_err(nvim, 'get_option_value', 'scrolloff', {scope = 'bogus'}))
|
||||||
|
eq("Invalid scope (expected 'local' or 'global')",
|
||||||
|
pcall_err(nvim, 'set_option_value', 'scrolloff', 1, {scope = 'bogus'}))
|
||||||
|
eq("Invalid scope: expected String, got Integer",
|
||||||
|
pcall_err(nvim, 'get_option_value', 'scrolloff', {scope = 42}))
|
||||||
|
end)
|
||||||
|
|
||||||
it('can get local values when global value is set', function()
|
it('can get local values when global value is set', function()
|
||||||
eq(0, nvim('get_option_value', 'scrolloff', {}))
|
eq(0, nvim('get_option_value', 'scrolloff', {}))
|
||||||
eq(-1, nvim('get_option_value', 'scrolloff', {scope = 'local'}))
|
eq(-1, nvim('get_option_value', 'scrolloff', {scope = 'local'}))
|
||||||
@ -1780,9 +1789,9 @@ describe('API', function()
|
|||||||
it('validates args', function()
|
it('validates args', function()
|
||||||
eq("Invalid key: 'blah'",
|
eq("Invalid key: 'blah'",
|
||||||
pcall_err(nvim, 'get_context', {blah={}}))
|
pcall_err(nvim, 'get_context', {blah={}}))
|
||||||
eq('invalid value for key: types',
|
eq("Invalid types: expected Array, got Integer",
|
||||||
pcall_err(nvim, 'get_context', {types=42}))
|
pcall_err(nvim, 'get_context', {types=42}))
|
||||||
eq('unexpected type: zub',
|
eq("Invalid type: 'zub'",
|
||||||
pcall_err(nvim, 'get_context', {types={'jumps', 'zub', 'zam',}}))
|
pcall_err(nvim, 'get_context', {types={'jumps', 'zub', 'zam',}}))
|
||||||
end)
|
end)
|
||||||
it('returns map of current editor state', function()
|
it('returns map of current editor state', function()
|
||||||
@ -2223,15 +2232,14 @@ describe('API', function()
|
|||||||
eq(5, meths.get_var('avar'))
|
eq(5, meths.get_var('avar'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('throws error on malformed arguments', function()
|
it('validation', function()
|
||||||
local req = {
|
local req = {
|
||||||
{'nvim_set_var', {'avar', 1}},
|
{'nvim_set_var', {'avar', 1}},
|
||||||
{'nvim_set_var'},
|
{'nvim_set_var'},
|
||||||
{'nvim_set_var', {'avar', 2}},
|
{'nvim_set_var', {'avar', 2}},
|
||||||
}
|
}
|
||||||
local status, err = pcall(meths.call_atomic, req)
|
eq('Items in calls array must be arrays of size 2',
|
||||||
eq(false, status)
|
pcall_err(meths.call_atomic, req))
|
||||||
ok(err:match('Items in calls array must be arrays of size 2') ~= nil)
|
|
||||||
-- call before was done, but not after
|
-- call before was done, but not after
|
||||||
eq(1, meths.get_var('avar'))
|
eq(1, meths.get_var('avar'))
|
||||||
|
|
||||||
@ -2239,18 +2247,16 @@ describe('API', function()
|
|||||||
{ 'nvim_set_var', { 'bvar', { 2, 3 } } },
|
{ 'nvim_set_var', { 'bvar', { 2, 3 } } },
|
||||||
12,
|
12,
|
||||||
}
|
}
|
||||||
status, err = pcall(meths.call_atomic, req)
|
eq("Invalid calls item: expected Array, got Integer",
|
||||||
eq(false, status)
|
pcall_err(meths.call_atomic, req))
|
||||||
ok(err:match('Items in calls array must be arrays') ~= nil)
|
|
||||||
eq({2,3}, meths.get_var('bvar'))
|
eq({2,3}, meths.get_var('bvar'))
|
||||||
|
|
||||||
req = {
|
req = {
|
||||||
{'nvim_set_current_line', 'little line'},
|
{'nvim_set_current_line', 'little line'},
|
||||||
{'nvim_set_var', {'avar', 3}},
|
{'nvim_set_var', {'avar', 3}},
|
||||||
}
|
}
|
||||||
status, err = pcall(meths.call_atomic, req)
|
eq("Invalid args: expected Array, got String",
|
||||||
eq(false, status)
|
pcall_err(meths.call_atomic, req))
|
||||||
ok(err:match('Args must be Array') ~= nil)
|
|
||||||
-- call before was done, but not after
|
-- call before was done, but not after
|
||||||
eq(1, meths.get_var('avar'))
|
eq(1, meths.get_var('avar'))
|
||||||
eq({''}, meths.buf_get_lines(0, 0, -1, true))
|
eq({''}, meths.buf_get_lines(0, 0, -1, true))
|
||||||
@ -2750,7 +2756,7 @@ describe('API', function()
|
|||||||
|
|
||||||
describe('nvim_get_option_info', function()
|
describe('nvim_get_option_info', function()
|
||||||
it('should error for unknown options', function()
|
it('should error for unknown options', function()
|
||||||
eq("no such option: 'bogus'", pcall_err(meths.get_option_info, 'bogus'))
|
eq("Invalid option (not found): 'bogus'", pcall_err(meths.get_option_info, 'bogus'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('should return the same options for short and long name', function()
|
it('should return the same options for short and long name', function()
|
||||||
@ -3031,10 +3037,10 @@ describe('API', function()
|
|||||||
eq(true, meths.del_mark('F'))
|
eq(true, meths.del_mark('F'))
|
||||||
eq({0, 0}, meths.buf_get_mark(buf, 'F'))
|
eq({0, 0}, meths.buf_get_mark(buf, 'F'))
|
||||||
end)
|
end)
|
||||||
it('fails when invalid marks are used', function()
|
it('validation', function()
|
||||||
eq(false, pcall(meths.del_mark, 'f'))
|
eq("Invalid mark name (must be file/uppercase): 'f'", pcall_err(meths.del_mark, 'f'))
|
||||||
eq(false, pcall(meths.del_mark, '!'))
|
eq("Invalid mark name (must be file/uppercase): '!'", pcall_err(meths.del_mark, '!'))
|
||||||
eq(false, pcall(meths.del_mark, 'fail'))
|
eq("Invalid mark name (must be a single char): 'fail'", pcall_err(meths.del_mark, 'fail'))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
describe('nvim_get_mark', function()
|
describe('nvim_get_mark', function()
|
||||||
@ -3048,10 +3054,10 @@ describe('API', function()
|
|||||||
assert(string.find(mark[4], "mybuf$"))
|
assert(string.find(mark[4], "mybuf$"))
|
||||||
eq({2, 2, buf.id, mark[4]}, mark)
|
eq({2, 2, buf.id, mark[4]}, mark)
|
||||||
end)
|
end)
|
||||||
it('fails when invalid marks are used', function()
|
it('validation', function()
|
||||||
eq(false, pcall(meths.del_mark, 'f'))
|
eq("Invalid mark name (must be file/uppercase): 'f'", pcall_err(meths.get_mark, 'f', {}))
|
||||||
eq(false, pcall(meths.del_mark, '!'))
|
eq("Invalid mark name (must be file/uppercase): '!'", pcall_err(meths.get_mark, '!', {}))
|
||||||
eq(false, pcall(meths.del_mark, 'fail'))
|
eq("Invalid mark name (must be a single char): 'fail'", pcall_err(meths.get_mark, 'fail', {}))
|
||||||
end)
|
end)
|
||||||
it('returns the expected when mark is not set', function()
|
it('returns the expected when mark is not set', function()
|
||||||
eq(true, meths.del_mark('A'))
|
eq(true, meths.del_mark('A'))
|
||||||
@ -3113,15 +3119,15 @@ describe('API', function()
|
|||||||
meths.eval_statusline('a%=b', { fillchar = '\031', maxwidth = 5 }))
|
meths.eval_statusline('a%=b', { fillchar = '\031', maxwidth = 5 }))
|
||||||
end)
|
end)
|
||||||
it('rejects multiple-character fillchar', function()
|
it('rejects multiple-character fillchar', function()
|
||||||
eq('fillchar must be a single character',
|
eq('Invalid fillchar (not a single character)',
|
||||||
pcall_err(meths.eval_statusline, '', { fillchar = 'aa' }))
|
pcall_err(meths.eval_statusline, '', { fillchar = 'aa' }))
|
||||||
end)
|
end)
|
||||||
it('rejects empty string fillchar', function()
|
it('rejects empty string fillchar', function()
|
||||||
eq('fillchar must be a single character',
|
eq('Invalid fillchar (not a single character)',
|
||||||
pcall_err(meths.eval_statusline, '', { fillchar = '' }))
|
pcall_err(meths.eval_statusline, '', { fillchar = '' }))
|
||||||
end)
|
end)
|
||||||
it('rejects non-string fillchar', function()
|
it('rejects non-string fillchar', function()
|
||||||
eq('fillchar must be a single character',
|
eq("Invalid fillchar: expected String, got Integer",
|
||||||
pcall_err(meths.eval_statusline, '', { fillchar = 1 }))
|
pcall_err(meths.eval_statusline, '', { fillchar = 1 }))
|
||||||
end)
|
end)
|
||||||
it('rejects invalid string', function()
|
it('rejects invalid string', function()
|
||||||
|
@ -1458,7 +1458,7 @@ describe('lua stdlib', function()
|
|||||||
]]
|
]]
|
||||||
eq('', funcs.luaeval "vim.bo.filetype")
|
eq('', funcs.luaeval "vim.bo.filetype")
|
||||||
eq(true, funcs.luaeval "vim.bo[BUF].modifiable")
|
eq(true, funcs.luaeval "vim.bo[BUF].modifiable")
|
||||||
matches("no such option: 'nosuchopt'$",
|
matches("Invalid option %(not found%): 'nosuchopt'$",
|
||||||
pcall_err(exec_lua, 'return vim.bo.nosuchopt'))
|
pcall_err(exec_lua, 'return vim.bo.nosuchopt'))
|
||||||
matches("Expected lua string$",
|
matches("Expected lua string$",
|
||||||
pcall_err(exec_lua, 'return vim.bo[0][0].autoread'))
|
pcall_err(exec_lua, 'return vim.bo[0][0].autoread'))
|
||||||
@ -1479,7 +1479,7 @@ describe('lua stdlib', function()
|
|||||||
eq(0, funcs.luaeval "vim.wo.cole")
|
eq(0, funcs.luaeval "vim.wo.cole")
|
||||||
eq(0, funcs.luaeval "vim.wo[0].cole")
|
eq(0, funcs.luaeval "vim.wo[0].cole")
|
||||||
eq(0, funcs.luaeval "vim.wo[1001].cole")
|
eq(0, funcs.luaeval "vim.wo[1001].cole")
|
||||||
matches("no such option: 'notanopt'$",
|
matches("Invalid option %(not found%): 'notanopt'$",
|
||||||
pcall_err(exec_lua, 'return vim.wo.notanopt'))
|
pcall_err(exec_lua, 'return vim.wo.notanopt'))
|
||||||
matches("Expected lua string$",
|
matches("Expected lua string$",
|
||||||
pcall_err(exec_lua, 'return vim.wo[0][0].list'))
|
pcall_err(exec_lua, 'return vim.wo[0][0].list'))
|
||||||
|
@ -751,8 +751,8 @@ describe('Buffer highlighting', function()
|
|||||||
|
|
||||||
it('validates contents', function()
|
it('validates contents', function()
|
||||||
-- this used to leak memory
|
-- this used to leak memory
|
||||||
eq('Chunk is not an array', pcall_err(set_virtual_text, id1, 0, {"texty"}, {}))
|
eq('Invalid chunk: expected Array, got String', pcall_err(set_virtual_text, id1, 0, {"texty"}, {}))
|
||||||
eq('Chunk is not an array', pcall_err(set_virtual_text, id1, 0, {{"very"}, "texty"}, {}))
|
eq('Invalid chunk: expected Array, got String', pcall_err(set_virtual_text, id1, 0, {{"very"}, "texty"}, {}))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can be retrieved', function()
|
it('can be retrieved', function()
|
||||||
|
Loading…
Reference in New Issue
Block a user