Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lua/diffbandit/folder/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local nvim = require("diffbandit.util.nvim")
local process = require("diffbandit.util.process")
local ui = require("diffbandit.util.ui")
local layout = require("diffbandit.util.layout")
local winbar = require("diffbandit.util.winbar")
local config_mod = require("diffbandit.config")

local Folder = {}
Expand Down Expand Up @@ -419,6 +420,7 @@ function Folder:open_layout()
end
set_window_options(gutter_win, layout.win_opts.gutter())
set_window_width(gutter_win, self.gutter_width)
winbar.align(self:winbar_spec())
self:resize_layout()
vim.api.nvim_set_current_win(left_win)
self.title = string.format("DiffBandit Folder: %s ↔ %s", self.left_root, self.right_root)
Expand Down Expand Up @@ -981,6 +983,11 @@ function Folder:run_digest_batch(batch, generation)
end)
end

-- The windows whose rows line up under a winbar (see util/winbar).
function Folder:winbar_spec()
return { content = { self.left_win, self.right_win }, gutters = { self.gutter_win } }
end

function Folder:setup_autocmds()
local augroup = vim.api.nvim_create_augroup("DiffBanditFolder" .. tostring(self.id), { clear = true })
self.autocmd_group = augroup
Expand All @@ -1001,6 +1008,11 @@ function Folder:setup_autocmds()
end,
})
end
winbar.watch(self, function()
return self:winbar_spec()
end, function()
self:render()
end)
vim.api.nvim_create_autocmd({ "VimResized", "WinResized" }, {
group = augroup,
callback = function()
Expand Down
18 changes: 16 additions & 2 deletions lua/diffbandit/kitty/canvas.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ local detect = require("diffbandit.kitty.detect")
local ids = require("diffbandit.kitty.ids")
local protocol = require("diffbandit.kitty.protocol")
local raster = require("diffbandit.kitty.raster")
local winbar = require("diffbandit.util.winbar")
local zlib = require("diffbandit.kitty.zlib")

local M = {}
Expand Down Expand Up @@ -140,7 +141,7 @@ function M.layout(host)
local right_edge = win_col(sr.win) + sr.col
local cols = right_edge - left_edge + 1
local core_start = win_col(host.connector_win) - left_edge
local rows = math.min(vim.api.nvim_win_get_height(host.connector_win), protocol.MAX_INDEX + 1)
local rows = math.min(vim.fn.winheight(host.connector_win), protocol.MAX_INDEX + 1)
if cols < 1 or cols > protocol.MAX_INDEX + 1 or rows < 1 or core_start < 0 then
return nil
end
Expand Down Expand Up @@ -207,6 +208,16 @@ local function transmit(host, st, bytes, width, height, layout)
medium = "d", data = payload, compressed = compressed, quiet = 1,
})
end
-- Re-sending an image id with a new cell grid (c/r) does not resize its
-- existing virtual placement in every terminal: Ghostty keeps the old
-- grid and stretches the new image over it, so the ribbons drift further
-- off the further down they are (a winbar or resize taking one row). Drop
-- the old image and placement first whenever the grid changes.
local grid = layout.cols .. "x" .. layout.rows
if st.placed_grid and st.placed_grid ~= grid then
table.insert(chunks, 1, protocol.delete(st.id))
end
st.placed_grid = grid
st.transmits = st.transmits + 1
local ok = detect.send(chunks)
detect.note_transmit()
Expand Down Expand Up @@ -522,8 +533,11 @@ local function ensure_float(host, st, layout)
-- Editor-relative at the connector's screen row and the screen-left wedge
-- column (relative="win" floats mis-position under some grids); paint
-- re-checks the position every time, so layout changes follow on the
-- next render.
-- next render. The window position is its top row, which is the winbar
-- when it has one (util/winbar keeps the bar uniform across the panes);
-- the ribbons start at the first text row below it.
local row = vim.api.nvim_win_get_position(host.connector_win)[1]
+ winbar.rows(host.connector_win)
local col = vim.api.nvim_win_get_position(layout.screen_left.win)[2] + layout.screen_left.col
local cfg = {
relative = "editor",
Expand Down
37 changes: 29 additions & 8 deletions lua/diffbandit/merge/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local merge_model = require("diffbandit.merge.model")
local nvim = require("diffbandit.util.nvim")
local keymaps = require("diffbandit.util.keymaps")
local layout = require("diffbandit.util.layout")
local winbar = require("diffbandit.util.winbar")
local document = require("diffbandit.util.document")
local pair_renderer = require("diffbandit.merge.pair_renderer")
local panel_mod = require("diffbandit.panel")
Expand Down Expand Up @@ -325,6 +326,22 @@ function Merge.start_for_path(path, opts, config)
return Merge.start(data, config, opts)
end

-- The windows whose rows line up under a winbar (see util/winbar).
function Merge:winbar_spec()
return {
content = { self.local_win, self.result_win, self.remote_win },
gutters = {
self.local_num_win,
self.local_result_connector_win,
self.result_left_num_win,
self.result_right_num_win,
self.result_remote_connector_win,
self.remote_num_win,
},
headers = { self.local_header_win, self.result_header_win, self.remote_header_win },
}
end

function Merge:configure_windows()
for _, win in ipairs({ self.local_win, self.result_win, self.remote_win }) do
if win and vim.api.nvim_win_is_valid(win) then
Expand Down Expand Up @@ -383,11 +400,7 @@ function Merge:configure_windows()
set_win_width(self.remote_header_win,
content_width + (remote_visible and (num_pane_width + core_width) or 0))
end
for _, win in ipairs({ self.local_win, self.result_win, self.remote_win }) do
if win and vim.api.nvim_win_is_valid(win) then
pcall(vim.api.nvim_set_option_value, "winbar", "", { scope = "local", win = win })
end
end
winbar.align(self:winbar_spec())
if self.panel then
local panel_config = config_mod.section(self.config, "git", "panel")
for _, win in ipairs({ self.panel.nav_win, self.panel.commit_win }) do
Expand Down Expand Up @@ -514,6 +527,14 @@ function Merge:setup_autocmds()
group = self.augroup,
callback = reassert_merge_maps,
})
-- Breadcrumb plugins set a pane's winbar lazily; keep the gutters on the
-- same bar row.
winbar.watch(self, function()
return self:winbar_spec()
end, function()
self:sync_gutter_viewports()
self:render()
end)
-- The three content panes scroll independently; DiffBandit owns gutter
-- synchronization (same contract as two-way sessions — no scrollbind).
vim.api.nvim_create_autocmd("WinScrolled", {
Expand Down Expand Up @@ -1652,9 +1673,9 @@ function Merge:goto_document_edge(edge)
local local_count = math.max(1, vim.api.nvim_buf_line_count(self.local_buf))
local result_count = math.max(1, vim.api.nvim_buf_line_count(self.result_buf))
local remote_count = math.max(1, vim.api.nvim_buf_line_count(self.remote_buf))
local local_height = self.local_win and vim.api.nvim_win_is_valid(self.local_win) and vim.api.nvim_win_get_height(self.local_win) or 1
local result_height = self.result_win and vim.api.nvim_win_is_valid(self.result_win) and vim.api.nvim_win_get_height(self.result_win) or 1
local remote_height = self.remote_win and vim.api.nvim_win_is_valid(self.remote_win) and vim.api.nvim_win_get_height(self.remote_win) or 1
local local_height = self.local_win and vim.api.nvim_win_is_valid(self.local_win) and nvim.get_window_height(self.local_win) or 1
local result_height = self.result_win and vim.api.nvim_win_is_valid(self.result_win) and nvim.get_window_height(self.result_win) or 1
local remote_height = self.remote_win and vim.api.nvim_win_is_valid(self.remote_win) and nvim.get_window_height(self.remote_win) or 1

local local_line = edge == "bottom" and local_count or 1
local result_line = edge == "bottom" and result_count or 1
Expand Down
24 changes: 17 additions & 7 deletions lua/diffbandit/session/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ local config_mod = require("diffbandit.config")
local view_builder = require("diffbandit.diff.view")
local diff_document = require("diffbandit.diff.document")
local kitty = require("diffbandit.kitty")
local winbar = require("diffbandit.util.winbar")

local Session = {}
Session.__index = Session
Expand Down Expand Up @@ -232,8 +233,8 @@ function Session:render_overviews()
return
end

local left_height = math.max(1, vim.api.nvim_win_get_height(self.left_win))
local right_height = math.max(1, vim.api.nvim_win_get_height(self.right_win))
local left_height = math.max(1, nvim.get_window_height(self.left_win))
local right_height = math.max(1, nvim.get_window_height(self.right_win))
local left_cursor = vim.api.nvim_win_is_valid(self.left_win) and vim.api.nvim_win_get_cursor(self.left_win)[1] or 1
local right_cursor = vim.api.nvim_win_is_valid(self.right_win) and vim.api.nvim_win_get_cursor(self.right_win)[1] or 1

Expand Down Expand Up @@ -268,7 +269,7 @@ function Session:get_scroll_padding()
local wins = { self.left_win, self.right_win, self.connector_win }
for _, win in ipairs(wins) do
if win and vim.api.nvim_win_is_valid(win) then
height = math.max(height, vim.api.nvim_win_get_height(win))
height = math.max(height, nvim.get_window_height(win))
end
end
return math.max(0, height - 1)
Expand Down Expand Up @@ -387,6 +388,15 @@ function Session:setup_autocmds()
end,
})

