diff options
author | bfredl <bjorn.linse@gmail.com> | 2023-08-01 14:01:19 +0200 |
---|---|---|
committer | bfredl <bjorn.linse@gmail.com> | 2023-08-07 13:11:15 +0200 |
commit | 7bc93e0e2f246dd78026a3472d929a0fe450f70d (patch) | |
tree | 9e5b99830c3f08e0ffd75c7a0533b39033490a5b | |
parent | c01e624b0762b24a4988bf9474a57d5b6278d180 (diff) | |
download | rneovim-7bc93e0e2f246dd78026a3472d929a0fe450f70d.tar.gz rneovim-7bc93e0e2f246dd78026a3472d929a0fe450f70d.tar.bz2 rneovim-7bc93e0e2f246dd78026a3472d929a0fe450f70d.zip |
refactor(api): use typed keysets
Initially this is just for geting rid of boilerplate,
but eventually the types could get exposed as metadata
-rwxr-xr-x | scripts/gen_eval_files.lua | 17 | ||||
-rw-r--r-- | src/nvim/api/autocmd.c | 92 | ||||
-rw-r--r-- | src/nvim/api/command.c | 240 | ||||
-rw-r--r-- | src/nvim/api/deprecated.c | 6 | ||||
-rw-r--r-- | src/nvim/api/extmark.c | 166 | ||||
-rw-r--r-- | src/nvim/api/keysets.h | 321 | ||||
-rw-r--r-- | src/nvim/api/options.c | 46 | ||||
-rw-r--r-- | src/nvim/api/private/defs.h | 12 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 87 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.h | 5 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 99 | ||||
-rw-r--r-- | src/nvim/api/vimscript.c | 14 | ||||
-rw-r--r-- | src/nvim/api/win_config.c | 206 | ||||
-rw-r--r-- | src/nvim/api/window.c | 37 | ||||
-rw-r--r-- | src/nvim/context.c | 3 | ||||
-rw-r--r-- | src/nvim/generators/gen_api_dispatch.lua | 87 | ||||
-rw-r--r-- | src/nvim/highlight.c | 40 | ||||
-rw-r--r-- | src/nvim/highlight_group.c | 25 | ||||
-rw-r--r-- | src/nvim/lua/converter.c | 73 | ||||
-rw-r--r-- | src/nvim/lua/converter.h | 4 | ||||
-rw-r--r-- | src/nvim/mapping.c | 37 | ||||
-rw-r--r-- | test/functional/api/extmark_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/api/highlight_spec.lua | 8 |
23 files changed, 766 insertions, 861 deletions
diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua index c3123fd47c..eea76958ae 100755 --- a/scripts/gen_eval_files.lua +++ b/scripts/gen_eval_files.lua @@ -260,19 +260,18 @@ local function get_api_keysets() local ret = {} --- @type table<string, vim.EvalFn> - --- @type {[1]: string, [2]: {[1]: string, [2]: string}[] }[] + --- @type {name: string, keys: string[], types: table<string,string>}[] local keysets = metadata.keysets - for _, keyset in ipairs(keysets) do - local kname = keyset[1] - local kdef = keyset[2] - for _, field in ipairs(kdef) do - field[2] = api_type(field[2]) + for _, k in ipairs(keysets) do + local params = {} + for _, key in ipairs(k.keys) do + table.insert(params, {key, api_type(k.types[key] or 'any')}) end - ret[kname] = { + ret[k.name] = { signature = 'NA', - name = kname, - params = kdef, + name = k.name, + params = params, } end diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 6ab1283f89..aa0c2695ad 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -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; } diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 75140beb88..2b09cfc4b2 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -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); diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 2e3ebd7c39..f5a77ece7e 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -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); } diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index d254373eb0..8a60d81649 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -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; - } -#define OPTION_TO_BOOL(target, name, val) \ - target = api_object_to_bool(opts->name, #name, val, err); \ - if (ERROR_SET(err)) { \ - goto error; \ + opts->end_row = opts->end_line; + did_end_line = true; } - 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; - }); + bool strict = GET_BOOL_OR_TRUE(opts, set_extmark, strict); - 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); - - if (HAS_KEY(opts->virt_lines)) { - VALIDATE_T("virt_lines", kObjectTypeArray, opts->virt_lines.type, { - goto error; - }); + bool virt_lines_leftcol = opts->virt_lines_leftcol; - 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); - - if (HAS_KEY(opts->priority)) { - VALIDATE_T("priority", kObjectTypeInteger, opts->priority.type, { - goto error; - }); - - Integer val = opts->priority.data.integer; + decor.virt_lines_above = opts->virt_lines_above; - VALIDATE_RANGE((val >= 0 && val <= UINT16_MAX), "priority", { + if (HAS_KEY(opts, set_extmark, priority)) { + VALIDATE_RANGE((opts->priority >= 0 && opts->priority <= 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|. diff --git a/src/nvim/api/keysets.h b/src/nvim/api/keysets.h index e08186161a..a47e278cad 100644 --- a/src/nvim/api/keysets.h +++ b/src/nvim/api/keysets.h @@ -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 diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 3d42f60940..e33cb72e8d 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -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. diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index b1b9e383b0..b467ce75a9 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -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 diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d0c8ab4dd4..bbc87422d0 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -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); + } } } diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index cb74c655cd..95e5cf67c8 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -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 })) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b4a6fa718b..22fe69e447 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -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); + fillchar = utf_ptr2char(opts->fillchar.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++; - } + int use_bools = (int)opts->use_winbar + (int)opts->use_tabline; - win_T *wp = use_tabline ? curwin : find_window_by_handle(window, err); + 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 { diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 8a598d86f0..4801562ad2 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -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; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 81a239d913..b666d1efa2 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -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; - } + // 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(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) { - api_set_error(err, kErrorTypeValidation, - "'relative' requires 'row'/'col' or 'bufpos'"); - 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; - } + 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)) { - 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_X(config, focusable)) { + fconfig->focusable = config->focusable; } - if (HAS_KEY(config->title_pos)) { - if (!HAS_KEY(config->title)) { - api_set_error(err, kErrorTypeException, "title_pos requires title to be set"); + 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 (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 } diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index f32a7e671d..f74071a002 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -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'", { - return rv; - }); - VALIDATE_T("end_vcol", kObjectTypeInteger, opts->end_vcol.type, { + 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; }); - 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; diff --git a/src/nvim/context.c b/src/nvim/context.c index f7b3491be7..ca28ed9970 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -271,8 +271,7 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly) size_t cmd_len = sizeof("func! ") + strlen(name); char *cmd = xmalloc(cmd_len); snprintf(cmd, cmd_len, "func! %s", name); - Dict(exec_opts) opts = { 0 }; - opts.output = BOOLEAN_OBJ(true); + Dict(exec_opts) opts = { .output = true }; String func_body = exec_impl(VIML_INTERNAL_CALL, cstr_as_string(cmd), &opts, &err); xfree(cmd); diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index fe63cb883e..9c15597fcc 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -67,13 +67,27 @@ local keysets = {} local function add_keyset(val) local keys = {} - for _,field in ipairs(val.fields) do + local types = {} + local is_set_name = 'is_set__' .. val.keyset_name .. '_' + local has_optional = false + for i,field in ipairs(val.fields) do if field.type ~= 'Object' then - error 'not yet implemented: types other than Object' + types[field.name] = field.type + end + if field.name ~= is_set_name and field.type ~= 'OptionalKeys' then + table.insert(keys, field.name) + else + if i > 1 then + error("'is_set__{type}_' must be first if present") + elseif field.name ~= is_set_name then + error(val.keyset_name..": name of first key should be "..is_set_name) + elseif field.type ~= 'OptionalKeys' then + error("'"..is_set_name.."' must have type 'OptionalKeys'") + end + has_optional = true end - table.insert(keys, {field.name, field.type}) end - table.insert(keysets, {val.keyset_name, keys}) + table.insert(keysets, {name=val.keyset_name, keys=keys, types=types, has_optional=has_optional}) end -- read each input file, parse and append to the api metadata @@ -232,53 +246,60 @@ output:write([[ ]]) -for _,keyset in ipairs(keysets) do - local name, keys = unpack(keyset) - local special = {} - local function sanitize(key) - if special[key] then - return key .. "_" - end - return key - end +for _,k in ipairs(keysets) do + local c_name = {} - local key_names = {} - for i = 1,#keys do - local kname = keys[i][1] - if vim.endswith(kname, "_") then - kname = string.sub(kname,1, #kname - 1) - special[kname] = true + for i = 1,#k.keys do + -- some keys, like "register" are c keywords and get + -- escaped with a trailing _ in the struct. + if vim.endswith(k.keys[i], "_") then + local orig = k.keys[i] + k.keys[i] = string.sub(k.keys[i],1, #(k.keys[i]) - 1) + c_name[k.keys[i]] = orig + k.types[k.keys[i]] = k.types[orig] end - key_names[i] = kname end - local neworder, hashfun = hashy.hashy_hash(name, key_names, function (idx) - return name.."_table["..idx.."].str" + + local neworder, hashfun = hashy.hashy_hash(k.name, k.keys, function (idx) + return k.name.."_table["..idx.."].str" end) - keysets_defs:write("extern KeySetLink "..name.."_table[];\n") + keysets_defs:write("extern KeySetLink "..k.name.."_table[];\n") - output:write("KeySetLink "..name.."_table[] = {\n") - for _, key in ipairs(neworder) do - output:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..sanitize(key)..")},\n") + local function typename(type) + if type ~= nil then + return "kObjectType"..type + else + return "kObjectTypeNil" + end + end + + output:write("KeySetLink "..k.name.."_table[] = {\n") + for i, key in ipairs(neworder) do + local ind = -1 + if k.has_optional then + ind = i + keysets_defs:write("#define KEYSET_OPTIDX_"..k.name.."__"..key.." "..ind.."\n") + end + output:write(' {"'..key..'", offsetof(KeyDict_'..k.name..", "..(c_name[key] or key).."), "..typename(k.types[key])..", "..ind.."},\n") end - output:write(' {NULL, 0},\n') + output:write(' {NULL, 0, kObjectTypeNil, -1},\n') output:write("};\n\n") output:write(hashfun) output:write([[ -Object *KeyDict_]]..name..[[_get_field(void *retval, const char *str, size_t len) +KeySetLink *KeyDict_]]..k.name..[[_get_field(const char *str, size_t len) { - int hash = ]]..name..[[_hash(str, len); + int hash = ]]..k.name..[[_hash(str, len); if (hash == -1) { return NULL; } - - return (Object *)((char *)retval + ]]..name..[[_table[hash].ptr_off); + return &]]..k.name..[[_table[hash]; } ]]) - keysets_defs:write("#define api_free_keydict_"..name.."(x) api_free_keydict(x, "..name.."_table)\n") + keysets_defs:write("#define api_free_keydict_"..k.name.."(x) api_free_keydict(x, "..k.name.."_table)\n") end local function real_type(type) @@ -666,7 +687,7 @@ local function process_function(fn) if (ERROR_SET(&err)) { luaL_where(lstate, 1); if (err_param) { - lua_pushstring(lstate, "param '"); + lua_pushstring(lstate, "Invalid '"); lua_pushstring(lstate, err_param); lua_pushstring(lstate, "': "); } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index fad113adc5..3755b7ae05 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -226,8 +226,8 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault) if (api_dict_to_keydict(&dict, KeyDict_highlight_get_field, ret.data.dictionary, &err)) { attrs = dict2hlattrs(&dict, true, &it.link_id, &err); - fallback = api_object_to_bool(dict.fallback, "fallback", true, &err); - tmp = api_object_to_bool(dict.fallback, "tmp", false, &err); + fallback = GET_BOOL_OR_TRUE(&dict, highlight, fallback); + tmp = dict.fallback; // or false if (it.link_id >= 0) { fallback = true; } @@ -938,6 +938,7 @@ void hlattrs2dict(Dictionary *hl, Dictionary *hl_attrs, HlAttrs ae, bool use_rgb HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err) { +#define HAS_KEY_X(d, key) HAS_KEY(d, highlight, key) HlAttrs hlattrs = HLATTRS_INIT; int32_t fg = -1, bg = -1, ctermfg = -1, ctermbg = -1, sp = -1; int blend = -1; @@ -946,7 +947,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e bool cterm_mask_provided = false; #define CHECK_FLAG(d, m, name, extra, flag) \ - if (api_object_to_bool(d->name##extra, #name, false, err)) { \ + if (d->name##extra) { \ if (flag & HL_UNDERLINE_MASK) { \ m &= ~HL_UNDERLINE_MASK; \ } \ @@ -971,52 +972,48 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE); CHECK_FLAG(dict, mask, default, _, HL_DEFAULT); - if (HAS_KEY(dict->fg)) { + if (HAS_KEY_X(dict, fg)) { fg = object_to_color(dict->fg, "fg", use_rgb, err); - } else if (HAS_KEY(dict->foreground)) { + } else if (HAS_KEY_X(dict, foreground)) { fg = object_to_color(dict->foreground, "foreground", use_rgb, err); } if (ERROR_SET(err)) { return hlattrs; } - if (HAS_KEY(dict->bg)) { + if (HAS_KEY_X(dict, bg)) { bg = object_to_color(dict->bg, "bg", use_rgb, err); - } else if (HAS_KEY(dict->background)) { + } else if (HAS_KEY_X(dict, background)) { bg = object_to_color(dict->background, "background", use_rgb, err); } if (ERROR_SET(err)) { return hlattrs; } - if (HAS_KEY(dict->sp)) { + if (HAS_KEY_X(dict, sp)) { sp = object_to_color(dict->sp, "sp", true, err); - } else if (HAS_KEY(dict->special)) { + } else if (HAS_KEY_X(dict, special)) { sp = object_to_color(dict->special, "special", true, err); } if (ERROR_SET(err)) { return hlattrs; } - if (HAS_KEY(dict->blend)) { - VALIDATE_T("blend", kObjectTypeInteger, dict->blend.type, { - return hlattrs; - }); - - Integer blend0 = dict->blend.data.integer; + if (HAS_KEY_X(dict, blend)) { + Integer blend0 = dict->blend; VALIDATE_RANGE((blend0 >= 0 && blend0 <= 100), "blend", { return hlattrs; }); blend = (int)blend0; } - if (HAS_KEY(dict->link) || HAS_KEY(dict->global_link)) { + if (HAS_KEY_X(dict, link) || HAS_KEY_X(dict, global_link)) { if (!link_id) { api_set_error(err, kErrorTypeValidation, "Invalid Key: '%s'", - HAS_KEY(dict->global_link) ? "global_link" : "link"); + HAS_KEY_X(dict, global_link) ? "global_link" : "link"); return hlattrs; } - if (HAS_KEY(dict->global_link)) { + if (HAS_KEY_X(dict, global_link)) { *link_id = object_to_hl_id(dict->global_link, "link", err); mask |= HL_GLOBAL; } else { @@ -1050,21 +1047,21 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e // empty list from Lua API should clear all cterm attributes // TODO(clason): handle via gen_api_dispatch cterm_mask_provided = true; - } else if (HAS_KEY(dict->cterm)) { + } else if (HAS_KEY_X(dict, cterm)) { VALIDATE_EXP(false, "cterm", "Dict", api_typename(dict->cterm.type), { return hlattrs; }); } #undef CHECK_FLAG - if (HAS_KEY(dict->ctermfg)) { + if (HAS_KEY_X(dict, ctermfg)) { ctermfg = object_to_color(dict->ctermfg, "ctermfg", false, err); if (ERROR_SET(err)) { return hlattrs; } } - if (HAS_KEY(dict->ctermbg)) { + if (HAS_KEY_X(dict, ctermbg)) { ctermbg = object_to_color(dict->ctermbg, "ctermbg", false, err); if (ERROR_SET(err)) { return hlattrs; @@ -1091,6 +1088,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e } return hlattrs; +#undef HAS_KEY_X } int object_to_color(Object val, char *key, bool rgb, Error *err) diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 9ae928a0d2..de53545195 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -832,9 +832,11 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) struct { int *dest; RgbValue val; Object name; } cattrs[] = { - { &g->sg_rgb_fg_idx, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, - { &g->sg_rgb_bg_idx, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, - { &g->sg_rgb_sp_idx, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, + { &g->sg_rgb_fg_idx, g->sg_rgb_fg, + HAS_KEY(dict, highlight, fg) ? dict->fg : dict->foreground }, + { &g->sg_rgb_bg_idx, g->sg_rgb_bg, + HAS_KEY(dict, highlight, bg) ? dict->bg : dict->background }, + { &g->sg_rgb_sp_idx, g->sg_rgb_sp, HAS_KEY(dict, highlight, sp) ? dict->sp : dict->special }, { NULL, -1, NIL }, }; @@ -1563,19 +1565,12 @@ static bool hlgroup2dict(Dictionary *hl, NS ns_id, int hl_id, Arena *arena) Dictionary ns_get_hl_defs(NS ns_id, Dict(get_highlight) *opts, Arena *arena, Error *err) { - Boolean link = api_object_to_bool(opts->link, "link", true, err); + Boolean link = GET_BOOL_OR_TRUE(opts, get_highlight, link); int id = -1; - if (opts->name.type != kObjectTypeNil) { - VALIDATE_T("highlight name", kObjectTypeString, opts->name.type, { - goto cleanup; - }); - String name = opts->name.data.string; - id = syn_check_group(name.data, name.size); - } else if (opts->id.type != kObjectTypeNil) { - VALIDATE_T("highlight id", kObjectTypeInteger, opts->id.type, { - goto cleanup; - }); - id = (int)opts->id.data.integer; + if (HAS_KEY(opts, get_highlight, name)) { + id = syn_check_group(opts->name.data, opts->name.size); + } else if (HAS_KEY(opts, get_highlight, id)) { + id = (int)opts->id; } if (id != -1) { diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 5b3b4dbe08..07c452deed 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -871,7 +871,8 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons { if (lua_type(lstate, -1) != LUA_TTABLE) { if (err) { - api_set_error(err, kErrorTypeValidation, "Expected lua table"); + api_set_error(err, kErrorTypeValidation, "Expected lua %s", + (type == kObjectTypeFloat) ? "number" : "table"); } return (LuaTableProps) { .type = kObjectTypeNil }; } @@ -884,7 +885,7 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons if (table_props.type != type) { if (err) { - api_set_error(err, kErrorTypeValidation, "Unexpected type"); + api_set_error(err, kErrorTypeValidation, "Expected %s-like lua table", api_typename(type)); } } @@ -1224,26 +1225,19 @@ LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err) return rv; } -#define GENERATE_INDEX_FUNCTION(type) \ - type nlua_pop_##type(lua_State *lstate, Error *err) \ - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ - { \ - type ret; \ - if (lua_type(lstate, -1) != LUA_TNUMBER) { \ - api_set_error(err, kErrorTypeValidation, "Expected Lua number"); \ - ret = (type) - 1; \ - } else { \ - ret = (type)lua_tonumber(lstate, -1); \ - } \ - lua_pop(lstate, 1); \ - return ret; \ +handle_T nlua_pop_handle(lua_State *lstate, Error *err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + handle_T ret; + if (lua_type(lstate, -1) != LUA_TNUMBER) { + api_set_error(err, kErrorTypeValidation, "Expected Lua number"); + ret = (handle_T) - 1; + } else { + ret = (handle_T)lua_tonumber(lstate, -1); } - -GENERATE_INDEX_FUNCTION(Buffer) -GENERATE_INDEX_FUNCTION(Window) -GENERATE_INDEX_FUNCTION(Tabpage) - -#undef GENERATE_INDEX_FUNCTION + lua_pop(lstate, 1); + return ret; +} /// Record some auxiliary values in vim module /// @@ -1292,7 +1286,8 @@ void nlua_init_types(lua_State *const lstate) lua_rawset(lstate, -3); } -void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err) +// lua specific variant of api_dict_to_keydict +void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, Error *err) { if (!lua_istable(L, -1)) { api_set_error(err, kErrorTypeValidation, "Expected lua table"); @@ -1305,14 +1300,44 @@ void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err) // [dict, key, value] size_t len; const char *s = lua_tolstring(L, -2, &len); - Object *field = hashy(retval, s, len); + KeySetLink *field = hashy(s, len); if (!field) { api_set_error(err, kErrorTypeValidation, "invalid key: %.*s", (int)len, s); lua_pop(L, 3); // [] return; } - *field = nlua_pop_Object(L, true, err); + if (field->opt_index >= 0) { + OptKeySet *ks = (OptKeySet *)retval; + ks->is_set_ |= (1ULL << field->opt_index); + } + char *mem = ((char *)retval + field->ptr_off); + + if (field->type == kObjectTypeNil) { + *(Object *)mem = nlua_pop_Object(L, true, err); + } else if (field->type == kObjectTypeInteger) { + *(Integer *)mem = nlua_pop_Integer(L, err); + } else if (field->type == kObjectTypeBoolean) { + *(Boolean *)mem = nlua_pop_Boolean(L, err); + } else if (field->type == kObjectTypeString) { + *(String *)mem = nlua_pop_String(L, err); + } else if (field->type == kObjectTypeFloat) { + *(Float *)mem = nlua_pop_Float(L, err); + } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow + || field->type == kObjectTypeTabpage) { + *(handle_T *)mem = nlua_pop_handle(L, err); + } else if (field->type == kObjectTypeArray) { + *(Array *)mem = nlua_pop_Array(L, err); + } else if (field->type == kObjectTypeDictionary) { + *(Dictionary *)mem = nlua_pop_Dictionary(L, false, err); + } else if (field->type == kObjectTypeLuaRef) { + *(LuaRef *)mem = nlua_pop_LuaRef(L, err); + } else { + abort(); + } + if (ERROR_SET(err)) { + break; + } } // [dict] lua_pop(L, 1); diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h index 241dd65cc7..d1d6b9d62a 100644 --- a/src/nvim/lua/converter.h +++ b/src/nvim/lua/converter.h @@ -9,6 +9,10 @@ #include "nvim/eval/typval_defs.h" #include "nvim/func_attr.h" +#define nlua_pop_Buffer nlua_pop_handle +#define nlua_pop_Window nlua_pop_handle +#define nlua_pop_Tabpage nlua_pop_handle + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/converter.h.generated.h" #endif diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index d7747ee291..0cb94e6f5b 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2558,26 +2558,22 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod const sctx_T save_current_sctx = api_set_sctx(channel_id); - if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) { - lua_funcref = opts->callback.data.luaref; - opts->callback.data.luaref = LUA_NOREF; - } MapArguments parsed_args = MAP_ARGUMENTS_INIT; if (opts) { -#define KEY_TO_BOOL(name) \ - parsed_args.name = api_object_to_bool(opts->name, #name, false, err); \ - if (ERROR_SET(err)) { \ - goto fail_and_free; \ - } - - KEY_TO_BOOL(nowait); - KEY_TO_BOOL(noremap); - KEY_TO_BOOL(silent); - KEY_TO_BOOL(script); - KEY_TO_BOOL(expr); - KEY_TO_BOOL(unique); - KEY_TO_BOOL(replace_keycodes); -#undef KEY_TO_BOOL + parsed_args.nowait = opts->nowait; + parsed_args.noremap = opts->noremap; + parsed_args.silent = opts->silent; + parsed_args.script = opts->script; + parsed_args.expr = opts->expr; + parsed_args.unique = opts->unique; + parsed_args.replace_keycodes = opts->replace_keycodes; + if (HAS_KEY(opts, keymap, callback)) { + lua_funcref = opts->callback; + opts->callback = LUA_NOREF; + } + if (HAS_KEY(opts, keymap, desc)) { + parsed_args.desc = string_to_cstr(opts->desc); + } } parsed_args.buffer = !global; @@ -2593,11 +2589,6 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod goto fail_and_free; } - if (opts != NULL && opts->desc.type == kObjectTypeString) { - parsed_args.desc = string_to_cstr(opts->desc.data.string); - } else { - parsed_args.desc = NULL; - } if (parsed_args.lhs_len > MAXMAPLEN || parsed_args.alt_lhs_len > MAXMAPLEN) { api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data); goto fail_and_free; diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index ec1c9245ba..6d8e3d8e0a 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -109,7 +109,7 @@ describe('API/extmarks', function() eq("Invalid 'virt_text_pos': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 'foo' })) eq("Invalid 'hl_mode': expected String, got Integer", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 0 })) eq("Invalid 'hl_mode': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 'foo' })) - eq("Invalid 'id': expected positive Integer", pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 })) + eq("Invalid 'id': expected Integer, got Array", pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 })) eq("Invalid mark position: expected 2 Integer items", pcall_err(get_extmarks, ns, {}, {-1, -1})) eq("Invalid mark position: expected mark id Integer or 2-item Array", pcall_err(get_extmarks, ns, true, {-1, -1})) -- No memory leak with virt_text, virt_lines, sign_text diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index d3a79327ae..5fa2235018 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -434,10 +434,8 @@ describe('API: get highlight', function() before_each(clear) it('validation', function() - eq( - 'Invalid highlight name: expected String, got Integer', - pcall_err(meths.get_hl, 0, { name = 177 }) - ) + eq("Invalid 'name': expected String, got Integer", + pcall_err(meths.get_hl, 0, { name = 177 })) eq('Highlight id out of bounds', pcall_err(meths.get_hl, 0, { name = 'Test set hl' })) end) @@ -534,7 +532,7 @@ describe('API: get highlight', function() eq('Highlight id out of bounds', pcall_err(meths.get_hl, 0, { id = 0 })) eq( - 'Invalid highlight id: expected Integer, got String', + "Invalid 'id': expected Integer, got String", pcall_err(meths.get_hl, 0, { id = 'Test_set_hl' }) ) |