fix: status bar 2 rows, explicit sidebar distribution, bigger Console

Three tweaks per latest feedback:

1. Status bar back to 2 rows (sep + content). dapui renders its own
   pane border above its top elements, which serves perfectly well as
   the bottom edge of our bar. Drawing our own bottom separator was
   producing two stacked rows of separator — the "dapui border on its
   own row under our status bar border" the user called out.

2. Sidebar pane distribution: was relying on dapui's 0.25-each config
   with rounding quirks sometimes making Breakpoints smaller than its
   neighbors. New pin_dapui_sizes explicitly computes total sidebar
   height and divides equally, handing any residual rows to Breakpoints
   (last pane) so it's always ≥ the other three.

3. Console height formula bumped: was max(5, floor(avail * 0.15))
   giving 5-7 rows on most terminals which felt "laughably small". Now
   max(8, floor(lines * 0.2)) — Console always at least 8 rows, scales
   to ~20% on tall terminals.

Bonus: VimResized now also re-pins dapui sizes, so if the user has a
plugin like focus.nvim auto-resizing on focus change, our layout heals
itself on any window-resize event rather than staying permanently
skewed.

Note on the "mouse-resize totally breaks the layout" bug: that's
almost certainly focus.nvim (which the user has installed) fighting
our winfixheight pins. focus.nvim actively resizes windows on cursor
movement; nothing we pin sticks while it's running. User should either
disable focus.nvim during debug mode or set its ignore list to cover
dapui_* and TurboDebugBar filetypes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-04-12 14:48:16 -06:00
1 parent 33f1e6ab73
commit f5647e53f6
1 file changed
+58 -38
+58 -38
View File
@@ -81,18 +81,11 @@ local ICON = {
local function build_default_layouts() local function build_default_layouts()
local pos = config.opts.sidebar == "right" and "right" or "left" local pos = config.opts.sidebar == "right" and "right" or "left"
-- Console height: max(5 rows, 15% of available vertical space). The -- Console height: max(8 rows, 20% of total editor lines). More
-- drift-back-to-huge bug is fixed by dapui.open({ reset = true }) in -- generous than the previous 15% — user reported the Console kept
-- M.enter, not by clamping the size small. User wants Console to -- feeling "laughably small" so we're allocating real estate.
-- scale up on tall terminals while never going below 5 rows.
local statusline_rows = vim.o.laststatus > 0 and 1 or 0
local cmdline_rows = math.max(1, vim.o.cmdheight)
local top_bar_rows = 3 -- sep + content + sep (restored to 3 rows for visual symmetry)
local bottom_bar_rows = 2 -- content + sep
local avail = vim.o.lines - top_bar_rows - bottom_bar_rows - statusline_rows - cmdline_rows
if avail < 10 then avail = 10 end
local console_size = config.opts.console_height local console_size = config.opts.console_height
or math.max(5, math.floor(avail * 0.15)) or math.max(8, math.floor(vim.o.lines * 0.2))
-- Sidebar width: ideal 40 cols, never less than 25, never more than 1/3 -- Sidebar width: ideal 40 cols, never less than 25, never more than 1/3
-- of tty width. Scales down gracefully on narrow terminals, stops at 40 -- of tty width. Scales down gracefully on narrow terminals, stops at 40
@@ -279,22 +272,17 @@ local function render_sbar()
local pad_right = remain - pad_left local pad_right = remain - pad_left
local content_line = brand_text .. string.rep(" ", pad_left) .. status_msg .. string.rep(" ", pad_right) .. state_chip local content_line = brand_text .. string.rep(" ", pad_left) .. status_msg .. string.rep(" ", pad_right) .. state_chip
-- 3 rows: sep / content / sep. Both separators render with the -- 2 rows: sep / content. No bottom separator — dapui renders its own
-- FloatBorder highlight to match dapui's own pane borders — user -- pane border above its top elements, which serves as our bottom
-- reported the dark transition at the bottom of a 2-row status bar -- edge. Drawing ours too was producing two stacked separator rows.
-- looked inconsistent with the light-grey borders around everything
-- else.
vim.bo[sbar_buf].modifiable = true vim.bo[sbar_buf].modifiable = true
vim.api.nvim_buf_set_lines(sbar_buf, 0, -1, false, { sep, content_line, sep }) vim.api.nvim_buf_set_lines(sbar_buf, 0, -1, false, { sep, content_line })
vim.bo[sbar_buf].modifiable = false vim.bo[sbar_buf].modifiable = false
vim.api.nvim_buf_clear_namespace(sbar_buf, bar_ns, 0, -1) vim.api.nvim_buf_clear_namespace(sbar_buf, bar_ns, 0, -1)
pcall(vim.api.nvim_buf_set_extmark, sbar_buf, bar_ns, 0, 0, { pcall(vim.api.nvim_buf_set_extmark, sbar_buf, bar_ns, 0, 0, {
end_row = 0, end_col = #sep, hl_group = "TurboDebugBarSeparator", end_row = 0, end_col = #sep, hl_group = "TurboDebugBarSeparator",
}) })
pcall(vim.api.nvim_buf_set_extmark, sbar_buf, bar_ns, 2, 0, {
end_row = 2, end_col = #sep, hl_group = "TurboDebugBarSeparator",
})
-- content -- content
pcall(vim.api.nvim_buf_set_extmark, sbar_buf, bar_ns, 1, 0, { pcall(vim.api.nvim_buf_set_extmark, sbar_buf, bar_ns, 1, 0, {
end_row = 1, end_col = #brand_text, hl_group = "TurboDebugBarBrand", end_row = 1, end_col = #brand_text, hl_group = "TurboDebugBarBrand",
@@ -499,7 +487,7 @@ local function open_bars()
sbar_win = vim.api.nvim_get_current_win() sbar_win = vim.api.nvim_get_current_win()
sbar_buf = vim.api.nvim_create_buf(false, true) sbar_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_win_set_buf(sbar_win, sbar_buf) vim.api.nvim_win_set_buf(sbar_win, sbar_buf)
vim.api.nvim_win_set_height(sbar_win, 3) vim.api.nvim_win_set_height(sbar_win, 2)
setup_bar_buf(sbar_buf) setup_bar_buf(sbar_buf)
setup_bar_win(sbar_win) setup_bar_win(sbar_win)
vim.wo[sbar_win].winfixheight = true vim.wo[sbar_win].winfixheight = true
@@ -534,30 +522,59 @@ local function open_bars()
render_bars() render_bars()
end end
-- Pin every dapui pane's height so layout drift from subsequent wincmd -- Pin dapui pane heights. Console gets its configured size. Sidebar
-- rearrangements (or from our own bar manipulations) can't mess with -- panes get explicit equal distribution with any rounding remainder
-- the sizing. Console is the main offender — without winfixheight it -- going to the LAST pane (Breakpoints) so it's never smaller than its
-- absorbs freed rows on re-entry and eventually fills the column. -- neighbors. winfixheight locks each pane against future rearrangements.
local function pin_dapui_sizes() local function pin_dapui_sizes()
local pane_fts = { -- gather one window per dapui pane filetype
dapui_stacks = true, dapui_scopes = true, dapui_watches = true, local pane_wins = {}
dapui_breakpoints = true, dapui_console = true, ["dap-repl"] = true,
}
for _, buf in ipairs(vim.api.nvim_list_bufs()) do for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if vim.api.nvim_buf_is_valid(buf) then if vim.api.nvim_buf_is_valid(buf) then
local ft = vim.bo[buf].filetype local ft = vim.bo[buf].filetype
if pane_fts[ft] then if ft == "dapui_stacks" or ft == "dapui_scopes" or ft == "dapui_watches"
for _, win in ipairs(vim.fn.win_findbuf(buf)) do or ft == "dapui_breakpoints" or ft == "dapui_console" or ft == "dap-repl" then
if vim.api.nvim_win_is_valid(win) then local wins = vim.fn.win_findbuf(buf)
if ft == "dapui_console" then if wins and wins[1] and vim.api.nvim_win_is_valid(wins[1]) then
pcall(vim.api.nvim_win_set_height, win, config.opts.console_height or 5) pane_wins[ft] = wins[1]
end
pcall(function() vim.wo[win].winfixheight = true end)
end
end end
end end
end end
end end
-- Console
if pane_wins.dapui_console then
local target = config.opts.console_height
or math.max(8, math.floor(vim.o.lines * 0.2))
pcall(vim.api.nvim_win_set_height, pane_wins.dapui_console, target)
end
-- Sidebar: explicit equal distribution. Compute total current height
-- of the four sidebar panes and divide evenly, handing any remainder
-- to Breakpoints (last pane) so it's never smaller than the others.
local order = { "dapui_stacks", "dapui_scopes", "dapui_watches", "dapui_breakpoints" }
local present = {}
local total = 0
for _, ft in ipairs(order) do
local w = pane_wins[ft]
if w then
present[#present + 1] = w
total = total + vim.api.nvim_win_get_height(w)
end
end
if #present > 1 and total > 0 then
local each = math.floor(total / #present)
local residual = total - (each * #present)
for i, w in ipairs(present) do
local h = each + (i == #present and residual or 0)
pcall(vim.api.nvim_win_set_height, w, h)
end
end
-- winfixheight on everything
for _, w in pairs(pane_wins) do
pcall(function() vim.wo[w].winfixheight = true end)
end
end end
local function close_bars() local function close_bars()
@@ -572,8 +589,11 @@ local function close_bars()
end end
local function reposition_bars() local function reposition_bars()
-- Splits auto-track VimResized via winfixheight; just re-render content. -- Splits auto-track VimResized via winfixheight; re-render the bar
-- content and re-pin dapui sizes so plugins like focus.nvim that
-- auto-resize on focus change can't permanently skew the layout.
render_bars() render_bars()
pin_dapui_sizes()
end end
-- ─── dap-ui setup ──────────────────────────────────────────────────────────── -- ─── dap-ui setup ────────────────────────────────────────────────────────────