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

@@ -11,6 +11,7 @@
#include "lauxlib.h"
#include "nvim/api/autocmd.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/ascii.h"
@@ -125,7 +126,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
});
}
if (HAS_KEY(opts->event)) {
if (HAS_KEY(opts, get_autocmds, event)) {
check_event = true;
Object v = opts->event;
@@ -148,13 +149,13 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err)
}
}
VALIDATE((!HAS_KEY(opts->pattern) || !HAS_KEY(opts->buffer)),
VALIDATE((!HAS_KEY(opts, get_autocmds, pattern) || !HAS_KEY(opts, get_autocmds, buffer)),
"%s", "Cannot use both 'pattern' and 'buffer'", {
goto cleanup;
});
int pattern_filter_count = 0;
if (HAS_KEY(opts->pattern)) {
if (HAS_KEY(opts, get_autocmds, pattern)) {
Object v = opts->pattern;
if (v.type == kObjectTypeString) {
pattern_filters[pattern_filter_count] = v.data.string.data;
@@ -209,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 (HAS_KEY(opts->buffer)) {
} else if (HAS_KEY(opts, get_autocmds, buffer)) {
VALIDATE_EXP(false, "buffer", "Integer or Array", api_typename(opts->buffer.type), {
goto cleanup;
});
@@ -408,12 +409,12 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
}
VALIDATE((!HAS_KEY(opts->callback) || !HAS_KEY(opts->command)),
VALIDATE((!HAS_KEY(opts, create_autocmd, callback) || !HAS_KEY(opts, create_autocmd, command)),
"%s", "Cannot use both 'callback' and 'command'", {
goto cleanup;
});
if (HAS_KEY(opts->callback)) {
if (HAS_KEY(opts, create_autocmd, callback)) {
// NOTE: We could accept callable tables, but that isn't common in the API.
Object *callback = &opts->callback;
@@ -442,36 +443,33 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
aucmd.type = CALLABLE_CB;
aucmd.callable.cb = cb;
} else if (HAS_KEY(opts->command)) {
Object *command = &opts->command;
VALIDATE_T("command", kObjectTypeString, command->type, {
goto cleanup;
});
} else if (HAS_KEY(opts, create_autocmd, command)) {
aucmd.type = CALLABLE_EX;
aucmd.callable.cmd = string_to_cstr(command->data.string);
aucmd.callable.cmd = string_to_cstr(opts->command);
} else {
VALIDATE(false, "%s", "Required: 'command' or 'callback'", {
goto cleanup;
});
}
bool is_once = api_object_to_bool(opts->once, "once", false, err);
bool is_nested = api_object_to_bool(opts->nested, "nested", false, err);
int au_group = get_augroup_from_object(opts->group, err);
if (au_group == AUGROUP_ERROR) {
goto cleanup;
}
if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) {
bool has_buffer = HAS_KEY(opts, create_autocmd, buffer);
VALIDATE((!HAS_KEY(opts, create_autocmd, pattern) || !has_buffer),
"%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", {
goto cleanup;
});
if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) {
goto cleanup;
}
if (HAS_KEY(opts->desc)) {
VALIDATE_T("desc", kObjectTypeString, opts->desc.type, {
goto cleanup;
});
desc = opts->desc.data.string.data;
if (HAS_KEY(opts, create_autocmd, desc)) {
desc = opts->desc.data;
}
if (patterns.size == 0) {
@@ -496,8 +494,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
pat.data.string.data,
(int)pat.data.string.size,
au_group,
is_once,
is_nested,
opts->once,
opts->nested,
desc,
aucmd);
});
@@ -568,7 +566,9 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
goto cleanup;
}
VALIDATE((!HAS_KEY(opts->pattern) || !HAS_KEY(opts->buffer)),
bool has_buffer = HAS_KEY(opts, clear_autocmds, buffer);
VALIDATE((!HAS_KEY(opts, clear_autocmds, pattern) || !has_buffer),
"%s", "Cannot use both 'pattern' and 'buffer'", {
goto cleanup;
});
@@ -578,7 +578,7 @@ void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
goto cleanup;
}
if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) {
if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) {
goto cleanup;
}
@@ -742,21 +742,22 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
});
}
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), {
bool has_buffer = false;
if (HAS_KEY(opts, exec_autocmds, buffer)) {
VALIDATE((!HAS_KEY(opts, exec_autocmds, pattern)),
"%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", {
goto cleanup;
});
buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err);
has_buffer = true;
buf = find_buffer_by_handle(opts->buffer, err);
if (ERROR_SET(err)) {
goto cleanup;
}
}
if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) {
if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, has_buffer, opts->buffer, err)) {
goto cleanup;
}
@@ -764,20 +765,19 @@ void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
ADD(patterns, STATIC_CSTR_TO_OBJ(""));
}
if (HAS_KEY(opts->data)) {
if (HAS_KEY(opts, exec_autocmds, data)) {
data = &opts->data;
}
modeline = api_object_to_bool(opts->modeline, "modeline", true, err);
modeline = GET_BOOL_OR_TRUE(opts, exec_autocmds, modeline);
bool did_aucmd = false;
FOREACH_ITEM(event_array, event_str, {
GET_ONE_EVENT(event_nr, event_str, cleanup)
FOREACH_ITEM(patterns, pat, {
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);
char *fname = !has_buffer ? pat.data.string.data : NULL;
did_aucmd |= apply_autocmds_group(event_nr, fname, NULL, true, au_group, buf, NULL, data);
})
})
@@ -837,17 +837,12 @@ static int get_augroup_from_object(Object group, Error *err)
}
}
static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Object buffer,
Error *err)
static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, bool has_buffer,
Buffer buffer, Error *err)
{
const char pattern_buflocal[BUFLOCAL_PAT_LEN];
VALIDATE((!HAS_KEY(pattern) || !HAS_KEY(buffer)),
"%s", "Cannot use both 'pattern' and 'buffer' for the same autocmd", {
return false;
});
if (HAS_KEY(pattern)) {
if (pattern.type != kObjectTypeNil) {
Object *v = &pattern;
if (v->type == kObjectTypeString) {
@@ -880,13 +875,8 @@ static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Ob
return false;
});
}
} else if (HAS_KEY(buffer)) {
VALIDATE_EXP((buffer.type == kObjectTypeInteger || buffer.type == kObjectTypeBuffer),
"buffer", "Integer", api_typename(buffer.type), {
return false;
});
buf_T *buf = find_buffer_by_handle((Buffer)buffer.data.integer, err);
} else if (has_buffer) {
buf_T *buf = find_buffer_by_handle(buffer, err);
if (ERROR_SET(err)) {
return false;
}

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);

View File

@@ -35,8 +35,7 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
FUNC_API_SINCE(7)
FUNC_API_DEPRECATED_SINCE(11)
{
Dict(exec_opts) opts = { 0 };
opts.output = BOOLEAN_OBJ(output);
Dict(exec_opts) opts = { .output = output };
return exec_impl(channel_id, src, &opts, err);
}
@@ -46,8 +45,7 @@ String nvim_command_output(uint64_t channel_id, String command, Error *err)
FUNC_API_SINCE(1)
FUNC_API_DEPRECATED_SINCE(7)
{
Dict(exec_opts) opts = { 0 };
opts.output = BOOLEAN_OBJ(true);
Dict(exec_opts) opts = { .output = true };
return exec_impl(channel_id, command, &opts, err);
}

