Merge pull request #24524 from bfredl/typed_keys

refactor(api): use typed keysets
This commit is contained in:
bfredl 2023-08-07 14:42:25 +02:00 committed by GitHub
commit 3a21c3afe6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 958 additions and 1042 deletions

View File

@ -134,6 +134,8 @@ The following new APIs and features were added.
• `vim.fn.*` • `vim.fn.*`
• `vim.api.*` • `vim.api.*`
• Improved messages for type errors in `vim.api.*` calls (including `opts` params)
============================================================================== ==============================================================================
CHANGED FEATURES *news-changed* CHANGED FEATURES *news-changed*

View File

@ -4,112 +4,112 @@
error('Cannot require a meta file') error('Cannot require a meta file')
--- @class vim.api.keyset.clear_autocmds --- @class vim.api.keyset.clear_autocmds
--- @field buffer any --- @field buffer integer
--- @field event any --- @field event any
--- @field group any --- @field group any
--- @field pattern any --- @field pattern any
--- @class vim.api.keyset.cmd --- @class vim.api.keyset.cmd
--- @field cmd any --- @field cmd string
--- @field range any --- @field range any[]
--- @field count any --- @field count integer
--- @field reg any --- @field reg string
--- @field bang any --- @field bang boolean
--- @field args any --- @field args any[]
--- @field magic any --- @field magic table<string,any>
--- @field mods any --- @field mods table<string,any>
--- @field nargs any --- @field nargs any
--- @field addr any --- @field addr any
--- @field nextcmd any --- @field nextcmd any
--- @class vim.api.keyset.cmd_magic --- @class vim.api.keyset.cmd_magic
--- @field file any --- @field file boolean
--- @field bar any --- @field bar boolean
--- @class vim.api.keyset.cmd_mods --- @class vim.api.keyset.cmd_mods
--- @field silent any --- @field silent boolean
--- @field emsg_silent any --- @field emsg_silent boolean
--- @field unsilent any --- @field unsilent boolean
--- @field filter any --- @field filter table<string,any>
--- @field sandbox any --- @field sandbox boolean
--- @field noautocmd any --- @field noautocmd boolean
--- @field browse any --- @field browse boolean
--- @field confirm any --- @field confirm boolean
--- @field hide any --- @field hide boolean
--- @field horizontal any --- @field horizontal boolean
--- @field keepalt any --- @field keepalt boolean
--- @field keepjumps any --- @field keepjumps boolean
--- @field keepmarks any --- @field keepmarks boolean
--- @field keeppatterns any --- @field keeppatterns boolean
--- @field lockmarks any --- @field lockmarks boolean
--- @field noswapfile any --- @field noswapfile boolean
--- @field tab any --- @field tab integer
--- @field verbose any --- @field verbose integer
--- @field vertical any --- @field vertical boolean
--- @field split any --- @field split string
--- @class vim.api.keyset.cmd_mods_filter --- @class vim.api.keyset.cmd_mods_filter
--- @field pattern any --- @field pattern string
--- @field force any --- @field force boolean
--- @class vim.api.keyset.cmd_opts --- @class vim.api.keyset.cmd_opts
--- @field output any --- @field output boolean
--- @class vim.api.keyset.context --- @class vim.api.keyset.context
--- @field types any --- @field types any[]
--- @class vim.api.keyset.create_augroup --- @class vim.api.keyset.create_augroup
--- @field clear any --- @field clear any
--- @class vim.api.keyset.create_autocmd --- @class vim.api.keyset.create_autocmd
--- @field buffer any --- @field buffer integer
--- @field callback any --- @field callback any
--- @field command any --- @field command string
--- @field desc any --- @field desc string
--- @field group any --- @field group any
--- @field nested any --- @field nested boolean
--- @field once any --- @field once boolean
--- @field pattern any --- @field pattern any
--- @class vim.api.keyset.echo_opts --- @class vim.api.keyset.echo_opts
--- @field verbose any --- @field verbose boolean
--- @class vim.api.keyset.eval_statusline --- @class vim.api.keyset.eval_statusline
--- @field winid any --- @field winid integer
--- @field maxwidth any --- @field maxwidth integer
--- @field fillchar any --- @field fillchar string
--- @field highlights any --- @field highlights boolean
--- @field use_winbar any --- @field use_winbar boolean
--- @field use_tabline any --- @field use_tabline boolean
--- @field use_statuscol_lnum any --- @field use_statuscol_lnum integer
--- @class vim.api.keyset.exec_autocmds --- @class vim.api.keyset.exec_autocmds
--- @field buffer any --- @field buffer integer
--- @field group any --- @field group any
--- @field modeline any --- @field modeline boolean
--- @field pattern any --- @field pattern any
--- @field data any --- @field data any
--- @class vim.api.keyset.exec_opts --- @class vim.api.keyset.exec_opts
--- @field output any --- @field output boolean
--- @class vim.api.keyset.float_config --- @class vim.api.keyset.float_config
--- @field row any --- @field row number
--- @field col any --- @field col number
--- @field width any --- @field width integer
--- @field height any --- @field height integer
--- @field anchor any --- @field anchor string
--- @field relative any --- @field relative string
--- @field win any --- @field win integer
--- @field bufpos any --- @field bufpos any[]
--- @field external any --- @field external boolean
--- @field focusable any --- @field focusable boolean
--- @field zindex any --- @field zindex integer
--- @field border any --- @field border any
--- @field title any --- @field title any
--- @field title_pos any --- @field title_pos string
--- @field style any --- @field style string
--- @field noautocmd any --- @field noautocmd boolean
--- @class vim.api.keyset.get_autocmds --- @class vim.api.keyset.get_autocmds
--- @field event any --- @field event any
@ -118,27 +118,27 @@ error('Cannot require a meta file')
--- @field buffer any --- @field buffer any
--- @class vim.api.keyset.get_commands --- @class vim.api.keyset.get_commands
--- @field builtin any --- @field builtin boolean
--- @class vim.api.keyset.get_highlight --- @class vim.api.keyset.get_highlight
--- @field id any --- @field id integer
--- @field name any --- @field name string
--- @field link any --- @field link boolean
--- @class vim.api.keyset.highlight --- @class vim.api.keyset.highlight
--- @field bold any --- @field bold boolean
--- @field standout any --- @field standout boolean
--- @field strikethrough any --- @field strikethrough boolean
--- @field underline any --- @field underline boolean
--- @field undercurl any --- @field undercurl boolean
--- @field underdouble any --- @field underdouble boolean
--- @field underdotted any --- @field underdotted boolean
--- @field underdashed any --- @field underdashed boolean
--- @field italic any --- @field italic boolean
--- @field reverse any --- @field reverse boolean
--- @field altfont any --- @field altfont boolean
--- @field nocombine any --- @field nocombine boolean
--- @field default_ any --- @field default boolean
--- @field cterm any --- @field cterm any
--- @field foreground any --- @field foreground any
--- @field fg any --- @field fg any
@ -150,100 +150,100 @@ error('Cannot require a meta file')
--- @field sp any --- @field sp any
--- @field link any --- @field link any
--- @field global_link any --- @field global_link any
--- @field fallback any --- @field fallback boolean
--- @field blend any --- @field blend integer
--- @field fg_indexed any --- @field fg_indexed boolean
--- @field bg_indexed any --- @field bg_indexed boolean
--- @class vim.api.keyset.highlight_cterm --- @class vim.api.keyset.highlight_cterm
--- @field bold any --- @field bold boolean
--- @field standout any --- @field standout boolean
--- @field strikethrough any --- @field strikethrough boolean
--- @field underline any --- @field underline boolean
--- @field undercurl any --- @field undercurl boolean
--- @field underdouble any --- @field underdouble boolean
--- @field underdotted any --- @field underdotted boolean
--- @field underdashed any --- @field underdashed boolean
--- @field italic any --- @field italic boolean
--- @field reverse any --- @field reverse boolean
--- @field altfont any --- @field altfont boolean
--- @field nocombine any --- @field nocombine boolean
--- @class vim.api.keyset.keymap --- @class vim.api.keyset.keymap
--- @field noremap any --- @field noremap boolean
--- @field nowait any --- @field nowait boolean
--- @field silent any --- @field silent boolean
--- @field script any --- @field script boolean
--- @field expr any --- @field expr boolean
--- @field unique any --- @field unique boolean
--- @field callback any --- @field callback function
--- @field desc any --- @field desc string
--- @field replace_keycodes any --- @field replace_keycodes boolean
--- @class vim.api.keyset.option --- @class vim.api.keyset.option
--- @field scope any --- @field scope string
--- @field win any --- @field win integer
--- @field buf any --- @field buf integer
--- @field filetype any --- @field filetype string
--- @class vim.api.keyset.runtime --- @class vim.api.keyset.runtime
--- @field is_lua any --- @field is_lua boolean
--- @field do_source any --- @field do_source boolean
--- @class vim.api.keyset.set_decoration_provider --- @class vim.api.keyset.set_decoration_provider
--- @field on_start any --- @field on_start function
--- @field on_buf any --- @field on_buf function
--- @field on_win any --- @field on_win function
--- @field on_line any --- @field on_line function
--- @field on_end any --- @field on_end function
--- @field _on_hl_def any --- @field _on_hl_def function
--- @field _on_spell_nav any --- @field _on_spell_nav function
--- @class vim.api.keyset.set_extmark --- @class vim.api.keyset.set_extmark
--- @field id any --- @field id integer
--- @field end_line any --- @field end_line integer
--- @field end_row any --- @field end_row integer
--- @field end_col any --- @field end_col integer
--- @field hl_group any --- @field hl_group any
--- @field virt_text any --- @field virt_text any[]
--- @field virt_text_pos any --- @field virt_text_pos string
--- @field virt_text_win_col any --- @field virt_text_win_col integer
--- @field virt_text_hide any --- @field virt_text_hide boolean
--- @field hl_eol any --- @field hl_eol boolean
--- @field hl_mode any --- @field hl_mode string
--- @field ephemeral any --- @field ephemeral boolean
--- @field priority any --- @field priority integer
--- @field right_gravity any --- @field right_gravity boolean
--- @field end_right_gravity any --- @field end_right_gravity boolean
--- @field virt_lines any --- @field virt_lines any[]
--- @field virt_lines_above any --- @field virt_lines_above boolean
--- @field virt_lines_leftcol any --- @field virt_lines_leftcol boolean
--- @field strict any --- @field strict boolean
--- @field sign_text any --- @field sign_text string
--- @field sign_hl_group any --- @field sign_hl_group any
--- @field number_hl_group any --- @field number_hl_group any
--- @field line_hl_group any --- @field line_hl_group any
--- @field cursorline_hl_group any --- @field cursorline_hl_group any
--- @field conceal any --- @field conceal string
--- @field spell any --- @field spell boolean
--- @field ui_watched any --- @field ui_watched boolean
--- @class vim.api.keyset.user_command --- @class vim.api.keyset.user_command
--- @field addr any --- @field addr any
--- @field bang any --- @field bang boolean
--- @field bar any --- @field bar boolean
--- @field complete any --- @field complete any
--- @field count any --- @field count any
--- @field desc any --- @field desc any
--- @field force any --- @field force boolean
--- @field keepscript any --- @field keepscript boolean
--- @field nargs any --- @field nargs any
--- @field preview any --- @field preview any
--- @field range any --- @field range any
--- @field register_ any --- @field register boolean
--- @class vim.api.keyset.win_text_height --- @class vim.api.keyset.win_text_height
--- @field start_row any --- @field start_row integer
--- @field end_row any --- @field end_row integer
--- @field start_vcol any --- @field start_vcol integer
--- @field end_vcol any --- @field end_vcol integer

