Files
lz.n/lua/lz/n/init.lua

119 lines
4.2 KiB
Lua

---@mod lz.n
---
---@brief [[
---A dead simple lazy-loading Lua library for Neovim plugins.
---
---It is intended to be used
---
---- by users of plugin managers that don't provide a convenient API for lazy-loading.
---- by plugin managers, to provide a convenient API for lazy-loading.
---@brief ]]
---@toc lz.n.contents
---@mod lz.n.api lz.n Lua API
local M = {}
if vim.fn.has("nvim-0.10.0") ~= 1 then
error("lz.n requires Neovim >= 0.10.0")
end
local deferred_ui_enter = vim.schedule_wrap(function()
if vim.v.exiting ~= vim.NIL then
return
end
vim.g.lz_n_did_deferred_ui_enter = true
vim.api.nvim_exec_autocmds("User", { pattern = "DeferredUIEnter", modeline = false })
end)
--- The function provides two overloads, each suited for different use cases:
---
---@overload fun(plugin: lz.n.Plugin)
--- **Stateless version:**
--- - Intended for: Use by a `lz.n.Handler`
--- - Description: This version should be used when working with `lz.n.Handler`
--- instances to maintain referential transparency.
--- Each handler has full authority over its internal state, ensuring it
--- remains isolated and unaffected by external influences,
--- thereby preventing multiple sources of truth.
--- - Note: If loading multiple plugins simultaneously,
--- handlers should iterate over |vim.deepcopy| of the plugins,
--- verifying they are still pending before each `trigger_load` call.
--- This practice allows for safe invocation of the stateful `trigger_load`
--- in `before` and `after` hooks.
---
---@overload fun(plugins: string | string[], opts?: lz.n.lookup.Opts): string[]
--- **Stateful version:**
--- - Returns: A list of plugin names that were skipped
--- (empty if all plugins were loaded).
--- - Intended for: Scenarios where handler state is unknown or inaccessible,
--- such as in `before` or `after` hooks.
--- - Description: This version allows you to load plugins by name.
--- It searches through the handlers, querying their `lookup` functions
--- to identify an appropriate plugin, and returns the first match.
--- You can fine-tune the search process by providing a [`lz.n.lookup.Opts` table](#lookup).
M.trigger_load = function(plugins, opts)
return require("lz.n.loader").load(plugins, function(name)
return M.lookup(name, opts)
end)
end
---@overload fun(spec: lz.n.Spec)
---Register a list with your plugin specs or a single plugin spec to be lazy-loaded.
---
---@overload fun(import: string)
---Register a Lua module name that contains your plugin spec(s) to be lazy-loaded.
function M.load(spec)
if type(spec) == "string" then
spec = { import = spec }
end
--- @cast spec lz.n.Spec
local spec_mod = require("lz.n.spec")
local plugins = spec_mod.parse(spec)
-- calls handler add functions
require("lz.n.handler").init(plugins)
-- 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)
if vim.v.vim_did_enter == 1 then
deferred_ui_enter()
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
--- Lookup a plugin that is pending to be loaded by name.
---@param name string
---@param opts? lz.n.lookup.Opts
---@return lz.n.Plugin?
function M.lookup(name, opts)
return require("lz.n.handler").lookup(name, opts)
end
---@class lz.n.lookup.Opts
---
--- The handlers to include in the search (filtered by `spec_field`)
--- In case of multiple filters, the order of the filter list
--- determines the order in which handlers' `lookup` functions are called.
---@field filter string | string[]
---Register a custom handler.
---@param handler lz.n.Handler
---@return boolean success
M.register_handler = function(handler)
return require("lz.n.handler").register_handler(handler)
end
return M