fix(git/status_view): scope dispatch to current tabpage + test cleanup

This commit is contained in:
2026-05-08 03:12:41 +02:00
parent 867b5c2a2e
commit ebfcaef240
8 changed files with 217 additions and 154 deletions
+63 -53
View File
@@ -1,15 +1,13 @@
local t = require("test")
local helpers = require("test.git.helpers")
local cmd = require("git.cmd")
local h = require("test.git.helpers")
local t = require("test")
require("git").init()
local git = helpers.git
---@param files table<string, string>?
---@return string dir
local function make_repo(files)
return helpers.make_repo(files, { cd = true })
return h.make_repo(files, { cd = true })
end
---@param actual string[]
@@ -49,12 +47,9 @@ t.test("parse_args handles escaped quote inside double quotes", function()
t.eq(cmd.parse_args([["a\"b" c]]), { 'a"b', "c" })
end)
t.test(
"parse_args treats backslash literally inside single quotes",
function()
t.eq(cmd.parse_args([['a\b' c]]), { "a\\b", "c" })
end
)
t.test("parse_args treats backslash literally inside single quotes", function()
t.eq(cmd.parse_args([['a\b' c]]), { "a\\b", "c" })
end)
t.test("parse_args concatenates adjacent quoted segments", function()
t.eq(cmd.parse_args([[foo"bar"baz]]), { "foobarbaz" })
@@ -162,19 +157,19 @@ end)
t.test("complete branch returns plain refs (no pseudo, no stash)", function()
local dir = make_repo({ a = "x" })
git(dir, "branch", "feature")
git(dir, "tag", "v1")
h.git(dir, "branch", "feature")
h.git(dir, "tag", "v1")
t.write(dir, "a", "modified")
git(dir, "stash")
h.git(dir, "stash")
local matches = cmd.complete("", "G branch ", 9)
eq_sorted(matches, { "feature", "main", "v1" })
end)
t.test("complete merge returns refs + pseudo + stash", function()
local dir = make_repo({ a = "x" })
git(dir, "branch", "feature")
h.git(dir, "branch", "feature")
t.write(dir, "a", "y")
git(dir, "stash")
h.git(dir, "stash")
local matches = cmd.complete("", "G merge ", 8)
eq_sorted(
matches,
@@ -184,15 +179,15 @@ end)
t.test("complete push first positional returns remotes", function()
local dir = make_repo({ a = "x" })
git(dir, "remote", "add", "origin", "/tmp/nope")
git(dir, "remote", "add", "upstream", "/tmp/nope")
h.git(dir, "remote", "add", "origin", "/tmp/nope")
h.git(dir, "remote", "add", "upstream", "/tmp/nope")
local matches = cmd.complete("", "G push ", 7)
eq_sorted(matches, { "origin", "upstream" })
end)
t.test("complete push second positional returns refs", function()
local dir = make_repo({ a = "x" })
git(dir, "branch", "feature")
h.git(dir, "branch", "feature")
local matches = cmd.complete("", "G push origin ", 14)
eq_sorted(matches, { "HEAD", "feature", "main" })
end)
@@ -203,9 +198,9 @@ t.test("complete add returns only unstaged/untracked paths", function()
t.write(dir, "newfile", "new")
local r = assert(require("git.repo").resolve(dir))
r:refresh()
vim.wait(500, function()
t.wait_for(function()
return r.status and #vim.tbl_keys(r.status.entries) > 0
end)
end, "git status to report entries", 500)
local matches = cmd.complete("", "G add ", 6)
eq_sorted(matches, { "newfile", "tracked" })
end)
@@ -218,19 +213,19 @@ t.test("complete after `--` returns tracked paths only", function()
end)
t.test("complete stash returns subsubcommands", function()
local dir = make_repo({ a = "x" })
make_repo({ a = "x" })
local matches = cmd.complete("p", "G stash p", 9)
eq_sorted(matches, { "pop", "push" })
end)
t.test("complete show with <rev>:<path> returns tree paths", function()
local dir = make_repo({ a = "x", ["sub/b"] = "y" })
make_repo({ a = "x", ["sub/b"] = "y" })
local matches = cmd.complete("HEAD:", "G show HEAD:", 12)
eq_sorted(matches, { "HEAD:a", "HEAD:sub/" })
end)
t.test("complete unknown subcommand falls back to tracked paths", function()
local dir = make_repo({ a = "x", b = "y" })
make_repo({ a = "x", b = "y" })
local matches = cmd.complete("", "G nonexistent ", 14)
eq_sorted(matches, { "a", "b" })
end)
@@ -250,14 +245,35 @@ end
---@param buf_name_pattern string
---@param timeout integer?
local function wait_buf_populated(buf_name_pattern, timeout)
vim.wait(timeout or 1000, function()
t.wait_for(function()
for _, b in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_get_name(b):match(buf_name_pattern) then
return #vim.api.nvim_buf_get_lines(b, 0, -1, false) > 1
end
end
return false
end)
end, "buffer matching " .. buf_name_pattern .. " to populate", timeout)
end
---Wait for a buffer matching `buf_name_pattern` to contain a line whose
---content equals `line`. Useful for asserting that re-running a :G
---command repopulated the buffer with new output.
---@param buf_name_pattern string
---@param line string
---@param timeout integer?
local function wait_buf_has_line(buf_name_pattern, line, timeout)
t.wait_for(function()
for _, b in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_get_name(b):match(buf_name_pattern) then
for _, l in ipairs(vim.api.nvim_buf_get_lines(b, 0, -1, false)) do
if l == line then
return true
end
end
end
end
return false
end, "buffer " .. buf_name_pattern .. " to contain " .. line, timeout)
end
t.test("run :G diff reuses the same buffer across invocations", function()
@@ -265,17 +281,17 @@ t.test("run :G diff reuses the same buffer across invocations", function()
t.write(dir, "a", "v2\n")
cmd.run({ "diff" })
wait_buf_populated("%[Git diff%]")
local first_count = count_bufs_named("%[Git diff%]")
t.eq(first_count, 1)
wait_buf_has_line("%[Git diff%]", "+v2")
t.eq(count_bufs_named("%[Git diff%]"), 1)
t.write(dir, "a", "v3\n")
cmd.run({ "diff" })
vim.wait(300)
wait_buf_has_line("%[Git diff%]", "+v3")
t.eq(count_bufs_named("%[Git diff%]"), 1, "second :G diff should reuse")
t.write(dir, "a", "v4\n")
cmd.run({ "diff" })
vim.wait(300)
wait_buf_has_line("%[Git diff%]", "+v4")
t.eq(count_bufs_named("%[Git diff%]"), 1, "third :G diff should reuse")
end)
@@ -293,13 +309,14 @@ end
t.test(":G show <CR> on + line opens the blob URI", function()
local dir = make_repo({ a = "first\n" })
t.write(dir, "a", "second\n")
git(dir, "add", "a")
git(dir, "commit", "-q", "-m", "second")
local r = assert(require("git.repo").resolve(dir))
local blob = vim.trim(git(dir, "rev-parse", "HEAD:a").stdout)
h.git(dir, "add", "a")
h.git(dir, "commit", "-q", "-m", "second")
assert(require("git.repo").resolve(dir))
local blob = h.git(dir, "rev-parse", "HEAD:a").stdout
cmd.run({ "show", "HEAD" })
wait_buf_populated("%[Git show HEAD%]")
---@type integer?
local diff_buf
for _, b in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_get_name(b):match("%[Git show HEAD%]") then
@@ -319,30 +336,23 @@ end)
t.test("<leader>gl log buffer refills after jumping back", function()
local dir = make_repo({ a = "v1\n" })
t.write(dir, "a", "v2\n")
helpers.git(dir, "add", "a")
helpers.git(dir, "commit", "-q", "-m", "second")
h.git(dir, "add", "a")
h.git(dir, "commit", "-q", "-m", "second")
require("git.log_view").open({ max_count = 1000 })
wait_buf_populated("^gitlog://")
local log_buf = vim.api.nvim_get_current_buf()
local log_win = vim.api.nvim_get_current_win()
t.truthy(
vim.api.nvim_buf_get_name(log_buf):match("^gitlog://")
)
local initial_lines =
#vim.api.nvim_buf_get_lines(log_buf, 0, -1, false)
t.truthy(vim.api.nvim_buf_get_name(log_buf):match("^gitlog://"))
local initial_lines = #vim.api.nvim_buf_get_lines(log_buf, 0, -1, false)
t.truthy(initial_lines >= 2)
-- Step into a commit, then <C-o> back to the log.
vim.api.nvim_win_set_cursor(log_win, { 1, 0 })
local cr =
vim.api.nvim_replace_termcodes("<CR>", true, false, true)
vim.api.nvim_feedkeys(cr, "x", false)
t.press("<CR>")
t.truthy(vim.api.nvim_buf_get_name(0):match("^git://"))
local co =
vim.api.nvim_replace_termcodes("<C-o>", true, false, true)
vim.api.nvim_feedkeys(co, "x", false)
t.press("<C-o>")
t.eq(vim.api.nvim_get_current_buf(), log_buf)
t.eq(
#vim.api.nvim_buf_get_lines(log_buf, 0, -1, false),
@@ -354,8 +364,8 @@ end)
t.test("<CR> still dispatches after navigating away and back", function()
local dir = make_repo({ a = "v1\n" })
t.write(dir, "a", "v2\n")
helpers.git(dir, "add", "a")
helpers.git(dir, "commit", "-q", "-m", "second")
h.git(dir, "add", "a")
h.git(dir, "commit", "-q", "-m", "second")
-- Open the HEAD commit object buffer. Its cat-file output includes a
-- "parent <sha>" line we can navigate from.
@@ -376,8 +386,7 @@ t.test("<CR> still dispatches after navigating away and back", function()
-- <C-o> back to first_obj_buf. With bufhidden=delete, vim re-reads the
-- URI, which previously raced with BufDelete-driven unbind and left
-- state cleared, so open_under_cursor returned false.
local co = vim.api.nvim_replace_termcodes("<C-o>", true, false, true)
vim.api.nvim_feedkeys(co, "x", false)
t.press("<C-o>")
t.eq(vim.api.nvim_get_current_buf(), first_obj_buf)
local tree_lnum = assert(find_line(first_obj_buf, "tree "))
vim.api.nvim_win_set_cursor(first_obj_win, { tree_lnum, 0 })
@@ -390,10 +399,11 @@ end)
t.test(":G diff <CR> on + line falls back to worktree file", function()
local dir = make_repo({ a = "v1\n" })
t.write(dir, "a", "v2\n")
local r = assert(require("git.repo").resolve(dir))
assert(require("git.repo").resolve(dir))
cmd.run({ "diff" })
wait_buf_populated("%[Git diff%]")
---@type integer?
local diff_buf
for _, b in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_get_name(b):match("%[Git diff%]") then