feat(vim.validate): improve fast form and deprecate spec form

Problem:

`vim.validate()` takes two forms when it only needs one.

Solution:

- Teach the fast form all the features of the spec form.
- Deprecate the spec form.
- General optimizations for both forms.
- Add a `message` argument which can be used alongside or in place
  of the `optional` argument.
This commit is contained in:
Lewis Russell 2024-10-18 11:33:12 +01:00 committed by Lewis Russell
parent 6fd13eedda
commit 3572319b4c
20 changed files with 355 additions and 374 deletions

View File

@ -22,6 +22,7 @@ API
LUA LUA
- vim.region() Use |getregionpos()| instead. - vim.region() Use |getregionpos()| instead.
- *vim.highlight* Renamed to |vim.hl|. - *vim.highlight* Renamed to |vim.hl|.
- vim.validate(opts: table) Use form 1. See |vim.validate()|.
DIAGNOSTICS DIAGNOSTICS
- *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead. - *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead.

View File

@ -2398,31 +2398,29 @@ vim.trim({s}) *vim.trim()*
• |lua-patterns| • |lua-patterns|
• https://www.lua.org/pil/20.2.html • https://www.lua.org/pil/20.2.html
vim.validate({opt}) *vim.validate()* *vim.validate()*
vim.validate({name}, {value}, {validator}, {optional}, {message})
Validate function arguments. Validate function arguments.
This function has two valid forms: This function has two valid forms:
1. vim.validate(name: str, value: any, type: string, optional?: bool) 1. `vim.validate(name, value, validator[, optional][, message])`
2. vim.validate(spec: table) Validates that argument {name} with value {value} satisfies
{validator}. If {optional} is given and is `true`, then {value} may be
Form 1 validates that argument {name} with value {value} has the type `nil`. If {message} is given, then it is used as the expected type in
{type}. {type} must be a value returned by |lua-type()|. If {optional} is the error message.
true, then {value} may be null. This form is significantly faster and Example: >lua
should be preferred for simple cases. function vim.startswith(s, prefix)
Example: >lua
function vim.startswith(s, prefix)
vim.validate('s', s, 'string') vim.validate('s', s, 'string')
vim.validate('prefix', prefix, 'string') vim.validate('prefix', prefix, 'string')
... ...
end end
< <
2. `vim.validate(spec)` (deprecated) where `spec` is of type
Form 2 validates a parameter specification (types and values). Specs are `table<string,[value:any, validator: vim.validate.Validator, optional_or_msg? : boolean|string]>)`
evaluated in alphanumeric order, until the first failure. Validates a argument specification. Specs are evaluated in alphanumeric
order, until the first failure.
Usage example: >lua Example: >lua
function user.new(name, age, hobbies) function user.new(name, age, hobbies)
vim.validate{ vim.validate{
name={name, 'string'}, name={name, 'string'},
age={age, 'number'}, age={age, 'number'},
@ -2433,40 +2431,44 @@ vim.validate({opt}) *vim.validate()*
< <
Examples with explicit argument values (can be run directly): >lua Examples with explicit argument values (can be run directly): >lua
vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} vim.validate('arg1', {'foo'}, 'table')
--> NOP (success)
vim.validate('arg2', 'foo', 'string')
--> NOP (success) --> NOP (success)
vim.validate{arg1={1, 'table'}} vim.validate('arg1', 1, 'table')
--> error('arg1: expected table, got number') --> error('arg1: expected table, got number')
vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} vim.validate('arg1', 3, function(a) return (a % 2) == 0 end, 'even number')
--> error('arg1: expected even number, got 3') --> error('arg1: expected even number, got 3')
< <
If multiple types are valid they can be given as a list. >lua If multiple types are valid they can be given as a list. >lua
vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}} vim.validate('arg1', {'foo'}, {'table', 'string'})
vim.validate('arg2', 'foo', {'table', 'string'})
-- NOP (success) -- NOP (success)
vim.validate{arg1={1, {'string', 'table'}}} vim.validate('arg1', 1, {'string', 'table'})
-- error('arg1: expected string|table, got number') -- error('arg1: expected string|table, got number')
< <
Note: ~
• `validator` set to a value returned by |lua-type()| provides the best
performance.
Parameters: ~ Parameters: ~
• {opt} (`table`) Names of parameters to validate. Each key is a • {name} (`string`) Argument name
parameter name; each value is a tuple in one of these forms: • {value} (`string`) Argument value
1. (arg_value, type_name, optional) • {validator} (`vim.validate.Validator`)
• arg_value: argument value • (`string|string[]`): Any value that can be returned
• type_name: string|table type name, one of: ("table", "t", from |lua-type()| in addition to `'callable'`:
"string", "s", "number", "n", "boolean", "b", "function", `'boolean'`, `'callable'`, `'function'`, `'nil'`,
"f", "nil", "thread", "userdata") or list of them. `'number'`, `'string'`, `'table'`, `'thread'`,
• optional: (optional) boolean, if true, `nil` is valid `'userdata'`.
2. (arg_value, fn, msg) • (`fun(val:any): boolean, string?`) A function that
• arg_value: argument value returns a boolean and an optional string message.
• fn: any function accepting one argument, returns true if • {optional} (`boolean?`) Argument is optional (may be omitted)
and only if the argument is valid. Can optionally return • {message} (`string?`) message when validation fails
an additional informative error message as the second
returned value.
• msg: (optional) error string if validation fails
============================================================================== ==============================================================================

View File

@ -200,6 +200,8 @@ LSP
LUA LUA
• |vim.fs.rm()| can delete files and directories. • |vim.fs.rm()| can delete files and directories.
• |vim.validate()| now has a new signature which uses less tables,
is more peformant and easier to read.
OPTIONS OPTIONS

View File

