refactor(git): gitlog as gitlog:// URI singleton, fix synthetic-URI cwd resolve

This commit is contained in:
2026-04-29 14:06:40 +02:00
parent 49c618959d
commit 781496dc13
4 changed files with 85 additions and 29 deletions
+7
View File
@@ -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" },
{ {
+65 -24
View File
@@ -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
vim.bo[buf].filetype = "gitlog" 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"
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
View File
@@ -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
View File
@@ -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)