diff --git a/lua/git/status_view.lua b/lua/git/status_view.lua index 18bc6cf..f9859cf 100644 --- a/lua/git/status_view.lua +++ b/lua/git/status_view.lua @@ -39,11 +39,7 @@ end ---@field placement ow.Git.StatusView.Placement ---@field lines table ---@field win integer? ----@field invocation_win integer? ----@field diff_left_win integer? ----@field diff_right_win integer? ---@field unsubscribe fun()? ----@field last_shown_key string? ---@type table local state = {} @@ -165,7 +161,6 @@ local function refresh(bufnr) if not s or not vim.api.nvim_buf_is_valid(bufnr) then return end - s.last_shown_key = nil render(bufnr, s.repo.status) end @@ -264,62 +259,6 @@ local function reset_diff_win(win) end) end ----@param s ow.Git.StatusView.State ----@return integer? -local function invocation_win_for(s) - local win = s.invocation_win - if not win or not vim.api.nvim_win_is_valid(win) then - return nil - end - if win == s.win then - return nil - end - if - vim.api.nvim_win_get_tabpage(win) - ~= vim.api.nvim_get_current_tabpage() - then - return nil - end - return win -end - ----@param s ow.Git.StatusView.State ----@param status_win integer ----@return integer? left ----@return integer? right -local function adopt_diff_wins(s, status_win) - local left = valid_in_current_tab(s.diff_left_win) and s.diff_left_win - or nil - local right = valid_in_current_tab(s.diff_right_win) and s.diff_right_win - or nil - if left and right then - return left, right - end - for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do - if win ~= status_win then - local role = vim.w[win].git_diff_role - if role == "left" and not left then - left = win - elseif role == "right" and not right then - right = win - end - end - end - return left, right -end - ----@param row ow.Git.Status.Row ----@return string -local function row_key(row) - local entry = row.entry - local orig - if entry.kind == "changed" then - ---@cast entry ow.Git.Status.ChangedEntry - orig = entry.orig - end - return row.section .. "|" .. entry.path .. "|" .. (orig or "") -end - ---@param target_win integer ---@param dir "left"|"right" ---@return integer @@ -333,23 +272,32 @@ local function vsplit_at(target_win, dir) return win end ----@param s ow.Git.StatusView.State ---@param status_win integer ----@param right_win integer? ----@return integer -local function ensure_right_win(s, status_win, right_win) - if right_win then - return right_win +---@return integer? +local function previous_target_win(status_win) + local n = vim.fn.winnr("#") + if n == 0 then + return nil end - local target = invocation_win_for(s) - if target then - right_win = target - else - right_win = vsplit_at(status_win, "right") - vim.api.nvim_win_set_width(status_win, WINDOW_WIDTH) + local win = vim.fn.win_getid(n) + if win == 0 or win == status_win or not valid_in_current_tab(win) then + return nil + end + local cfg = vim.api.nvim_win_get_config(win) + if cfg.relative and cfg.relative ~= "" then + return nil + end + return win +end + +---@param status_win integer +---@param keep integer +local function close_other_diff_wins(status_win, keep) + for _, win in ipairs(vim.api.nvim_tabpage_list_wins(0)) do + if win ~= status_win and win ~= keep and vim.wo[win].diff then + pcall(vim.api.nvim_win_close, win, false) + end end - reset_diff_win(right_win) - return right_win end ---@param s ow.Git.StatusView.State @@ -384,68 +332,35 @@ local function view_row(s, row, focus_left) return end - local key = row_key(row) - local left_win, right_win = adopt_diff_wins(s, status_win) - local want_pair = left and right - - if s.last_shown_key == key then - local intact = (want_pair and left_win and right_win) - or (not want_pair and right_win and not left_win) - if intact then - local target = focus_left and (left_win or right_win) or status_win - vim.api.nvim_set_current_win(target) - return - end + local target = previous_target_win(status_win) + if not target then + target = vsplit_at(status_win, "right") end + close_other_diff_wins(status_win, target) + vim.api.nvim_win_set_width(status_win, WINDOW_WIDTH) + reset_diff_win(target) + diff.set_diff(target, false) - if not want_pair then - if left_win and vim.api.nvim_win_is_valid(left_win) then - pcall(vim.api.nvim_win_close, left_win, false) - left_win = nil - s.diff_left_win = nil - end - right_win = ensure_right_win(s, status_win, right_win) - s.diff_right_win = right_win - vim.w[right_win].git_diff_role = "right" - local side = left or right + if not (left and right) then + local side = right or left ---@cast side ow.Git.Diff.Side - diff.set_diff(right_win, false) - vim.api.nvim_win_set_buf(right_win, side.buf) + vim.api.nvim_win_set_buf(target, side.buf) if side.name then util.set_buf_name(side.buf, side.name) end - s.last_shown_key = key - vim.api.nvim_set_current_win(focus_left and right_win or status_win) + vim.api.nvim_set_current_win(focus_left and target or status_win) return end ---@cast left ow.Git.Diff.Side ---@cast right ow.Git.Diff.Side - if left_win and not right_win then - right_win = vsplit_at(left_win, "right") - reset_diff_win(right_win) - elseif right_win and not left_win then - left_win = vsplit_at(right_win, "left") - reset_diff_win(left_win) - elseif not (left_win or right_win) then - right_win = ensure_right_win(s, status_win, nil) - left_win = vsplit_at(right_win, "left") - reset_diff_win(left_win) - local combined = vim.api.nvim_win_get_width(left_win) - + vim.api.nvim_win_get_width(right_win) - vim.api.nvim_win_set_width(left_win, math.floor(combined / 2)) - end - ---@cast left_win -nil - ---@cast right_win -nil - - vim.w[left_win].git_diff_role = "left" - vim.w[right_win].git_diff_role = "right" - s.diff_left_win = left_win - s.diff_right_win = right_win - - diff.update_pair(left_win, right_win, { left = left, right = right }) - s.last_shown_key = key + local left_win = vsplit_at(target, "left") + reset_diff_win(left_win) + local combined = vim.api.nvim_win_get_width(left_win) + + vim.api.nvim_win_get_width(target) + vim.api.nvim_win_set_width(left_win, math.floor(combined / 2)) + diff.update_pair(left_win, target, { left = left, right = right }) vim.api.nvim_set_current_win(focus_left and left_win or status_win) end @@ -640,14 +555,12 @@ end ---@param r ow.Git.Repo ---@param placement ow.Git.StatusView.Placement ---@param win integer? ----@param invocation_win integer? -local function setup_buffer(bufnr, r, placement, win, invocation_win) +local function setup_buffer(bufnr, r, placement, win) state[bufnr] = { repo = r, placement = placement, lines = {}, win = win, - invocation_win = invocation_win, } local function k(lhs, rhs, desc) @@ -743,7 +656,6 @@ function M.open(opts) local s = state[buf] if s then s.win = win - s.invocation_win = previous_win s.placement = placement end set_keymaps(buf, placement) @@ -805,7 +717,7 @@ function M.read_uri(buf) win = nil end if not state[buf] then - setup_buffer(buf, r, "current", win, nil) + setup_buffer(buf, r, "current", win) else state[buf].win = win end diff --git a/test/git/status_view_test.lua b/test/git/status_view_test.lua index 328f2e7..ddb5711 100644 --- a/test/git/status_view_test.lua +++ b/test/git/status_view_test.lua @@ -39,16 +39,28 @@ local function find_sidebar() end end ----Find a diff-role window in the given tabpage (or current). +---Find a diff window in the given tabpage (or current). "left" / "right" +---is determined by column position: the layout is [sidebar | left | right], +---so the leftmost &diff window is the left pane and the rightmost is the +---right pane. ---@param role "left"|"right" ---@param tab integer? ---@return integer? local function find_diff_win(role, tab) + local diffs = {} for _, w in ipairs(vim.api.nvim_tabpage_list_wins(tab or 0)) do - if vim.w[w].git_diff_role == role then - return w + if vim.wo[w].diff then + table.insert(diffs, w) end end + table.sort(diffs, function(a, b) + return vim.api.nvim_win_get_position(a)[2] + < vim.api.nvim_win_get_position(b)[2] + end) + if role == "left" then + return diffs[1] + end + return diffs[#diffs] end ---@param file_path string @@ -161,7 +173,6 @@ t.test( setup_sidebar_with_unstaged_file("foo.txt", "v1\n", "v2\n") local tab1 = vim.api.nvim_get_current_tabpage() - -- First show diff in tab1, so state.diff_*_win point at tab1. vim.api.nvim_set_current_win(sidebar_win) vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 }) t.press("")