refactor(api): handle option dicts properly

Do not copy a lot of lua strings (dict keys) to just strequal() them
Just compare them directly to a dedicated hash function.

feat(generators): HASHY McHASHFACE
This commit is contained in:
Björn Linse
2021-08-21 13:55:12 +02:00
parent 9337fff8aa
commit 32565922ef
22 changed files with 729 additions and 536 deletions

View File

@@ -858,7 +858,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
/// @see |nvim_set_keymap()|
///
/// @param buffer Buffer handle, or 0 for current buffer
void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dictionary opts,
void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dict(keymap) *opts,
Error *err)
FUNC_API_SINCE(6)
{
@@ -874,8 +874,7 @@ void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
FUNC_API_SINCE(6)
{
String rhs = { .data = "", .size = 0 };
Dictionary opts = ARRAY_DICT_INIT;
modify_keymap(buffer, true, mode, lhs, rhs, opts, err);
modify_keymap(buffer, true, mode, lhs, rhs, NULL, err);
}
/// Gets a map of buffer-local |user-commands|.
@@ -885,22 +884,13 @@ void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err)
/// @param[out] err Error details, if any.
///
/// @returns Map of maps describing commands.
Dictionary nvim_buf_get_commands(Buffer buffer, Dictionary opts, Error *err)
Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err)
FUNC_API_SINCE(4)
{
bool global = (buffer == -1);
bool builtin = false;
for (size_t i = 0; i < opts.size; i++) {
String k = opts.items[i].key;
Object v = opts.items[i].value;
if (!strequal("builtin", k.data)) {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
return (Dictionary)ARRAY_DICT_INIT;
}
if (strequal("builtin", k.data)) {
builtin = v.data.boolean;
}
bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err);
if (ERROR_SET(err)) {
return (Dictionary)ARRAY_DICT_INIT;
}
if (global) {
@@ -1485,7 +1475,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// @param[out] err Error details, if any
/// @return Id of the created/updated extmark
Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col,
Dictionary opts, Error *err)
Dict(set_extmark) *opts, Error *err)
FUNC_API_SINCE(7)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -1498,211 +1488,174 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
return 0;
}
bool ephemeral = false;
uint64_t id = 0;
int line2 = -1;
Decoration decor = DECORATION_INIT;
colnr_T col2 = -1;
if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) {
id = (uint64_t)opts->id.data.integer;
} else if (HAS_KEY(opts->id)) {
api_set_error(err, kErrorTypeValidation, "id is not a positive integer");
goto error;
}
bool right_gravity = true;
bool end_right_gravity = false;
bool end_gravity_set = false;
int line2 = -1;
if (opts->end_line.type == kObjectTypeInteger) {
Integer val = opts->end_line.data.integer;
if (val < 0 || val > buf->b_ml.ml_line_count) {
api_set_error(err, kErrorTypeValidation, "end_line value outside range");
goto error;
} else {
line2 = (int)val;
}
} else if (HAS_KEY(opts->end_line)) {
api_set_error(err, kErrorTypeValidation, "end_line is not an integer");
goto error;
}
colnr_T col2 = -1;
if (opts->end_col.type == kObjectTypeInteger) {
Integer val = opts->end_col.data.integer;
if (val < 0 || val > MAXCOL) {
api_set_error(err, kErrorTypeValidation, "end_col value outside range");
goto error;
} else {
col2 = (int)val;
}
} else if (HAS_KEY(opts->end_col)) {
api_set_error(err, kErrorTypeValidation, "end_col is not an integer");
goto error;
}
Decoration decor = DECORATION_INIT;
if (HAS_KEY(opts->hl_group)) {
decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
if (ERROR_SET(err)) {
goto error;
}
}
if (opts->virt_text.type == kObjectTypeArray) {
decor.virt_text = parse_virt_text(opts->virt_text.data.array, err,
&decor.virt_text_width);
if (ERROR_SET(err)) {
goto error;
}
} else if (HAS_KEY(opts->virt_text)) {
api_set_error(err, kErrorTypeValidation, "virt_text is not an Array");
goto error;
}
if (opts->virt_text_pos.type == kObjectTypeString) {
String str = opts->virt_text_pos.data.string;
if (strequal("eol", str.data)) {
decor.virt_text_pos = kVTEndOfLine;
} else if (strequal("overlay", str.data)) {
decor.virt_text_pos = kVTOverlay;
} else if (strequal("right_align", str.data)) {
decor.virt_text_pos = kVTRightAlign;
} else {
api_set_error(err, kErrorTypeValidation, "virt_text_pos: invalid value");
goto error;
}
} else if (HAS_KEY(opts->virt_text_pos)) {
api_set_error(err, kErrorTypeValidation, "virt_text_pos is not a String");
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)) {
api_set_error(err, kErrorTypeValidation,
"virt_text_win_col is not a Number of the correct size");
goto error;
}
#define OPTION_TO_BOOL(target, name, val) \
target = api_object_to_bool(opts-> name, #name, val, err); \
if (ERROR_SET(err)) { \
goto error; \
}
OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
OPTION_TO_BOOL(decor.hl_eol, hl_eol, false);
if (opts->hl_mode.type == kObjectTypeString) {
String str = opts->hl_mode.data.string;
if (strequal("replace", str.data)) {
decor.hl_mode = kHlModeReplace;
} else if (strequal("combine", str.data)) {
decor.hl_mode = kHlModeCombine;
} else if (strequal("blend", str.data)) {
decor.hl_mode = kHlModeBlend;
} else {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos: invalid value");
goto error;
}
} else if (HAS_KEY(opts->hl_mode)) {
api_set_error(err, kErrorTypeValidation, "hl_mode is not a String");
goto error;
}
VirtLines virt_lines = KV_INITIAL_VALUE;
bool virt_lines_above = false;
bool virt_lines_leftcol = false;
for (size_t i = 0; i < opts.size; i++) {
String k = opts.items[i].key;
Object *v = &opts.items[i].value;
if (strequal("id", k.data)) {
if (v->type != kObjectTypeInteger || v->data.integer <= 0) {
api_set_error(err, kErrorTypeValidation,
"id is not a positive integer");
if (opts->virt_lines.type == kObjectTypeArray) {
Array a = opts->virt_lines.data.array;
for (size_t j = 0; j < a.size; j++) {
if (a.items[j].type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation, "virt_text_line item is not an Array");
goto error;
}
id = (uint64_t)v->data.integer;
} else if (strequal("end_line", k.data)) {
if (v->type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"end_line is not an integer");
goto error;
}
if (v->data.integer < 0 || v->data.integer > buf->b_ml.ml_line_count) {
api_set_error(err, kErrorTypeValidation,
"end_line value outside range");
goto error;
}
line2 = (int)v->data.integer;
} else if (strequal("end_col", k.data)) {
if (v->type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"end_col is not an integer");
goto error;
}
if (v->data.integer < 0 || v->data.integer > MAXCOL) {
api_set_error(err, kErrorTypeValidation,
"end_col value outside range");
goto error;
}
col2 = (colnr_T)v->data.integer;
} else if (strequal("hl_group", k.data)) {
String hl_group;
switch (v->type) {
case kObjectTypeString:
hl_group = v->data.string;
decor.hl_id = syn_check_group((char_u *)(hl_group.data),
(int)hl_group.size);
break;
case kObjectTypeInteger:
decor.hl_id = (int)v->data.integer;
break;
default:
api_set_error(err, kErrorTypeValidation,
"hl_group is not valid.");
goto error;
}
} else if (strequal("virt_text", k.data)) {
if (v->type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation,
"virt_text is not an Array");
goto error;
}
decor.virt_text = parse_virt_text(v->data.array, err,
&decor.virt_text_width);
int dummig;
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
kv_push(virt_lines, jtem);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("virt_text_pos", k.data)) {
if (v->type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos is not a String");
goto error;
}
String str = v->data.string;
if (strequal("eol", str.data)) {
decor.virt_text_pos = kVTEndOfLine;
} else if (strequal("overlay", str.data)) {
decor.virt_text_pos = kVTOverlay;
} else if (strequal("right_align", str.data)) {
decor.virt_text_pos = kVTRightAlign;
} else {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos: invalid value");
goto error;
}
} else if (strequal("virt_text_win_col", k.data)) {
if (v->type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"virt_text_win_col is not a Number of the correct size");
goto error;
}
decor.col = (int)v->data.integer;
decor.virt_text_pos = kVTWinCol;
} else if (strequal("virt_text_hide", k.data)) {
decor.virt_text_hide = api_object_to_bool(*v,
"virt_text_hide", false, err);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("virt_lines", k.data)) {
if (v->type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation,
"virt_lines is not an Array");
goto error;
}
Array a = v->data.array;
for (size_t j = 0; j < a.size; j++) {
if (a.items[j].type != kObjectTypeArray) {
api_set_error(err, kErrorTypeValidation,
"virt_text_line item is not an Array");
goto error;
}
int dummig;
VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig);
kv_push(virt_lines, jtem);
if (ERROR_SET(err)) {
goto error;
}
}
} else if (strequal("virt_lines_above", k.data)) {
virt_lines_above = api_object_to_bool(*v, "virt_lines_above", false, err);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("virt_lines_leftcol", k.data)) {
virt_lines_leftcol = api_object_to_bool(*v, "virt_lines_leftcol", false, err);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("hl_eol", k.data)) {
decor.hl_eol = api_object_to_bool(*v, "hl_eol", false, err);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("hl_mode", k.data)) {
if (v->type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation,
"hl_mode is not a String");
goto error;
}
String str = v->data.string;
if (strequal("replace", str.data)) {
decor.hl_mode = kHlModeReplace;
} else if (strequal("combine", str.data)) {
decor.hl_mode = kHlModeCombine;
} else if (strequal("blend", str.data)) {
decor.hl_mode = kHlModeBlend;
} else {
api_set_error(err, kErrorTypeValidation,
"virt_text_pos: invalid value");
goto error;
}
} else if (strequal("ephemeral", k.data)) {
ephemeral = api_object_to_bool(*v, "ephemeral", false, err);
if (ERROR_SET(err)) {
goto error;
}
} else if (strequal("priority", k.data)) {
if (v->type != kObjectTypeInteger) {
api_set_error(err, kErrorTypeValidation,
"priority is not a Number of the correct size");
goto error;
}
if (v->data.integer < 0 || v->data.integer > UINT16_MAX) {
api_set_error(err, kErrorTypeValidation,
"priority is not a valid value");
goto error;
}
decor.priority = (DecorPriority)v->data.integer;
} else if (strequal("right_gravity", k.data)) {
if (v->type != kObjectTypeBoolean) {
api_set_error(err, kErrorTypeValidation,
"right_gravity must be a boolean");
goto error;
}
right_gravity = v->data.boolean;
} else if (strequal("end_right_gravity", k.data)) {
if (v->type != kObjectTypeBoolean) {
api_set_error(err, kErrorTypeValidation,
"end_right_gravity must be a boolean");
goto error;
}
end_right_gravity = v->data.boolean;
end_gravity_set = true;
} else {
api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data);
goto error;
}
} else if (HAS_KEY(opts->virt_lines)) {
api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array");
goto error;
}
OPTION_TO_BOOL(virt_lines_above, virt_lines_above, false);
OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false);
if (opts->priority.type == kObjectTypeInteger) {
Integer val = opts->priority.data.integer;
if (val < 0 || val > UINT16_MAX) {
api_set_error(err, kErrorTypeValidation, "priority is not a valid value");
goto error;
}
decor.priority = (DecorPriority)val;
} else if (HAS_KEY(opts->priority)) {
api_set_error(err, kErrorTypeValidation, "priority is not a Number of the correct size");
goto error;
}
bool right_gravity = true;
OPTION_TO_BOOL(right_gravity, right_gravity, true);
// Only error out if they try to set end_right_gravity without
// setting end_col or end_line
if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) {
api_set_error(err, kErrorTypeValidation,
"cannot set end_right_gravity without setting end_line or end_col");
goto error;
}
bool end_right_gravity = false;
OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false);
size_t len = 0;
bool ephemeral = false;
OPTION_TO_BOOL(ephemeral, ephemeral, false);
if (line < 0 || line > buf->b_ml.ml_line_count) {
api_set_error(err, kErrorTypeValidation, "line value outside range");
return 0;
@@ -1717,15 +1670,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
return 0;
}
// Only error out if they try to set end_right_gravity without
// setting end_col or end_line
if (line2 == -1 && col2 == -1 && end_gravity_set) {
api_set_error(err, kErrorTypeValidation,
"cannot set end_right_gravity "
"without setting end_line or end_col");
}
if (col2 >= 0) {
if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) {
len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false));
@@ -1744,15 +1688,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = 0;
}
if (decor.virt_text_pos == kVTRightAlign) {
decor.col = 0;
for (size_t i = 0; i < kv_size(decor.virt_text); i++) {
decor.col
+= (int)mb_string2cells((char_u *)kv_A(decor.virt_text, i).text);
}
}
Decoration *d = NULL;
if (ephemeral) {