@ -467,13 +467,11 @@ vim.cmd = setmetatable({}, {
-- These are the vim.env/v/g/o/bo/wo variable magic accessors. -- These are the vim.env/v/g/o/bo/wo variable magic accessors.
do do
local validate = vim.validate
--- @param scope string --- @param scope string
--- @param handle? false|integer --- @param handle? false|integer
--- @return vim.var_accessor --- @return vim.var_accessor
local function make_dict_accessor(scope, handle) local function make_dict_accessor(scope, handle)
validate('scope', scope, 'string') vim.validate('scope', scope, 'string')
local mt = {} local mt = {}
function mt:__newindex(k, v) function mt:__newindex(k, v)
return vim._setvar(scope, handle or 0, k, v) return vim._setvar(scope, handle or 0, k, v)
@ -589,7 +587,7 @@ end
---@param timeout integer Number of milliseconds to wait before calling `fn` ---@param timeout integer Number of milliseconds to wait before calling `fn`
---@return table timer luv timer object ---@return table timer luv timer object
function vim.defer_fn(fn, timeout) function vim.defer_fn(fn, timeout)
vim.validate({ fn = { fn, 'c', true } }) vim.validate('fn', fn, 'callable', true)
local timer = assert(vim.uv.new_timer()) local timer = assert(vim.uv.new_timer())
timer:start( timer:start(
timeout, timeout,
@ -680,10 +678,8 @@ function vim.on_key(fn, ns_id)
return vim.tbl_count(on_key_cbs) return vim.tbl_count(on_key_cbs)
end end
vim.validate({ vim.validate('fn', fn, 'callable', true)
fn = { fn, 'c', true }, vim.validate('ns_id', ns_id, 'number', true)
ns_id = { ns_id, 'n', true },
})
if ns_id == nil or ns_id == 0 then if ns_id == nil or ns_id == 0 then
ns_id = vim.api.nvim_create_namespace('') ns_id = vim.api.nvim_create_namespace('')

View File

@ -59,9 +59,9 @@ end
--- @param callback vim._watch.Callback Callback for new events --- @param callback vim._watch.Callback Callback for new events
--- @return fun() cancel Stops the watcher --- @return fun() cancel Stops the watcher
function M.watch(path, opts, callback) function M.watch(path, opts, callback)
vim.validate('path', path, 'string', false) vim.validate('path', path, 'string')
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
vim.validate('callback', callback, 'function', false) vim.validate('callback', callback, 'function')
opts = opts or {} opts = opts or {}
@ -125,9 +125,9 @@ end
--- @param callback vim._watch.Callback Callback for new events --- @param callback vim._watch.Callback Callback for new events
--- @return fun() cancel Stops the watcher --- @return fun() cancel Stops the watcher
function M.watchdirs(path, opts, callback) function M.watchdirs(path, opts, callback)
vim.validate('path', path, 'string', false) vim.validate('path', path, 'string')
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
vim.validate('callback', callback, 'function', false) vim.validate('callback', callback, 'function')
opts = opts or {} opts = opts or {}
local debounce = opts.debounce or 500 local debounce = opts.debounce or 500

View File

@ -478,7 +478,7 @@ end
--- @return vim.Diagnostic[] --- @return vim.Diagnostic[]
local function reformat_diagnostics(format, diagnostics) local function reformat_diagnostics(format, diagnostics)
vim.validate('format', format, 'function') vim.validate('format', format, 'function')
vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
local formatted = vim.deepcopy(diagnostics, true) local formatted = vim.deepcopy(diagnostics, true)
for _, diagnostic in ipairs(formatted) do for _, diagnostic in ipairs(formatted) do
@ -1056,7 +1056,7 @@ end
function M.set(namespace, bufnr, diagnostics, opts) function M.set(namespace, bufnr, diagnostics, opts)
vim.validate('namespace', namespace, 'number') vim.validate('namespace', namespace, 'number')
vim.validate('bufnr', bufnr, 'number') vim.validate('bufnr', bufnr, 'number')
vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
bufnr = get_bufnr(bufnr) bufnr = get_bufnr(bufnr)
@ -1336,7 +1336,7 @@ M.handlers.signs = {
show = function(namespace, bufnr, diagnostics, opts) show = function(namespace, bufnr, diagnostics, opts)
vim.validate('namespace', namespace, 'number') vim.validate('namespace', namespace, 'number')
vim.validate('bufnr', bufnr, 'number') vim.validate('bufnr', bufnr, 'number')
vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
bufnr = get_bufnr(bufnr) bufnr = get_bufnr(bufnr)
@ -1457,7 +1457,7 @@ M.handlers.underline = {
show = function(namespace, bufnr, diagnostics, opts) show = function(namespace, bufnr, diagnostics, opts)
vim.validate('namespace', namespace, 'number') vim.validate('namespace', namespace, 'number')
vim.validate('bufnr', bufnr, 'number') vim.validate('bufnr', bufnr, 'number')
vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
bufnr = get_bufnr(bufnr) bufnr = get_bufnr(bufnr)
@ -1524,7 +1524,7 @@ M.handlers.virtual_text = {
show = function(namespace, bufnr, diagnostics, opts) show = function(namespace, bufnr, diagnostics, opts)
vim.validate('namespace', namespace, 'number') vim.validate('namespace', namespace, 'number')
vim.validate('bufnr', bufnr, 'number') vim.validate('bufnr', bufnr, 'number')
vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
bufnr = get_bufnr(bufnr) bufnr = get_bufnr(bufnr)
@ -1709,15 +1709,7 @@ end
function M.show(namespace, bufnr, diagnostics, opts) function M.show(namespace, bufnr, diagnostics, opts)
vim.validate('namespace', namespace, 'number', true) vim.validate('namespace', namespace, 'number', true)
vim.validate('bufnr', bufnr, 'number', true) vim.validate('bufnr', bufnr, 'number', true)
vim.validate({ vim.validate('diagnostics', diagnostics, vim.islist, true, 'a list of diagnostics')
diagnostics = {
diagnostics,
function(v)
return v == nil or vim.islist(v)
end,
'a list of diagnostics',
},
})
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
if not bufnr or not namespace then if not bufnr or not namespace then
@ -1869,13 +1861,7 @@ function M.open_float(opts, ...)
local highlights = {} --- @type table[] local highlights = {} --- @type table[]
local header = if_nil(opts.header, 'Diagnostics:') local header = if_nil(opts.header, 'Diagnostics:')
if header then if header then
vim.validate({ vim.validate('header', header, { 'string', 'table' }, "'string' or 'table'")
header = {
header,
{ 'string', 'table' },
"'string' or 'table'",
},
})
if type(header) == 'table' then if type(header) == 'table' then
-- Don't insert any lines for an empty string -- Don't insert any lines for an empty string
if string.len(if_nil(header[1], '')) > 0 then if string.len(if_nil(header[1], '')) > 0 then
@ -1903,13 +1889,12 @@ function M.open_float(opts, ...)
local prefix, prefix_hl_group --- @type string?, string? local prefix, prefix_hl_group --- @type string?, string?
if prefix_opt then if prefix_opt then
vim.validate({ vim.validate(
prefix = { 'prefix',
prefix_opt, prefix_opt,
{ 'string', 'table', 'function' }, { 'string', 'table', 'function' },
"'string' or 'table' or 'function'", "'string' or 'table' or 'function'"
}, )
})
if type(prefix_opt) == 'string' then if type(prefix_opt) == 'string' then
prefix, prefix_hl_group = prefix_opt, 'NormalFloat' prefix, prefix_hl_group = prefix_opt, 'NormalFloat'
elseif type(prefix_opt) == 'table' then elseif type(prefix_opt) == 'table' then
@ -1923,13 +1908,12 @@ function M.open_float(opts, ...)
local suffix, suffix_hl_group --- @type string?, string? local suffix, suffix_hl_group --- @type string?, string?
if suffix_opt then if suffix_opt then
vim.validate({ vim.validate(
suffix = { 'suffix',
suffix_opt, suffix_opt,
{ 'string', 'table', 'function' }, { 'string', 'table', 'function' },
"'string' or 'table' or 'function'", "'string' or 'table' or 'function'"
}, )
})
if type(suffix_opt) == 'string' then if type(suffix_opt) == 'string' then
suffix, suffix_hl_group = suffix_opt, 'NormalFloat' suffix, suffix_hl_group = suffix_opt, 'NormalFloat'
elseif type(suffix_opt) == 'table' then elseif type(suffix_opt) == 'table' then
@ -2239,7 +2223,7 @@ local errlist_type_map = {
---@param diagnostics vim.Diagnostic[] ---@param diagnostics vim.Diagnostic[]
---@return table[] : Quickfix list items |setqflist-what| ---@return table[] : Quickfix list items |setqflist-what|
function M.toqflist(diagnostics) function M.toqflist(diagnostics)
vim.validate({ diagnostics = { diagnostics, vim.islist, 'a list of diagnostics' } }) vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics')
local list = {} --- @type table[] local list = {} --- @type table[]
for _, v in ipairs(diagnostics) do for _, v in ipairs(diagnostics) do

View File

@ -229,7 +229,7 @@ end
---@return (string[]) # Normalized paths |vim.fs.normalize()| of all matching items ---@return (string[]) # Normalized paths |vim.fs.normalize()| of all matching items
function M.find(names, opts) function M.find(names, opts)
opts = opts or {} opts = opts or {}
vim.validate({ names = { names, { 'string', 'table', 'function' } } }) vim.validate('names', names, { 'string', 'table', 'function' })
vim.validate('path', opts.path, 'string', true) vim.validate('path', opts.path, 'string', true)
vim.validate('upward', opts.upward, 'boolean', true) vim.validate('upward', opts.upward, 'boolean', true)
vim.validate('stop', opts.stop, 'string', true) vim.validate('stop', opts.stop, 'string', true)

View File

@ -39,10 +39,8 @@ end
--- @param strong? boolean --- @param strong? boolean
--- @return F --- @return F
return function(hash, fn, strong) return function(hash, fn, strong)
vim.validate({ vim.validate('hash', hash, { 'number', 'string', 'function' })
hash = { hash, { 'number', 'string', 'function' } }, vim.validate('fn', fn, 'function')
fn = { fn, 'function' },
})
---@type table<any,table<any,any>> ---@type table<any,table<any,any>>
local cache = {} local cache = {}

View File

@ -135,19 +135,7 @@ local yank_cancel --- @type fun()?
--- - event event structure (default vim.v.event) --- - event event structure (default vim.v.event)
--- - priority integer priority (default |vim.hl.priorities|`.user`) --- - priority integer priority (default |vim.hl.priorities|`.user`)
function M.on_yank(opts) function M.on_yank(opts)
vim.validate({ vim.validate('opts', opts, 'table', true)
opts = {
opts,
function(t)
if t == nil then
return true
else
return type(t) == 'table'
end
end,
'a table or nil to configure options (see `:h vim.hl.on_yank`)',
},
})
opts = opts or {} opts = opts or {}
local event = opts.event or vim.v.event local event = opts.event or vim.v.event
local on_macro = opts.on_macro or false local on_macro = opts.on_macro or false

View File

@ -42,12 +42,10 @@ local keymap = {}
---@see |mapcheck()| ---@see |mapcheck()|
---@see |mapset()| ---@see |mapset()|
function keymap.set(mode, lhs, rhs, opts) function keymap.set(mode, lhs, rhs, opts)
vim.validate({ vim.validate('mode', mode, { 'string', 'table' })
mode = { mode, { 's', 't' } }, vim.validate('lhs', lhs, 'string')
lhs = { lhs, 's' }, vim.validate('rhs', rhs, { 'string', 'function' })
rhs = { rhs, { 's', 'f' } }, vim.validate('opts', opts, 'table', true)
opts = { opts, 't', true },
})
opts = vim.deepcopy(opts or {}, true) opts = vim.deepcopy(opts or {}, true)
@ -107,11 +105,9 @@ end
---@param opts? vim.keymap.del.Opts ---@param opts? vim.keymap.del.Opts
---@see |vim.keymap.set()| ---@see |vim.keymap.set()|
function keymap.del(modes, lhs, opts) function keymap.del(modes, lhs, opts)
vim.validate({ vim.validate('mode', modes, { 'string', 'table' })
mode = { modes, { 's', 't' } }, vim.validate('lhs', lhs, 'string')
lhs = { lhs, 's' }, vim.validate('opts', opts, 'table', true)
opts = { opts, 't', true },
})
opts = opts or {} opts = opts or {}
modes = type(modes) == 'string' and { modes } or modes modes = type(modes) == 'string' and { modes } or modes

View File

@ -349,24 +349,22 @@ end
--- @param config vim.lsp.ClientConfig --- @param config vim.lsp.ClientConfig
local function validate_config(config) local function validate_config(config)
validate('config', config, 'table') validate('config', config, 'table')
validate({ validate('handlers', config.handlers, 'table', true)
handlers = { config.handlers, 't', true }, validate('capabilities', config.capabilities, 'table', true)
capabilities = { config.capabilities, 't', true }, validate('cmd_cwd', config.cmd_cwd, optional_validator(is_dir), 'directory')
cmd_cwd = { config.cmd_cwd, optional_validator(is_dir), 'directory' }, validate('cmd_env', config.cmd_env, 'table', true)
cmd_env = { config.cmd_env, 't', true }, validate('detached', config.detached, 'boolean', true)
detached = { config.detached, 'b', true }, validate('name', config.name, 'string', true)
name = { config.name, 's', true }, validate('on_error', config.on_error, 'function', true)
on_error = { config.on_error, 'f', true }, validate('on_exit', config.on_exit, { 'function', 'table' }, true)
on_exit = { config.on_exit, { 'f', 't' }, true }, validate('on_init', config.on_init, { 'function', 'table' }, true)
on_init = { config.on_init, { 'f', 't' }, true }, validate('on_attach', config.on_attach, { 'function', 'table' }, true)
on_attach = { config.on_attach, { 'f', 't' }, true }, validate('settings', config.settings, 'table', true)
settings = { config.settings, 't', true }, validate('commands', config.commands, 'table', true)
commands = { config.commands, 't', true }, validate('before_init', config.before_init, { 'function', 'table' }, true)
before_init = { config.before_init, { 'f', 't' }, true }, validate('offset_encoding', config.offset_encoding, 'string', true)
offset_encoding = { config.offset_encoding, 's', true }, validate('flags', config.flags, 'table', true)
flags = { config.flags, 't', true }, validate('get_language_id', config.get_language_id, 'function', true)
get_language_id = { config.get_language_id, 'f', true },
})
assert( assert(
( (

View File

@ -132,17 +132,11 @@ end
---@return boolean success true if operation was successful ---@return boolean success true if operation was successful
---@return string msg full path if operation was successful, else error message ---@return string msg full path if operation was successful, else error message
function M.trust(opts) function M.trust(opts)
vim.validate({ vim.validate('path', opts.path, 'string', true)
path = { opts.path, 's', true }, vim.validate('bufnr', opts.bufnr, 'number', true)
bufnr = { opts.bufnr, 'n', true }, vim.validate('action', opts.action, function(m)
action = { return m == 'allow' or m == 'deny' or m == 'remove'
opts.action, end, [["allow" or "deny" or "remove"]])
function(m)
return m == 'allow' or m == 'deny' or m == 'remove'
end,
[["allow" or "deny" or "remove"]],
},
})
---@cast opts vim.trust.opts ---@cast opts vim.trust.opts
local path = opts.path local path = opts.path

View File

@ -251,7 +251,8 @@ end
---@param t table<any, T> Table ---@param t table<any, T> Table
---@return table : Table of transformed values ---@return table : Table of transformed values
function vim.tbl_map(func, t) function vim.tbl_map(func, t)
vim.validate({ func = { func, 'c' }, t = { t, 't' } }) vim.validate('func', func, 'callable')
vim.validate('t', t, 'table')
--- @cast t table<any,any> --- @cast t table<any,any>
local rettab = {} --- @type table<any,any> local rettab = {} --- @type table<any,any>
@ -268,7 +269,8 @@ end
---@param t table<any, T> (table) Table ---@param t table<any, T> (table) Table
---@return T[] : Table of filtered values ---@return T[] : Table of filtered values
function vim.tbl_filter(func, t) function vim.tbl_filter(func, t)
vim.validate({ func = { func, 'c' }, t = { t, 't' } }) vim.validate('func', func, 'callable')
vim.validate('t', t, 'table')
--- @cast t table<any,any> --- @cast t table<any,any>
local rettab = {} --- @type table<any,any> local rettab = {} --- @type table<any,any>
@ -311,7 +313,7 @@ function vim.tbl_contains(t, value, opts)
local pred --- @type fun(v: any): boolean? local pred --- @type fun(v: any): boolean?
if opts and opts.predicate then if opts and opts.predicate then
vim.validate({ value = { value, 'c' } }) vim.validate('value', value, 'callable')
pred = value pred = value
else else
pred = function(v) pred = function(v)
@ -779,237 +781,226 @@ function vim.endswith(s, suffix)
end end
do do
--- @alias vim.validate.LuaType --- @alias vim.validate.Validator
--- | 'nil' --- | type|'callable'
--- | 'number' --- | (type|'callable')[]
--- | 'string' --- | fun(v:any):boolean, string?
--- | 'boolean'
--- | 'table'
--- | 'function'
--- | 'thread'
--- | 'userdata'
---
--- @alias vim.validate.Type vim.validate.LuaType | 't' | 's' | 'n' | 'f' | 'c'
local type_names = { local type_aliases = {
['table'] = 'table',
t = 'table',
['string'] = 'string',
s = 'string',
['number'] = 'number',
n = 'number',
['boolean'] = 'boolean',
b = 'boolean', b = 'boolean',
['function'] = 'function',
f = 'function',
['callable'] = 'callable',
c = 'callable', c = 'callable',
['nil'] = 'nil', f = 'function',
['thread'] = 'thread', n = 'number',
['userdata'] = 'userdata', s = 'string',
t = 'table',
} }
--- @nodoc --- @nodoc
--- @class vim.validate.Spec [any, string|string[], boolean] --- @class vim.validate.Spec
--- @field [1] any Argument value --- @field [1] any Argument value
--- @field [2] vim.validate.Type|vim.validate.Type[]|fun(v:any):boolean, string? Type name, or callable --- @field [2] vim.validate.Validator Argument validator
--- @field [3]? boolean --- @field [3]? boolean|string Optional flag or error message
local function _is_type(val, t) local function is_type(val, t)
return type(val) == t or (t == 'callable' and vim.is_callable(val)) return type(val) == t or (t == 'callable' and vim.is_callable(val))
end end
--- @param param_name string --- @param param_name string
--- @param spec vim.validate.Spec --- @param val any
--- @param validator vim.validate.Validator
--- @param message? string
--- @param allow_alias? boolean Allow short type names: 'n', 's', 't', 'b', 'f', 'c'
--- @return string? --- @return string?
local function is_param_valid(param_name, spec) local function is_valid(param_name, val, validator, message, allow_alias)
if type(spec) ~= 'table' then if type(validator) == 'string' then
return string.format('opt[%s]: expected table, got %s', param_name, type(spec)) local expected = allow_alias and type_aliases[validator] or validator
end
local val = spec[1] -- Argument value if not expected then
local types = spec[2] -- Type name, or callable return string.format('invalid type name: %s', validator)
local optional = (true == spec[3]) end
if type(types) == 'string' then if not is_type(val, expected) then
types = { types } return string.format('%s: expected %s, got %s', param_name, expected, type(val))
end end
elseif vim.is_callable(validator) then
if vim.is_callable(types) then
-- Check user-provided validation function -- Check user-provided validation function
local valid, optional_message = types(val) local valid, opt_msg = validator(val)
if not valid then if not valid then
local error_message = local err_msg =
string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val)) string.format('%s: expected %s, got %s', param_name, message or '?', tostring(val))
if optional_message ~= nil then
error_message = string.format('%s. Info: %s', error_message, optional_message) if opt_msg then
err_msg = string.format('%s. Info: %s', err_msg, opt_msg)
end end
return error_message return err_msg
end end
elseif type(types) == 'table' then elseif type(validator) == 'table' then
local success = false for _, t in ipairs(validator) do
for i, t in ipairs(types) do local expected = allow_alias and type_aliases[t] or t
local t_name = type_names[t] if not expected then
if not t_name then
return string.format('invalid type name: %s', t) return string.format('invalid type name: %s', t)
end end
types[i] = t_name
if (optional and val == nil) or _is_type(val, t_name) then if is_type(val, expected) then
success = true return -- success
break
end end
end end
if not success then
return string.format( -- Normalize validator types for error message
'%s: expected %s, got %s', if allow_alias then
param_name, for i, t in ipairs(validator) do
table.concat(types, '|'), validator[i] = type_aliases[t] or t
type(val) end
)
end end
return string.format(
'%s: expected %s, got %s',
param_name,
table.concat(validator, '|'),
type(val)
)
else else
return string.format('invalid type name: %s', tostring(types)) return string.format('invalid validator: %s', tostring(validator))
end end
end end
--- @param opt table<vim.validate.Type,vim.validate.Spec> --- @param opt table<type|'callable',vim.validate.Spec>
--- @return boolean, string? --- @return string?
local function is_valid(opt) local function validate_spec(opt)
if type(opt) ~= 'table' then
return false, string.format('opt: expected table, got %s', type(opt))
end
local report --- @type table<string,string>? local report --- @type table<string,string>?
for param_name, spec in pairs(opt) do for param_name, spec in pairs(opt) do
local msg = is_param_valid(param_name, spec) local err_msg --- @type string?
if msg then if type(spec) ~= 'table' then
err_msg = string.format('opt[%s]: expected table, got %s', param_name, type(spec))
else
local value, validator = spec[1], spec[2]
local msg = type(spec[3]) == 'string' and spec[3] or nil --[[@as string?]]
local optional = spec[3] == true
if not (optional and value == nil) then
err_msg = is_valid(param_name, value, validator, msg, true)
end
end
if err_msg then
report = report or {} report = report or {}
report[param_name] = msg report[param_name] = err_msg
end end
end end
if report then if report then
for _, msg in vim.spairs(report) do -- luacheck: ignore for _, msg in vim.spairs(report) do -- luacheck: ignore
return false, msg return msg
end end
end end
return true
end end
--- Validate function arguments. --- Validate function arguments.
--- ---
--- This function has two valid forms: --- This function has two valid forms:
--- ---
--- 1. vim.validate(name: str, value: any, type: string, optional?: bool) --- 1. `vim.validate(name, value, validator[, optional][, message])`
--- 2. vim.validate(spec: table)
--- ---
--- Form 1 validates that argument {name} with value {value} has the type --- Validates that argument {name} with value {value} satisfies
--- {type}. {type} must be a value returned by |lua-type()|. If {optional} is --- {validator}. If {optional} is given and is `true`, then {value} may be
--- true, then {value} may be null. This form is significantly faster and --- `nil`. If {message} is given, then it is used as the expected type in the
--- should be preferred for simple cases. --- error message.
--- ---
--- Example: --- Example:
--- ---
--- ```lua --- ```lua
--- function vim.startswith(s, prefix) --- function vim.startswith(s, prefix)
--- vim.validate('s', s, 'string') --- vim.validate('s', s, 'string')
--- vim.validate('prefix', prefix, 'string') --- vim.validate('prefix', prefix, 'string')
--- ... --- ...
--- end --- end
--- ``` --- ```
--- ---
--- Form 2 validates a parameter specification (types and values). Specs are --- 2. `vim.validate(spec)` (deprecated)
--- evaluated in alphanumeric order, until the first failure. --- where `spec` is of type
--- `table<string,[value:any, validator: vim.validate.Validator, optional_or_msg? : boolean|string]>)`
--- ---
--- Usage example: --- Validates a argument specification.
--- Specs are evaluated in alphanumeric order, until the first failure.
--- ---
--- ```lua --- Example:
--- function user.new(name, age, hobbies) ---
--- vim.validate{ --- ```lua
--- name={name, 'string'}, --- function user.new(name, age, hobbies)
--- age={age, 'number'}, --- vim.validate{
--- hobbies={hobbies, 'table'}, --- name={name, 'string'},
--- } --- age={age, 'number'},
--- ... --- hobbies={hobbies, 'table'},
--- end --- }
--- ``` --- ...
--- end
--- ```
--- ---
--- Examples with explicit argument values (can be run directly): --- Examples with explicit argument values (can be run directly):
--- ---
--- ```lua --- ```lua
--- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}} --- vim.validate('arg1', {'foo'}, 'table')
--- --> NOP (success)
--- vim.validate('arg2', 'foo', 'string')
--- --> NOP (success) --- --> NOP (success)
--- ---
--- vim.validate{arg1={1, 'table'}} --- vim.validate('arg1', 1, 'table')
--- --> error('arg1: expected table, got number') --- --> error('arg1: expected table, got number')
--- ---
--- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}} --- vim.validate('arg1', 3, function(a) return (a % 2) == 0 end, 'even number')
--- --> error('arg1: expected even number, got 3') --- --> error('arg1: expected even number, got 3')
--- ``` --- ```
--- ---
--- If multiple types are valid they can be given as a list. --- If multiple types are valid they can be given as a list.
--- ---
--- ```lua --- ```lua
--- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}} --- vim.validate('arg1', {'foo'}, {'table', 'string'})
--- vim.validate('arg2', 'foo', {'table', 'string'})
--- -- NOP (success) --- -- NOP (success)
--- ---
--- vim.validate{arg1={1, {'string', 'table'}}} --- vim.validate('arg1', 1, {'string', 'table'})
--- -- error('arg1: expected string|table, got number') --- -- error('arg1: expected string|table, got number')
--- ``` --- ```
--- ---
---@param opt table<vim.validate.Type,vim.validate.Spec> (table) Names of parameters to validate. Each key is a parameter --- @note `validator` set to a value returned by |lua-type()| provides the
--- name; each value is a tuple in one of these forms: --- best performance.
--- 1. (arg_value, type_name, optional) ---
--- - arg_value: argument value --- @param name string Argument name
--- - type_name: string|table type name, one of: ("table", "t", "string", --- @param value string Argument value
--- "s", "number", "n", "boolean", "b", "function", "f", "nil", --- @param validator vim.validate.Validator
--- "thread", "userdata") or list of them. --- - (`string|string[]`): Any value that can be returned from |lua-type()| in addition to
--- - optional: (optional) boolean, if true, `nil` is valid --- `'callable'`: `'boolean'`, `'callable'`, `'function'`, `'nil'`, `'number'`, `'string'`, `'table'`,
--- 2. (arg_value, fn, msg) --- `'thread'`, `'userdata'`.
--- - arg_value: argument value --- - (`fun(val:any): boolean, string?`) A function that returns a boolean and an optional
--- - fn: any function accepting one argument, returns true if and --- string message.
--- only if the argument is valid. Can optionally return an additional --- @param optional? boolean Argument is optional (may be omitted)
--- informative error message as the second returned value. --- @param message? string message when validation fails
--- - msg: (optional) error string if validation fails --- @overload fun(name: string, val: any, validator: vim.validate.Validator, message: string)
--- @overload fun(name: string, val: any, expected: vim.validate.LuaType, optional?: boolean) --- @overload fun(spec: table<string,[any, vim.validate.Validator, boolean|string]>)
function vim.validate(opt, ...) function vim.validate(name, value, validator, optional, message)
local ok = false local err_msg --- @type string?
local err_msg ---@type string? if validator then -- Form 1
local narg = select('#', ...) -- Check validator as a string first to optimize the common case.
if narg == 0 then local ok = (type(value) == validator) or (value == nil and optional == true)
ok, err_msg = is_valid(opt)
elseif narg >= 2 then
-- Overloaded signature for fast/simple cases
local name = opt --[[@as string]]
local v, expected, optional = ... ---@type string, string, boolean?
local actual = type(v)
ok = (actual == expected) or (v == nil and optional == true)
if not ok then if not ok then
if not jit and (actual ~= 'string' or actual ~= 'number') then local msg = type(optional) == 'string' and optional or message --[[@as string?]]
-- PUC-Lua can only handle string and number for %s in string.format() -- Check more complicated validators
v = vim.inspect(v) err_msg = is_valid(name, value, validator, msg, false)
end
err_msg = ('%s: expected %s, got %s%s'):format(
name,
expected,
actual,
v and (' (%s)'):format(v) or ''
)
end end
elseif type(name) == 'table' then -- Form 2
vim.deprecate('vim.validate', 'vim.validate(name, value, validator, optional_or_msg)', '1.0')
err_msg = validate_spec(name)
else else
error('invalid arguments') error('invalid arguments')
end end
if not ok then if err_msg then
error(err_msg, 2) error(err_msg, 2)
end end
end end
end end
--- Returns true if object `f` can be called as a function. --- Returns true if object `f` can be called as a function.
--- ---
---@param f any Any object ---@param f any Any object

View File

@ -17,10 +17,8 @@ local M = {}
--- otherwise. {seq} is the control sequence for the capability if found, or nil for --- otherwise. {seq} is the control sequence for the capability if found, or nil for
--- boolean capabilities. --- boolean capabilities.
function M.query(caps, cb) function M.query(caps, cb)
vim.validate({ vim.validate('caps', caps, { 'string', 'table' })
caps = { caps, { 'string', 'table' } }, vim.validate('cb', cb, 'function')
cb = { cb, 'f' },
})
if type(caps) ~= 'table' then if type(caps) ~= 'table' then
caps = { caps } caps = { caps }

View File

@ -241,11 +241,9 @@ end
--- ---
---@return boolean True if the {node} contains the {range} ---@return boolean True if the {node} contains the {range}
function M.node_contains(node, range) function M.node_contains(node, range)
vim.validate({ -- allow a table so nodes can be mocked
-- allow a table so nodes can be mocked vim.validate('node', node, { 'userdata', 'table' })
node = { node, { 'userdata', 'table' } }, vim.validate('range', range, M._range.validate, 'integer list with 4 or 6 elements')
range = { range, M._range.validate, 'integer list with 4 or 6 elements' },
})
return M._range.contains({ node:range() }, range) return M._range.contains({ node:range() }, range)
end end

View File

@ -154,10 +154,8 @@ end
--- @param lang string Name of parser --- @param lang string Name of parser
--- @param filetype string|string[] Filetype(s) to associate with lang --- @param filetype string|string[] Filetype(s) to associate with lang
function M.register(lang, filetype) function M.register(lang, filetype)
vim.validate({ vim.validate('lang', lang, 'string')
lang = { lang, 'string' }, vim.validate('filetype', filetype, { 'string', 'table' })
filetype = { filetype, { 'string', 'table' } },
})
for _, f in ipairs(ensure_list(filetype)) do for _, f in ipairs(ensure_list(filetype)) do
if f ~= '' then if f ~= '' then

View File

@ -37,8 +37,8 @@ local M = {}
--- `idx` is the 1-based index of `item` within `items`. --- `idx` is the 1-based index of `item` within `items`.
--- `nil` if the user aborted the dialog. --- `nil` if the user aborted the dialog.
function M.select(items, opts, on_choice) function M.select(items, opts, on_choice)
vim.validate('items', items, 'table', false) vim.validate('items', items, 'table')
vim.validate('on_choice', on_choice, 'function', false) vim.validate('on_choice', on_choice, 'function')
opts = opts or {} opts = opts or {}
local choices = { opts.prompt or 'Select one of:' } local choices = { opts.prompt or 'Select one of:' }
local format_item = opts.format_item or tostring local format_item = opts.format_item or tostring
@ -85,7 +85,7 @@ end
--- `nil` if the user aborted the dialog. --- `nil` if the user aborted the dialog.
function M.input(opts, on_confirm) function M.input(opts, on_confirm)
vim.validate('opts', opts, 'table', true) vim.validate('opts', opts, 'table', true)
vim.validate('on_confirm', on_confirm, 'function', false) vim.validate('on_confirm', on_confirm, 'function')
opts = (opts and not vim.tbl_isempty(opts)) and opts or vim.empty_dict() opts = (opts and not vim.tbl_isempty(opts)) and opts or vim.empty_dict()

View File

@ -325,10 +325,8 @@ function M.commit(dependency_name, commit)
end end
function M.version(dependency_name, version) function M.version(dependency_name, version)
vim.validate { vim.validate('dependency_name', dependency_name, 'string')
dependency_name = { dependency_name, 's' }, vim.validate('version', version, 'string')
version = { version, 's' },
}
local dependency = assert(get_dependency(dependency_name)) local dependency = assert(get_dependency(dependency_name))
verify_cmakelists_committed() verify_cmakelists_committed()
local commit_sha = get_gh_commit_sha(dependency.repo, version) local commit_sha = get_gh_commit_sha(dependency.repo, version)

View File

@ -1289,25 +1289,15 @@ end
--- ---
--- @return nvim.gen_help_html.gen_result result --- @return nvim.gen_help_html.gen_result result
function M.gen(help_dir, to_dir, include, commit, parser_path) function M.gen(help_dir, to_dir, include, commit, parser_path)
vim.validate { vim.validate('help_dir', help_dir, function(d)
help_dir = { return vim.fn.isdirectory(vim.fs.normalize(d)) == 1
help_dir, end, 'valid directory')
function(d) vim.validate('to_dir', to_dir, 'string')
return vim.fn.isdirectory(vim.fs.normalize(d)) == 1 vim.validate('include', include, 'table', true)
end, vim.validate('commit', commit, 'sring', true)
'valid directory', vim.validate('parser_path', parser_path, function(f)
}, return vim.fn.filereadable(vim.fs.normalize(f)) == 1
to_dir = { to_dir, 's' }, end, true, 'valid vimdoc.{so,dll} filepath')
include = { include, 't', true },
commit = { commit, 's', true },
parser_path = {
parser_path,
function(f)
return f == nil or vim.fn.filereadable(vim.fs.normalize(f)) == 1
end,
'valid vimdoc.{so,dll} filepath',
},
}
local err_count = 0 local err_count = 0
local redirects_count = 0 local redirects_count = 0
@ -1410,23 +1400,13 @@ end
--- ---
--- @return nvim.gen_help_html.validate_result result --- @return nvim.gen_help_html.validate_result result
function M.validate(help_dir, include, parser_path) function M.validate(help_dir, include, parser_path)
vim.validate { vim.validate('help_dir', help_dir, function(d)
help_dir = { return vim.fn.isdirectory(vim.fs.normalize(d)) == 1
help_dir, end, 'valid directory')
function(d) vim.validate('include', include, 'table', true)
return vim.fn.isdirectory(vim.fs.normalize(d)) == 1 vim.validate('parser_path', parser_path, function(f)
end, return vim.fn.filereadable(vim.fs.normalize(f)) == 1
'valid directory', end, true, 'valid vimdoc.{so,dll} filepath')
},
include = { include, 't', true },
parser_path = {
parser_path,
function(f)
return f == nil or vim.fn.filereadable(vim.fs.normalize(f)) == 1
end,
'valid vimdoc.{so,dll} filepath',
},
}
local err_count = 0 ---@type integer local err_count = 0 ---@type integer
local files_to_errors = {} ---@type table<string, string[]> local files_to_errors = {} ---@type table<string, string[]>
ensure_runtimepath() ensure_runtimepath()

View File

@ -1358,7 +1358,79 @@ describe('lua stdlib', function()
eq('{"a": {}, "b": []}', exec_lua([[ return vim.fn.json_encode({a=vim.empty_dict(), b={}}) ]])) eq('{"a": {}, "b": []}', exec_lua([[ return vim.fn.json_encode({a=vim.empty_dict(), b={}}) ]]))
end) end)
it('vim.validate', function() it('vim.validate (fast form)', function()
exec_lua("vim.validate('arg1', {}, 'table')")
exec_lua("vim.validate('arg1', nil, 'table', true)")
exec_lua("vim.validate('arg1', { foo='foo' }, 'table')")
exec_lua("vim.validate('arg1', { 'foo' }, 'table')")
exec_lua("vim.validate('arg1', 'foo', 'string')")
exec_lua("vim.validate('arg1', nil, 'string', true)")
exec_lua("vim.validate('arg1', 1, 'number')")
exec_lua("vim.validate('arg1', 0, 'number')")
exec_lua("vim.validate('arg1', 0.1, 'number')")
exec_lua("vim.validate('arg1', nil, 'number', true)")
exec_lua("vim.validate('arg1', true, 'boolean')")
exec_lua("vim.validate('arg1', false, 'boolean')")
exec_lua("vim.validate('arg1', nil, 'boolean', true)")
exec_lua("vim.validate('arg1', function()end, 'function')")
exec_lua("vim.validate('arg1', nil, 'function', true)")
exec_lua("vim.validate('arg1', nil, 'nil')")
exec_lua("vim.validate('arg1', nil, 'nil', true)")
exec_lua("vim.validate('arg1', coroutine.create(function()end), 'thread')")
exec_lua("vim.validate('arg1', nil, 'thread', true)")
exec_lua("vim.validate('arg1', 2, function(a) return (a % 2) == 0 end, 'even number')")
exec_lua("vim.validate('arg1', 5, {'number', 'string'})")
exec_lua("vim.validate('arg2', 'foo', {'number', 'string'})")
matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number'))
matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string'))
matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table'))
matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function'))
matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string'))
matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table'))
matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function'))
matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number'))
matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate('arg1', 1, 'x')"))
matches('invalid validator: 1', pcall_err(exec_lua, "vim.validate('arg1', 1, 1)"))
matches('invalid arguments', pcall_err(exec_lua, "vim.validate('arg1', { 1 })"))
-- Validated parameters are required by default.
matches(
'arg1: expected string, got nil',
pcall_err(exec_lua, "vim.validate('arg1', nil, 'string')")
)
-- Explicitly required.
matches(
'arg1: expected string, got nil',
pcall_err(exec_lua, "vim.validate('arg1', nil, 'string', false)")
)
matches(
'arg1: expected table, got number',
pcall_err(exec_lua, "vim.validate('arg1', 1, 'table')")
)
matches(
'arg1: expected even number, got 3',
pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end, 'even number')")
)
matches(
'arg1: expected %?, got 3',
pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1 end)")
)
matches(
'arg1: expected number|string, got nil',
pcall_err(exec_lua, "vim.validate('arg1', nil, {'number', 'string'})")
)
-- Pass an additional message back.
matches(
'arg1: expected %?, got 3. Info: TEST_MSG',
pcall_err(exec_lua, "vim.validate('arg1', 3, function(a) return a == 1, 'TEST_MSG' end)")
)
end)
it('vim.validate (spec form)', function()
exec_lua("vim.validate{arg1={{}, 'table' }}") exec_lua("vim.validate{arg1={{}, 'table' }}")
exec_lua("vim.validate{arg1={{}, 't' }}") exec_lua("vim.validate{arg1={{}, 't' }}")
exec_lua("vim.validate{arg1={nil, 't', true }}") exec_lua("vim.validate{arg1={nil, 't', true }}")
@ -1387,29 +1459,11 @@ describe('lua stdlib', function()
exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}") exec_lua("vim.validate{arg1={{}, 't' }, arg2={ 'foo', 's' }}")
exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}") exec_lua("vim.validate{arg1={2, function(a) return (a % 2) == 0 end, 'even number' }}")
exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}") exec_lua("vim.validate{arg1={5, {'n', 's'} }, arg2={ 'foo', {'n', 's'} }}")
vim.validate('arg1', 5, 'number')
vim.validate('arg1', '5', 'string')
vim.validate('arg1', { 5 }, 'table')
vim.validate('arg1', function()
return 5
end, 'function')
vim.validate('arg1', nil, 'number', true)
vim.validate('arg1', nil, 'string', true)
vim.validate('arg1', nil, 'table', true)
vim.validate('arg1', nil, 'function', true)
matches('arg1: expected number, got nil', pcall_err(vim.validate, 'arg1', nil, 'number'))
matches('arg1: expected string, got nil', pcall_err(vim.validate, 'arg1', nil, 'string'))
matches('arg1: expected table, got nil', pcall_err(vim.validate, 'arg1', nil, 'table'))
matches('arg1: expected function, got nil', pcall_err(vim.validate, 'arg1', nil, 'function'))
matches('arg1: expected string, got number', pcall_err(vim.validate, 'arg1', 5, 'string'))
matches('arg1: expected table, got number', pcall_err(vim.validate, 'arg1', 5, 'table'))
matches('arg1: expected function, got number', pcall_err(vim.validate, 'arg1', 5, 'function'))
matches('arg1: expected number, got string', pcall_err(vim.validate, 'arg1', '5', 'number'))
matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }")) matches('expected table, got number', pcall_err(exec_lua, "vim.validate{ 1, 'x' }"))
matches('invalid type name: x', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}")) matches('arg1: expected x, got number', pcall_err(exec_lua, "vim.validate{ arg1={ 1, 'x' }}"))
matches('invalid type name: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}')) matches('invalid validator: 1', pcall_err(exec_lua, 'vim.validate{ arg1={ 1, 1 }}'))
matches('invalid type name: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}')) matches('invalid validator: nil', pcall_err(exec_lua, 'vim.validate{ arg1={ 1 }}'))
-- Validated parameters are required by default. -- Validated parameters are required by default.
matches( matches(
@ -4094,10 +4148,15 @@ describe('vim.keymap', function()
) )
matches( matches(
'opts: expected table, got function', 'rhs: expected string|function, got number',
pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 42, function() end)]]) pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 42, function() end)]])
) )
matches(
'opts: expected table, got function',
pcall_err(exec_lua, [[vim.keymap.set({}, 'x', 'x', function() end)]])
)
matches( matches(
'rhs: expected string|function, got number', 'rhs: expected string|function, got number',
pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 42)]]) pcall_err(exec_lua, [[vim.keymap.set('z', 'x', 42)]])