refactor(git): rebuild status_view diff layout per selection
This commit is contained in:
+42
-130
@@ -39,11 +39,7 @@ end
|
|||||||
---@field placement ow.Git.StatusView.Placement
|
---@field placement ow.Git.StatusView.Placement
|
||||||
---@field lines table<integer, ow.Git.StatusView.Item>
|
---@field lines table<integer, ow.Git.StatusView.Item>
|
||||||
---@field win integer?
|
---@field win integer?
|
||||||
---@field invocation_win integer?
|
|
||||||
---@field diff_left_win integer?
|
|
||||||
---@field diff_right_win integer?
|
|
||||||
---@field unsubscribe fun()?
|
---@field unsubscribe fun()?
|
||||||
---@field last_shown_key string?
|
|
||||||
|
|
||||||
---@type table<integer, ow.Git.StatusView.State>
|
---@type table<integer, ow.Git.StatusView.State>
|
||||||
local state = {}
|
local state = {}
|
||||||
@@ -165,7 +161,6 @@ local function refresh(bufnr)
|
|||||||
if not s or not vim.api.nvim_buf_is_valid(bufnr) then
|
if not s or not vim.api.nvim_buf_is_valid(bufnr) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
s.last_shown_key = nil
|
|
||||||
render(bufnr, s.repo.status)
|
render(bufnr, s.repo.status)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -264,62 +259,6 @@ local function reset_diff_win(win)
|
|||||||
end)
|
end)
|
||||||
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 target_win integer
|
||||||
---@param dir "left"|"right"
|
---@param dir "left"|"right"
|
||||||
---@return integer
|
---@return integer
|
||||||
@@ -333,23 +272,32 @@ local function vsplit_at(target_win, dir)
|
|||||||
return win
|
return win
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param s ow.Git.StatusView.State
|
|
||||||
---@param status_win integer
|
---@param status_win integer
|
||||||
---@param right_win integer?
|
---@return integer?
|
||||||
---@return integer
|
local function previous_target_win(status_win)
|
||||||
local function ensure_right_win(s, status_win, right_win)
|
local n = vim.fn.winnr("#")
|
||||||
if right_win then
|
if n == 0 then
|
||||||
return right_win
|
return nil
|
||||||
end
|
end
|
||||||
local target = invocation_win_for(s)
|
local win = vim.fn.win_getid(n)
|
||||||
if target then
|
if win == 0 or win == status_win or not valid_in_current_tab(win) then
|
||||||
right_win = target
|
return nil
|
||||||
else
|
end
|
||||||
right_win = vsplit_at(status_win, "right")
|
local cfg = vim.api.nvim_win_get_config(win)
|
||||||
vim.api.nvim_win_set_width(status_win, WINDOW_WIDTH)
|
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
|
end
|
||||||
reset_diff_win(right_win)
|
|
||||||
return right_win
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param s ow.Git.StatusView.State
|
---@param s ow.Git.StatusView.State
|
||||||
@@ -384,68 +332,35 @@ local function view_row(s, row, focus_left)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local key = row_key(row)
|
local target = previous_target_win(status_win)
|
||||||
local left_win, right_win = adopt_diff_wins(s, status_win)
|
if not target then
|
||||||
local want_pair = left and right
|
target = vsplit_at(status_win, "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
|
|
||||||
end
|
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 not (left and right) then
|
||||||
if left_win and vim.api.nvim_win_is_valid(left_win) then
|
local side = right or left
|
||||||
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
|
|
||||||
---@cast side ow.Git.Diff.Side
|
---@cast side ow.Git.Diff.Side
|
||||||
diff.set_diff(right_win, false)
|
vim.api.nvim_win_set_buf(target, side.buf)
|
||||||
vim.api.nvim_win_set_buf(right_win, side.buf)
|
|
||||||
if side.name then
|
if side.name then
|
||||||
util.set_buf_name(side.buf, side.name)
|
util.set_buf_name(side.buf, side.name)
|
||||||
end
|
end
|
||||||
s.last_shown_key = key
|
vim.api.nvim_set_current_win(focus_left and target or status_win)
|
||||||
vim.api.nvim_set_current_win(focus_left and right_win or status_win)
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
---@cast left ow.Git.Diff.Side
|
---@cast left ow.Git.Diff.Side
|
||||||
---@cast right ow.Git.Diff.Side
|
---@cast right ow.Git.Diff.Side
|
||||||
|
|
||||||
if left_win and not right_win then
|
local left_win = vsplit_at(target, "left")
|
||||||
right_win = vsplit_at(left_win, "right")
|
reset_diff_win(left_win)
|
||||||
reset_diff_win(right_win)
|
local combined = vim.api.nvim_win_get_width(left_win)
|
||||||
elseif right_win and not left_win then
|
+ vim.api.nvim_win_get_width(target)
|
||||||
left_win = vsplit_at(right_win, "left")
|
vim.api.nvim_win_set_width(left_win, math.floor(combined / 2))
|
||||||
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
|
|
||||||
|
|
||||||
|
diff.update_pair(left_win, target, { left = left, right = right })
|
||||||
vim.api.nvim_set_current_win(focus_left and left_win or status_win)
|
vim.api.nvim_set_current_win(focus_left and left_win or status_win)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -640,14 +555,12 @@ end
|
|||||||
---@param r ow.Git.Repo
|
---@param r ow.Git.Repo
|
||||||
---@param placement ow.Git.StatusView.Placement
|
---@param placement ow.Git.StatusView.Placement
|
||||||
---@param win integer?
|
---@param win integer?
|
||||||
---@param invocation_win integer?
|
local function setup_buffer(bufnr, r, placement, win)
|
||||||
local function setup_buffer(bufnr, r, placement, win, invocation_win)
|
|
||||||
state[bufnr] = {
|
state[bufnr] = {
|
||||||
repo = r,
|
repo = r,
|
||||||
placement = placement,
|
placement = placement,
|
||||||
lines = {},
|
lines = {},
|
||||||
win = win,
|
win = win,
|
||||||
invocation_win = invocation_win,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local function k(lhs, rhs, desc)
|
local function k(lhs, rhs, desc)
|
||||||
@@ -743,7 +656,6 @@ function M.open(opts)
|
|||||||
local s = state[buf]
|
local s = state[buf]
|
||||||
if s then
|
if s then
|
||||||
s.win = win
|
s.win = win
|
||||||
s.invocation_win = previous_win
|
|
||||||
s.placement = placement
|
s.placement = placement
|
||||||
end
|
end
|
||||||
set_keymaps(buf, placement)
|
set_keymaps(buf, placement)
|
||||||
@@ -805,7 +717,7 @@ function M.read_uri(buf)
|
|||||||
win = nil
|
win = nil
|
||||||
end
|
end
|
||||||
if not state[buf] then
|
if not state[buf] then
|
||||||
setup_buffer(buf, r, "current", win, nil)
|
setup_buffer(buf, r, "current", win)
|
||||||
else
|
else
|
||||||
state[buf].win = win
|
state[buf].win = win
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,16 +39,28 @@ local function find_sidebar()
|
|||||||
end
|
end
|
||||||
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 role "left"|"right"
|
||||||
---@param tab integer?
|
---@param tab integer?
|
||||||
---@return integer?
|
---@return integer?
|
||||||
local function find_diff_win(role, tab)
|
local function find_diff_win(role, tab)
|
||||||
|
local diffs = {}
|
||||||
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(tab or 0)) do
|
for _, w in ipairs(vim.api.nvim_tabpage_list_wins(tab or 0)) do
|
||||||
if vim.w[w].git_diff_role == role then
|
if vim.wo[w].diff then
|
||||||
return w
|
table.insert(diffs, w)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
---@param file_path string
|
---@param file_path string
|
||||||
@@ -161,7 +173,6 @@ t.test(
|
|||||||
setup_sidebar_with_unstaged_file("foo.txt", "v1\n", "v2\n")
|
setup_sidebar_with_unstaged_file("foo.txt", "v1\n", "v2\n")
|
||||||
local tab1 = vim.api.nvim_get_current_tabpage()
|
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_set_current_win(sidebar_win)
|
||||||
vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 })
|
vim.api.nvim_win_set_cursor(sidebar_win, { line, 0 })
|
||||||
t.press("<Tab>")
|
t.press("<Tab>")
|
||||||
|
|||||||
Reference in New Issue
Block a user