feat(build): build files and functions are now async. use coroutine.yield to interrupt and report progress

This commit is contained in:
Folke Lemaitre 2024-06-24 14:16:00 +02:00
parent 73ea05feda
commit 97704cf48a
No known key found for this signature in database
GPG Key ID: 41F8B1FBACAE2040
4 changed files with 102 additions and 38 deletions

View File

@ -183,12 +183,6 @@ M.defaults = {
skip_if_doc_exists = true, skip_if_doc_exists = true,
}, },
state = vim.fn.stdpath("state") .. "/lazy/state.json", -- state info for checker and other things state = vim.fn.stdpath("state") .. "/lazy/state.json", -- state info for checker and other things
build = {
-- Plugins can provide a `build.lua` file that will be executed when the plugin is installed
-- or updated. When the plugin spec also has a `build` command, the plugin's `build.lua` not be
-- executed. In this case, a warning message will be shown.
warn_on_override = true,
},
-- Enable profiling of lazy.nvim. This will add some overhead, -- Enable profiling of lazy.nvim. This will add some overhead,
-- so only enable this when you are debugging lazy.nvim -- so only enable this when you are debugging lazy.nvim
profiling = { profiling = {

View File

@ -74,6 +74,32 @@ function Task:start()
self:_check() self:_check()
end end
---@param fn async fun()
function Task:async(fn)
local co = coroutine.create(fn)
local check = vim.uv.new_check()
check:start(vim.schedule_wrap(function()
local status = coroutine.status(co)
if status == "dead" then
check:stop()
self:_check()
elseif status == "suspended" then
local ok, res = coroutine.resume(co)
if not ok then
error(res)
elseif res then
self.status = res
self.output = self.output .. "\n" .. res
vim.api.nvim_exec_autocmds("User", { pattern = "LazyRender", modeline = false })
end
end
end))
table.insert(self._running, function()
return check:is_active()
end)
end
---@private ---@private
function Task:_check() function Task:_check()
for _, state in ipairs(self._running) do for _, state in ipairs(self._running) do

View File

@ -8,25 +8,67 @@ local M = {}
---@param plugin LazyPlugin ---@param plugin LazyPlugin
local function get_build_file(plugin) local function get_build_file(plugin)
for _, path in ipairs({ "build.lua", "build/init.lua" }) do for _, path in ipairs({ "build.lua", "build/init.lua" }) do
path = plugin.dir .. "/" .. path if Util.file_exists(plugin.dir .. "/" .. path) then
if Util.file_exists(path) then
return path return path
end end
end end
end end
local B = {}
---@param task LazyTask
function B.rockspec(task)
local root = Config.options.rocks.root .. "/" .. task.plugin.name
vim.fn.mkdir(root, "p")
task:spawn("luarocks", {
args = {
"--tree",
root,
"--server",
Config.options.rocks.server,
"--dev",
"--lua-version",
"5.1",
"make",
"--force-fast",
},
cwd = task.plugin.dir,
})
end
---@param task LazyTask
---@param build string
function B.cmd(task, build)
local cmd = vim.api.nvim_parse_cmd(build:sub(2), {}) --[[@as vim.api.keyset.cmd]]
task.output = vim.api.nvim_cmd(cmd, { output = true })
end
---@param task LazyTask
---@param build string
function B.shell(task, build)
local shell = vim.env.SHELL or vim.o.shell
local shell_args = shell:find("cmd.exe", 1, true) and "/c" or "-c"
task:spawn(shell, {
args = { shell_args, build },
cwd = task.plugin.dir,
})
end
M.build = { M.build = {
---@param opts? {force:boolean} ---@param opts? {force:boolean}
skip = function(plugin, opts) skip = function(plugin, opts)
if opts and opts.force then if opts and opts.force then
return false return false
end end
return not (plugin._.dirty and (plugin.build or get_build_file(plugin))) return not ((plugin._.dirty or plugin._.build) and (plugin.build or get_build_file(plugin)))
end, end,
run = function(self) run = function(self)
vim.cmd([[silent! runtime plugin/rplugin.vim]]) vim.cmd([[silent! runtime plugin/rplugin.vim]])
if self.plugin.build ~= "rockspec" then
Loader.load(self.plugin, { task = "build" }) Loader.load(self.plugin, { task = "build" })
end
local builders = self.plugin.build local builders = self.plugin.build
@ -35,39 +77,29 @@ M.build = {
return return
end end
local build_file = get_build_file(self.plugin) builders = builders or get_build_file(self.plugin)
if build_file then
if builders then
if Config.options.build.warn_on_override then
Util.warn(
("Plugin **%s** provides its own build script, but you also defined a `build` command.\nThe `build.lua` file will not be used"):format(
self.plugin.name
)
)
end
else
builders = function()
Loader.source(build_file)
end
end
end
if builders then if builders then
builders = type(builders) == "table" and builders or { builders } builders = type(builders) == "table" and builders or { builders }
---@cast builders (string|fun(LazyPlugin))[] ---@cast builders (string|fun(LazyPlugin))[]
for _, build in ipairs(builders) do for _, build in ipairs(builders) do
if type(build) == "string" and build:sub(1, 1) == ":" then if type(build) == "function" then
local cmd = vim.api.nvim_parse_cmd(build:sub(2), {}) self:async(function()
self.output = vim.api.nvim_cmd(cmd, { output = true })
elseif type(build) == "function" then
build(self.plugin) build(self.plugin)
end)
elseif build == "rockspec" then
B.rockspec(self)
elseif build:sub(1, 1) == ":" then
B.cmd(self, build)
elseif build:match("%.lua$") then
local file = self.plugin.dir .. "/" .. build
local chunk, err = loadfile(file)
if not chunk or err then
error(err)
end
self:async(chunk)
else else
local shell = vim.env.SHELL or vim.o.shell B.shell(self, build)
local shell_args = shell:find("cmd.exe", 1, true) and "/c" or "-c"
self:spawn(shell, {
args = { shell_args, build },
cwd = self.plugin.dir,
})
end end
end end
end end

View File

@ -231,6 +231,18 @@ function M.markdown(msg, opts)
) )
end end
---@async
---@param ms number
function M.sleep(ms)
local continue = false
vim.defer_fn(function()
continue = true
end, ms)
while not continue do
coroutine.yield()
end
end
function M._dump(value, result) function M._dump(value, result)
local t = type(value) local t = type(value)
if t == "number" or t == "boolean" then if t == "number" or t == "boolean" then