From ffd5584a05ce14e91f2c64bd27286f3fc3db3f66 Mon Sep 17 00:00:00 2001 From: Oscar Wallberg Date: Tue, 19 May 2026 16:29:09 +0200 Subject: [PATCH] refactor(git): replace status_view URI scheme with path-style name --- lua/git/status_view.lua | 93 +++++++---------------------------- plugin/git.lua | 11 ++--- test/git/status_view_test.lua | 90 +++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 84 deletions(-) diff --git a/lua/git/status_view.lua b/lua/git/status_view.lua index af991d2..6fca2ae 100644 --- a/lua/git/status_view.lua +++ b/lua/git/status_view.lua @@ -7,8 +7,6 @@ local util = require("git.core.util") local M = {} -M.URI_PREFIX = "gitstatus://" - ---@type ow.Git.StatusView.Placement[] M.PLACEMENTS = { "sidebar", "split", "current" } @@ -16,14 +14,10 @@ M.PLACEMENTS = { "sidebar", "split", "current" } local SECTIONS = { "untracked", "unstaged", "staged", "unmerged" } local WINDOW_WIDTH = 50 ----@param name string ----@return integer? bufnr -local function find_buf(name) - for _, b in ipairs(vim.api.nvim_list_bufs()) do - if vim.api.nvim_buf_get_name(b) == name then - return b - end - end +---@param r ow.Git.Repo +---@return string +local function buf_name_for(r) + return r.worktree .. "/Git Status" end ---@alias ow.Git.StatusView.Placement "sidebar"|"split"|"current" @@ -697,8 +691,9 @@ function M.open(opts) util.error("not in a git repository") return end + local previous_win = vim.api.nvim_get_current_win() - local buf = vim.fn.bufadd(M.URI_PREFIX .. r.worktree) + local buf = vim.fn.bufadd(buf_name_for(r)) local visible = vim.fn.bufwinid(buf) if visible ~= -1 then @@ -707,78 +702,24 @@ function M.open(opts) return end - local was_loaded = vim.api.nvim_buf_is_loaded(buf) - local win = place(buf, placement) - - vim.bo[buf].bufhidden = placement == "sidebar" and "wipe" or "hide" - local s = state[buf] - if s then - s.win = win - s.placement = placement + if not state[buf] then + vim.fn.bufload(buf) + repo.bind(buf, r) + util.setup_scratch(buf, {}) + vim.bo[buf].filetype = "gitstatus" + setup_buffer(buf, r, placement) end + vim.bo[buf].bufhidden = placement == "sidebar" and "wipe" or "hide" + + local win = place(buf, placement) + state[buf].win = win + state[buf].placement = placement set_keymaps(buf, placement) if placement == "sidebar" then vim.api.nvim_set_current_win(previous_win) end - if was_loaded then - refresh(buf) - end - r:refresh() -end - ----@param buf integer -function M.read_uri(buf) - local name = vim.api.nvim_buf_get_name(buf) - local raw = name:sub(#M.URI_PREFIX + 1) - if raw == "" then - return - end - local worktree = vim.fs.abspath(raw) - local r = repo.resolve(worktree) - if not r then - util.error("not a git worktree: %s", raw) - return - end - if r.worktree ~= worktree then - util.warning("%s is not a worktree root, using %s", raw, r.worktree) - end - local canonical = M.URI_PREFIX .. r.worktree - if name ~= canonical then - local existing = find_buf(canonical) - if existing and existing ~= buf then - local win = vim.api.nvim_get_current_win() - if vim.api.nvim_win_get_buf(win) == buf then - vim.api.nvim_win_set_buf(win, existing) - end - vim.api.nvim_buf_delete(buf, { force = true }) - local s = state[existing] - if s then - s.win = win - s.placement = "current" - end - refresh(existing) - r:refresh() - return - end - pcall(vim.api.nvim_buf_set_name, buf, canonical) - end - repo.bind(buf, r) - - util.setup_scratch(buf, { bufhidden = "hide" }) - vim.bo[buf].filetype = "gitstatus" - - ---@type integer? - local win = vim.fn.bufwinid(buf) - if win == -1 then - win = nil - end - if not state[buf] then - setup_buffer(buf, r, "current", win) - else - state[buf].win = win - end refresh(buf) r:refresh() end diff --git a/plugin/git.lua b/plugin/git.lua index 2ae914f..46a4532 100644 --- a/plugin/git.lua +++ b/plugin/git.lua @@ -103,14 +103,6 @@ vim.api.nvim_create_autocmd("BufReadCmd", { require("git.log_view").read_uri(args.buf) end, }) -vim.api.nvim_create_autocmd("BufReadCmd", { - pattern = "gitstatus://*", - group = group, - callback = function(args) - require("git.status_view").read_uri(args.buf) - end, -}) - vim.api.nvim_create_user_command("G", function(opts) local cmd = require("git.cmd") cmd.run(cmd.parse_args(opts.args), { bang = opts.bang }) @@ -233,6 +225,9 @@ vim.keymap.set("n", "(git-diff-horizontal-head)", function() }) end, { silent = true, desc = "Diff against HEAD (horizontal)" }) +vim.keymap.set("n", "(git-status-open)", function() + require("git.status_view").open() +end, { silent = true, desc = "Open git status sidebar" }) vim.keymap.set("n", "(git-status-toggle)", function() require("git.status_view").toggle() end, { silent = true, desc = "Toggle git status sidebar" }) diff --git a/test/git/status_view_test.lua b/test/git/status_view_test.lua index 205f0c0..350687f 100644 --- a/test/git/status_view_test.lua +++ b/test/git/status_view_test.lua @@ -315,3 +315,93 @@ t.test( ) end ) + +t.test("sidebar buffer is named /Git Status", function() + local repo = h.make_repo({ ["foo.txt"] = "x\n" }) + vim.cmd("cd " .. repo) + require("git.status_view").open({ placement = "sidebar" }) + local r = assert(require("git.core.repo").find(vim.fn.getcwd())) + local buf = find_sidebar() + assert(buf, "sidebar buffer should exist") + t.eq( + vim.api.nvim_buf_get_name(buf), + r.worktree .. "/Git Status", + "buffer name should be /Git Status" + ) +end) + +t.test( + "calling open twice without closing focuses the existing sidebar", + function() + local repo = h.make_repo({ ["foo.txt"] = "x\n" }) + vim.cmd("cd " .. repo) + require("git.status_view").open({ placement = "sidebar" }) + local first = find_sidebar() + assert(first, "first sidebar buffer should exist") + + require("git.status_view").open({ placement = "sidebar" }) + local second = find_sidebar() + assert(second, "second sidebar buffer should exist") + t.eq( + first, + second, + "consecutive opens should reuse the visible sidebar" + ) + local count = 0 + for _, b in ipairs(vim.api.nvim_list_bufs()) do + if vim.bo[b].filetype == "gitstatus" then + count = count + 1 + end + end + t.eq(count, 1, "only one gitstatus buffer should exist") + end +) + +t.test("opening for different worktrees creates separate buffers", function() + local repo_a = h.make_repo({ ["a.txt"] = "x\n" }) + local repo_b = h.make_repo({ ["b.txt"] = "y\n" }) + + vim.cmd("cd " .. repo_a) + require("git.status_view").open({ placement = "sidebar" }) + local buf_a = find_sidebar() + require("git.status_view").toggle() + + vim.cmd("cd " .. repo_b) + require("git.status_view").open({ placement = "sidebar" }) + local buf_b = find_sidebar() + + assert(buf_a and buf_b) + t.truthy( + buf_a ~= buf_b, + "different worktrees should produce different buffers" + ) +end) + +t.test("sidebar buffer is buftype=nofile and not buflisted", function() + local repo = h.make_repo({ ["foo.txt"] = "x\n" }) + vim.cmd("cd " .. repo) + require("git.status_view").open({ placement = "sidebar" }) + local buf = find_sidebar() + assert(buf, "sidebar buffer should exist") + t.eq(vim.bo[buf].buftype, "nofile", "buftype should be nofile") + t.eq(vim.bo[buf].buflisted, false, "buflisted should be false") +end) + +t.test("sidebar buffer name does not get written to disk", function() + local repo = h.make_repo({ ["foo.txt"] = "x\n" }) + vim.cmd("cd " .. repo) + require("git.status_view").open({ placement = "sidebar" }) + local buf = find_sidebar() + assert(buf, "sidebar buffer should exist") + local name = vim.api.nvim_buf_get_name(buf) + vim.api.nvim_buf_call(buf, function() + pcall(function() + vim.cmd("silent! write") + end) + end) + t.eq( + vim.uv.fs_stat(name), + nil, + "no real file should be created at the sidebar buffer's path" + ) +end)