refactor(git): older-on-left placement, drop empty placeholders

This commit is contained in:
2026-04-30 05:47:04 +02:00
parent 3210fe1124
commit 0fb0e4557e
4 changed files with 134 additions and 137 deletions
+86 -75
View File
@@ -488,18 +488,6 @@ local function head_pane(worktree, path)
}
end
---@param worktree string
---@param path string
---@param kind "index"|"HEAD"|"worktree"
---@return ow.Git.DiffSide
local function absent_pane(worktree, path, kind)
local rel = vim.fn.fnamemodify(vim.fs.joinpath(worktree, path), ":.")
return {
buf = util.empty_buf(),
name = string.format("[absent %s] %s", kind, rel),
}
end
---@param worktree string
---@param path string
---@return ow.Git.DiffSide
@@ -513,13 +501,6 @@ end
---@param entry ow.Git.FileEntry
---@return ow.Git.DiffSide
local function index_pane(s, entry)
local in_index = not (
entry.section == "Untracked"
or (entry.section == "Staged" and entry.x == "D")
)
if not in_index then
return absent_pane(s.worktree, entry.path, "index")
end
return {
buf = object.buf_for(s.worktree, ":0:" .. entry.path),
name = util.uri(":0:" .. entry.path),
@@ -529,39 +510,40 @@ end
---@param s ow.Git.SidebarState
---@param entry ow.Git.FileEntry
---@return ow.Git.DiffSide?
local function other_pane(s, entry)
local p = entry.path
local worktree = s.worktree
local function older_pane(s, entry)
if entry.section == "Staged" then
if entry.x == "A" then
return absent_pane(worktree, p, "HEAD")
end
if entry.x == "D" then
return head_pane(worktree, p)
return nil
end
-- HEAD holds the pre-rename path
return head_pane(worktree, entry.orig or p)
return head_pane(s.worktree, entry.orig or entry.path)
end
if entry.section == "Unstaged" then
if entry.y == "D" then
return absent_pane(worktree, p, "worktree")
end
return worktree_pane(worktree, p)
end
if entry.section == "Untracked" then
return worktree_pane(worktree, p)
return index_pane(s, entry)
end
return nil
end
---@param s ow.Git.SidebarState
---@param entry ow.Git.FileEntry
---@return ow.Git.DiffPair?
local function compute_pair(s, entry)
local other = other_pane(s, entry)
if not other then
return nil
---@return ow.Git.DiffSide?
local function newer_pane(s, entry)
if entry.section == "Staged" then
if entry.x == "D" then
return nil
end
return index_pane(s, entry)
end
return { left = index_pane(s, entry), right = other }
if entry.section == "Unstaged" then
if entry.y == "D" then
return nil
end
return worktree_pane(s.worktree, entry.path)
end
if entry.section == "Untracked" then
return worktree_pane(s.worktree, entry.path)
end
return nil
end
---@param win integer
@@ -642,10 +624,32 @@ local function vsplit_at(target_win, dir)
true,
{ split = dir, win = target_win }
)
vim.cmd("clearjumps")
vim.cmd.clearjumps()
return win
end
---Make sure `right_win` exists, repurposing the invocation window or
---splitting the sidebar. Returns the right window.
---@param s ow.Git.SidebarState
---@param sidebar_win integer
---@param right_win integer?
---@return integer
local function ensure_right_win(s, sidebar_win, right_win)
if right_win then
return right_win
end
local target = invocation_win_for(s)
if target then
right_win = target
else
-- Sidebar-only case: split steals from sidebar, restore width.
right_win = vsplit_at(sidebar_win, "right")
vim.api.nvim_win_set_width(sidebar_win, SIDEBAR_WIDTH)
end
reset_diff_win(right_win)
return right_win
end
---@param s ow.Git.SidebarState
---@param entry ow.Git.SidebarEntry
---@param focus_left boolean
@@ -659,22 +663,49 @@ local function view_entry(s, entry, focus_left)
return
end
local left_win, right_win = adopt_diff_wins(s, sidebar_win)
local left = older_pane(s, entry)
local right = newer_pane(s, entry)
if not left and not right then
util.warning("no content for %s entry: %s", entry.section, entry.path)
return
end
local key = entry_key(entry)
local left_win, right_win = adopt_diff_wins(s, sidebar_win)
local want_pair = left and right
if s.last_shown_key == key and left_win and right_win then
if focus_left then
vim.api.nvim_set_current_win(left_win)
else
vim.api.nvim_set_current_win(sidebar_win)
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 sidebar_win
vim.api.nvim_set_current_win(target)
return
end
return
end
local pair = compute_pair(s, entry)
if not pair then
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, sidebar_win, right_win)
s.diff_right_win = right_win
vim.w[right_win].git_diff_role = "right"
local side = left or right
---@cast side ow.Git.DiffSide
diff.set_diff(right_win, false)
vim.api.nvim_win_set_buf(right_win, 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 sidebar_win)
return
end
---@cast left ow.Git.DiffSide
---@cast right ow.Git.DiffSide
if left_win and not right_win then
right_win = vsplit_at(left_win, "right")
@@ -683,25 +714,9 @@ local function view_entry(s, entry, focus_left)
left_win = vsplit_at(right_win, "left")
reset_diff_win(left_win)
elseif not (left_win or right_win) then
local target = invocation_win_for(s)
if target then
right_win = target
reset_diff_win(right_win)
left_win = vsplit_at(right_win, "left")
reset_diff_win(left_win)
else
-- Invocation window is gone (closed or in another tabpage).
-- Open the diff pair by splitting from the sidebar. winfixwidth
-- keeps the sidebar at 50 when there are other windows to
-- absorb the split. If the sidebar is the only window in the
-- tab, the split has to take from the sidebar itself, so
-- restore the width explicitly.
right_win = vsplit_at(sidebar_win, "right")
reset_diff_win(right_win)
left_win = vsplit_at(right_win, "left")
reset_diff_win(left_win)
vim.api.nvim_win_set_width(sidebar_win, SIDEBAR_WIDTH)
end
right_win = ensure_right_win(s, sidebar_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))
@@ -713,14 +728,10 @@ local function view_entry(s, entry, focus_left)
s.diff_left_win = left_win
s.diff_right_win = right_win
diff.update_pair(left_win, right_win, pair)
diff.update_pair(left_win, right_win, { left = left, right = right })
s.last_shown_key = key
if focus_left then
vim.api.nvim_set_current_win(left_win)
else
vim.api.nvim_set_current_win(sidebar_win)
end
vim.api.nvim_set_current_win(focus_left and left_win or sidebar_win)
end
---@param focus_left boolean