feat: spec.rocks is no longer needed & added support for installing any luarock

This commit is contained in:
Folke Lemaitre 2024-06-24 14:14:41 +02:00
parent b3ee5b96f2
commit fcfd54835d
10 changed files with 120 additions and 235 deletions

View File

@ -226,9 +226,6 @@ M.mapleader = nil
---@type string
M.maplocalleader = nil
---@type {specs:string, tree:string, path:string, cpath:string}
M.rocks = {}
function M.headless()
return #vim.api.nvim_list_uis() == 0
end
@ -279,17 +276,6 @@ function M.setup(opts)
M.mapleader = vim.g.mapleader
M.maplocalleader = vim.g.maplocalleader
M.rocks = {
specs = M.options.rocks.root .. "/specs",
tree = M.options.rocks.root .. "/tree",
path = M.options.rocks.root .. "/tree/share/lua/5.1",
cpath = M.options.rocks.root .. "/tree/lib/lua/5.1",
}
vim.fn.mkdir(M.rocks.specs, "p")
vim.fn.mkdir(M.rocks.tree, "p")
package.path = package.path .. ";" .. M.rocks.path .. "/?.lua;" .. M.rocks.path .. "/?/init.lua;"
package.cpath = package.cpath .. ";" .. M.rocks.cpath .. "/?." .. (jit.os:find("Windows") and "dll" or "so") .. ";"
if M.headless() then
require("lazy.view.commands").setup()
end

View File

