Skip to content

Commit

Permalink
feat: plugin manager tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Nov 20, 2022
1 parent 0219a53 commit a612e6f
Show file tree
Hide file tree
Showing 3 changed files with 364 additions and 0 deletions.
90 changes: 90 additions & 0 deletions lua/lazy/process.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
local M = {}

---@alias ProcessOpts {args: string[], cwd?: string, on_line?:fun(string), on_exit?: fun(ok:boolean, output:string)}

function M.spawn(cmd, opts)
opts = opts or {}
local env = {
"GIT_TERMINAL_PROMPT=0",
"GIT_SSH_COMMAND=ssh -oBatchMode=yes",
}

for key, value in
pairs(vim.loop.os_environ() --[[@as string[] ]])
do
table.insert(env, key .. "=" .. value)
end
local stdout = vim.loop.new_pipe()
local stderr = vim.loop.new_pipe()
local output = ""
---@type vim.loop.Process
local handle = nil
handle = vim.loop.spawn(cmd, {
stdio = { nil, stdout, stderr },
args = opts.args,
cwd = opts.cwd,
env = env,
}, function(exit_code)
handle:close()
stdout:close()
stderr:close()
local check = vim.loop.new_check()
check:start(function()
if not stdout:is_closing() or not stderr:is_closing() then
return
end
check:stop()
if opts.on_exit then
output = output:gsub("[^\r\n]+\r", "")
vim.schedule(function()
opts.on_exit(exit_code == 0, output)
end)
end
end)
end)
if not handle then
if opts.on_exit then
opts.on_exit(false, "Failed to spawn process " .. cmd .. " " .. vim.inspect(opts))
end
return
end
local function on_output(err, data)
assert(not err, err)
if data then
output = output .. data:gsub("\r\n", "\n")
local lines = vim.split(vim.trim(output:gsub("\r$", "")):gsub("[^\n\r]+\r", ""), "\n")
if opts.on_line then
vim.schedule(function()
opts.on_line(lines[#lines])
end)
end
end
end
vim.loop.read_start(stdout, on_output)
vim.loop.read_start(stderr, on_output)
return handle
end
-- FIXME: can be removed?
function M.all_done(slot0)
for slot4, slot5 in ipairs(slot0) do
if slot5 and not slot5:is_closing() then
return false
end
end
return true
end
return M
68 changes: 68 additions & 0 deletions lua/lazy/runner.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---@class Runner
---@field _tasks LazyTask[]
local Runner = {}

function Runner.new()
local self = setmetatable({}, {
__index = Runner,
})
self._tasks = {}

return self
end

---@param task LazyTask
function Runner:add(task)
table.insert(self._tasks, task)
task:start()
end

function Runner:is_empty()
return #self._tasks == 0
end

---@return LazyPlugin[]
function Runner:plugins()
---@param task LazyTask
return vim.tbl_map(function(task)
return task.plugin
end, self._tasks)
end

function Runner:tasks()
return self._tasks
end

---@param cb? fun()
function Runner:wait(cb)
if #self._tasks == 0 then
return cb and cb()
end

local done = false
local check = vim.loop.new_check()

check:start(function()
for _, task in ipairs(self._tasks) do
if task.running then
return
end
end

check:stop()

done = true

if cb then
vim.schedule(cb)
end
end)

if not cb then
while not done do
vim.wait(100)
end
end
end

return Runner
206 changes: 206 additions & 0 deletions lua/lazy/task.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
local Process = require("lazy.process")
local Loader = require("lazy.loader")

---@class LazyTask
---@field plugin LazyPlugin
---@field type TaskType
---@field running boolean
local Task = {}

---@alias TaskType "update"|"install"|"run"|"clean"

---@param plugin LazyPlugin
---@param type TaskType
function Task.new(plugin, type)
local self = setmetatable({}, {
__index = Task,
})
self.plugin = plugin
self.type = type
self.output = ""
self.status = ""
plugin.tasks = plugin.tasks or {}
table.insert(plugin.tasks, self)
return self
end

function Task:_done()
self.running = false

vim.cmd("do User LazyRender")
end

function Task:clean()
local function rm(path)
for _, entry in ipairs(Util.scandir(path)) do
if entry.type == "directory" then
rm(entry.path)
else
vim.loop.fs_unlink(entry.path)
end
end

vim.loop.fs_rmdir(path)
end

local stat = vim.loop.fs_stat(self.plugin.dir)

if stat.type == "directory" then
rm(self.plugin.dir)
else
vim.loop.fs_unlink(self.plugin.dir)
end

self.plugin.installed = false
self.running = false
end

function Task:install()
if Util.file_exists(self.plugin.uri) then
vim.loop.fs_symlink(self.plugin.uri, self.plugin.dir, {
dir = true,
})
vim.opt.runtimepath:append(self.plugin.uri)
self:_done()
else
local args = {
"clone",
self.plugin.uri,
"--depth=1",
"--recurse-submodules",
"--shallow-submodules",
"--progress",
}

if self.plugin.branch then
vim.list_extend(args, {
"-b",
self.plugin.branch,
})
end

table.insert(args, self.plugin.dir)
self:spawn("git", {
args = args,
on_exit = function(ok)
if ok then
self.plugin.installed = true
self.plugin.dirty = true
end
end,
})
end
end

function Task:run()
Loader.load(self.plugin)

local run = self.plugin.run

if run then
if type(run) == "string" and run:sub(1, 1) == ":" then
vim.cmd(run:sub(2))
elseif type(run) == "function" then
run()
else
local args = vim.split(run, "%s+")

return self:spawn(table.remove(args, 1), {
args = args,
cwd = self.plugin.dir,
})
end
end

self:_done()
end

---@param cmd string
---@param opts ProcessOpts
function Task:spawn(cmd, opts)
opts = opts or {}
local on_line = opts.on_line
local on_exit = opts.on_exit

function opts.on_line(line)
self.status = line

if on_line then
pcall(on_line, line)
end

vim.cmd("do User LazyRender")
end

function opts.on_exit(ok, output)
self.output = output

if not ok then
self.error = output
end

if on_exit then
pcall(on_exit, ok, output)
end

self:_done()
end

Process.spawn(cmd, opts)
end

function Task:start()
self.running = true
local ok, err = pcall(function()
if self.type == "update" then
self:update()
elseif self.type == "install" then
self:install()
elseif self.type == "run" then
self:run()
elseif self.type == "clean" then
self:clean()
end
end)

if not ok then
self.error = err or "failed"

self:_done()
end
end

function Task:update()
if Util.file_exists(self.plugin.uri) then
if vim.loop.fs_realpath(self.plugin.uri) ~= vim.loop.fs_realpath(self.plugin.dir) then
vim.loop.fs_unlink(self.plugin.dir)
vim.loop.fs_symlink(self.plugin.uri, self.plugin.dir, {
dir = true,
})
vim.opt.runtimepath:append(self.plugin.uri)
end

self:_done()
else
local args = {
"pull",
"--recurse-submodules",
"--update-shallow",
"--progress",
}
local git = Util.git_info(self.plugin.dir)

self:spawn("git", {
args = args,
cwd = self.plugin.dir,
on_exit = function(ok)
if ok then
local git_new = Util.git_info(self.plugin.dir)
self.plugin.dirty = not vim.deep_equal(git, git_new)
end
end,
})
end
end

return Task

0 comments on commit a612e6f

Please sign in to comment.