diff options
author | Josh Rahm <rahm@google.com> | 2022-07-18 19:37:18 +0000 |
---|---|---|
committer | Josh Rahm <rahm@google.com> | 2022-07-18 19:37:18 +0000 |
commit | 308e1940dcd64aa6c344c403d4f9e0dda58d9c5c (patch) | |
tree | 35fe43e01755e0f312650667004487a44d6b7941 /runtime/lua/vim/shared.lua | |
parent | 96a00c7c588b2f38a2424aeeb4ea3581d370bf2d (diff) | |
parent | e8c94697bcbe23a5c7b07c292b90a6b70aadfa87 (diff) | |
download | rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.gz rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.tar.bz2 rneovim-308e1940dcd64aa6c344c403d4f9e0dda58d9c5c.zip |
Merge remote-tracking branch 'upstream/master' into rahm
Diffstat (limited to 'runtime/lua/vim/shared.lua')
-rw-r--r-- | runtime/lua/vim/shared.lua | 323 |
1 files changed, 198 insertions, 125 deletions
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index e170befa4c..d6c3e25b3b 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -1,8 +1,10 @@ -- Functions shared by Nvim and its test-suite. -- --- The singular purpose of this module is to share code with the Nvim --- test-suite. If, in the future, Nvim itself is used to run the test-suite --- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua +-- 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) local vim = vim or {} @@ -13,8 +15,8 @@ local vim = vim or {} --- copied and will throw an error. --- ---@param orig table Table to copy ----@returns New table of copied keys and (nested) values. -function vim.deepcopy(orig) end -- luacheck: no unused +---@return table Table of copied keys and (nested) values. +function vim.deepcopy(orig) end -- luacheck: no unused vim.deepcopy = (function() local function _id(v) return v @@ -22,7 +24,9 @@ vim.deepcopy = (function() local deepcopy_funcs = { table = function(orig, cache) - if cache[orig] then return cache[orig] end + if cache[orig] then + return cache[orig] + end local copy = {} cache[orig] = copy @@ -44,7 +48,7 @@ vim.deepcopy = (function() if f then return f(orig, cache or {}) else - error("Cannot deepcopy object of type "..type(orig)) + error('Cannot deepcopy object of type ' .. type(orig)) end end end)() @@ -55,19 +59,19 @@ end)() ---@see https://www.lua.org/pil/20.2.html ---@see http://lua-users.org/wiki/StringLibraryTutorial --- ----@param s String to split ----@param sep Separator string or pattern ----@param plain If `true` use `sep` literally (passed to String.find) ----@returns Iterator over the split components +---@param s string String to split +---@param sep string Separator or pattern +---@param plain boolean If `true` use `sep` literally (passed to string.find) +---@return function Iterator over the split components function vim.gsplit(s, sep, plain) - vim.validate{s={s,'s'},sep={sep,'s'},plain={plain,'b',true}} + vim.validate({ s = { s, 's' }, sep = { sep, 's' }, plain = { plain, 'b', true } }) local start = 1 local done = false local function _pass(i, j, ...) if i then - assert(j+1 > start, "Infinite loop detected") + assert(j + 1 > start, 'Infinite loop detected') local seg = s:sub(start, i - 1) start = j + 1 return seg, ... @@ -85,7 +89,7 @@ function vim.gsplit(s, sep, plain) if start == #s then done = true end - return _pass(start+1, start) + return _pass(start + 1, start) end return _pass(s:find(sep, start, plain)) end @@ -103,13 +107,13 @@ end --- ---@see |vim.gsplit()| --- ----@param s String to split ----@param sep Separator string or pattern ----@param kwargs Keyword arguments: +---@param s string String to split +---@param sep string Separator or pattern +---@param kwargs table Keyword arguments: --- - plain: (boolean) If `true` use `sep` literally (passed to string.find) --- - trimempty: (boolean) If `true` remove empty items from the front --- and back of the list ----@returns List-like table of the split components. +---@return table List of split components function vim.split(s, sep, kwargs) local plain local trimempty = false @@ -117,7 +121,7 @@ function vim.split(s, sep, kwargs) -- Support old signature for backward compatibility plain = kwargs else - vim.validate { kwargs = {kwargs, 't', true} } + vim.validate({ kwargs = { kwargs, 't', true } }) kwargs = kwargs or {} plain = kwargs.plain trimempty = kwargs.trimempty @@ -126,7 +130,7 @@ function vim.split(s, sep, kwargs) local t = {} local skip = trimempty for c in vim.gsplit(s, sep, plain) do - if c ~= "" then + if c ~= '' then skip = false end @@ -137,7 +141,7 @@ function vim.split(s, sep, kwargs) if trimempty then for i = #t, 1, -1 do - if t[i] ~= "" then + if t[i] ~= '' then break end table.remove(t, i) @@ -152,10 +156,10 @@ end --- ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ----@param t Table ----@returns list of keys +---@param t table Table +---@return table List of keys function vim.tbl_keys(t) - assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) local keys = {} for k, _ in pairs(t) do @@ -167,10 +171,10 @@ end --- Return a list of all values used in a table. --- However, the order of the return table of values is not guaranteed. --- ----@param t Table ----@returns list of values +---@param t table Table +---@return table List of values function vim.tbl_values(t) - assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) local values = {} for _, v in pairs(t) do @@ -181,10 +185,11 @@ end --- Apply a function to all values of a table. --- ----@param func function or callable table ----@param t table +---@param func function|table Function or callable table +---@param t table Table +---@return table Table of transformed values function vim.tbl_map(func, t) - vim.validate{func={func,'c'},t={t,'t'}} + vim.validate({ func = { func, 'c' }, t = { t, 't' } }) local rettab = {} for k, v in pairs(t) do @@ -195,10 +200,11 @@ end --- Filter a table using a predicate function --- ----@param func function or callable table ----@param t table +---@param func function|table Function or callable table +---@param t table Table +---@return table Table of filtered values function vim.tbl_filter(func, t) - vim.validate{func={func,'c'},t={t,'t'}} + vim.validate({ func = { func, 'c' }, t = { t, 't' } }) local rettab = {} for _, entry in pairs(t) do @@ -211,13 +217,13 @@ end --- Checks if a list-like (vector) table contains `value`. --- ----@param t Table to check ----@param value Value to compare ----@returns true if `t` contains `value` +---@param t table Table to check +---@param value any Value to compare +---@return boolean `true` if `t` contains `value` function vim.tbl_contains(t, value) - vim.validate{t={t,'t'}} + vim.validate({ t = { t, 't' } }) - for _,v in ipairs(t) do + for _, v in ipairs(t) do if v == value then return true end @@ -229,25 +235,30 @@ end --- ---@see https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ----@param t Table to check +---@param t table Table to check +---@return boolean `true` if `t` is empty function vim.tbl_isempty(t) - assert(type(t) == 'table', string.format("Expected table, got %s", type(t))) + assert(type(t) == 'table', string.format('Expected table, got %s', type(t))) return next(t) == nil end ---- we only merge empty tables or tables that are not a list +--- We only merge empty tables or tables that are not a list ---@private local function can_merge(v) - return type(v) == "table" and (vim.tbl_isempty(v) or not vim.tbl_islist(v)) + return type(v) == 'table' and (vim.tbl_isempty(v) or not vim.tbl_islist(v)) end local function tbl_extend(behavior, deep_extend, ...) - if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then - error('invalid "behavior": '..tostring(behavior)) + if behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force' then + error('invalid "behavior": ' .. tostring(behavior)) end if select('#', ...) < 2 then - error('wrong number of arguments (given '..tostring(1 + select('#', ...))..', expected at least 3)') + error( + 'wrong number of arguments (given ' + .. tostring(1 + select('#', ...)) + .. ', expected at least 3)' + ) end local ret = {} @@ -257,15 +268,15 @@ local function tbl_extend(behavior, deep_extend, ...) for i = 1, select('#', ...) do local tbl = select(i, ...) - vim.validate{["after the second argument"] = {tbl,'t'}} + vim.validate({ ['after the second argument'] = { tbl, 't' } }) if tbl then for k, v in pairs(tbl) do if deep_extend and can_merge(v) and can_merge(ret[k]) then ret[k] = tbl_extend(behavior, true, ret[k], v) elseif behavior ~= 'force' and ret[k] ~= nil then if behavior == 'error' then - error('key found in more than one map: '..k) - end -- Else behavior is "keep". + error('key found in more than one map: ' .. k) + end -- Else behavior is "keep". else ret[k] = v end @@ -279,11 +290,12 @@ end --- ---@see |extend()| --- ----@param behavior Decides what to do if a key is found in more than one map: +---@param behavior string Decides what to do if a key is found in more than one map: --- - "error": raise an error --- - "keep": use value from the leftmost map --- - "force": use value from the rightmost map ----@param ... Two or more map-like tables. +---@param ... table Two or more map-like tables +---@return table Merged table function vim.tbl_extend(behavior, ...) return tbl_extend(behavior, false, ...) end @@ -292,25 +304,30 @@ end --- ---@see |tbl_extend()| --- ----@param behavior Decides what to do if a key is found in more than one map: +---@param behavior string Decides what to do if a key is found in more than one map: --- - "error": raise an error --- - "keep": use value from the leftmost map --- - "force": use value from the rightmost map ----@param ... Two or more map-like tables. +---@param ... table Two or more map-like tables +---@return table Merged table function vim.tbl_deep_extend(behavior, ...) return tbl_extend(behavior, true, ...) end --- Deep compare values for equality --- ---- Tables are compared recursively unless they both provide the `eq` methamethod. +--- Tables are compared recursively unless they both provide the `eq` metamethod. --- All other types are compared using the equality `==` operator. ----@param a first value ----@param b second value ----@returns `true` if values are equals, else `false`. +---@param a any First value +---@param b any Second value +---@return boolean `true` if values are equals, else `false` function vim.deep_equal(a, b) - if a == b then return true end - if type(a) ~= type(b) then return false end + 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 @@ -329,40 +346,77 @@ end --- Add the reverse lookup values to an existing table. --- For example: ---- `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }` --- ---Do note that it *modifies* the input. ----@param o table The table to add the reverse to. +--- ``tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A = 1 }`` +--- +--- Note that this *modifies* the input. +---@param o table Table to add the reverse to +---@return table o 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))) + error( + string.format( + 'The reverse lookup found an existing value for %q while processing key %q', + tostring(v), + tostring(k) + ) + ) end o[v] = k end return o end +--- Index into a table (first argument) via string keys passed as subsequent arguments. +--- Return `nil` if the key does not exist. +--- +--- Examples: +--- <pre> +--- vim.tbl_get({ key = { nested_key = true }}, 'key', 'nested_key') == true +--- vim.tbl_get({ key = {}}, 'key', 'nested_key') == nil +--- </pre> +--- +---@param o table Table to index +---@param ... string Optional strings (0 or more, variadic) via which to index the table +--- +---@return any Nested value indexed by key (if it exists), else nil +function vim.tbl_get(o, ...) + local keys = { ... } + if #keys == 0 then + return + end + for i, k in ipairs(keys) do + if type(o[k]) ~= 'table' and next(keys, i) then + return nil + end + o = o[k] + if o == nil then + return + end + end + return o +end + --- Extends a list-like table with the values of another list-like table. --- --- NOTE: This mutates dst! --- ---@see |vim.tbl_extend()| --- ----@param dst list which will be modified and appended to. ----@param src list from which values will be inserted. ----@param start Start index on src. defaults to 1 ----@param finish Final index on src. defaults to #src ----@returns dst +---@param dst table List which will be modified and appended to +---@param src table List from which values will be inserted +---@param start number Start index on src. Defaults to 1 +---@param finish number Final index on src. Defaults to `#src` +---@return table dst function vim.list_extend(dst, src, start, finish) - vim.validate { - dst = {dst, 't'}; - src = {src, 't'}; - start = {start, 'n', true}; - finish = {finish, 'n', true}; - } + 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]) end @@ -374,15 +428,15 @@ end --- ---@see From https://github.com/premake/premake-core/blob/master/src/base/table.lua --- ----@param t List-like table ----@returns Flattened copy of the given list-like table. +---@param t table List-like table +---@return table Flattened copy of the given list-like table function vim.tbl_flatten(t) local result = {} local function _tbl_flatten(_t) local n = #_t for i = 1, n do local v = _t[i] - if type(v) == "table" then + if type(v) == 'table' then _tbl_flatten(v) elseif v then table.insert(result, v) @@ -399,8 +453,8 @@ end --- |vim.empty_dict()| or returned as a dict-like |API| or Vimscript result, --- for example from |rpcrequest()| or |vim.fn|. --- ----@param t Table ----@returns `true` if array-like table, else `false`. +---@param t table Table +---@return boolean `true` if array-like table, else `false` function vim.tbl_islist(t) if type(t) ~= 'table' then return false @@ -409,7 +463,7 @@ function vim.tbl_islist(t) local count = 0 for k, _ in pairs(t) do - if type(k) == "number" then + if type(k) == 'number' then count = count + 1 else return false @@ -436,26 +490,28 @@ end --- </pre> --- ---@see https://github.com/Tieske/Penlight/blob/master/lua/pl/tablex.lua ----@param t Table ----@returns Number that is the number of the value in table +---@param t table Table +---@return number Number of non-nil values in table function vim.tbl_count(t) - vim.validate{t={t,'t'}} + vim.validate({ t = { t, 't' } }) local count = 0 - for _ in pairs(t) do count = count + 1 end + for _ in pairs(t) do + count = count + 1 + end return count end --- Creates a copy of a table containing only elements from start to end (inclusive) --- ----@param list table table ----@param start integer Start range of slice ----@param finish integer End range of slice ----@returns Copy of table sliced from start to finish (inclusive) +---@param list table Table +---@param start number Start range of slice +---@param finish number End range of slice +---@return table Copy of table sliced from start to finish (inclusive) 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] + new_list[#new_list + 1] = list[i] end return new_list end @@ -463,40 +519,40 @@ end --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- ---@see https://www.lua.org/pil/20.2.html ----@param s String to trim ----@returns String with whitespace removed from its beginning and end +---@param s string String to trim +---@return string String with whitespace removed from its beginning and end function vim.trim(s) - vim.validate{s={s,'s'}} + vim.validate({ s = { s, 's' } }) return s:match('^%s*(.*%S)') or '' end --- Escapes magic chars in a Lua pattern. --- ---@see https://github.com/rxi/lume ----@param s String to escape ----@returns %-escaped pattern string +---@param s string String to escape +---@return string %-escaped pattern string function vim.pesc(s) - vim.validate{s={s,'s'}} + vim.validate({ s = { s, 's' } }) return s:gsub('[%(%)%.%%%+%-%*%?%[%]%^%$]', '%%%1') end --- Tests if `s` starts with `prefix`. --- ----@param s (string) a string ----@param prefix (string) a prefix ----@return (boolean) true if `prefix` is a prefix of s +---@param s string String +---@param prefix string Prefix to match +---@return boolean `true` if `prefix` is a prefix of `s` function vim.startswith(s, prefix) - vim.validate { s = {s, 's'}; prefix = {prefix, 's'}; } + vim.validate({ s = { s, 's' }, prefix = { prefix, 's' } }) return s:sub(1, #prefix) == prefix end --- Tests if `s` ends with `suffix`. --- ----@param s (string) a string ----@param suffix (string) a suffix ----@return (boolean) true if `suffix` is a suffix of s +---@param s string String +---@param suffix string Suffix to match +---@return boolean `true` if `suffix` is a suffix of `s` function vim.endswith(s, suffix) - vim.validate { s = {s, 's'}; suffix = {suffix, 's'}; } + vim.validate({ s = { s, 's' }, suffix = { suffix, 's' } }) return #suffix == 0 or s:sub(-#suffix) == suffix end @@ -536,7 +592,7 @@ end --- --- </pre> --- ----@param opt table of parameter names to validations. Each key is a parameter +---@param opt table Names of parameters to validate. Each key is a parameter --- name; each value is a tuple in one of these forms: --- 1. (arg_value, type_name, optional) --- - arg_value: argument value @@ -550,18 +606,24 @@ end --- only if the argument is valid. Can optionally return an additional --- informative error message as the second returned value. --- - msg: (optional) error string if validation fails -function vim.validate(opt) end -- luacheck: no unused +function vim.validate(opt) end -- luacheck: no unused do local type_names = { - ['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', + ['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', } @@ -580,21 +642,22 @@ do return false, string.format('opt[%s]: expected table, got %s', param_name, type(spec)) end - local val = spec[1] -- Argument value. - local types = spec[2] -- Type name, or callable. + local val = spec[1] -- Argument value + local types = spec[2] -- Type name, or callable local optional = (true == spec[3]) if type(types) == 'string' then - types = {types} + types = { types } end if vim.is_callable(types) then - -- Check user-provided validation function. + -- Check user-provided validation function local valid, optional_message = types(val) if not valid then - local error_message = string.format("%s: expected %s, got %s", param_name, (spec[3] or '?'), tostring(val)) + local error_message = + string.format('%s: expected %s, got %s', param_name, (spec[3] or '?'), tostring(val)) if optional_message ~= nil then - error_message = error_message .. string.format(". Info: %s", optional_message) + error_message = error_message .. string.format('. Info: %s', optional_message) end return false, error_message @@ -614,10 +677,16 @@ do end end if not success then - return false, string.format("%s: expected %s, got %s", param_name, table.concat(types, '|'), type(val)) + return false, + string.format( + '%s: expected %s, got %s', + param_name, + table.concat(types, '|'), + type(val) + ) end else - return false, string.format("invalid type name: %s", tostring(types)) + return false, string.format('invalid type name: %s', tostring(types)) end end @@ -633,12 +702,16 @@ do end --- Returns true if object `f` can be called as a function. --- ----@param f Any object ----@return true if `f` is callable, else false +---@param f any Any object +---@return boolean `true` if `f` is callable, else `false` function vim.is_callable(f) - if type(f) == 'function' then return true end + if type(f) == 'function' then + return true + end local m = getmetatable(f) - if m == nil then return false end + if m == nil then + return false + end return type(m.__call) == 'function' end |