mirror of
https://github.com/zoriya/lz.n.git
synced 2026-06-06 12:04:06 +00:00
feat: handler.state module
This commit is contained in:
@@ -2,8 +2,8 @@ local loader = require("lz.n.loader")
|
||||
|
||||
---@class lz.n.CmdHandler: lz.n.Handler
|
||||
|
||||
---@type table<string, table<string, lz.n.Plugin[]>>
|
||||
local pending = {}
|
||||
---@type lz.n.handler.State
|
||||
local state = require("lz.n.handler.state").new()
|
||||
|
||||
---@type lz.n.CmdHandler
|
||||
local M = {
|
||||
@@ -13,25 +13,14 @@ local M = {
|
||||
---@param name string
|
||||
---@return lz.n.Plugin?
|
||||
function M.lookup(name)
|
||||
return require("lz.n.handler.extra").lookup(pending, name)
|
||||
return state.lookup_plugin(name)
|
||||
end
|
||||
|
||||
---@param cmd string
|
||||
---@return string[] loaded_plugin_names
|
||||
local function load(cmd)
|
||||
vim.api.nvim_del_user_command(cmd)
|
||||
local plugins = pending[cmd]
|
||||
-- Make sure trigger_load calls in before hooks can't interfere with the state,
|
||||
-- but they can load a plugin before it's loaded by this handler
|
||||
vim
|
||||
.iter(vim.deepcopy(pending[cmd]))
|
||||
---@param plugin lz.n.Plugin
|
||||
:each(function(_, plugin)
|
||||
if pending[cmd][plugin.name] then
|
||||
loader.load(plugin)
|
||||
end
|
||||
end)
|
||||
return vim.tbl_keys(plugins)
|
||||
return state.each_pending(cmd, loader.load)
|
||||
end
|
||||
|
||||
---@param cmd string
|
||||
@@ -88,14 +77,9 @@ end
|
||||
|
||||
---@param name string
|
||||
function M.del(name)
|
||||
vim.iter(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)
|
||||
state.del(name, function(cmd)
|
||||
pcall(vim.api.nvim_del_user_command, cmd)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param plugin lz.n.Plugin
|
||||
@@ -105,8 +89,7 @@ function M.add(plugin)
|
||||
end
|
||||
---@param cmd string
|
||||
vim.iter(plugin.cmd):each(function(cmd)
|
||||
pending[cmd] = pending[cmd] or {}
|
||||
pending[cmd][plugin.name] = plugin
|
||||
state.insert(cmd, plugin)
|
||||
add_cmd(cmd)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -3,8 +3,8 @@ local loader = require("lz.n.loader")
|
||||
---@class lz.n.ColorschemeHandler: lz.n.Handler
|
||||
---@field augroup? integer
|
||||
|
||||
---@type table<string, table<string, lz.n.Plugin[]>>
|
||||
local pending = {}
|
||||
---@type lz.n.handler.State
|
||||
local state = require("lz.n.handler.state").new()
|
||||
|
||||
---@type lz.n.ColorschemeHandler
|
||||
local M = {
|
||||
@@ -15,33 +15,17 @@ local M = {
|
||||
---@param name string
|
||||
---@return lz.n.Plugin?
|
||||
function M.lookup(name)
|
||||
return require("lz.n.handler.extra").lookup(pending, name)
|
||||
return state.lookup_plugin(name)
|
||||
end
|
||||
|
||||
---@param name string
|
||||
function M.del(name)
|
||||
vim.iter(pending):each(function(_, plugins)
|
||||
plugins[name] = nil
|
||||
end)
|
||||
state.del(name)
|
||||
end
|
||||
|
||||
---@param name string
|
||||
local function on_colorscheme(name)
|
||||
local plugins = pending[name] or {}
|
||||
if vim.tbl_isempty(plugins) then
|
||||
-- already loaded
|
||||
return
|
||||
end
|
||||
-- Make sure trigger_load calls in before hooks can't interfere with the state,
|
||||
-- but they can load a plugin before it's loaded by this handler
|
||||
vim
|
||||
.iter(vim.deepcopy(pending[name]))
|
||||
---@param plugin lz.n.Plugin
|
||||
:each(function(_, plugin)
|
||||
if pending[name][plugin.name] then
|
||||
loader.load(plugin)
|
||||
end
|
||||
end)
|
||||
state.each_pending(name, loader.load)
|
||||
end
|
||||
|
||||
local function init()
|
||||
@@ -65,8 +49,7 @@ function M.add(plugin)
|
||||
init()
|
||||
---@param colorscheme string
|
||||
vim.iter(plugin.colorscheme):each(function(colorscheme)
|
||||
pending[colorscheme] = pending[colorscheme] or {}
|
||||
pending[colorscheme][plugin.name] = plugin
|
||||
state.insert(colorscheme, plugin)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
+12
-24
@@ -18,8 +18,8 @@ local lz_n_events = {
|
||||
|
||||
lz_n_events["User DeferredUIEnter"] = lz_n_events.DeferredUIEnter
|
||||
|
||||
---@type table<string, table<string, lz.n.Plugin[]>>
|
||||
local pending = {}
|
||||
---@type lz.n.handler.State
|
||||
local state = require("lz.n.handler.state").new()
|
||||
|
||||
---@type lz.n.EventHandler
|
||||
local M = {
|
||||
@@ -61,7 +61,7 @@ local M = {
|
||||
---@param name string
|
||||
---@return lz.n.Plugin?
|
||||
function M.lookup(name)
|
||||
return require("lz.n.handler.extra").lookup(pending, name)
|
||||
return state.lookup_plugin(name)
|
||||
end
|
||||
|
||||
-- Get all augroups for an event
|
||||
@@ -89,7 +89,7 @@ local event_triggers = {
|
||||
---@return lz.n.EventOpts[]
|
||||
local function get_state(event, buf, data)
|
||||
---@type lz.n.EventOpts[]
|
||||
local state = {}
|
||||
local st = {}
|
||||
while event do
|
||||
---@type lz.n.EventOpts
|
||||
local event_opts = {
|
||||
@@ -98,11 +98,11 @@ local function get_state(event, buf, data)
|
||||
buffer = buf,
|
||||
data = data,
|
||||
}
|
||||
table.insert(state, 1, event_opts)
|
||||
table.insert(st, 1, event_opts)
|
||||
data = nil -- only pass the data to the first event
|
||||
event = event_triggers[event]
|
||||
end
|
||||
return state
|
||||
return st
|
||||
end
|
||||
|
||||
-- Trigger an event
|
||||
@@ -152,24 +152,15 @@ local function add_event(event)
|
||||
once = true,
|
||||
pattern = event.pattern,
|
||||
callback = function(ev)
|
||||
if done or not pending[event.id] then
|
||||
if done or not state.has_pending_plugins(event.id) then
|
||||
return
|
||||
end
|
||||
-- HACK: work-around for https://github.com/neovim/neovim/issues/25526
|
||||
done = true
|
||||
local state = get_state(ev.event, ev.buf, ev.data)
|
||||
-- Make sure trigger_load calls in before hooks can't interfere with the state,
|
||||
-- but they can load a plugin before it's loaded by this handler
|
||||
vim
|
||||
.iter(vim.deepcopy(pending[event.id]))
|
||||
---@param plugin lz.n.Plugin
|
||||
:each(function(_, plugin)
|
||||
if pending[event.id][plugin.name] then
|
||||
loader.load(plugin)
|
||||
end
|
||||
end)
|
||||
local st = get_state(ev.event, ev.buf, ev.data)
|
||||
state.each_pending(event.id, loader.load)
|
||||
---@param s lz.n.EventOpts
|
||||
vim.iter(state):each(function(s)
|
||||
vim.iter(st):each(function(s)
|
||||
trigger(s)
|
||||
end)
|
||||
end,
|
||||
@@ -180,17 +171,14 @@ end
|
||||
function M.add(plugin)
|
||||
---@param event lz.n.Event
|
||||
vim.iter(plugin.event or {}):each(function(event)
|
||||
pending[event.id] = pending[event.id] or {}
|
||||
pending[event.id][plugin.name] = plugin
|
||||
state.insert(event.id, plugin)
|
||||
add_event(event)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param name string
|
||||
function M.del(name)
|
||||
vim.iter(pending):each(function(_, plugins)
|
||||
plugins[name] = nil
|
||||
end)
|
||||
state.del(name)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
---@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
|
||||
@@ -23,8 +23,8 @@ local function parse(value, mode)
|
||||
return ret
|
||||
end
|
||||
|
||||
---@type table<string, table<string, lz.n.Plugin[]>>
|
||||
local pending = {}
|
||||
---@type lz.n.handler.State
|
||||
local state = require("lz.n.handler.state").new()
|
||||
|
||||
---@type lz.n.KeysHandler
|
||||
local M = {
|
||||
@@ -48,7 +48,7 @@ local M = {
|
||||
---@param name string
|
||||
---@return lz.n.Plugin?
|
||||
function M.lookup(name)
|
||||
return require("lz.n.handler.extra").lookup(pending, name)
|
||||
return state.lookup_plugin(name)
|
||||
end
|
||||
|
||||
local skip = { mode = true, id = true, ft = true, rhs = true, lhs = true }
|
||||
@@ -103,16 +103,7 @@ local function add_keys(keys)
|
||||
vim.keymap.set(keys.mode, lhs, function()
|
||||
-- always delete the mapping immediately to prevent recursive mappings
|
||||
del(keys)
|
||||
-- Make sure trigger_load calls in before hooks can't interfere with the state,
|
||||
-- but they can load a plugin before it's loaded by this handler
|
||||
vim
|
||||
.iter(vim.deepcopy(pending[keys.id]))
|
||||
---@param plugin lz.n.Plugin
|
||||
:each(function(_, plugin)
|
||||
if pending[keys.id][plugin.name] then
|
||||
loader.load(plugin)
|
||||
end
|
||||
end)
|
||||
state.each_pending(keys.id, loader.load)
|
||||
-- Create the real buffer-local mapping
|
||||
if keys.ft then
|
||||
set(keys, buf)
|
||||
@@ -136,7 +127,7 @@ local function add_keys(keys)
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
pattern = keys.ft,
|
||||
callback = function(event)
|
||||
if pending[keys.id] then
|
||||
if state.has_pending_plugins[keys.id] then
|
||||
add(event.buf)
|
||||
else
|
||||
-- Only create the mapping if its managed by lz.n
|
||||
@@ -154,17 +145,14 @@ end
|
||||
function M.add(plugin)
|
||||
---@param key lz.n.Keys
|
||||
vim.iter(plugin.keys or {}):each(function(key)
|
||||
pending[key.id] = pending[key.id] or {}
|
||||
pending[key.id][plugin.name] = plugin
|
||||
state.insert(key.id, plugin)
|
||||
add_keys(key)
|
||||
end)
|
||||
end
|
||||
|
||||
---@param name string
|
||||
function M.del(name)
|
||||
vim.iter(pending):each(function(_, plugins)
|
||||
plugins[name] = nil
|
||||
end)
|
||||
state.del(name)
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
---@mod lz.n.handler.state Safe state management for handlers
|
||||
---
|
||||
---@brief [[
|
||||
---This module is to be used by |lz.n.Handler| implementations.
|
||||
---It provides an API for safely managing handler state,
|
||||
---ensuring that `trigger_load` can be called in plugin hooks.
|
||||
---@brief ]]
|
||||
|
||||
local state = {}
|
||||
|
||||
---@return lz.n.handler.State
|
||||
function state.new()
|
||||
---@type table<string, table<string, lz.n.Plugin>>
|
||||
local pending = {}
|
||||
|
||||
---@type lz.n.handler.State
|
||||
return {
|
||||
insert = function(key, plugin)
|
||||
pending[key] = pending[key] or {}
|
||||
pending[key][plugin.name] = plugin
|
||||
end,
|
||||
|
||||
del = function(plugin_name, callback)
|
||||
vim.iter(pending)
|
||||
:filter(function(_, plugins)
|
||||
return plugins[plugin_name] ~= nil
|
||||
end)
|
||||
:each(
|
||||
---@param key string
|
||||
---@param plugins lz.n.Plugin[]
|
||||
function(key, plugins)
|
||||
if callback then
|
||||
callback(key)
|
||||
end
|
||||
plugins[plugin_name] = nil
|
||||
end
|
||||
)
|
||||
end,
|
||||
|
||||
has_pending_plugins = function(key)
|
||||
return pending[key] ~= nil and not vim.tbl_isempty(pending[key])
|
||||
end,
|
||||
|
||||
lookup_plugin = function(plugin_name)
|
||||
return vim
|
||||
.iter(pending)
|
||||
---@param plugins table<string, lz.n.Plugin>
|
||||
:map(function(_, plugins)
|
||||
return plugins[plugin_name]
|
||||
end)
|
||||
---@param plugin lz.n.Plugin?
|
||||
:find(function(plugin)
|
||||
return plugin ~= nil
|
||||
end)
|
||||
end,
|
||||
|
||||
each_pending = function(key, callback)
|
||||
local plugins = pending[key] or {}
|
||||
vim
|
||||
.iter(vim.deepcopy(plugins))
|
||||
---@param plugin lz.n.Plugin
|
||||
:each(function(_, plugin)
|
||||
if pending[key][plugin.name] then
|
||||
callback(plugin)
|
||||
end
|
||||
end)
|
||||
return vim.tbl_keys(plugins)
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
---@class lz.n.handler.State
|
||||
---
|
||||
---Insert a plugin by key.
|
||||
---@field insert fun(key: string, plugin: lz.n.Plugin)
|
||||
---
|
||||
---Remove a plugin by its name.
|
||||
---@field del fun(plugin_name: string, callback?: fun(key: string))
|
||||
---
|
||||
---Check if there are pending plugins for a key
|
||||
---@field has_pending_plugins fun(key: string):boolean
|
||||
---
|
||||
---Lookup a plugin by its name.
|
||||
---@field lookup_plugin fun(plugin_name: string):lz.n.Plugin?
|
||||
---
|
||||
---Safely apply a callback to all pending plugins by key.
|
||||
---@field each_pending fun(key: string, callback: fun(plugin: lz.n.Plugin)): string[]
|
||||
|
||||
return state
|
||||
Reference in New Issue
Block a user