diff --git a/README.md b/README.md index 45bde6e..72e3930 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,9 @@ - [x] move tasks etc to Plugin.state - [ ] allow setting up plugins through config - [ ] handlers imply opt -- [ ] dependencies imply opt for deps +- [x] dependencies imply opt for deps - [x] fix local plugin spec +- [ ] investigate all opt=true. Simplifies logic (easily switch between opt/start afterwards) ## 📦 Differences with Packer diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua index ffdcfd1..a05603d 100644 --- a/lua/lazy/core/plugin.lua +++ b/lua/lazy/core/plugin.lua @@ -38,6 +38,7 @@ M.dirty = false ---@field name string display name and name used for plugin config files ---@field uri string ---@field dir string +---@field dep? boolean True if this plugin is only in the spec as a dependency ---@field enabled? boolean|(fun():boolean) ---@field opt? boolean ---@field dependencies? string[] @@ -82,7 +83,8 @@ function Spec.load(modname, modpath) end ---@param plugin LazyPlugin -function Spec:add(plugin) +---@param is_dep? boolean +function Spec:add(plugin, is_dep) local pkg = plugin[1] if type(pkg) ~= "string" then Util.error("Invalid plugin spec " .. vim.inspect(plugin)) @@ -108,27 +110,30 @@ function Spec:add(plugin) plugin.name = slash and name:sub(#name - slash + 2) or pkg:gsub("%W+", "_") end + plugin.dep = is_dep + M.process_local(plugin) local other = self.plugins[plugin.name] - self.plugins[plugin.name] = other and vim.tbl_extend("force", self.plugins[plugin.name], plugin) or plugin + self.plugins[plugin.name] = other and M.merge(other, plugin) or plugin return self.plugins[plugin.name] end ---@param spec LazySpec ---@param results? string[] -function Spec:normalize(spec, results) +---@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 }).name) + table.insert(results, self:add({ spec }, is_dep).name) elseif #spec > 1 or Util.is_list(spec) then ---@cast spec LazySpec[] for _, s in ipairs(spec) do - self:normalize(s, results) + self:normalize(s, results, is_dep) end elseif spec.enabled == nil or spec.enabled == true or (type(spec.enabled) == "function" and spec.enabled()) then ---@cast spec LazyPlugin - local plugin = self:add(spec) - plugin.dependencies = plugin.dependencies and self:normalize(plugin.dependencies, {}) or nil + local plugin = self:add(spec, is_dep) + plugin.dependencies = plugin.dependencies and self:normalize(plugin.dependencies, {}, true) or nil table.insert(results, plugin.name) end return results @@ -152,22 +157,55 @@ function Spec.revive(spec) return spec end -function M.update_state(check_clean) +---@param old LazyPlugin +---@param new LazyPlugin +---@return LazyPlugin +function M.merge(old, new) + local is_dep = old.dep and new.dep + + local Handler = require("lazy.core.handler") + ---@diagnostic disable-next-line: no-unknown + for k, v in pairs(new) do + if k == "dep" then + elseif old[k] ~= nil and old[k] ~= v then + if Handler.handlers[k] then + local values = type(v) == "string" and { v } or v + vim.list_extend(values, type(old[k]) == "string" and { old[k] } or old[k]) + ---@diagnostic disable-next-line: no-unknown + old[k] = values + else + error("Merging plugins is not supported for key `" .. k .. "`") + end + else + ---@diagnostic disable-next-line: no-unknown + old[k] = v + end + end + old.dep = is_dep + return old +end + +---@param opts? {clean:boolean, installed:boolean, plugins?: LazyPlugin[]} +function M.update_state(opts) + opts = opts or {} + ---@type table<"opt"|"start", table> local installed = { opt = {}, start = {} } - for opt, packs in pairs(installed) do - Util.ls(Config.options.packpath .. "/" .. opt, function(_, name, type) - if type == "directory" or type == "link" then - packs[name] = type - end - end) + if opts.installed ~= false then + for opt, packs in pairs(installed) do + Util.ls(Config.options.packpath .. "/" .. opt, function(_, name, type) + if type == "directory" or type == "link" then + packs[name] = type + end + end) + end end - for _, plugin in pairs(Config.plugins) do + for _, plugin in pairs(opts.plugins or Config.plugins) do plugin._ = plugin._ or {} plugin[1] = plugin["1"] or plugin[1] if plugin.opt == nil then - plugin.opt = Config.options.opt + plugin.opt = plugin.dep or Config.options.opt end local opt = plugin.opt and "opt" or "start" plugin.dir = Config.options.packpath .. "/" .. opt .. "/" .. plugin.name @@ -179,7 +217,7 @@ function M.update_state(check_clean) end end - if check_clean then + if opts.clean then Config.to_clean = {} for opt, packs in pairs(installed) do for pack in pairs(packs) do @@ -244,7 +282,7 @@ function M.load() for _, spec in ipairs(specs) do for _, plugin in pairs(spec.plugins) do local other = Config.plugins[plugin.name] - Config.plugins[plugin.name] = other and vim.tbl_extend("force", other, plugin) or plugin + Config.plugins[plugin.name] = other and M.merge(other, plugin) or plugin end end diff --git a/lua/lazy/manage/init.lua b/lua/lazy/manage/init.lua index c478f1c..d77115f 100644 --- a/lua/lazy/manage/init.lua +++ b/lua/lazy/manage/init.lua @@ -115,7 +115,7 @@ end ---@param opts? ManagerOpts function M.clean(opts) - Plugin.update_state(true) + Plugin.update_state({ clean = true }) M.run({ pipeline = { "fs.clean" }, plugins = Config.to_clean, @@ -123,7 +123,7 @@ function M.clean(opts) end function M.clear() - Plugin.update_state(true) + Plugin.update_state({ clean = true }) for _, plugin in pairs(Config.plugins) do plugin._.updated = nil plugin._.cloned = nil diff --git a/lua/lazy/view/init.lua b/lua/lazy/view/init.lua index c391235..51bb8e0 100644 --- a/lua/lazy/view/init.lua +++ b/lua/lazy/view/init.lua @@ -13,7 +13,7 @@ M.modes = { { name = "log", key = "L", desc = "Show recent updates for all plugins" }, { name = "restore", key = "R", desc = "Updates all plugins to the state in the lockfile" }, { name = "profile", key = "P", desc = "Show detailed profiling", toggle = true }, - { name = "help", key = "g?", hide = true, desc = "Toggle this help page", toggle = true }, + { name = "help", key = "?", hide = true, desc = "Toggle this help page", toggle = true }, { plugin = true, name = "update", key = "u", desc = "Update this plugin. This will also update the lockfile" }, { diff --git a/lua/lazy/view/render.lua b/lua/lazy/view/render.lua index 96e39dc..6c528f7 100644 --- a/lua/lazy/view/render.lua +++ b/lua/lazy/view/render.lua @@ -2,7 +2,6 @@ local Config = require("lazy.core.config") local Util = require("lazy.util") local Sections = require("lazy.view.sections") local Handler = require("lazy.core.handler") -local Plugin = require("lazy.core.plugin") local Git = require("lazy.manage.git") local Text = require("lazy.view.text") @@ -95,7 +94,7 @@ end function M:title() self:append(" lazy.nvim ", "LazyH1"):center():nl() - self:append("press "):append("g?", "LazySpecial"):append(" for help"):center():nl() + self:append("press "):append("", "LazySpecial"):append(" for help"):center():nl() self:append("https://github.com/folke/lazy.nvim", "LazyMuted"):center():nl() local View = require("lazy.view") diff --git a/tests/core/plugin_spec.lua b/tests/core/plugin_spec.lua index e474d7f..4792860 100644 --- a/tests/core/plugin_spec.lua +++ b/tests/core/plugin_spec.lua @@ -5,7 +5,7 @@ local assert = require("luassert") Config.setup() -describe("plugin spec", function() +describe("plugin spec uri/name", function() local tests = { { { "~/foo" }, { [1] = "~/foo", name = "foo", uri = vim.fn.fnamemodify("~/foo", ":p") } }, { { "/tmp/foo" }, { [1] = "/tmp/foo", name = "foo", uri = "/tmp/foo" } }, @@ -19,7 +19,7 @@ describe("plugin spec", function() } for _, test in ipairs(tests) do - it("parses uri " .. vim.inspect(test[1]):gsub("%s+", " "), function() + it("parses " .. vim.inspect(test[1]):gsub("%s+", " "), function() local spec = Plugin.Spec.new(test[1]) local plugins = vim.tbl_values(spec.plugins) assert.equal(1, #plugins) @@ -27,3 +27,67 @@ describe("plugin spec", function() end) end end) + +describe("plugin spec opt", function() + it("handles dependencies", function() + local tests = { + { "foo/bar", dependencies = { "foo/dep1", "foo/dep2" } }, + { "foo/bar", dependencies = { { "foo/dep1" }, "foo/dep2" } }, + { { { "foo/bar", dependencies = { { "foo/dep1" }, "foo/dep2" } } } }, + } + Config.options.opt = false + for _, test in ipairs(tests) do + local spec = Plugin.Spec.new(test) + Plugin.update_state({ plugins = spec.plugins }) + assert(vim.tbl_count(spec.plugins) == 3) + assert(#spec.plugins.bar.dependencies == 2) + assert(spec.plugins.bar.dep ~= true) + assert(spec.plugins.bar.opt == false) + assert(spec.plugins.dep1.dep == true) + assert(spec.plugins.dep1.opt == true) + assert(spec.plugins.dep2.dep == true) + assert(spec.plugins.dep2.opt == true) + end + end) + + it("handles opt from dep", function() + Config.options.opt = false + local spec = Plugin.Spec.new({ "foo/dep1", { "foo/bar", dependencies = { "foo/dep1", "foo/dep2" } } }) + Plugin.update_state({ plugins = spec.plugins }) + assert.same(3, vim.tbl_count(spec.plugins)) + assert(spec.plugins.bar.dep ~= true) + assert(spec.plugins.bar.opt == false) + assert(spec.plugins.dep2.dep == true) + assert(spec.plugins.dep2.opt == true) + assert(spec.plugins.dep1.dep ~= true) + assert(spec.plugins.dep1.opt == false) + end) + + it("merges lazy loaders", function() + local tests = { + { { "foo/bar", module = "mod1" }, { "foo/bar", module = "mod2" } }, + { { "foo/bar", module = { "mod1" } }, { "foo/bar", module = { "mod2" } } }, + { { "foo/bar", module = "mod1" }, { "foo/bar", module = { "mod2" } } }, + } + for _, test in ipairs(tests) do + local spec = Plugin.Spec.new(test) + assert(vim.tbl_count(spec.plugins) == 1) + assert(type(spec.plugins.bar.module) == "table") + assert(#spec.plugins.bar.module == 2) + assert(vim.tbl_contains(spec.plugins.bar.module, "mod1")) + assert(vim.tbl_contains(spec.plugins.bar.module, "mod2")) + end + end) + + it("refuses to merge", function() + assert.has.errors(function() + Plugin.Spec.new({ + { "foo/dep1", config = 1 }, + { + "foo/bar", + dependencies = { { "foo/dep1", config = 2 }, "foo/dep2" }, + }, + }) + end) + end) +end)