mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
fix(api): allow empty Lua table for nested dicts #22268
Problem:
The Lua-API bridge allows Dict params to be empty Lua (list) tables at
the function-signature level. But not for _nested_ Dicts, because they
are not modeled:
fae7540732/src/nvim/api/keysets.lua (L184)
Some API functions like nvim_cmd check for kObjectTypeDictionary and
don't handle the case of empty Lua tables (treated as "Array").
Solution:
Introduce VALIDATE_T_DICT and use it in places where
kObjectTypeDictionary was being checked directly.
fixes #21005
This commit is contained in:
parent
bcae4af374
commit
09b3432eaf
@ -350,20 +350,24 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VALIDATION_ERROR(...) \
|
||||
#define VALIDATE_MOD(cond, mod_, name_) \
|
||||
do { \
|
||||
api_set_error(err, kErrorTypeValidation, __VA_ARGS__); \
|
||||
goto end; \
|
||||
if (!(cond)) { \
|
||||
api_set_error(err, kErrorTypeValidation, "Command cannot accept %s: %s", (mod_), (name_)); \
|
||||
goto end; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
bool output;
|
||||
OBJ_TO_BOOL(output, opts->output, false, "'output'");
|
||||
|
||||
// First, parse the command name and check if it exists and is valid.
|
||||
if (!HAS_KEY(cmd->cmd) || cmd->cmd.type != kObjectTypeString
|
||||
|| cmd->cmd.data.string.data[0] == NUL) {
|
||||
VALIDATION_ERROR("'cmd' must be a non-empty String");
|
||||
}
|
||||
VALIDATE_R(HAS_KEY(cmd->cmd), "cmd", {
|
||||
goto end;
|
||||
});
|
||||
VALIDATE_EXP((cmd->cmd.type == kObjectTypeString && cmd->cmd.data.string.data[0] != NUL),
|
||||
"cmd", "non-empty String", NULL, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
cmdname = string_to_cstr(cmd->cmd.data.string);
|
||||
ea.cmd = cmdname;
|
||||
@ -382,12 +386,12 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
|
||||
}
|
||||
|
||||
if (p == NULL || ea.cmdidx == CMD_SIZE) {
|
||||
VALIDATION_ERROR("Command not found: %s", cmdname);
|
||||
}
|
||||
if (is_cmd_ni(ea.cmdidx)) {
|
||||
VALIDATION_ERROR("Command not implemented: %s", cmdname);
|
||||
}
|
||||
VALIDATE((p != NULL && ea.cmdidx != CMD_SIZE), "Command not found: %s", cmdname, {
|
||||
goto end;
|
||||
});
|
||||
VALIDATE(!is_cmd_ni(ea.cmdidx), "Command not implemented: %s", cmdname, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
// Get the command flags so that we can know what type of arguments the command uses.
|
||||
// Not required for a user command since `find_ex_command` already deals with it in that case.
|
||||
@ -397,9 +401,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
|
||||
// Parse command arguments since it's needed to get the command address type.
|
||||
if (HAS_KEY(cmd->args)) {
|
||||
if (cmd->args.type != kObjectTypeArray) {
|
||||
VALIDATION_ERROR("'args' must be an Array");
|
||||
}
|
||||
VALIDATE_T("args", kObjectTypeArray, cmd->args.type, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
// Process all arguments. Convert non-String arguments to String and check if String arguments
|
||||
// have non-whitespace characters.
|
||||
@ -421,14 +425,15 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
snprintf(data_str, NUMBUFLEN, "%" PRId64, elem.data.integer);
|
||||
break;
|
||||
case kObjectTypeString:
|
||||
if (string_iswhite(elem.data.string)) {
|
||||
VALIDATION_ERROR("String command argument must have at least one non-whitespace "
|
||||
"character");
|
||||
}
|
||||
VALIDATE_EXP(!string_iswhite(elem.data.string), "command arg", "non-whitespace", NULL, {
|
||||
goto end;
|
||||
});
|
||||
data_str = xstrndup(elem.data.string.data, elem.data.string.size);
|
||||
break;
|
||||
default:
|
||||
VALIDATION_ERROR("Invalid type for command argument");
|
||||
VALIDATE_EXP(false, "command arg", "valid type", api_typename(elem.type), {
|
||||
goto end;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@ -456,9 +461,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
break;
|
||||
}
|
||||
|
||||
if (!argc_valid) {
|
||||
VALIDATION_ERROR("Incorrect number of arguments supplied");
|
||||
}
|
||||
VALIDATE(argc_valid, "%s", "Wrong number of arguments", {
|
||||
goto end;
|
||||
});
|
||||
}
|
||||
|
||||
// Simply pass the first argument (if it exists) as the arg pointer to `set_cmd_addr_type()`
|
||||
@ -466,22 +471,23 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
set_cmd_addr_type(&ea, args.size > 0 ? args.items[0].data.string.data : NULL);
|
||||
|
||||
if (HAS_KEY(cmd->range)) {
|
||||
if (!(ea.argt & EX_RANGE)) {
|
||||
VALIDATION_ERROR("Command cannot accept a range");
|
||||
} else if (cmd->range.type != kObjectTypeArray) {
|
||||
VALIDATION_ERROR("'range' must be an Array");
|
||||
} else if (cmd->range.data.array.size > 2) {
|
||||
VALIDATION_ERROR("'range' cannot contain more than two elements");
|
||||
}
|
||||
VALIDATE_MOD((ea.argt & EX_RANGE), "range", cmd->cmd.data.string.data);
|
||||
VALIDATE_T("range", kObjectTypeArray, cmd->range.type, {
|
||||
goto end;
|
||||
});
|
||||
VALIDATE_EXP((cmd->range.data.array.size <= 2), "range", "<=2 elements", NULL, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
Array range = cmd->range.data.array;
|
||||
ea.addr_count = (int)range.size;
|
||||
|
||||
for (size_t i = 0; i < range.size; i++) {
|
||||
Object elem = range.items[i];
|
||||
if (elem.type != kObjectTypeInteger || elem.data.integer < 0) {
|
||||
VALIDATION_ERROR("'range' element must be a non-negative Integer");
|
||||
}
|
||||
VALIDATE_EXP((elem.type == kObjectTypeInteger && elem.data.integer >= 0),
|
||||
"range element", "non-negative Integer", NULL, {
|
||||
goto end;
|
||||
});
|
||||
}
|
||||
|
||||
if (range.size > 0) {
|
||||
@ -489,9 +495,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
ea.line2 = (linenr_T)range.items[range.size - 1].data.integer;
|
||||
}
|
||||
|
||||
if (invalid_range(&ea) != NULL) {
|
||||
VALIDATION_ERROR("Invalid range provided");
|
||||
}
|
||||
VALIDATE_S((invalid_range(&ea) == NULL), "range", "", {
|
||||
goto end;
|
||||
});
|
||||
}
|
||||
if (ea.addr_count == 0) {
|
||||
if (ea.argt & EX_DFLALL) {
|
||||
@ -507,38 +513,38 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
}
|
||||
|
||||
if (HAS_KEY(cmd->count)) {
|
||||
if (!(ea.argt & EX_COUNT)) {
|
||||
VALIDATION_ERROR("Command cannot accept a count");
|
||||
} else if (cmd->count.type != kObjectTypeInteger || cmd->count.data.integer < 0) {
|
||||
VALIDATION_ERROR("'count' must be a non-negative Integer");
|
||||
}
|
||||
VALIDATE_MOD((ea.argt & EX_COUNT), "count", cmd->cmd.data.string.data);
|
||||
VALIDATE_EXP((cmd->count.type == kObjectTypeInteger && cmd->count.data.integer >= 0),
|
||||
"count", "non-negative Integer", NULL, {
|
||||
goto end;
|
||||
});
|
||||
set_cmd_count(&ea, (linenr_T)cmd->count.data.integer, true);
|
||||
}
|
||||
|
||||
if (HAS_KEY(cmd->reg)) {
|
||||
if (!(ea.argt & EX_REGSTR)) {
|
||||
VALIDATION_ERROR("Command cannot accept a register");
|
||||
} else if (cmd->reg.type != kObjectTypeString || cmd->reg.data.string.size != 1) {
|
||||
VALIDATION_ERROR("'reg' must be a single character");
|
||||
}
|
||||
VALIDATE_MOD((ea.argt & EX_REGSTR), "register", cmd->cmd.data.string.data);
|
||||
VALIDATE_EXP((cmd->reg.type == kObjectTypeString && cmd->reg.data.string.size == 1),
|
||||
"reg", "single character", cmd->reg.data.string.data, {
|
||||
goto end;
|
||||
});
|
||||
char regname = cmd->reg.data.string.data[0];
|
||||
if (regname == '=') {
|
||||
VALIDATION_ERROR("Cannot use register \"=");
|
||||
} else if (!valid_yank_reg(regname, ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx))) {
|
||||
VALIDATION_ERROR("Invalid register: \"%c", regname);
|
||||
}
|
||||
VALIDATE((regname != '='), "%s", "Cannot use register \"=", {
|
||||
goto end;
|
||||
});
|
||||
VALIDATE(valid_yank_reg(regname, ea.cmdidx != CMD_put && !IS_USER_CMDIDX(ea.cmdidx)),
|
||||
"Invalid register: \"%c", regname, {
|
||||
goto end;
|
||||
});
|
||||
ea.regname = (uint8_t)regname;
|
||||
}
|
||||
|
||||
OBJ_TO_BOOL(ea.forceit, cmd->bang, false, "'bang'");
|
||||
if (ea.forceit && !(ea.argt & EX_BANG)) {
|
||||
VALIDATION_ERROR("Command cannot accept a bang");
|
||||
}
|
||||
VALIDATE_MOD((!ea.forceit || (ea.argt & EX_BANG)), "bang", cmd->cmd.data.string.data);
|
||||
|
||||
if (HAS_KEY(cmd->magic)) {
|
||||
if (cmd->magic.type != kObjectTypeDictionary) {
|
||||
VALIDATION_ERROR("'magic' must be a Dictionary");
|
||||
}
|
||||
VALIDATE_T_DICT("magic", cmd->magic, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
Dict(cmd_magic) magic = { 0 };
|
||||
if (!api_dict_to_keydict(&magic, KeyDict_cmd_magic_get_field,
|
||||
@ -559,9 +565,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
}
|
||||
|
||||
if (HAS_KEY(cmd->mods)) {
|
||||
if (cmd->mods.type != kObjectTypeDictionary) {
|
||||
VALIDATION_ERROR("'mods' must be a Dictionary");
|
||||
}
|
||||
VALIDATE_T_DICT("mods", cmd->mods, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
Dict(cmd_mods) mods = { 0 };
|
||||
if (!api_dict_to_keydict(&mods, KeyDict_cmd_mods_get_field, cmd->mods.data.dictionary, err)) {
|
||||
@ -569,9 +575,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
}
|
||||
|
||||
if (HAS_KEY(mods.filter)) {
|
||||
if (mods.filter.type != kObjectTypeDictionary) {
|
||||
VALIDATION_ERROR("'mods.filter' must be a Dictionary");
|
||||
}
|
||||
VALIDATE_T_DICT("mods.filter", mods.filter, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
Dict(cmd_mods_filter) filter = { 0 };
|
||||
|
||||
@ -581,9 +587,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
}
|
||||
|
||||
if (HAS_KEY(filter.pattern)) {
|
||||
if (filter.pattern.type != kObjectTypeString) {
|
||||
VALIDATION_ERROR("'mods.filter.pattern' must be a String");
|
||||
}
|
||||
VALIDATE_T2(filter.pattern, kObjectTypeString, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
OBJ_TO_BOOL(cmdinfo.cmdmod.cmod_filter_force, filter.force, false, "'mods.filter.force'");
|
||||
|
||||
@ -598,18 +604,20 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
}
|
||||
|
||||
if (HAS_KEY(mods.tab)) {
|
||||
if (mods.tab.type != kObjectTypeInteger) {
|
||||
VALIDATION_ERROR("'mods.tab' must be an Integer");
|
||||
} else if ((int)mods.tab.data.integer >= 0) {
|
||||
VALIDATE_T2(mods.tab, kObjectTypeInteger, {
|
||||
goto end;
|
||||
});
|
||||
if ((int)mods.tab.data.integer >= 0) {
|
||||
// Silently ignore negative integers to allow mods.tab to be set to -1.
|
||||
cmdinfo.cmdmod.cmod_tab = (int)mods.tab.data.integer + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (HAS_KEY(mods.verbose)) {
|
||||
if (mods.verbose.type != kObjectTypeInteger) {
|
||||
VALIDATION_ERROR("'mods.verbose' must be an Integer");
|
||||
} else if ((int)mods.verbose.data.integer >= 0) {
|
||||
VALIDATE_T2(mods.verbose, kObjectTypeInteger, {
|
||||
goto end;
|
||||
});
|
||||
if ((int)mods.verbose.data.integer >= 0) {
|
||||
// Silently ignore negative integers to allow mods.verbose to be set to -1.
|
||||
cmdinfo.cmdmod.cmod_verbose = (int)mods.verbose.data.integer + 1;
|
||||
}
|
||||
@ -624,9 +632,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
cmdinfo.cmdmod.cmod_split |= (horizontal ? WSP_HOR : 0);
|
||||
|
||||
if (HAS_KEY(mods.split)) {
|
||||
if (mods.split.type != kObjectTypeString) {
|
||||
VALIDATION_ERROR("'mods.split' must be a String");
|
||||
}
|
||||
VALIDATE_T2(mods.split, kObjectTypeString, {
|
||||
goto end;
|
||||
});
|
||||
|
||||
if (*mods.split.data.string.data == NUL) {
|
||||
// Empty string, do nothing.
|
||||
@ -641,7 +649,9 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
} else if (strcmp(mods.split.data.string.data, "botright") == 0) {
|
||||
cmdinfo.cmdmod.cmod_split |= WSP_BOT;
|
||||
} else {
|
||||
VALIDATION_ERROR("Invalid value for 'mods.split'");
|
||||
VALIDATE_S(false, "mods.split", "", {
|
||||
goto end;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,9 +676,10 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
|
||||
cmdinfo.cmdmod.cmod_flags |= CMOD_SILENT;
|
||||
}
|
||||
|
||||
if ((cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX) && !(ea.argt & EX_SBOXOK)) {
|
||||
VALIDATION_ERROR("Command cannot be run in sandbox");
|
||||
}
|
||||
VALIDATE(!((cmdinfo.cmdmod.cmod_flags & CMOD_SANDBOX) && !(ea.argt & EX_SBOXOK)),
|
||||
"%s", "Command cannot be run in sandbox", {
|
||||
goto end;
|
||||
});
|
||||
}
|
||||
|
||||
// Finally, build the command line string that will be stored inside ea.cmdlinep.
|
||||
@ -739,7 +750,7 @@ end:
|
||||
|
||||
#undef OBJ_TO_BOOL
|
||||
#undef OBJ_TO_CMOD_FLAG
|
||||
#undef VALIDATION_ERROR
|
||||
#undef VALIDATE_MOD
|
||||
}
|
||||
|
||||
/// Check if a string contains only whitespace characters.
|
||||
|
@ -38,12 +38,35 @@
|
||||
|
||||
#define VALIDATE_T(name, expected_t, actual_t, code) \
|
||||
do { \
|
||||
STATIC_ASSERT(expected_t != kObjectTypeDictionary, "use VALIDATE_T_DICT"); \
|
||||
if (expected_t != actual_t) { \
|
||||
api_err_exp(err, name, api_typename(expected_t), api_typename(actual_t)); \
|
||||
code; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/// Checks that `obj_` has type `expected_t`.
|
||||
#define VALIDATE_T2(obj_, expected_t, code) \
|
||||
do { \
|
||||
STATIC_ASSERT(expected_t != kObjectTypeDictionary, "use VALIDATE_T_DICT"); \
|
||||
if ((obj_).type != expected_t) { \
|
||||
api_err_exp(err, STR(obj_), api_typename(expected_t), api_typename((obj_).type)); \
|
||||
code; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/// Checks that `obj_` has Dict type. Also allows empty Array in a Lua context.
|
||||
#define VALIDATE_T_DICT(name, obj_, code) \
|
||||
do { \
|
||||
if ((obj_).type != kObjectTypeDictionary \
|
||||
&& !(channel_id == LUA_INTERNAL_CALL \
|
||||
&& (obj_).type == kObjectTypeArray \
|
||||
&& (obj_).data.array.size == 0)) { \
|
||||
api_err_exp(err, name, api_typename(kObjectTypeDictionary), api_typename((obj_).type)); \
|
||||
code; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VALIDATE_RANGE(cond, name, code) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
|
@ -1025,7 +1025,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
|
||||
// TODO(clason): handle via gen_api_dispatch
|
||||
cterm_mask_provided = true;
|
||||
} else if (HAS_KEY(dict->cterm)) {
|
||||
VALIDATE_T("cterm", kObjectTypeDictionary, dict->cterm.type, {
|
||||
VALIDATE_EXP(false, "cterm", "Dict", api_typename(dict->cterm.type), {
|
||||
return hlattrs;
|
||||
});
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ describe('nvim_get_commands', function()
|
||||
eq({}, meths.get_commands({builtin=false}))
|
||||
end)
|
||||
|
||||
it('validates input', function()
|
||||
it('validation', function()
|
||||
eq('builtin=true not implemented', pcall_err(meths.get_commands,
|
||||
{builtin=true}))
|
||||
eq("Invalid key: 'foo'", pcall_err(meths.get_commands,
|
||||
|
@ -3826,14 +3826,71 @@ describe('API', function()
|
||||
meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
|
||||
eq(true, meths.get_option_value("cursorline", {}))
|
||||
end)
|
||||
|
||||
it('validation', function()
|
||||
eq("Invalid 'cmd': expected non-empty String",
|
||||
pcall_err(meths.cmd, { cmd = ""}, {}))
|
||||
eq("Invalid 'cmd': expected non-empty String",
|
||||
pcall_err(meths.cmd, { cmd = {}}, {}))
|
||||
eq("Invalid 'args': expected Array, got Boolean",
|
||||
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",
|
||||
pcall_err(meths.cmd, { cmd = "set", args = {' '}, }, {}))
|
||||
eq("Invalid command arg: expected valid type, got Array",
|
||||
pcall_err(meths.cmd, { cmd = "set", args = {{}}, }, {}))
|
||||
eq("Wrong number of arguments",
|
||||
pcall_err(meths.cmd, { cmd = "aboveleft", args = {}, }, {}))
|
||||
eq("Command cannot accept bang: print",
|
||||
pcall_err(meths.cmd, { cmd = "print", args = {}, bang = true }, {}))
|
||||
|
||||
eq("Command cannot accept range: set",
|
||||
pcall_err(meths.cmd, { cmd = "set", args = {}, range = {1} }, {}))
|
||||
eq("Invalid 'range': expected Array, got Boolean",
|
||||
pcall_err(meths.cmd, { cmd = "print", args = {}, range = true }, {}))
|
||||
eq("Invalid 'range': expected <=2 elements",
|
||||
pcall_err(meths.cmd, { cmd = "print", args = {}, range = {1,2,3,4} }, {}))
|
||||
eq("Invalid range element: expected non-negative Integer",
|
||||
pcall_err(meths.cmd, { cmd = "print", args = {}, range = {-1} }, {}))
|
||||
|
||||
eq("Command cannot accept count: set",
|
||||
pcall_err(meths.cmd, { cmd = "set", args = {}, count = 1 }, {}))
|
||||
eq("Invalid 'count': expected non-negative Integer",
|
||||
pcall_err(meths.cmd, { cmd = "print", args = {}, count = true }, {}))
|
||||
eq("Invalid 'count': expected non-negative Integer",
|
||||
pcall_err(meths.cmd, { cmd = "print", args = {}, count = -1 }, {}))
|
||||
|
||||
eq("Command cannot accept register: set",
|
||||
pcall_err(meths.cmd, { cmd = "set", args = {}, reg = 'x' }, {}))
|
||||
eq('Cannot use register "=',
|
||||
pcall_err(meths.cmd, { cmd = "put", args = {}, reg = '=' }, {}))
|
||||
eq("Invalid 'reg': expected single character, got xx",
|
||||
pcall_err(meths.cmd, { cmd = "put", args = {}, reg = 'xx' }, {}))
|
||||
|
||||
-- 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 = {}, mods = {} }]]))
|
||||
|
||||
-- Lua call does not allow non-empty list-like {} for dict item.
|
||||
eq("Invalid 'magic': expected Dict, got Array",
|
||||
pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { 'a' } }]]))
|
||||
eq("Invalid key: 'bogus'",
|
||||
pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, magic = { bogus = true } }]]))
|
||||
eq("Invalid key: 'bogus'",
|
||||
pcall_err(exec_lua, [[return vim.cmd{ cmd = "set", args = {}, mods = { bogus = true } }]]))
|
||||
end)
|
||||
|
||||
it('captures output', function()
|
||||
eq("foo", meths.cmd({ cmd = "echo", args = { '"foo"' } }, { output = true }))
|
||||
end)
|
||||
|
||||
it('sets correct script context', function()
|
||||
meths.cmd({ cmd = "set", args = { "cursorline" } }, {})
|
||||
local str = meths.exec([[verbose set cursorline?]], true)
|
||||
neq(nil, str:find("cursorline\n\tLast set from API client %(channel id %d+%)"))
|
||||
end)
|
||||
|
||||
it('works with range', function()
|
||||
insert [[
|
||||
line1
|
||||
|
Loading…
Reference in New Issue
Block a user