From 475e14bfa7c13de1f89a31123406371fd2d3b951 Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Sun, 12 Apr 2026 15:18:58 -0600 Subject: [PATCH] fix: detect tabline appear/disappear, recreate killed bars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two visual regressions: 1. bufferline.nvim dynamically toggles the tabline when the buffer count crosses 2 (stepping into a library file adds a buffer, showtabline flips from 0 to 2). That reserves a row at the top of the editor grid; every window shifts down and our bars can either get squeezed or outright killed. Previous VimResized handler only caught editor-level resizes, not the subtle tabline-toggle event. Now we listen to: - VimResized — editor size change - OptionSet showtabline — bufferline / user flips showtabline - BufEnter/BufWinEnter/TabEnter — catch the case where bufferline computes its tabline string via %{%...%} which nvim renders without firing OptionSet. Guarded so it only runs recovery when a bar is actually missing. All three call the same recover_chrome() which recreates any bar that's gone and re-renders content. dapui sizes are not touched (user's manual resizes stick). 2. TurboDebugBarSeparator was linked to FloatBorder which reads darker than WinSeparator in most colorschemes. Dapui draws its own pane dividers with WinSeparator, producing a visible color seam between our bar's bottom edge and the adjacent dapui pane top. Relinked to WinSeparator so the bar edges match dapui's dividers. Co-Authored-By: Claude Opus 4.6 (1M context) --- lua/turbo-debug/mode.lua | 63 ++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/lua/turbo-debug/mode.lua b/lua/turbo-debug/mode.lua index 816a14c..ffcc656 100644 --- a/lua/turbo-debug/mode.lua +++ b/lua/turbo-debug/mode.lua @@ -134,7 +134,11 @@ local function define_highlights() -- default background). StatusLine was too dark and created a visible -- seam between the bar and the surrounding panes. hl(0, "TurboDebugBar", { default = true, link = "Normal" }) - hl(0, "TurboDebugBarSeparator", { default = true, link = "FloatBorder" }) + -- WinSeparator (not FloatBorder) so the bar edges match the dividers + -- dapui draws between its own panes. FloatBorder tends to read darker + -- than WinSeparator in most colorschemes, which created a visible + -- mismatch between our bar's bottom and the adjacent dapui pane top. + hl(0, "TurboDebugBarSeparator", { default = true, link = "WinSeparator" }) -- Control labels: link to Normal so the text blends with the bar's -- Normal-bg surface. StatusLine bg was creating a visible color patch -- behind each label. The key letter still pops via TurboDebugBarKey @@ -1099,23 +1103,50 @@ function M.enter() if active then M._install_for_buf(args.buf) end end, }) - -- On editor resize: if a bar got killed by shrink-below-minimum, - -- recreate it. Do NOT re-pin dapui sizes — whatever the user - -- (or nvim's shrink/expand redistribution) arrived at is respected. - vim.api.nvim_create_autocmd("VimResized", { + -- Recovery handler shared by several layout-change events. + -- 1) Editor resize (VimResized) + -- 2) Tabline appearing/disappearing — bufferline.nvim dynamically + -- toggles showtabline based on buffer count. When it flips, every + -- window shifts by one row and our bars can get killed or + -- squeezed. + -- 3) Arbitrary WinResized (mouse drag on pane borders, plugin-driven + -- wincmd=, etc.) + -- We don't re-pin dapui sizes here — user's manual resizes should + -- stick. We just recreate any bar that got nuked and re-render + -- content. + local function recover_chrome() + vim.schedule(function() + if not active then return end + if not (sbar_win and vim.api.nvim_win_is_valid(sbar_win)) then + sbar_win, sbar_buf = nil, nil + end + if not (cbar_win and vim.api.nvim_win_is_valid(cbar_win)) then + cbar_win, cbar_buf = nil, nil + end + open_bars() -- idempotent + render_bars() + end) + end + vim.api.nvim_create_autocmd("VimResized", { group = group, callback = recover_chrome }) + vim.api.nvim_create_autocmd("OptionSet", { + group = group, + pattern = "showtabline", + callback = recover_chrome, + }) + -- Fallback: BufEnter/BufWinEnter catches bufferline showing the + -- tabline when a second buffer appears (bufferline doesn't always + -- call `:set showtabline=N`; sometimes it just computes the tabline + -- string which causes nvim to reserve the row on its own). + vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter", "TabEnter" }, { group = group, callback = function() - vim.schedule(function() - if not active then return end - if not (sbar_win and vim.api.nvim_win_is_valid(sbar_win)) then - sbar_win, sbar_buf = nil, nil - end - if not (cbar_win and vim.api.nvim_win_is_valid(cbar_win)) then - cbar_win, cbar_buf = nil, nil - end - open_bars() -- idempotent; only creates what's missing - render_bars() - end) + -- cheap guard: only recover if one of our bars is actually gone + if not active then return end + if sbar_win and vim.api.nvim_win_is_valid(sbar_win) + and cbar_win and vim.api.nvim_win_is_valid(cbar_win) then + return + end + recover_chrome() end, }) for _, buf in ipairs(vim.api.nvim_list_bufs()) do