295 lines
6.7 KiB
Lua
295 lines
6.7 KiB
Lua
local log = require("log")
|
|
|
|
---@class ow.Plugin.Spec
|
|
---@field [1] string
|
|
---@field name? string
|
|
---@field version? string | vim.VersionRange
|
|
---@field lazy? boolean
|
|
---@field build? string[] | fun(self: ow.Plugin): boolean
|
|
|
|
---@class ow.Plugin : ow.Plugin.Spec
|
|
---@field path string
|
|
|
|
---@type ow.Plugin[]
|
|
local plugins = {}
|
|
|
|
---@param path string
|
|
---@return boolean success
|
|
---@return string? err
|
|
local function exec(path)
|
|
local chunk, load_err = loadfile(path)
|
|
if not chunk then
|
|
return false, load_err
|
|
end
|
|
|
|
local ok, call_err = pcall(chunk)
|
|
if not ok then
|
|
return false, call_err
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
---@param plugin ow.Plugin
|
|
local function load(plugin)
|
|
vim.cmd.packadd(plugin.name)
|
|
|
|
local name = plugin.name:match("[^.]+"):lower()
|
|
local path = string.format(
|
|
"%s/lua/plugins/%s.lua",
|
|
vim.fn.stdpath("config"),
|
|
name
|
|
)
|
|
|
|
if vim.uv.fs_stat(path) then
|
|
local ok, err = exec(path)
|
|
if not ok then
|
|
log.error("Failed to load %s: %s", name, err)
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param spec string | ow.Plugin.Spec
|
|
---@return vim.pack.Spec
|
|
local function to_pack_spec(spec)
|
|
if type(spec) == "string" then
|
|
return { src = spec }
|
|
end
|
|
|
|
return {
|
|
src = spec[1],
|
|
name = spec.name,
|
|
version = spec.version,
|
|
data = {
|
|
lazy = spec.lazy,
|
|
build = spec.build,
|
|
},
|
|
}
|
|
end
|
|
|
|
---@param plugin ow.Plugin
|
|
---@return boolean success
|
|
local function run_build(plugin)
|
|
if type(plugin.build) == "function" then
|
|
return plugin.build(plugin)
|
|
elseif type(plugin.build) == "table" then
|
|
local ret = vim.system(
|
|
plugin.build --[[@as table]],
|
|
{ cwd = plugin.path }
|
|
):wait()
|
|
return ret.code == 0 and ret.signal == 0
|
|
end
|
|
|
|
log.error("invalid build parameter for %s", plugin.name)
|
|
return false
|
|
end
|
|
|
|
---@class ow.Plugin.PackEvent.Data
|
|
---@field active boolean
|
|
---@field kind "install" | "update" | "delete"
|
|
---@field spec vim.pack.Spec
|
|
---@field path string
|
|
|
|
---@class ow.Plugin.PackEvent : vim.api.keyset.create_autocmd.callback_args
|
|
---@field data ow.Plugin.PackEvent.Data
|
|
|
|
---@param plugin ow.Plugin
|
|
---@param events ow.Plugin.PackEvent[]
|
|
---@return boolean success
|
|
local function handle_pack_events(plugin, events)
|
|
if not plugin.build then
|
|
return true
|
|
end
|
|
|
|
for _, ev in ipairs(events) do
|
|
if ev.data.spec.name == plugin.name
|
|
and ev.event == "PackChanged"
|
|
and (ev.data.kind == "install" or ev.data.kind == "update")
|
|
then
|
|
vim.cmd.packadd(plugin.name)
|
|
return run_build(plugin)
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
---@type uv.uv_fs_event_t?
|
|
local watcher = nil
|
|
|
|
local M = {}
|
|
|
|
function M.get_paths()
|
|
local paths = {}
|
|
|
|
for _, plugin in ipairs(plugins) do
|
|
table.insert(paths, plugin.path)
|
|
end
|
|
|
|
return paths
|
|
end
|
|
|
|
---@return string[]
|
|
function M.get_names()
|
|
local names = {}
|
|
|
|
for _, plugin in ipairs(plugins) do
|
|
table.insert(names, plugin.name:match("[^.]+"):lower())
|
|
end
|
|
|
|
return names
|
|
end
|
|
|
|
---@param name string
|
|
function M.reload(name)
|
|
local path = string.format(
|
|
"%s/lua/plugins/%s.lua",
|
|
vim.fn.stdpath("config"),
|
|
name
|
|
)
|
|
|
|
if not vim.uv.fs_stat(path) then
|
|
log.error("No config file found for %s", name)
|
|
return
|
|
end
|
|
|
|
local ok, err = exec(path)
|
|
if ok then
|
|
log.info("Reloaded %s", name)
|
|
else
|
|
log.error("Failed to reload %s: %s", name, err)
|
|
end
|
|
end
|
|
|
|
function M.watch()
|
|
if watcher then
|
|
return
|
|
end
|
|
|
|
local plugins_dir = string.format(
|
|
"%s/lua/plugins",
|
|
vim.fn.stdpath("config")
|
|
)
|
|
|
|
watcher = vim.uv.new_fs_event()
|
|
if not watcher then
|
|
log.error("Failed to create fs_event watcher")
|
|
return
|
|
end
|
|
|
|
---@type table<string, uv.uv_timer_t>
|
|
local timers = {}
|
|
|
|
watcher:start(
|
|
plugins_dir,
|
|
{},
|
|
vim.schedule_wrap(function(err, filename)
|
|
if err then
|
|
log.error("Watch error: %s", err)
|
|
return
|
|
end
|
|
|
|
if not filename or not filename:match("%.lua$") then
|
|
return
|
|
end
|
|
|
|
if timers[filename] then
|
|
timers[filename]:stop()
|
|
else
|
|
timers[filename] = vim.uv.new_timer()
|
|
end
|
|
|
|
timers[filename]:start(
|
|
100,
|
|
0,
|
|
vim.schedule_wrap(function()
|
|
timers[filename]:stop()
|
|
timers[filename]:close()
|
|
timers[filename] = nil
|
|
|
|
local path = plugins_dir .. "/" .. filename
|
|
if not vim.uv.fs_stat(path) then
|
|
return
|
|
end
|
|
|
|
local ok, load_err = exec(path)
|
|
if ok then
|
|
log.info("Reloaded %s", filename)
|
|
else
|
|
log.error(
|
|
"Failed to reload %s: %s",
|
|
filename,
|
|
load_err
|
|
)
|
|
end
|
|
end)
|
|
)
|
|
end)
|
|
)
|
|
end
|
|
|
|
function M.unwatch()
|
|
if not watcher then
|
|
return
|
|
end
|
|
|
|
watcher:stop()
|
|
watcher:close()
|
|
watcher = nil
|
|
end
|
|
|
|
---@param specs (string | ow.Plugin.Spec)[]
|
|
function M.setup(specs)
|
|
---@type ow.Plugin.PackEvent[]
|
|
local events = {}
|
|
local id = vim.api.nvim_create_autocmd(
|
|
{ "PackChangedPre", "PackChanged" },
|
|
{
|
|
---@param ev ow.Plugin.PackEvent
|
|
callback = function(ev)
|
|
table.insert(events, ev)
|
|
end
|
|
}
|
|
)
|
|
vim.pack.add(vim.tbl_map(to_pack_spec, specs), {
|
|
load = function(data)
|
|
local d = data.spec.data or {}
|
|
---@type ow.Plugin
|
|
local plugin = {
|
|
[1] = data.spec.src,
|
|
name = data.spec.name,
|
|
version = data.spec.version,
|
|
lazy = d.lazy or false,
|
|
build = d.build,
|
|
path = data.path,
|
|
}
|
|
table.insert(plugins, plugin)
|
|
end
|
|
})
|
|
vim.api.nvim_del_autocmd(id)
|
|
|
|
local lazy_plugins = {}
|
|
for _, plugin in ipairs(plugins) do
|
|
if not handle_pack_events(plugin, events) then
|
|
return
|
|
end
|
|
|
|
if plugin.lazy then
|
|
table.insert(lazy_plugins, plugin)
|
|
else
|
|
load(plugin)
|
|
end
|
|
end
|
|
|
|
vim.api.nvim_create_autocmd("UIEnter", {
|
|
once = true,
|
|
callback = vim.schedule_wrap(function()
|
|
for _, plugin in ipairs(lazy_plugins) do
|
|
load(plugin)
|
|
end
|
|
end)
|
|
})
|
|
end
|
|
|
|
return M
|