refactor(git): gitlog as gitlog:// URI singleton, fix synthetic-URI cwd resolve
This commit is contained in:
@@ -43,6 +43,13 @@ function M.setup()
|
|||||||
object.read_uri(args.buf)
|
object.read_uri(args.buf)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
vim.api.nvim_create_autocmd("BufReadCmd", {
|
||||||
|
pattern = "gitlog://*",
|
||||||
|
group = group,
|
||||||
|
callback = function(args)
|
||||||
|
log.read_uri(args.buf)
|
||||||
|
end,
|
||||||
|
})
|
||||||
vim.api.nvim_create_autocmd(
|
vim.api.nvim_create_autocmd(
|
||||||
{ "BufReadPost", "BufNewFile", "BufWritePost", "FileChangedShellPost" },
|
{ "BufReadPost", "BufNewFile", "BufWritePost", "FileChangedShellPost" },
|
||||||
{
|
{
|
||||||
|
|||||||
+64
-23
@@ -5,24 +5,12 @@ local M = {}
|
|||||||
|
|
||||||
local LOG_FORMAT = "%h %ad {%an}%d %s"
|
local LOG_FORMAT = "%h %ad {%an}%d %s"
|
||||||
local DEFAULT_MAX_COUNT = 1000
|
local DEFAULT_MAX_COUNT = 1000
|
||||||
|
local URI_PREFIX = "gitlog://"
|
||||||
|
|
||||||
---@class ow.Git.LogOpts
|
---@param worktree string
|
||||||
---@field max_count integer? cap on commits to show. Nil uses the default, <= 0 means "all"
|
---@param max_count integer
|
||||||
|
---@return string?
|
||||||
---@param opts ow.Git.LogOpts?
|
local function fetch(worktree, max_count)
|
||||||
function M.open(opts)
|
|
||||||
opts = opts or {}
|
|
||||||
local max_count = opts.max_count
|
|
||||||
if max_count == nil then
|
|
||||||
max_count = DEFAULT_MAX_COUNT
|
|
||||||
end
|
|
||||||
|
|
||||||
local _, worktree = repo.resolve_cwd()
|
|
||||||
if not worktree then
|
|
||||||
util.warning("not in a git repository")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cmd = {
|
local cmd = {
|
||||||
"git",
|
"git",
|
||||||
"log",
|
"log",
|
||||||
@@ -35,21 +23,74 @@ function M.open(opts)
|
|||||||
if max_count > 0 then
|
if max_count > 0 then
|
||||||
table.insert(cmd, "--max-count=" .. max_count)
|
table.insert(cmd, "--max-count=" .. max_count)
|
||||||
end
|
end
|
||||||
|
return util.exec(cmd, { cwd = worktree })
|
||||||
|
end
|
||||||
|
|
||||||
local stdout = util.exec(cmd, { cwd = worktree })
|
---@param buf integer
|
||||||
|
local function populate(buf)
|
||||||
|
local worktree = vim.b[buf].git_worktree
|
||||||
|
local max_count = vim.b[buf].git_log_max_count or DEFAULT_MAX_COUNT
|
||||||
|
local stdout = fetch(worktree, max_count)
|
||||||
if not stdout then
|
if not stdout then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- `hide` keeps the log around when the user navigates into a commit
|
|
||||||
-- via `<CR>` (which replaces the current buffer); `<C-^>` jumps back.
|
|
||||||
local buf = util.new_scratch({ bufhidden = "hide" })
|
|
||||||
vim.b[buf].git_worktree = worktree
|
|
||||||
vim.bo[buf].modifiable = true
|
vim.bo[buf].modifiable = true
|
||||||
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))
|
||||||
vim.bo[buf].modifiable = false
|
vim.bo[buf].modifiable = false
|
||||||
vim.bo[buf].modified = false
|
vim.bo[buf].modified = false
|
||||||
|
end
|
||||||
|
|
||||||
|
---BufReadCmd handler for `gitlog://<worktree>` URIs.
|
||||||
|
---@param buf integer
|
||||||
|
function M.read_uri(buf)
|
||||||
|
local name = vim.api.nvim_buf_get_name(buf)
|
||||||
|
local worktree = name:sub(#URI_PREFIX + 1)
|
||||||
|
if worktree == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.b[buf].git_worktree = worktree
|
||||||
|
vim.bo[buf].swapfile = false
|
||||||
|
vim.bo[buf].bufhidden = "hide"
|
||||||
|
vim.bo[buf].buftype = "nofile"
|
||||||
|
-- Skip the assignment when ft is already set so re-runs don't
|
||||||
|
-- re-fire `FileType` autocmds (ftplugin reload, treesitter attach).
|
||||||
|
if vim.bo[buf].filetype ~= "gitlog" then
|
||||||
vim.bo[buf].filetype = "gitlog"
|
vim.bo[buf].filetype = "gitlog"
|
||||||
|
end
|
||||||
|
|
||||||
|
populate(buf)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class ow.Git.LogOpts
|
||||||
|
---@field max_count integer? cap on commits to show. Nil uses the default, <= 0 means "all"
|
||||||
|
|
||||||
|
---@param opts ow.Git.LogOpts?
|
||||||
|
function M.open(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
local _, worktree = repo.resolve_cwd()
|
||||||
|
if not worktree then
|
||||||
|
util.warning("not in a git repository")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local buf = vim.fn.bufadd(URI_PREFIX .. worktree)
|
||||||
|
vim.b[buf].git_worktree = worktree
|
||||||
|
vim.b[buf].git_log_max_count = opts.max_count or DEFAULT_MAX_COUNT
|
||||||
|
local was_loaded = vim.api.nvim_buf_is_loaded(buf)
|
||||||
|
|
||||||
|
local win = vim.fn.bufwinid(buf)
|
||||||
|
if win == -1 then
|
||||||
|
util.place_buf(buf, nil)
|
||||||
|
else
|
||||||
|
vim.api.nvim_set_current_win(win)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- `place_buf` triggers `bufload` -> `read_uri` for a fresh buffer.
|
||||||
|
-- An already-loaded buffer needs an explicit refresh.
|
||||||
|
if was_loaded then
|
||||||
|
populate(buf)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
+8
-1
@@ -182,7 +182,11 @@ function M.read_uri(buf)
|
|||||||
vim.b[buf].git_worktree = worktree
|
vim.b[buf].git_worktree = worktree
|
||||||
|
|
||||||
vim.bo[buf].swapfile = false
|
vim.bo[buf].swapfile = false
|
||||||
vim.bo[buf].bufhidden = "wipe"
|
-- `unload` frees content when the buffer is hidden, but keeps the
|
||||||
|
-- bufnr in the buffer list so the jumplist's `(bufnr, line, col)`
|
||||||
|
-- entries stay valid. `<C-o>` back to a hidden URI buffer triggers
|
||||||
|
-- the BufReadCmd again, refreshing content via `cat-file -p`.
|
||||||
|
vim.bo[buf].bufhidden = "unload"
|
||||||
|
|
||||||
---@type string?
|
---@type string?
|
||||||
local stdout = pending_content[buf]
|
local stdout = pending_content[buf]
|
||||||
@@ -228,6 +232,9 @@ function M.read_uri(buf)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if stdout then
|
if stdout then
|
||||||
|
-- Reload paths (`:e`, `<C-o>` to an unloaded buf) re-enter
|
||||||
|
-- with `modifiable = false` from the prior load.
|
||||||
|
vim.bo[buf].modifiable = true
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -84,14 +84,15 @@ function M.resolve(path)
|
|||||||
return vim.fs.normalize(gitdir), worktree
|
return vim.fs.normalize(gitdir), worktree
|
||||||
end
|
end
|
||||||
|
|
||||||
---Resolve the gitdir/worktree from the current buffer's file path, falling
|
---Resolve the gitdir/worktree from the current buffer's file path,
|
||||||
---back to `vim.fn.getcwd()` when the buffer is unnamed. Returns nil for
|
---falling back to `vim.fn.getcwd()` when the buffer is unnamed or
|
||||||
---both when not inside a git repo.
|
---carries a synthetic URI (`git://`, `gitlog://`) that isn't a real
|
||||||
|
---filesystem path. Returns nil for both when not inside a git repo.
|
||||||
---@return string? gitdir
|
---@return string? gitdir
|
||||||
---@return string? worktree
|
---@return string? worktree
|
||||||
function M.resolve_cwd()
|
function M.resolve_cwd()
|
||||||
local path = vim.api.nvim_buf_get_name(0)
|
local path = vim.api.nvim_buf_get_name(0)
|
||||||
if path == "" then
|
if path == "" or path:match("^%a+://") then
|
||||||
path = vim.fn.getcwd()
|
path = vim.fn.getcwd()
|
||||||
end
|
end
|
||||||
return M.resolve(path)
|
return M.resolve(path)
|
||||||
|
|||||||
Reference in New Issue
Block a user