diff options
-rw-r--r-- | runtime/lua/vim/shared.lua | 41 | ||||
-rw-r--r-- | test/functional/lua/vim_spec.lua | 99 |
2 files changed, 129 insertions, 11 deletions
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index d18fcfaf95..2135bfc837 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -200,16 +200,7 @@ function vim.tbl_isempty(t) return next(t) == nil end ---- Merges two or more map-like tables. ---- ---@see |extend()| ---- ---@param behavior 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. -function vim.tbl_extend(behavior, ...) +local function tbl_extend(behavior, deep_extend, ...) if (behavior ~= 'error' and behavior ~= 'keep' and behavior ~= 'force') then error('invalid "behavior": '..tostring(behavior)) end @@ -228,7 +219,9 @@ function vim.tbl_extend(behavior, ...) vim.validate{["after the second argument"] = {tbl,'t'}} if tbl then for k, v in pairs(tbl) do - if behavior ~= 'force' and ret[k] ~= nil then + if type(v) == 'table' and deep_extend and not vim.tbl_islist(v) then + ret[k] = tbl_extend(behavior, true, ret[k] or vim.empty_dict(), 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". @@ -241,6 +234,32 @@ function vim.tbl_extend(behavior, ...) return ret end +--- Merges two or more map-like tables. +--- +--@see |extend()| +--- +--@param behavior 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. +function vim.tbl_extend(behavior, ...) + return tbl_extend(behavior, false, ...) +end + +--- Merges recursively two or more map-like tables. +--- +--@see |tbl_extend()| +--- +--@param behavior 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. +function vim.tbl_deep_extend(behavior, ...) + return tbl_extend(behavior, true, ...) +end + --- Deep compare values for equality function vim.deep_equal(a, b) if a == b then return true end diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 79d523b5c6..46ae56955b 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -478,6 +478,17 @@ describe('lua stdlib', function() return vim.tbl_islist(c) and vim.tbl_count(c) == 0 ]])) + ok(exec_lua([[ + local a = {x = {a = 1, b = 2}} + local b = {x = {a = 2, c = {y = 3}}} + local c = vim.tbl_extend("keep", a, b) + + local count = 0 + for _ in pairs(c) do count = count + 1 end + + return c.x.a == 1 and c.x.b == 2 and c.x.c == nil and count == 1 + ]])) + eq('Error executing lua: .../shared.lua: invalid "behavior": nil', pcall_err(exec_lua, [[ return vim.tbl_extend() @@ -497,6 +508,94 @@ describe('lua stdlib', function() ) end) + it('vim.tbl_deep_extend', function() + ok(exec_lua([[ + local a = {x = {a = 1, b = 2}} + local b = {x = {a = 2, c = {y = 3}}} + local c = vim.tbl_deep_extend("keep", a, b) + + local count = 0 + for _ in pairs(c) do count = count + 1 end + + return c.x.a == 1 and c.x.b == 2 and c.x.c.y == 3 and count == 1 + ]])) + + ok(exec_lua([[ + local a = {x = {a = 1, b = 2}} + local b = {x = {a = 2, c = {y = 3}}} + local c = vim.tbl_deep_extend("force", a, b) + + local count = 0 + for _ in pairs(c) do count = count + 1 end + + return c.x.a == 2 and c.x.b == 2 and c.x.c.y == 3 and count == 1 + ]])) + + ok(exec_lua([[ + local a = {x = {a = 1, b = 2}} + local b = {x = {a = 2, c = {y = 3}}} + local c = {x = {c = 4, d = {y = 4}}} + local d = vim.tbl_deep_extend("keep", a, b, c) + + local count = 0 + for _ in pairs(c) do count = count + 1 end + + return d.x.a == 1 and d.x.b == 2 and d.x.c.y == 3 and d.x.d.y == 4 and count == 1 + ]])) + + ok(exec_lua([[ + local a = {x = {a = 1, b = 2}} + local b = {x = {a = 2, c = {y = 3}}} + local c = {x = {c = 4, d = {y = 4}}} + local d = vim.tbl_deep_extend("force", a, b, c) + + local count = 0 + for _ in pairs(c) do count = count + 1 end + + return d.x.a == 2 and d.x.b == 2 and d.x.c == 4 and d.x.d.y == 4 and count == 1 + ]])) + + ok(exec_lua([[ + local a = vim.empty_dict() + local b = {} + local c = vim.tbl_deep_extend("keep", a, b) + + local count = 0 + for _ in pairs(c) do count = count + 1 end + + return not vim.tbl_islist(c) and count == 0 + ]])) + + ok(exec_lua([[ + local a = {} + local b = vim.empty_dict() + local c = vim.tbl_deep_extend("keep", a, b) + + local count = 0 + for _ in pairs(c) do count = count + 1 end + + return vim.tbl_islist(c) and count == 0 + ]])) + + eq('Error executing lua: .../shared.lua: invalid "behavior": nil', + pcall_err(exec_lua, [[ + return vim.tbl_deep_extend() + ]]) + ) + + eq('Error executing lua: .../shared.lua: wrong number of arguments (given 1, expected at least 3)', + pcall_err(exec_lua, [[ + return vim.tbl_deep_extend("keep") + ]]) + ) + + eq('Error executing lua: .../shared.lua: wrong number of arguments (given 2, expected at least 3)', + pcall_err(exec_lua, [[ + return vim.tbl_deep_extend("keep", {}) + ]]) + ) + end) + it('vim.tbl_count', function() eq(0, exec_lua [[ return vim.tbl_count({}) ]]) eq(0, exec_lua [[ return vim.tbl_count(vim.empty_dict()) ]]) |