mirror of
https://github.com/neovim/neovim.git
synced 2025-09-11 13:58:18 +00: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:
@@ -16,6 +16,7 @@
|
||||
#include "nvim/api/buffer.h"
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/api/private/validate.h"
|
||||
#include "nvim/ascii.h"
|
||||
#include "nvim/autocmd.h"
|
||||
#include "nvim/buffer.h"
|
||||
@@ -179,11 +180,9 @@ Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer,
|
||||
if (is_lua) {
|
||||
for (size_t j = 0; cbs[j].name; j++) {
|
||||
if (strequal(cbs[j].name, k.data)) {
|
||||
if (v->type != kObjectTypeLuaRef) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"%s is not a function", cbs[j].name);
|
||||
VALIDATE_T(cbs[j].name, kObjectTypeLuaRef, v->type, {
|
||||
goto error;
|
||||
}
|
||||
});
|
||||
*(cbs[j].dest) = v->data.luaref;
|
||||
v->data.luaref = LUA_NOREF;
|
||||
key_used = true;
|
||||
@@ -194,26 +193,23 @@ Boolean nvim_buf_attach(uint64_t channel_id, Buffer buffer, Boolean send_buffer,
|
||||
if (key_used) {
|
||||
continue;
|
||||
} else if (strequal("utf_sizes", k.data)) {
|
||||
if (v->type != kObjectTypeBoolean) {
|
||||
api_set_error(err, kErrorTypeValidation, "utf_sizes must be boolean");
|
||||
VALIDATE_T("utf_sizes", kObjectTypeBoolean, v->type, {
|
||||
goto error;
|
||||
}
|
||||
});
|
||||
cb.utf_sizes = v->data.boolean;
|
||||
key_used = true;
|
||||
} else if (strequal("preview", k.data)) {
|
||||
if (v->type != kObjectTypeBoolean) {
|
||||
api_set_error(err, kErrorTypeValidation, "preview must be boolean");
|
||||
VALIDATE_T("preview", kObjectTypeBoolean, v->type, {
|
||||
goto error;
|
||||
}
|
||||
});
|
||||
cb.preview = v->data.boolean;
|
||||
key_used = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!key_used) {
|
||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
||||
VALIDATE_S(key_used, "key", k.data, {
|
||||
goto error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
end = normalize_index(buf, end, true, &oob);
|
||||
|
||||
if (strict_indexing && oob) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
||||
VALIDATE((!strict_indexing || !oob), "Index out of bounds", {
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
|
||||
if (start >= end) {
|
||||
// Return 0-length array
|
||||
@@ -328,20 +323,15 @@ end:
|
||||
static bool check_string_array(Array arr, bool disallow_nl, Error *err)
|
||||
{
|
||||
for (size_t i = 0; i < arr.size; i++) {
|
||||
if (arr.items[i].type != kObjectTypeString) {
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"All items in the replacement array must be strings");
|
||||
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;
|
||||
if (memchr(l.data, NL, l.size)) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"String cannot contain newlines");
|
||||
VALIDATE(!memchr(l.data, NL, l.size), "String cannot contain newlines", {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
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);
|
||||
end = normalize_index(buf, end, true, &oob);
|
||||
|
||||
if (strict_indexing && oob) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
||||
VALIDATE((!strict_indexing || !oob), "Index out of bounds", {
|
||||
return;
|
||||
}
|
||||
|
||||
if (start > end) {
|
||||
api_set_error(err,
|
||||
kErrorTypeValidation,
|
||||
"Argument \"start\" is higher than \"end\"");
|
||||
});
|
||||
VALIDATE((start <= end), "\"start\" is higher than \"end\"", {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
||||
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++) {
|
||||
int64_t lnum = start + (int64_t)i;
|
||||
|
||||
if (lnum >= MAXLNUM) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
||||
VALIDATE(lnum < MAXLNUM, "Index value is too high", {
|
||||
goto end;
|
||||
}
|
||||
});
|
||||
|
||||
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
||||
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++) {
|
||||
int64_t lnum = start + (int64_t)i - 1;
|
||||
|
||||
if (lnum >= MAXLNUM) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
||||
VALIDATE(lnum < MAXLNUM, "Index value is too high", {
|
||||
goto end;
|
||||
}
|
||||
});
|
||||
|
||||
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||
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!
|
||||
// start_row, end_row within buffer len (except add text past the end?)
|
||||
start_row = normalize_index(buf, start_row, false, &oob);
|
||||
if (oob) {
|
||||
api_set_error(err, kErrorTypeValidation, "start_row out of bounds");
|
||||
VALIDATE((!oob), "start_row out of bounds", {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
end_row = normalize_index(buf, end_row, false, &oob);
|
||||
if (oob) {
|
||||
api_set_error(err, kErrorTypeValidation, "end_row out of bounds");
|
||||
VALIDATE((!oob), "end_row out of bounds", {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
char *str_at_start = 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.
|
||||
str_at_start = xstrdup(ml_get_buf(buf, (linenr_T)start_row, false));
|
||||
size_t len_at_start = strlen(str_at_start);
|
||||
if (start_col < 0 || (size_t)start_col > len_at_start) {
|
||||
api_set_error(err, kErrorTypeValidation, "start_col out of bounds");
|
||||
VALIDATE((start_col >= 0 && (size_t)start_col <= len_at_start), "start_col out of bounds", {
|
||||
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);
|
||||
if (end_col < 0 || (size_t)end_col > len_at_end) {
|
||||
api_set_error(err, kErrorTypeValidation, "end_col out of bounds");
|
||||
VALIDATE((end_col >= 0 && (size_t)end_col <= len_at_end), "end_col out of bounds", {
|
||||
goto early_end;
|
||||
}
|
||||
});
|
||||
|
||||
if (start_row > end_row || (end_row == start_row && start_col > end_col)) {
|
||||
api_set_error(err, kErrorTypeValidation, "start is higher than end");
|
||||
VALIDATE((start_row <= end_row && !(end_row == start_row && start_col > end_col)),
|
||||
"start is higher than end", {
|
||||
goto early_end;
|
||||
}
|
||||
});
|
||||
|
||||
bool disallow_nl = (channel_id != VIML_INTERNAL_CALL);
|
||||
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++) {
|
||||
int64_t lnum = start_row + (int64_t)i;
|
||||
|
||||
if (lnum >= MAXLNUM) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
||||
VALIDATE((lnum < MAXLNUM), "Index value is too high", {
|
||||
goto end;
|
||||
}
|
||||
});
|
||||
|
||||
if (ml_replace((linenr_T)lnum, lines[i], false) == FAIL) {
|
||||
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++) {
|
||||
int64_t lnum = start_row + (int64_t)i - 1;
|
||||
|
||||
if (lnum >= MAXLNUM) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index value is too high");
|
||||
VALIDATE((lnum < MAXLNUM), "Index value is too high", {
|
||||
goto end;
|
||||
}
|
||||
});
|
||||
|
||||
if (ml_append((linenr_T)lnum, lines[i], 0, false) == FAIL) {
|
||||
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;
|
||||
|
||||
if (opts.size > 0) {
|
||||
api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
|
||||
VALIDATE((opts.size == 0), "opts dict isn't empty", {
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
end_row = normalize_index(buf, end_row, false, &oob);
|
||||
|
||||
if (oob) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
||||
VALIDATE((!oob), "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.
|
||||
if (start_row > end_row) {
|
||||
api_set_error(err, kErrorTypeValidation, "start is higher than end");
|
||||
VALIDATE((start_row <= end_row), "start is higher than end", {
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (index < 0 || index > buf->b_ml.ml_line_count) {
|
||||
api_set_error(err, kErrorTypeValidation, "Index out of bounds");
|
||||
VALIDATE((index >= 0 && index <= buf->b_ml.ml_line_count), "Index out of bounds", {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
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)) {
|
||||
unload = api_object_to_bool(v, "unload", false, err);
|
||||
} else {
|
||||
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
|
||||
return;
|
||||
VALIDATE_S(false, "key", k.data, {
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1174,20 +1148,16 @@ Boolean nvim_buf_del_mark(Buffer buffer, String name, Error *err)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (name.size != 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Mark name must be a single character");
|
||||
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
fmark_T *fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, *name.data);
|
||||
|
||||
// fm is NULL when there's no mark with the given name
|
||||
if (fm == NULL) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid mark name: '%c'",
|
||||
*name.data);
|
||||
VALIDATE_S((fm != NULL), "mark name", name.data, {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
// 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) {
|
||||
@@ -1224,11 +1194,9 @@ Boolean nvim_buf_set_mark(Buffer buffer, String name, Integer line, Integer col,
|
||||
return res;
|
||||
}
|
||||
|
||||
if (name.size != 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Mark name must be a single character");
|
||||
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (name.size != 1) {
|
||||
api_set_error(err, kErrorTypeValidation,
|
||||
"Mark name must be a single character");
|
||||
VALIDATE_S((name.size == 1), "mark name (must be a single char)", name.data, {
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
|
||||
fmark_T *fm;
|
||||
pos_T pos;
|
||||
char mark = *name.data;
|
||||
|
||||
fm = mark_get(buf, curwin, NULL, kMarkAllNoResolve, mark);
|
||||
if (fm == NULL) {
|
||||
api_set_error(err, kErrorTypeValidation, "Invalid mark name");
|
||||
VALIDATE_S((fm != NULL), "mark name", name.data, {
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
// (0, 0) uppercase/file mark set in another buffer.
|
||||
if (fm->fnum != buf->handle) {
|
||||
pos.lnum = 0;
|
||||
|
Reference in New Issue
Block a user