1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-04 04:50:05 +08:00
SpaceVim/bundle/nvim-tree.lua/lua/nvim-tree/git/runner.lua
2022-05-19 09:03:59 +08:00

159 lines
3.8 KiB
Lua
Vendored

local uv = vim.loop
local log = require "nvim-tree.log"
local utils = require "nvim-tree.utils"
local Runner = {}
Runner.__index = Runner
function Runner:_parse_status_output(line)
local status = line:sub(1, 2)
-- removing `"` when git is returning special file status containing spaces
local path = line:sub(4, -2):gsub('^"', ""):gsub('"$', "")
-- replacing slashes if on windows
if vim.fn.has "win32" == 1 then
path = path:gsub("/", "\\")
end
if #status > 0 and #path > 0 then
self.output[utils.path_remove_trailing(utils.path_join { self.project_root, path })] = status
end
return #line
end
function Runner:_handle_incoming_data(prev_output, incoming)
if incoming and utils.str_find(incoming, "\n") then
local prev = prev_output .. incoming
local i = 1
for line in prev:gmatch "[^\n]*\n" do
i = i + self:_parse_status_output(line)
end
return prev:sub(i, -1)
end
if incoming then
return prev_output .. incoming
end
for line in prev_output:gmatch "[^\n]*\n" do
self._parse_status_output(line)
end
return nil
end
function Runner:_getopts(stdout_handle, stderr_handle)
local untracked = self.list_untracked and "-u" or nil
local ignored = (self.list_untracked and self.list_ignored) and "--ignored=matching" or "--ignored=no"
return {
args = { "--no-optional-locks", "status", "--porcelain=v1", ignored, untracked },
cwd = self.project_root,
stdio = { nil, stdout_handle, stderr_handle },
}
end
function Runner:_log_raw_output(output)
if output and type(output) == "string" then
log.raw("git", "%s", output)
end
end
function Runner:_run_git_job()
local handle, pid
local stdout = uv.new_pipe(false)
local stderr = uv.new_pipe(false)
local timer = uv.new_timer()
local function on_finish(rc)
self.rc = rc or 0
if timer:is_closing() or stdout:is_closing() or stderr:is_closing() or (handle and handle:is_closing()) then
return
end
timer:stop()
timer:close()
stdout:read_stop()
stderr:read_stop()
stdout:close()
stderr:close()
if handle then
handle:close()
end
pcall(uv.kill, pid)
end
local opts = self:_getopts(stdout, stderr)
log.line("git", "running job with timeout %dms", self.timeout)
log.line("git", "git %s", table.concat(opts.args, " "))
handle, pid = uv.spawn(
"git",
opts,
vim.schedule_wrap(function(rc)
on_finish(rc)
end)
)
timer:start(
self.timeout,
0,
vim.schedule_wrap(function()
on_finish(-1)
end)
)
local output_leftover = ""
local function manage_stdout(err, data)
if err then
return
end
self:_log_raw_output(data)
output_leftover = self:_handle_incoming_data(output_leftover, data)
end
local function manage_stderr(_, data)
self:_log_raw_output(data)
end
uv.read_start(stdout, vim.schedule_wrap(manage_stdout))
uv.read_start(stderr, vim.schedule_wrap(manage_stderr))
end
function Runner:_wait()
local function is_done()
return self.rc ~= nil
end
while not vim.wait(30, is_done) do
end
end
-- This module runs a git process, which will be killed if it takes more than timeout which defaults to 400ms
function Runner.run(opts)
local ps = log.profile_start("git job %s", opts.project_root)
local self = setmetatable({
project_root = opts.project_root,
list_untracked = opts.list_untracked,
list_ignored = opts.list_ignored,
timeout = opts.timeout or 400,
output = {},
rc = nil, -- -1 indicates timeout
}, Runner)
self:_run_git_job()
self:_wait()
log.profile_end(ps, "git job %s", opts.project_root)
if self.rc == -1 then
log.line("git", "job timed out")
elseif self.rc ~= 0 then
log.line("git", "job failed with return code %d", self.rc)
else
log.line("git", "job success")
end
return self.output
end
return Runner