From 18b198f293c792412281ca765d6952d1f125accb Mon Sep 17 00:00:00 2001 From: Oscar Wallberg Date: Wed, 29 Apr 2026 10:39:48 +0200 Subject: [PATCH] refactor(git): scratch buffers wipe by default, consolidate factories in util --- lua/git/commit.lua | 1 - lua/git/diff.lua | 29 ------------------------- lua/git/log.lua | 4 +++- lua/git/object.lua | 11 ++-------- lua/git/sidebar.lua | 4 ++-- lua/git/util.lua | 53 +++++++++++++++++++++++++++++++++++---------- 6 files changed, 48 insertions(+), 54 deletions(-) diff --git a/lua/git/commit.lua b/lua/git/commit.lua index 820b073..e580655 100644 --- a/lua/git/commit.lua +++ b/lua/git/commit.lua @@ -33,7 +33,6 @@ function M.commit(opts) proxy_buf = buf proxy_win = win vim.bo[buf].buftype = "acwrite" - vim.bo[buf].bufhidden = "wipe" vim.bo[buf].modifiable = true vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) vim.bo[buf].modified = false diff --git a/lua/git/diff.lua b/lua/git/diff.lua index aa57d8f..3d8f52a 100644 --- a/lua/git/diff.lua +++ b/lua/git/diff.lua @@ -3,35 +3,6 @@ local util = require("git.util") local M = {} ----@class ow.Git.EmptyBufOpts ----@field name string? ----@field bufhidden ("hide"|"wipe")? defaults to "wipe" - ----Build a read-only scratch buffer, optionally naming it. When `opts.name` ----is set and a loaded buffer with that name already exists, returns it ----instead of creating a duplicate. ----@param opts ow.Git.EmptyBufOpts? ----@return integer -function M.empty_buf(opts) - opts = opts or {} - if opts.name then - local existing = vim.fn.bufnr(opts.name) - if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then - return existing - end - end - local buf = vim.api.nvim_create_buf(false, true) - 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 - return buf -end - ---Name a buffer and re-run filetype detection from the (re-)set name. ---Wrapped in `pcall` because a buffer with that name may already exist ---(E95). diff --git a/lua/git/log.lua b/lua/git/log.lua index d5b4328..7ff7bb9 100644 --- a/lua/git/log.lua +++ b/lua/git/log.lua @@ -41,7 +41,9 @@ function M.show(opts) return end - local buf = util.new_scratch() + -- `hide` keeps the log around when the user navigates into a commit + -- via `` (which replaces the current buffer); `` jumps back. + local buf = util.new_scratch({ bufhidden = "hide" }) vim.b[buf].git_worktree = worktree vim.bo[buf].modifiable = true vim.api.nvim_buf_set_lines(buf, 0, -1, false, util.split_lines(stdout)) diff --git a/lua/git/object.lua b/lua/git/object.lua index b6a9123..68d38ce 100644 --- a/lua/git/object.lua +++ b/lua/git/object.lua @@ -229,10 +229,7 @@ end local function blob_buf(worktree, blob, path, ref) local revspec = ref .. ":" .. path if is_zero(blob) then - return diff.empty_buf({ - name = util.uri(revspec), - bufhidden = "hide", - }) + return util.empty_buf({ name = util.uri(revspec) }) end return M.buf_for(worktree, revspec) end @@ -269,10 +266,7 @@ end ---@field split (false|"above"|"below"|"left"|"right")? forwarded to `util.new_scratch`. Default opens a new horizontal split. ---Place a `git://` URI buffer in a window per `opts.split`. ----`bufadd` dedups against existing buffers, so re-opening the same URI ----reuses the buffer (and `bufload` no-ops). `read_uri` defaults to ----`bufhidden=wipe` (right for diff sides), but navigation buffers ----should persist across window closes, so override to `hide`. +---`bufadd` dedups against existing buffers; `bufload` no-ops if loaded. ---@param worktree string ---@param uri string ---@param sha string written to `b:git_ref` so `` navigation in the buffer can resolve relative paths @@ -283,7 +277,6 @@ local function open_uri(worktree, uri, sha, opts, default_ft) vim.b[buf].git_worktree = worktree vim.b[buf].git_ref = sha vim.fn.bufload(buf) - vim.bo[buf].bufhidden = "hide" if default_ft and vim.bo[buf].filetype == "" then vim.bo[buf].filetype = default_ft end diff --git a/lua/git/sidebar.lua b/lua/git/sidebar.lua index 5a307c3..8c795df 100644 --- a/lua/git/sidebar.lua +++ b/lua/git/sidebar.lua @@ -495,7 +495,7 @@ end ---@return ow.Git.DiffSide local function absent_pane(worktree, path, kind) return { - buf = diff.empty_buf(), + buf = util.empty_buf(), name = string.format( "[absent %s] %s", kind, @@ -865,7 +865,7 @@ local function open(worktree) end local previous_win = vim.api.nvim_get_current_win() - local bufnr, win = util.new_scratch({ split = "left", bufhidden = "wipe" }) + local bufnr, win = util.new_scratch({ split = "left" }) vim.bo[bufnr].filetype = "gitsidebar" vim.wo[win].number = false diff --git a/lua/git/util.lua b/lua/git/util.lua index 0a7974a..56477dd 100644 --- a/lua/git/util.lua +++ b/lua/git/util.lua @@ -44,6 +44,44 @@ function M.parse_revspec(revspec) return { stage = nil, path = path } end +---@class ow.Git.ScratchOpts +---@field name string? +---@field bufhidden ("hide"|"wipe")? defaults to "wipe" + +---Configure a fresh buffer as a read-only scratch and optionally name +---it. Shared by `empty_buf` and `new_scratch`; the public functions +---differ in whether they dedup-by-name or place the buffer in a window. +---@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 + +---Build a read-only scratch buffer, optionally naming it. When `opts.name` +---is set and a loaded buffer with that name already exists, returns it +---instead of creating a duplicate. +---@param opts ow.Git.ScratchOpts? +---@return integer +function M.empty_buf(opts) + opts = opts or {} + if opts.name then + local existing = vim.fn.bufnr(opts.name) + if existing ~= -1 and vim.api.nvim_buf_is_loaded(existing) then + return existing + end + end + local buf = vim.api.nvim_create_buf(false, true) + setup_scratch(buf, opts) + return buf +end + ---Place a buffer in the current window or a new split per `split`. ---`false` replaces the current buffer (drops a `'` mark first so `''` ---jumps back); a direction string opens a leftabove split; nil falls @@ -62,12 +100,10 @@ function M.place_buf(buf, split) }) end ----@class ow.Git.NewScratchOpts ----@field name string? ----@field bufhidden ("hide"|"wipe")? defaults to "hide" +---@class ow.Git.NewScratchOpts : ow.Git.ScratchOpts ---@field split (false|"above"|"below"|"left"|"right")? defaults to splitbelow-aware horizontal. `false` places the buffer in the current window (drops a `'` mark first so the user can jump back). ----Create a fresh non-modifiable scratch buffer and place it. Default split +---Create a fresh read-only scratch buffer and place it. Default split ---direction is horizontal, honouring `splitbelow`. Caller flips ---`modifiable`, fills the buffer, and sets `filetype` once content lands. ---@param opts ow.Git.NewScratchOpts? @@ -76,14 +112,7 @@ end function M.new_scratch(opts) opts = opts or {} local buf = vim.api.nvim_create_buf(false, true) - vim.bo[buf].buftype = "nofile" - vim.bo[buf].bufhidden = opts.bufhidden or "hide" - 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 + setup_scratch(buf, opts) return buf, M.place_buf(buf, opts.split) end