@ -69,9 +69,8 @@ function M.install_missing()
for _, plugin in pairs(Config.plugins) do
local installed = plugin._.installed
local has_errors = Plugin.has_errors(plugin)
local rocks_installed = plugin._.rocks_installed ~= false
if not has_errors and not (installed and rocks_installed) then
if not has_errors and not (installed and not plugin._.build) then
for _, colorscheme in ipairs(Config.options.install.colorscheme) do
if colorscheme == "default" then
break
@ -344,6 +343,10 @@ function M._load(plugin, reason, opts)
M.add_to_rtp(plugin)
if plugin._.pkg and plugin._.pkg.source == "rockspec" then
M.add_to_luapath(plugin)
end
if plugin.dependencies then
Util.try(function()
M.load(plugin.dependencies, {})
@ -487,6 +490,15 @@ function M.add_to_rtp(plugin)
vim.opt.rtp = rtp
end
---@param plugin LazyPlugin
function M.add_to_luapath(plugin)
local root = Config.options.rocks.root .. "/" .. plugin.name
local path = root .. "/share/lua/5.1"
local cpath = root .. "/lib/lua/5.1"
package.path = package.path .. ";" .. path .. "/?.lua;" .. path .. "/?/init.lua;"
package.cpath = package.cpath .. ";" .. cpath .. "/?." .. (jit.os:find("Windows") and "dll" or "so") .. ";"
end
function M.source(path)
Util.track({ runtime = path })
Util.try(function()

View File

@ -33,10 +33,11 @@ function M:load_pkgs()
if not Config.options.pkg.enabled then
return
end
local specs = Pkg.spec()
for dir, spec in pairs(specs) do
local meta, fragment = self:add(spec)
local specs = Pkg.get()
for dir, pkg in pairs(specs) do
local meta, fragment = self:add(pkg.spec)
if meta and fragment then
meta._.pkg = pkg
-- tag all package fragments as optional
for _, fid in ipairs(meta._.frags) do
local frag = self.fragments:get(fid)
@ -165,18 +166,23 @@ function M:_rebuild(name)
assert(#plugin._.frags > 0, "no fragments found for plugin " .. name)
---@type table<number, boolean>
local done = {}
for _, fid in ipairs(plugin._.frags) do
local fragment = self.fragments:get(fid)
assert(fragment, "fragment " .. fid .. " not found, for plugin " .. name)
---@diagnostic disable-next-line: no-unknown
super = setmetatable(fragment.spec, super and { __index = super } or nil)
plugin._.dep = plugin._.dep and fragment.dep
plugin.optional = plugin.optional and (rawget(fragment.spec, "optional") == true)
plugin.url = fragment.url or plugin.url
if not done[fid] then
done[fid] = true
local fragment = self.fragments:get(fid)
assert(fragment, "fragment " .. fid .. " not found, for plugin " .. name)
---@diagnostic disable-next-line: no-unknown
super = setmetatable(fragment.spec, super and { __index = super } or nil)
plugin._.dep = plugin._.dep and fragment.dep
plugin.optional = plugin.optional and (rawget(fragment.spec, "optional") == true)
plugin.url = fragment.url or plugin.url
-- dependencies
for _, dep in ipairs(fragment.deps or {}) do
table.insert(plugin.dependencies, self.fragments:get(dep).name)
-- dependencies
for _, dep in ipairs(fragment.deps or {}) do
table.insert(plugin.dependencies, self.fragments:get(dep).name)
end
end
end

View File

@ -236,7 +236,7 @@ function M.update_state()
installed[name] = nil
end
require("lazy.manage.rocks").update_state()
M.update_rocks_state()
Config.to_clean = {}
for pack, dir_type in pairs(installed) do
@ -253,6 +253,23 @@ function M.update_state()
end
end
function M.update_rocks_state()
local root = Config.options.rocks.root
---@type table<string,string>
local installed = {}
Util.ls(root, function(_, name, type)
if type == "directory" then
installed[name] = name
end
end)
for _, plugin in pairs(Config.plugins) do
if plugin._.pkg and plugin._.pkg.source == "rockspec" then
plugin._.build = not installed[plugin.name]
end
end
end
---@param path string
function M.local_spec(path)
local file = vim.secure.read(path)
@ -321,11 +338,11 @@ function M.load()
-- copy state. This wont do anything during startup
for name, plugin in pairs(existing) do
if Config.plugins[name] then
local dep = Config.plugins[name]._.dep
local frags = Config.plugins[name]._.frags
local new_state = Config.plugins[name]._
Config.plugins[name]._ = plugin._
Config.plugins[name]._.dep = dep
Config.plugins[name]._.frags = frags
Config.plugins[name]._.dep = new_state.dep
Config.plugins[name]._.frags = new_state.frags
-- Config.plugins[name]._.tasks = new_state.tasks
end
end
Util.track()

View File

@ -82,13 +82,12 @@ function M.install(opts)
pipeline = {
"git.clone",
{ "git.checkout", lockfile = opts.lockfile },
"rocks.install",
"plugin.docs",
"wait",
"plugin.build",
},
plugins = function(plugin)
return plugin.url and not (plugin._.installed and plugin._.rocks_installed ~= false)
return plugin.url and not (plugin._.installed and not plugin._.build)
end,
}, opts):wait(function()
require("lazy.manage.lock").update()
@ -107,7 +106,6 @@ function M.update(opts)
"git.fetch",
"git.status",
{ "git.checkout", lockfile = opts.lockfile },
"rocks.install",
"plugin.docs",
"wait",
"plugin.build",
@ -224,7 +222,7 @@ function M.clear(plugins)
if plugin._.tasks then
---@param task LazyTask
plugin._.tasks = vim.tbl_filter(function(task)
return task:is_running()
return task:is_running() or task.error
end, plugin._.tasks)
end
end

View File

@ -1,90 +0,0 @@
--# selene:allow(incorrect_standard_library_use)
local Config = require("lazy.core.config")
local Util = require("lazy.core.util")
---@class LazyRock
---@field plugin string
---@field name string
---@field spec string
---@field installed boolean
local M = {}
---@type LazyRock[]
M.rocks = {}
---@param ... string
---@return string[]
function M.args(...)
local ret = {
"--tree",
Config.rocks.tree,
"--server",
Config.options.rocks.server,
"--dev",
"--lua-version",
"5.1",
}
vim.list_extend(ret, { ... })
return ret
end
---@param plugin LazyPlugin
function M.get_rockspec(plugin)
local rocks = vim.tbl_map(function(rock)
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(
rockspec_file,
([[
rockspec_format = "3.0"
package = "lazy-%s"
version = "scm-1"
source = { url = "%s" }
dependencies = %s
build = { type = "builtin" }
]]):format(plugin.name, plugin.url, vim.inspect(plugin.rocks))
)
return rockspec_file
end
function M.update_state()
local root = Config.rocks.tree .. "/lib/luarocks/rocks-5.1"
---@type table<string,string>
local installed = {}
Util.ls(root, function(_, name, type)
if type == "directory" then
installed[name] = name
end
end)
---@type LazyRock[]
local rocks = {}
M.rocks = rocks
for _, plugin in pairs(Config.plugins) do
if plugin.rocks then
plugin._.rocks = {}
plugin._.rocks_installed = true
for _, spec in ipairs(plugin.rocks) do
spec = vim.trim(spec)
local name = spec:gsub("%s.*", "")
local rock = {
plugin = plugin.name,
name = name,
spec = spec,
installed = installed[name] ~= nil,
}
if rock.name ~= "lua" then
plugin._.rocks_installed = plugin._.rocks_installed and rock.installed
table.insert(plugin._.rocks, rock)
table.insert(rocks, rock)
end
end
end
end
end
return M

View File

@ -1,57 +0,0 @@
local Rocks = require("lazy.manage.rocks")
---@type table<string, LazyTaskDef>
local M = {}
local running = false
local has_rocks = nil ---@type boolean?
M.install = {
skip = function(plugin)
return plugin._.rocks_installed ~= false
end,
run = function(self)
if has_rocks == nil then
has_rocks = vim.fn.executable("luarocks") == 1
end
if not has_rocks then
self.error = "This plugin has luarocks dependencies,\nbut the `luarocks` executable is not found.\nPlease install https://luarocks.org/ to continue.\n"
.. "luarock deps: "
.. vim.inspect(self.plugin.rocks)
return
end
local started = false
local function install()
started = true
self.status = "luarocks (install)"
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
self:spawn("luarocks", {
args = Rocks.args("install", "--deps-mode", "one", "--deps-only", Rocks.get_rockspec(self.plugin)),
on_exit = function(ok)
running = false
if ok then
self.plugin._.rocks_installed = true
end
end,
})
end
local timer = vim.uv.new_timer()
timer:start(0, 100, function()
if not running then
running = true
timer:stop()
vim.schedule(install)
end
end)
self.status = "luarocks (pending)"
table.insert(self._running, function()
return not started
end)
end,
}
return M

View File

@ -2,23 +2,28 @@ local Config = require("lazy.core.config")
local Util = require("lazy.util")
local M = {}
M.VERSION = 7
M.VERSION = 8
M.dirty = false
---@alias LazyPkgSpec LazySpec | fun():LazySpec
---@class LazyPkg
---@field source string
---@field name string
---@field dir string
---@field source "lazy" | "packspec" | "rockspec"
---@field file string
---@field spec LazyPluginSpec
---@class LazyPkgSpec
---@field file string
---@field spec? LazySpec
---@class LazyPkgInput: LazyPkg
---@field spec? LazySpec|fun():LazySpec
---@field code? string
---@class LazyPkgSource
---@field get fun(plugin:LazyPlugin):LazyPkgInput?
---@field name string
---@field get fun(plugin:LazyPlugin):LazyPkgSpec?
---@class LazyPkgCache
---@field pkgs LazyPkg[]
---@field version number
---@type table<string, LazyPkg>?
M.cache = nil
@ -27,27 +32,40 @@ function M.update()
---@type LazyPkgSource[]
local sources = {}
for _, s in ipairs(Config.options.pkg.sources) do
sources[#sources + 1] = require("lazy.pkg." .. s)
sources[#sources + 1] = {
name = s,
get = require("lazy.pkg." .. s).get,
}
end
M.cache = {}
---@type LazyPkgCache
local ret = {
version = M.VERSION,
pkgs = {},
}
for _, plugin in pairs(Config.plugins) do
if plugin._.installed then
for _, source in ipairs(sources) do
local spec = source.get(plugin)
if spec then
spec.name = plugin.name
---@type LazyPkg
local pkg = {
name = plugin.name,
dir = plugin.dir,
source = source.name,
file = spec.file,
spec = spec.spec or {},
}
if type(spec.code) == "string" then
spec.spec = { _raw = spec.code }
spec.code = nil
pkg.spec = { _raw = spec.code }
end
M.cache[plugin.dir] = spec
table.insert(ret.pkgs, pkg)
break
end
end
end
end
local code = "return " .. Util.dump({ version = M.VERSION, specs = M.cache })
local code = "return " .. Util.dump(ret)
vim.fn.mkdir(vim.fn.fnamemodify(Config.options.pkg.cache, ":h"), "p")
Util.write_file(Config.options.pkg.cache, code)
M.dirty = false
@ -63,9 +81,18 @@ local function _load()
if not chunk then
error(err)
end
---@type LazyPkgCache?
local ret = chunk()
if ret and ret.version == M.VERSION then
M.cache = ret.specs
M.cache = {}
for _, pkg in ipairs(ret.pkgs) do
if type(pkg.spec) == "function" then
pkg.spec = pkg.spec()
end
-- wrap in the scope of the plugin
pkg.spec = { pkg.name, specs = pkg.spec }
M.cache[pkg.dir] = pkg
end
end
end, "Error loading pkg:")
end
@ -80,34 +107,12 @@ end
---@param dir string
---@return LazyPkg?
---@overload fun():table<string, LazyPkg>
function M.get(dir)
local ret = M.cache[dir]
if not ret then
return
if dir then
return M.cache[dir]
end
if type(ret.spec) == "function" then
ret.spec = ret.spec()
end
return ret
end
function M.spec()
---@type table<string,LazyPluginSpec>
local ret = {}
for dir in pairs(M.cache) do
local pkg = M.get(dir)
local spec = pkg and pkg.spec
if pkg and spec then
spec = type(spec) == "table" and vim.deepcopy(spec) or spec
---@cast spec LazySpec
ret[dir] = { pkg.name, specs = spec }
end
end
return ret
return M.cache
end
return setmetatable(M, {

View File

@ -4,7 +4,7 @@ local Util = require("lazy.core.util")
local M = {}
M.dev_suffix = "-scm-1.rockspec"
M.dev_suffix = "-1.rockspec"
M.skip = { "lua" }
---@class RockSpec
@ -14,7 +14,7 @@ M.skip = { "lua" }
---@field dependencies string[]
---@param plugin LazyPlugin
---@return LazyPkg?
---@return LazyPkgSpec?
function M.get(plugin)
local rockspec_file ---@type string?
Util.ls(plugin.dir, function(path, name, t)
@ -43,13 +43,21 @@ function M.get(plugin)
return not vim.tbl_contains(M.skip, name)
end, rockspec and rockspec.dependencies or {})
return #rocks > 0
local use = #rocks > 0
use = true
local lazy = nil
if not vim.uv.fs_stat(plugin.dir .. "/lua") then
lazy = false
end
return use
and {
source = "rockspec",
file = vim.fn.fnamemodify(rockspec_file, ":t"),
spec = {
plugin.name,
rocks = rocks,
build = "rockspec",
lazy = lazy,
},
}
or nil

View File

@ -8,6 +8,7 @@
---@field dep? boolean True if this plugin is only in the spec as a dependency
---@field dir? string Explicit dir or dev set for this plugin
---@field dirty? boolean
---@field build? boolean
---@field frags? number[]
---@field handlers? LazyPluginHandlers
---@field installed? boolean
@ -15,13 +16,12 @@
---@field kind? LazyPluginKind
---@field loaded? {[string]:string}|{time:number}
---@field outdated? boolean
---@field rocks? LazyRock[]
---@field rocks_installed? boolean
---@field rtp_loaded? boolean
---@field tasks? LazyTask[]
---@field updated? {from:string, to:string}
---@field updates? {from:GitInfo, to:GitInfo}
---@field working? boolean
---@field pkg? LazyPkg
---@alias PluginOpts table|fun(self:LazyPlugin, opts:table):table?
@ -29,7 +29,7 @@
---@field init? fun(self:LazyPlugin) Will always be run
---@field deactivate? fun(self:LazyPlugin) Unload/Stop a plugin
---@field config? fun(self:LazyPlugin, opts:table)|true Will be executed when loading the plugin
---@field build? string|fun(self:LazyPlugin)|(string|fun(self:LazyPlugin))[]
---@field build? string|async fun(self:LazyPlugin)|(string|async fun(self:LazyPlugin))[]
---@field opts? PluginOpts
---@class LazyPluginHandlers