2019-05-19 17:58:54 +02:00
|
|
|
-- Functions shared by Nvim and its test-suite.
|
|
|
|
|
--
|
2022-03-06 13:13:10 +01:00
|
|
|
-- These are "pure" lua functions not depending of the state of the editor.
|
|
|
|
|
-- Thus they should always be available whenever nvim-related lua code is run,
|
|
|
|
|
-- regardless if it is code in the editor itself, or in worker threads/processes,
|
|
|
|
|
-- or the test suite. (Eventually the test suite will be run in a worker process,
|
|
|
|
|
-- so this wouldn't be a separate case to consider)
|
2019-01-18 00:44:35 +01:00
|
|
|
|
2022-10-09 12:40:56 +02:00
|
|
|
vim = vim or {}
|
2019-01-18 00:44:35 +01:00
|
|
|
|
2019-05-19 17:58:54 +02:00
|
|
|
--- Returns a deep copy of the given object. Non-table objects are copied as
|
|
|
|
|
--- in a typical Lua assignment, whereas table objects are copied recursively.
|
2020-04-18 17:04:37 -06:00
|
|
|
--- Functions are naively copied, so functions in the copied table point to the
|
|
|
|
|
--- same functions as those in the input table. Userdata and threads are not
|
|
|
|
|
--- copied and will throw an error.
|
2019-05-19 17:58:54 +02:00
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T: table
|
|
|
|
|
---@param orig T Table to copy
|
|
|
|
|
---@return T Table of copied keys and (nested) values.
|
2019-10-23 10:50:42 +09:00
|
|
|
function vim.deepcopy(orig) end -- luacheck: no unused
|
|
|
|
|
vim.deepcopy = (function()
|
|
|
|
|
local function _id(v)
|
|
|
|
|
return v
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local deepcopy_funcs = {
|
2021-11-26 16:06:43 +06:00
|
|
|
table = function(orig, cache)
|
|
|
|
|
if cache[orig] then
|
|
|
|
|
return cache[orig]
|
|
|
|
|
end
|
2019-10-23 10:50:42 +09:00
|
|
|
local copy = {}
|
2020-02-13 11:55:43 +09:00
|
|
|
|
2021-11-26 16:06:43 +06:00
|
|
|
cache[orig] = copy
|
|
|
|
|
local mt = getmetatable(orig)
|
2019-10-23 10:50:42 +09:00
|
|
|
for k, v in pairs(orig) do
|
2021-11-26 16:06:43 +06:00
|
|
|
copy[vim.deepcopy(k, cache)] = vim.deepcopy(v, cache)
|
2019-10-23 10:50:42 +09:00
|
|
|
end
|
2021-11-26 16:06:43 +06:00
|
|
|
return setmetatable(copy, mt)
|
2019-10-23 10:50:42 +09:00
|
|
|
end,
|
|
|
|
|
number = _id,
|
|
|
|
|
string = _id,
|
|
|
|
|
['nil'] = _id,
|
|
|
|
|
boolean = _id,
|
2020-04-18 17:04:37 -06:00
|
|
|
['function'] = _id,
|
2019-10-23 10:50:42 +09:00
|
|
|
}
|
|
|
|
|
|
2021-11-26 16:06:43 +06:00
|
|
|
return function(orig, cache)
|
2020-04-18 17:04:37 -06:00
|
|
|
local f = deepcopy_funcs[type(orig)]
|
|
|
|
|
if f then
|
2021-11-26 16:06:43 +06:00
|
|
|
return f(orig, cache or {})
|
2020-04-18 17:04:37 -06:00
|
|
|
else
|
2022-11-14 20:26:27 +01:00
|
|
|
if type(orig) == 'userdata' and orig == vim.NIL then
|
|
|
|
|
return vim.NIL
|
|
|
|
|
end
|
2020-04-18 17:04:37 -06:00
|
|
|
error('Cannot deepcopy object of type ' .. type(orig))
|
|
|
|
|
end
|
2019-10-23 10:50:42 +09:00
|
|
|
end
|
|
|
|
|
end)()
|
2019-05-19 17:58:54 +02:00
|
|
|
|
2019-05-19 18:31:40 +02:00
|
|
|
--- Splits a string at each instance of a separator.
|
|
|
|
|
---
|
2023-03-20 08:12:33 +01:00
|
|
|
--- Example:
|
|
|
|
|
--- <pre>lua
|
|
|
|
|
--- for s in vim.gsplit(':aa::b:', ':', {plain=true}) do
|
|
|
|
|
--- print(s)
|
|
|
|
|
--- end
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
2023-03-22 15:14:51 +01:00
|
|
|
--- If you want to also inspect the separator itself (instead of discarding it), use
|
|
|
|
|
--- |string.gmatch()|. Example:
|
|
|
|
|
--- <pre>lua
|
|
|
|
|
--- for word, num in ('foo111bar222'):gmatch('([^0-9]*)(%d*)') do
|
|
|
|
|
--- print(('word: %s num: %s'):format(word, num))
|
|
|
|
|
--- end
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
2023-03-20 13:36:06 +01:00
|
|
|
--- @see |string.gmatch()|
|
|
|
|
|
--- @see |vim.split()|
|
|
|
|
|
--- @see |luaref-patterns|
|
|
|
|
|
--- @see https://www.lua.org/pil/20.2.html
|
|
|
|
|
--- @see http://lua-users.org/wiki/StringLibraryTutorial
|
2019-05-19 18:31:40 +02:00
|
|
|
---
|
2023-03-20 13:36:06 +01:00
|
|
|
--- @param s string String to split
|
|
|
|
|
--- @param sep string Separator or pattern
|
|
|
|
|
--- @param opts (table|nil) Keyword arguments |kwargs|:
|
2023-03-20 08:12:33 +01:00
|
|
|
--- - plain: (boolean) Use `sep` literally (as in string.find).
|
|
|
|
|
--- - trimempty: (boolean) Discard empty segments at start and end of the sequence.
|
|
|
|
|
---@return fun():string|nil (function) Iterator over the split components
|
|
|
|
|
function vim.gsplit(s, sep, opts)
|
|
|
|
|
local plain
|
|
|
|
|
local trimempty = false
|
|
|
|
|
if type(opts) == 'boolean' then
|
|
|
|
|
plain = opts -- For backwards compatibility.
|
|
|
|
|
else
|
|
|
|
|
vim.validate({ s = { s, 's' }, sep = { sep, 's' }, opts = { opts, 't', true } })
|
|
|
|
|
opts = opts or {}
|
2023-03-22 15:14:51 +01:00
|
|
|
plain, trimempty = opts.plain, opts.trimempty
|
2023-03-20 08:12:33 +01:00
|
|
|
end
|
2019-05-19 18:31:40 +02:00
|
|
|
|
|
|
|
|
local start = 1
|
|
|
|
|
local done = false
|
2023-03-20 08:12:33 +01:00
|
|
|
|
|
|
|
|
-- For `trimempty`:
|
|
|
|
|
local empty_start = true -- Only empty segments seen so far.
|
|
|
|
|
local empty_segs = 0 -- Empty segments found between non-empty segments.
|
|
|
|
|
local nonemptyseg = nil
|
2019-05-19 18:31:40 +02:00
|
|
|
|
|
|
|
|
local function _pass(i, j, ...)
|
|
|
|
|
if i then
|
|
|
|
|
assert(j + 1 > start, 'Infinite loop detected')
|
|
|
|
|
local seg = s:sub(start, i - 1)
|
|
|
|
|
start = j + 1
|
|
|
|
|
return seg, ...
|
|
|
|
|
else
|
|
|
|
|
done = true
|
|
|
|
|
return s:sub(start)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return function()
|
2023-03-20 08:12:33 +01:00
|
|
|
if trimempty and empty_segs > 0 then
|
|
|
|
|
-- trimempty: Pop the collected empty segments.
|
|
|
|
|
empty_segs = empty_segs - 1
|
|
|
|
|
return ''
|
|
|
|
|
elseif trimempty and nonemptyseg then
|
|
|
|
|
local seg = nonemptyseg
|
|
|
|
|
nonemptyseg = nil
|
|
|
|
|
return seg
|
|
|
|
|
elseif done or (s == '' and sep == '') then
|
|
|
|
|
return nil
|
|
|
|
|
elseif sep == '' then
|
2019-05-19 18:31:40 +02:00
|
|
|
if start == #s then
|
|
|
|
|
done = true
|
|
|
|
|
end
|
|
|
|
|
return _pass(start + 1, start)
|
|
|
|
|
end
|
2023-03-20 08:12:33 +01:00
|
|
|
|
|
|
|
|
local seg = _pass(s:find(sep, start, plain))
|
|
|
|
|
|
|
|
|
|
-- Trim empty segments from start/end.
|
|
|
|
|
if trimempty and seg == '' then
|
|
|
|
|
while not done and seg == '' do
|
|
|
|
|
empty_segs = empty_segs + 1
|
|
|
|
|
seg = _pass(s:find(sep, start, plain))
|
|
|
|
|
end
|
|
|
|
|
if done and seg == '' then
|
|
|
|
|
return nil
|
|
|
|
|
elseif empty_start then
|
|
|
|
|
empty_start = false
|
|
|
|
|
empty_segs = 0
|
|
|
|
|
return seg
|
|
|
|
|
end
|
|
|
|
|
nonemptyseg = seg ~= '' and seg or nil
|
|
|
|
|
seg = ''
|
|
|
|
|
empty_segs = empty_segs - 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return seg
|
2019-05-19 18:31:40 +02:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Splits a string at each instance of a separator.
|
|
|
|
|
---
|
|
|
|
|
--- Examples:
|
2022-11-23 12:31:49 +01:00
|
|
|
--- <pre>lua
|
|
|
|
|
--- split(":aa::b:", ":") --> {'','aa','','b',''}
|
|
|
|
|
--- split("axaby", "ab?") --> {'','x','y'}
|
|
|
|
|
--- split("x*yz*o", "*", {plain=true}) --> {'x','yz','o'}
|
|
|
|
|
--- split("|x|y|z|", "|", {trimempty=true}) --> {'x', 'y', 'z'}
|
2019-05-19 18:31:40 +02:00
|
|
|
--- </pre>
|
feat: add trimempty optional parameter to vim.split
The `split()` VimL function trims empty items from the returned list by
default, so that, e.g.
split("\nhello\nworld\n\n", "\n")
returns
["hello", "world"]
The Lua implementation of vim.split does not do this. For example,
vim.split("\nhello\nworld\n\n", "\n")
returns
{'', 'hello', 'world', '', ''}
Add an optional parameter to the vim.split function that, when true,
trims these empty elements from the front and back of the returned
table. This is only possible for vim.split and not vim.gsplit; because
vim.gsplit is an iterator, there is no way for it to know if the current
item is the last non-empty item.
Note that in order to preserve backward compatibility, the parameter for
the Lua vim.split function is `trimempty`, while the VimL function uses
`keepempty` (i.e. they are opposites). This means there is a disconnect
between these two functions that may surprise users.
2021-07-29 14:48:04 -06:00
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see |vim.gsplit()|
|
2023-03-22 15:14:51 +01:00
|
|
|
---@see |string.gmatch()|
|
2019-05-19 18:31:40 +02:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param s string String to split
|
|
|
|
|
---@param sep string Separator or pattern
|
2023-03-20 08:12:33 +01:00
|
|
|
---@param opts (table|nil) Keyword arguments |kwargs| accepted by |vim.gsplit()|
|
2022-09-27 22:44:01 +02:00
|
|
|
---@return string[] List of split components
|
2023-03-20 08:12:33 +01:00
|
|
|
function vim.split(s, sep, opts)
|
feat: add trimempty optional parameter to vim.split
The `split()` VimL function trims empty items from the returned list by
default, so that, e.g.
split("\nhello\nworld\n\n", "\n")
returns
["hello", "world"]
The Lua implementation of vim.split does not do this. For example,
vim.split("\nhello\nworld\n\n", "\n")
returns
{'', 'hello', 'world', '', ''}
Add an optional parameter to the vim.split function that, when true,
trims these empty elements from the front and back of the returned
table. This is only possible for vim.split and not vim.gsplit; because
vim.gsplit is an iterator, there is no way for it to know if the current
item is the last non-empty item.
Note that in order to preserve backward compatibility, the parameter for
the Lua vim.split function is `trimempty`, while the VimL function uses
`keepempty` (i.e. they are opposites). This means there is a disconnect
between these two functions that may surprise users.
2021-07-29 14:48:04 -06:00
|
|
|
local t = {}
|
2023-03-20 08:12:33 +01:00
|
|
|
for c in vim.gsplit(s, sep, opts) do
|
|
|
|
|
table.insert(t, c)
|
feat: add trimempty optional parameter to vim.split
The `split()` VimL function trims empty items from the returned list by
default, so that, e.g.
split("\nhello\nworld\n\n", "\n")
returns
["hello", "world"]
The Lua implementation of vim.split does not do this. For example,
vim.split("\nhello\nworld\n\n", "\n")
returns
{'', 'hello', 'world', '', ''}
Add an optional parameter to the vim.split function that, when true,
trims these empty elements from the front and back of the returned
table. This is only possible for vim.split and not vim.gsplit; because
vim.gsplit is an iterator, there is no way for it to know if the current
item is the last non-empty item.
Note that in order to preserve backward compatibility, the parameter for
the Lua vim.split function is `trimempty`, while the VimL function uses
`keepempty` (i.e. they are opposites). This means there is a disconnect
between these two functions that may surprise users.
2021-07-29 14:48:04 -06:00
|
|
|
end
|
2019-05-19 18:31:40 +02:00
|
|
|
return t
|
|
|
|
|
end
|
|
|
|
|
|
2019-11-13 12:55:26 -08:00
|
|
|
--- Return a list of all keys used in a table.
|
|
|
|
|
--- However, the order of the return table of keys is not guaranteed.
|
|
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
|
2019-11-13 12:55:26 -08:00
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T: table
|
2022-10-24 21:53:53 +09:00
|
|
|
---@param t table<T, any> (table) Table
|
2022-09-28 13:22:08 +02:00
|
|
|
---@return T[] (list) List of keys
|
2019-11-13 12:55:26 -08:00
|
|
|
function vim.tbl_keys(t)
|
|
|
|
|
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
|
|
|
|
|
|
|
|
|
|
local keys = {}
|
|
|
|
|
for k, _ in pairs(t) do
|
|
|
|
|
table.insert(keys, k)
|
|
|
|
|
end
|
|
|
|
|
return keys
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Return a list of all values used in a table.
|
|
|
|
|
--- However, the order of the return table of values is not guaranteed.
|
|
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T
|
2022-09-28 13:22:08 +02:00
|
|
|
---@param t table<any, T> (table) Table
|
|
|
|
|
---@return T[] (list) List of values
|
2019-11-13 12:55:26 -08:00
|
|
|
function vim.tbl_values(t)
|
|
|
|
|
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
|
|
|
|
|
|
|
|
|
|
local values = {}
|
|
|
|
|
for _, v in pairs(t) do
|
|
|
|
|
table.insert(values, v)
|
|
|
|
|
end
|
|
|
|
|
return values
|
|
|
|
|
end
|
|
|
|
|
|
2020-02-18 17:41:29 +09:00
|
|
|
--- Apply a function to all values of a table.
|
|
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T
|
2022-09-28 13:22:08 +02:00
|
|
|
---@param func fun(value: T): any (function) Function
|
|
|
|
|
---@param t table<any, T> (table) Table
|
2022-05-11 18:23:46 +02:00
|
|
|
---@return table Table of transformed values
|
2020-02-18 17:41:29 +09:00
|
|
|
function vim.tbl_map(func, t)
|
|
|
|
|
vim.validate({ func = { func, 'c' }, t = { t, 't' } })
|
|
|
|
|
|
|
|
|
|
local rettab = {}
|
|
|
|
|
for k, v in pairs(t) do
|
|
|
|
|
rettab[k] = func(v)
|
|
|
|
|
end
|
|
|
|
|
return rettab
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Filter a table using a predicate function
|
|
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T
|
2022-09-28 13:22:08 +02:00
|
|
|
---@param func fun(value: T): boolean (function) Function
|
|
|
|
|
---@param t table<any, T> (table) Table
|
|
|
|
|
---@return T[] (table) Table of filtered values
|
2020-02-18 17:41:29 +09:00
|
|
|
function vim.tbl_filter(func, t)
|
|
|
|
|
vim.validate({ func = { func, 'c' }, t = { t, 't' } })
|
|
|
|
|
|
|
|
|
|
local rettab = {}
|
|
|
|
|
for _, entry in pairs(t) do
|
|
|
|
|
if func(entry) then
|
|
|
|
|
table.insert(rettab, entry)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return rettab
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-14 10:39:57 +02:00
|
|
|
--- Checks if a table contains a given value, specified either directly or via
|
|
|
|
|
--- a predicate that is checked for each value.
|
|
|
|
|
---
|
|
|
|
|
--- Example:
|
|
|
|
|
--- <pre>lua
|
|
|
|
|
--- vim.tbl_contains({ 'a', { 'b', 'c' } }, function(v)
|
|
|
|
|
--- return vim.deep_equal(v, { 'b', 'c' })
|
|
|
|
|
--- end, { predicate = true })
|
|
|
|
|
--- -- true
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
|
|
|
|
---@see |vim.list_contains()| for checking values in list-like tables
|
2019-05-19 17:58:54 +02:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param t table Table to check
|
2023-04-14 10:39:57 +02:00
|
|
|
---@param value any Value to compare or predicate function reference
|
|
|
|
|
---@param opts (table|nil) Keyword arguments |kwargs|:
|
|
|
|
|
--- - predicate: (boolean) `value` is a function reference to be checked (default false)
|
|
|
|
|
---@return boolean `true` if `t` contains `value`
|
|
|
|
|
function vim.tbl_contains(t, value, opts)
|
|
|
|
|
vim.validate({ t = { t, 't' }, opts = { opts, 't', true } })
|
|
|
|
|
|
|
|
|
|
local pred
|
|
|
|
|
if opts and opts.predicate then
|
|
|
|
|
vim.validate({ value = { value, 'c' } })
|
|
|
|
|
pred = value
|
|
|
|
|
else
|
|
|
|
|
pred = function(v)
|
|
|
|
|
return v == value
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for _, v in pairs(t) do
|
|
|
|
|
if pred(v) then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Checks if a list-like table (integer keys without gaps) contains `value`.
|
|
|
|
|
---
|
|
|
|
|
---@see |vim.tbl_contains()| for checking values in general tables
|
|
|
|
|
---
|
|
|
|
|
---@param t table Table to check (must be list-like, not validated)
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param value any Value to compare
|
|
|
|
|
---@return boolean `true` if `t` contains `value`
|
2023-04-14 10:39:57 +02:00
|
|
|
function vim.list_contains(t, value)
|
2019-11-10 22:18:59 -08:00
|
|
|
vim.validate({ t = { t, 't' } })
|
2019-10-21 23:46:28 +09:00
|
|
|
|
2019-05-18 16:00:06 +02:00
|
|
|
for _, v in ipairs(t) do
|
|
|
|
|
if v == value then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
2020-08-31 00:51:35 -07:00
|
|
|
--- Checks if a table is empty.
|
|
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see https://github.com/premake/premake-core/blob/master/src/base/table.lua
|
2020-08-31 00:51:35 -07:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param t table Table to check
|
|
|
|
|
---@return boolean `true` if `t` is empty
|
2019-11-13 12:55:26 -08:00
|
|
|
function vim.tbl_isempty(t)
|
|
|
|
|
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
|
|
|
|
|
return next(t) == nil
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-14 19:01:08 +09:00
|
|
|
--- We only merge empty tables or tables that are not an array (indexed by integers)
|
2021-08-22 14:55:28 -06:00
|
|
|
---@private
|
2021-07-19 17:52:44 +02:00
|
|
|
local function can_merge(v)
|
2023-04-14 19:01:08 +09:00
|
|
|
return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_isarray(v))
|
2021-07-19 17:52:44 +02:00
|
|
|
end
|
|
|
|
|
|
2020-05-18 02:24:34 +09:00
|
|
|
local function tbl_extend(behavior, deep_extend, ...)
|
2019-01-18 00:44:35 +01:00
|
|
|
if behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force' then
|
|
|
|
|
error('invalid "behavior": ' .. tostring(behavior))
|
|
|
|
|
end
|
2020-02-13 15:02:30 +09:00
|
|
|
|
|
|
|
|
if select('#', ...) < 2 then
|
|
|
|
|
error(
|
|
|
|
|
'wrong number of arguments (given '
|
|
|
|
|
.. tostring(1 + select('#', ...))
|
|
|
|
|
.. ', expected at least 3)'
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
2019-01-18 00:44:35 +01:00
|
|
|
local ret = {}
|
2020-02-13 15:02:30 +09:00
|
|
|
if vim._empty_dict_mt ~= nil and getmetatable(select(1, ...)) == vim._empty_dict_mt then
|
|
|
|
|
ret = vim.empty_dict()
|
|
|
|
|
end
|
|
|
|
|
|
2019-01-18 00:44:35 +01:00
|
|
|
for i = 1, select('#', ...) do
|
|
|
|
|
local tbl = select(i, ...)
|
2020-02-13 15:02:30 +09:00
|
|
|
vim.validate({ ['after the second argument'] = { tbl, 't' } })
|
2019-01-18 00:44:35 +01:00
|
|
|
if tbl then
|
|
|
|
|
for k, v in pairs(tbl) do
|
2021-07-19 17:52:44 +02:00
|
|
|
if deep_extend and can_merge(v) and can_merge(ret[k]) then
|
|
|
|
|
ret[k] = tbl_extend(behavior, true, ret[k], v)
|
2020-05-18 02:24:34 +09:00
|
|
|
elseif behavior ~= 'force' and ret[k] ~= nil then
|
2019-01-18 00:44:35 +01:00
|
|
|
if behavior == 'error' then
|
|
|
|
|
error('key found in more than one map: ' .. k)
|
|
|
|
|
end -- Else behavior is "keep".
|
|
|
|
|
else
|
|
|
|
|
ret[k] = v
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return ret
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-18 02:24:34 +09:00
|
|
|
--- Merges two or more map-like tables.
|
|
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see |extend()|
|
2020-05-18 02:24:34 +09:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param behavior string Decides what to do if a key is found in more than one map:
|
2020-05-18 02:24:34 +09:00
|
|
|
--- - "error": raise an error
|
|
|
|
|
--- - "keep": use value from the leftmost map
|
|
|
|
|
--- - "force": use value from the rightmost map
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param ... table Two or more map-like tables
|
|
|
|
|
---@return table Merged table
|
2020-05-18 02:24:34 +09:00
|
|
|
function vim.tbl_extend(behavior, ...)
|
|
|
|
|
return tbl_extend(behavior, false, ...)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Merges recursively two or more map-like tables.
|
|
|
|
|
---
|
2022-09-25 19:58:27 -04:00
|
|
|
---@see |vim.tbl_extend()|
|
2020-05-18 02:24:34 +09:00
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T1: table
|
|
|
|
|
---@generic T2: table
|
2022-09-28 13:22:08 +02:00
|
|
|
---@param behavior "error"|"keep"|"force" (string) Decides what to do if a key is found in more than one map:
|
2020-05-18 02:24:34 +09:00
|
|
|
--- - "error": raise an error
|
|
|
|
|
--- - "keep": use value from the leftmost map
|
|
|
|
|
--- - "force": use value from the rightmost map
|
2022-09-27 22:44:01 +02:00
|
|
|
---@param ... T2 Two or more map-like tables
|
2022-09-28 13:22:08 +02:00
|
|
|
---@return T1|T2 (table) Merged table
|
2020-05-18 02:24:34 +09:00
|
|
|
function vim.tbl_deep_extend(behavior, ...)
|
|
|
|
|
return tbl_extend(behavior, true, ...)
|
|
|
|
|
end
|
|
|
|
|
|
2019-11-13 12:55:26 -08:00
|
|
|
--- Deep compare values for equality
|
2021-09-10 08:22:40 -05:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
--- Tables are compared recursively unless they both provide the `eq` metamethod.
|
2021-09-10 08:22:40 -05:00
|
|
|
--- All other types are compared using the equality `==` operator.
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param a any First value
|
|
|
|
|
---@param b any Second value
|
|
|
|
|
---@return boolean `true` if values are equals, else `false`
|
2019-11-13 12:55:26 -08:00
|
|
|
function vim.deep_equal(a, b)
|
|
|
|
|
if a == b then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
if type(a) ~= type(b) then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
if type(a) == 'table' then
|
|
|
|
|
for k, v in pairs(a) do
|
|
|
|
|
if not vim.deep_equal(v, b[k]) then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-09-10 08:22:40 -05:00
|
|
|
for k, _ in pairs(b) do
|
|
|
|
|
if a[k] == nil then
|
2019-11-13 12:55:26 -08:00
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Add the reverse lookup values to an existing table.
|
|
|
|
|
--- For example:
|
|
|
|
|
--- ``tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }``
|
2022-05-11 18:23:46 +02:00
|
|
|
---
|
|
|
|
|
--- Note that this *modifies* the input.
|
|
|
|
|
---@param o table Table to add the reverse to
|
|
|
|
|
---@return table o
|
2019-11-13 12:55:26 -08:00
|
|
|
function vim.tbl_add_reverse_lookup(o)
|
|
|
|
|
local keys = vim.tbl_keys(o)
|
|
|
|
|
for _, k in ipairs(keys) do
|
|
|
|
|
local v = o[k]
|
|
|
|
|
if o[v] then
|
|
|
|
|
error(
|
|
|
|
|
string.format(
|
|
|
|
|
'The reverse lookup found an existing value for %q while processing key %q',
|
|
|
|
|
tostring(v),
|
|
|
|
|
tostring(k)
|
2022-05-09 11:23:51 +02:00
|
|
|
)
|
|
|
|
|
)
|
2019-11-13 12:55:26 -08:00
|
|
|
end
|
|
|
|
|
o[v] = k
|
|
|
|
|
end
|
|
|
|
|
return o
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-24 12:01:04 -07:00
|
|
|
--- Index into a table (first argument) via string keys passed as subsequent arguments.
|
|
|
|
|
--- Return `nil` if the key does not exist.
|
2022-05-11 18:23:46 +02:00
|
|
|
---
|
2022-03-24 12:01:04 -07:00
|
|
|
--- Examples:
|
2022-11-23 12:31:49 +01:00
|
|
|
--- <pre>lua
|
2022-03-24 12:01:04 -07:00
|
|
|
--- vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true
|
|
|
|
|
--- vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param o table Table to index
|
|
|
|
|
---@param ... string Optional strings (0 or more, variadic) via which to index the table
|
2022-03-24 12:01:04 -07:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@return any Nested value indexed by key (if it exists), else nil
|
2022-03-24 12:01:04 -07:00
|
|
|
function vim.tbl_get(o, ...)
|
|
|
|
|
local keys = { ... }
|
|
|
|
|
if #keys == 0 then
|
2022-12-15 02:27:23 +01:00
|
|
|
return nil
|
2022-03-24 12:01:04 -07:00
|
|
|
end
|
2022-05-01 21:08:05 +02:00
|
|
|
for i, k in ipairs(keys) do
|
2022-03-24 12:01:04 -07:00
|
|
|
o = o[k]
|
|
|
|
|
if o == nil then
|
2022-12-15 02:27:23 +01:00
|
|
|
return nil
|
|
|
|
|
elseif type(o) ~= 'table' and next(keys, i) then
|
|
|
|
|
return nil
|
2022-03-24 12:01:04 -07:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return o
|
|
|
|
|
end
|
|
|
|
|
|
2019-11-25 01:08:02 -08:00
|
|
|
--- Extends a list-like table with the values of another list-like table.
|
|
|
|
|
---
|
|
|
|
|
--- NOTE: This mutates dst!
|
|
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see |vim.tbl_extend()|
|
2019-11-25 01:08:02 -08:00
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T: table
|
|
|
|
|
---@param dst T List which will be modified and appended to
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param src table List from which values will be inserted
|
2023-03-04 22:07:39 +09:00
|
|
|
---@param start (integer|nil) Start index on src. Defaults to 1
|
|
|
|
|
---@param finish (integer|nil) Final index on src. Defaults to `#src`
|
2022-09-27 22:44:01 +02:00
|
|
|
---@return T dst
|
2019-11-20 17:09:21 -08:00
|
|
|
function vim.list_extend(dst, src, start, finish)
|
|
|
|
|
vim.validate({
|
|
|
|
|
dst = { dst, 't' },
|
|
|
|
|
src = { src, 't' },
|
|
|
|
|
start = { start, 'n', true },
|
|
|
|
|
finish = { finish, 'n', true },
|
|
|
|
|
})
|
|
|
|
|
for i = start or 1, finish or #src do
|
|
|
|
|
table.insert(dst, src[i])
|
2019-11-13 12:55:26 -08:00
|
|
|
end
|
|
|
|
|
return dst
|
|
|
|
|
end
|
|
|
|
|
|
2019-05-19 17:58:54 +02:00
|
|
|
--- Creates a copy of a list-like table such that any nested tables are
|
|
|
|
|
--- "unrolled" and appended to the result.
|
|
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua
|
2019-11-13 12:55:26 -08:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param t table List-like table
|
|
|
|
|
---@return table Flattened copy of the given list-like table
|
2019-10-23 10:50:42 +09:00
|
|
|
function vim.tbl_flatten(t)
|
2019-05-18 16:00:06 +02:00
|
|
|
local result = {}
|
|
|
|
|
local function _tbl_flatten(_t)
|
|
|
|
|
local n = #_t
|
|
|
|
|
for i = 1, n do
|
|
|
|
|
local v = _t[i]
|
|
|
|
|
if type(v) == 'table' then
|
|
|
|
|
_tbl_flatten(v)
|
|
|
|
|
elseif v then
|
|
|
|
|
table.insert(result, v)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
_tbl_flatten(t)
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
2023-01-23 10:26:46 +01:00
|
|
|
--- Enumerate a table sorted by its keys.
|
|
|
|
|
---
|
|
|
|
|
---@see Based on https://github.com/premake/premake-core/blob/master/src/base/table.lua
|
|
|
|
|
---
|
|
|
|
|
---@param t table List-like table
|
|
|
|
|
---@return iterator over sorted keys and their values
|
|
|
|
|
function vim.spairs(t)
|
|
|
|
|
assert(type(t) == 'table', string.format('Expected table, got %s', type(t)))
|
|
|
|
|
|
|
|
|
|
-- collect the keys
|
|
|
|
|
local keys = {}
|
|
|
|
|
for k in pairs(t) do
|
|
|
|
|
table.insert(keys, k)
|
|
|
|
|
end
|
|
|
|
|
table.sort(keys)
|
|
|
|
|
|
|
|
|
|
-- Return the iterator function.
|
|
|
|
|
-- TODO(justinmk): Return "iterator function, table {t}, and nil", like pairs()?
|
|
|
|
|
local i = 0
|
|
|
|
|
return function()
|
|
|
|
|
i = i + 1
|
|
|
|
|
if keys[i] then
|
|
|
|
|
return keys[i], t[keys[i]]
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-14 19:01:08 +09:00
|
|
|
--- Tests if a Lua table can be treated as an array (a table indexed by integers).
|
2020-07-02 07:09:17 -04:00
|
|
|
---
|
2020-08-31 00:51:35 -07:00
|
|
|
--- Empty table `{}` is assumed to be an array, unless it was created by
|
|
|
|
|
--- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result,
|
|
|
|
|
--- for example from |rpcrequest()| or |vim.fn|.
|
2019-11-13 12:55:26 -08:00
|
|
|
---
|
2023-04-14 19:01:08 +09:00
|
|
|
---@param t table
|
|
|
|
|
---@return boolean `true` if array-like table, else `false`.
|
|
|
|
|
function vim.tbl_isarray(t)
|
2019-11-13 12:55:26 -08:00
|
|
|
if type(t) ~= 'table' then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local count = 0
|
|
|
|
|
|
|
|
|
|
for k, _ in pairs(t) do
|
2023-04-14 19:01:08 +09:00
|
|
|
--- Check if the number k is an integer
|
|
|
|
|
if type(k) == 'number' and k == math.floor(k) then
|
2019-11-13 12:55:26 -08:00
|
|
|
count = count + 1
|
|
|
|
|
else
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if count > 0 then
|
|
|
|
|
return true
|
|
|
|
|
else
|
2019-11-27 20:45:41 +01:00
|
|
|
-- TODO(bfredl): in the future, we will always be inside nvim
|
|
|
|
|
-- then this check can be deleted.
|
|
|
|
|
if vim._empty_dict_mt == nil then
|
2022-10-24 21:53:53 +09:00
|
|
|
return false
|
2019-11-27 20:45:41 +01:00
|
|
|
end
|
|
|
|
|
return getmetatable(t) ~= vim._empty_dict_mt
|
2019-11-13 12:55:26 -08:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-14 19:01:08 +09:00
|
|
|
--- Tests if a Lua table can be treated as a list (a table indexed by consecutive integers starting from 1).
|
|
|
|
|
---
|
|
|
|
|
--- Empty table `{}` is assumed to be an list, unless it was created by
|
|
|
|
|
--- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result,
|
|
|
|
|
--- for example from |rpcrequest()| or |vim.fn|.
|
|
|
|
|
---
|
|
|
|
|
---@param t table
|
|
|
|
|
---@return boolean `true` if list-like table, else `false`.
|
|
|
|
|
function vim.tbl_islist(t)
|
|
|
|
|
if type(t) ~= 'table' then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local num_elem = vim.tbl_count(t)
|
|
|
|
|
|
|
|
|
|
if num_elem == 0 then
|
|
|
|
|
-- TODO(bfredl): in the future, we will always be inside nvim
|
|
|
|
|
-- then this check can be deleted.
|
|
|
|
|
if vim._empty_dict_mt == nil then
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
return getmetatable(t) ~= vim._empty_dict_mt
|
|
|
|
|
else
|
|
|
|
|
for i = 1, num_elem do
|
|
|
|
|
if t[i] == nil then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-03-02 16:38:43 +09:00
|
|
|
--- Counts the number of non-nil values in table `t`.
|
|
|
|
|
---
|
2022-11-23 12:31:49 +01:00
|
|
|
--- <pre>lua
|
|
|
|
|
--- vim.tbl_count({ a=1, b=2 }) --> 2
|
|
|
|
|
--- vim.tbl_count({ 1, 2 }) --> 2
|
2020-03-02 16:38:43 +09:00
|
|
|
--- </pre>
|
|
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param t table Table
|
2023-03-04 22:07:39 +09:00
|
|
|
---@return integer Number of non-nil values in table
|
2020-03-02 16:38:43 +09:00
|
|
|
function vim.tbl_count(t)
|
|
|
|
|
vim.validate({ t = { t, 't' } })
|
|
|
|
|
|
|
|
|
|
local count = 0
|
|
|
|
|
for _ in pairs(t) do
|
|
|
|
|
count = count + 1
|
|
|
|
|
end
|
|
|
|
|
return count
|
|
|
|
|
end
|
|
|
|
|
|
2021-03-07 17:18:32 -08:00
|
|
|
--- Creates a copy of a table containing only elements from start to end (inclusive)
|
|
|
|
|
---
|
2022-09-27 22:44:01 +02:00
|
|
|
---@generic T
|
2022-09-28 13:22:08 +02:00
|
|
|
---@param list T[] (list) Table
|
2023-03-04 22:07:39 +09:00
|
|
|
---@param start integer|nil Start range of slice
|
|
|
|
|
---@param finish integer|nil End range of slice
|
2022-09-28 13:22:08 +02:00
|
|
|
---@return T[] (list) Copy of table sliced from start to finish (inclusive)
|
2021-03-07 17:18:32 -08:00
|
|
|
function vim.list_slice(list, start, finish)
|
|
|
|
|
local new_list = {}
|
|
|
|
|
for i = start or 1, finish or #list do
|
|
|
|
|
new_list[#new_list + 1] = list[i]
|
|
|
|
|
end
|
|
|
|
|
return new_list
|
|
|
|
|
end
|
|
|
|
|
|
2019-05-25 10:00:41 +02:00
|
|
|
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
|
2019-05-20 23:06:14 +02:00
|
|
|
---
|
2023-01-16 06:32:23 +09:00
|
|
|
---@see |luaref-patterns|
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see https://www.lua.org/pil/20.2.html
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param s string String to trim
|
|
|
|
|
---@return string String with whitespace removed from its beginning and end
|
2019-10-23 10:50:42 +09:00
|
|
|
function vim.trim(s)
|
2019-11-10 22:18:59 -08:00
|
|
|
vim.validate({ s = { s, 's' } })
|
2019-05-20 23:06:14 +02:00
|
|
|
return s:match('^%s*(.*%S)') or ''
|
|
|
|
|
end
|
|
|
|
|
|
2022-08-08 18:58:32 +02:00
|
|
|
--- Escapes magic chars in |lua-patterns|.
|
2019-09-03 22:51:45 +02:00
|
|
|
---
|
2021-08-22 14:55:28 -06:00
|
|
|
---@see https://github.com/rxi/lume
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param s string String to escape
|
|
|
|
|
---@return string %-escaped pattern string
|
2019-10-23 10:50:42 +09:00
|
|
|
function vim.pesc(s)
|
2019-11-10 22:18:59 -08:00
|
|
|
vim.validate({ s = { s, 's' } })
|
2022-10-24 21:53:53 +09:00
|
|
|
return (s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1'))
|
2019-09-03 22:51:45 +02:00
|
|
|
end
|
|
|
|
|
|
2020-01-12 23:41:55 -08:00
|
|
|
--- Tests if `s` starts with `prefix`.
|
|
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param s string String
|
|
|
|
|
---@param prefix string Prefix to match
|
|
|
|
|
---@return boolean `true` if `prefix` is a prefix of `s`
|
2019-12-01 05:32:55 -08:00
|
|
|
function vim.startswith(s, prefix)
|
|
|
|
|
vim.validate({ s = { s, 's' }, prefix = { prefix, 's' } })
|
|
|
|
|
return s:sub(1, #prefix) == prefix
|
|
|
|
|
end
|
|
|
|
|
|
2020-01-12 23:41:55 -08:00
|
|
|
--- Tests if `s` ends with `suffix`.
|
|
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param s string String
|
|
|
|
|
---@param suffix string Suffix to match
|
|
|
|
|
---@return boolean `true` if `suffix` is a suffix of `s`
|
2019-12-01 05:32:55 -08:00
|
|
|
function vim.endswith(s, suffix)
|
|
|
|
|
vim.validate({ s = { s, 's' }, suffix = { suffix, 's' } })
|
|
|
|
|
return #suffix == 0 or s:sub(-#suffix) == suffix
|
|
|
|
|
end
|
|
|
|
|
|
2019-11-10 19:58:14 -08:00
|
|
|
--- Validates a parameter specification (types and values).
|
2019-10-28 20:52:18 +09:00
|
|
|
---
|
2019-11-10 22:18:59 -08:00
|
|
|
--- Usage example:
|
2022-11-23 12:31:49 +01:00
|
|
|
--- <pre>lua
|
2019-11-10 19:58:14 -08:00
|
|
|
--- function user.new(name, age, hobbies)
|
|
|
|
|
--- vim.validate{
|
|
|
|
|
--- name={name, 'string'},
|
|
|
|
|
--- age={age, 'number'},
|
|
|
|
|
--- hobbies={hobbies, 'table'},
|
|
|
|
|
--- }
|
|
|
|
|
--- ...
|
|
|
|
|
--- end
|
2019-11-10 22:18:59 -08:00
|
|
|
--- </pre>
|
|
|
|
|
---
|
|
|
|
|
--- Examples with explicit argument values (can be run directly):
|
2022-11-23 12:31:49 +01:00
|
|
|
--- <pre>lua
|
2019-11-10 22:18:59 -08:00
|
|
|
--- vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
|
2022-11-23 12:31:49 +01:00
|
|
|
--- --> NOP (success)
|
2019-11-10 22:18:59 -08:00
|
|
|
---
|
2019-11-10 19:58:14 -08:00
|
|
|
--- vim.validate{arg1={1, 'table'}}
|
2022-11-23 12:31:49 +01:00
|
|
|
--- --> error('arg1: expected table, got number')
|
2019-11-10 22:18:59 -08:00
|
|
|
---
|
2019-11-10 19:58:14 -08:00
|
|
|
--- vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
|
2022-11-23 12:31:49 +01:00
|
|
|
--- --> error('arg1: expected even number, got 3')
|
2019-10-28 20:52:18 +09:00
|
|
|
--- </pre>
|
|
|
|
|
---
|
2022-01-02 01:35:15 +06:00
|
|
|
--- If multiple types are valid they can be given as a list.
|
2022-11-23 12:31:49 +01:00
|
|
|
--- <pre>lua
|
2022-01-02 01:35:15 +06:00
|
|
|
--- vim.validate{arg1={{'foo'}, {'table', 'string'}}, arg2={'foo', {'table', 'string'}}}
|
2022-11-23 12:31:49 +01:00
|
|
|
--- --> NOP (success)
|
2022-01-02 01:35:15 +06:00
|
|
|
---
|
2023-04-04 19:07:33 +02:00
|
|
|
--- vim.validate{arg1={1, {'string', 'table'}}}
|
2022-11-23 12:31:49 +01:00
|
|
|
--- --> error('arg1: expected string|table, got number')
|
2022-01-02 01:35:15 +06:00
|
|
|
---
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param opt table Names of parameters to validate. Each key is a parameter
|
2019-11-10 19:58:14 -08:00
|
|
|
--- name; each value is a tuple in one of these forms:
|
2019-11-10 22:18:59 -08:00
|
|
|
--- 1. (arg_value, type_name, optional)
|
2019-11-10 19:58:14 -08:00
|
|
|
--- - arg_value: argument value
|
2022-01-02 01:35:15 +06:00
|
|
|
--- - type_name: string|table type name, one of: ("table", "t", "string",
|
2019-11-10 19:58:14 -08:00
|
|
|
--- "s", "number", "n", "boolean", "b", "function", "f", "nil",
|
2022-01-02 01:35:15 +06:00
|
|
|
--- "thread", "userdata") or list of them.
|
2019-11-10 19:58:14 -08:00
|
|
|
--- - optional: (optional) boolean, if true, `nil` is valid
|
2019-11-10 22:18:59 -08:00
|
|
|
--- 2. (arg_value, fn, msg)
|
2019-11-10 19:58:14 -08:00
|
|
|
--- - arg_value: argument value
|
|
|
|
|
--- - fn: any function accepting one argument, returns true if and
|
2020-03-26 14:24:04 -04:00
|
|
|
--- only if the argument is valid. Can optionally return an additional
|
|
|
|
|
--- informative error message as the second returned value.
|
2019-11-10 19:58:14 -08:00
|
|
|
--- - msg: (optional) error string if validation fails
|
|
|
|
|
function vim.validate(opt) end -- luacheck: no unused
|
2020-03-26 14:24:04 -04:00
|
|
|
|
|
|
|
|
do
|
2019-11-10 19:58:14 -08:00
|
|
|
local type_names = {
|
2020-03-26 14:24:04 -04:00
|
|
|
['table'] = 'table',
|
|
|
|
|
t = 'table',
|
|
|
|
|
['string'] = 'string',
|
|
|
|
|
s = 'string',
|
|
|
|
|
['number'] = 'number',
|
|
|
|
|
n = 'number',
|
|
|
|
|
['boolean'] = 'boolean',
|
|
|
|
|
b = 'boolean',
|
|
|
|
|
['function'] = 'function',
|
|
|
|
|
f = 'function',
|
|
|
|
|
['callable'] = 'callable',
|
|
|
|
|
c = 'callable',
|
|
|
|
|
['nil'] = 'nil',
|
|
|
|
|
['thread'] = 'thread',
|
|
|
|
|
['userdata'] = 'userdata',
|
2019-11-10 19:58:14 -08:00
|
|
|
}
|
2020-03-26 14:24:04 -04:00
|
|
|
|
2019-11-10 22:18:59 -08:00
|
|
|
local function _is_type(val, t)
|
lsp: vim.lsp.diagnostic (#12655)
Breaking Changes:
- Deprecated all `vim.lsp.util.{*diagnostics*}()` functions.
- Instead, all functions must be found in vim.lsp.diagnostic
- For now, they issue a warning ONCE per neovim session. In a
"little while" we will remove them completely.
- `vim.lsp.callbacks` has moved to `vim.lsp.handlers`.
- For a "little while" we will just redirect `vim.lsp.callbacks` to
`vim.lsp.handlers`. However, we will remove this at some point, so
it is recommended that you change all of your references to
`callbacks` into `handlers`.
- This also means that for functions like |vim.lsp.start_client()|
and similar, keyword style arguments have moved from "callbacks"
to "handlers". Once again, these are currently being forward, but
will cease to be forwarded in a "little while".
- Changed the highlight groups for LspDiagnostic highlight as they were
inconsistently named.
- For more information, see |lsp-highlight-diagnostics|
- Changed the sign group names as well, to be consistent with
|lsp-highlight-diagnostics|
General Enhancements:
- Rewrote much of the getting started help document for lsp. It also
provides a much nicer configuration strategy, so as to not recommend
globally overwriting builtin neovim mappings.
LSP Enhancements:
- Introduced the concept of |lsp-handlers| which will allow much better
customization for users without having to copy & paste entire files /
functions / etc.
Diagnostic Enhancements:
- "goto next diagnostic" |vim.lsp.diagnostic.goto_next()|
- "goto prev diagnostic" |vim.lsp.diagnostic.goto_prev()|
- For each of the gotos, auto open diagnostics is available as a
configuration option
- Configurable diagnostic handling:
- See |vim.lsp.diagnostic.on_publish_diagnostics()|
- Delay display until after insert mode
- Configure signs
- Configure virtual text
- Configure underline
- Set the location list with the buffers diagnostics.
- See |vim.lsp.diagnostic.set_loclist()|
- Better performance for getting counts and line diagnostics
- They are now cached on save, to enhance lookups.
- Particularly useful for checking in statusline, etc.
- Actual testing :)
- See ./test/functional/plugin/lsp/diagnostic_spec.lua
- Added `guisp` for underline highlighting
NOTE: "a little while" means enough time to feel like most plugins and
plugin authors have had a chance to refactor their code to use the
updated calls. Then we will remove them completely. There is no need to
keep them, because we don't have any released version of neovim that
exposes these APIs. I'm trying to be nice to people following HEAD :)
Co-authored: [Twitch Chat 2020](https://twitch.tv/teej_dv)
2020-11-12 22:21:34 -05:00
|
|
|
return type(val) == t or (t == 'callable' and vim.is_callable(val))
|
2019-10-28 20:52:18 +09:00
|
|
|
end
|
|
|
|
|
|
2021-11-29 20:31:19 -07:00
|
|
|
---@private
|
2020-03-26 14:24:04 -04:00
|
|
|
local function is_valid(opt)
|
|
|
|
|
if type(opt) ~= 'table' then
|
|
|
|
|
return false, string.format('opt: expected table, got %s', type(opt))
|
|
|
|
|
end
|
|
|
|
|
|
2019-11-10 19:58:14 -08:00
|
|
|
for param_name, spec in pairs(opt) do
|
2020-03-26 14:24:04 -04:00
|
|
|
if type(spec) ~= 'table' then
|
|
|
|
|
return false, string.format('opt[%s]: expected table, got %s', param_name, type(spec))
|
|
|
|
|
end
|
2019-10-28 20:52:18 +09:00
|
|
|
|
2022-05-11 18:23:46 +02:00
|
|
|
local val = spec[1] -- Argument value
|
|
|
|
|
local types = spec[2] -- Type name, or callable
|
2019-11-10 19:58:14 -08:00
|
|
|
local optional = (true == spec[3])
|
2019-10-28 20:52:18 +09:00
|
|
|
|
2022-01-02 01:35:15 +06:00
|
|
|
if type(types) == 'string' then
|
|
|
|
|
types = { types }
|
|
|
|
|
end
|
2020-03-26 14:24:04 -04:00
|
|
|
|
2022-01-02 01:35:15 +06:00
|
|
|
if vim.is_callable(types) then
|
2022-05-11 18:23:46 +02:00
|
|
|
-- Check user-provided validation function
|
2022-01-02 01:35:15 +06:00
|
|
|
local valid, optional_message = types(val)
|
2020-03-26 14:24:04 -04:00
|
|
|
if not valid then
|
2022-01-01 12:58:34 -07:00
|
|
|
local error_message =
|
|
|
|
|
string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val))
|
2020-09-12 19:04:22 -07:00
|
|
|
if optional_message ~= nil then
|
2020-03-26 14:24:04 -04:00
|
|
|
error_message = error_message .. string.format('. Info: %s', optional_message)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return false, error_message
|
2019-11-10 19:58:14 -08:00
|
|
|
end
|
2022-01-02 01:35:15 +06:00
|
|
|
elseif type(types) == 'table' then
|
|
|
|
|
local success = false
|
|
|
|
|
for i, t in ipairs(types) do
|
|
|
|
|
local t_name = type_names[t]
|
|
|
|
|
if not t_name then
|
|
|
|
|
return false, string.format('invalid type name: %s', t)
|
|
|
|
|
end
|
|
|
|
|
types[i] = t_name
|
|
|
|
|
|
|
|
|
|
if (optional and val == nil) or _is_type(val, t_name) then
|
|
|
|
|
success = true
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if not success then
|
|
|
|
|
return false,
|
|
|
|
|
string.format(
|
|
|
|
|
'%s: expected %s, got %s',
|
|
|
|
|
param_name,
|
|
|
|
|
table.concat(types, '|'),
|
|
|
|
|
type(val)
|
2022-07-07 18:27:18 +02:00
|
|
|
)
|
2022-01-02 01:35:15 +06:00
|
|
|
end
|
2020-03-26 14:24:04 -04:00
|
|
|
else
|
2022-01-02 01:35:15 +06:00
|
|
|
return false, string.format('invalid type name: %s', tostring(types))
|
2019-10-28 20:52:18 +09:00
|
|
|
end
|
|
|
|
|
end
|
2020-03-26 14:24:04 -04:00
|
|
|
|
|
|
|
|
return true, nil
|
2019-10-28 20:52:18 +09:00
|
|
|
end
|
|
|
|
|
|
2020-03-26 14:24:04 -04:00
|
|
|
function vim.validate(opt)
|
|
|
|
|
local ok, err_msg = is_valid(opt)
|
|
|
|
|
if not ok then
|
2021-11-06 08:26:10 -06:00
|
|
|
error(err_msg, 2)
|
2020-03-26 14:24:04 -04:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-11-10 22:18:59 -08:00
|
|
|
--- Returns true if object `f` can be called as a function.
|
2019-10-28 20:52:18 +09:00
|
|
|
---
|
2022-05-11 18:23:46 +02:00
|
|
|
---@param f any Any object
|
|
|
|
|
---@return boolean `true` if `f` is callable, else `false`
|
2019-10-28 20:52:18 +09:00
|
|
|
function vim.is_callable(f)
|
|
|
|
|
if type(f) == 'function' then
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
local m = getmetatable(f)
|
|
|
|
|
if m == nil then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
return type(m.__call) == 'function'
|
|
|
|
|
end
|
|
|
|
|
|
2022-09-07 08:39:56 +02:00
|
|
|
--- Creates a table whose members are automatically created when accessed, if they don't already
|
|
|
|
|
--- exist.
|
|
|
|
|
---
|
|
|
|
|
--- They mimic defaultdict in python.
|
|
|
|
|
---
|
2022-09-14 11:08:31 +02:00
|
|
|
--- If {create} is `nil`, this will create a defaulttable whose constructor function is
|
2022-09-07 08:39:56 +02:00
|
|
|
--- this function, effectively allowing to create nested tables on the fly:
|
|
|
|
|
---
|
2022-11-23 12:31:49 +01:00
|
|
|
--- <pre>lua
|
2022-09-07 08:39:56 +02:00
|
|
|
--- local a = vim.defaulttable()
|
|
|
|
|
--- a.b.c = 1
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
2023-04-01 08:02:58 -06:00
|
|
|
---@param create function?(key:any):any The function called to create a missing value.
|
2022-09-07 08:39:56 +02:00
|
|
|
---@return table Empty table with metamethod
|
|
|
|
|
function vim.defaulttable(create)
|
2023-04-01 08:02:58 -06:00
|
|
|
create = create or function(_)
|
|
|
|
|
return vim.defaulttable()
|
|
|
|
|
end
|
2022-09-07 08:39:56 +02:00
|
|
|
return setmetatable({}, {
|
|
|
|
|
__index = function(tbl, key)
|
2023-04-01 08:02:58 -06:00
|
|
|
rawset(tbl, key, create(key))
|
2022-09-07 08:39:56 +02:00
|
|
|
return rawget(tbl, key)
|
|
|
|
|
end,
|
|
|
|
|
})
|
|
|
|
|
end
|
|
|
|
|
|
2023-04-17 12:54:19 -06:00
|
|
|
--- Create an Iter |lua-iter| object from a table or iterator.
|
|
|
|
|
---
|
|
|
|
|
--- The input value can be a table or a function iterator (see |luaref-in|).
|
|
|
|
|
---
|
|
|
|
|
--- This function wraps the input value into an interface which allows chaining
|
|
|
|
|
--- multiple pipeline stages in an efficient manner. Each pipeline stage
|
|
|
|
|
--- receives as input the output values from the prior stage. The values used in
|
|
|
|
|
--- the first stage of the pipeline depend on the type passed to this function:
|
|
|
|
|
---
|
|
|
|
|
--- - List tables pass only the value of each element
|
|
|
|
|
--- - Non-list tables pass both the key and value of each element
|
|
|
|
|
--- - Function iterators pass all of the values returned by their respective
|
|
|
|
|
--- function
|
|
|
|
|
---
|
|
|
|
|
--- Examples:
|
|
|
|
|
--- <pre>lua
|
|
|
|
|
--- local it = vim.iter({ 1, 2, 3, 4, 5 })
|
|
|
|
|
--- it:map(function(v)
|
|
|
|
|
--- return v * 3
|
|
|
|
|
--- end)
|
|
|
|
|
--- it:rev()
|
|
|
|
|
--- it:skip(2)
|
|
|
|
|
--- it:totable()
|
|
|
|
|
--- -- { 9, 6, 3 }
|
|
|
|
|
---
|
|
|
|
|
--- vim.iter(ipairs({ 1, 2, 3, 4, 5 })):map(function(i, v)
|
|
|
|
|
--- if i > 2 then return v end
|
|
|
|
|
--- end):totable()
|
|
|
|
|
--- -- { 3, 4, 5 }
|
|
|
|
|
---
|
|
|
|
|
--- local it = vim.iter(vim.gsplit('1,2,3,4,5', ','))
|
|
|
|
|
--- it:map(function(s) return tonumber(s) end)
|
|
|
|
|
--- for i, d in it:enumerate() do
|
|
|
|
|
--- print(string.format("Column %d is %d", i, d))
|
|
|
|
|
--- end
|
|
|
|
|
--- -- Column 1 is 1
|
|
|
|
|
--- -- Column 2 is 2
|
|
|
|
|
--- -- Column 3 is 3
|
|
|
|
|
--- -- Column 4 is 4
|
|
|
|
|
--- -- Column 5 is 5
|
|
|
|
|
---
|
|
|
|
|
--- vim.iter({ a = 1, b = 2, c = 3, z = 26 }):any(function(k, v)
|
|
|
|
|
--- return k == 'z'
|
|
|
|
|
--- end)
|
|
|
|
|
--- -- true
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
|
|
|
|
---@see |lua-iter|
|
|
|
|
|
---
|
|
|
|
|
---@param src table|function Table or iterator.
|
|
|
|
|
---@return Iter @|lua-iter|
|
|
|
|
|
function vim.iter(src, ...)
|
|
|
|
|
local Iter = require('vim.iter')
|
|
|
|
|
return Iter.new(src, ...)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Collect an iterator into a table.
|
|
|
|
|
---
|
|
|
|
|
--- This is a convenience function that performs:
|
|
|
|
|
--- <pre>lua
|
|
|
|
|
--- vim.iter(f):totable()
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
|
|
|
|
---@param f function Iterator function
|
|
|
|
|
---@return table
|
|
|
|
|
function vim.totable(f, ...)
|
|
|
|
|
return vim.iter(f, ...):totable()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Filter a table or iterator.
|
|
|
|
|
---
|
|
|
|
|
--- This is a convenience function that performs:
|
|
|
|
|
--- <pre>lua
|
|
|
|
|
--- vim.iter(src):filter(f):totable()
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
|
|
|
|
---@see |Iter:filter()|
|
|
|
|
|
---
|
|
|
|
|
---@param f function(...):bool Filter function. Accepts the current iterator or table values as
|
|
|
|
|
--- arguments and returns true if those values should be kept in the
|
|
|
|
|
--- final table
|
|
|
|
|
---@param src table|function Table or iterator function to filter
|
|
|
|
|
---@return table
|
|
|
|
|
function vim.filter(f, src, ...)
|
|
|
|
|
return vim.iter(src, ...):filter(f):totable()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Map and filter a table or iterator.
|
|
|
|
|
---
|
|
|
|
|
--- This is a convenience function that performs:
|
|
|
|
|
--- <pre>lua
|
|
|
|
|
--- vim.iter(src):map(f):totable()
|
|
|
|
|
--- </pre>
|
|
|
|
|
---
|
|
|
|
|
---@see |Iter:map()|
|
|
|
|
|
---
|
|
|
|
|
---@param f function(...):?any Map function. Accepts the current iterator or table values as
|
|
|
|
|
--- arguments and returns one or more new values. Nil values are removed
|
|
|
|
|
--- from the final table.
|
|
|
|
|
---@param src table|function Table or iterator function to filter
|
|
|
|
|
---@return table
|
|
|
|
|
function vim.map(f, src, ...)
|
|
|
|
|
return vim.iter(src, ...):map(f):totable()
|
|
|
|
|
end
|
|
|
|
|
|
2019-10-23 10:50:42 +09:00
|
|
|
return vim
|