refactor(git): make the git module self-contained under git.util
This commit is contained in:
+4
-5
@@ -1,7 +1,6 @@
|
|||||||
local git = require("git")
|
local git = require("git")
|
||||||
local log = require("log")
|
|
||||||
local repo = require("git.repo")
|
local repo = require("git.repo")
|
||||||
local util = require("util")
|
local util = require("git.util")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -23,7 +22,7 @@ local cached_cmds
|
|||||||
---@param result vim.SystemCompleted
|
---@param result vim.SystemCompleted
|
||||||
local function populate_cached_cmds(result)
|
local function populate_cached_cmds(result)
|
||||||
if result.code ~= 0 then
|
if result.code ~= 0 then
|
||||||
log.error("git --list-cmds failed: %s", vim.trim(result.stderr or ""))
|
util.error("git --list-cmds failed: %s", vim.trim(result.stderr or ""))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
cached_cmds = {}
|
cached_cmds = {}
|
||||||
@@ -140,7 +139,7 @@ local function run_in_split(worktree, args, conf)
|
|||||||
vim.bo[buf].modifiable = false
|
vim.bo[buf].modifiable = false
|
||||||
vim.bo[buf].modified = false
|
vim.bo[buf].modified = false
|
||||||
if obj.code ~= 0 then
|
if obj.code ~= 0 then
|
||||||
log.error(
|
util.error(
|
||||||
"git %s failed: %s",
|
"git %s failed: %s",
|
||||||
args[1] or "",
|
args[1] or "",
|
||||||
vim.trim(obj.stderr or "")
|
vim.trim(obj.stderr or "")
|
||||||
@@ -216,7 +215,7 @@ end
|
|||||||
function M.run(args)
|
function M.run(args)
|
||||||
local _, worktree = repo.resolve_cwd()
|
local _, worktree = repo.resolve_cwd()
|
||||||
if not worktree then
|
if not worktree then
|
||||||
log.warning("not in a git repository")
|
util.warning("not in a git repository")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -1,6 +1,5 @@
|
|||||||
local editor = require("git.editor")
|
local editor = require("git.editor")
|
||||||
local git = require("git")
|
local git = require("git")
|
||||||
local log = require("log")
|
|
||||||
local repo = require("git.repo")
|
local repo = require("git.repo")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
@@ -10,7 +9,7 @@ function M.commit(opts)
|
|||||||
local amend = opts and opts.amend or false
|
local amend = opts and opts.amend or false
|
||||||
local _, worktree = repo.resolve_cwd()
|
local _, worktree = repo.resolve_cwd()
|
||||||
if not worktree then
|
if not worktree then
|
||||||
log.warning("not in a git repository")
|
util.warning("not in a git repository")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ function M.commit(opts)
|
|||||||
local out = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
|
local out = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
|
||||||
local fw, werr = io.open(file_path, "w")
|
local fw, werr = io.open(file_path, "w")
|
||||||
if not fw then
|
if not fw then
|
||||||
log.error("failed to write %s: %s", file_path, werr or "")
|
util.error("failed to write %s: %s", file_path, werr or "")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
fw:write(table.concat(out, "\n"))
|
fw:write(table.concat(out, "\n"))
|
||||||
@@ -68,12 +67,12 @@ function M.commit(opts)
|
|||||||
vim.api.nvim_buf_delete(proxy_buf, { force = true })
|
vim.api.nvim_buf_delete(proxy_buf, { force = true })
|
||||||
end
|
end
|
||||||
if result.code ~= 0 then
|
if result.code ~= 0 then
|
||||||
log.error("git commit failed: %s", vim.trim(result.stderr or ""))
|
util.error("git commit failed: %s", vim.trim(result.stderr or ""))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local out = vim.trim(result.stdout or "")
|
local out = vim.trim(result.stdout or "")
|
||||||
if out ~= "" then
|
if out ~= "" then
|
||||||
log.info("%s", out)
|
util.info("%s", out)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
+10
-14
@@ -1,6 +1,5 @@
|
|||||||
local log = require("log")
|
|
||||||
local repo = require("git.repo")
|
local repo = require("git.repo")
|
||||||
local util = require("util")
|
local util = require("git.util")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -15,7 +14,7 @@ local function attach_index_writer(buf, worktree, path)
|
|||||||
vim.api.nvim_buf_get_lines(buf, 0, -1, false),
|
vim.api.nvim_buf_get_lines(buf, 0, -1, false),
|
||||||
"\n"
|
"\n"
|
||||||
) .. "\n"
|
) .. "\n"
|
||||||
local hash_stdout = util.system_sync(
|
local hash_stdout = util.exec(
|
||||||
{ "git", "hash-object", "-w", "--stdin" },
|
{ "git", "hash-object", "-w", "--stdin" },
|
||||||
{ cwd = worktree, stdin = body }
|
{ cwd = worktree, stdin = body }
|
||||||
)
|
)
|
||||||
@@ -26,7 +25,7 @@ local function attach_index_writer(buf, worktree, path)
|
|||||||
local mode = vim.b[buf].git_index_mode
|
local mode = vim.b[buf].git_index_mode
|
||||||
if not mode then
|
if not mode then
|
||||||
mode = "100644"
|
mode = "100644"
|
||||||
local ls = util.system_sync(
|
local ls = util.exec(
|
||||||
{ "git", "ls-files", "-s", "--", path },
|
{ "git", "ls-files", "-s", "--", path },
|
||||||
{ cwd = worktree, silent = true }
|
{ cwd = worktree, silent = true }
|
||||||
)
|
)
|
||||||
@@ -42,7 +41,7 @@ local function attach_index_writer(buf, worktree, path)
|
|||||||
-- (mode,sha,path), which doesn't survive paths containing a
|
-- (mode,sha,path), which doesn't survive paths containing a
|
||||||
-- comma.
|
-- comma.
|
||||||
if
|
if
|
||||||
not util.system_sync({
|
not util.exec({
|
||||||
"git",
|
"git",
|
||||||
"update-index",
|
"update-index",
|
||||||
"--cacheinfo",
|
"--cacheinfo",
|
||||||
@@ -87,7 +86,7 @@ function M.read_uri(buf)
|
|||||||
|
|
||||||
local worktree = vim.b[buf].git_worktree or select(2, repo.resolve_cwd())
|
local worktree = vim.b[buf].git_worktree or select(2, repo.resolve_cwd())
|
||||||
if not worktree then
|
if not worktree then
|
||||||
log.error("git BufReadCmd %s: cannot resolve worktree", name)
|
util.error("git BufReadCmd %s: cannot resolve worktree", name)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
vim.b[buf].git_worktree = worktree
|
vim.b[buf].git_worktree = worktree
|
||||||
@@ -104,10 +103,7 @@ function M.read_uri(buf)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local revspec = ref == "index" and (":" .. path) or (ref .. ":" .. path)
|
local revspec = ref == "index" and (":" .. path) or (ref .. ":" .. path)
|
||||||
local stdout = util.system_sync(
|
local stdout = util.exec({ "git", "show", revspec }, { cwd = worktree })
|
||||||
{ "git", "show", revspec },
|
|
||||||
{ cwd = worktree }
|
|
||||||
)
|
|
||||||
if stdout then
|
if stdout then
|
||||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout))
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout))
|
||||||
end
|
end
|
||||||
@@ -207,21 +203,21 @@ function M.split(opts)
|
|||||||
local cur_buf = vim.api.nvim_get_current_buf()
|
local cur_buf = vim.api.nvim_get_current_buf()
|
||||||
local cur_path = vim.api.nvim_buf_get_name(cur_buf)
|
local cur_path = vim.api.nvim_buf_get_name(cur_buf)
|
||||||
if cur_path == "" then
|
if cur_path == "" then
|
||||||
log.warning("no file in current buffer")
|
util.warning("no file in current buffer")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if vim.bo[cur_buf].buftype ~= "" then
|
if vim.bo[cur_buf].buftype ~= "" then
|
||||||
log.warning("cannot diff this buffer (not a worktree file)")
|
util.warning("cannot diff this buffer (not a worktree file)")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local _, worktree = repo.resolve(cur_path)
|
local _, worktree = repo.resolve(cur_path)
|
||||||
if not worktree then
|
if not worktree then
|
||||||
log.warning("not in a git repository")
|
util.warning("not in a git repository")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local rel = vim.fs.relpath(worktree, cur_path)
|
local rel = vim.fs.relpath(worktree, cur_path)
|
||||||
if not rel then
|
if not rel then
|
||||||
log.warning("file is outside the worktree")
|
util.warning("file is outside the worktree")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -1,5 +1,3 @@
|
|||||||
local log = require("log")
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local SENTINEL = "__NVIM_GIT_EDIT__"
|
local SENTINEL = "__NVIM_GIT_EDIT__"
|
||||||
@@ -59,7 +57,7 @@ local function build_stderr_handler(on_open)
|
|||||||
end
|
end
|
||||||
local ok, err = pcall(on_open, abs_path, done)
|
local ok, err = pcall(on_open, abs_path, done)
|
||||||
if not ok then
|
if not ok then
|
||||||
log.error("git.editor on_open failed: %s", tostring(err))
|
util.error("git.editor on_open failed: %s", tostring(err))
|
||||||
done()
|
done()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
+3
-4
@@ -1,7 +1,6 @@
|
|||||||
local git = require("git")
|
local git = require("git")
|
||||||
local log = require("log")
|
|
||||||
local repo = require("git.repo")
|
local repo = require("git.repo")
|
||||||
local util = require("util")
|
local util = require("git.util")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ function M.show(opts)
|
|||||||
|
|
||||||
local _, worktree = repo.resolve_cwd()
|
local _, worktree = repo.resolve_cwd()
|
||||||
if not worktree then
|
if not worktree then
|
||||||
log.warning("not in a git repository")
|
util.warning("not in a git repository")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ function M.show(opts)
|
|||||||
table.insert(cmd, "--max-count=" .. max_count)
|
table.insert(cmd, "--max-count=" .. max_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
local stdout = util.system_sync(cmd, { cwd = worktree })
|
local stdout = util.exec(cmd, { cwd = worktree })
|
||||||
if not stdout then
|
if not stdout then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
+8
-6
@@ -1,5 +1,4 @@
|
|||||||
local log = require("log")
|
local util = require("git.util")
|
||||||
local util = require("util")
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -76,7 +75,7 @@ function M.resolve(path)
|
|||||||
f:close()
|
f:close()
|
||||||
local gitdir = content:match("gitdir:%s*(%S+)")
|
local gitdir = content:match("gitdir:%s*(%S+)")
|
||||||
if not gitdir then
|
if not gitdir then
|
||||||
log.warning(".git file at %s has no `gitdir:` line", found)
|
util.warning(".git file at %s has no `gitdir:` line", found)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if not gitdir:match("^/") then
|
if not gitdir:match("^/") then
|
||||||
@@ -104,7 +103,7 @@ end
|
|||||||
---@field buffers table<integer, true> set of registered buffer numbers
|
---@field buffers table<integer, true> set of registered buffer numbers
|
||||||
---@field watcher? uv.uv_fs_event_t
|
---@field watcher? uv.uv_fs_event_t
|
||||||
---@field refresh fun(self: ow.Git.Repo)
|
---@field refresh fun(self: ow.Git.Repo)
|
||||||
---@field refresh_handle ow.Util.DebounceHandle
|
---@field refresh_handle ow.Git.Util.DebounceHandle
|
||||||
local Repo = {}
|
local Repo = {}
|
||||||
Repo.__index = Repo
|
Repo.__index = Repo
|
||||||
|
|
||||||
@@ -188,7 +187,10 @@ local function do_refresh(repo)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.warning("git status failed: %s", vim.trim(obj.stderr or ""))
|
util.warning(
|
||||||
|
"git status failed: %s",
|
||||||
|
vim.trim(obj.stderr or "")
|
||||||
|
)
|
||||||
end
|
end
|
||||||
local dirty = false
|
local dirty = false
|
||||||
for buf in pairs(repo.buffers) do
|
for buf in pairs(repo.buffers) do
|
||||||
@@ -336,7 +338,7 @@ function M.rev_parse(worktree, ref, short)
|
|||||||
table.insert(cmd, "--short")
|
table.insert(cmd, "--short")
|
||||||
end
|
end
|
||||||
table.insert(cmd, ref)
|
table.insert(cmd, ref)
|
||||||
local stdout = util.system_sync(cmd, { cwd = worktree, silent = true })
|
local stdout = util.exec(cmd, { cwd = worktree, silent = true })
|
||||||
local trimmed = stdout and vim.trim(stdout) or ""
|
local trimmed = stdout and vim.trim(stdout) or ""
|
||||||
return trimmed ~= "" and trimmed or nil
|
return trimmed ~= "" and trimmed or nil
|
||||||
end
|
end
|
||||||
|
|||||||
+3
-4
@@ -1,8 +1,7 @@
|
|||||||
local diff = require("git.diff")
|
local diff = require("git.diff")
|
||||||
local git = require("git")
|
local git = require("git")
|
||||||
local log = require("log")
|
|
||||||
local repo = require("git.repo")
|
local repo = require("git.repo")
|
||||||
local util = require("util")
|
local util = require("git.util")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
@@ -109,7 +108,7 @@ end
|
|||||||
---@param section ow.Git.DiffSection
|
---@param section ow.Git.DiffSection
|
||||||
local function show_diff(ctx, section)
|
local function show_diff(ctx, section)
|
||||||
if not section.pre_blob or not section.post_blob then
|
if not section.pre_blob or not section.post_blob then
|
||||||
log.warning("no index line; cannot determine blob SHAs")
|
util.warning("no index line; cannot determine blob SHAs")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local parent = ctx.parent_ref or "0"
|
local parent = ctx.parent_ref or "0"
|
||||||
@@ -152,7 +151,7 @@ function M.open_commit(worktree, ref, opts)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local stdout = util.system_sync({ "git", "show", ref }, { cwd = worktree })
|
local stdout = util.exec({ "git", "show", ref }, { cwd = worktree })
|
||||||
if not stdout then
|
if not stdout then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
+8
-9
@@ -1,6 +1,5 @@
|
|||||||
local diff = require("git.diff")
|
local diff = require("git.diff")
|
||||||
local git = require("git")
|
local git = require("git")
|
||||||
local log = require("log")
|
|
||||||
local repo = require("git.repo")
|
local repo = require("git.repo")
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
@@ -263,7 +262,7 @@ local function enrich_with_log(worktree, branch, groups)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.error(
|
util.error(
|
||||||
"git log %s failed: %s",
|
"git log %s failed: %s",
|
||||||
p.f.range,
|
p.f.range,
|
||||||
vim.trim(result.stderr or "")
|
vim.trim(result.stderr or "")
|
||||||
@@ -298,7 +297,7 @@ local function fetch_status(worktree, prefetched_stdout, callback)
|
|||||||
{ cwd = worktree, text = true },
|
{ cwd = worktree, text = true },
|
||||||
vim.schedule_wrap(function(obj)
|
vim.schedule_wrap(function(obj)
|
||||||
if obj.code ~= 0 then
|
if obj.code ~= 0 then
|
||||||
log.error("git status failed: %s", vim.trim(obj.stderr or ""))
|
util.error("git status failed: %s", vim.trim(obj.stderr or ""))
|
||||||
local branch = { ahead = 0, behind = 0 }
|
local branch = { ahead = 0, behind = 0 }
|
||||||
local groups = {
|
local groups = {
|
||||||
Untracked = {},
|
Untracked = {},
|
||||||
@@ -761,7 +760,7 @@ local function action_stage()
|
|||||||
{ cwd = s.worktree },
|
{ cwd = s.worktree },
|
||||||
vim.schedule_wrap(function(obj)
|
vim.schedule_wrap(function(obj)
|
||||||
if obj.code ~= 0 then
|
if obj.code ~= 0 then
|
||||||
log.error("git add failed: %s", vim.trim(obj.stderr or ""))
|
util.error("git add failed: %s", vim.trim(obj.stderr or ""))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
)
|
)
|
||||||
@@ -786,7 +785,7 @@ local function action_unstage()
|
|||||||
{ cwd = s.worktree },
|
{ cwd = s.worktree },
|
||||||
vim.schedule_wrap(function(obj)
|
vim.schedule_wrap(function(obj)
|
||||||
if obj.code ~= 0 then
|
if obj.code ~= 0 then
|
||||||
log.error(
|
util.error(
|
||||||
"git restore --staged failed: %s",
|
"git restore --staged failed: %s",
|
||||||
vim.trim(obj.stderr or "")
|
vim.trim(obj.stderr or "")
|
||||||
)
|
)
|
||||||
@@ -802,7 +801,7 @@ local function action_discard()
|
|||||||
end
|
end
|
||||||
---@cast entry ow.Git.FileEntry
|
---@cast entry ow.Git.FileEntry
|
||||||
if entry.section == "Staged" then
|
if entry.section == "Staged" then
|
||||||
log.warning("file has staged changes; unstage first with 'u'")
|
util.warning("file has staged changes; unstage first with 'u'")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -821,7 +820,7 @@ local function action_discard()
|
|||||||
local target = vim.fs.joinpath(s.worktree, entry.path)
|
local target = vim.fs.joinpath(s.worktree, entry.path)
|
||||||
local rc = vim.fn.delete(target, is_dir and "rf" or "")
|
local rc = vim.fn.delete(target, is_dir and "rf" or "")
|
||||||
if rc ~= 0 then
|
if rc ~= 0 then
|
||||||
log.error("failed to delete %s", entry.path)
|
util.error("failed to delete %s", entry.path)
|
||||||
end
|
end
|
||||||
refresh(vim.api.nvim_get_current_buf())
|
refresh(vim.api.nvim_get_current_buf())
|
||||||
end
|
end
|
||||||
@@ -833,7 +832,7 @@ local function action_discard()
|
|||||||
{ cwd = s.worktree },
|
{ cwd = s.worktree },
|
||||||
vim.schedule_wrap(function(obj)
|
vim.schedule_wrap(function(obj)
|
||||||
if obj.code ~= 0 then
|
if obj.code ~= 0 then
|
||||||
log.error(
|
util.error(
|
||||||
"git checkout failed: %s",
|
"git checkout failed: %s",
|
||||||
vim.trim(obj.stderr or "")
|
vim.trim(obj.stderr or "")
|
||||||
)
|
)
|
||||||
@@ -950,7 +949,7 @@ function M.toggle()
|
|||||||
end
|
end
|
||||||
local _, worktree = repo.resolve_cwd()
|
local _, worktree = repo.resolve_cwd()
|
||||||
if not worktree then
|
if not worktree then
|
||||||
log.warning("not in a git repository")
|
util.warning("not in a git repository")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
open(worktree)
|
open(worktree)
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
---@param fmt string
|
||||||
|
---@param ... any
|
||||||
|
function M.error(fmt, ...)
|
||||||
|
vim.notify(fmt:format(...), vim.log.levels.ERROR)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param fmt string
|
||||||
|
---@param ... any
|
||||||
|
function M.warning(fmt, ...)
|
||||||
|
vim.notify(fmt:format(...), vim.log.levels.WARN)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param fmt string
|
||||||
|
---@param ... any
|
||||||
|
function M.info(fmt, ...)
|
||||||
|
vim.notify(fmt:format(...), vim.log.levels.INFO)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param fmt string
|
||||||
|
---@param ... any
|
||||||
|
function M.debug(fmt, ...)
|
||||||
|
vim.notify(fmt:format(...), vim.log.levels.DEBUG)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Split a string on newlines, dropping the trailing empty element that an
|
||||||
|
---input ending in `\n` produces. Convenient for slicing subprocess stdout
|
||||||
|
---into a list of lines without a phantom blank at the end.
|
||||||
|
---@param content string
|
||||||
|
---@return string[]
|
||||||
|
function M.split_lines(content)
|
||||||
|
local lines = vim.split(content, "\n", { plain = true, trimempty = false })
|
||||||
|
if #lines > 0 and lines[#lines] == "" then
|
||||||
|
table.remove(lines)
|
||||||
|
end
|
||||||
|
return lines
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class ow.Git.Util.DebounceHandle
|
||||||
|
---@field cancel fun()
|
||||||
|
---@field flush fun()
|
||||||
|
---@field pending fun(): boolean
|
||||||
|
---@field close fun()
|
||||||
|
|
||||||
|
---@generic F: fun(...)
|
||||||
|
---@param fn F
|
||||||
|
---@param delay integer
|
||||||
|
---@return F, ow.Git.Util.DebounceHandle
|
||||||
|
function M.debounce(fn, delay)
|
||||||
|
local timer = assert(vim.uv.new_timer())
|
||||||
|
local args ---@type table?
|
||||||
|
local gen = 0
|
||||||
|
local fired_gen = 0
|
||||||
|
|
||||||
|
local cb_main = vim.schedule_wrap(function()
|
||||||
|
-- Identity check: the libuv fire may have been superseded by a
|
||||||
|
-- re-arm or a cancel between the timer firing and this scheduled
|
||||||
|
-- callback running.
|
||||||
|
if fired_gen ~= gen or args == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local a = args
|
||||||
|
args = nil
|
||||||
|
fn(vim.F.unpack_len(a))
|
||||||
|
end)
|
||||||
|
|
||||||
|
local cb_uv = function()
|
||||||
|
fired_gen = gen
|
||||||
|
cb_main()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function call(...)
|
||||||
|
args = vim.F.pack_len(...)
|
||||||
|
gen = gen + 1
|
||||||
|
timer:start(delay, 0, cb_uv)
|
||||||
|
end
|
||||||
|
|
||||||
|
return call,
|
||||||
|
{
|
||||||
|
cancel = function()
|
||||||
|
timer:stop()
|
||||||
|
args = nil
|
||||||
|
end,
|
||||||
|
flush = function()
|
||||||
|
if args == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
timer:stop()
|
||||||
|
local a = args
|
||||||
|
args = nil
|
||||||
|
fn(vim.F.unpack_len(a))
|
||||||
|
end,
|
||||||
|
pending = function()
|
||||||
|
return args ~= nil
|
||||||
|
end,
|
||||||
|
close = function()
|
||||||
|
timer:stop()
|
||||||
|
if not timer:is_closing() then
|
||||||
|
timer:close()
|
||||||
|
end
|
||||||
|
args = nil
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class ow.Git.ExecOpts
|
||||||
|
---@field cwd string?
|
||||||
|
---@field stdin string?
|
||||||
|
---@field silent boolean? suppress the auto-log on non-zero exit
|
||||||
|
---@field on_done fun(stdout: string?)? if set, run async and deliver stdout (or nil on failure) here on the main loop instead of returning sync
|
||||||
|
|
||||||
|
---Run a system command. Default is sync: returns stdout on success or
|
||||||
|
---nil on failure (logging stderr unless `opts.silent`). When
|
||||||
|
---`opts.on_done` is set, runs async via `vim.schedule_wrap` and
|
||||||
|
---delivers the same stdout-or-nil value to that callback instead.
|
||||||
|
---
|
||||||
|
---Async mode returns nil immediately. Callers that need access to the
|
||||||
|
---raw stderr / exit code in the failure path should opt out of this
|
||||||
|
---helper and use `vim.system` directly.
|
||||||
|
---@param cmd string[]
|
||||||
|
---@param opts ow.Git.ExecOpts?
|
||||||
|
---@return string?
|
||||||
|
function M.exec(cmd, opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local sys_opts = { cwd = opts.cwd, stdin = opts.stdin, text = true }
|
||||||
|
|
||||||
|
local function handle(result)
|
||||||
|
if result.code ~= 0 then
|
||||||
|
if not opts.silent then
|
||||||
|
local label = cmd[2] and (cmd[1] .. " " .. cmd[2])
|
||||||
|
or cmd[1]
|
||||||
|
or "?"
|
||||||
|
M.error("%s failed: %s", label, vim.trim(result.stderr or ""))
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return result.stdout or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.on_done then
|
||||||
|
vim.system(
|
||||||
|
cmd,
|
||||||
|
sys_opts,
|
||||||
|
vim.schedule_wrap(function(result)
|
||||||
|
opts.on_done(handle(result))
|
||||||
|
end)
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return handle(vim.system(cmd, sys_opts):wait())
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -441,29 +441,4 @@ function M.split_lines(content)
|
|||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
---Run a system command synchronously and return stdout on success. On
|
|
||||||
---non-zero exit, logs stderr via `log.error` and returns nil. Pass
|
|
||||||
---`opts.silent` to suppress the auto-log when failure is expected (e.g.
|
|
||||||
---probe-style commands like `git rev-parse` against a possibly-missing
|
|
||||||
---ref).
|
|
||||||
---@param cmd string[]
|
|
||||||
---@param opts { cwd: string?, stdin: string?, silent: boolean? }?
|
|
||||||
---@return string?
|
|
||||||
function M.system_sync(cmd, opts)
|
|
||||||
opts = opts or {}
|
|
||||||
local result = vim.system(cmd, {
|
|
||||||
cwd = opts.cwd,
|
|
||||||
stdin = opts.stdin,
|
|
||||||
text = true,
|
|
||||||
}):wait()
|
|
||||||
if result.code ~= 0 then
|
|
||||||
if not opts.silent then
|
|
||||||
local label = cmd[2] and (cmd[1] .. " " .. cmd[2]) or cmd[1] or "?"
|
|
||||||
log.error("%s failed: %s", label, vim.trim(result.stderr or ""))
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return result.stdout or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
Reference in New Issue
Block a user