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
+18 -7
View File
@@ -4,10 +4,17 @@ local loader = require("lz.n.loader")
---@type lz.n.CmdHandler
local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {},
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
local function load(cmd)
vim.api.nvim_del_user_command(cmd)
@@ -66,12 +73,16 @@ local function add_cmd(cmd)
})
end
---@param plugin lz.n.Plugin
function M.del(plugin)
pcall(vim.api.nvim_del_user_command, plugin.cmd)
vim.iter(M.pending):each(function(_, plugins)
plugins[plugin.name] = nil
end)
---@param name string
function M.del(name)
vim.iter(M.pending)
:filter(function(_, plugins)
return plugins[name] ~= nil
end)
:each(function(cmd, plugins)
pcall(vim.api.nvim_del_user_command, cmd)
plugins[name] = nil
end)
end
---@param plugin lz.n.Plugin
@@ -82,7 +93,7 @@ function M.add(plugin)
---@param cmd string
vim.iter(plugin.cmd):each(function(cmd)
M.pending[cmd] = M.pending[cmd] or {}
M.pending[cmd][plugin.name] = plugin.name
M.pending[cmd][plugin.name] = plugin
add_cmd(cmd)
end)
end
+11 -4
View File
@@ -5,15 +5,22 @@ local loader = require("lz.n.loader")
---@type lz.n.ColorschemeHandler
local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {},
augroup = nil,
spec_field = "colorscheme",
}
---@param plugin lz.n.Plugin
function M.del(plugin)
---@param name string
---@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)
plugins[plugin.name] = nil
plugins[name] = nil
end)
end
@@ -49,7 +56,7 @@ function M.add(plugin)
---@param colorscheme string
vim.iter(plugin.colorscheme):each(function(colorscheme)
M.pending[colorscheme] = M.pending[colorscheme] or {}
M.pending[colorscheme][plugin.name] = plugin.name
M.pending[colorscheme][plugin.name] = plugin
end)
end
+11 -4
View File
@@ -20,6 +20,7 @@ lz_n_events["User DeferredUIEnter"] = lz_n_events.DeferredUIEnter
---@type lz.n.EventHandler
local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {},
events = {},
group = vim.api.nvim_create_augroup("lz_n_handler_event", { clear = true }),
@@ -56,6 +57,12 @@ local M = {
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
---@param event string
---@return string[]
@@ -166,15 +173,15 @@ function M.add(plugin)
---@param event lz.n.Event
vim.iter(plugin.event or {}):each(function(event)
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)
end)
end
---@param plugin lz.n.Plugin
function M.del(plugin)
---@param name string
function M.del(name)
vim.iter(M.pending):each(function(_, plugins)
plugins[plugin.name] = nil
plugins[name] = nil
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
local M = {
pending = {},
spec_field = "ft",
---@param value string
---@return lz.n.Event
@@ -16,6 +15,7 @@ local M = {
pattern = value,
}
end,
lookup = event.lookup,
}
---@param plugin lz.n.Plugin
@@ -23,9 +23,9 @@ function M.add(plugin)
event.add(plugin)
end
---@param plugin lz.n.Plugin
function M.del(plugin)
event.del(plugin)
---@param name string
function M.del(name)
event.del(name)
end
return M
+18 -4
View File
@@ -8,6 +8,21 @@ local handlers = {
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
---@return boolean
function M.is_lazy(spec)
@@ -41,12 +56,11 @@ local function enable(plugin)
end)
end
function M.disable(plugin)
---@param name string
function M.disable(name)
---@param handler lz.n.Handler
vim.iter(handlers):each(function(_, handler)
if handler.del then
handler.del(plugin)
end
handler.del(name)
end)
end
+11 -4
View File
@@ -25,6 +25,7 @@ end
---@type lz.n.KeysHandler
local M = {
---@type table<string, table<string, lz.n.Plugin[]>>
pending = {},
spec_field = "keys",
---@param value string|lz.n.KeysSpec
@@ -43,6 +44,12 @@ local M = {
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 }
---@param keys lz.n.Keys
@@ -142,15 +149,15 @@ function M.add(plugin)
---@param key lz.n.Keys
vim.iter(plugin.keys or {}):each(function(key)
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)
end)
end
---@param plugin lz.n.Plugin
function M.del(plugin)
---@param name string
function M.del(name)
vim.iter(M.pending):each(function(_, plugins)
plugins[plugin.name] = nil
plugins[name] = nil
end)
end
+26 -33
View File
@@ -15,10 +15,21 @@ local deferred_ui_enter = vim.schedule_wrap(function()
end)
---@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[])
M.trigger_load = require("lz.n.loader").load
--- Accepts plugin names (`string | string[]`, when called in another
--- 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(import: string)
@@ -28,42 +39,17 @@ function M.load(spec)
end
--- @cast spec 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)
-- 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
require("lz.n.handler").init(plugins)
-- because this calls the handler's del functions,
-- this should be ran after the handlers are given the plugin.
-- even if the plugin isnt supposed to have been added to any of them
-- Because this calls the handlers' `del` functions,
-- this should be ran after the plugins are registered with the handlers.
-- 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)
-- in addition, this allows even startup plugins to call
-- require('lz.n').trigger_load('someplugin') safely
if vim.v.vim_did_enter == 1 then
deferred_ui_enter()
@@ -76,4 +62,11 @@ function M.load(spec)
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
+5 -7
View File
@@ -1,7 +1,5 @@
---@mod lz.n.loader
local state = require("lz.n.state")
local M = {}
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
return
end
require("lz.n.handler").disable(plugin)
require("lz.n.handler").disable(plugin.name)
---@type fun(name: string) | nil
local load_impl = plugin.load or vim.tbl_get(vim.g, "lz_n", "load")
if type(load_impl) == "function" then
@@ -101,7 +99,8 @@ local function hook(hook_key, plugin)
end
---@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
---@cast plugins (string|lz.n.Plugin)[]
for _, plugin in pairs(plugins) do
@@ -109,9 +108,8 @@ function M.load(plugins)
-- https://github.com/nvim-neorocks/lz.n/pull/21
local loadable = true
if type(plugin) == "string" then
if state.plugins[plugin] then
plugin = state.plugins[plugin]
else
plugin = lookup and lookup(plugin) or plugin
if type(plugin) == "string" then
vim.notify("Plugin " .. plugin .. " not found", vim.log.levels.ERROR, { title = "lz.n" })
loadable = false
end
+10 -1
View File
@@ -89,9 +89,18 @@ error("Cannot import a meta module")
--- @field load? fun(name: string)
--- @class lz.n.Handler
---
--- The |lz.n.PluginSpec| field used to configure this handler.
--- @field spec_field string
---
--- Add a plugin to this handler.
--- @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
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