feat: added support for plugin packages by lazy, rockspec and packspec

This commit is contained in:
Folke Lemaitre 2024-06-18 21:54:54 +02:00
parent 2d4f2cb507
commit 1e0ab0574e
11 changed files with 281 additions and 171 deletions

View File

@ -31,8 +31,19 @@ M.defaults = {
-- increase downloads a lot. -- increase downloads a lot.
filter = true, filter = true,
}, },
pkg = {
enabled = true,
cache = vim.fn.stdpath("state") .. "/lazy/pkg-cache.lua",
versions = true, -- Honor versions in pkg sources
sources = {
"lazy",
"rockspec",
"packspec",
},
},
rocks = { rocks = {
root = vim.fn.stdpath("data") .. "/lazy-rocks", root = vim.fn.stdpath("data") .. "/lazy-rocks",
server = "https://nvim-neorocks.github.io/rocks-binaries/",
}, },
dev = { dev = {
---@type string | fun(plugin: LazyPlugin): string directory where you store your local plugin projects ---@type string | fun(plugin: LazyPlugin): string directory where you store your local plugin projects
@ -182,11 +193,6 @@ M.defaults = {
-- Track each new require in the Lazy profiling tab -- Track each new require in the Lazy profiling tab
require = false, require = false,
}, },
packspec = {
enabled = true,
versions = true, -- Honor dependency versions in packspecs
path = vim.fn.stdpath("state") .. "/lazy/packspec.lua",
},
debug = false, debug = false,
} }
@ -306,9 +312,9 @@ function M.setup(opts)
-- useful for plugin developers when making changes to a packspec file -- useful for plugin developers when making changes to a packspec file
vim.api.nvim_create_autocmd("BufWritePost", { vim.api.nvim_create_autocmd("BufWritePost", {
pattern = "package.lua", pattern = "lazy.lua",
callback = function() callback = function()
require("lazy.view.commands").cmd("packspec") require("lazy.view.commands").cmd("pkg")
end, end,
}) })
end, end,

View File

