diff --git a/README.md b/README.md index b6e7381..bf780b9 100755 --- a/README.md +++ b/README.md @@ -103,10 +103,18 @@ require("lz.n").load(plugins) ``` - **plugins**: this should be a `table` or a `string` - - `table`: a list with your [Plugin Spec](#plugin-spec) + - `table`: + - A list with your [Plugin Specs](#plugin-spec) + - Or a single plugin spec. - `string`: a Lua module name that contains your [Plugin Spec](#plugin-spec). See [Structuring Your Plugins](#structuring-your-plugins) +> [!TIP] +> +> You can call `load()` as you would call `lazy.nvim`'s `setup()`. +> Or, you can also use it to register individual plugin specs for lazy +> loading. + ### Plugin spec @@ -123,6 +131,7 @@ require("lz.n").load(plugins) | **keys** | `string?` or `string[]` or `lz.n.KeysSpec[]` | Lazy-load on key mapping. | `keys` | | **colorscheme** | `string?` or `string[]` | Lazy-load on colorscheme. | None. `lazy.nvim` lazy-loads colorschemes automatically[^2]. | | **priority** | `number?` | Only useful for **start** plugins (not lazy-loaded) to force loading certain plugins first. Default priority is `50` (or `1000` if `colorscheme` is set). | `priority` | +| **load** | `fun(string)?` | Can be used to override the `load()` function for an individual plugin. | None. | [^1]: In contrast to `lazy.nvim`'s `name` field, a `lz.n.PluginSpec`'s `name` *is not optional*. diff --git a/lua/lz/n/init.lua b/lua/lz/n/init.lua index 177e003..9b1b8e0 100644 --- a/lua/lz/n/init.lua +++ b/lua/lz/n/init.lua @@ -16,26 +16,40 @@ end) ---@param spec string | lz.n.Spec function M.load(spec) - if vim.g.lz_n_did_load then - return vim.notify("lz.n has already loaded your plugins.", vim.log.levels.WARN, { title = "lz.n" }) - end - vim.g.lz_n_did_load = true - if type(spec) == "string" then spec = { import = spec } end - ---@cast spec lz.n.Spec - local plugins = require("lz.n.spec").parse(spec) + --- @cast spec lz.n.Spec + local spec_mod = require("lz.n.spec") + local is_single_plugin_spec = spec_mod.is_single_plugin_spec(spec) + if not is_single_plugin_spec then + if vim.g.lz_n_did_load then + return vim.notify( + "lz.n.load() should only be called on a list of plugin specs once.", + vim.log.levels.WARN, + { title = "lz.n" } + ) + end + vim.g.lz_n_did_load = true + end + local plugins = spec_mod.parse(spec) require("lz.n.loader").load_startup_plugins(plugins) - require("lz.n.state").plugins = plugins + + local state = require("lz.n.state") + if is_single_plugin_spec then + state.plugins = vim.tbl_deep_extend("force", state.plugins, plugins) + else + state.plugins = plugins + end require("lz.n.handler").init(plugins) if vim.v.vim_did_enter == 1 then deferred_ui_enter() - else + elseif not vim.g.lz_n_did_create_deferred_ui_enter_autocmd then vim.api.nvim_create_autocmd("UIEnter", { once = true, callback = deferred_ui_enter, }) + vim.g.lz_n_did_create_deferred_ui_enter_autocmd = true end end diff --git a/lua/lz/n/loader.lua b/lua/lz/n/loader.lua index 28c1e96..9710c07 100644 --- a/lua/lz/n/loader.lua +++ b/lua/lz/n/loader.lua @@ -16,7 +16,7 @@ function M._load(plugin) end require("lz.n.handler").disable(plugin) ---@type fun(name: string) | nil - local load_impl = vim.tbl_get(vim.g, "lz_n", "load") + local load_impl = plugin.load or vim.tbl_get(vim.g, "lz_n", "load") if type(load_impl) == "function" then load_impl(plugin.name) else diff --git a/lua/lz/n/meta.lua b/lua/lz/n/meta.lua index 94ac857..0c4f1f6 100644 --- a/lua/lz/n/meta.lua +++ b/lua/lz/n/meta.lua @@ -9,6 +9,10 @@ error("Cannot import a meta module") --- Only useful for lazy=false plugins to force loading certain plugins first. --- Default priority is 50 --- @field priority? number +--- +--- Set this to override the `load` function for an individual plugin. +--- Defaults to `vim.g.lz_n.load()`, see |lz.n.Config|. +--- @field load? fun(name: string) --- @alias lz.n.Event {id:string, event:string[]|string, pattern?:string[]|string} --- @alias lz.n.EventSpec string|{event?:string|string[], pattern?:string|string[]}|string[] diff --git a/lua/lz/n/spec.lua b/lua/lz/n/spec.lua index 6ab5291..4e7dcbc 100644 --- a/lua/lz/n/spec.lua +++ b/lua/lz/n/spec.lua @@ -123,16 +123,28 @@ local function parse(spec) return result end +---@param spec lz.n.Spec +---@return boolean +function M.is_spec_list(spec) + return #spec > 1 or vim.islist(spec) and #spec > 1 +end + +---@param spec lz.n.Spec +---@return boolean +function M.is_single_plugin_spec(spec) + return type(spec[1]) == "string" +end + ---@private ---@param spec lz.n.Spec ---@param result table function M._normalize(spec, result) - if #spec > 1 or vim.islist(spec) and #spec > 1 then + if M.is_spec_list(spec) then ---@cast spec lz.n.Spec[] for _, sp in ipairs(spec) do M._normalize(sp, result) end - elseif spec[1] then + elseif M.is_single_plugin_spec(spec) then ---@cast spec lz.n.PluginSpec result[spec[1]] = parse(spec) elseif spec.import then diff --git a/spec/lz_n_spec.lua b/spec/lz_n_spec.lua index add412f..afcd03b 100644 --- a/spec/lz_n_spec.lua +++ b/spec/lz_n_spec.lua @@ -6,44 +6,77 @@ local loader = require("lz.n.loader") local spy = require("luassert.spy") describe("lz.n", function() - it("load", function() - local spy_load = spy.on(loader, "_load") - lz.load({ - { - "neorg", - }, - { - "crates.nvim", - ft = { "toml", "rust" }, - }, - { - "telescope.nvim", - keys = "tt", - cmd = "Telescope", - }, - }) - assert.spy(spy_load).called(1) - assert.spy(spy_load).called_with({ - name = "neorg", - lazy = false, - }) - vim.api.nvim_exec_autocmds("FileType", { pattern = "toml" }) - assert.spy(spy_load).called(2) - assert.spy(spy_load).called_with({ - name = "crates.nvim", - lazy = true, - event = { - require("lz.n.handler.ft").parse("toml"), - require("lz.n.handler.ft").parse("rust"), - }, - }) - vim.cmd.Telescope() - assert.spy(spy_load).called(3) - assert.spy(spy_load).called_with({ - name = "telescope.nvim", - lazy = true, - cmd = { "Telescope" }, - keys = { require("lz.n.handler.keys").parse("tt") }, - }) + describe("load", function() + it("list of plugin specs", function() + local spy_load = spy.on(loader, "_load") + lz.load({ + { + "neorg", + }, + { + "crates.nvim", + ft = { "toml", "rust" }, + }, + { + "telescope.nvim", + keys = "tt", + cmd = "Telescope", + }, + }) + assert.spy(spy_load).called(1) + assert.spy(spy_load).called_with({ + name = "neorg", + lazy = false, + }) + vim.api.nvim_exec_autocmds("FileType", { pattern = "toml" }) + assert.spy(spy_load).called(2) + assert.spy(spy_load).called_with({ + name = "crates.nvim", + lazy = true, + event = { + require("lz.n.handler.ft").parse("toml"), + require("lz.n.handler.ft").parse("rust"), + }, + }) + vim.cmd.Telescope() + assert.spy(spy_load).called(3) + assert.spy(spy_load).called_with({ + name = "telescope.nvim", + lazy = true, + cmd = { "Telescope" }, + keys = { require("lz.n.handler.keys").parse("tt") }, + }) + end) + it("individual plugin specs", function() + local spy_load = spy.on(loader, "_load") + lz.load({ + "foo.nvim", + keys = "ff", + }) + assert.spy(spy_load).called(0) + local feed = vim.api.nvim_replace_termcodes("ff", true, true, true) + vim.api.nvim_feedkeys(feed, "ix", false) + assert.spy(spy_load).called(1) + lz.load({ + "bar.nvim", + cmd = "Bar", + }) + vim.cmd.Bar() + assert.spy(spy_load).called(2) + end) + it("can override load implementation via plugin spec", function() + local loaded = false + lz.load({ + "baz.nvim", + keys = "bb", + load = function() + loaded = true + end, + }) + assert.False(loaded) + local feed = vim.api.nvim_replace_termcodes("bb", true, true, true) + vim.api.nvim_feedkeys(feed, "ix", false) + assert.True(loaded) + end) end) end)