-- A winbar set on a source pane after open (breadcrumb plugins attach
-- lazily) moves its text a row down; give the gutters the same bar row.
winbar.watch(self, function()
return session_layout.winbar_spec(self)
end, function()
self:sync_gutter_viewports()
self:render()
end)

vim.api.nvim_create_autocmd("WinScrolled", {
group = augroup,
callback = function(event)
Expand Down Expand Up @@ -872,8 +882,8 @@ function Session:align_chunk_viewports(chunk)
-- keeps the UI responsive and coalesces repaints. Long jumps outside the
-- window render synchronously so no unstyled rows ever appear.
local clip = self.last_render_clip
local left_height = vim.api.nvim_win_is_valid(self.left_win) and vim.api.nvim_win_get_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and vim.api.nvim_win_get_height(self.right_win) or 1
local left_height = vim.api.nvim_win_is_valid(self.left_win) and nvim.get_window_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and nvim.get_window_height(self.right_win) or 1
local within_styled_window = clip ~= nil
and left_topline >= clip.left_min
and (left_topline + left_height) <= clip.left_max
Expand Down Expand Up @@ -942,8 +952,8 @@ function Session:goto_document_edge(edge)
local right_line_count = math.max(1, #self.view.right)
local left_line = edge == "bottom" and left_line_count or 1
local right_line = edge == "bottom" and right_line_count or 1
local left_height = vim.api.nvim_win_is_valid(self.left_win) and vim.api.nvim_win_get_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and vim.api.nvim_win_get_height(self.right_win) or 1
local left_height = vim.api.nvim_win_is_valid(self.left_win) and nvim.get_window_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and nvim.get_window_height(self.right_win) or 1
local left_topline = edge == "bottom" and math.max(1, left_line_count - left_height + 1) or 1
local right_topline = edge == "bottom" and math.max(1, right_line_count - right_height + 1) or 1

Expand Down
19 changes: 19 additions & 0 deletions lua/diffbandit/session/layout.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ local nvim = require("diffbandit.util.nvim")
local document = require("diffbandit.util.document")
local config_mod = require("diffbandit.config")
local layout = require("diffbandit.util.layout")
local winbar = require("diffbandit.util.winbar")

local set_buffer_options = nvim.set_buffer_options
local set_window_options = nvim.set_window_options
Expand Down Expand Up @@ -240,11 +241,27 @@ end
-- queue) silently reverts them to that buffer's defaults — 'wrap' coming
-- back on desyncs the pane's screen rows from its unwrappable gutters,
-- and 'scrolloff'/folds undo ]c alignment. Re-apply after every right-buffer swap.
-- The windows whose rows line up under a winbar (see util/winbar).
local function winbar_spec(session)
return {
content = { session.left_win, session.right_win },
gutters = {
session.left_overview_win,
session.left_num_win,
session.connector_win,
session.right_num_win,
session.right_overview_win,
},
headers = { session.left_header_win, session.center_header_win, session.right_header_win },
}
end

local function apply_right_source_window_options(session)
local right_source_winhl = session.overview_enabled and layout.winhl.hidden_source or layout.winhl.source
set_window_options(session.right_win, layout.win_opts.source(right_source_winhl, {
signcolumn = session.right.editable and "auto" or "no",
}))
winbar.align(winbar_spec(session))
end

local function apply_window_options(session)
Expand Down Expand Up @@ -341,6 +358,8 @@ function M.apply_right_source_window_options(session)
apply_right_source_window_options(session)
end

M.winbar_spec = winbar_spec

function M.open(session)
vim.cmd("tabnew")
session.tabpage = vim.api.nvim_get_current_tabpage()
Expand Down
4 changes: 2 additions & 2 deletions lua/diffbandit/session/render.lua
Original file line number Diff line number Diff line change
Expand Up @@ -234,15 +234,15 @@ local function build_render_context(self)
local left_lines, right_lines = self:display_lines()
local left_topline = get_win_view_topline(self.left_win)
local right_topline = get_win_view_topline(self.right_win)
local left_height = vim.api.nvim_win_is_valid(self.left_win) and vim.api.nvim_win_get_height(self.left_win) or 1
local left_height = vim.api.nvim_win_is_valid(self.left_win) and nvim.get_window_height(self.left_win) or 1
local right_text_hl_mode = self.right.editable and "combine" or "replace"

-- Compute connector routing lanes using extracted paths module. Projection
-- and planning are the most expensive render stages, and both are pure
-- functions of the viewport state — cache per state so revisited viewports
-- (navigation back and forth, debounced duplicate repaints) are free.
local paths = self:base_paths()
local win_height = vim.api.nvim_win_is_valid(self.right_win) and vim.api.nvim_win_get_height(self.right_win) or 1
local win_height = vim.api.nvim_win_is_valid(self.right_win) and nvim.get_window_height(self.right_win) or 1
local kitty = kitty_mode(self)
local plan_cache_key = string.format("%s:%s:%d:%d:%d:%d%s",
tostring(left_topline), tostring(right_topline), left_height, win_height,
Expand Down
10 changes: 5 additions & 5 deletions lua/diffbandit/session/render_host.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ local get_win_view_topline = nvim.get_win_view_topline
local function live_viewport_render_key(self)
local left_topline = get_win_view_topline(self.left_win)
local right_topline = get_win_view_topline(self.right_win)
local left_height = vim.api.nvim_win_is_valid(self.left_win) and vim.api.nvim_win_get_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and vim.api.nvim_win_get_height(self.right_win) or 1
local left_height = vim.api.nvim_win_is_valid(self.left_win) and nvim.get_window_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and nvim.get_window_height(self.right_win) or 1
return ui.viewport_render_key(left_topline, right_topline, left_height, right_height, self.current_chunk)
end

Expand Down Expand Up @@ -275,8 +275,8 @@ end
function M.project_paths_for_viewport(self, paths)
local left_topline = get_win_view_topline(self.left_win)
local right_topline = get_win_view_topline(self.right_win)
local left_height = vim.api.nvim_win_is_valid(self.left_win) and vim.api.nvim_win_get_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and vim.api.nvim_win_get_height(self.right_win) or 1
local left_height = vim.api.nvim_win_is_valid(self.left_win) and nvim.get_window_height(self.left_win) or 1
local right_height = vim.api.nvim_win_is_valid(self.right_win) and nvim.get_window_height(self.right_win) or 1
return self:project_paths_for_toplines(paths, left_topline, right_topline, left_height, right_height)
end

Expand All @@ -288,7 +288,7 @@ function M.precompute_connector_core_width(self)
required_core = require("diffbandit.kitty").core_width(self.config)
else
local viewport_rows = vim.api.nvim_win_is_valid(self.left_win)
and vim.api.nvim_win_get_height(self.left_win) or nil
and nvim.get_window_height(self.left_win) or nil
required_core = connector.pressure_core_width(
self:base_paths(),
connector_width.base(self.view, self.config),
Expand Down
3 changes: 3 additions & 0 deletions lua/diffbandit/util/layout.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ M.winhl = {
source = split .. ",CursorLine:DiffBanditCursorLine,SignColumn:DiffBanditSignColumn",
hidden_source = hidden_split .. ",CursorLine:DiffBanditCursorLine,SignColumn:DiffBanditSignColumn",
gutter = "Normal:DiffBanditConnectorContext,NormalNC:DiffBanditConnectorContext,"
.. "WinBar:DiffBanditConnectorContext,WinBarNC:DiffBanditConnectorContext,"
.. split .. ",CursorLine:DiffBanditCursorLine",
overview = "Normal:DiffBanditOverviewContext,NormalNC:DiffBanditOverviewContext,"
.. "WinBar:DiffBanditOverviewContext,WinBarNC:DiffBanditOverviewContext,"
.. hidden_split .. ",CursorLine:DiffBanditCursorLine",
status = "Normal:DiffBanditStatus,NormalNC:DiffBanditStatus,"
.. "StatusLine:DiffBanditStatusLine,StatusLineNC:DiffBanditStatusLine,"
.. "WinBar:DiffBanditStatus,WinBarNC:DiffBanditStatus,"
.. split .. ",CursorLine:DiffBanditStatus",
panel = split .. ",Normal:DiffBanditStatus,NormalNC:DiffBanditStatus,CursorLine:DiffBanditCursorLine",
}
Expand Down
4 changes: 3 additions & 1 deletion lua/diffbandit/util/nvim.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ function M.get_window_width(win)
return 0
end

-- Text rows of a window. nvim_win_get_height counts the winbar row too;
-- every DiffBandit viewport computation means the rows code is drawn on.
function M.get_window_height(win)
if win and vim.api.nvim_win_is_valid(win) then
return vim.api.nvim_win_get_height(win)
return math.max(0, vim.fn.winheight(win))
end
return 0
end
Expand Down
Loading
Loading