@ -1,125 +0,0 @@
local Config = require("lazy.core.config")
local Util = require("lazy.util")
---@class PackSpec
---@field dependencies? table<string, string>
---@field lazy? LazyPluginSpec
local M = {}
M.lazy_file = "lazy.lua"
M.pkg_file = "pkg.json"
M.enable_lazy_file = false
---@alias LazyPkg {lazy?:(fun():LazySpec), pkg?:PackSpec}
---@type table<string, LazyPkg>
M.packspecs = nil
---@type table<string, LazySpec>
M.specs = {}
---@param spec LazyPkg
---@param plugin LazyPlugin
---@return LazySpec?
local function convert(plugin, spec)
---@type LazySpec
local ret = {}
local pkg = spec.pkg
if pkg then
if pkg.dependencies then
for url, version in pairs(pkg.dependencies) do
if (not Config.options.packspec.versions) or version == "*" or version == "" then
version = nil
end
-- HACK: Add `.git` to github urls
if url:find("github") and not url:match("%.git$") then
url = url .. ".git"
end
ret[#ret + 1] = { url = url, version = version }
end
end
local p = pkg.lazy
if p then
p.url = p.url or plugin.url
p.dir = p.dir or plugin.dir
ret[#ret + 1] = p
end
end
if spec.lazy then
ret[#ret + 1] = spec.lazy()
end
return ret
end
local function load()
Util.track("packspec")
M.packspecs = {}
if vim.loop.fs_stat(Config.options.packspec.path) then
Util.try(function()
M.packspecs = loadfile(Config.options.packspec.path)()
end, "Error loading packspecs:")
end
Util.track()
end
---@param plugin LazyPlugin
---@return LazySpec?
function M.get(plugin)
if not M.packspecs then
load()
end
if not M.packspecs[plugin.dir] then
return
end
M.specs[plugin.dir] = M.specs[plugin.dir] or convert(plugin, M.packspecs[plugin.dir])
return vim.deepcopy(M.specs[plugin.dir])
end
function M.update()
local ret = {}
for _, plugin in pairs(Config.plugins) do
local spec = {
pkg = M.pkg(plugin),
lazy = M.enable_lazy_file and M.lazy_pkg(plugin) or nil,
}
if not vim.tbl_isempty(spec) then
ret[plugin.dir] = spec
end
end
local code = "return " .. Util.dump(ret)
Util.write_file(Config.options.packspec.path, code)
M.packspecs = nil
M.specs = {}
end
---@param plugin LazyPlugin
function M.lazy_pkg(plugin)
local file = Util.norm(plugin.dir .. "/" .. M.lazy_file)
if Util.file_exists(file) then
---@type LazySpec
local chunk = Util.try(function()
return loadfile(file)
end, "`" .. M.lazy_file .. "` for **" .. plugin.name .. "** has errors:")
if chunk then
return { _raw = ([[function() %s end]]):format(Util.read_file(file)) }
else
Util.error("Invalid `package.lua` for **" .. plugin.name .. "**")
end
end
end
---@param plugin LazyPlugin
function M.pkg(plugin)
local file = Util.norm(plugin.dir .. "/" .. M.pkg_file)
if Util.file_exists(file) then
---@type PackSpec
return Util.try(function()
return vim.json.decode(Util.read_file(file))
end, "`" .. M.pkg_file .. "` for **" .. plugin.name .. "** has errors:")
end
end
return M

View File

@ -1,5 +1,5 @@
local Config = require("lazy.core.config") local Config = require("lazy.core.config")
local Packspec = require("lazy.core.packspec") local Pkg = require("lazy.pkg")
local Util = require("lazy.core.util") local Util = require("lazy.core.util")
---@class LazyCorePlugin ---@class LazyCorePlugin
@ -16,7 +16,7 @@ M.loading = false
---@field notifs {msg:string, level:number, file?:string}[] ---@field notifs {msg:string, level:number, file?:string}[]
---@field importing? string ---@field importing? string
---@field optional? boolean ---@field optional? boolean
---@field packspecs table<string, boolean> ---@field pkgs table<string, boolean>
---@field names table<string,string> ---@field names table<string,string>
local Spec = {} local Spec = {}
M.Spec = Spec M.Spec = Spec
@ -35,7 +35,7 @@ function Spec.new(spec, opts)
self.dirty = {} self.dirty = {}
self.notifs = {} self.notifs = {}
self.ignore_installed = {} self.ignore_installed = {}
self.packspecs = {} self.pkgs = {}
self.optional = opts and opts.optional self.optional = opts and opts.optional
self.names = {} self.names = {}
if spec then if spec then
@ -173,11 +173,11 @@ function Spec:add(plugin, results)
end end
-- import the plugin's spec -- import the plugin's spec
if Config.options.packspec.enabled and plugin.dir and not self.packspecs[plugin.dir] then if Config.options.pkg.enabled and plugin.dir and not self.pkgs[plugin.dir] then
self.packspecs[plugin.dir] = true self.pkgs[plugin.dir] = true
local packspec = Packspec.get(plugin) local pkg = Pkg.get_spec(plugin)
if packspec then if pkg then
self:normalize(packspec, nil, true) self:normalize(pkg, nil)
end end
end end

View File

@ -93,7 +93,7 @@ function M.install(opts)
}, opts):wait(function() }, opts):wait(function()
require("lazy.manage.lock").update() require("lazy.manage.lock").update()
require("lazy.help").update() require("lazy.help").update()
require("lazy.core.packspec").update() require("lazy.pkg").update()
end) end)
end end
@ -119,7 +119,7 @@ function M.update(opts)
}, opts):wait(function() }, opts):wait(function()
require("lazy.manage.lock").update() require("lazy.manage.lock").update()
require("lazy.help").update() require("lazy.help").update()
require("lazy.core.packspec").update() require("lazy.pkg").update()
end) end)
end end
-- --

View File

