mirror of
https://github.com/neovim/neovim.git
synced 2025-10-01 23:48:32 +00:00
refactor(api): VALIDATE macros #22256
- VALIDATE() takes a format string - deduplicate check_string_array - VALIDATE_RANGE - validate UI args
This commit is contained in:
@@ -125,7 +125,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->event.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(opts->event)) {
|
||||
check_event = true;
|
||||
|
||||
Object v = opts->event;
|
||||
@@ -148,13 +148,13 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
||||
}
|
||||
}
|
||||
|
||||
VALIDATE((opts->pattern.type == kObjectTypeNil || opts->buffer.type == kObjectTypeNil),
|
||||
"Cannot use both 'pattern' and 'buffer'", {
|
||||
VALIDATE((!HAS_KEY(opts->pattern) || !HAS_KEY(opts->buffer)),
|
||||
"%s", "Cannot use both 'pattern' and 'buffer'", {
|
||||
goto cleanup;
|
||||
});
|
||||
|
||||
int pattern_filter_count = 0;
|
||||
if (opts->pattern.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(opts->pattern)) {
|
||||
Object v = opts->pattern;
|
||||
if (v.type == kObjectTypeString) {
|
||||
pattern_filters[pattern_filter_count] = v.data.string.data;
|
||||
@@ -210,7 +210,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
|
||||
snprintf(pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle);
|
||||
ADD(buffers, CSTR_TO_OBJ(pattern_buflocal));
|
||||
});
|
||||
} else if (opts->buffer.type != kObjectTypeNil) {
|
||||
} else if (HAS_KEY(opts->buffer)) {
|
||||
VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), {
|
||||
goto cleanup;
|
||||
});
|
||||
@@ -413,10 +413,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
||||
{
|
||||
int64_t autocmd_id = -1;
|
||||
char *desc = NULL;
|
||||
|
||||
Array patterns = ARRAY_DICT_INIT;
|
||||
Array event_array = ARRAY_DICT_INIT;
|
||||
|
||||
AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT;
|
||||
Callback cb = CALLBACK_NONE;
|
||||
|
||||
@@ -424,12 +422,12 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VALIDATE((opts->callback.type == kObjectTypeNil || opts->command.type == kObjectTypeNil),
|
||||
"Cannot use both 'callback' and 'command'", {
|
||||
VALIDATE((!HAS_KEY(opts->callback) || !HAS_KEY(opts->command)),
|
||||
"%s", "Cannot use both 'callback' and 'command'", {
|
||||
goto cleanup;
|
||||
});
|
||||
|
||||
if (opts->callback.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(opts->callback)) {
|
||||
// NOTE: We could accept callable tables, but that isn't common in the API.
|
||||
|
||||
Object *callback = &opts->callback;
|
||||
@@ -458,7 +456,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
||||
|
||||
aucmd.type = CALLABLE_CB;
|
||||
aucmd.callable.cb = cb;
|
||||
} else if (opts->command.type != kObjectTypeNil) {
|
||||
} else if (HAS_KEY(opts->command)) {
|
||||
Object *command = &opts->command;
|
||||
VALIDATE_T("command", kObjectTypeString, command->type, {
|
||||
goto cleanup;
|
||||
@@ -466,7 +464,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
||||
aucmd.type = CALLABLE_EX;
|
||||
aucmd.callable.cmd = string_to_cstr(command->data.string);
|
||||
} else {
|
||||
VALIDATE_S(false, "'command' or 'callback' is required", "", {
|
||||
VALIDATE(false, "%s", "Required: 'command' or 'callback'", {
|
||||
goto cleanup;
|
||||
});
|
||||
}
|
||||
@@ -483,7 +481,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (opts->desc.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(opts->desc)) {
|
||||
VALIDATE_T("desc", kObjectTypeString, opts->desc.type, {
|
||||
goto cleanup;
|
||||
});
|
||||
@@ -494,7 +492,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
|
||||
ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*")));
|
||||
}
|
||||
|
||||
VALIDATE_R((event_array.size != 0), "event", {
|
||||
VALIDATE_R((event_array.size > 0), "event", {
|
||||
goto cleanup;
|
||||
});
|
||||
|
||||
@@ -586,8 +584,8 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VALIDATE((opts->pattern.type == kObjectTypeNil || opts->buffer.type == kObjectTypeNil),
|
||||
"Cannot use both 'pattern' and 'buffer'", {
|
||||
VALIDATE((!HAS_KEY(opts->pattern) || !HAS_KEY(opts->buffer)),
|
||||
"%s", "Cannot use both 'pattern' and 'buffer'", {
|
||||
goto cleanup;
|
||||
});
|
||||
|
||||
@@ -764,7 +762,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->buffer.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(opts->buffer)) {
|
||||
Object buf_obj = opts->buffer;
|
||||
VALIDATE_EXP((buf_obj.type == kObjectTypeInteger || buf_obj.type == kObjectTypeBuffer),
|
||||
"buffer", "Integer", api_typename(buf_obj.type), {
|
||||
@@ -786,7 +784,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
|
||||
ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("")));
|
||||
}
|
||||
|
||||
if (opts->data.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(opts->data)) {
|
||||
data = &opts->data;
|
||||
}
|
||||
|
||||
@@ -797,7 +795,7 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
|
||||
GET_ONE_EVENT(event_nr, event_str, cleanup)
|
||||
|
||||
FOREACH_ITEM(patterns, pat, {
|
||||
char *fname = opts->buffer.type == kObjectTypeNil ? pat.data.string.data : NULL;
|
||||
char *fname = !HAS_KEY(opts->buffer) ? pat.data.string.data : NULL;
|
||||
did_aucmd |=
|
||||
apply_autocmds_group(event_nr, fname, NULL, true, au_group, buf, NULL, data);
|
||||
})
|
||||
@@ -812,41 +810,19 @@ cleanup:
|
||||
api_free_array(patterns);
|
||||
}
|
||||
|
||||
static bool check_autocmd_string_array(Array arr, char *k, Error *err)
|
||||
{
|
||||
FOREACH_ITEM(arr, entry, {
|
||||
if (entry.type != kObjectTypeString) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid '%s' item type: expected String, got %s",
|
||||
k, api_typename(entry.type));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow newlines in the middle of the line.
|
||||
const String l = entry.data.string;
|
||||
if (memchr(l.data, NL, l.size)) {
|
||||
api_set_error(err, kErrorTypeValidation, "'%s' item cannot contain newlines", k);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err)
|
||||
{
|
||||
if (v->type == kObjectTypeString) {
|
||||
ADD(*array, copy_object(*v, NULL));
|
||||
} else if (v->type == kObjectTypeArray) {
|
||||
if (!check_autocmd_string_array(v->data.array, k, err)) {
|
||||
if (!check_string_array(v->data.array, k, true, err)) {
|
||||
return false;
|
||||
}
|
||||
*array = copy_array(v->data.array, NULL);
|
||||
} else {
|
||||
if (required) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Invalid '%s' type: expected Array or String, got %s",
|
||||
k, api_typename(v->type));
|
||||
VALIDATE_EXP(!required, k, "Array or String", api_typename(v->type), {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -886,12 +862,12 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob
|
||||
{
|
||||
const char pattern_buflocal[BUFLOCAL_PAT_LEN];
|
||||
|
||||
VALIDATE((pattern.type == kObjectTypeNil || buffer.type == kObjectTypeNil),
|
||||
"Cannot use both 'pattern' and 'buffer' for the same autocmd", {
|
||||
VALIDATE((!HAS_KEY(pattern) || !HAS_KEY(buffer)),
|
||||
"%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", {
|
||||
return false;
|
||||
});
|
||||
|
||||
if (pattern.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(pattern)) {
|
||||
Object *v = &pattern;
|
||||
|
||||
if (v->type == kObjectTypeString) {
|
||||
@@ -904,7 +880,7 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob
|
||||
patlen = aucmd_pattern_length(pat);
|
||||
}
|
||||
} else if (v->type == kObjectTypeArray) {
|
||||
if (!check_autocmd_string_array(v->data.array, "pattern", err)) {
|
||||
if (!check_string_array(v->data.array, "pattern", true, err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -924,7 +900,7 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob
|
||||
return false;
|
||||
});
|
||||
}
|
||||
} else if (buffer.type != kObjectTypeNil) {
|
||||
} else if (HAS_KEY(buffer)) {
|
||||
VALIDATE_EXP((buffer.type == kObjectTypeInteger || buffer.type == kObjectTypeBuffer),
|
||||
"buffer", "Integer", api_typename(buffer.type), {
|
||||
return false;
|
||||
|
@@ -293,7 +293,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id,
|
||||
start = normalize_index(buf, start, true, &oob);
|
||||
end = normalize_index(buf, end, true, &oob);
|
||||
|
||||
VALIDATE((!strict_indexing || !oob), "Index out of bounds", {
|
||||
VALIDATE((!strict_indexing || !oob), "%s", "Index out of bounds", {
|
||||
return rv;
|
||||
});
|
||||
|
||||
@@ -320,23 +320,6 @@ end:
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool check_string_array(Array arr, bool disallow_nl, Error *err)
|
||||
{
|
||||
for (size_t i = 0; i < arr.size; i++) {
|
||||
VALIDATE_T("replacement item", kObjectTypeString, arr.items[i].type, {
|
||||
return false;
|
||||
});
|
||||
// Disallow newlines in the middle of the line.
|
||||
if (disallow_nl) {
|
||||
const String l = arr.items[i].data.string;
|
||||
VALIDATE(!memchr(l.data, NL, l.size), "String cannot contain newlines", {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Sets (replaces) a line-range in the buffer.
|
||||
///
|
||||
/// Indexing is zero-based, end-exclusive. Negative indices are interpreted
|
||||
@@ -373,15 +356,15 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
||||
start = normalize_index(buf, start, true, &oob);
|
||||
end = normalize_index(buf, end, true, &oob);
|
||||
|
||||
VALIDATE((!strict_indexing || !oob), "Index out of bounds", {
|
||||
VALIDATE((!strict_indexing || !oob), "%s", "Index out of bounds", {
|
||||
return;
|
||||
});
|
||||
VALIDATE((start <= end), "\"start\" is higher than \"end\"", {
|
||||
VALIDATE((start <= end), "%s", "'start' is higher than 'end'", {
|
||||
return;
|
||||
});
|
||||
|
||||
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
||||
if (!check_string_array(replacement, disallow_nl, err)) {
|
||||
if (!check_string_array(replacement, "replacement string", disallow_nl, err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -438,7 +421,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
||||
for (size_t i = 0; i < to_replace; i++) {
|
||||
int64_t lnum = start + (int64_t)i;
|
||||
|
||||
VALIDATE(lnum < MAXLNUM, "Index value is too high", {
|
||||
VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
|
||||
goto end;
|
||||
});
|
||||
|
||||
@@ -457,7 +440,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
|
||||
for (size_t i = to_replace; i < new_len; i++) {
|
||||
int64_t lnum = start + (int64_t)i - 1;
|
||||
|
||||
VALIDATE(lnum < MAXLNUM, "Index value is too high", {
|
||||
VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
|
||||
goto end;
|
||||
});
|
||||
|
||||
@@ -546,12 +529,12 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
||||
// check range is ordered and everything!
|
||||
// start_row, end_row within buffer len (except add text past the end?)
|
||||
start_row = normalize_index(buf, start_row, false, &oob);
|
||||
VALIDATE((!oob), "start_row out of bounds", {
|
||||
VALIDATE_RANGE((!oob), "start_row", {
|
||||
return;
|
||||
});
|
||||
|
||||
end_row = normalize_index(buf, end_row, false, &oob);
|
||||
VALIDATE((!oob), "end_row out of bounds", {
|
||||
VALIDATE_RANGE((!oob), "end_row", {
|
||||
return;
|
||||
});
|
||||
|
||||
@@ -561,24 +544,24 @@ 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.
|
||||
str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row, false));
|
||||
size_t len_at_start = strlen(str_at_start);
|
||||
VALIDATE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col out of bounds", {
|
||||
VALIDATE_RANGE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col", {
|
||||
goto early_end;
|
||||
});
|
||||
|
||||
// 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));
|
||||
size_t len_at_end = strlen(str_at_end);
|
||||
VALIDATE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col out of bounds", {
|
||||
VALIDATE_RANGE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col", {
|
||||
goto early_end;
|
||||
});
|
||||
|
||||
VALIDATE((start_row <= end_row && !(end_row == start_row && start_col > end_col)),
|
||||
"start is higher than end", {
|
||||
"%s", "'start' is higher than 'end'", {
|
||||
goto early_end;
|
||||
});
|
||||
|
||||
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
||||
if (!check_string_array(replacement, disallow_nl, err)) {
|
||||
if (!check_string_array(replacement, "replacement string", disallow_nl, err)) {
|
||||
goto early_end;
|
||||
}
|
||||
|
||||
@@ -681,7 +664,7 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
|
||||
for (size_t i = 0; i < to_replace; i++) {
|
||||
int64_t lnum = start_row + (int64_t)i;
|
||||
|
||||
VALIDATE((lnum < MAXLNUM), "Index value is too high", {
|
||||
VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
|
||||
goto end;
|
||||
});
|
||||
|
||||
@@ -698,7 +681,7 @@ 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++) {
|
||||
int64_t lnum = start_row + (int64_t)i - 1;
|
||||
|
||||
VALIDATE((lnum < MAXLNUM), "Index value is too high", {
|
||||
VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
|
||||
goto end;
|
||||
});
|
||||
|
||||
@@ -777,7 +760,7 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
|
||||
{
|
||||
Array rv = ARRAY_DICT_INIT;
|
||||
|
||||
VALIDATE((opts.size == 0), "opts dict isn't empty", {
|
||||
VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", {
|
||||
return rv;
|
||||
});
|
||||
|
||||
@@ -796,14 +779,14 @@ ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer,
|
||||
start_row = normalize_index(buf, start_row, false, &oob);
|
||||
end_row = normalize_index(buf, end_row, false, &oob);
|
||||
|
||||
VALIDATE((!oob), "Index out of bounds", {
|
||||
VALIDATE((!oob), "%s", "Index out of bounds", {
|
||||
return rv;
|
||||
});
|
||||
|
||||
// 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
|
||||
// order to maintain symmetry with nvim_buf_set_text.
|
||||
VALIDATE((start_row <= end_row), "start is higher than end", {
|
||||
VALIDATE((start_row <= end_row), "%s", "'start' is higher than 'end'", {
|
||||
return rv;
|
||||
});
|
||||
|
||||
@@ -881,7 +864,7 @@ Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err)
|
||||
return -1;
|
||||
}
|
||||
|
||||
VALIDATE((index >= 0 && index <= buf->b_ml.ml_line_count), "Index out of bounds", {
|
||||
VALIDATE((index >= 0 && index <= buf->b_ml.ml_line_count), "%s", "Index out of bounds", {
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
@@ -100,7 +100,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
|
||||
{
|
||||
Dictionary result = ARRAY_DICT_INIT;
|
||||
|
||||
VALIDATE((opts.size == 0), "opts dict isn't empty", {
|
||||
VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", {
|
||||
return result;
|
||||
});
|
||||
|
||||
@@ -1021,7 +1021,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
||||
name.data, {
|
||||
goto err;
|
||||
});
|
||||
VALIDATE((!HAS_KEY(opts->range) || !HAS_KEY(opts->count)),
|
||||
VALIDATE((!HAS_KEY(opts->range) || !HAS_KEY(opts->count)), "%s",
|
||||
"Cannot use both 'range' and 'count'", {
|
||||
goto err;
|
||||
});
|
||||
@@ -1065,7 +1065,7 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
||||
});
|
||||
}
|
||||
|
||||
VALIDATE((!HAS_KEY(opts->complete) || argt), "'complete' used without 'nargs'", {
|
||||
VALIDATE((!HAS_KEY(opts->complete) || argt), "%s", "'complete' used without 'nargs'", {
|
||||
goto err;
|
||||
});
|
||||
|
||||
@@ -1086,8 +1086,9 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
||||
def = opts->range.data.integer;
|
||||
addr_type_arg = ADDR_LINES;
|
||||
} else if (HAS_KEY(opts->range)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
|
||||
goto err;
|
||||
VALIDATE_S(false, "range", "", {
|
||||
goto err;
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->count.type == kObjectTypeBoolean) {
|
||||
@@ -1101,23 +1102,25 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
||||
addr_type_arg = ADDR_OTHER;
|
||||
def = opts->count.data.integer;
|
||||
} else if (HAS_KEY(opts->count)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'");
|
||||
goto err;
|
||||
VALIDATE_S(false, "count", "", {
|
||||
goto err;
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->addr.type == kObjectTypeString) {
|
||||
if (parse_addr_type_arg(opts->addr.data.string.data, (int)opts->addr.data.string.size,
|
||||
&addr_type_arg) != OK) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
|
||||
if (HAS_KEY(opts->addr)) {
|
||||
VALIDATE_T("addr", kObjectTypeString, opts->addr.type, {
|
||||
goto err;
|
||||
}
|
||||
});
|
||||
|
||||
VALIDATE_S(OK == parse_addr_type_arg(opts->addr.data.string.data,
|
||||
(int)opts->addr.data.string.size, &addr_type_arg), "addr",
|
||||
opts->addr.data.string.data, {
|
||||
goto err;
|
||||
});
|
||||
|
||||
if (addr_type_arg != ADDR_LINES) {
|
||||
argt |= EX_ZEROR;
|
||||
}
|
||||
} else if (HAS_KEY(opts->addr)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (api_object_to_bool(opts->bang, "bang", false, err)) {
|
||||
@@ -1153,23 +1156,25 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
||||
compl = EXPAND_USER_LUA;
|
||||
compl_luaref = api_new_luaref(opts->complete.data.luaref);
|
||||
} else if (opts->complete.type == kObjectTypeString) {
|
||||
if (parse_compl_arg(opts->complete.data.string.data,
|
||||
(int)opts->complete.data.string.size, &compl, &argt,
|
||||
&compl_arg) != OK) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
|
||||
VALIDATE_S(OK == parse_compl_arg(opts->complete.data.string.data,
|
||||
(int)opts->complete.data.string.size, &compl, &argt,
|
||||
&compl_arg),
|
||||
"complete", opts->complete.data.string.data, {
|
||||
goto err;
|
||||
}
|
||||
});
|
||||
} else if (HAS_KEY(opts->complete)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
|
||||
goto err;
|
||||
VALIDATE(false, "%s", "Invalid complete: expected Function or String", {
|
||||
goto err;
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->preview.type == kObjectTypeLuaRef) {
|
||||
if (HAS_KEY(opts->preview)) {
|
||||
VALIDATE_T("preview", kObjectTypeLuaRef, opts->preview.type, {
|
||||
goto err;
|
||||
});
|
||||
|
||||
argt |= EX_PREVIEW;
|
||||
preview_luaref = api_new_luaref(opts->preview.data.luaref);
|
||||
} else if (HAS_KEY(opts->preview)) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid value for 'preview'");
|
||||
goto err;
|
||||
}
|
||||
|
||||
switch (command.type) {
|
||||
@@ -1185,8 +1190,9 @@ void create_user_command(String name, Object command, Dict(user_command) *opts,
|
||||
rep = command.data.string.data;
|
||||
break;
|
||||
default:
|
||||
api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function");
|
||||
goto err;
|
||||
VALIDATE(false, "%s", "Invalid command: expected Function or String", {
|
||||
goto err;
|
||||
});
|
||||
}
|
||||
|
||||
if (uc_add_command(name.data, name.size, rep, argt, def, flags, compl, compl_arg, compl_luaref,
|
||||
|
@@ -520,7 +520,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
|
||||
// For backward compatibility we support "end_line" as an alias for "end_row"
|
||||
if (HAS_KEY(opts->end_line)) {
|
||||
VALIDATE(!HAS_KEY(opts->end_row), "cannot use both end_row and end_line", {
|
||||
VALIDATE(!HAS_KEY(opts->end_row), "%s", "cannot use both 'end_row' and 'end_line'", {
|
||||
goto error;
|
||||
});
|
||||
opts->end_row = opts->end_line;
|
||||
@@ -535,30 +535,29 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
bool strict = true;
|
||||
OPTION_TO_BOOL(strict, strict, true);
|
||||
|
||||
if (opts->end_row.type == kObjectTypeInteger) {
|
||||
Integer val = opts->end_row.data.integer;
|
||||
VALIDATE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)),
|
||||
"end_row value outside range", {
|
||||
goto error;
|
||||
});
|
||||
line2 = (int)val;
|
||||
} else if (HAS_KEY(opts->end_row)) {
|
||||
if (HAS_KEY(opts->end_row)) {
|
||||
VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
Integer val = opts->end_row.data.integer;
|
||||
VALIDATE_RANGE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)), "end_row", {
|
||||
goto error;
|
||||
});
|
||||
line2 = (int)val;
|
||||
}
|
||||
|
||||
colnr_T col2 = -1;
|
||||
if (opts->end_col.type == kObjectTypeInteger) {
|
||||
Integer val = opts->end_col.data.integer;
|
||||
VALIDATE((val >= 0 && val <= MAXCOL), "end_col value outside range", {
|
||||
goto error;
|
||||
});
|
||||
col2 = (int)val;
|
||||
} else if (HAS_KEY(opts->end_col)) {
|
||||
if (HAS_KEY(opts->end_col)) {
|
||||
VALIDATE_T("end_col", kObjectTypeInteger, opts->end_col.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
Integer val = opts->end_col.data.integer;
|
||||
VALIDATE_RANGE((val >= 0 && val <= MAXCOL), "end_col", {
|
||||
goto error;
|
||||
});
|
||||
col2 = (int)val;
|
||||
}
|
||||
|
||||
// uncrustify:off
|
||||
@@ -588,33 +587,37 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->conceal.type == kObjectTypeString) {
|
||||
if (HAS_KEY(opts->conceal)) {
|
||||
VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
String c = opts->conceal.data.string;
|
||||
decor.conceal = true;
|
||||
if (c.size) {
|
||||
decor.conceal_char = utf_ptr2char(c.data);
|
||||
}
|
||||
has_decor = true;
|
||||
} else if (HAS_KEY(opts->conceal)) {
|
||||
VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, {
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->virt_text.type == kObjectTypeArray) {
|
||||
if (HAS_KEY(opts->virt_text)) {
|
||||
VALIDATE_T("virt_text", kObjectTypeArray, opts->virt_text.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
|
||||
&decor.virt_text_width);
|
||||
has_decor = true;
|
||||
if (ERROR_SET(err)) {
|
||||
goto error;
|
||||
}
|
||||
} else if (HAS_KEY(opts->virt_text)) {
|
||||
VALIDATE_T("virt_text", kObjectTypeArray, opts->virt_text.type, {
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->virt_text_pos.type == kObjectTypeString) {
|
||||
if (HAS_KEY(opts->virt_text_pos)) {
|
||||
VALIDATE_T("virt_text_pos", kObjectTypeString, opts->virt_text_pos.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
String str = opts->virt_text_pos.data.string;
|
||||
if (strequal("eol", str.data)) {
|
||||
decor.virt_text_pos = kVTEndOfLine;
|
||||
@@ -627,19 +630,15 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
} else if (HAS_KEY(opts->virt_text_pos)) {
|
||||
VALIDATE_T("virt_text_pos", kObjectTypeString, opts->virt_text_pos.type, {
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->virt_text_win_col.type == kObjectTypeInteger) {
|
||||
decor.col = (int)opts->virt_text_win_col.data.integer;
|
||||
decor.virt_text_pos = kVTWinCol;
|
||||
} else if (HAS_KEY(opts->virt_text_win_col)) {
|
||||
if (HAS_KEY(opts->virt_text_win_col)) {
|
||||
VALIDATE_T("virt_text_win_col", kObjectTypeInteger, opts->virt_text_win_col.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
decor.col = (int)opts->virt_text_win_col.data.integer;
|
||||
decor.virt_text_pos = kVTWinCol;
|
||||
}
|
||||
|
||||
OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
|
||||
@@ -667,7 +666,11 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
bool virt_lines_leftcol = false;
|
||||
OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false);
|
||||
|
||||
if (opts->virt_lines.type == kObjectTypeArray) {
|
||||
if (HAS_KEY(opts->virt_lines)) {
|
||||
VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
Array a = opts->virt_lines.data.array;
|
||||
for (size_t j = 0; j < a.size; j++) {
|
||||
VALIDATE_T("virt_text_line", kObjectTypeArray, a.items[j].type, {
|
||||
@@ -681,37 +684,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
}
|
||||
has_decor = true;
|
||||
}
|
||||
} else if (HAS_KEY(opts->virt_lines)) {
|
||||
VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, {
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
|
||||
OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false);
|
||||
|
||||
if (opts->priority.type == kObjectTypeInteger) {
|
||||
Integer val = opts->priority.data.integer;
|
||||
|
||||
VALIDATE_S((val >= 0 && val <= UINT16_MAX), "priority", "(out of range)", {
|
||||
goto error;
|
||||
});
|
||||
decor.priority = (DecorPriority)val;
|
||||
} else if (HAS_KEY(opts->priority)) {
|
||||
if (HAS_KEY(opts->priority)) {
|
||||
VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
Integer val = opts->priority.data.integer;
|
||||
|
||||
VALIDATE_RANGE((val >= 0 && val <= UINT16_MAX), "priority", {
|
||||
goto error;
|
||||
});
|
||||
decor.priority = (DecorPriority)val;
|
||||
}
|
||||
|
||||
if (opts->sign_text.type == kObjectTypeString) {
|
||||
if (HAS_KEY(opts->sign_text)) {
|
||||
VALIDATE_T("sign_text", kObjectTypeString, opts->sign_text.type, {
|
||||
goto error;
|
||||
});
|
||||
|
||||
VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data.string.data),
|
||||
"sign_text", "", {
|
||||
goto error;
|
||||
});
|
||||
has_decor = true;
|
||||
} else if (HAS_KEY(opts->sign_text)) {
|
||||
VALIDATE_T("sign_text", kObjectTypeString, opts->sign_text.type, {
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
|
||||
bool right_gravity = true;
|
||||
@@ -720,7 +719,7 @@ 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
|
||||
// setting end_col or end_row
|
||||
VALIDATE(!(line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)),
|
||||
"cannot set end_right_gravity without setting end_row or end_col", {
|
||||
"%s", "cannot set end_right_gravity without end_row or end_col", {
|
||||
goto error;
|
||||
});
|
||||
|
||||
@@ -732,7 +731,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
bool ephemeral = false;
|
||||
OPTION_TO_BOOL(ephemeral, ephemeral, false);
|
||||
|
||||
if (opts->spell.type == kObjectTypeNil) {
|
||||
if (!HAS_KEY(opts->spell)) {
|
||||
decor.spell = kNone;
|
||||
} else {
|
||||
bool spell = false;
|
||||
@@ -746,12 +745,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
has_decor = true;
|
||||
}
|
||||
|
||||
VALIDATE_S((line >= 0), "line", "(out of range)", {
|
||||
VALIDATE_RANGE((line >= 0), "line", {
|
||||
goto error;
|
||||
});
|
||||
|
||||
if (line > buf->b_ml.ml_line_count) {
|
||||
VALIDATE_S(!strict, "line", "(out of range)", {
|
||||
VALIDATE_RANGE(!strict, "line", {
|
||||
goto error;
|
||||
});
|
||||
line = buf->b_ml.ml_line_count;
|
||||
@@ -762,12 +761,12 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
if (col == -1) {
|
||||
col = (Integer)len;
|
||||
} else if (col > (Integer)len) {
|
||||
VALIDATE_S(!strict, "col", "(out of range)", {
|
||||
VALIDATE_RANGE(!strict, "col", {
|
||||
goto error;
|
||||
});
|
||||
col = (Integer)len;
|
||||
} else if (col < -1) {
|
||||
VALIDATE_S(false, "col", "(out of range)", {
|
||||
VALIDATE_RANGE(false, "col", {
|
||||
goto error;
|
||||
});
|
||||
}
|
||||
@@ -783,7 +782,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
line2 = (int)line;
|
||||
}
|
||||
if (col2 > (Integer)len) {
|
||||
VALIDATE_S(!strict, "end_col", "(out of range)", {
|
||||
VALIDATE_RANGE(!strict, "end_col", {
|
||||
goto error;
|
||||
});
|
||||
col2 = (int)len;
|
||||
@@ -886,10 +885,10 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
|
||||
return 0;
|
||||
}
|
||||
|
||||
VALIDATE_S((line >= 0 && line < MAXLNUM), "line number", "(out of range)", {
|
||||
VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
|
||||
return 0;
|
||||
});
|
||||
VALIDATE_S((col_start >= 0 && col_start <= MAXCOL), "column", "(out of range)", {
|
||||
VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
|
||||
return 0;
|
||||
});
|
||||
|
||||
@@ -948,7 +947,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
|
||||
return;
|
||||
}
|
||||
|
||||
VALIDATE_S((line_start >= 0 && line_start < MAXLNUM), "line number", "(out of range)", {
|
||||
VALIDATE_RANGE((line_start >= 0 && line_start < MAXLNUM), "line number", {
|
||||
return;
|
||||
});
|
||||
|
||||
@@ -1092,7 +1091,7 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in
|
||||
VALIDATE((pos.size == 2
|
||||
&& pos.items[0].type == kObjectTypeInteger
|
||||
&& pos.items[1].type == kObjectTypeInteger),
|
||||
"Invalid position: expected 2 Integer items", {
|
||||
"%s", "Invalid position: expected 2 Integer items", {
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -1102,7 +1101,7 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in
|
||||
*col = (colnr_T)(pos_col >= 0 ? pos_col : MAXCOL);
|
||||
return true;
|
||||
} else {
|
||||
VALIDATE(false, "Invalid position: expected mark id Integer or 2-item Array", {
|
||||
VALIDATE(false, "%s", "Invalid position: expected mark id Integer or 2-item Array", {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -1153,7 +1152,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
|
||||
});
|
||||
Array chunk = chunks.items[i].data.array;
|
||||
VALIDATE((chunk.size > 0 && chunk.size <= 2 && chunk.items[0].type == kObjectTypeString),
|
||||
"Invalid chunk: expected Array with 1 or 2 Strings", {
|
||||
"%s", "Invalid chunk: expected Array with 1 or 2 Strings", {
|
||||
goto free_exit;
|
||||
});
|
||||
|
||||
|
@@ -26,54 +26,54 @@
|
||||
static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_type, void **from,
|
||||
Error *err)
|
||||
{
|
||||
if (opts->scope.type == kObjectTypeString) {
|
||||
if (HAS_KEY(opts->scope)) {
|
||||
VALIDATE_T("scope", kObjectTypeString, opts->scope.type, {
|
||||
return FAIL;
|
||||
});
|
||||
|
||||
if (!strcmp(opts->scope.data.string.data, "local")) {
|
||||
*scope = OPT_LOCAL;
|
||||
} else if (!strcmp(opts->scope.data.string.data, "global")) {
|
||||
*scope = OPT_GLOBAL;
|
||||
} else {
|
||||
VALIDATE(false, "Invalid scope (expected 'local' or 'global')", {
|
||||
VALIDATE(false, "%s", "Invalid scope: expected 'local' or 'global'", {
|
||||
return FAIL;
|
||||
});
|
||||
}
|
||||
} else if (HAS_KEY(opts->scope)) {
|
||||
VALIDATE_T("scope", kObjectTypeString, opts->scope.type, {
|
||||
return FAIL;
|
||||
});
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
*opt_type = SREQ_GLOBAL;
|
||||
|
||||
if (opts->win.type == kObjectTypeInteger) {
|
||||
if (HAS_KEY(opts->win)) {
|
||||
VALIDATE_T("win", kObjectTypeInteger, opts->win.type, {
|
||||
return FAIL;
|
||||
});
|
||||
|
||||
*opt_type = SREQ_WIN;
|
||||
*from = find_window_by_handle((int)opts->win.data.integer, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return FAIL;
|
||||
}
|
||||
} else if (HAS_KEY(opts->win)) {
|
||||
VALIDATE_T("win", kObjectTypeInteger, opts->win.type, {
|
||||
return FAIL;
|
||||
});
|
||||
}
|
||||
|
||||
if (opts->buf.type == kObjectTypeInteger) {
|
||||
if (HAS_KEY(opts->buf)) {
|
||||
VALIDATE_T("buf", kObjectTypeInteger, opts->buf.type, {
|
||||
return FAIL;
|
||||
});
|
||||
|
||||
*scope = OPT_LOCAL;
|
||||
*opt_type = SREQ_BUF;
|
||||
*from = find_buffer_by_handle((int)opts->buf.data.integer, err);
|
||||
if (ERROR_SET(err)) {
|
||||
return FAIL;
|
||||
}
|
||||
} else if (HAS_KEY(opts->buf)) {
|
||||
VALIDATE_T("buf", kObjectTypeInteger, opts->buf.type, {
|
||||
return FAIL;
|
||||
});
|
||||
}
|
||||
|
||||
VALIDATE((!HAS_KEY(opts->scope) || !HAS_KEY(opts->buf)), "cannot use both 'scope' and 'buf'", {
|
||||
VALIDATE((!HAS_KEY(opts->scope) || !HAS_KEY(opts->buf)), "%s",
|
||||
"cannot use both 'scope' and 'buf'", {
|
||||
return FAIL;
|
||||
});
|
||||
VALIDATE((!HAS_KEY(opts->win) || !HAS_KEY(opts->buf)), "cannot use both 'buf' and 'win'", {
|
||||
VALIDATE((!HAS_KEY(opts->win) || !HAS_KEY(opts->buf)), "%s", "cannot use both 'buf' and 'win'", {
|
||||
return FAIL;
|
||||
});
|
||||
|
||||
@@ -427,21 +427,21 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object
|
||||
char *stringval = NULL;
|
||||
|
||||
if (flags & SOPT_BOOL) {
|
||||
VALIDATE_FMT(value.type == kObjectTypeBoolean, "Option '%s' value must be Boolean", name.data, {
|
||||
VALIDATE(value.type == kObjectTypeBoolean, "Option '%s' value must be Boolean", name.data, {
|
||||
return;
|
||||
});
|
||||
numval = value.data.boolean;
|
||||
} else if (flags & SOPT_NUM) {
|
||||
VALIDATE_FMT(value.type == kObjectTypeInteger, "Option '%s' value must be Integer", name.data, {
|
||||
VALIDATE(value.type == kObjectTypeInteger, "Option '%s' value must be Integer", name.data, {
|
||||
return;
|
||||
});
|
||||
VALIDATE_FMT((value.data.integer <= INT_MAX && value.data.integer >= INT_MIN),
|
||||
"Option '%s' value is out of range", name.data, {
|
||||
VALIDATE((value.data.integer <= INT_MAX && value.data.integer >= INT_MIN),
|
||||
"Option '%s' value is out of range", name.data, {
|
||||
return;
|
||||
});
|
||||
numval = (int)value.data.integer;
|
||||
} else {
|
||||
VALIDATE_FMT(value.type == kObjectTypeString, "Option '%s' value must be String", name.data, {
|
||||
VALIDATE(value.type == kObjectTypeString, "Option '%s' value must be String", name.data, {
|
||||
return;
|
||||
});
|
||||
stringval = value.data.string.data;
|
||||
|
@@ -821,8 +821,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err)
|
||||
} else if (obj.type == kObjectTypeInteger) {
|
||||
return MAX((int)obj.data.integer, 0);
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"%s is not a valid highlight", what);
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid highlight: %s", what);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
28
src/nvim/api/private/validate.c
Normal file
28
src/nvim/api/private/validate.c
Normal file
@@ -0,0 +1,28 @@
|
||||
// This is an open source non-commercial project. Dear PVS-Studio, please check
|
||||
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/private/validate.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/private/validate.c.generated.h"
|
||||
#endif
|
||||
|
||||
bool check_string_array(Array arr, char *name, bool disallow_nl, Error *err)
|
||||
{
|
||||
snprintf(IObuff, sizeof(IObuff), "'%s' item", name);
|
||||
for (size_t i = 0; i < arr.size; i++) {
|
||||
VALIDATE_T(IObuff, kObjectTypeString, arr.items[i].type, {
|
||||
return false;
|
||||
});
|
||||
// Disallow newlines in the middle of the line.
|
||||
if (disallow_nl) {
|
||||
const String l = arr.items[i].data.string;
|
||||
VALIDATE(!memchr(l.data, NL, l.size), "'%s' item contains newlines", name, {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
@@ -24,19 +24,11 @@
|
||||
} \
|
||||
} 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); \
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid %s: expected %s, got %s", \
|
||||
name, expected, actual); \
|
||||
code; \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -50,20 +42,27 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VALIDATE(cond, msg_, code) \
|
||||
#define VALIDATE(cond, fmt_, fmt_arg1, code) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
api_set_error(err, kErrorTypeValidation, "%s", msg_); \
|
||||
api_set_error(err, kErrorTypeValidation, fmt_, fmt_arg1); \
|
||||
code; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VALIDATE_FMT(cond, fmt_, msg_, code) \
|
||||
#define VALIDATE_RANGE(cond, name, code) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
api_set_error(err, kErrorTypeValidation, fmt_, msg_); \
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid '%s': out of range", name); \
|
||||
code; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VALIDATE_R(cond, name, code) \
|
||||
VALIDATE(cond, "Required: '%s'", name, code);
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "api/private/validate.h.generated.h"
|
||||
#endif
|
||||
|
||||
#endif // NVIM_API_PRIVATE_VALIDATE_H
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "klib/kvec.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/private/validate.h"
|
||||
#include "nvim/api/ui.h"
|
||||
#include "nvim/autocmd.h"
|
||||
#include "nvim/channel.h"
|
||||
@@ -291,22 +292,20 @@ void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *e
|
||||
ui_set_option(ui, false, name, value, error);
|
||||
}
|
||||
|
||||
static void ui_set_option(UI *ui, bool init, String name, Object value, Error *error)
|
||||
static void ui_set_option(UI *ui, bool init, String name, Object value, Error *err)
|
||||
{
|
||||
if (strequal(name.data, "override")) {
|
||||
if (value.type != kObjectTypeBoolean) {
|
||||
api_set_error(error, kErrorTypeValidation, "override must be a Boolean");
|
||||
VALIDATE_T("override", kObjectTypeBoolean, value.type, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
ui->override = value.data.boolean;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strequal(name.data, "rgb")) {
|
||||
if (value.type != kObjectTypeBoolean) {
|
||||
api_set_error(error, kErrorTypeValidation, "rgb must be a Boolean");
|
||||
VALIDATE_T("rgb", kObjectTypeBoolean, value.type, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
ui->rgb = value.data.boolean;
|
||||
// A little drastic, but only takes effect for legacy uis. For linegrid UI
|
||||
// only changes metadata for nvim_list_uis(), no refresh needed.
|
||||
@@ -317,62 +316,56 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
|
||||
}
|
||||
|
||||
if (strequal(name.data, "term_name")) {
|
||||
if (value.type != kObjectTypeString) {
|
||||
api_set_error(error, kErrorTypeValidation, "term_name must be a String");
|
||||
VALIDATE_T("term_name", kObjectTypeString, value.type, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
set_tty_option("term", string_to_cstr(value.data.string));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strequal(name.data, "term_colors")) {
|
||||
if (value.type != kObjectTypeInteger) {
|
||||
api_set_error(error, kErrorTypeValidation, "term_colors must be a Integer");
|
||||
VALIDATE_T("term_colors", kObjectTypeInteger, value.type, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
t_colors = (int)value.data.integer;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strequal(name.data, "term_background")) {
|
||||
if (value.type != kObjectTypeString) {
|
||||
api_set_error(error, kErrorTypeValidation, "term_background must be a String");
|
||||
VALIDATE_T("term_background", kObjectTypeString, value.type, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
set_tty_background(value.data.string.data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strequal(name.data, "stdin_fd")) {
|
||||
if (value.type != kObjectTypeInteger || value.data.integer < 0) {
|
||||
api_set_error(error, kErrorTypeValidation, "stdin_fd must be a non-negative Integer");
|
||||
VALIDATE_T("stdin_fd", kObjectTypeInteger, value.type, {
|
||||
return;
|
||||
}
|
||||
|
||||
if (starting != NO_SCREEN) {
|
||||
api_set_error(error, kErrorTypeValidation,
|
||||
"stdin_fd can only be used with first attached ui");
|
||||
});
|
||||
VALIDATE_INT((value.data.integer >= 0), "stdin_fd", value.data.integer, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
VALIDATE((starting == NO_SCREEN), "%s", "stdin_fd can only be used with first attached UI", {
|
||||
return;
|
||||
});
|
||||
|
||||
stdin_fd = (int)value.data.integer;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strequal(name.data, "stdin_tty")) {
|
||||
if (value.type != kObjectTypeBoolean) {
|
||||
api_set_error(error, kErrorTypeValidation, "stdin_tty must be a Boolean");
|
||||
VALIDATE_T("stdin_tty", kObjectTypeBoolean, value.type, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
stdin_isatty = value.data.boolean;
|
||||
return;
|
||||
}
|
||||
|
||||
if (strequal(name.data, "stdout_tty")) {
|
||||
if (value.type != kObjectTypeBoolean) {
|
||||
api_set_error(error, kErrorTypeValidation, "stdout_tty must be a Boolean");
|
||||
VALIDATE_T("stdout_tty", kObjectTypeBoolean, value.type, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
stdout_isatty = value.data.boolean;
|
||||
return;
|
||||
}
|
||||
@@ -383,17 +376,15 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
|
||||
for (UIExtension i = 0; i < kUIExtCount; i++) {
|
||||
if (strequal(name.data, ui_ext_names[i])
|
||||
|| (i == kUIPopupmenu && is_popupmenu)) {
|
||||
if (value.type != kObjectTypeBoolean) {
|
||||
api_set_error(error, kErrorTypeValidation, "%s must be a Boolean",
|
||||
name.data);
|
||||
VALIDATE_EXP((value.type == kObjectTypeBoolean), name.data, "Boolean",
|
||||
api_typename(value.type), {
|
||||
return;
|
||||
}
|
||||
});
|
||||
bool boolval = value.data.boolean;
|
||||
if (!init && i == kUILinegrid && boolval != ui->ui_ext[i]) {
|
||||
// There shouldn't be a reason for an UI to do this ever
|
||||
// so explicitly don't support this.
|
||||
api_set_error(error, kErrorTypeValidation,
|
||||
"ext_linegrid option cannot be changed");
|
||||
api_set_error(err, kErrorTypeValidation, "ext_linegrid option cannot be changed");
|
||||
}
|
||||
ui->ui_ext[i] = boolval;
|
||||
if (!init) {
|
||||
@@ -403,8 +394,7 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
|
||||
}
|
||||
}
|
||||
|
||||
api_set_error(error, kErrorTypeValidation, "No such UI option: %s",
|
||||
name.data);
|
||||
api_set_error(err, kErrorTypeValidation, "No such UI option: %s", name.data);
|
||||
}
|
||||
|
||||
/// Tell Nvim to resize a grid. Triggers a grid_resize event with the requested
|
||||
|
@@ -87,10 +87,9 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Arena *arena, Error *er
|
||||
Dictionary result = ARRAY_DICT_INIT;
|
||||
int id = syn_name2id(name.data);
|
||||
|
||||
if (id == 0) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid highlight name: %s", name.data);
|
||||
VALIDATE_S((id != 0), "highlight name", name.data, {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
return nvim_get_hl_by_id(id, rgb, arena, err);
|
||||
}
|
||||
|
||||
@@ -104,10 +103,9 @@ Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Arena *arena, Error *er
|
||||
FUNC_API_SINCE(3)
|
||||
{
|
||||
Dictionary dic = ARRAY_DICT_INIT;
|
||||
if (syn_get_final_id((int)hl_id) == 0) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid highlight id: %" PRId64, hl_id);
|
||||
VALIDATE_INT((syn_get_final_id((int)hl_id) != 0), "highlight id", hl_id, {
|
||||
return dic;
|
||||
}
|
||||
});
|
||||
int attrcode = syn_id2attr((int)hl_id);
|
||||
return hl_get_attr_by_id(attrcode, rgb, arena, err);
|
||||
}
|
||||
@@ -174,10 +172,9 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err)
|
||||
FUNC_API_SINCE(7)
|
||||
{
|
||||
int hl_id = syn_check_group(name.data, name.size);
|
||||
if (hl_id == 0) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid highlight name: %s", name.data);
|
||||
VALIDATE_S((hl_id != 0), "highlight name", name.data, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
int link_id = -1;
|
||||
|
||||
HlAttrs attrs = dict2hlattrs(val, true, &link_id, err);
|
||||
@@ -403,11 +400,9 @@ void nvim_input_mouse(String button, String action, String modifier, Integer gri
|
||||
continue;
|
||||
}
|
||||
int mod = name_to_mod_mask(byte);
|
||||
if (mod == 0) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"invalid modifier %c", byte);
|
||||
VALIDATE((mod != 0), "Invalid modifier: %c", byte, {
|
||||
return;
|
||||
}
|
||||
});
|
||||
modmask |= mod;
|
||||
}
|
||||
|
||||
@@ -500,7 +495,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
|
||||
Integer nvim_strwidth(String text, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
VALIDATE((text.size <= INT_MAX), "text length (too long)", {
|
||||
VALIDATE_S((text.size <= INT_MAX), "text length", "(too long)", {
|
||||
return 0;
|
||||
});
|
||||
|
||||
@@ -574,8 +569,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 source = api_object_to_bool(opts->do_source, "do_source", false, err);
|
||||
VALIDATE((!source || nlua_is_deferred_safe()), "'do_source' used in fast callback", {});
|
||||
|
||||
VALIDATE((!source || nlua_is_deferred_safe()), "%s", "'do_source' used in fast callback", {});
|
||||
if (ERROR_SET(err)) {
|
||||
return (Array)ARRAY_DICT_INIT;
|
||||
}
|
||||
@@ -599,7 +593,7 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E
|
||||
void nvim_set_current_dir(String dir, Error *err)
|
||||
FUNC_API_SINCE(1)
|
||||
{
|
||||
VALIDATE((dir.size < MAXPATHL), "directory name (too long)", {
|
||||
VALIDATE_S((dir.size < MAXPATHL), "directory name", "(too long)", {
|
||||
return;
|
||||
});
|
||||
|
||||
@@ -1067,7 +1061,7 @@ void nvim_chan_send(Integer chan, String data, Error *err)
|
||||
|
||||
channel_send((uint64_t)chan, data.data, data.size,
|
||||
false, &error);
|
||||
VALIDATE(!error, error, {});
|
||||
VALIDATE(!error, "%s", error, {});
|
||||
}
|
||||
|
||||
/// Gets the current list of tabpage handles.
|
||||
@@ -1334,12 +1328,11 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err)
|
||||
FUNC_API_SINCE(6)
|
||||
{
|
||||
Array types = ARRAY_DICT_INIT;
|
||||
if (opts->types.type == kObjectTypeArray) {
|
||||
types = opts->types.data.array;
|
||||
} else if (opts->types.type != kObjectTypeNil) {
|
||||
if (HAS_KEY(opts->types)) {
|
||||
VALIDATE_T("types", kObjectTypeArray, opts->types.type, {
|
||||
return (Dictionary)ARRAY_DICT_INIT;
|
||||
});
|
||||
types = opts->types.data.array;
|
||||
}
|
||||
|
||||
int int_types = types.size > 0 ? 0 : kCtxAll;
|
||||
@@ -1643,7 +1636,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *er
|
||||
goto theend;
|
||||
});
|
||||
Array call = calls.items[i].data.array;
|
||||
VALIDATE((call.size == 2), "Items in calls array must be arrays of size 2", {
|
||||
VALIDATE((call.size == 2), "%s", "calls item must be a 2-item Array", {
|
||||
goto theend;
|
||||
});
|
||||
VALIDATE_T("name", kObjectTypeString, call.items[0].type, {
|
||||
@@ -1824,10 +1817,9 @@ Array nvim_get_proc_children(Integer pid, Error *err)
|
||||
Array rvobj = ARRAY_DICT_INIT;
|
||||
int *proc_list = NULL;
|
||||
|
||||
if (pid <= 0 || pid > INT_MAX) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
|
||||
VALIDATE_INT((pid > 0 && pid <= INT_MAX), "pid", pid, {
|
||||
goto end;
|
||||
}
|
||||
});
|
||||
|
||||
size_t proc_count;
|
||||
int rv = os_proc_children((int)pid, &proc_list, &proc_count);
|
||||
@@ -1866,10 +1858,10 @@ Object nvim_get_proc(Integer pid, Error *err)
|
||||
rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT;
|
||||
rvobj.type = kObjectTypeDictionary;
|
||||
|
||||
if (pid <= 0 || pid > INT_MAX) {
|
||||
api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid);
|
||||
VALIDATE_INT((pid > 0 && pid <= INT_MAX), "pid", pid, {
|
||||
return NIL;
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef MSWIN
|
||||
rvobj.data.dictionary = os_proc_info((int)pid);
|
||||
if (rvobj.data.dictionary.size == 0) { // Process not found.
|
||||
@@ -1911,7 +1903,7 @@ void nvim_select_popupmenu_item(Integer item, Boolean insert, Boolean finish, Di
|
||||
Error *err)
|
||||
FUNC_API_SINCE(6)
|
||||
{
|
||||
VALIDATE((opts.size == 0), "opts dict isn't empty", {
|
||||
VALIDATE((opts.size == 0), "%s", "opts dict isn't empty", {
|
||||
return;
|
||||
});
|
||||
|
||||
@@ -2100,7 +2092,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
||||
|
||||
if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) {
|
||||
const char *const errmsg = check_stl_option(str.data);
|
||||
VALIDATE(!errmsg, errmsg, {
|
||||
VALIDATE(!errmsg, "%s", errmsg, {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
@@ -2119,7 +2111,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
||||
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)", {
|
||||
"%s", "Invalid fillchar: expected single character", {
|
||||
return result;
|
||||
});
|
||||
fillchar = utf_ptr2char(opts->fillchar.data.string.data);
|
||||
@@ -2145,10 +2137,9 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (use_winbar && use_tabline) {
|
||||
api_set_error(err, kErrorTypeValidation, "use_winbar and use_tabline are mutually exclusive");
|
||||
VALIDATE(!(use_winbar && use_tabline), "%s", "Cannot use both 'use_winbar' and 'use_tabline'", {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
win_T *wp, *ewp;
|
||||
|
||||
|
Reference in New Issue
Block a user