diff --git a/lua/lazy/core/loader.lua b/lua/lazy/core/loader.lua index 597933b..c5cdca2 100644 --- a/lua/lazy/core/loader.lua +++ b/lua/lazy/core/loader.lua @@ -464,10 +464,8 @@ function M.add_to_rtp(plugin) local rtp = vim.api.nvim_get_runtime_file("", true) local idx_dir, idx_after - local is_win = jit.os:find("Windows") - for i, path in ipairs(rtp) do - if is_win then + if Util.is_win then path = Util.norm(path) end if path == Config.me then diff --git a/lua/lazy/core/meta.lua b/lua/lazy/core/meta.lua index 6978d96..4645f1b 100644 --- a/lua/lazy/core/meta.lua +++ b/lua/lazy/core/meta.lua @@ -33,9 +33,7 @@ function M:load_pkgs() if not Config.options.pkg.enabled then return end - local have_rockspec = false for _, pkg in ipairs(Pkg.get()) do - have_rockspec = have_rockspec or pkg.source == "rockspec" local meta, fragment = self:add(pkg.spec) if meta and fragment then meta._.pkg = pkg @@ -48,12 +46,6 @@ function M:load_pkgs() self.pkgs[pkg.dir] = fragment.id end end - if have_rockspec then - local hererocks = Pkg.hererocks() - if hererocks then - self:add(hererocks) - end - end end --- Remove a plugin and all its fragments. diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua index cfc7b62..8c746a9 100644 --- a/lua/lazy/core/plugin.lua +++ b/lua/lazy/core/plugin.lua @@ -335,6 +335,20 @@ function M.load() lazy._.loaded = {} end + -- add hererocks when enabled and needed + if Config.options.rocks.hererocks then + for _, plugin in pairs(Config.spec.plugins) do + if plugin.build == "rockspec" then + Config.spec.meta:add({ + "luarocks/hererocks", + build = "rockspec", + lazy = true, + }) + break + end + end + end + local existing = Config.plugins Config.plugins = Config.spec.plugins -- copy state. This wont do anything during startup diff --git a/lua/lazy/core/util.lua b/lua/lazy/core/util.lua index 9a65a85..f42cf6a 100644 --- a/lua/lazy/core/util.lua +++ b/lua/lazy/core/util.lua @@ -5,6 +5,7 @@ local M = {} ---@type LazyProfile[] M._profiles = { { name = "lazy" } } +M.is_win = jit.os:find("Windows") ---@param data (string|{[string]:string})? ---@param time number? diff --git a/lua/lazy/health.lua b/lua/lazy/health.lua index c0a8981..3199f75 100644 --- a/lua/lazy/health.lua +++ b/lua/lazy/health.lua @@ -8,23 +8,62 @@ local start = vim.health.start or vim.health.report_start local ok = vim.health.ok or vim.health.report_ok local warn = vim.health.warn or vim.health.report_warn local error = vim.health.error or vim.health.report_error +local info = vim.health.info or vim.health.report_info + +---@class LazyHealth +---@field error? fun(msg:string) +---@field warn? fun(msg:string) +---@field ok? fun(msg:string) + +---@class LazyHealthHave : LazyHealth +---@field version? string +---@field version_pattern? string +---@field optional? boolean + +---@param cmd string|string[] +---@param opts? LazyHealthHave +function M.have(cmd, opts) + opts = vim.tbl_extend("force", { + error = error, + warn = warn, + ok = ok, + version = "--version", + }, opts or {}) + + cmd = type(cmd) == "table" and cmd or { cmd } + ---@cast cmd string[] + ---@type string? + local found + for _, c in ipairs(cmd) do + if vim.fn.executable(c) == 1 then + local version = vim.fn.system(c .. " " .. opts.version) or "" + version = vim.trim(vim.split(version, "\n")[1]) + version = version:gsub("^%s*" .. vim.pesc(c) .. "%s*", "") + if opts.version_pattern and not version:find(opts.version_pattern, 1, true) then + opts.warn(("`%s` version `%s` needed, but found `%s`"):format(c, opts.version_pattern, version)) + else + found = ("{%s} `%s`"):format(c, version) + break + end + end + end + if found then + opts.ok(found) + return true + else + (opts.optional and opts.warn or opts.error)( + ("{%s} %snot installed"):format( + table.concat(cmd, "} or {"), + opts.version_pattern and "version `" .. opts.version_pattern .. "` " or "" + ) + ) + end +end function M.check() start("lazy.nvim") - if vim.fn.executable("git") == 1 then - ok("'git' installed") - else - error("'git' not installed?") - end - - if Config.options.rocks.enabled then - if vim.fn.executable("luarocks") == 1 then - ok("'luarocks' installed") - else - error("'luarocks' not installed. Either install it or set opts.rocks.enabled = false") - end - end + M.have("git") local sites = vim.opt.packpath:get() local default_site = vim.fn.stdpath("data") .. "/site" @@ -45,7 +84,7 @@ function M.check() ok("no existing packages found by other package managers") end - for _, name in ipairs({ "packer", "plugged", "paq" }) do + for _, name in ipairs({ "packer", "plugged", "paq", "pckr", "mini.deps" }) do for _, path in ipairs(vim.opt.rtp:get()) do if path:find(name, 1, true) then error("Found paths on the rtp from another plugin manager `" .. name .. "`") @@ -82,6 +121,18 @@ function M.check() end end end + + start("luarocks") + if Config.options.rocks.enabled then + if Config.options.rocks.hererocks then + info("checking `hererocks` installation") + else + info("checking `luarocks` installation") + end + require("lazy.pkg.rockspec").check({ error = error, warn = warn, ok = ok }) + else + ok("luarocks disabled") + end end ---@param plugin LazyPlugin diff --git a/lua/lazy/manage/task/init.lua b/lua/lazy/manage/task/init.lua index d86ef5f..df8e792 100644 --- a/lua/lazy/manage/task/init.lua +++ b/lua/lazy/manage/task/init.lua @@ -12,6 +12,7 @@ local Process = require("lazy.manage.process") ---@field output string ---@field status string ---@field error? string +---@field warn? string ---@field private _task fun(task:LazyTask) ---@field private _running LazyPluginState[] ---@field private _started? number @@ -74,6 +75,30 @@ function Task:start() self:_check() end +---@param msg string|string[] +---@param severity? vim.diagnostic.Severity +function Task:notify(msg, severity) + local var = severity == vim.diagnostic.severity.ERROR and "error" + or severity == vim.diagnostic.severity.WARN and "warn" + or "output" + msg = type(msg) == "table" and table.concat(msg, "\n") or msg + ---@cast msg string + ---@diagnostic disable-next-line: no-unknown + self[var] = self[var] and (self[var] .. "\n" .. msg) or msg + self.status = msg + vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false }) +end + +---@param msg string|string[] +function Task:notify_error(msg) + self:notify(msg, vim.diagnostic.severity.ERROR) +end + +---@param msg string|string[] +function Task:notify_warn(msg) + self:notify(msg, vim.diagnostic.severity.WARN) +end + ---@param fn async fun() function Task:async(fn) local co = coroutine.create(fn) diff --git a/lua/lazy/manage/task/plugin.lua b/lua/lazy/manage/task/plugin.lua index cfdf8e0..cb426ec 100644 --- a/lua/lazy/manage/task/plugin.lua +++ b/lua/lazy/manage/task/plugin.lua @@ -1,5 +1,6 @@ local Config = require("lazy.core.config") local Loader = require("lazy.core.loader") +local Rocks = require("lazy.pkg.rockspec") local Util = require("lazy.util") ---@type table @@ -16,52 +17,6 @@ end local B = {} ----@param task LazyTask -function B.rockspec(task) - ---@type table - local env = {} - - local luarocks = "luarocks" - if Config.options.rocks.hererocks then - local is_win = jit.os:find("Windows") - local sep = is_win and ";" or ":" - local hererocks = Config.options.rocks.root .. "/hererocks/bin" - if is_win then - hererocks = hererocks:gsub("/", "\\") - end - local path = vim.split(vim.env.PATH, sep) - table.insert(path, 1, hererocks) - env = { - PATH = table.concat(path, sep), - } - if is_win then - luarocks = luarocks .. ".bat" - end - local plugin = Config.plugins.hererocks - -- hererocks is still building, so skip for now - if plugin and plugin._.build then - return - end - end - - local root = Config.options.rocks.root .. "/" .. task.plugin.name - task:spawn(luarocks, { - args = { - "--tree", - root, - "--server", - Config.options.rocks.server, - "--dev", - "--lua-version", - "5.1", - "make", - "--force-fast", - }, - cwd = task.plugin.dir, - env = env, - }) -end - ---@param task LazyTask ---@param build string function B.cmd(task, build) @@ -114,7 +69,7 @@ M.build = { build(self.plugin) end) elseif build == "rockspec" then - B.rockspec(self) + Rocks.build(self) elseif build:sub(1, 1) == ":" then B.cmd(self, build) elseif build:match("%.lua$") then diff --git a/lua/lazy/pkg/init.lua b/lua/lazy/pkg/init.lua index 3a7dd4c..b9620ca 100644 --- a/lua/lazy/pkg/init.lua +++ b/lua/lazy/pkg/init.lua @@ -112,32 +112,6 @@ local function _load() Util.track() end ----@return LazyPluginSpec?, string? -function M.hererocks() - if not (Config.options.rocks.enabled and Config.options.rocks.hererocks) then - return - end - - local root = Config.options.rocks.root .. "/hererocks" - - local cmd = { - "python", - "hererocks.py", - "--verbose", - "-l", - "5.1", - "-r", - "latest", - root, - } - - return { - "luarocks/hererocks", - lazy = true, - build = table.concat(cmd, " "), - }, root -end - ---@param dir string ---@return LazyPkg? ---@overload fun():LazyPkg[] diff --git a/lua/lazy/pkg/rockspec.lua b/lua/lazy/pkg/rockspec.lua index 0eba34c..abbd0ba 100644 --- a/lua/lazy/pkg/rockspec.lua +++ b/lua/lazy/pkg/rockspec.lua @@ -1,31 +1,8 @@ --# selene:allow(incorrect_standard_library_use) local Config = require("lazy.core.config") +local Health = require("lazy.health") local Util = require("lazy.util") -local M = {} - -M.dev_suffix = "-1.rockspec" -M.skip = { "lua" } - -M.rewrites = { - ["plenary.nvim"] = { "nvim-lua/plenary.nvim", lazy = true }, -} - ----@param plugin LazyPlugin -function M.deps(plugin) - local root = Config.options.rocks.root .. "/" .. plugin.name - local manifest_file = root .. "/lib/luarocks/rocks-5.1/manifest" - local manifest = {} - pcall(function() - local load, err = loadfile(manifest_file, "t", manifest) - if not load then - error(err) - end - load() - end) - return vim.tbl_keys(manifest.repository or {}) -end - ---@class RockSpec ---@field rockspec_format string ---@field package string @@ -33,6 +10,190 @@ end ---@field dependencies string[] ---@field build? {build_type?: string, modules?: any[]} +---@class RockManifest +---@field repository table + +local M = {} + +M.dev_suffix = "-1.rockspec" +M.skip = { "lua" } +M.rewrites = { + ["plenary.nvim"] = { "nvim-lua/plenary.nvim", lazy = true }, +} +M.python = { "python3", "python" } + +---@class HereRocks +M.hererocks = {} + +---@param task LazyTask +function M.hererocks.build(task) + local root = Config.options.rocks.root .. "/hererocks" + + ---@param p string + local python = vim.tbl_filter(function(p) + return vim.fn.executable(p) == 1 + end, M.python)[1] + + task:spawn(python, { + args = { + "hererocks.py", + "--verbose", + "-l", + "5.1", + "-r", + "latest", + root, + }, + cwd = task.plugin.dir, + }) +end + +---@param bin string +function M.hererocks.bin(bin) + local hererocks = Config.options.rocks.root .. "/hererocks/bin" + if Util.is_win then + bin = bin .. ".bat" + end + return Util.norm(hererocks .. "/" .. bin) +end + +-- check if hererocks is building +---@return boolean? +function M.hererocks.building() + return vim.tbl_get(Config.plugins.hererocks or {}, "_", "build") +end + +---@param opts? LazyHealth +function M.check(opts) + opts = vim.tbl_extend("force", { + error = Util.error, + warn = Util.warn, + ok = function() end, + }, opts or {}) + + local ok = false + if Config.options.rocks.hererocks then + if M.hererocks.building() then + ok = true + else + ok = Health.have(M.python, opts) + ok = Health.have(M.hererocks.bin("luarocks")) and ok + ok = Health.have( + M.hererocks.bin("lua"), + vim.tbl_extend("force", opts, { + version = "-v", + version_pattern = "5.1", + }) + ) and ok + end + else + ok = Health.have("luarocks", opts) + ok = ( + Health.have( + { "lua5.1", "lua" }, + vim.tbl_extend("force", opts, { + version = "-v", + version_pattern = "5.1", + }) + ) + ) and ok + end + return ok +end + +---@param task LazyTask +function M.build(task) + if + not M.check({ + error = function(msg) + task:notify_error(msg:gsub("[{}]", "`")) + end, + warn = function(msg) + task:notify_warn(msg) + end, + ok = function(msg) end, + }) + then + task:notify_warn({ + "", + "This plugin requires `luarocks`. Try one of the following:", + " - fix your `luarocks` installation", + Config.options.rocks.hererocks and " - disable *hererocks* with `opts.rocks.hererocks = false`" + or " - enable `hererocks` with `opts.rocks.hererocks = true`", + " - disable `luarocks` support completely with `opts.rocks.enabled = false`", + }) + return + end + + if task.plugin.name == "hererocks" then + return M.hererocks.build(task) + end + + local env = {} + local luarocks = "luarocks" + if Config.options.rocks.hererocks then + -- hererocks is still building, so skip for now + -- a new build will happen in the next round + if M.hererocks.building() then + return + end + + local sep = Util.is_win and ";" or ":" + local hererocks = Config.options.rocks.root .. "/hererocks/bin" + if Util.is_win then + hererocks = hererocks:gsub("/", "\\") + end + local path = vim.split(vim.env.PATH, sep) + table.insert(path, 1, hererocks) + env = { + PATH = table.concat(path, sep), + } + if Util.is_win then + luarocks = luarocks .. ".bat" + end + end + + local root = Config.options.rocks.root .. "/" .. task.plugin.name + task:spawn(luarocks, { + args = { + "--tree", + root, + "--server", + Config.options.rocks.server, + "--dev", + "--lua-version", + "5.1", + "make", + "--force-fast", + }, + cwd = task.plugin.dir, + env = env, + }) +end + +---@param file string +---@return table? +function M.parse(file) + local ret = {} + return pcall(function() + loadfile(file, "t", ret)() + end) and ret or nil +end + +---@param plugin LazyPlugin +function M.deps(plugin) + local root = Config.options.rocks.root .. "/" .. plugin.name + ---@type RockManifest? + local manifest = M.parse(root .. "/lib/luarocks/rocks-5.1/manifest") + return manifest and vim.tbl_keys(manifest.repository or {}) +end + +---@param file string +---@return RockSpec? +function M.rockspec(file) + return M.parse(file) +end + ---@param plugin LazyPlugin ---@return LazyPkgSpec? function M.get(plugin) @@ -56,14 +217,7 @@ function M.get(plugin) return end - ---@type RockSpec? - ---@diagnostic disable-next-line: missing-fields - local rockspec = {} - local load, err = loadfile(rockspec_file, "t", rockspec) - if not load then - error(err) - end - load() + local rockspec = M.rockspec(rockspec_file) if not rockspec then return diff --git a/lua/lazy/view/colors.lua b/lua/lazy/view/colors.lua index 83269f9..e55c56b 100644 --- a/lua/lazy/view/colors.lua +++ b/lua/lazy/view/colors.lua @@ -31,18 +31,20 @@ M.colors = { ButtonActive = "Visual", TaskOutput = "MsgArea", -- task output TaskError = "ErrorMsg", -- task errors + TaskWarning = "WarningMsg", -- task errors Dir = "@markup.link", -- directory Url = "@markup.link", -- url + Bold = { bold = true }, + Italic = { italic = true }, } M.did_setup = false function M.set_hl() for hl_group, link in pairs(M.colors) do - vim.api.nvim_set_hl(0, "Lazy" .. hl_group, { - link = link, - default = true, - }) + local hl = type(link) == "table" and link or { link = link } + hl.default = true + vim.api.nvim_set_hl(0, "Lazy" .. hl_group, hl) end end @@ -54,6 +56,11 @@ function M.setup() M.did_setup = true M.set_hl() + vim.api.nvim_create_autocmd("VimEnter", { + callback = function() + M.set_hl() + end, + }) vim.api.nvim_create_autocmd("ColorScheme", { callback = function() M.set_hl() diff --git a/lua/lazy/view/render.lua b/lua/lazy/view/render.lua index 7d91dd8..61faca1 100644 --- a/lua/lazy/view/render.lua +++ b/lua/lazy/view/render.lua @@ -394,6 +394,11 @@ function M:diagnostics(plugin) message = task.name .. " failed", severity = vim.diagnostic.severity.ERROR, }) + elseif task.warn then + self:diagnostic({ + message = task.name .. " warning", + severity = vim.diagnostic.severity.WARN, + }) end end end @@ -434,6 +439,22 @@ function M:plugin(plugin) { name = plugin.name, from = plugin_start, to = self:row() - 1, kind = plugin._.kind } end +---@param str string +---@param hl? string|Extmark +---@param opts? {indent?: number, prefix?: string, wrap?: boolean} +function M:markdown(str, hl, opts) + local lines = vim.split(str, "\n") + for _, line in ipairs(lines) do + self:append(line, hl, opts):highlight({ + ["`.-`"] = "@markup.raw.markdown_inline", + ["%*.-%*"] = "LazyItalic", + ["%*%*.-%*%*"] = "LazyBold", + ["^%s*-"] = "Special", + }) + self:nl() + end +end + ---@param plugin LazyPlugin function M:tasks(plugin) for _, task in ipairs(plugin._.tasks or {}) do @@ -443,13 +464,16 @@ function M:tasks(plugin) self:nl() end if task.error then - self:append(vim.trim(task.error), "LazyTaskError", { indent = 6 }) - self:nl() - elseif task.name == "log" then + self:markdown(task.error, "LazyTaskError", { indent = 6 }) + end + if task.warn then + self:markdown(task.warn, "LazyTaskWarning", { indent = 6 }) + end + if not task.error and not task.warn and task.name == "log" then self:log(task) - elseif self.view:is_selected(plugin) and task.output ~= "" and task.output ~= task.error then - self:append(vim.trim(task.output), "LazyTaskOutput", { indent = 6 }) - self:nl() + end + if self.view:is_selected(plugin) or (task.error or task.warn) then + self:markdown(vim.trim(task.output), "LazyTaskOutput", { indent = 6 }) end end end @@ -512,7 +536,7 @@ function M:details(plugin) end end local rocks = require("lazy.pkg.rockspec").deps(plugin) - if not vim.tbl_isempty(rocks) then + if rocks then table.insert(props, { "rocks", vim.inspect(rocks) }) end