local h = require("helpers") require("git").init() ---Run the cursor-restore autocmd that was responsible for the original ---cursor-jump bug. Replicating it lets the regression test exercise the ---same interaction the user had in their config. local function install_cursor_restore_autocmd() vim.api.nvim_create_autocmd("BufReadPost", { pattern = "*", command = 'silent! normal! g`"zv', }) end ---@param sidebar_buf integer ---@param needle string ---@return integer? local function find_line(sidebar_buf, needle) for i, l in ipairs(vim.api.nvim_buf_get_lines(sidebar_buf, 0, -1, false)) do if l:match(needle) then return i end end end ---@return integer? sidebar_buf, integer? sidebar_win local function find_sidebar() local buf, win for _, b in ipairs(vim.api.nvim_list_bufs()) do if vim.bo[b].filetype == "gitstatus" then buf = b end end for _, w in ipairs(vim.api.nvim_list_wins()) do if vim.api.nvim_win_get_buf(w) == buf then win = w end end return buf, win end ---@param role string ---@return integer? local function find_diff_win(role) for _, w in ipairs(vim.api.nvim_list_wins()) do if vim.w[w].git_diff_role == role then return w end end end ---Set up a repo with one tracked-and-modified file, open the sidebar, and ---return the sidebar window plus the line of the file's entry. ---@param file_path string ---@param committed_content string ---@param worktree_content string ---@return integer sidebar_win ---@return integer entry_line local function setup_sidebar_with_unstaged_file( file_path, committed_content, worktree_content ) local repo = h.make_repo({ [file_path] = committed_content }) h.write(repo, file_path, worktree_content) vim.cmd("cd " .. repo) require("git.status_view").open({ placement = "sidebar" }) local sidebar_buf, sidebar_win = find_sidebar() assert(sidebar_buf, "sidebar buffer should exist") assert(sidebar_win, "sidebar window should exist") local r = assert( require("git.repo").find(vim.fn.getcwd()), "repo should resolve for the test worktree" ) r:refresh() vim.wait(1000, function() return r.status and #r.status:by_kind("unstaged") > 0 end) local entry_line = assert( find_line(sidebar_buf, vim.pesc(file_path) .. "$"), file_path .. " should appear in sidebar" ) return sidebar_win, entry_line end local function press(keys) local rhs = vim.api.nvim_replace_termcodes(keys, true, false, true) vim.api.nvim_feedkeys(rhs, "x", false) end ---@param cond fun(): boolean ---@param msg string local function wait_for(cond, msg) h.truthy(vim.wait(1000, cond), "timed out waiting for: " .. msg) end h.test( "stage with diff open: sidebar cursor stays put", function() install_cursor_restore_autocmd() local sidebar_win, line = setup_sidebar_with_unstaged_file( "zsh/rc", "ZSH=true\n", "ZSH=true\nmodified\n" ) vim.api.nvim_set_current_win(sidebar_win) vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 }) press("") wait_for(function() return find_diff_win("left") ~= nil end, "diff windows to appear") local r = assert(require("git.repo").find(vim.fn.getcwd())) vim.api.nvim_set_current_win(sidebar_win) press("s") wait_for(function() return #r.status:by_kind("staged") > 0 end, "stage to propagate to repo state") h.eq( vim.api.nvim_win_get_cursor(sidebar_win), { line, 0 }, "sidebar cursor should remain at the entry's original line" ) end ) h.test( "stage with diff open: diff foldmethod is preserved on refresh", function() local sidebar_win, line = setup_sidebar_with_unstaged_file( "zsh/rc", "# vim: set ft=zsh nowrap:\nZSH=true\n", "# vim: set ft=zsh nowrap:\nZSH=true\nmodified\n" ) vim.api.nvim_set_current_win(sidebar_win) vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 }) press("") wait_for(function() return find_diff_win("left") ~= nil end, "diff windows to appear") local left_win = assert(find_diff_win("left")) h.eq( vim.wo[left_win].foldmethod, "diff", "left diff foldmethod should be 'diff' after Tab" ) local r = assert(require("git.repo").find(vim.fn.getcwd())) vim.api.nvim_set_current_win(sidebar_win) press("s") wait_for(function() return #r.status:by_kind("staged") > 0 end, "stage to propagate to repo state") h.eq( vim.wo[left_win].foldmethod, "diff", "left diff foldmethod should still be 'diff' after stage refresh" ) end ) h.test("refresh on stage updates the index URI buffer's content", function() local sidebar_win, line = setup_sidebar_with_unstaged_file( "foo.txt", "v1\n", "v2\n" ) vim.api.nvim_set_current_win(sidebar_win) vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 }) press("") wait_for(function() return find_diff_win("left") ~= nil end, "diff windows to appear") local left_win = assert(find_diff_win("left")) local index_buf = vim.api.nvim_win_get_buf(left_win) h.eq( vim.api.nvim_buf_get_lines(index_buf, 0, -1, false), { "v1" }, "index pane should initially show committed content" ) vim.api.nvim_set_current_win(sidebar_win) press("s") wait_for(function() local first = vim.api.nvim_buf_get_lines(index_buf, 0, -1, false)[1] return first == "v2" end, "index pane to refresh to staged content") h.eq( vim.api.nvim_buf_get_lines(index_buf, 0, -1, false), { "v2" }, "index pane should reflect staged content after refresh" ) end) h.report()