refactor(api): use typed keysets

Initially this is just for geting rid of boilerplate,
but eventually the types could get exposed as metadata
This commit is contained in:
bfredl
2023-08-01 14:01:19 +02:00
parent c01e624b07
commit 7bc93e0e2f
23 changed files with 771 additions and 866 deletions

View File

@@ -341,16 +341,6 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
} \
} while (0)
#define OBJ_TO_CMOD_FLAG(flag, value, default, varname) \
do { \
if (api_object_to_bool(value, varname, default, err)) { \
cmdinfo.cmdmod.cmod_flags |= (flag); \
} \
if (ERROR_SET(err)) { \
goto end; \
} \
} while (0)
#define VALIDATE_MOD(cond, mod_, name_) \
do { \
if (!(cond)) { \
@@ -359,18 +349,14 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
} \
} while (0)
bool output;
OBJ_TO_BOOL(output, opts->output, false, "'output'");
VALIDATE_R(HAS_KEY(cmd->cmd), "cmd", {
VALIDATE_R(HAS_KEY(cmd, cmd, cmd), "cmd", {
goto end;
});
VALIDATE_EXP((cmd->cmd.type == kObjectTypeString && cmd->cmd.data.string.data[0] != NUL),
"cmd", "non-empty String", NULL, {
VALIDATE_EXP((cmd->cmd.data[0] != NUL), "cmd", "non-empty String", NULL, {
goto end;
});
cmdname = string_to_cstr(cmd->cmd.data.string);
cmdname = string_to_cstr(cmd->cmd);
ea.cmd = cmdname;
char *p = find_ex_command(&ea, NULL);
@@ -407,15 +393,11 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
}
// Parse command arguments since it's needed to get the command address type.
if (HAS_KEY(cmd->args)) {
VALIDATE_T("args", kObjectTypeArray, cmd->args.type, {
goto end;
});
if (HAS_KEY(cmd, cmd, args)) {
// Process all arguments. Convert non-String arguments to String and check if String arguments
// have non-whitespace characters.
for (size_t i = 0; i < cmd->args.data.array.size; i++) {
Object elem = cmd->args.data.array.items[i];
for (size_t i = 0; i < cmd->args.size; i++) {
Object elem = cmd->args.items[i];
char *data_str;
switch (elem.type) {
@@ -477,16 +459,13 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
// since it only ever checks the first argument.
set_cmd_addr_type(&ea, args.size > 0 ? args.items[0].data.string.data : NULL);
if (HAS_KEY(cmd->range)) {
VALIDATE_MOD((ea.argt & EX_RANGE), "range", cmd->cmd.data.string.data);
VALIDATE_T("range", kObjectTypeArray, cmd->range.type, {
goto end;
});
VALIDATE_EXP((cmd->range.data.array.size <= 2), "range", "<=2 elements", NULL, {
if (HAS_KEY(cmd, cmd, range)) {
VALIDATE_MOD((ea.argt & EX_RANGE), "range", cmd->cmd.data);
VALIDATE_EXP((cmd->range.size <= 2), "range", "<=2 elements", NULL, {
goto end;
});
Array range = cmd->range.data.array;
Array range = cmd->range;
ea.addr_count = (int)range.size;
for (size_t i = 0; i < range.size; i++) {
@@ -519,22 +498,21 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
}
}
if (HAS_KEY(cmd->count)) {
VALIDATE_MOD((ea.argt & EX_COUNT), "count", cmd->cmd.data.string.data);
VALIDATE_EXP((cmd->count.type == kObjectTypeInteger && cmd->count.data.integer >= 0),
"count", "non-negative Integer", NULL, {
if (HAS_KEY(cmd, cmd, count)) {
VALIDATE_MOD((ea.argt & EX_COUNT), "count", cmd->cmd.data);
VALIDATE_EXP((cmd->count >= 0), "count", "non-negative Integer", NULL, {
goto end;
});
set_cmd_count(&ea, (linenr_T)cmd->count.data.integer, true);
set_cmd_count(&ea, (linenr_T)cmd->count, true);
}
if (HAS_KEY(cmd->reg)) {
VALIDATE_MOD((ea.argt & EX_REGSTR), "register", cmd->cmd.data.string.data);
VALIDATE_EXP((cmd->reg.type == kObjectTypeString && cmd->reg.data.string.size == 1),
"reg", "single character", cmd->reg.data.string.data, {
if (HAS_KEY(cmd, cmd, reg)) {
VALIDATE_MOD((ea.argt & EX_REGSTR), "register", cmd->cmd.data);
VALIDATE_EXP((cmd->reg.size == 1),
"reg", "single character", cmd->reg.data, {
goto end;
});
char regname = cmd->reg.data.string.data[0];
char regname = cmd->reg.data[0];
VALIDATE((regname != '='), "%s", "Cannot use register \"=", {
goto end;
});
@@ -545,22 +523,17 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
ea.regname = (uint8_t)regname;
}
OBJ_TO_BOOL(ea.forceit, cmd->bang, false, "'bang'");
VALIDATE_MOD((!ea.forceit || (ea.argt & EX_BANG)), "bang", cmd->cmd.data.string.data);
ea.forceit = cmd->bang;
VALIDATE_MOD((!ea.forceit || (ea.argt & EX_BANG)), "bang", cmd->cmd.data);
if (HAS_KEY(cmd->magic)) {
VALIDATE_T_DICT("magic", cmd->magic, {
goto end;
});
Dict(cmd_magic) magic = { 0 };
if (!api_dict_to_keydict(&magic, KeyDict_cmd_magic_get_field,
cmd->magic.data.dictionary, err)) {
if (HAS_KEY(cmd, cmd, magic)) {
Dict(cmd_magic) magic[1] = { 0 };
if (!api_dict_to_keydict(magic, KeyDict_cmd_magic_get_field, cmd->magic, err)) {
goto end;
}
OBJ_TO_BOOL(cmdinfo.magic.file, magic.file, ea.argt & EX_XFILE, "'magic.file'");
OBJ_TO_BOOL(cmdinfo.magic.bar, magic.bar, ea.argt & EX_TRLBAR, "'magic.bar'");
cmdinfo.magic.file = HAS_KEY(magic, cmd_magic, file) ? magic->file : (ea.argt & EX_XFILE);
cmdinfo.magic.bar = HAS_KEY(magic, cmd_magic, bar) ? magic->bar : (ea.argt & EX_TRLBAR);
if (cmdinfo.magic.file) {
ea.argt |= EX_XFILE;
} else {
@@ -571,89 +544,63 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
cmdinfo.magic.bar = ea.argt & EX_TRLBAR;
}
if (HAS_KEY(cmd->mods)) {
VALIDATE_T_DICT("mods", cmd->mods, {
goto end;
});
Dict(cmd_mods) mods = { 0 };
if (!api_dict_to_keydict(&mods, KeyDict_cmd_mods_get_field, cmd->mods.data.dictionary, err)) {
if (HAS_KEY(cmd, cmd, mods)) {
Dict(cmd_mods) mods[1] = { 0 };
if (!api_dict_to_keydict(mods, KeyDict_cmd_mods_get_field, cmd->mods, err)) {
goto end;
}
if (HAS_KEY(mods.filter)) {
VALIDATE_T_DICT("mods.filter", mods.filter, {
goto end;
});
Dict(cmd_mods_filter) filter = { 0 };
if (HAS_KEY(mods, cmd_mods, filter)) {
Dict(cmd_mods_filter) filter[1] = { 0 };
if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field,
mods.filter.data.dictionary, err)) {
mods->filter, err)) {
goto end;
}
if (HAS_KEY(filter.pattern)) {
VALIDATE_T2(filter.pattern, kObjectTypeString, {
goto end;
});
OBJ_TO_BOOL(cmdinfo.cmdmod.cmod_filter_force, filter.force, false, "'mods.filter.force'");
if (HAS_KEY(filter, cmd_mods_filter, pattern)) {
cmdinfo.cmdmod.cmod_filter_force = filter->force;
// "filter! // is not no-op, so add a filter if either the pattern is non-empty or if filter
// is inverted.
if (*filter.pattern.data.string.data != NUL || cmdinfo.cmdmod.cmod_filter_force) {
cmdinfo.cmdmod.cmod_filter_pat = string_to_cstr(filter.pattern.data.string);
if (*filter->pattern.data != NUL || cmdinfo.cmdmod.cmod_filter_force) {
cmdinfo.cmdmod.cmod_filter_pat = string_to_cstr(filter->pattern);
cmdinfo.cmdmod.cmod_filter_regmatch.regprog = vim_regcomp(cmdinfo.cmdmod.cmod_filter_pat,
RE_MAGIC);
}
}
}
if (HAS_KEY(mods.tab)) {
VALIDATE_T2(mods.tab, kObjectTypeInteger, {
goto end;
});
if ((int)mods.tab.data.integer >= 0) {
if (HAS_KEY(mods, cmd_mods, tab)) {
if ((int)mods->tab >= 0) {
// Silently ignore negative integers to allow mods.tab to be set to -1.
cmdinfo.cmdmod.cmod_tab = (int)mods.tab.data.integer + 1;
cmdinfo.cmdmod.cmod_tab = (int)mods->tab + 1;
}
}
if (HAS_KEY(mods.verbose)) {
VALIDATE_T2(mods.verbose, kObjectTypeInteger, {
goto end;
});
if ((int)mods.verbose.data.integer >= 0) {
if (HAS_KEY(mods, cmd_mods, verbose)) {
if ((int)mods->verbose >= 0) {
// Silently ignore negative integers to allow mods.verbose to be set to -1.
cmdinfo.cmdmod.cmod_verbose = (int)mods.verbose.data.integer + 1;
cmdinfo.cmdmod.cmod_verbose = (int)mods->verbose + 1;
}
}
bool vertical;
OBJ_TO_BOOL(vertical, mods.vertical, false, "'mods.vertical'");
cmdinfo.cmdmod.cmod_split |= (vertical ? WSP_VERT : 0);
cmdinfo.cmdmod.cmod_split |= (mods->vertical ? WSP_VERT : 0);
bool horizontal;
OBJ_TO_BOOL(horizontal, mods.horizontal, false, "'mods.horizontal'");
cmdinfo.cmdmod.cmod_split |= (horizontal ? WSP_HOR : 0);
cmdinfo.cmdmod.cmod_split |= (mods->horizontal ? WSP_HOR : 0);
if (HAS_KEY(mods.split)) {
VALIDATE_T2(mods.split, kObjectTypeString, {
goto end;
});
if (*mods.split.data.string.data == NUL) {
if (HAS_KEY(mods, cmd_mods, split)) {
if (*mods->split.data == NUL) {
// Empty string, do nothing.
} else if (strcmp(mods.split.data.string.data, "aboveleft") == 0
|| strcmp(mods.split.data.string.data, "leftabove") == 0) {
} else if (strcmp(mods->split.data, "aboveleft") == 0
|| strcmp(mods->split.data, "leftabove") == 0) {
cmdinfo.cmdmod.cmod_split |= WSP_ABOVE;
} else if (strcmp(mods.split.data.string.data, "belowright") == 0
|| strcmp(mods.split.data.string.data, "rightbelow") == 0) {
} else if (strcmp(mods->split.data, "belowright") == 0
|| strcmp(mods->split.data, "rightbelow") == 0) {
cmdinfo.cmdmod.cmod_split |= WSP_BELOW;
} else if (strcmp(mods.split.data.string.data, "topleft") == 0) {
} else if (strcmp(mods->split.data, "topleft") == 0) {
cmdinfo.cmdmod.cmod_split |= WSP_TOP;
} else if (strcmp(mods.split.data.string.data, "botright") == 0) {
} else if (strcmp(mods->split.data, "botright") == 0) {
cmdinfo.cmdmod.cmod_split |= WSP_BOT;
} else {
VALIDATE_S(false, "mods.split", "", {
@@ -662,20 +609,25 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
}
}
OBJ_TO_CMOD_FLAG(CMOD_SILENT, mods.silent, false, "'mods.silent'");
OBJ_TO_CMOD_FLAG(CMOD_ERRSILENT, mods.emsg_silent, false, "'mods.emsg_silent'");
OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods.unsilent, false, "'mods.unsilent'");
OBJ_TO_CMOD_FLAG(CMOD_SANDBOX, mods.sandbox, false, "'mods.sandbox'");
OBJ_TO_CMOD_FLAG(CMOD_NOAUTOCMD, mods.noautocmd, false, "'mods.noautocmd'");
OBJ_TO_CMOD_FLAG(CMOD_BROWSE, mods.browse, false, "'mods.browse'");
OBJ_TO_CMOD_FLAG(CMOD_CONFIRM, mods.confirm, false, "'mods.confirm'");
OBJ_TO_CMOD_FLAG(CMOD_HIDE, mods.hide, false, "'mods.hide'");
OBJ_TO_CMOD_FLAG(CMOD_KEEPALT, mods.keepalt, false, "'mods.keepalt'");
OBJ_TO_CMOD_FLAG(CMOD_KEEPJUMPS, mods.keepjumps, false, "'mods.keepjumps'");
OBJ_TO_CMOD_FLAG(CMOD_KEEPMARKS, mods.keepmarks, false, "'mods.keepmarks'");
OBJ_TO_CMOD_FLAG(CMOD_KEEPPATTERNS, mods.keeppatterns, false, "'mods.keeppatterns'");
OBJ_TO_CMOD_FLAG(CMOD_LOCKMARKS, mods.lockmarks, false, "'mods.lockmarks'");
OBJ_TO_CMOD_FLAG(CMOD_NOSWAPFILE, mods.noswapfile, false, "'mods.noswapfile'");
#define OBJ_TO_CMOD_FLAG(flag, value) \
if (value) { \
cmdinfo.cmdmod.cmod_flags |= (flag); \
}
OBJ_TO_CMOD_FLAG(CMOD_SILENT, mods->silent);
OBJ_TO_CMOD_FLAG(CMOD_ERRSILENT, mods->emsg_silent);
OBJ_TO_CMOD_FLAG(CMOD_UNSILENT, mods->unsilent);
OBJ_TO_CMOD_FLAG(CMOD_SANDBOX, mods->sandbox);
OBJ_TO_CMOD_FLAG(CMOD_NOAUTOCMD, mods->noautocmd);
OBJ_TO_CMOD_FLAG(CMOD_BROWSE, mods->browse);
OBJ_TO_CMOD_FLAG(CMOD_CONFIRM, mods->confirm);
OBJ_TO_CMOD_FLAG(CMOD_HIDE, mods->hide);
OBJ_TO_CMOD_FLAG(CMOD_KEEPALT, mods->keepalt);
OBJ_TO_CMOD_FLAG(CMOD_KEEPJUMPS, mods->keepjumps);
OBJ_TO_CMOD_FLAG(CMOD_KEEPMARKS, mods->keepmarks);
OBJ_TO_CMOD_FLAG(CMOD_KEEPPATTERNS, mods->keeppatterns);
OBJ_TO_CMOD_FLAG(CMOD_LOCKMARKS, mods->lockmarks);
OBJ_TO_CMOD_FLAG(CMOD_NOSWAPFILE, mods->noswapfile);
if (cmdinfo.cmdmod.cmod_flags & CMOD_ERRSILENT) {
// CMOD_ERRSILENT must imply CMOD_SILENT, otherwise apply_cmdmod() and undo_cmdmod() won't
@@ -699,13 +651,13 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
garray_T * const save_capture_ga = capture_ga;
const int save_msg_col = msg_col;
if (output) {
if (opts->output) {
ga_init(&capture_local, 1, 80);
capture_ga = &capture_local;
}
TRY_WRAP(err, {
if (output) {
if (opts->output) {
msg_silent++;
msg_col = 0; // prevent leading spaces
}
@@ -714,7 +666,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
execute_cmd(&ea, &cmdinfo, false);
});
if (output) {
if (opts->output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
// Put msg_col back where it was, since nothing should have been written.
@@ -726,7 +678,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
goto clear_ga;
}
if (output && capture_local.ga_len > 1) {
if (opts->output && capture_local.ga_len > 1) {
retv = (String){
.data = capture_local.ga_data,
.size = (size_t)capture_local.ga_len,
@@ -740,7 +692,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
goto end;
}
clear_ga:
if (output) {
if (opts->output) {
ga_clear(&capture_local);
}
end:
@@ -1037,7 +989,7 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
name.data, {
goto err;
});
VALIDATE((!HAS_KEY(opts->range) || !HAS_KEY(opts->count)), "%s",
VALIDATE((!HAS_KEY(opts, user_command, range) || !HAS_KEY(opts, user_command, count)), "%s",
"Cannot use both 'range' and 'count'", {
goto err;
});
@@ -1075,13 +1027,14 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
goto err;
});
}
} else if (HAS_KEY(opts->nargs)) {
} else if (HAS_KEY(opts, user_command, nargs)) {
VALIDATE_S(false, "nargs", "", {
goto err;
});
}
VALIDATE((!HAS_KEY(opts->complete) || argt), "%s", "'complete' used without 'nargs'", {
VALIDATE((!HAS_KEY(opts, user_command, complete) || argt),
"%s", "'complete' used without 'nargs'", {
goto err;
});
@@ -1101,7 +1054,7 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
argt |= EX_RANGE | EX_ZEROR;
def = opts->range.data.integer;
addr_type_arg = ADDR_LINES;
} else if (HAS_KEY(opts->range)) {
} else if (HAS_KEY(opts, user_command, range)) {
VALIDATE_S(false, "range", "", {
goto err;
});
@@ -1117,13 +1070,13 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
addr_type_arg = ADDR_OTHER;
def = opts->count.data.integer;
} else if (HAS_KEY(opts->count)) {
} else if (HAS_KEY(opts, user_command, count)) {
VALIDATE_S(false, "count", "", {
goto err;
});
}
if (HAS_KEY(opts->addr)) {
if (HAS_KEY(opts, user_command, addr)) {
VALIDATE_T("addr", kObjectTypeString, opts->addr.type, {
goto err;
});
@@ -1139,31 +1092,23 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
}
}
if (api_object_to_bool(opts->bang, "bang", false, err)) {
if (opts->bang) {
argt |= EX_BANG;
} else if (ERROR_SET(err)) {
goto err;
}
if (api_object_to_bool(opts->bar, "bar", false, err)) {
if (opts->bar) {
argt |= EX_TRLBAR;
} else if (ERROR_SET(err)) {
goto err;
}
if (api_object_to_bool(opts->register_, "register", false, err)) {
if (opts->register_) {
argt |= EX_REGSTR;
} else if (ERROR_SET(err)) {
goto err;
}
if (api_object_to_bool(opts->keepscript, "keepscript", false, err)) {
if (opts->keepscript) {
argt |= EX_KEEPSCRIPT;
} else if (ERROR_SET(err)) {
goto err;
}
bool force = api_object_to_bool(opts->force, "force", true, err);
bool force = GET_BOOL_OR_TRUE(opts, user_command, force);
if (ERROR_SET(err)) {
goto err;
}
@@ -1178,13 +1123,13 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
"complete", opts->complete.data.string.data, {
goto err;
});
} else if (HAS_KEY(opts->complete)) {
} else if (HAS_KEY(opts, user_command, complete)) {
VALIDATE_EXP(false, "complete", "Function or String", NULL, {
goto err;
});
}
if (HAS_KEY(opts->preview)) {
if (HAS_KEY(opts, user_command, preview)) {
VALIDATE_T("preview", kObjectTypeLuaRef, opts->preview.type, {
goto err;
});
@@ -1254,13 +1199,12 @@ Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error
FUNC_API_SINCE(4)
{
bool global = (buffer == -1);
bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err);
if (ERROR_SET(err)) {
return (Dictionary)ARRAY_DICT_INIT;
}
if (global) {
if (builtin) {
if (opts->builtin) {
api_set_error(err, kErrorTypeValidation, "builtin=true not implemented");
return (Dictionary)ARRAY_DICT_INIT;
}
@@ -1268,7 +1212,7 @@ Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error
}
buf_T *buf = find_buffer_by_handle(buffer, err);
if (builtin || !buf) {
if (opts->builtin || !buf) {
return (Dictionary)ARRAY_DICT_INIT;
}
return commands_array(buf);