-
Notifications
You must be signed in to change notification settings - Fork 295
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
364 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |