diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua index 1a6d439..fbd676d 100644 --- a/lua/lazy/core/plugin.lua +++ b/lua/lazy/core/plugin.lua @@ -50,6 +50,7 @@ local M = {} ---@class LazySpecLoader ---@field plugins table +---@field errors string[] local Spec = {} M.Spec = Spec @@ -57,6 +58,7 @@ M.Spec = Spec function Spec.new(spec) local self = setmetatable({}, { __index = Spec }) self.plugins = {} + self.errors = {} if spec then self:normalize(spec) end @@ -100,7 +102,7 @@ function Spec:add(plugin, is_dep) plugin.dir = Config.options.root .. "/" .. plugin.name end else - Util.error("Invalid plugin spec " .. vim.inspect(plugin)) + self:error("Invalid plugin spec " .. vim.inspect(plugin)) end plugin.event = type(plugin.event) == "string" and { plugin.event } or plugin.event @@ -116,13 +118,23 @@ function Spec:add(plugin, is_dep) return self.plugins[plugin.name] end +function Spec:error(error) + self.errors[#self.errors + 1] = error + Util.error(error) +end + ---@param spec LazySpec ---@param results? string[] ---@param is_dep? boolean function Spec:normalize(spec, results, is_dep) results = results or {} if type(spec) == "string" then - table.insert(results, self:add({ spec }, is_dep).name) + if is_dep and not spec:find("/", 1, true) then + -- spec is a plugin name + table.insert(results, spec) + else + table.insert(results, self:add({ spec }, is_dep).name) + end elseif #spec > 1 or Util.is_list(spec) then ---@cast spec LazySpec[] for _, s in ipairs(spec) do @@ -160,7 +172,7 @@ function Spec:merge(old, new) ---@diagnostic disable-next-line: no-unknown old[k] = values else - Util.error("Merging plugins is not supported for key `" .. k .. "`\n" .. vim.inspect({ old = old, new = new })) + self:error("Merging plugins is not supported for key `" .. k .. "`\n" .. vim.inspect({ old = old, new = new })) end else ---@diagnostic disable-next-line: no-unknown diff --git a/tests/core/plugin_spec.lua b/tests/core/plugin_spec.lua index 7973414..132d096 100644 --- a/tests/core/plugin_spec.lua +++ b/tests/core/plugin_spec.lua @@ -26,6 +26,7 @@ describe("plugin spec url/name", function() end local spec = Plugin.Spec.new(test[1]) local plugins = vim.tbl_values(spec.plugins) + assert(#spec.errors == 0) assert.equal(1, #plugins) assert.same(test[2], plugins[1]) end) @@ -41,7 +42,8 @@ describe("plugin spec opt", function() { { { "foo/bar", dependencies = { { "foo/dep1" }, "foo/dep2" } } } }, } for _, test in ipairs(tests) do - local spec = Plugin.Spec.new(test) + local spec = Plugin.Spec.new(vim.deepcopy(test)) + assert(#spec.errors == 0) Config.plugins = spec.plugins Plugin.update_state() assert(vim.tbl_count(spec.plugins) == 3) @@ -52,12 +54,168 @@ describe("plugin spec opt", function() assert(spec.plugins.dep1.lazy == true) assert(spec.plugins.dep2._.dep == true) assert(spec.plugins.dep2.lazy == true) + spec = Plugin.Spec.new(test) + for _, plugin in pairs(spec.plugins) do + plugin.dir = nil + end + assert.same(spec.plugins, { + bar = { + "foo/bar", + _ = {}, + dependencies = { "dep1", "dep2" }, + name = "bar", + url = "https://github.com/foo/bar.git", + }, + dep1 = { + "foo/dep1", + _ = { + dep = true, + }, + name = "dep1", + url = "https://github.com/foo/dep1.git", + }, + dep2 = { + "foo/dep2", + _ = { + dep = true, + }, + name = "dep2", + url = "https://github.com/foo/dep2.git", + }, + }) end end) + describe("deps", function() + it("handles dep names", function() + Config.options.defaults.lazy = false + local tests = { + { { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } }, "foo/dep1" }, + { "foo/dep1", { "foo/bar", dependencies = { { "dep1" }, "foo/dep2" } } }, + } + for _, test in ipairs(tests) do + local spec = Plugin.Spec.new(vim.deepcopy(test)) + assert(#spec.errors == 0) + Config.plugins = spec.plugins + Plugin.update_state() + spec = Plugin.Spec.new(test) + for _, plugin in pairs(spec.plugins) do + plugin.dir = nil + end + assert.same(spec.plugins, { + bar = { + "foo/bar", + _ = {}, + dependencies = { "dep1", "dep2" }, + name = "bar", + url = "https://github.com/foo/bar.git", + }, + dep1 = { + "foo/dep1", + _ = {}, + name = "dep1", + url = "https://github.com/foo/dep1.git", + }, + dep2 = { + "foo/dep2", + _ = { + dep = true, + }, + name = "dep2", + url = "https://github.com/foo/dep2.git", + }, + }) + end + end) + + it("handles opt from dep", function() + Config.options.defaults.lazy = false + local spec = Plugin.Spec.new({ "foo/dep1", { "foo/bar", dependencies = { "foo/dep1", "foo/dep2" } } }) + assert(#spec.errors == 0) + Config.plugins = spec.plugins + Plugin.update_state() + assert.same(3, vim.tbl_count(spec.plugins)) + assert(spec.plugins.bar._.dep ~= true) + assert(spec.plugins.bar.lazy == false) + assert(spec.plugins.dep2._.dep == true) + assert(spec.plugins.dep2.lazy == true) + assert(spec.plugins.dep1._.dep ~= true) + assert(spec.plugins.dep1.lazy == false) + end) + + it("handles defaults opt", function() + do + Config.options.defaults.lazy = true + local spec = Plugin.Spec.new({ "foo/bar" }) + assert(#spec.errors == 0) + Config.plugins = spec.plugins + Plugin.update_state() + assert(spec.plugins.bar.lazy == true) + end + do + Config.options.defaults.lazy = false + local spec = Plugin.Spec.new({ "foo/bar" }) + Config.plugins = spec.plugins + Plugin.update_state() + assert(spec.plugins.bar.lazy == false) + end + end) + + it("handles opt from dep", function() + Config.options.defaults.lazy = false + local spec = Plugin.Spec.new({ "foo/bar", event = "foo" }) + assert(#spec.errors == 0) + Config.plugins = spec.plugins + Plugin.update_state() + assert.same(1, vim.tbl_count(spec.plugins)) + assert(spec.plugins.bar._.dep ~= true) + assert(spec.plugins.bar.lazy == true) + end) + + it("merges lazy loaders", function() + local tests = { + { { "foo/bar", event = "mod1" }, { "foo/bar", event = "mod2" } }, + { { "foo/bar", event = { "mod1" } }, { "foo/bar", event = { "mod2" } } }, + { { "foo/bar", event = "mod1" }, { "foo/bar", event = { "mod2" } } }, + } + for _, test in ipairs(tests) do + local spec = Plugin.Spec.new(test) + assert(#spec.errors == 0) + assert(vim.tbl_count(spec.plugins) == 1) + assert(type(spec.plugins.bar.event) == "table") + assert(#spec.plugins.bar.event == 2) + assert(vim.tbl_contains(spec.plugins.bar.event, "mod1")) + assert(vim.tbl_contains(spec.plugins.bar.event, "mod2")) + end + end) + + it("refuses to merge", function() + local spec = Plugin.Spec.new({ + { "foo/dep1", config = 1 }, + { + "foo/bar", + dependencies = { { "foo/dep1", config = 2 }, "foo/dep2" }, + }, + }) + assert(#spec.errors > 0) + end) + + -- it("recursive deps", function() + -- local spec = Plugin.Spec.new({ + -- "nvim-telescope/telescope.nvim", + -- dependencies = { + -- "nvim-lua/plenary.nvim", + -- { "nvim-telescope/telescope-fzf-native.nvim", build = "make" }, + -- { "nvim-telescope/telescope-frecency.nvim", dependencies = "kkharji/sqlite.lua" }, + -- }, + -- }) + -- end) + end) + it("handles opt from dep", function() Config.options.defaults.lazy = false local spec = Plugin.Spec.new({ "foo/dep1", { "foo/bar", dependencies = { "foo/dep1", "foo/dep2" } } }) + assert(#spec.errors == 0) Config.plugins = spec.plugins Plugin.update_state() assert.same(3, vim.tbl_count(spec.plugins)) @@ -73,6 +231,7 @@ describe("plugin spec opt", function() do Config.options.defaults.lazy = true local spec = Plugin.Spec.new({ "foo/bar" }) + assert(#spec.errors == 0) Config.plugins = spec.plugins Plugin.update_state() assert(spec.plugins.bar.lazy == true) @@ -89,6 +248,7 @@ describe("plugin spec opt", function() it("handles opt from dep", function() Config.options.defaults.lazy = false local spec = Plugin.Spec.new({ "foo/bar", event = "foo" }) + assert(#spec.errors == 0) Config.plugins = spec.plugins Plugin.update_state() assert.same(1, vim.tbl_count(spec.plugins)) @@ -104,6 +264,7 @@ describe("plugin spec opt", function() } for _, test in ipairs(tests) do local spec = Plugin.Spec.new(test) + assert(#spec.errors == 0) assert(vim.tbl_count(spec.plugins) == 1) assert(type(spec.plugins.bar.event) == "table") assert(#spec.plugins.bar.event == 2) @@ -113,13 +274,14 @@ describe("plugin spec opt", function() end) it("refuses to merge", function() - Plugin.Spec.new({ + local spec = Plugin.Spec.new({ { "foo/dep1", config = 1 }, { "foo/bar", dependencies = { { "foo/dep1", config = 2 }, "foo/dep2" }, }, }) + assert(#spec.errors > 0) end) -- it("recursive deps", function()