Files

118 lines
2.9 KiB
Lua

local util = require("git.core.util")
local M = {}
local SENTINEL = "__NVIM_GIT_EDIT__"
local SCRIPT = string.format(
[=[set -eu
flag="${TMPDIR:-/tmp}/nvim-git-editor-$$.done"
trap 'rm -f "$flag"' EXIT
abs=$(realpath "$1")
printf '%s\t%%s\t%%s\n' "$flag" "$abs" >&2
while [ ! -e "$flag" ]; do
sleep 0.05
done
]=],
SENTINEL
)
---@param s string
---@return string
local function shq(s)
return "'" .. s:gsub("'", "'\\''") .. "'"
end
local GIT_EDITOR = "sh -c " .. shq(SCRIPT) .. " --"
---@param on_open fun(file_path: string, done: fun())
---@return fun(err: string?, data: string?), fun(result: vim.SystemCompleted)
local function build_stderr_handler(on_open)
local pending = ""
local stderr_buf = {}
local function dispatch(flag_path, abs_path)
vim.schedule(function()
local fired = false
local function done()
if fired then
return
end
fired = true
local fw = io.open(flag_path, "w")
if fw then
fw:close()
end
end
local ok, err = pcall(on_open, abs_path, done)
if not ok then
util.error("git.editor on_open failed: %s", tostring(err))
done()
end
end)
end
local pattern = "^" .. SENTINEL .. "\t(.-)\t(.+)$"
local function on_stderr(_, data)
if not data or data == "" then
return
end
pending = pending .. data
while true do
local nl = pending:find("\n", 1, true)
if not nl then
break
end
local line = pending:sub(1, nl - 1)
pending = pending:sub(nl + 1)
local flag, abs = line:match(pattern)
if flag then
dispatch(flag, abs)
else
table.insert(stderr_buf, line)
table.insert(stderr_buf, "\n")
end
end
end
local function finalize(result)
if pending ~= "" then
table.insert(stderr_buf, pending)
end
result.stderr = table.concat(stderr_buf)
end
return on_stderr, finalize
end
---@param cmd string[]
---@param opts? { cwd?: string, env?: table<string,string> }
---@param on_open fun(file_path: string, done: fun())
---@param on_exit fun(result: vim.SystemCompleted)
function M.run(cmd, opts, on_open, on_exit)
opts = opts or {}
local on_stderr, finalize = build_stderr_handler(on_open)
local env = vim.tbl_extend("force", opts.env or {}, {
GIT_EDITOR = GIT_EDITOR,
GIT_SEQUENCE_EDITOR = GIT_EDITOR,
})
vim.system(
cmd,
{
cwd = opts.cwd,
text = true,
env = env,
stderr = on_stderr,
},
vim.schedule_wrap(function(result)
finalize(result)
on_exit(result)
end)
)
end
return M