@ -16,33 +16,32 @@ M.rocks = {}
---@param ... string ---@param ... string
---@return string[] ---@return string[]
function M.args(...) function M.args(...)
local ret = { "--tree", Config.rocks.tree, "--lua-version", "5.1" } local ret = {
"--tree",
Config.rocks.tree,
"--server",
Config.options.rocks.server,
"--dev",
"--lua-version",
"5.1",
}
vim.list_extend(ret, { ... }) vim.list_extend(ret, { ... })
return ret return ret
end end
function M.parse(rockspec_file)
local rockspec = {}
local ret, err = loadfile(rockspec_file, "t", rockspec)
if not ret then
error(err)
end
ret()
return rockspec
end
-- dd(M.parse("/home/folke/.local/share/nvim/lazy/neorg/neorg-scm-1.rockspec"))
---@param plugin LazyPlugin ---@param plugin LazyPlugin
function M.get_rockspec(plugin) function M.get_rockspec(plugin)
assert(plugin.rocks and #plugin.rocks > 0, plugin.name .. " has no rocks") local rocks = vim.tbl_map(function(rock)
local rockspec_file = Config.rocks.specs .. "/lazy-" .. plugin.name .. "-0.0-0.rockspec" return rock.name
end, plugin._.rocks)
assert(rocks and #rocks > 0, plugin.name .. " has no rocks")
local rockspec_file = Config.rocks.specs .. "/lazy-" .. plugin.name .. "-scm-1.rockspec"
require("lazy.util").write_file( require("lazy.util").write_file(
rockspec_file, rockspec_file,
([[ ([[
rockspec_format = "3.0" rockspec_format = "3.0"
package = "lazy-%s" package = "lazy-%s"
version = "0.0-0" version = "scm-1"
source = { url = "%s" } source = { url = "%s" }
dependencies = %s dependencies = %s
build = { type = "builtin" } build = { type = "builtin" }
@ -78,6 +77,7 @@ function M.update_state()
spec = spec, spec = spec,
installed = installed[name] ~= nil, installed = installed[name] ~= nil,
} }
if rock.name ~= "lua" then
plugin._.rocks_installed = plugin._.rocks_installed and rock.installed plugin._.rocks_installed = plugin._.rocks_installed and rock.installed
table.insert(plugin._.rocks, rock) table.insert(plugin._.rocks, rock)
table.insert(rocks, rock) table.insert(rocks, rock)
@ -85,5 +85,6 @@ function M.update_state()
end end
end end
end end
end
return M return M

92
lua/lazy/pkg/init.lua Normal file
View File

@ -0,0 +1,92 @@
local Config = require("lazy.core.config")
local Util = require("lazy.util")
local M = {}
---@alias LazyPkgSpec LazySpec | fun():LazySpec
---@class LazyPkg
---@field source string
---@field file string
---@field spec? LazySpec
---@field chunk? string|fun():LazySpec
---@class LazyPkgSource
---@field get fun(plugin:LazyPlugin):LazyPkg
---@type table<string, LazyPkg>?
M.cache = nil
function M.update()
---@type LazyPkgSource[]
local sources = {}
for _, s in ipairs(Config.options.pkg.sources) do
sources[#sources + 1] = require("lazy.pkg." .. s)
end
---@type table<string, LazyPkg>
local ret = {}
for _, plugin in pairs(Config.plugins) do
for _, source in ipairs(sources) do
local spec = source.get(plugin)
if spec then
if type(spec.chunk) == "function" then
spec.chunk = string.dump(spec.chunk)
end
ret[plugin.dir] = spec
break
end
end
end
local code = "return " .. Util.dump(ret)
Util.write_file(Config.options.pkg.cache, code)
M.cache = nil
end
local function _load()
Util.track("pkg")
M.cache = {}
if vim.uv.fs_stat(Config.options.pkg.cache) then
Util.try(function()
local chunk, err = loadfile(Config.options.pkg.cache)
if not chunk then
error(err)
end
M.cache = chunk()
end, "Error loading pkg:")
end
Util.track()
end
---@param plugin LazyPlugin
---@return LazyPkg?
function M.get(plugin)
if not M.cache then
_load()
end
local ret = M.cache[plugin.dir]
if not ret then
return
end
if ret.chunk and not ret.spec then
if type(ret.chunk) == "string" then
ret.chunk = load(ret.chunk, "@" .. plugin.dir)
end
ret.spec = ret.chunk()
ret.chunk = nil
end
return ret
end
---@param plugin LazyPlugin
---@return LazySpec?
function M.get_spec(plugin)
local pkg = M.get(plugin)
local spec = pkg and pkg.spec
return spec and type(spec) == "table" and vim.deepcopy(spec) or spec
end
return M

28
lua/lazy/pkg/lazy.lua Normal file
View File

@ -0,0 +1,28 @@
local Util = require("lazy.util")
local M = {}
M.lazy_file = "lazy.lua"
---@param plugin LazyPlugin
---@return LazyPkg?
function M.get(plugin)
local file = Util.norm(plugin.dir .. "/" .. M.lazy_file)
if Util.file_exists(file) then
---@type fun(): LazySpec
local chunk = Util.try(function()
local ret, err = loadfile(file)
return err and error(err) or ret
end, "`" .. M.lazy_file .. "` for **" .. plugin.name .. "** has errors:")
if not chunk then
Util.error("Invalid `" .. M.lazy_file .. "` for **" .. plugin.name .. "**")
end
return {
source = "lazy",
file = M.lazy_file,
chunk = chunk,
}
end
end
return M

54
lua/lazy/pkg/packspec.lua Normal file
View File

@ -0,0 +1,54 @@
local Util = require("lazy.util")
---@class PackSpec
---@field dependencies? table<string, string>
---@field lazy? LazyPluginSpec
---
local M = {}
M.pkg_file = "pkg.json"
---@param plugin LazyPlugin
---@return LazyPkg?
function M.get(plugin)
local file = Util.norm(plugin.dir .. "/" .. M.pkg_file)
if not Util.file_exists(file) then
return
end
---@type PackSpec
local pkg = Util.try(function()
return vim.json.decode(Util.read_file(file))
end, "`" .. M.pkg_file .. "` for **" .. plugin.name .. "** has errors:")
if not pkg then
return
end
---@type LazySpec
local ret = {}
if pkg.dependencies then
for url, version in pairs(pkg.dependencies) do
-- HACK: Add `.git` to github urls
if url:find("github") and not url:match("%.git$") then
url = url .. ".git"
end
ret[#ret + 1] = { url = url, version = version }
end
end
local p = pkg.lazy
if p then
p.url = p.url or plugin.url
p.dir = p.dir or plugin.dir
ret[#ret + 1] = p
end
if pkg.lazy then
ret[#ret + 1] = pkg.lazy
end
return {
source = "lazy",
file = M.pkg_file,
spec = ret,
}
end
return M

55
lua/lazy/pkg/rockspec.lua Normal file
View File

@ -0,0 +1,55 @@
--# selene:allow(incorrect_standard_library_use)
local Util = require("lazy.core.util")
local M = {}
M.dev_suffix = "-scm-1.rockspec"
M.skip = { "lua" }
---@class RockSpec
---@field rockspec_format string
---@field package string
---@field version string
---@field dependencies string[]
---@param plugin LazyPlugin
---@return LazyPkg?
function M.get(plugin)
local rockspec_file ---@type string?
Util.ls(plugin.dir, function(path, name, t)
if t == "file" and name:sub(-#M.dev_suffix) == M.dev_suffix then
rockspec_file = path
return false
end
end)
if not rockspec_file then
return
end
---@type RockSpec?
local rockspec = {}
local ret, err = loadfile(rockspec_file, "t", rockspec)
if not ret then
error(err)
end
ret()
return rockspec
and rockspec.package
and {
source = "rockspec",
file = rockspec_file,
spec = {
dir = plugin.dir,
url = plugin.url,
rocks = vim.tbl_filter(function(dep)
local name = dep:gsub("%s.*", "")
return not vim.tbl_contains(M.skip, name)
end, rockspec.dependencies),
},
}
or nil
end
return M

View File

@ -241,22 +241,21 @@ function M._dump(value, result)
table.insert(result, value._raw) table.insert(result, value._raw)
elseif t == "table" then elseif t == "table" then
table.insert(result, "{") table.insert(result, "{")
local i = 1 for _, v in ipairs(value) do
M._dump(v, result)
table.insert(result, ",")
end
---@diagnostic disable-next-line: no-unknown ---@diagnostic disable-next-line: no-unknown
for k, v in pairs(value) do for k, v in pairs(value) do
if k == i then if type(k) == "string" then
elseif type(k) == "string" then
if k:match("^[a-zA-Z]+$") then if k:match("^[a-zA-Z]+$") then
table.insert(result, ("%s="):format(k)) table.insert(result, ("%s="):format(k))
else else
table.insert(result, ("[%q]="):format(k)) table.insert(result, ("[%q]="):format(k))
end end
else
table.insert(result, k .. "=")
end
M._dump(v, result) M._dump(v, result)
table.insert(result, ",") table.insert(result, ",")
i = i + 1 end
end end
table.insert(result, "}") table.insert(result, "}")
else else

View File

@ -35,11 +35,11 @@ M.commands = {
vim.cmd.checkhealth("lazy") vim.cmd.checkhealth("lazy")
end, end,
pkg = function(opts) pkg = function(opts)
local Pkg = require("lazy.core.packspec") local Pkg = require("lazy.pkg")
Pkg.update() Pkg.update()
require("lazy.manage.reloader").reload({ require("lazy.manage.reloader").reload({
{ {
file = "packspec", --Config.options.packspec.path, file = "pkg",
what = "changed", what = "changed",
}, },
}) })