View File

@ -289,19 +289,18 @@ local function get_api_keysets_meta()
local ret = {} --- @type table<string, vim.EvalFn> 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 local keysets = metadata.keysets
for _, keyset in ipairs(keysets) do for _, k in ipairs(keysets) do
local kname = keyset[1] local params = {}
local kdef = keyset[2] for _, key in ipairs(k.keys) do
for _, field in ipairs(kdef) do table.insert(params, {key, api_type(k.types[key] or 'any')})
field[2] = api_type(field[2])
end end
ret[kname] = { ret[k.name] = {
signature = 'NA', signature = 'NA',
name = kname, name = k.name,
params = kdef, params = params,
} }
end end

View File

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

View File

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

View File

@ -35,8 +35,7 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
FUNC_API_SINCE(7) FUNC_API_SINCE(7)
FUNC_API_DEPRECATED_SINCE(11) FUNC_API_DEPRECATED_SINCE(11)
{ {
Dict(exec_opts) opts = { 0 }; Dict(exec_opts) opts = { .output = output };
opts.output = BOOLEAN_OBJ(output);
return exec_impl(channel_id, src, &opts, err); 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_SINCE(1)
FUNC_API_DEPRECATED_SINCE(7) FUNC_API_DEPRECATED_SINCE(7)
{ {
Dict(exec_opts) opts = { 0 }; Dict(exec_opts) opts = { .output = true };
opts.output = BOOLEAN_OBJ(true);
return exec_impl(channel_id, command, &opts, err); return exec_impl(channel_id, command, &opts, err);
} }

View File

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

View File

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

View File

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

View File

@ -124,10 +124,20 @@ struct key_value_pair {
Object value; 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 { typedef struct {
char *str; char *str;
size_t ptr_off; size_t ptr_off;
ObjectType type; // kObjectTypeNil == untyped
int opt_index;
} KeySetLink; } KeySetLink;
typedef KeySetLink *(*FieldHashfn)(const char *str, size_t len);
#endif // NVIM_API_PRIVATE_DEFS_H #endif // NVIM_API_PRIVATE_DEFS_H

View File

@ -16,6 +16,7 @@
#include "nvim/api/private/converter.h" #include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/buffer_defs.h" #include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
@ -915,17 +916,84 @@ free_exit:
return (HlMessage)KV_INITIAL_VALUE; 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++) { for (size_t i = 0; i < dict.size; i++) {
String k = dict.items[i].key; String k = dict.items[i].key;
Object *field = hashy(rv, k.data, k.size); KeySetLink *field = hashy(k.data, k.size);
if (!field) { if (!field) {
api_set_error(err, kErrorTypeValidation, "Invalid key: '%.*s'", (int)k.size, k.data); api_set_error(err, kErrorTypeValidation, "Invalid key: '%.*s'", (int)k.size, k.data);
return false; 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; 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) void api_free_keydict(void *dict, KeySetLink *table)
{ {
for (size_t i = 0; table[i].str; i++) { for (size_t i = 0; table[i].str; i++) {
api_free_object(*(Object *)((char *)dict + table[i].ptr_off)); char *mem = ((char *)dict + table[i].ptr_off);
if (table[i].type == kObjectTypeNil) {
api_free_object(*(Object *)mem);
} else if (table[i].type == kObjectTypeString) {
api_free_string(*(String *)mem);
} else if (table[i].type == kObjectTypeArray) {
api_free_array(*(Array *)mem);
} else if (table[i].type == kObjectTypeDictionary) {
api_free_dictionary(*(Dictionary *)mem);
} else if (table[i].type == kObjectTypeLuaRef) {
api_free_luaref(*(LuaRef *)mem);
}
} }
} }

View File

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

View File

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

View File

@ -61,7 +61,7 @@ Dictionary nvim_exec2(uint64_t channel_id, String src, Dict(exec_opts) *opts, Er
return result; 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)); 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) 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; const int save_msg_silent = msg_silent;
garray_T *const save_capture_ga = capture_ga; garray_T *const save_capture_ga = capture_ga;
const int save_msg_col = msg_col; const int save_msg_col = msg_col;
garray_T capture_local; garray_T capture_local;
if (output) { if (opts->output) {
ga_init(&capture_local, 1, 80); ga_init(&capture_local, 1, 80);
capture_ga = &capture_local; capture_ga = &capture_local;
} }
try_start(); try_start();
if (output) { if (opts->output) {
msg_silent++; msg_silent++;
msg_col = 0; // prevent leading spaces 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); const sctx_T save_current_sctx = api_set_sctx(channel_id);
do_source_str(src.data, "nvim_exec2()"); do_source_str(src.data, "nvim_exec2()");
if (output) { if (opts->output) {
capture_ga = save_capture_ga; capture_ga = save_capture_ga;
msg_silent = save_msg_silent; msg_silent = save_msg_silent;
// Put msg_col back where it was, since nothing should have been written. // 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; goto theend;
} }
if (output && capture_local.ga_len > 1) { if (opts->output && capture_local.ga_len > 1) {
String s = (String){ String s = (String){
.data = capture_local.ga_data, .data = capture_local.ga_data,
.size = (size_t)capture_local.ga_len, .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. return s; // Caller will free the memory.
} }
theend: theend:
if (output) { if (opts->output) {
ga_clear(&capture_local); ga_clear(&capture_local);
} }
return (String)STRING_INIT; return (String)STRING_INIT;

View File

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

View File

@ -7,6 +7,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "nvim/api/private/defs.h" #include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h" #include "nvim/api/private/validate.h"
#include "nvim/api/window.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; bool oob = false;
if (HAS_KEY(opts->start_row)) { if (HAS_KEY(opts, win_text_height, start_row)) {
VALIDATE_T("start_row", kObjectTypeInteger, opts->start_row.type, { start_lnum = (linenr_T)normalize_index(buf, opts->start_row, false, &oob);
return rv;
});
start_lnum = (linenr_T)normalize_index(buf, opts->start_row.data.integer, false, &oob);
} }
if (HAS_KEY(opts->end_row)) { if (HAS_KEY(opts, win_text_height, end_row)) {
VALIDATE_T("end_row", kObjectTypeInteger, opts->end_row.type, { end_lnum = (linenr_T)normalize_index(buf, opts->end_row, false, &oob);
return rv;
});
end_lnum = (linenr_T)normalize_index(buf, opts->end_row.data.integer, false, &oob);
} }
VALIDATE(!oob, "%s", "Line index out of bounds", { 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; return rv;
}); });
if (HAS_KEY(opts->start_vcol)) { if (HAS_KEY(opts, win_text_height, start_vcol)) {
VALIDATE(HAS_KEY(opts->start_row), "%s", "'start_vcol' specified without 'start_row'", { VALIDATE(HAS_KEY(opts, win_text_height, start_row),
"%s", "'start_vcol' specified without 'start_row'", {
return rv; return rv;
}); });
VALIDATE_T("start_vcol", kObjectTypeInteger, opts->start_vcol.type, { start_vcol = opts->start_vcol;
return rv;
});
start_vcol = opts->start_vcol.data.integer;
VALIDATE_RANGE((start_vcol >= 0 && start_vcol <= MAXCOL), "start_vcol", { VALIDATE_RANGE((start_vcol >= 0 && start_vcol <= MAXCOL), "start_vcol", {
return rv; return rv;
}); });
} }
if (HAS_KEY(opts->end_vcol)) { if (HAS_KEY(opts, win_text_height, end_vcol)) {
VALIDATE(HAS_KEY(opts->end_row), "%s", "'end_vcol' specified without 'end_row'", { VALIDATE(HAS_KEY(opts, win_text_height, end_row),
"%s", "'end_vcol' specified without 'end_row'", {
return rv; return rv;
}); });
VALIDATE_T("end_vcol", kObjectTypeInteger, opts->end_vcol.type, { end_vcol = opts->end_vcol;
return rv;
});
end_vcol = opts->end_vcol.data.integer;
VALIDATE_RANGE((end_vcol >= 0 && end_vcol <= MAXCOL), "end_vcol", { VALIDATE_RANGE((end_vcol >= 0 && end_vcol <= MAXCOL), "end_vcol", {
return rv; 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 fill = 0;
int64_t all = win_text_height(win, start_lnum, start_vcol, end_lnum, end_vcol, &fill); 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); const int64_t end_fill = win_get_fill(win, line_count + 1);
fill += end_fill; fill += end_fill;
all += end_fill; all += end_fill;

View File

@ -271,8 +271,7 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly)
size_t cmd_len = sizeof("func! ") + strlen(name); size_t cmd_len = sizeof("func! ") + strlen(name);
char *cmd = xmalloc(cmd_len); char *cmd = xmalloc(cmd_len);
snprintf(cmd, cmd_len, "func! %s", name); snprintf(cmd, cmd_len, "func! %s", name);
Dict(exec_opts) opts = { 0 }; Dict(exec_opts) opts = { .output = true };
opts.output = BOOLEAN_OBJ(true);
String func_body = exec_impl(VIML_INTERNAL_CALL, cstr_as_string(cmd), String func_body = exec_impl(VIML_INTERNAL_CALL, cstr_as_string(cmd),
&opts, &err); &opts, &err);
xfree(cmd); xfree(cmd);

View File

@ -67,13 +67,27 @@ local keysets = {}
local function add_keyset(val) local function add_keyset(val)
local keys = {} 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 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 end
table.insert(keys, {field.name, field.type})
end end
table.insert(keysets, {val.keyset_name, keys}) table.insert(keysets, {name=val.keyset_name, keys=keys, types=types, has_optional=has_optional})
end end
-- read each input file, parse and append to the api metadata -- read each input file, parse and append to the api metadata
@ -232,53 +246,60 @@ output:write([[
]]) ]])
for _,keyset in ipairs(keysets) do for _,k in ipairs(keysets) do
local name, keys = unpack(keyset) local c_name = {}
local special = {}
local function sanitize(key) for i = 1,#k.keys do
if special[key] then -- some keys, like "register" are c keywords and get
return key .. "_" -- 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 end
return key
end end
local key_names = {} local neworder, hashfun = hashy.hashy_hash(k.name, k.keys, function (idx)
for i = 1,#keys do return k.name.."_table["..idx.."].str"
local kname = keys[i][1]
if vim.endswith(kname, "_") then
kname = string.sub(kname,1, #kname - 1)
special[kname] = true
end
key_names[i] = kname
end
local neworder, hashfun = hashy.hashy_hash(name, key_names, function (idx)
return name.."_table["..idx.."].str"
end) end)
keysets_defs:write("extern KeySetLink "..name.."_table[];\n") keysets_defs:write("extern KeySetLink "..k.name.."_table[];\n")
output:write("KeySetLink "..name.."_table[] = {\n") local function typename(type)
for _, key in ipairs(neworder) do if type ~= nil then
output:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..sanitize(key)..")},\n") return "kObjectType"..type
else
return "kObjectTypeNil"
end
end end
output:write(' {NULL, 0},\n')
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, kObjectTypeNil, -1},\n')
output:write("};\n\n") output:write("};\n\n")
output:write(hashfun) output:write(hashfun)
output:write([[ 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) { if (hash == -1) {
return NULL; return NULL;
} }
return &]]..k.name..[[_table[hash];
return (Object *)((char *)retval + ]]..name..[[_table[hash].ptr_off);
} }
]]) ]])
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 end
local function real_type(type) local function real_type(type)
@ -558,6 +579,7 @@ local function process_function(fn)
static int %s(lua_State *lstate) static int %s(lua_State *lstate)
{ {
Error err = ERROR_INIT; Error err = ERROR_INIT;
char *err_param = 0;
if (lua_gettop(lstate) != %i) { if (lua_gettop(lstate) != %i) {
api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s"); api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s");
goto exit_0; goto exit_0;
@ -605,19 +627,22 @@ local function process_function(fn)
extra = "true, " extra = "true, "
end end
local errshift = 0 local errshift = 0
local seterr = ''
if string.match(param_type, '^KeyDict_') then if string.match(param_type, '^KeyDict_') then
write_shifted_output(output, string.format([[ write_shifted_output(output, string.format([[
%s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, %s&err);]], param_type, cparam, cparam, param_type, extra)) %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &err);]], param_type, cparam, cparam, param_type))
cparam = '&'..cparam cparam = '&'..cparam
errshift = 1 -- free incomplete dict on error errshift = 1 -- free incomplete dict on error
else else
write_shifted_output(output, string.format([[ write_shifted_output(output, string.format([[
const %s %s = nlua_pop_%s(lstate, %s&err);]], param[1], cparam, param_type, extra)) const %s %s = nlua_pop_%s(lstate, %s&err);]], param[1], cparam, param_type, extra))
seterr = [[
err_param = "]]..param[2]..[[";]]
end end
write_shifted_output(output, string.format([[ write_shifted_output(output, string.format([[
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {]]..seterr..[[
goto exit_%u; goto exit_%u;
} }
@ -661,9 +686,14 @@ local function process_function(fn)
exit_0: exit_0:
if (ERROR_SET(&err)) { if (ERROR_SET(&err)) {
luaL_where(lstate, 1); luaL_where(lstate, 1);
if (err_param) {
lua_pushstring(lstate, "Invalid '");
lua_pushstring(lstate, err_param);
lua_pushstring(lstate, "': ");
}
lua_pushstring(lstate, err.msg); lua_pushstring(lstate, err.msg);
api_clear_error(&err); api_clear_error(&err);
lua_concat(lstate, 2); lua_concat(lstate, err_param ? 5 : 2);
return lua_error(lstate); return lua_error(lstate);
} }
]] ]]

View File

@ -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, if (api_dict_to_keydict(&dict, KeyDict_highlight_get_field,
ret.data.dictionary, &err)) { ret.data.dictionary, &err)) {
attrs = dict2hlattrs(&dict, true, &it.link_id, &err); attrs = dict2hlattrs(&dict, true, &it.link_id, &err);
fallback = api_object_to_bool(dict.fallback, "fallback", true, &err); fallback = GET_BOOL_OR_TRUE(&dict, highlight, fallback);
tmp = api_object_to_bool(dict.fallback, "tmp", false, &err); tmp = dict.fallback; // or false
if (it.link_id >= 0) { if (it.link_id >= 0) {
fallback = true; 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) 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; HlAttrs hlattrs = HLATTRS_INIT;
int32_t fg = -1, bg = -1, ctermfg = -1, ctermbg = -1, sp = -1; int32_t fg = -1, bg = -1, ctermfg = -1, ctermbg = -1, sp = -1;
int blend = -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; bool cterm_mask_provided = false;
#define CHECK_FLAG(d, m, name, extra, flag) \ #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) { \ if (flag & HL_UNDERLINE_MASK) { \
m &= ~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, nocombine, , HL_NOCOMBINE);
CHECK_FLAG(dict, mask, default, _, HL_DEFAULT); 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); 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); fg = object_to_color(dict->foreground, "foreground", use_rgb, err);
} }
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
return hlattrs; return hlattrs;
} }
if (HAS_KEY(dict->bg)) { if (HAS_KEY_X(dict, bg)) {
bg = object_to_color(dict->bg, "bg", use_rgb, err); 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); bg = object_to_color(dict->background, "background", use_rgb, err);
} }
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
return hlattrs; return hlattrs;
} }
if (HAS_KEY(dict->sp)) { if (HAS_KEY_X(dict, sp)) {
sp = object_to_color(dict->sp, "sp", true, err); 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); sp = object_to_color(dict->special, "special", true, err);
} }
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
return hlattrs; return hlattrs;
} }
if (HAS_KEY(dict->blend)) { if (HAS_KEY_X(dict, blend)) {
VALIDATE_T("blend", kObjectTypeInteger, dict->blend.type, { Integer blend0 = dict->blend;
return hlattrs;
});
Integer blend0 = dict->blend.data.integer;
VALIDATE_RANGE((blend0 >= 0 && blend0 <= 100), "blend", { VALIDATE_RANGE((blend0 >= 0 && blend0 <= 100), "blend", {
return hlattrs; return hlattrs;
}); });
blend = (int)blend0; 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) { if (!link_id) {
api_set_error(err, kErrorTypeValidation, "Invalid Key: '%s'", 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; 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); *link_id = object_to_hl_id(dict->global_link, "link", err);
mask |= HL_GLOBAL; mask |= HL_GLOBAL;
} else { } 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 // empty list from Lua API should clear all cterm attributes
// TODO(clason): handle via gen_api_dispatch // TODO(clason): handle via gen_api_dispatch
cterm_mask_provided = true; 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), { VALIDATE_EXP(false, "cterm", "Dict", api_typename(dict->cterm.type), {
return hlattrs; return hlattrs;
}); });
} }
#undef CHECK_FLAG #undef CHECK_FLAG
if (HAS_KEY(dict->ctermfg)) { if (HAS_KEY_X(dict, ctermfg)) {
ctermfg = object_to_color(dict->ctermfg, "ctermfg", false, err); ctermfg = object_to_color(dict->ctermfg, "ctermfg", false, err);
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
return hlattrs; return hlattrs;
} }
} }
if (HAS_KEY(dict->ctermbg)) { if (HAS_KEY_X(dict, ctermbg)) {
ctermbg = object_to_color(dict->ctermbg, "ctermbg", false, err); ctermbg = object_to_color(dict->ctermbg, "ctermbg", false, err);
if (ERROR_SET(err)) { if (ERROR_SET(err)) {
return hlattrs; return hlattrs;
@ -1091,6 +1088,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
} }
return hlattrs; return hlattrs;
#undef HAS_KEY_X
} }
int object_to_color(Object val, char *key, bool rgb, Error *err) int object_to_color(Object val, char *key, bool rgb, Error *err)