View File

@@ -10,6 +10,7 @@
#include "lauxlib.h"
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/buffer_defs.h"
@@ -581,40 +582,32 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
});
uint32_t id = 0;
if (HAS_KEY(opts->id)) {
VALIDATE_EXP((opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0),
"id", "positive Integer", NULL, {
if (HAS_KEY(opts, set_extmark, id)) {
VALIDATE_EXP((opts->id > 0), "id", "positive Integer", NULL, {
goto error;
});
id = (uint32_t)opts->id.data.integer;
id = (uint32_t)opts->id;
}
int line2 = -1;
bool did_end_line = false;
// 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), "%s", "cannot use both 'end_row' and 'end_line'", {
if (HAS_KEY(opts, set_extmark, end_line)) {
VALIDATE(!HAS_KEY(opts, set_extmark, end_row),
"%s", "cannot use both 'end_row' and 'end_line'", {
goto error;
});
opts->end_row = opts->end_line;
did_end_line = true;
}
#define OPTION_TO_BOOL(target, name, val) \
target = api_object_to_bool(opts->name, #name, val, err); \
if (ERROR_SET(err)) { \
goto error; \
}
bool strict = GET_BOOL_OR_TRUE(opts, set_extmark, strict);
bool strict = true;
OPTION_TO_BOOL(strict, strict, true);
if (HAS_KEY(opts->end_row)) {
VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, {
goto error;
});
Integer val = opts->end_row.data.integer;
if (HAS_KEY(opts, set_extmark, end_row) || did_end_line) {
Integer val = opts->end_row;
VALIDATE_RANGE((val >= 0 && !(val > buf->b_ml.ml_line_count && strict)), "end_row", {
goto error;
});
@@ -622,12 +615,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
colnr_T col2 = -1;
if (HAS_KEY(opts->end_col)) {
VALIDATE_T("end_col", kObjectTypeInteger, opts->end_col.type, {
goto error;
});
Integer val = opts->end_col.data.integer;
if (HAS_KEY(opts, set_extmark, end_col)) {
Integer val = opts->end_col;
VALIDATE_RANGE((val >= 0 && val <= MAXCOL), "end_col", {
goto error;
});
@@ -636,6 +625,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
// uncrustify:off
// TODO(bfredl): keyset type alias for hl_group? (nil|int|string)
struct {
const char *name;
Object *opt;
@@ -652,7 +642,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
// uncrustify:on
for (int j = 0; hls[j].name && hls[j].dest; j++) {
if (HAS_KEY(*hls[j].opt)) {
if (hls[j].opt->type != kObjectTypeNil) {
*hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
if (ERROR_SET(err)) {
goto error;
@@ -661,12 +651,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
}
if (HAS_KEY(opts->conceal)) {
VALIDATE_T("conceal", kObjectTypeString, opts->conceal.type, {
goto error;
});
String c = opts->conceal.data.string;
if (HAS_KEY(opts, set_extmark, conceal)) {
String c = opts->conceal;
decor.conceal = true;
if (c.size) {
decor.conceal_char = utf_ptr2char(c.data);
@@ -674,25 +660,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
has_decor = true;
}
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);
if (HAS_KEY(opts, set_extmark, virt_text)) {
decor.virt_text = parse_virt_text(opts->virt_text, err, &decor.virt_text_width);
has_decor = true;
if (ERROR_SET(err)) {
goto error;
}
}
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 (HAS_KEY(opts, set_extmark, virt_text_pos)) {
String str = opts->virt_text_pos;
if (strequal("eol", str.data)) {
decor.virt_text_pos = kVTEndOfLine;
} else if (strequal("overlay", str.data)) {
@@ -708,24 +685,16 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
}
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;
if (HAS_KEY(opts, set_extmark, virt_text_win_col)) {
decor.col = (int)opts->virt_text_win_col;
decor.virt_text_pos = kVTWinCol;
}
OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false);
OPTION_TO_BOOL(decor.hl_eol, hl_eol, false);
decor.hl_eol = opts->hl_eol;
decor.virt_text_hide = opts->virt_text_hide;
if (HAS_KEY(opts->hl_mode)) {
VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, {
goto error;
});
String str = opts->hl_mode.data.string;
if (HAS_KEY(opts, set_extmark, hl_mode)) {
String str = opts->hl_mode;
if (strequal("replace", str.data)) {
decor.hl_mode = kHlModeReplace;
} else if (strequal("combine", str.data)) {
@@ -744,15 +713,10 @@ 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);
bool virt_lines_leftcol = opts->virt_lines_leftcol;
if (HAS_KEY(opts->virt_lines)) {
VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, {
goto error;
});
Array a = opts->virt_lines.data.array;
if (HAS_KEY(opts, set_extmark, virt_lines)) {
Array a = opts->virt_lines;
for (size_t j = 0; j < a.size; j++) {
VALIDATE_T("virt_text_line", kObjectTypeArray, a.items[j].type, {
goto error;
@@ -767,61 +731,44 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
}
OPTION_TO_BOOL(decor.virt_lines_above, virt_lines_above, false);
decor.virt_lines_above = opts->virt_lines_above;
if (HAS_KEY(opts->priority)) {
VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, {
if (HAS_KEY(opts, set_extmark, priority)) {
VALIDATE_RANGE((opts->priority >= 0 && opts->priority <= UINT16_MAX), "priority", {
goto error;
});
Integer val = opts->priority.data.integer;
VALIDATE_RANGE((val >= 0 && val <= UINT16_MAX), "priority", {
goto error;
});
decor.priority = (DecorPriority)val;
decor.priority = (DecorPriority)opts->priority;
}
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),
if (HAS_KEY(opts, set_extmark, sign_text)) {
VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data),
"sign_text", "", {
goto error;
});
has_decor = true;
}
bool right_gravity = true;
OPTION_TO_BOOL(right_gravity, right_gravity, true);
bool right_gravity = GET_BOOL_OR_TRUE(opts, set_extmark, right_gravity);
// 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)),
VALIDATE(!(line2 == -1 && col2 == -1 && HAS_KEY(opts, set_extmark, end_right_gravity)),
"%s", "cannot set end_right_gravity without end_row or end_col", {
goto error;
});
bool end_right_gravity = false;
OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false);
bool end_right_gravity = opts->end_right_gravity;
size_t len = 0;
bool ephemeral = false;
OPTION_TO_BOOL(ephemeral, ephemeral, false);
if (!HAS_KEY(opts->spell)) {
if (!HAS_KEY(opts, set_extmark, spell)) {
decor.spell = kNone;
} else {
bool spell = false;
OPTION_TO_BOOL(spell, spell, false);
decor.spell = spell ? kTrue : kFalse;
decor.spell = opts->spell ? kTrue : kFalse;
has_decor = true;
}
OPTION_TO_BOOL(decor.ui_watched, ui_watched, false);
decor.ui_watched = opts->ui_watched;
if (decor.ui_watched) {
has_decor = true;
}
@@ -836,7 +783,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
});
line = buf->b_ml.ml_line_count;
} else if (line < buf->b_ml.ml_line_count) {
len = ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1, false));
len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line + 1, false));
}
if (col == -1) {
@@ -854,7 +801,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
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));
len = opts->ephemeral ? MAXCOL : strlen(ml_get_buf(buf, (linenr_T)line2 + 1, false));
} else if (line2 == buf->b_ml.ml_line_count) {
// We are trying to add an extmark past final newline
len = 0;
@@ -873,10 +820,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.win && decor_state.win->w_buffer == buf) {
if (opts->ephemeral && decor_state.win && decor_state.win->w_buffer == buf) {
decor_add_ephemeral((int)line, (int)col, line2, col2, &decor, (uint64_t)ns_id, id);
} else {
if (ephemeral) {
if (opts->ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
goto error;
}
@@ -1107,7 +1054,7 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
struct {
const char *name;
Object *source;
LuaRef *source;
LuaRef *dest;
} cbs[] = {
{ "on_start", &opts->on_start, &p->redraw_start },
@@ -1121,25 +1068,18 @@ void nvim_set_decoration_provider(Integer ns_id, Dict(set_decoration_provider) *
};
for (size_t i = 0; cbs[i].source && cbs[i].dest && cbs[i].name; i++) {
Object *v = cbs[i].source;
if (v->type == kObjectTypeNil) {
LuaRef *v = cbs[i].source;
if (*v <= 0) {
continue;
}
VALIDATE_T(cbs[i].name, kObjectTypeLuaRef, v->type, {
goto error;
});
*(cbs[i].dest) = v->data.luaref;
v->data.luaref = LUA_NOREF;
*(cbs[i].dest) = *v;
*v = LUA_NOREF;
}
p->active = true;
p->hl_valid++;
p->hl_cached = false;
return;
error:
decor_provider_clear(p);
}
/// Gets the line and column of an |extmark|.

View File

@@ -4,135 +4,144 @@
#include "nvim/api/private/defs.h"
typedef struct {
Object types;
OptionalKeys is_set__context_;
Array types;
} Dict(context);
typedef struct {
Object on_start;
Object on_buf;
Object on_win;
Object on_line;
Object on_end;
Object _on_hl_def;
Object _on_spell_nav;
OptionalKeys is_set__set_decoration_provider_;
LuaRef on_start;
LuaRef on_buf;
LuaRef on_win;
LuaRef on_line;
LuaRef on_end;
LuaRef _on_hl_def;
LuaRef _on_spell_nav;
} Dict(set_decoration_provider);
typedef struct {
Object id;
Object end_line;
Object end_row;
Object end_col;
OptionalKeys is_set__set_extmark_;
Integer id;
Integer end_line;
Integer end_row;
Integer end_col;
Object hl_group;
Object virt_text;
Object virt_text_pos;
Object virt_text_win_col;
Object virt_text_hide;
Object hl_eol;
Object hl_mode;
Object ephemeral;
Object priority;
Object right_gravity;
Object end_right_gravity;
Object virt_lines;
Object virt_lines_above;
Object virt_lines_leftcol;
Object strict;
Object sign_text;
Array virt_text;
String virt_text_pos;
Integer virt_text_win_col;
Boolean virt_text_hide;
Boolean hl_eol;
String hl_mode;
Boolean ephemeral;
Integer priority;
Boolean right_gravity;
Boolean end_right_gravity;
Array virt_lines;
Boolean virt_lines_above;
Boolean virt_lines_leftcol;
Boolean strict;
String sign_text;
Object sign_hl_group;
Object number_hl_group;
Object line_hl_group;
Object cursorline_hl_group;
Object conceal;
Object spell;
Object ui_watched;
String conceal;
Boolean spell;
Boolean ui_watched;
} Dict(set_extmark);
typedef struct {
Object noremap;
Object nowait;
Object silent;
Object script;
Object expr;
Object unique;
Object callback;
Object desc;
Object replace_keycodes;
OptionalKeys is_set__keymap_;
Boolean noremap;
Boolean nowait;
Boolean silent;
Boolean script;
Boolean expr;
Boolean unique;
LuaRef callback;
String desc;
Boolean replace_keycodes;
} Dict(keymap);
typedef struct {
Object builtin;
Boolean builtin;
} Dict(get_commands);
typedef struct {
OptionalKeys is_set__user_command_;
Object addr;
Object bang;
Object bar;
Boolean bang;
Boolean bar;
Object complete;
Object count;
Object desc;
Object force;
Object keepscript;
Boolean force;
Boolean keepscript;
Object nargs;
Object preview;
Object range;
Object register_;
Boolean register_;
} Dict(user_command);
typedef struct {
Object row;
Object col;
Object width;
Object height;
Object anchor;
Object relative;
Object win;
Object bufpos;
Object external;
Object focusable;
Object zindex;
OptionalKeys is_set__float_config_;
Float row;
Float col;
Integer width;
Integer height;
String anchor;
String relative;
Window win;
Array bufpos;
Boolean external;
Boolean focusable;
Integer zindex;
Object border;
Object title;
Object title_pos;
Object style;
Object noautocmd;
String title_pos;
String style;
Boolean noautocmd;
} Dict(float_config);
typedef struct {
Object is_lua;
Object do_source;
Boolean is_lua;
Boolean do_source;
} Dict(runtime);
typedef struct {
Object winid;
Object maxwidth;
Object fillchar;
Object highlights;
Object use_winbar;
Object use_tabline;
Object use_statuscol_lnum;
OptionalKeys is_set__eval_statusline_;
Window winid;
Integer maxwidth;
String fillchar;
Boolean highlights;
Boolean use_winbar;
Boolean use_tabline;
Integer use_statuscol_lnum;
} Dict(eval_statusline);
typedef struct {
Object scope;
Object win;
Object buf;
Object filetype;
OptionalKeys is_set__option_;
String scope;
Window win;
Buffer buf;
String filetype;
} Dict(option);
typedef struct {
Object bold;
Object standout;
Object strikethrough;
Object underline;
Object undercurl;
Object underdouble;
Object underdotted;
Object underdashed;
Object italic;
Object reverse;
Object altfont;
Object nocombine;
Object default_;
OptionalKeys is_set__highlight_;
Boolean bold;
Boolean standout;
Boolean strikethrough;
Boolean underline;
Boolean undercurl;
Boolean underdouble;
Boolean underdotted;
Boolean underdashed;
Boolean italic;
Boolean reverse;
Boolean altfont;
Boolean nocombine;
Boolean default_;
Object cterm;
Object foreground;
Object fg;
@@ -144,67 +153,73 @@ typedef struct {
Object sp;
Object link;
Object global_link;
Object fallback;
Object blend;
Object fg_indexed;
Object bg_indexed;
Boolean fallback;
Integer blend;
Boolean fg_indexed;
Boolean bg_indexed;
} Dict(highlight);
typedef struct {
Object bold;
Object standout;
Object strikethrough;
Object underline;
Object undercurl;
Object underdouble;
Object underdotted;
Object underdashed;
Object italic;
Object reverse;
Object altfont;
Object nocombine;
Boolean bold;
Boolean standout;
Boolean strikethrough;
Boolean underline;
Boolean undercurl;
Boolean underdouble;
Boolean underdotted;
Boolean underdashed;
Boolean italic;
Boolean reverse;
Boolean altfont;
Boolean nocombine;
} Dict(highlight_cterm);
typedef struct {
Object id;
Object name;
Object link;
OptionalKeys is_set__get_highlight_;
Integer id;
String name;
Boolean link;
} Dict(get_highlight);
typedef struct {
Object start_row;
Object end_row;
Object start_vcol;
Object end_vcol;
OptionalKeys is_set__win_text_height_;
Integer start_row;
Integer end_row;
Integer start_vcol;
Integer end_vcol;
} Dict(win_text_height);
typedef struct {
Object buffer;
OptionalKeys is_set__clear_autocmds_;
Buffer buffer;
Object event;
Object group;
Object pattern;
} Dict(clear_autocmds);
typedef struct {
Object buffer;
OptionalKeys is_set__create_autocmd_;
Buffer buffer;
Object callback;
Object command;
Object desc;
String command;
String desc;
Object group;
Object nested;
Object once;
Boolean nested;
Boolean once;
Object pattern;
} Dict(create_autocmd);
typedef struct {
Object buffer;
OptionalKeys is_set__exec_autocmds_;
Buffer buffer;
Object group;
Object modeline;
Boolean modeline;
Object pattern;
Object data;
} Dict(exec_autocmds);
typedef struct {
OptionalKeys is_set__get_autocmds_;
Object event;
Object group;
Object pattern;
@@ -216,62 +231,66 @@ typedef struct {
} Dict(create_augroup);
typedef struct {
Object cmd;
Object range;
Object count;
Object reg;
Object bang;
Object args;
Object magic;
Object mods;
OptionalKeys is_set__cmd_;
String cmd;
Array range;
Integer count;
String reg;
Boolean bang;
Array args;
Dictionary magic;
Dictionary mods;
Object nargs;
Object addr;
Object nextcmd;
} Dict(cmd);
typedef struct {
Object file;
Object bar;
OptionalKeys is_set__cmd_magic_;
Boolean file;
Boolean bar;
} Dict(cmd_magic);
typedef struct {
Object silent;
Object emsg_silent;
Object unsilent;
Object filter;
Object sandbox;
Object noautocmd;
Object browse;
Object confirm;
Object hide;
Object horizontal;
Object keepalt;
Object keepjumps;
Object keepmarks;
Object keeppatterns;
Object lockmarks;
Object noswapfile;
Object tab;
Object verbose;
Object vertical;
Object split;
OptionalKeys is_set__cmd_mods_;
Boolean silent;
Boolean emsg_silent;
Boolean unsilent;
Dictionary filter;
Boolean sandbox;
Boolean noautocmd;
Boolean browse;
Boolean confirm;
Boolean hide;
Boolean horizontal;
Boolean keepalt;
Boolean keepjumps;
Boolean keepmarks;
Boolean keeppatterns;
Boolean lockmarks;
Boolean noswapfile;
Integer tab;
Integer verbose;
Boolean vertical;
String split;
} Dict(cmd_mods);
typedef struct {
Object pattern;
Object force;
OptionalKeys is_set__cmd_mods_filter_;
String pattern;
Boolean force;
} Dict(cmd_mods_filter);
typedef struct {
Object output;
Boolean output;
} Dict(cmd_opts);
typedef struct {
Object verbose;
Boolean verbose;
} Dict(echo_opts);
typedef struct {
Object output;
Boolean output;
} Dict(exec_opts);
#endif // NVIM_API_KEYSETS_H

View File

@@ -8,6 +8,7 @@
#include "nvim/api/options.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/autocmd.h"
@@ -26,14 +27,11 @@
static int validate_option_value_args(Dict(option) *opts, char *name, int *scope, int *opt_type,
void **from, char **filetype, Error *err)
{
if (HAS_KEY(opts->scope)) {
VALIDATE_T("scope", kObjectTypeString, opts->scope.type, {
return FAIL;
});
if (!strcmp(opts->scope.data.string.data, "local")) {
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
if (HAS_KEY_X(opts, scope)) {
if (!strcmp(opts->scope.data, "local")) {
*scope = OPT_LOCAL;
} else if (!strcmp(opts->scope.data.string.data, "global")) {
} else if (!strcmp(opts->scope.data, "global")) {
*scope = OPT_GLOBAL;
} else {
VALIDATE_EXP(false, "scope", "'local' or 'global'", NULL, {
@@ -44,51 +42,40 @@ static int validate_option_value_args(Dict(option) *opts, char *name, int *scope
*opt_type = SREQ_GLOBAL;
if (filetype != NULL && HAS_KEY(opts->filetype)) {
VALIDATE_T("scope", kObjectTypeString, opts->filetype.type, {
return FAIL;
});
*filetype = opts->filetype.data.string.data;
if (filetype != NULL && HAS_KEY_X(opts, filetype)) {
*filetype = opts->filetype.data;
}
if (HAS_KEY(opts->win)) {
VALIDATE_T_HANDLE("win", kObjectTypeWindow, opts->win.type, {
return FAIL;
});
if (HAS_KEY_X(opts, win)) {
*opt_type = SREQ_WIN;
*from = find_window_by_handle((int)opts->win.data.integer, err);
*from = find_window_by_handle(opts->win, err);
if (ERROR_SET(err)) {
return FAIL;
}
}
if (HAS_KEY(opts->buf)) {
VALIDATE_T_HANDLE("buf", kObjectTypeBuffer, opts->buf.type, {
return FAIL;
});
if (HAS_KEY_X(opts, buf)) {
*scope = OPT_LOCAL;
*opt_type = SREQ_BUF;
*from = find_buffer_by_handle((int)opts->buf.data.integer, err);
*from = find_buffer_by_handle(opts->buf, err);
if (ERROR_SET(err)) {
return FAIL;
}
}
VALIDATE((!HAS_KEY(opts->filetype)
|| !(HAS_KEY(opts->buf) || HAS_KEY(opts->scope) || HAS_KEY(opts->win))),
VALIDATE((!HAS_KEY_X(opts, filetype)
|| !(HAS_KEY_X(opts, buf) || HAS_KEY_X(opts, scope) || HAS_KEY_X(opts, win))),
"%s", "cannot use 'filetype' with 'scope', 'buf' or 'win'", {
return FAIL;
});
VALIDATE((!HAS_KEY(opts->scope) || !HAS_KEY(opts->buf)), "%s",
VALIDATE((!HAS_KEY_X(opts, scope) || !HAS_KEY_X(opts, buf)), "%s",
"cannot use both 'scope' and 'buf'", {
return FAIL;
});
VALIDATE((!HAS_KEY(opts->win) || !HAS_KEY(opts->buf)), "%s", "cannot use both 'buf' and 'win'", {
VALIDATE((!HAS_KEY_X(opts, win) || !HAS_KEY_X(opts, buf)),
"%s", "cannot use both 'buf' and 'win'", {
return FAIL;
});
@@ -111,6 +98,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, int *scope
}
return OK;
#undef HAS_KEY_X
}
/// Create a dummy buffer and run the FileType autocmd on it.

View File

@@ -124,10 +124,20 @@ struct key_value_pair {
Object value;
};
typedef Object *(*field_hash)(void *retval, const char *str, size_t len);
typedef uint64_t OptionalKeys;
// this is the prefix of all keysets with optional keys
typedef struct {
OptionalKeys is_set_;
} OptKeySet;
typedef struct {
char *str;
size_t ptr_off;
ObjectType type; // kObjectTypeNil == untyped
int opt_index;
} KeySetLink;
typedef KeySetLink *(*FieldHashfn)(const char *str, size_t len);
#endif // NVIM_API_PRIVATE_DEFS_H

View File

@@ -16,6 +16,7 @@
#include "nvim/api/private/converter.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/buffer_defs.h"
#include "nvim/eval/typval.h"
@@ -915,17 +916,84 @@ free_exit:
return (HlMessage)KV_INITIAL_VALUE;
}
bool api_dict_to_keydict(void *rv, field_hash hashy, Dictionary dict, Error *err)
// see also nlua_pop_keydict for the lua specific implementation
bool api_dict_to_keydict(void *retval, FieldHashfn hashy, Dictionary dict, Error *err)
{
for (size_t i = 0; i < dict.size; i++) {
String k = dict.items[i].key;
Object *field = hashy(rv, k.data, k.size);
KeySetLink *field = hashy(k.data, k.size);
if (!field) {
api_set_error(err, kErrorTypeValidation, "Invalid key: '%.*s'", (int)k.size, k.data);
return false;
}
*field = dict.items[i].value;
if (field->opt_index >= 0) {
OptKeySet *ks = (OptKeySet *)retval;
ks->is_set_ |= (1ULL << field->opt_index);
}
char *mem = ((char *)retval + field->ptr_off);
Object *value = &dict.items[i].value;
if (field->type == kObjectTypeNil) {
*(Object *)mem = *value;
} else if (field->type == kObjectTypeInteger) {
VALIDATE_T(field->str, kObjectTypeInteger, value->type, {
return false;
});
*(Integer *)mem = value->data.integer;
} else if (field->type == kObjectTypeFloat) {
Float *val = (Float *)mem;
if (value->type == kObjectTypeInteger) {
*val = (Float)value->data.integer;
} else {
VALIDATE_T(field->str, kObjectTypeFloat, value->type, {
return false;
});
*val = value->data.floating;
}
} else if (field->type == kObjectTypeBoolean) {
// caller should check HAS_KEY to override the nil behavior, or GET_BOOL_OR_TRUE
// to directly use true when nil
*(Boolean *)mem = api_object_to_bool(*value, field->str, false, err);
if (ERROR_SET(err)) {
return false;
}
} else if (field->type == kObjectTypeString) {
VALIDATE_T(field->str, kObjectTypeString, value->type, {
return false;
});
*(String *)mem = value->data.string;
} else if (field->type == kObjectTypeArray) {
VALIDATE_T(field->str, kObjectTypeArray, value->type, {
return false;
});
*(Array *)mem = value->data.array;
} else if (field->type == kObjectTypeDictionary) {
Dictionary *val = (Dictionary *)mem;
// allow empty array as empty dict for lua (directly or via lua-client RPC)
if (value->type == kObjectTypeArray && value->data.array.size == 0) {
*val = (Dictionary)ARRAY_DICT_INIT;
} else if (value->type == kObjectTypeDictionary) {
*val = value->data.dictionary;
} else {
api_err_exp(err, field->str, api_typename(field->type), api_typename(value->type));
return false;
}
} else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow
|| field->type == kObjectTypeTabpage) {
if (value->type == kObjectTypeInteger || value->type == field->type) {
*(handle_T *)mem = (handle_T)value->data.integer;
} else {
api_err_exp(err, field->str, api_typename(field->type), api_typename(value->type));
return false;
}
} else if (field->type == kObjectTypeLuaRef) {
api_set_error(err, kErrorTypeValidation, "Invalid key: '%.*s' is only allowed from Lua",
(int)k.size, k.data);
return false;
} else {
abort();
}
}
return true;
@@ -934,7 +1002,18 @@ bool api_dict_to_keydict(void *rv, field_hash hashy, Dictionary dict, Error *err
void api_free_keydict(void *dict, KeySetLink *table)
{
for (size_t i = 0; table[i].str; i++) {
api_free_object(*(Object *)((char *)dict + table[i].ptr_off));
char *mem = ((char *)dict + table[i].ptr_off);
if (table[i].type == kObjectTypeNil) {
api_free_object(*(Object *)mem);
} else if (table[i].type == kObjectTypeString) {
api_free_string(*(String *)mem);
} else if (table[i].type == kObjectTypeArray) {
api_free_array(*(Array *)mem);
} else if (table[i].type == kObjectTypeDictionary) {
api_free_dictionary(*(Dictionary *)mem);
} else if (table[i].type == kObjectTypeLuaRef) {
api_free_luaref(*(LuaRef *)mem);
}
}
}

View File

@@ -63,8 +63,9 @@
#define NIL ((Object)OBJECT_INIT)
#define NULL_STRING ((String)STRING_INIT)
// currently treat key=vim.NIL as if the key was missing
#define HAS_KEY(o) ((o).type != kObjectTypeNil)
#define HAS_KEY(d, typ, key) (((d)->is_set__##typ##_ & (1 << KEYSET_OPTIDX_##typ##__##key)) != 0)
#define GET_BOOL_OR_TRUE(d, typ, key) (HAS_KEY(d, typ, key) ? (d)->key : true)
#define PUT(dict, k, v) \
kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v }))

View File

@@ -558,16 +558,15 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E
FUNC_API_SINCE(8)
FUNC_API_FAST
{
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()), "%s", "'do_source' used in fast callback", {});
VALIDATE((!opts->do_source || nlua_is_deferred_safe()), "%s", "'do_source' used in fast callback",
{});
if (ERROR_SET(err)) {
return (Array)ARRAY_DICT_INIT;
}
ArrayOf(String) res = runtime_get_named(is_lua, pat, all);
ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all);
if (source) {
if (opts->do_source) {
for (size_t i = 0; i < res.size; i++) {
String name = res.items[i].data.string;
(void)do_source(name.data, false, DOSO_NONE, NULL);
@@ -718,15 +717,13 @@ void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
goto error;
}
bool verbose = api_object_to_bool(opts->verbose, "verbose", false, err);
if (verbose) {
if (opts->verbose) {
verbose_enter();
}
msg_multiattr(hl_msg, history ? "echomsg" : "echo", history);
if (verbose) {
if (opts->verbose) {
verbose_leave();
verbose_stop(); // flush now
}
@@ -1323,11 +1320,8 @@ Dictionary nvim_get_context(Dict(context) *opts, Error *err)
FUNC_API_SINCE(6)
{
Array types = ARRAY_DICT_INIT;
if (HAS_KEY(opts->types)) {
VALIDATE_T("types", kObjectTypeArray, opts->types.type, {
return (Dictionary)ARRAY_DICT_INIT;
});
types = opts->types.data.array;
if (HAS_KEY(opts, context, types)) {
types = opts->types;
}
int int_types = types.size > 0 ? 0 : kCtxAll;
@@ -2091,12 +2085,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
int maxwidth;
int fillchar = 0;
int use_bools = 0;
int statuscol_lnum = 0;
Window window = 0;
bool use_winbar = false;
bool use_tabline = false;
bool highlights = false;
if (str.size < 2 || memcmp(str.data, "%!", 2) != 0) {
const char *const errmsg = check_stl_option(str.data);
@@ -2105,58 +2095,28 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
});
}
if (HAS_KEY(opts->winid)) {
VALIDATE_T("winid", kObjectTypeInteger, opts->winid.type, {
return result;
});
window = (Window)opts->winid.data.integer;
if (HAS_KEY(opts, eval_statusline, winid)) {
window = opts->winid;
}
if (HAS_KEY(opts->fillchar)) {
VALIDATE_T("fillchar", kObjectTypeString, opts->fillchar.type, {
return result;
});
VALIDATE_EXP((opts->fillchar.data.string.size != 0
&& ((size_t)utf_ptr2len(opts->fillchar.data.string.data)
== opts->fillchar.data.string.size)),
if (HAS_KEY(opts, eval_statusline, fillchar)) {
VALIDATE_EXP((*opts->fillchar.data != 0
&& ((size_t)utf_ptr2len(opts->fillchar.data) == opts->fillchar.size)),
"fillchar", "single character", NULL, {
return result;
});
fillchar = utf_ptr2char(opts->fillchar.data.string.data);
}
if (HAS_KEY(opts->highlights)) {
highlights = api_object_to_bool(opts->highlights, "highlights", false, err);
if (ERROR_SET(err)) {
return result;
}
}
if (HAS_KEY(opts->use_winbar)) {
use_winbar = api_object_to_bool(opts->use_winbar, "use_winbar", false, err);
if (ERROR_SET(err)) {
return result;
}
use_bools++;
}
if (HAS_KEY(opts->use_tabline)) {
use_tabline = api_object_to_bool(opts->use_tabline, "use_tabline", false, err);
if (ERROR_SET(err)) {
return result;
}
use_bools++;
fillchar = utf_ptr2char(opts->fillchar.data);
}
win_T *wp = use_tabline ? curwin : find_window_by_handle(window, err);
int use_bools = (int)opts->use_winbar + (int)opts->use_tabline;
win_T *wp = opts->use_tabline ? curwin : find_window_by_handle(window, err);
if (wp == NULL) {
api_set_error(err, kErrorTypeException, "unknown winid %d", window);
return result;
}
if (HAS_KEY(opts->use_statuscol_lnum)) {
VALIDATE_T("use_statuscol_lnum", kObjectTypeInteger, opts->use_statuscol_lnum.type, {
return result;
});
statuscol_lnum = (int)opts->use_statuscol_lnum.data.integer;
if (HAS_KEY(opts, eval_statusline, use_statuscol_lnum)) {
statuscol_lnum = (int)opts->use_statuscol_lnum;
VALIDATE_RANGE(statuscol_lnum > 0 && statuscol_lnum <= wp->w_buffer->b_ml.ml_line_count,
"use_statuscol_lnum", {
return result;
@@ -2172,11 +2132,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
statuscol_T statuscol = { 0 };
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
if (use_tabline) {
if (opts->use_tabline) {
fillchar = ' ';
} else {
if (fillchar == 0) {
if (use_winbar) {
if (opts->use_winbar) {
fillchar = wp->w_p_fcs_chars.wbr;
} else {
int attr;
@@ -2220,15 +2180,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
}
}
if (HAS_KEY(opts->maxwidth)) {
VALIDATE_T("maxwidth", kObjectTypeInteger, opts->maxwidth.type, {
return result;
});
maxwidth = (int)opts->maxwidth.data.integer;
if (HAS_KEY(opts, eval_statusline, maxwidth)) {
maxwidth = (int)opts->maxwidth;
} else {
maxwidth = statuscol_lnum ? win_col_off(wp)
: (use_tabline || (!use_winbar && global_stl_height() > 0)) ? Columns : wp->w_width;
: (opts->use_tabline
|| (!opts->use_winbar && global_stl_height() > 0)) ? Columns : wp->w_width;
}
char buf[MAXPATHL];
@@ -2246,7 +2203,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
0,
fillchar,
maxwidth,
highlights ? &hltab : NULL,
opts->highlights ? &hltab : NULL,
NULL,
statuscol_lnum ? &statuscol : NULL);
@@ -2255,7 +2212,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
// Restore original value of 'cursorbind'
wp->w_p_crb = p_crb_save;
if (highlights) {
if (opts->highlights) {
Array hl_values = ARRAY_DICT_INIT;
const char *grpname;
char user_group[15]; // strlen("User") + strlen("2147483647") + NUL
@@ -2264,7 +2221,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
// add the default highlight at the beginning of the highlight list
if (hltab->start == NULL || (hltab->start - buf) != 0) {
Dictionary hl_info = ARRAY_DICT_INIT;
grpname = get_default_stl_hl(use_tabline ? NULL : wp, use_winbar, stc_hl_id);
grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id);
PUT(hl_info, "start", INTEGER_OBJ(0));
PUT(hl_info, "group", CSTR_TO_OBJ(grpname));
@@ -2278,7 +2235,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
PUT(hl_info, "start", INTEGER_OBJ(sp->start - buf));
if (sp->userhl == 0) {
grpname = get_default_stl_hl(use_tabline ? NULL : wp, use_winbar, stc_hl_id);
grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id);
} else if (sp->userhl < 0) {
grpname = syn_id2name(-sp->userhl);
} else {

View File

@@ -61,7 +61,7 @@ Dictionary nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Er
return result;
}
if (HAS_KEY(opts->output) && api_object_to_bool(opts->output, "opts.output", false, err)) {
if (opts->output) {
PUT(result, "output", STRING_OBJ(output));
}
@@ -70,19 +70,17 @@ Dictionary nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Er
String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *err)
{
Boolean output = api_object_to_bool(opts->output, "opts.output", false, err);
const int save_msg_silent = msg_silent;
garray_T *const save_capture_ga = capture_ga;
const int save_msg_col = msg_col;
garray_T capture_local;
if (output) {
if (opts->output) {
ga_init(&capture_local, 1, 80);
capture_ga = &capture_local;
}
try_start();
if (output) {
if (opts->output) {
msg_silent++;
msg_col = 0; // prevent leading spaces
}
@@ -90,7 +88,7 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
const sctx_T save_current_sctx = api_set_sctx(channel_id);
do_source_str(src.data, "nvim_exec2()");
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.
@@ -104,7 +102,7 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
goto theend;
}
if (output && capture_local.ga_len > 1) {
if (opts->output && capture_local.ga_len > 1) {
String s = (String){
.data = capture_local.ga_data,
.size = (size_t)capture_local.ga_len,
@@ -118,7 +116,7 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
return s; // Caller will free the memory.
}
theend:
if (output) {
if (opts->output) {
ga_clear(&capture_local);
}
return (String)STRING_INIT;

View File

@@ -7,6 +7,7 @@
#include "klib/kvec.h"
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii.h"
@@ -231,7 +232,7 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
win_config_float(win, fconfig);
win->w_pos_changed = true;
}
if (HAS_KEY(config->style)) {
if (HAS_KEY(config, float_config, style)) {
if (fconfig.style == kWinStyleMinimal) {
win_set_minimal_style(win);
didset_window_options(win, true);
@@ -380,12 +381,8 @@ static bool parse_float_bufpos(Array bufpos, lpos_T *out)
return true;
}
static void parse_border_title(Object title, Object title_pos, FloatConfig *fconfig, Error *err)
static void parse_border_title(Object title, FloatConfig *fconfig, Error *err)
{
if (!parse_title_pos(title_pos, fconfig, err)) {
return;
}
if (title.type == kObjectTypeString) {
if (title.data.string.size == 0) {
fconfig->title = false;
@@ -415,24 +412,14 @@ static void parse_border_title(Object title, Object title_pos, FloatConfig *fcon
fconfig->title = true;
}
static bool parse_title_pos(Object title_pos, FloatConfig *fconfig, Error *err)
static bool parse_title_pos(String title_pos, FloatConfig *fconfig, Error *err)
{
if (!HAS_KEY(title_pos)) {
if (title_pos.size == 0) {
fconfig->title_pos = kAlignLeft;
return true;
}
if (title_pos.type != kObjectTypeString) {
api_set_error(err, kErrorTypeValidation, "title_pos must be string");
return false;
}
if (title_pos.data.string.size == 0) {
fconfig->title_pos = kAlignLeft;
return true;
}
char *pos = title_pos.data.string.data;
char *pos = title_pos.data;
if (strequal(pos, "left")) {
fconfig->title_pos = kAlignLeft;
@@ -559,110 +546,90 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf,
bool new_win, Error *err)
{
#define HAS_KEY_X(d, key) HAS_KEY(d, float_config, key)
bool has_relative = false, relative_is_win = false;
if (config->relative.type == kObjectTypeString) {
// ignore empty string, to match nvim_win_get_config
if (config->relative.data.string.size > 0) {
if (!parse_float_relative(config->relative.data.string, &fconfig->relative)) {
api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key");
return false;
}
if (!(HAS_KEY(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) {
api_set_error(err, kErrorTypeValidation,
"'relative' requires 'row'/'col' or 'bufpos'");
return false;
}
has_relative = true;
fconfig->external = false;
if (fconfig->relative == kFloatRelativeWindow) {
relative_is_win = true;
fconfig->bufpos.lnum = -1;
}
// ignore empty string, to match nvim_win_get_config
if (HAS_KEY_X(config, relative) && config->relative.size > 0) {
if (!parse_float_relative(config->relative, &fconfig->relative)) {
api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key");
return false;
}
if (!(HAS_KEY_X(config, row) && HAS_KEY_X(config, col)) && !HAS_KEY_X(config, bufpos)) {
api_set_error(err, kErrorTypeValidation,
"'relative' requires 'row'/'col' or 'bufpos'");
return false;
}
has_relative = true;
fconfig->external = false;
if (fconfig->relative == kFloatRelativeWindow) {
relative_is_win = true;
fconfig->bufpos.lnum = -1;
}
} else if (HAS_KEY(config->relative)) {
api_set_error(err, kErrorTypeValidation, "'relative' key must be String");
return false;
}
if (config->anchor.type == kObjectTypeString) {
if (!parse_float_anchor(config->anchor.data.string, &fconfig->anchor)) {
if (HAS_KEY_X(config, anchor)) {
if (!parse_float_anchor(config->anchor, &fconfig->anchor)) {
api_set_error(err, kErrorTypeValidation, "Invalid value of 'anchor' key");
return false;
}
} else if (HAS_KEY(config->anchor)) {
api_set_error(err, kErrorTypeValidation, "'anchor' key must be String");
return false;
}
if (HAS_KEY(config->row)) {
if (HAS_KEY_X(config, row)) {
if (!has_relative) {
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'");
return false;
} else if (config->row.type == kObjectTypeInteger) {
fconfig->row = (double)config->row.data.integer;
} else if (config->row.type == kObjectTypeFloat) {
fconfig->row = config->row.data.floating;
} else {
api_set_error(err, kErrorTypeValidation,
"'row' key must be Integer or Float");
return false;
}
fconfig->row = config->row;
}
if (HAS_KEY(config->col)) {
if (HAS_KEY_X(config, col)) {
if (!has_relative) {
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'");
return false;
} else if (config->col.type == kObjectTypeInteger) {
fconfig->col = (double)config->col.data.integer;
} else if (config->col.type == kObjectTypeFloat) {
fconfig->col = config->col.data.floating;
} else {
api_set_error(err, kErrorTypeValidation,
"'col' key must be Integer or Float");
return false;
}
fconfig->col = config->col;
}
if (HAS_KEY(config->bufpos)) {
if (HAS_KEY_X(config, bufpos)) {
if (!has_relative) {
api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'");
return false;
} else if (config->bufpos.type == kObjectTypeArray) {
if (!parse_float_bufpos(config->bufpos.data.array, &fconfig->bufpos)) {
} else {
if (!parse_float_bufpos(config->bufpos, &fconfig->bufpos)) {
api_set_error(err, kErrorTypeValidation, "Invalid value of 'bufpos' key");
return false;
}
if (!HAS_KEY(config->row)) {
if (!HAS_KEY_X(config, row)) {
fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1;
}
if (!HAS_KEY(config->col)) {
if (!HAS_KEY_X(config, col)) {
fconfig->col = 0;
}
} else {
api_set_error(err, kErrorTypeValidation, "'bufpos' key must be Array");
return false;
}
}
if (config->width.type == kObjectTypeInteger && config->width.data.integer > 0) {
fconfig->width = (int)config->width.data.integer;
} else if (HAS_KEY(config->width)) {
api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer");
return false;
if (HAS_KEY_X(config, width)) {
if (config->width > 0) {
fconfig->width = (int)config->width;
} else {
api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer");
return false;
}
} else if (!reconf) {
api_set_error(err, kErrorTypeValidation, "Must specify 'width'");
return false;
}
if (config->height.type == kObjectTypeInteger && config->height.data.integer > 0) {
fconfig->height = (int)config->height.data.integer;
} else if (HAS_KEY(config->height)) {
api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer");
return false;
if (HAS_KEY_X(config, height)) {
if (config->height > 0) {
fconfig->height = (int)config->height;
} else {
api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer");
return false;
}
} else if (!reconf) {
api_set_error(err, kErrorTypeValidation, "Must specify 'height'");
return false;
@@ -670,26 +637,20 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
if (relative_is_win) {
fconfig->window = curwin->handle;
if (config->win.type == kObjectTypeInteger || config->win.type == kObjectTypeWindow) {
if (config->win.data.integer > 0) {
fconfig->window = (Window)config->win.data.integer;
if (HAS_KEY_X(config, win)) {
if (config->win > 0) {
fconfig->window = config->win;
}
} else if (HAS_KEY(config->win)) {
api_set_error(err, kErrorTypeValidation, "'win' key must be Integer or Window");
return false;
}
} else {
if (HAS_KEY(config->win)) {
if (HAS_KEY_X(config, win)) {
api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'");
return false;
}
}
if (HAS_KEY(config->external)) {
fconfig->external = api_object_to_bool(config->external, "'external' key", false, err);
if (ERROR_SET(err)) {
return false;
}
if (HAS_KEY_X(config, external)) {
fconfig->external = config->external;
if (has_relative && fconfig->external) {
api_set_error(err, kErrorTypeValidation,
"Only one of 'relative' and 'external' must be used");
@@ -708,30 +669,22 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
return false;
}
if (HAS_KEY(config->focusable)) {
fconfig->focusable = api_object_to_bool(config->focusable, "'focusable' key", false, err);
if (ERROR_SET(err)) {
if (HAS_KEY_X(config, focusable)) {
fconfig->focusable = config->focusable;
}
if (HAS_KEY_X(config, zindex)) {
if (config->zindex > 0) {
fconfig->zindex = (int)config->zindex;
} else {
api_set_error(err, kErrorTypeValidation, "'zindex' key must be a positive Integer");
return false;
}
}
if (config->zindex.type == kObjectTypeInteger && config->zindex.data.integer > 0) {
fconfig->zindex = (int)config->zindex.data.integer;
} else if (HAS_KEY(config->zindex)) {
api_set_error(err, kErrorTypeValidation, "'zindex' key must be a positive Integer");
return false;
}
if (HAS_KEY(config->title_pos)) {
if (!HAS_KEY(config->title)) {
api_set_error(err, kErrorTypeException, "title_pos requires title to be set");
return false;
}
}
if (HAS_KEY(config->title)) {
if (HAS_KEY_X(config, title)) {
// title only work with border
if (!HAS_KEY(config->border) && !fconfig->border) {
if (!HAS_KEY_X(config, border) && !fconfig->border) {
api_set_error(err, kErrorTypeException, "title requires border to be set");
return false;
}
@@ -739,42 +692,49 @@ static bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig,
if (fconfig->title) {
clear_virttext(&fconfig->title_chunks);
}
parse_border_title(config->title, config->title_pos, fconfig, err);
parse_border_title(config->title, fconfig, err);
if (ERROR_SET(err)) {
return false;
}
// handles unset 'title_pos' same as empty string
if (!parse_title_pos(config->title_pos, fconfig, err)) {
return false;
}
} else {
if (HAS_KEY_X(config, title_pos)) {
api_set_error(err, kErrorTypeException, "title_pos requires title to be set");
return false;
}
}
if (HAS_KEY(config->border)) {
if (HAS_KEY_X(config, border)) {
parse_border_style(config->border, fconfig, err);
if (ERROR_SET(err)) {
return false;
}
}
if (config->style.type == kObjectTypeString) {
if (config->style.data.string.data[0] == NUL) {
if (HAS_KEY_X(config, style)) {
if (config->style.data[0] == NUL) {
fconfig->style = kWinStyleUnused;
} else if (striequal(config->style.data.string.data, "minimal")) {
} else if (striequal(config->style.data, "minimal")) {
fconfig->style = kWinStyleMinimal;
} else {
api_set_error(err, kErrorTypeValidation, "Invalid value of 'style' key");
return false;
}
} else if (HAS_KEY(config->style)) {
api_set_error(err, kErrorTypeValidation, "'style' key must be String");
return false;
}
if (HAS_KEY(config->noautocmd)) {
if (HAS_KEY_X(config, noautocmd)) {
if (!new_win) {
api_set_error(err, kErrorTypeValidation, "Invalid key: 'noautocmd'");
return false;
}
fconfig->noautocmd = api_object_to_bool(config->noautocmd, "'noautocmd' key", false, err);
if (ERROR_SET(err)) {
return false;
}
fconfig->noautocmd = config->noautocmd;
}
return true;
#undef HAS_KEY_X
}

View File

@@ -7,6 +7,7 @@
#include <stdlib.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/window.h"
@@ -513,18 +514,12 @@ Dictionary nvim_win_text_height(Window window, Dict(win_text_height) *opts, Aren
bool oob = false;
if (HAS_KEY(opts->start_row)) {
VALIDATE_T("start_row", kObjectTypeInteger, opts->start_row.type, {
return rv;
});
start_lnum = (linenr_T)normalize_index(buf, opts->start_row.data.integer, false, &oob);
if (HAS_KEY(opts, win_text_height, start_row)) {
start_lnum = (linenr_T)normalize_index(buf, opts->start_row, false, &oob);
}
if (HAS_KEY(opts->end_row)) {
VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, {
return rv;
});
end_lnum = (linenr_T)normalize_index(buf, opts->end_row.data.integer, false, &oob);
if (HAS_KEY(opts, win_text_height, end_row)) {
end_lnum = (linenr_T)normalize_index(buf, opts->end_row, false, &oob);
}
VALIDATE(!oob, "%s", "Line index out of bounds", {
@@ -534,27 +529,23 @@ Dictionary nvim_win_text_height(Window window, Dict(win_text_height) *opts, Aren
return rv;
});
if (HAS_KEY(opts->start_vcol)) {
VALIDATE(HAS_KEY(opts->start_row), "%s", "'start_vcol' specified without 'start_row'", {
if (HAS_KEY(opts, win_text_height, start_vcol)) {
VALIDATE(HAS_KEY(opts, win_text_height, start_row),
"%s", "'start_vcol' specified without 'start_row'", {
return rv;
});
VALIDATE_T("start_vcol", kObjectTypeInteger, opts->start_vcol.type, {
return rv;
});
start_vcol = opts->start_vcol.data.integer;
start_vcol = opts->start_vcol;
VALIDATE_RANGE((start_vcol >= 0 && start_vcol <= MAXCOL), "start_vcol", {
return rv;
});
}
if (HAS_KEY(opts->end_vcol)) {
VALIDATE(HAS_KEY(opts->end_row), "%s", "'end_vcol' specified without 'end_row'", {
if (HAS_KEY(opts, win_text_height, end_vcol)) {
VALIDATE(HAS_KEY(opts, win_text_height, end_row),
"%s", "'end_vcol' specified without 'end_row'", {
return rv;
});
VALIDATE_T("end_vcol", kObjectTypeInteger, opts->end_vcol.type, {
return rv;
});
end_vcol = opts->end_vcol.data.integer;
end_vcol = opts->end_vcol;
VALIDATE_RANGE((end_vcol >= 0 && end_vcol <= MAXCOL), "end_vcol", {
return rv;
});
@@ -568,7 +559,7 @@ Dictionary nvim_win_text_height(Window window, Dict(win_text_height) *opts, Aren
int64_t fill = 0;
int64_t all = win_text_height(win, start_lnum, start_vcol, end_lnum, end_vcol, &fill);
if (!HAS_KEY(opts->end_row)) {
if (!HAS_KEY(opts, win_text_height, end_row)) {
const int64_t end_fill = win_get_fill(win, line_count + 1);
fill += end_fill;
all += end_fill;