Skip to content

Commit

Permalink
feat: extend lz.n with custom handlers (#17)
Browse files Browse the repository at this point in the history
added require('lz.n').register_handler(lz.n.HandlerSpec)

added lz.n.Plugin.extras for extra spec items from custom handlers.

feat: extend lz.n with custom handlers

feat: extend lz.n with custom handlers

added tests

Update spec/register_handler_spec.lua



Update spec/register_handler_spec.lua



feat: extend lz.n with custom handlers

Update README.md



Update lua/lz/n/handler/init.lua

Co-authored-by: Marc Jakobi <[email protected]>
  • Loading branch information
BirdeeHub and mrcjkb authored Jun 24, 2024
1 parent d530764 commit d61186f
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 26 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,42 @@ Or
├── init.lua
```

### Custom Handlers

You may register your own handlers to lazy-load plugins via
other triggers not already covered by the plugin spec.

You should register all handlers before calling `require('lz.n').load`,
because they will not be retroactively applied to
the `load` calls that occur before they are registered.

The `register_handler` function returns a boolean that indicates success.

`require("lz.n").register_handler(handler: lz.n.Handler): boolean`

#### lz.n.Handler

<!-- markdownlint-disable MD013 -->
| Property | Type | Description |
|----------|------|-------------|
| spec_field | `string` | the `lz.n.PluginSpec` field defined by 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 |
<!-- markdownlint-enable MD013 -->

When writing custom handlers,
you can load the plugin and run the hooks from
the spec with the following function:

```lua
---@type fun(plugins: string | lz.n.Plugin | string[] | lz.n.Plugin[])
require('lz.n').trigger_load
```

The function accepts plugin names or parsed plugin specs.
It will call the handler's `del` function (if it exists) after the `before` hooks,
and before `load` of the plugin's spec.

## :green_heart: Contributing

All contributions are welcome!
Expand Down
2 changes: 1 addition & 1 deletion lua/lz/n/handler/cmd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local loader = require("lz.n.loader")
---@type lz.n.CmdHandler
local M = {
pending = {},
type = "cmd",
spec_field = "cmd",
}

---@param cmd string
Expand Down
2 changes: 1 addition & 1 deletion lua/lz/n/handler/colorscheme.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ local loader = require("lz.n.loader")
---@type lz.n.ColorschemeHandler
local M = {
pending = {},
type = "colorscheme",
augroup = nil,
spec_field = "colorscheme",
}

---@param plugin lz.n.Plugin
Expand Down
2 changes: 1 addition & 1 deletion lua/lz/n/handler/event.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ local M = {
pending = {},
events = {},
group = vim.api.nvim_create_augroup("lz_n_handler_event", { clear = true }),
type = "event",
spec_field = "event",
---@param spec lz.n.EventSpec
parse = function(spec)
local ret = lz_n_events[spec]
Expand Down
2 changes: 1 addition & 1 deletion lua/lz/n/handler/ft.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ local event = require("lz.n.handler.event")
---@type lz.n.FtHandler
local M = {
pending = {},
type = "ft",
spec_field = "ft",
---@param value string
---@return lz.n.Event
parse = function(value)
Expand Down
42 changes: 26 additions & 16 deletions lua/lz/n/handler/init.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
---@class lz.n.Handler
---@field type lz.n.HandlerTypes
---@field pending table<string, table<string, string>> -- key: plugin_name: plugin_name
---@field add fun(plugin: lz.n.Plugin)
---@field del? fun(plugin: lz.n.Plugin)

local M = {}

---@enum lz.n.HandlerTypes
M.types = {
cmd = "cmd",
event = "event",
ft = "ft",
keys = "keys",
colorscheme = "colorscheme",
}

local handlers = {
cmd = require("lz.n.handler.cmd"),
event = require("lz.n.handler.event"),
Expand All @@ -23,6 +8,31 @@ local handlers = {
colorscheme = require("lz.n.handler.colorscheme"),
}

---@param spec lz.n.PluginSpec
---@return boolean
function M.is_lazy(spec)
---@diagnostic disable-next-line: undefined-field
return spec.lazy or vim.iter(handlers):any(function(spec_field, _)
return spec[spec_field] ~= nil
end)
end

---@param handler lz.n.Handler
---@return boolean success
function M.register_handler(handler)
if handlers[handler.spec_field] == nil then
handlers[handler.spec_field] = handler
return true
else
vim.notify(
"Handler already exists for " .. handler.spec_field .. ". Refusing to register new handler.",
vim.log.levels.ERROR,
{ title = "lz.n" }
)
return false
end
end

---@param plugin lz.n.Plugin
local function enable(plugin)
for _, handler in pairs(handlers) do
Expand All @@ -32,7 +42,7 @@ end

function M.disable(plugin)
for _, handler in pairs(handlers) do
if type(handler.del) == "function" then
if handler.del then
handler.del(plugin)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lua/lz/n/handler/keys.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ local loader = require("lz.n.loader")
---@type lz.n.KeysHandler
local M = {
pending = {},
type = "keys",
spec_field = "keys",
---@param value string|lz.n.KeysSpec
---@param mode? string
---@return lz.n.Keys
Expand Down
6 changes: 6 additions & 0 deletions lua/lz/n/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ local deferred_ui_enter = vim.schedule_wrap(function()
vim.api.nvim_exec_autocmds("User", { pattern = "DeferredUIEnter", modeline = false })
end)

---@type fun(handler: lz.n.Handler): boolean
M.register_handler = require("lz.n.handler").register_handler

---@type fun(plugins: string | lz.n.Plugin | string[] | lz.n.Plugin[])
M.trigger_load = require("lz.n.loader").load

---@overload fun(spec: lz.n.Spec)
---@overload fun(import: string)
function M.load(spec)
Expand Down
5 changes: 5 additions & 0 deletions lua/lz/n/meta.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,10 @@ error("Cannot import a meta module")
--- Takes the plugin name (not the module name). Defaults to |packadd| if not set.
--- @field load? fun(name: string)

--- @class lz.n.Handler
--- @field spec_field string
--- @field add fun(plugin: lz.n.Plugin)
--- @field del? fun(plugin: lz.n.Plugin)

--- @type lz.n.Config
vim.g.lz_n = vim.g.lz_n
6 changes: 1 addition & 5 deletions lua/lz/n/spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,7 @@ local function parse(spec)
table.insert(result.colorscheme, _colorscheme_spec)
end
end
result.lazy = result.lazy
or result.event ~= nil
or result.keys ~= nil
or result.cmd ~= nil
or result.colorscheme ~= nil
result.lazy = require("lz.n.handler").is_lazy(spec)
return result
end

Expand Down
47 changes: 47 additions & 0 deletions spec/register_handler_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---@diagnostic disable: invisible
vim.g.lz_n = {
load = function() end,
}
local lz_n = require("lz.n")
local spy = require("luassert.spy")

describe("handlers.custom", function()
---@class TestHandler: lz.n.Handler
---@type TestHandler
local hndl = {
spec_field = "testfield",
add = function(_) end,
del = function(_) end,
}
local addspy = spy.on(hndl, "add")
local delspy = spy.on(hndl, "del")
it("Duplicate handlers fail to register", function()
local notispy = spy.new(function() end)
-- NOTE: teardown fails if you don't temporarily replace vim.notify
local og_notify = vim.notify
vim.notify = notispy
assert.False(lz_n.register_handler(require("lz.n.handler.ft")))
assert.spy(notispy).called(1)
vim.notify = og_notify
end)
it("can add plugins to the handler", function()
assert.True(lz_n.register_handler(hndl))
lz_n.load({
"testplugin",
testfield = { "a", "b" },
})
assert.spy(addspy).called_with({
name = "testplugin",
testfield = { "a", "b" },
lazy = true,
})
end)
it("loading a plugin removes it from the handler", function()
lz_n.trigger_load("testplugin")
assert.spy(delspy).called_with({
name = "testplugin",
testfield = { "a", "b" },
lazy = true,
})
end)
end)

0 comments on commit d61186f

Please sign in to comment.