Files
nvim/lua/git/util.lua
T

200 lines
4.9 KiB
Lua

local M = {}
---@class ow.Git.ScratchOpts
---@field name string?
---@field bufhidden ("hide"|"wipe")?
---@param buf integer
---@param opts ow.Git.ScratchOpts
local function setup_scratch(buf, opts)
vim.bo[buf].buftype = "nofile"
vim.bo[buf].bufhidden = opts.bufhidden or "wipe"
vim.bo[buf].swapfile = false
vim.bo[buf].modifiable = false
vim.bo[buf].modified = false
if opts.name then
pcall(vim.api.nvim_buf_set_name, buf, opts.name)
end
end
---@param buf integer
---@param name string
function M.set_buf_name(buf, name)
pcall(vim.api.nvim_buf_set_name, buf, name)
local ft = vim.filetype.match({ buf = buf })
if ft then
vim.bo[buf].filetype = ft
end
end
---@param buf integer
---@param split (false|"above"|"below"|"left"|"right")?
---@return integer win
function M.place_buf(buf, split)
if split == false then
vim.cmd.normal({ "m'", bang = true })
vim.api.nvim_set_current_buf(buf)
return vim.api.nvim_get_current_win()
end
local win = vim.api.nvim_open_win(buf, true, {
split = split or (vim.o.splitbelow and "below" or "above"),
})
vim.cmd.clearjumps()
return win
end
---@class ow.Git.NewScratchOpts : ow.Git.ScratchOpts
---@field split (false|"above"|"below"|"left"|"right")?
---@param opts ow.Git.NewScratchOpts?
---@return integer buf
---@return integer win
function M.new_scratch(opts)
opts = opts or {}
local buf = vim.api.nvim_create_buf(false, true)
setup_scratch(buf, opts)
return buf, M.place_buf(buf, opts.split)
end
---@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
---@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?
---@field on_done fun(stdout: string?)?
---@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