feat!: simplify state management + idempotent trigger_load (#56)

* feat!: simplify state management + idempotent `trigger_load(name)`

* feat!: `lz.n.Handler.del` now takes a plugin name (`string`)
This commit is contained in:
Marc Jakobi
2024-08-20 11:53:37 +02:00
committed by GitHub
parent c2e5c361ed
commit 701d6acc03
18 changed files with 226 additions and 122 deletions
+57 -13
View File
@@ -315,7 +315,9 @@ Or
├── init.lua ├── init.lua
``` ```
### Custom Handlers ## :electric_plug: API
### Custom handlers
You may register your own handlers to lazy-load plugins via You may register your own handlers to lazy-load plugins via
other triggers not already covered by the plugin spec. other triggers not already covered by the plugin spec.
@@ -335,25 +337,67 @@ require("lz.n").register_handler(handler)
#### `lz.n.Handler` #### `lz.n.Handler`
<!-- markdownlint-disable MD013 --> <!-- markdownlint-disable MD013 -->
| Property | Type | Description | | Property | Type | Description |
|----------|------|-------------| | --- | --- | --- |
| spec_field | `string` | the `lz.n.PluginSpec` field defined by the handler | | spec_field | `string` | the `lz.n.PluginSpec` field used to configure the handler |
| add | `fun(plugin: lz.n.Plugin)` | adds a plugin to the handler | | add | `fun(plugin: lz.n.Plugin)` | adds a plugin to the handler |
| del | `fun(plugin: lz.n.Plugin)?` | removes a plugin from the handler | | del | `fun(name: string)` | removes a plugin from the handler by name |
| lookup | `fun(name: string):lz.n.Plugin?` | lookup a plugin managed by this handler by name |
<!-- markdownlint-enable MD013 --> <!-- markdownlint-enable MD013 -->
When writing custom handlers, ### Lua API
you can load the plugin and run the hooks from
the spec with the following function: The following Lua functions are part of the public API.
> [!WARNING]
>
> If you use internal functions or modules that are not listed here,
> things may break without a major version bump.
#### `trigger_load`
You can manually load a plugin (and run the hooks from the spec)
with the following function:
```lua ```lua
---@type fun(plugins: string | lz.n.Plugin | string[] | lz.n.Plugin[]) ---@type fun(plugins: string | string[] | lz.n.Plugin | lz.n.Plugin[])
require('lz.n').trigger_load require('lz.n').trigger_load
``` ```
The function accepts plugin names or parsed plugin specs. The function accepts plugin names (`string | string[]`, when called in another
It will call the handler's `del` function (if it exists) after the `before` hooks, plugin's hook), or `lz.n.Plugin` items (when called by a `lz.n.Handler`).
and before `load` of the plugin's spec. If called with a plugin name, it will use the registered
handlers' `lookup` functions to search for a plugin to load
(loading the first one it finds).
Once a plugin has been loaded, it will be removed from all handlers (via `del`).
As a result, calling `trigger_load` with a plugin name is idempotent.
> [!IMPORTANT]
>
> This can be used to influence the order in which plugins are lazy-loaded,
> for example when a plugin depends on another one.
>
> However, we strongly recommend you consider alternatives before doing so.
> lz.n was designed with automatic dependency management in mind and hence
> **does not provide an API** to declare dependencies in the `lz.n.PluginSpec`.
> Plugin dependencies are usually just Lua libraries, which can be added to the
> `package.path` without any noticeable impact on startup time.
>
> If a plugin relies on another plugin's `plugin/` or `after/plugin/` scripts
> having been sourced before it is loaded, we consider this a bug, as this is not
> supported by Neovim's built-in loading mechanisms.
>
> Similarly, requiring users to worry about *the order in which they configure*
> plugins is bad design and defeats the purpose of automatic dependency management.
#### `lookup`
To lookup a plugin that is pending to be loaded by name, use:
```lua
---@type fun(name: string):lz.n.Plugin?
require('lz.n').lookup
```
### Extensions ### Extensions
+18 -7
View File
@@ -4,10 +4,17 @@ local loader = require("lz.n.loader")
---@type lz.n.CmdHandler ---@type lz.n.CmdHandler
local M = { local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {}, pending = {},
spec_field = "cmd", spec_field = "cmd",
} }
---@param name string
---@return lz.n.Plugin?
function M.lookup(name)
return require("lz.n.handler.extra").lookup(M.pending, name)
end
---@param cmd string ---@param cmd string
local function load(cmd) local function load(cmd)
vim.api.nvim_del_user_command(cmd) vim.api.nvim_del_user_command(cmd)
@@ -66,12 +73,16 @@ local function add_cmd(cmd)
}) })
end end
---@param plugin lz.n.Plugin ---@param name string
function M.del(plugin) function M.del(name)
pcall(vim.api.nvim_del_user_command, plugin.cmd) vim.iter(M.pending)
vim.iter(M.pending):each(function(_, plugins) :filter(function(_, plugins)
plugins[plugin.name] = nil return plugins[name] ~= nil
end) end)
:each(function(cmd, plugins)
pcall(vim.api.nvim_del_user_command, cmd)
plugins[name] = nil
end)
end end
---@param plugin lz.n.Plugin ---@param plugin lz.n.Plugin
@@ -82,7 +93,7 @@ function M.add(plugin)
---@param cmd string ---@param cmd string
vim.iter(plugin.cmd):each(function(cmd) vim.iter(plugin.cmd):each(function(cmd)
M.pending[cmd] = M.pending[cmd] or {} M.pending[cmd] = M.pending[cmd] or {}
M.pending[cmd][plugin.name] = plugin.name M.pending[cmd][plugin.name] = plugin
add_cmd(cmd) add_cmd(cmd)
end) end)
end end
+11 -4
View File
@@ -5,15 +5,22 @@ local loader = require("lz.n.loader")
---@type lz.n.ColorschemeHandler ---@type lz.n.ColorschemeHandler
local M = { local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {}, pending = {},
augroup = nil, augroup = nil,
spec_field = "colorscheme", spec_field = "colorscheme",
} }
---@param plugin lz.n.Plugin ---@param name string
function M.del(plugin) ---@return lz.n.Plugin?
function M.lookup(name)
return require("lz.n.handler.extra").lookup(M.pending, name)
end
---@param name string
function M.del(name)
vim.iter(M.pending):each(function(_, plugins) vim.iter(M.pending):each(function(_, plugins)
plugins[plugin.name] = nil plugins[name] = nil
end) end)
end end
@@ -49,7 +56,7 @@ function M.add(plugin)
---@param colorscheme string ---@param colorscheme string
vim.iter(plugin.colorscheme):each(function(colorscheme) vim.iter(plugin.colorscheme):each(function(colorscheme)
M.pending[colorscheme] = M.pending[colorscheme] or {} M.pending[colorscheme] = M.pending[colorscheme] or {}
M.pending[colorscheme][plugin.name] = plugin.name M.pending[colorscheme][plugin.name] = plugin
end) end)
end end
+11 -4
View File
@@ -20,6 +20,7 @@ lz_n_events["User DeferredUIEnter"] = lz_n_events.DeferredUIEnter
---@type lz.n.EventHandler ---@type lz.n.EventHandler
local M = { local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {}, pending = {},
events = {}, events = {},
group = vim.api.nvim_create_augroup("lz_n_handler_event", { clear = true }), group = vim.api.nvim_create_augroup("lz_n_handler_event", { clear = true }),
@@ -56,6 +57,12 @@ local M = {
end, end,
} }
---@param name string
---@return lz.n.Plugin?
function M.lookup(name)
return require("lz.n.handler.extra").lookup(M.pending, name)
end
-- Get all augroups for an event -- Get all augroups for an event
---@param event string ---@param event string
---@return string[] ---@return string[]
@@ -166,15 +173,15 @@ function M.add(plugin)
---@param event lz.n.Event ---@param event lz.n.Event
vim.iter(plugin.event or {}):each(function(event) vim.iter(plugin.event or {}):each(function(event)
M.pending[event.id] = M.pending[event.id] or {} M.pending[event.id] = M.pending[event.id] or {}
M.pending[event.id][plugin.name] = plugin.name M.pending[event.id][plugin.name] = plugin
add_event(event) add_event(event)
end) end)
end end
---@param plugin lz.n.Plugin ---@param name string
function M.del(plugin) function M.del(name)
vim.iter(M.pending):each(function(_, plugins) vim.iter(M.pending):each(function(_, plugins)
plugins[plugin.name] = nil plugins[name] = nil
end) end)
end end
+23
View File
@@ -0,0 +1,23 @@
---@mod lz.n.handler.extra Helper functions for use by handlers
local M = {}
---Look up a plugin in a plugin table, commonly used by handlers to
---keep track of plugins they manage
---@param plugin_tbl table<unknown, table<string, lz.n.Plugin>>
---@param name string
---@return lz.n.Plugin?
function M.lookup(plugin_tbl, name)
return vim
.iter(plugin_tbl)
---@param plugins table<string, lz.n.Plugin>
:map(function(_, plugins)
return plugins[name]
end)
---@param plugin lz.n.Plugin?
:find(function(plugin)
return plugin ~= nil
end)
end
return M
+4 -4
View File
@@ -5,7 +5,6 @@ local event = require("lz.n.handler.event")
---@type lz.n.FtHandler ---@type lz.n.FtHandler
local M = { local M = {
pending = {},
spec_field = "ft", spec_field = "ft",
---@param value string ---@param value string
---@return lz.n.Event ---@return lz.n.Event
@@ -16,6 +15,7 @@ local M = {
pattern = value, pattern = value,
} }
end, end,
lookup = event.lookup,
} }
---@param plugin lz.n.Plugin ---@param plugin lz.n.Plugin
@@ -23,9 +23,9 @@ function M.add(plugin)
event.add(plugin) event.add(plugin)
end end
---@param plugin lz.n.Plugin ---@param name string
function M.del(plugin) function M.del(name)
event.del(plugin) event.del(name)
end end
return M return M
+18 -4
View File
@@ -8,6 +8,21 @@ local handlers = {
colorscheme = require("lz.n.handler.colorscheme"), colorscheme = require("lz.n.handler.colorscheme"),
} }
---@param name string
---@return lz.n.Plugin?
function M.lookup(name)
return vim
.iter(handlers)
---@param handler lz.n.Handler
:map(function(_, handler)
return handler.lookup(name)
end)
---@param result lz.n.Plugin?
:find(function(result)
return result ~= nil
end)
end
---@param spec lz.n.PluginSpec ---@param spec lz.n.PluginSpec
---@return boolean ---@return boolean
function M.is_lazy(spec) function M.is_lazy(spec)
@@ -41,12 +56,11 @@ local function enable(plugin)
end) end)
end end
function M.disable(plugin) ---@param name string
function M.disable(name)
---@param handler lz.n.Handler ---@param handler lz.n.Handler
vim.iter(handlers):each(function(_, handler) vim.iter(handlers):each(function(_, handler)
if handler.del then handler.del(name)
handler.del(plugin)
end
end) end)
end end
+11 -4
View File
@@ -25,6 +25,7 @@ end
---@type lz.n.KeysHandler ---@type lz.n.KeysHandler
local M = { local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {}, pending = {},
spec_field = "keys", spec_field = "keys",
---@param value string|lz.n.KeysSpec ---@param value string|lz.n.KeysSpec
@@ -43,6 +44,12 @@ local M = {
end, end,
} }
---@param name string
---@return lz.n.Plugin?
function M.lookup(name)
return require("lz.n.handler.extra").lookup(M.pending, name)
end
local skip = { mode = true, id = true, ft = true, rhs = true, lhs = true } local skip = { mode = true, id = true, ft = true, rhs = true, lhs = true }
---@param keys lz.n.Keys ---@param keys lz.n.Keys
@@ -142,15 +149,15 @@ function M.add(plugin)
---@param key lz.n.Keys ---@param key lz.n.Keys
vim.iter(plugin.keys or {}):each(function(key) vim.iter(plugin.keys or {}):each(function(key)
M.pending[key.id] = M.pending[key.id] or {} M.pending[key.id] = M.pending[key.id] or {}
M.pending[key.id][plugin.name] = plugin.name M.pending[key.id][plugin.name] = plugin
add_keys(key) add_keys(key)
end) end)
end end
---@param plugin lz.n.Plugin ---@param name string
function M.del(plugin) function M.del(name)
vim.iter(M.pending):each(function(_, plugins) vim.iter(M.pending):each(function(_, plugins)
plugins[plugin.name] = nil plugins[name] = nil
end) end)
end end
+26 -33
View File
@@ -15,10 +15,21 @@ local deferred_ui_enter = vim.schedule_wrap(function()
end) end)
---@type fun(handler: lz.n.Handler): boolean ---@type fun(handler: lz.n.Handler): boolean
M.register_handler = require("lz.n.handler").register_handler M.register_handler = function(...)
return require("lz.n.handler").register_handler(...)
end
---@type fun(plugins: string | lz.n.Plugin | string[] | lz.n.Plugin[]) --- Accepts plugin names (`string | string[]`, when called in another
M.trigger_load = require("lz.n.loader").load --- plugin's hook), or |lz.n.Plugin| items (when called by a |lz.n.Handler|).
--- If called with a plugin name, it will use the registered
--- handlers' `lookup` functions to search for a plugin to load
--- (loading the first one it finds).
--- Once a plugin has been loaded, it will be removed from all handlers (via `del`).
--- As a result, calling `trigger_load` with a plugin name is idempotent.
---@param plugins string | lz.n.Plugin | string[] | lz.n.Plugin[]
M.trigger_load = function(plugins)
require("lz.n.loader").load(plugins, M.lookup)
end
---@overload fun(spec: lz.n.Spec) ---@overload fun(spec: lz.n.Spec)
---@overload fun(import: string) ---@overload fun(import: string)
@@ -28,42 +39,17 @@ function M.load(spec)
end end
--- @cast spec lz.n.Spec --- @cast spec lz.n.Spec
local spec_mod = require("lz.n.spec") local spec_mod = require("lz.n.spec")
local is_single_plugin_spec = spec_mod.is_single_plugin_spec(spec)
local plugins = spec_mod.parse(spec) local plugins = spec_mod.parse(spec)
-- add to state before loading anything, to prevent multiple loads being called
-- from within other eager plugin specs
local state = require("lz.n.state")
if is_single_plugin_spec then
local ok, updated_plugins = pcall(vim.tbl_deep_extend, "error", state.plugins, plugins)
if not ok then
return vim.schedule(function()
vim.notify("Cannot load the same plugin specs more than once", vim.log.levels.ERROR, { title = "lz.n" })
end)
end
state.plugins = updated_plugins
else
if state.plugins[spec[1]] then
return vim.schedule(function()
vim.notify(
("Plugin %s has already been registered for lazy loading"):format(spec[1]),
vim.log.levels.ERROR,
{ title = "lz.n" }
)
end)
end
state.plugins = plugins
end
-- calls handler add functions -- calls handler add functions
require("lz.n.handler").init(plugins) require("lz.n.handler").init(plugins)
-- because this calls the handler's del functions, -- Because this calls the handlers' `del` functions,
-- this should be ran after the handlers are given the plugin. -- this should be ran after the plugins are registered with the handlers.
-- even if the plugin isnt supposed to have been added to any of them -- even if an eager plugin isn't supposed to have been added to any of them
-- This allows even startup plugins to call
-- `require('lz.n').trigger_load()` safely
require("lz.n.loader").load_startup_plugins(plugins) require("lz.n.loader").load_startup_plugins(plugins)
-- in addition, this allows even startup plugins to call
-- require('lz.n').trigger_load('someplugin') safely
if vim.v.vim_did_enter == 1 then if vim.v.vim_did_enter == 1 then
deferred_ui_enter() deferred_ui_enter()
@@ -76,4 +62,11 @@ function M.load(spec)
end end
end end
---Lookup a plugin that is pending to be loaded by name.
---@param name string
---@return lz.n.Plugin?
function M.lookup(name)
return require("lz.n.handler").lookup(name)
end
return M return M
+5 -7
View File
@@ -1,7 +1,5 @@
---@mod lz.n.loader ---@mod lz.n.loader
local state = require("lz.n.state")
local M = {} local M = {}
local DEFAULT_PRIORITY = 50 local DEFAULT_PRIORITY = 50
@@ -14,7 +12,7 @@ function M._load(plugin)
if plugin.enabled == false or (type(plugin.enabled) == "function" and not plugin.enabled()) then if plugin.enabled == false or (type(plugin.enabled) == "function" and not plugin.enabled()) then
return return
end end
require("lz.n.handler").disable(plugin) require("lz.n.handler").disable(plugin.name)
---@type fun(name: string) | nil ---@type fun(name: string) | nil
local load_impl = plugin.load or 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 if type(load_impl) == "function" then
@@ -101,7 +99,8 @@ local function hook(hook_key, plugin)
end end
---@param plugins string | lz.n.Plugin | string[] | lz.n.Plugin[] ---@param plugins string | lz.n.Plugin | string[] | lz.n.Plugin[]
function M.load(plugins) ---@param lookup? fun(name: string): lz.n.Plugin?
function M.load(plugins, lookup)
plugins = (type(plugins) == "string" or plugins.name) and { plugins } or plugins plugins = (type(plugins) == "string" or plugins.name) and { plugins } or plugins
---@cast plugins (string|lz.n.Plugin)[] ---@cast plugins (string|lz.n.Plugin)[]
for _, plugin in pairs(plugins) do for _, plugin in pairs(plugins) do
@@ -109,9 +108,8 @@ function M.load(plugins)
-- https://github.com/nvim-neorocks/lz.n/pull/21 -- https://github.com/nvim-neorocks/lz.n/pull/21
local loadable = true local loadable = true
if type(plugin) == "string" then if type(plugin) == "string" then
if state.plugins[plugin] then plugin = lookup and lookup(plugin) or plugin
plugin = state.plugins[plugin] if type(plugin) == "string" then
else
vim.notify("Plugin " .. plugin .. " not found", vim.log.levels.ERROR, { title = "lz.n" }) vim.notify("Plugin " .. plugin .. " not found", vim.log.levels.ERROR, { title = "lz.n" })
loadable = false loadable = false
end end
+10 -1
View File
@@ -89,9 +89,18 @@ error("Cannot import a meta module")
--- @field load? fun(name: string) --- @field load? fun(name: string)
--- @class lz.n.Handler --- @class lz.n.Handler
---
--- The |lz.n.PluginSpec| field used to configure this handler.
--- @field spec_field string --- @field spec_field string
---
--- Add a plugin to this handler.
--- @field add fun(plugin: lz.n.Plugin) --- @field add fun(plugin: lz.n.Plugin)
--- @field del? fun(plugin: lz.n.Plugin) ---
--- Remove a plugin from this handler by name.
--- @field del fun(name: string)
---
--- Lookup a plugin by name.
--- @field lookup fun(name: string): lz.n.Plugin?
--- @type lz.n.Config --- @type lz.n.Config
vim.g.lz_n = vim.g.lz_n vim.g.lz_n = vim.g.lz_n
-8
View File
@@ -1,8 +0,0 @@
---@mod lz.n.state
local M = {}
---@type table<string, lz.n.Plugin>
M.plugins = {}
return M
-3
View File
@@ -3,7 +3,6 @@ vim.g.lz_n = {
load = function() end, load = function() end,
} }
local cmd = require("lz.n.handler.cmd") local cmd = require("lz.n.handler.cmd")
local state = require("lz.n.state")
local loader = require("lz.n.loader") local loader = require("lz.n.loader")
local spy = require("luassert.spy") local spy = require("luassert.spy")
@@ -15,7 +14,6 @@ describe("handlers.cmd", function()
cmd = { "Foo" }, cmd = { "Foo" },
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
cmd.add(plugin) cmd.add(plugin)
assert.is_not_nil(vim.cmd.Foo) assert.is_not_nil(vim.cmd.Foo)
local counter = 0 local counter = 0
@@ -49,7 +47,6 @@ describe("handlers.cmd", function()
cmd = commands, cmd = commands,
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
cmd.add(plugin) cmd.add(plugin)
vim.cmd[commands[1]]() vim.cmd[commands[1]]()
vim.cmd[commands[2]]() vim.cmd[commands[2]]()
-2
View File
@@ -2,7 +2,6 @@ vim.g.lz_n = {
load = function() end, load = function() end,
} }
local colorscheme = require("lz.n.handler.colorscheme") local colorscheme = require("lz.n.handler.colorscheme")
local state = require("lz.n.state")
local loader = require("lz.n.loader") local loader = require("lz.n.loader")
local spy = require("luassert.spy") local spy = require("luassert.spy")
@@ -14,7 +13,6 @@ describe("handlers.colorscheme", function()
colorscheme = { "sweetie" }, colorscheme = { "sweetie" },
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
colorscheme.add(plugin) colorscheme.add(plugin)
pcall(vim.cmd.colorscheme, "sweetie") pcall(vim.cmd.colorscheme, "sweetie")
pcall(vim.cmd.colorscheme, "sweetie") pcall(vim.cmd.colorscheme, "sweetie")
-5
View File
@@ -3,7 +3,6 @@ vim.g.lz_n = {
load = function() end, load = function() end,
} }
local event = require("lz.n.handler.event") local event = require("lz.n.handler.event")
local state = require("lz.n.state")
local loader = require("lz.n.loader") local loader = require("lz.n.loader")
local spy = require("luassert.spy") local spy = require("luassert.spy")
@@ -52,7 +51,6 @@ describe("handlers.event", function()
event = { event.parse("BufEnter") }, event = { event.parse("BufEnter") },
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
event.add(plugin) event.add(plugin)
vim.api.nvim_exec_autocmds("BufEnter", {}) vim.api.nvim_exec_autocmds("BufEnter", {})
vim.api.nvim_exec_autocmds("BufEnter", {}) vim.api.nvim_exec_autocmds("BufEnter", {})
@@ -67,7 +65,6 @@ describe("handlers.event", function()
event = events, event = events,
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
event.add(plugin) event.add(plugin)
vim.api.nvim_exec_autocmds(events[1].event, { vim.api.nvim_exec_autocmds(events[1].event, {
pattern = ".lua", pattern = ".lua",
@@ -98,7 +95,6 @@ describe("handlers.event", function()
group = vim.api.nvim_create_augroup("foo", {}), group = vim.api.nvim_create_augroup("foo", {}),
}) })
end end
state.plugins[plugin.name] = plugin
event.add(plugin) event.add(plugin)
vim.api.nvim_exec_autocmds("BufEnter", {}) vim.api.nvim_exec_autocmds("BufEnter", {})
assert.True(triggered) assert.True(triggered)
@@ -111,7 +107,6 @@ describe("handlers.event", function()
event = { event.parse("DeferredUIEnter") }, event = { event.parse("DeferredUIEnter") },
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
event.add(plugin) event.add(plugin)
vim.api.nvim_exec_autocmds("User", { pattern = "DeferredUIEnter", modeline = false }) vim.api.nvim_exec_autocmds("User", { pattern = "DeferredUIEnter", modeline = false })
assert.spy(spy_load).called(1) assert.spy(spy_load).called(1)
-2
View File
@@ -3,7 +3,6 @@ vim.g.lz_n = {
load = function() end, load = function() end,
} }
local ft = require("lz.n.handler.ft") local ft = require("lz.n.handler.ft")
local state = require("lz.n.state")
local loader = require("lz.n.loader") local loader = require("lz.n.loader")
local spy = require("luassert.spy") local spy = require("luassert.spy")
@@ -22,7 +21,6 @@ describe("handlers.ft", function()
event = { ft.parse("rust") }, event = { ft.parse("rust") },
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
ft.add(plugin) ft.add(plugin)
vim.api.nvim_exec_autocmds("FileType", { pattern = "rust" }) vim.api.nvim_exec_autocmds("FileType", { pattern = "rust" })
vim.api.nvim_exec_autocmds("FileType", { pattern = "rust" }) vim.api.nvim_exec_autocmds("FileType", { pattern = "rust" })
-4
View File
@@ -3,7 +3,6 @@ vim.g.lz_n = {
load = function() end, load = function() end,
} }
local keys = require("lz.n.handler.keys") local keys = require("lz.n.handler.keys")
local state = require("lz.n.state")
local loader = require("lz.n.loader") local loader = require("lz.n.loader")
local spy = require("luassert.spy") local spy = require("luassert.spy")
@@ -30,7 +29,6 @@ describe("handlers.keys", function()
keys = keys.parse(lhs), keys = keys.parse(lhs),
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
keys.add(plugin) keys.add(plugin)
local feed = vim.api.nvim_replace_termcodes("<Ignore>" .. lhs, true, true, true) local feed = vim.api.nvim_replace_termcodes("<Ignore>" .. lhs, true, true, true)
vim.api.nvim_feedkeys(feed, "ix", false) vim.api.nvim_feedkeys(feed, "ix", false)
@@ -47,7 +45,6 @@ describe("handlers.keys", function()
keys = lzkeys, keys = lzkeys,
} }
local spy_load = spy.on(loader, "_load") local spy_load = spy.on(loader, "_load")
state.plugins[plugin.name] = plugin
keys.add(plugin) keys.add(plugin)
local feed1 = vim.api.nvim_replace_termcodes("<Ignore>" .. lzkeys[1].lhs, true, true, true) local feed1 = vim.api.nvim_replace_termcodes("<Ignore>" .. lzkeys[1].lhs, true, true, true)
vim.api.nvim_feedkeys(feed1, "ix", false) vim.api.nvim_feedkeys(feed1, "ix", false)
@@ -74,7 +71,6 @@ describe("handlers.keys", function()
end) end)
orig_load(...) orig_load(...)
end end
state.plugins[plugin.name] = plugin
keys.add(plugin) keys.add(plugin)
local feed = vim.api.nvim_replace_termcodes("<Ignore>" .. lhs, true, true, true) local feed = vim.api.nvim_replace_termcodes("<Ignore>" .. lhs, true, true, true)
vim.api.nvim_feedkeys(feed, "ix", false) vim.api.nvim_feedkeys(feed, "ix", false)
+32 -17
View File
@@ -5,16 +5,35 @@ vim.g.lz_n = {
local lz_n = require("lz.n") local lz_n = require("lz.n")
local spy = require("luassert.spy") local spy = require("luassert.spy")
---@type lz.n.Plugin
local testplugin = {
name = "testplugin",
testfield = { "a", "b" },
lazy = true,
}
describe("handlers.custom", function() describe("handlers.custom", function()
---@class TestHandler: lz.n.Handler ---@class TestHandler: lz.n.Handler
local mock_state = {}
---@type TestHandler ---@type TestHandler
local hndl = { local mock_hndl = {
spec_field = "testfield", spec_field = "testfield",
add = function(_) end, add = function(plugin)
del = function(_) end, mock_state[plugin.name] = plugin
end,
---@param name string
del = function(name)
mock_state[name] = nil
end,
---@param name string
---@return lz.n.Plugin?
lookup = function(name)
return mock_state[name]
end,
} }
local addspy = spy.on(hndl, "add")
local delspy = spy.on(hndl, "del") local addspy = spy.on(mock_hndl, "add")
local delspy = spy.on(mock_hndl, "del")
it("Duplicate handlers fail to register", function() it("Duplicate handlers fail to register", function()
local notispy = spy.new(function() end) local notispy = spy.new(function() end)
-- NOTE: teardown fails if you don't temporarily replace vim.notify -- NOTE: teardown fails if you don't temporarily replace vim.notify
@@ -25,23 +44,19 @@ describe("handlers.custom", function()
vim.notify = og_notify vim.notify = og_notify
end) end)
it("can add plugins to the handler", function() it("can add plugins to the handler", function()
assert.True(lz_n.register_handler(hndl)) assert.True(lz_n.register_handler(mock_hndl))
lz_n.load({ lz_n.load({
"testplugin", "testplugin",
testfield = { "a", "b" }, testfield = { "a", "b" },
}) })
assert.spy(addspy).called_with({ assert.spy(addspy).called_with(testplugin)
name = "testplugin",
testfield = { "a", "b" },
lazy = true,
})
end) end)
it("loading a plugin removes it from the handler", function() it("loading a plugin removes it from the handler", function()
lz_n.trigger_load("testplugin") lz_n.trigger_load(testplugin.name)
assert.spy(delspy).called_with({ assert.spy(delspy).called_with(testplugin.name)
name = "testplugin", end)
testfield = { "a", "b" }, it("trigger_load is idempotent when called with a plugin name", function()
lazy = true, lz_n.trigger_load(testplugin.name)
}) assert.spy(delspy).called(1)
end) end)
end) end)