View File

@ -832,9 +832,11 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
struct { struct {
int *dest; RgbValue val; Object name; int *dest; RgbValue val; Object name;
} cattrs[] = { } cattrs[] = {
{ &g->sg_rgb_fg_idx, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, { &g->sg_rgb_fg_idx, g->sg_rgb_fg,
{ &g->sg_rgb_bg_idx, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, HAS_KEY(dict, highlight, fg) ? dict->fg : dict->foreground },
{ &g->sg_rgb_sp_idx, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, { &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 }, { 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) 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; int id = -1;
if (opts->name.type != kObjectTypeNil) { if (HAS_KEY(opts, get_highlight, name)) {
VALIDATE_T("highlight name", kObjectTypeString, opts->name.type, { id = syn_check_group(opts->name.data, opts->name.size);
goto cleanup; } else if (HAS_KEY(opts, get_highlight, id)) {
}); id = (int)opts->id;
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 (id != -1) { if (id != -1) {

View File

@ -813,7 +813,7 @@ String nlua_pop_String(lua_State *lstate, Error *err)
{ {
if (lua_type(lstate, -1) != LUA_TSTRING) { if (lua_type(lstate, -1) != LUA_TSTRING) {
lua_pop(lstate, 1); lua_pop(lstate, 1);
api_set_error(err, kErrorTypeValidation, "Expected lua string"); api_set_error(err, kErrorTypeValidation, "Expected Lua string");
return (String) { .size = 0, .data = NULL }; return (String) { .size = 0, .data = NULL };
} }
String ret; String ret;
@ -834,7 +834,7 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err)
{ {
if (lua_type(lstate, -1) != LUA_TNUMBER) { if (lua_type(lstate, -1) != LUA_TNUMBER) {
lua_pop(lstate, 1); lua_pop(lstate, 1);
api_set_error(err, kErrorTypeValidation, "Expected lua number"); api_set_error(err, kErrorTypeValidation, "Expected Lua number");
return 0; return 0;
} }
const lua_Number n = lua_tonumber(lstate, -1); const lua_Number n = lua_tonumber(lstate, -1);
@ -871,7 +871,8 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons
{ {
if (lua_type(lstate, -1) != LUA_TTABLE) { if (lua_type(lstate, -1) != LUA_TTABLE) {
if (err) { 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 }; 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 (table_props.type != type) {
if (err) { if (err) {
api_set_error(err, kErrorTypeValidation, "Unexpected type"); api_set_error(err, kErrorTypeValidation, "Expected %s-like Lua table", api_typename(type));
} }
} }
@ -1168,7 +1169,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
break; break;
case kObjectTypeNil: case kObjectTypeNil:
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"Cannot convert given lua table"); "Cannot convert given Lua table");
break; break;
default: default:
abort(); abort();
@ -1224,26 +1225,19 @@ LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err)
return rv; return rv;
} }
#define GENERATE_INDEX_FUNCTION(type) \ handle_T nlua_pop_handle(lua_State *lstate, Error *err)
type nlua_pop_##type(lua_State *lstate, Error *err) \ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \ {
{ \ handle_T ret;
type ret; \ if (lua_type(lstate, -1) != LUA_TNUMBER) {
if (lua_type(lstate, -1) != LUA_TNUMBER) { \ api_set_error(err, kErrorTypeValidation, "Expected Lua number");
api_set_error(err, kErrorTypeValidation, "Expected Lua number"); \ ret = (handle_T) - 1;
ret = (type) - 1; \ } else {
} else { \ ret = (handle_T)lua_tonumber(lstate, -1);
ret = (type)lua_tonumber(lstate, -1); \
} \
lua_pop(lstate, 1); \
return ret; \
} }
lua_pop(lstate, 1);
GENERATE_INDEX_FUNCTION(Buffer) return ret;
GENERATE_INDEX_FUNCTION(Window) }
GENERATE_INDEX_FUNCTION(Tabpage)
#undef GENERATE_INDEX_FUNCTION
/// Record some auxiliary values in vim module /// Record some auxiliary values in vim module
/// ///
@ -1292,10 +1286,11 @@ void nlua_init_types(lua_State *const lstate)
lua_rawset(lstate, -3); 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, char **err_opt, Error *err)
{ {
if (!lua_istable(L, -1)) { if (!lua_istable(L, -1)) {
api_set_error(err, kErrorTypeValidation, "Expected lua table"); api_set_error(err, kErrorTypeValidation, "Expected Lua table");
lua_pop(L, -1); lua_pop(L, -1);
return; return;
} }
@ -1305,14 +1300,45 @@ void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err)
// [dict, key, value] // [dict, key, value]
size_t len; size_t len;
const char *s = lua_tolstring(L, -2, &len); const char *s = lua_tolstring(L, -2, &len);
Object *field = hashy(retval, s, len); KeySetLink *field = hashy(s, len);
if (!field) { if (!field) {
api_set_error(err, kErrorTypeValidation, "invalid key: %.*s", (int)len, s); api_set_error(err, kErrorTypeValidation, "invalid key: %.*s", (int)len, s);
lua_pop(L, 3); // [] lua_pop(L, 3); // []
return; 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)) {
*err_opt = field->str;
break;
}
} }
// [dict] // [dict]
lua_pop(L, 1); lua_pop(L, 1);

View File

@ -9,6 +9,10 @@
#include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_defs.h"
#include "nvim/func_attr.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 #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/converter.h.generated.h" # include "lua/converter.h.generated.h"
#endif #endif

View File

@ -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); 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; MapArguments parsed_args = MAP_ARGUMENTS_INIT;
if (opts) { if (opts) {
#define KEY_TO_BOOL(name) \ parsed_args.nowait = opts->nowait;
parsed_args.name = api_object_to_bool(opts->name, #name, false, err); \ parsed_args.noremap = opts->noremap;
if (ERROR_SET(err)) { \ parsed_args.silent = opts->silent;
goto fail_and_free; \ parsed_args.script = opts->script;
} parsed_args.expr = opts->expr;
parsed_args.unique = opts->unique;
KEY_TO_BOOL(nowait); parsed_args.replace_keycodes = opts->replace_keycodes;
KEY_TO_BOOL(noremap); if (HAS_KEY(opts, keymap, callback)) {
KEY_TO_BOOL(silent); lua_funcref = opts->callback;
KEY_TO_BOOL(script); opts->callback = LUA_NOREF;
KEY_TO_BOOL(expr); }
KEY_TO_BOOL(unique); if (HAS_KEY(opts, keymap, desc)) {
KEY_TO_BOOL(replace_keycodes); parsed_args.desc = string_to_cstr(opts->desc);
#undef KEY_TO_BOOL }
} }
parsed_args.buffer = !global; 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; 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) { 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); api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
goto fail_and_free; goto fail_and_free;

View File

@ -737,7 +737,7 @@ describe('autocmd api', function()
eq("Invalid 'group': 0", pcall_err(meths.exec_autocmds, 'FileType', { eq("Invalid 'group': 0", pcall_err(meths.exec_autocmds, 'FileType', {
group = 0, group = 0,
})) }))
eq("Invalid 'buffer': expected Integer, got Array", pcall_err(meths.exec_autocmds, 'FileType', { eq("Invalid 'buffer': expected Buffer, got Array", pcall_err(meths.exec_autocmds, 'FileType', {
buffer = {}, buffer = {},
})) }))
eq("Invalid 'event' item: expected String, got Array", pcall_err(meths.exec_autocmds, eq("Invalid 'event' item: expected String, got Array", pcall_err(meths.exec_autocmds,

View File

@ -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 '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': 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 '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 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})) 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 -- No memory leak with virt_text, virt_lines, sign_text

View File

@ -434,10 +434,8 @@ describe('API: get highlight', function()
before_each(clear) before_each(clear)
it('validation', function() it('validation', function()
eq( eq("Invalid 'name': expected String, got Integer",
'Invalid highlight name: expected String, got Integer', pcall_err(meths.get_hl, 0, { name = 177 }))
pcall_err(meths.get_hl, 0, { name = 177 })
)
eq('Highlight id out of bounds', pcall_err(meths.get_hl, 0, { name = 'Test set hl' })) eq('Highlight id out of bounds', pcall_err(meths.get_hl, 0, { name = 'Test set hl' }))
end) 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('Highlight id out of bounds', pcall_err(meths.get_hl, 0, { id = 0 }))
eq( eq(
'Invalid highlight id: expected Integer, got String', "Invalid 'id': expected Integer, got String",
pcall_err(meths.get_hl, 0, { id = 'Test_set_hl' }) pcall_err(meths.get_hl, 0, { id = 'Test_set_hl' })
) )

View File

@ -4112,12 +4112,10 @@ describe('API', function()
it('validation', function() it('validation', function()
eq("Invalid 'cmd': expected non-empty String", eq("Invalid 'cmd': expected non-empty String",
pcall_err(meths.cmd, { cmd = ""}, {})) pcall_err(meths.cmd, { cmd = ""}, {}))
eq("Invalid 'cmd': expected non-empty String", eq("Invalid 'cmd': expected String, got Array",
pcall_err(meths.cmd, { cmd = {}}, {})) pcall_err(meths.cmd, { cmd = {}}, {}))
eq("Invalid 'args': expected Array, got Boolean", eq("Invalid 'args': expected Array, got Boolean",
pcall_err(meths.cmd, { cmd = "set", args = true }, {})) pcall_err(meths.cmd, { cmd = "set", args = true }, {}))
eq("Invalid 'magic': expected Dict, got Array",
pcall_err(meths.cmd, { cmd = "set", args = {}, magic = {} }, {}))
eq("Invalid command arg: expected non-whitespace", eq("Invalid command arg: expected non-whitespace",
pcall_err(meths.cmd, { cmd = "set", args = {' '}, }, {})) pcall_err(meths.cmd, { cmd = "set", args = {' '}, }, {}))
eq("Invalid command arg: expected valid type, got Array", eq("Invalid command arg: expected valid type, got Array",
@ -4138,7 +4136,7 @@ describe('API', function()
eq("Command cannot accept count: set", eq("Command cannot accept count: set",
pcall_err(meths.cmd, { cmd = "set", args = {}, count = 1 }, {})) pcall_err(meths.cmd, { cmd = "set", args = {}, count = 1 }, {}))
eq("Invalid 'count': expected non-negative Integer", eq("Invalid 'count': expected Integer, got Boolean",
pcall_err(meths.cmd, { cmd = "print", args = {}, count = true }, {})) pcall_err(meths.cmd, { cmd = "print", args = {}, count = true }, {}))
eq("Invalid 'count': expected non-negative Integer", eq("Invalid 'count': expected non-negative Integer",
pcall_err(meths.cmd, { cmd = "print", args = {}, count = -1 }, {})) pcall_err(meths.cmd, { cmd = "print", args = {}, count = -1 }, {}))
@ -4158,9 +4156,10 @@ describe('API', function()
-- Lua call allows empty {} for dict item. -- Lua call allows empty {} for dict item.
eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, magic = {} }]])) eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, magic = {} }]]))
eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, mods = {} }]])) eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, mods = {} }]]))
eq('', meths.cmd({ cmd = "set", args = {}, magic = {} }, {}))
-- Lua call does not allow non-empty list-like {} for dict item. -- Lua call does not allow non-empty list-like {} for dict item.
eq("Invalid 'magic': expected Dict, got Array", eq("Invalid 'magic': Expected Dict-like Lua table",
pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { 'a' } }]])) pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { 'a' } }]]))
eq("Invalid key: 'bogus'", eq("Invalid key: 'bogus'",
pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { bogus = true } }]])) pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { bogus = true } }]]))

View File

@ -173,7 +173,7 @@ describe('luaeval(vim.api.…)', function()
it('errors out correctly when working with API', function() it('errors out correctly when working with API', function()
-- Conversion errors -- Conversion errors
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'obj': Cannot convert given Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])))
-- Errors in number of arguments -- Errors in number of arguments
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument', eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
@ -183,32 +183,32 @@ describe('luaeval(vim.api.…)', function()
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 2 arguments', eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 2 arguments',
remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2, 3)")]])))
-- Error in argument types -- Error in argument types
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua string', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'name': Expected Lua string]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2)")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_var(1, 2)")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua number', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'start': Expected Lua number]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 'test', 1, false)")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Number is not integral', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'start': Number is not integral]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim_buf_get_lines(0, 1.5, 1, false)")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected Lua number', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'window': Expected Lua number]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_win_is_valid(nil)")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim_win_is_valid(nil)")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'flt': Expected Lua number]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float('test')")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'flt': Expected Float-like Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float({[vim.type_idx]=vim.types.dictionary})")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_float({[vim.type_idx]=vim.types.dictionary})")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'arr': Expected Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array(1)")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array(1)")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'arr': Expected Array-like Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array({[vim.type_idx]=vim.types.dictionary})")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_array({[vim.type_idx]=vim.types.dictionary})")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'dct': Expected Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary(1)")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Unexpected type', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Invalid 'dct': Expected Dict-like Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim__id_dictionary({[vim.type_idx]=vim.types.array})")]])))
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected lua table', eq([[Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected Lua table]],
remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_keymap('', '', '', '')")]]))) remove_trace(exc_exec([[call luaeval("vim.api.nvim_set_keymap('', '', '', '')")]])))
-- TODO: check for errors with Tabpage argument -- TODO: check for errors with Tabpage argument

View File

@ -1511,7 +1511,7 @@ describe('lua stdlib', function()
eq(true, funcs.luaeval "vim.bo[BUF].modifiable") eq(true, funcs.luaeval "vim.bo[BUF].modifiable")
matches("Unknown option 'nosuchopt'$", matches("Unknown option 'nosuchopt'$",
pcall_err(exec_lua, 'return vim.bo.nosuchopt')) pcall_err(exec_lua, 'return vim.bo.nosuchopt'))
matches("Expected lua string$", matches("Expected Lua string$",
pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) pcall_err(exec_lua, 'return vim.bo[0][0].autoread'))
matches("Invalid buffer id: %-1$", matches("Invalid buffer id: %-1$",
pcall_err(exec_lua, 'return vim.bo[-1].filetype')) pcall_err(exec_lua, 'return vim.bo[-1].filetype'))