fix: 3-row bars with both edges bright + un-squish on tabline toggle
Bars now render top+bottom bright separators (not just outward edge) so the sbar's bottom and cbar's top read as proper chrome regardless of what the adjacent dapui pane puts in its winbar. Fixes the "top bar's bottom border is the wrong color" reading where the sbar's inward edge was falling back to dapui's grey pane-title row. Adds `ensure_bar_heights()` to un-squish bars that nvim compressed to 0 rows (not closed — just hidden) when bufferline's tabline reclaims a row on buffer-count change. `winfixheight` is a preference, not a guarantee; when the grid has to give up a row, nvim sometimes picks a `winfixheight` window anyway. Wire into VimResized + WinResized so the bar pops back on every geometry shift. Also add `WinBar:WinBar,WinBarNC:WinBar` to the dapui winhighlight strings (and apply it as an initial baseline in set_dapui_window_opts) so pane titles stay bright on themes that dim WinBarNC.
This commit is contained in:
1 file changed
+80
-46
+80
-46
@@ -200,9 +200,17 @@ local function define_highlights()
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- winhighlight strings applied to dapui panes. `WinBar:WinBar,WinBarNC:WinBar`
|
||||||
|
-- is defensive — if the user's theme makes WinBarNC dimmer than WinBar, this
|
||||||
|
-- forces the pane's winbar row to stay bright even when the pane is
|
||||||
|
-- unfocused (nvim renders unfocused wins with WinBarNC). Nordfox happens to
|
||||||
|
-- make them identical; other themes don't.
|
||||||
|
local DAPUI_WINHL_ACTIVE = "WinBar:WinBar,WinBarNC:WinBar,WinSeparator:TurboDebugBorderActive,FloatBorder:TurboDebugBorderActive"
|
||||||
|
local DAPUI_WINHL_INACTIVE = "WinBar:WinBar,WinBarNC:WinBar,WinSeparator:TurboDebugBorderInactive,FloatBorder:TurboDebugBorderInactive"
|
||||||
|
|
||||||
local function setup_active_win_highlights()
|
local function setup_active_win_highlights()
|
||||||
local active_hl = "WinSeparator:TurboDebugBorderActive,FloatBorder:TurboDebugBorderActive"
|
local active_hl = DAPUI_WINHL_ACTIVE
|
||||||
local inactive_hl = "WinSeparator:TurboDebugBorderInactive,FloatBorder:TurboDebugBorderInactive"
|
local inactive_hl = DAPUI_WINHL_INACTIVE
|
||||||
|
|
||||||
local group = vim.api.nvim_create_augroup("TurboDebugActiveWin", { clear = true })
|
local group = vim.api.nvim_create_augroup("TurboDebugActiveWin", { clear = true })
|
||||||
local function apply(win, hl)
|
local function apply(win, hl)
|
||||||
@@ -309,19 +317,21 @@ 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
|
||||||
|
|
||||||
-- 2 rows: sep / content.
|
-- 3 rows: top sep / content / bottom sep. BOTH edges use the bright
|
||||||
-- Only the outward (top) edge gets our separator. The inward (bottom)
|
-- TurboDebugBarSeparator color so the bar reads as a clearly framed
|
||||||
-- edge abuts dapui's top pane, whose winbar row (pane title like
|
-- region regardless of what sits above or below (tabline, dapui pane,
|
||||||
-- "🧊 Call Stack") serves as the visual boundary. Drawing our own
|
-- source buffer — all have different chrome conventions).
|
||||||
-- bottom separator would stack two bright rows of chrome there.
|
|
||||||
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 })
|
vim.api.nvim_buf_set_lines(sbar_buf, 0, -1, false, { sep, content_line, sep })
|
||||||
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 row
|
-- content row
|
||||||
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",
|
||||||
@@ -401,36 +411,37 @@ local function render_cbar()
|
|||||||
if ctrl_pad_right < 1 then ctrl_pad_right = 1 end
|
if ctrl_pad_right < 1 then ctrl_pad_right = 1 end
|
||||||
local content_line = string.rep(" ", ctrl_pad_left) .. controls_text .. string.rep(" ", ctrl_pad_right) .. help_text
|
local content_line = string.rep(" ", ctrl_pad_left) .. controls_text .. string.rep(" ", ctrl_pad_right) .. help_text
|
||||||
|
|
||||||
-- 2 rows: content / sep.
|
-- 3 rows: top sep / content / bottom sep. Both edges bright for a
|
||||||
-- Only the outward (bottom) edge gets our separator. The inward
|
-- visible frame, independent of what sits above (dapui Console) or
|
||||||
-- (top) edge abuts dapui's bottom pane (Breakpoints) whose winbar
|
-- below (statusline/tabline).
|
||||||
-- row acts as the visual boundary. Drawing our own top separator
|
|
||||||
-- would stack two bright rows of chrome there.
|
|
||||||
vim.bo[cbar_buf].modifiable = true
|
vim.bo[cbar_buf].modifiable = true
|
||||||
vim.api.nvim_buf_set_lines(cbar_buf, 0, -1, false, { content_line, sep })
|
vim.api.nvim_buf_set_lines(cbar_buf, 0, -1, false, { sep, content_line, sep })
|
||||||
vim.bo[cbar_buf].modifiable = false
|
vim.bo[cbar_buf].modifiable = false
|
||||||
|
|
||||||
vim.api.nvim_buf_clear_namespace(cbar_buf, bar_ns, 0, -1)
|
vim.api.nvim_buf_clear_namespace(cbar_buf, bar_ns, 0, -1)
|
||||||
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 1, 0, {
|
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 0, 0, {
|
||||||
end_row = 1, end_col = #sep, hl_group = "TurboDebugBarSeparator",
|
end_row = 0, end_col = #sep, hl_group = "TurboDebugBarSeparator",
|
||||||
|
})
|
||||||
|
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 2, 0, {
|
||||||
|
end_row = 2, end_col = #sep, hl_group = "TurboDebugBarSeparator",
|
||||||
})
|
})
|
||||||
|
|
||||||
-- control labels on row 0 (content row)
|
-- control labels on row 1 (content row)
|
||||||
local ctrl_byte_offset = ctrl_pad_left
|
local ctrl_byte_offset = ctrl_pad_left
|
||||||
for _, span in ipairs(ctrl_spans) do
|
for _, span in ipairs(ctrl_spans) do
|
||||||
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 0, ctrl_byte_offset + span[1], {
|
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 1, ctrl_byte_offset + span[1], {
|
||||||
end_row = 0, end_col = ctrl_byte_offset + span[2], hl_group = span[3],
|
end_row = 1, end_col = ctrl_byte_offset + span[2], hl_group = span[3],
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
for _, span in ipairs(ctrl_key_spans) do
|
for _, span in ipairs(ctrl_key_spans) do
|
||||||
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 0, ctrl_byte_offset + span[1], {
|
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 1, ctrl_byte_offset + span[1], {
|
||||||
end_row = 0, end_col = ctrl_byte_offset + span[2], hl_group = "TurboDebugBarKey",
|
end_row = 1, end_col = ctrl_byte_offset + span[2], hl_group = "TurboDebugBarKey",
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
local help_byte_start = #content_line - #help_text
|
local help_byte_start = #content_line - #help_text
|
||||||
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 0, help_byte_start, {
|
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 1, help_byte_start, {
|
||||||
end_row = 0, end_col = #content_line, hl_group = "TurboDebugBarCtrl",
|
end_row = 1, end_col = #content_line, hl_group = "TurboDebugBarCtrl",
|
||||||
})
|
})
|
||||||
|
|
||||||
cbar_zones = {}
|
cbar_zones = {}
|
||||||
@@ -439,12 +450,12 @@ local function render_cbar()
|
|||||||
local byte_end = ctrl_byte_offset + z[2]
|
local byte_end = ctrl_byte_offset + z[2]
|
||||||
local dcol_start = vim.fn.strdisplaywidth(content_line:sub(1, byte_start))
|
local dcol_start = vim.fn.strdisplaywidth(content_line:sub(1, byte_start))
|
||||||
local dcol_end = vim.fn.strdisplaywidth(content_line:sub(1, byte_end))
|
local dcol_end = vim.fn.strdisplaywidth(content_line:sub(1, byte_end))
|
||||||
-- content is on buffer row 0 = window line 1
|
-- content is on buffer row 1 = window line 2
|
||||||
cbar_zones[#cbar_zones + 1] = { 1, dcol_start, dcol_end, z[3] }
|
cbar_zones[#cbar_zones + 1] = { 2, dcol_start, dcol_end, z[3] }
|
||||||
end
|
end
|
||||||
local help_dcol_start = vim.fn.strdisplaywidth(content_line:sub(1, help_byte_start))
|
local help_dcol_start = vim.fn.strdisplaywidth(content_line:sub(1, help_byte_start))
|
||||||
local help_dcol_end = vim.fn.strdisplaywidth(content_line)
|
local help_dcol_end = vim.fn.strdisplaywidth(content_line)
|
||||||
cbar_zones[#cbar_zones + 1] = { 1, help_dcol_start, help_dcol_end,
|
cbar_zones[#cbar_zones + 1] = { 2, help_dcol_start, help_dcol_end,
|
||||||
function() require("turbo-debug.help").open() end }
|
function() require("turbo-debug.help").open() end }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -532,7 +543,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, 2)
|
vim.api.nvim_win_set_height(sbar_win, 3)
|
||||||
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
|
||||||
@@ -551,7 +562,7 @@ local function open_bars()
|
|||||||
cbar_win = vim.api.nvim_get_current_win()
|
cbar_win = vim.api.nvim_get_current_win()
|
||||||
cbar_buf = vim.api.nvim_create_buf(false, true)
|
cbar_buf = vim.api.nvim_create_buf(false, true)
|
||||||
vim.api.nvim_win_set_buf(cbar_win, cbar_buf)
|
vim.api.nvim_win_set_buf(cbar_win, cbar_buf)
|
||||||
vim.api.nvim_win_set_height(cbar_win, 2)
|
vim.api.nvim_win_set_height(cbar_win, 3)
|
||||||
setup_bar_buf(cbar_buf)
|
setup_bar_buf(cbar_buf)
|
||||||
setup_bar_win(cbar_win)
|
setup_bar_win(cbar_win)
|
||||||
vim.wo[cbar_win].winfixheight = true
|
vim.wo[cbar_win].winfixheight = true
|
||||||
@@ -626,6 +637,25 @@ local function close_bars()
|
|||||||
sbar_zones, cbar_zones = {}, {}
|
sbar_zones, cbar_zones = {}, {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- nvim sometimes *squishes* a bar to 0 rows rather than closing it when
|
||||||
|
-- grid pressure spikes (notably: bufferline's tabline flipping visible on
|
||||||
|
-- buffer-count change reclaims one row from somewhere, and `winfixheight`
|
||||||
|
-- is only a preference not a guarantee). The window object stays valid;
|
||||||
|
-- the content just becomes invisible. Force each bar back to 3 rows any
|
||||||
|
-- time we detect it shrunk below that.
|
||||||
|
local function ensure_bar_heights()
|
||||||
|
if sbar_win and vim.api.nvim_win_is_valid(sbar_win) then
|
||||||
|
if vim.api.nvim_win_get_height(sbar_win) < 3 then
|
||||||
|
pcall(vim.api.nvim_win_set_height, sbar_win, 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if cbar_win and vim.api.nvim_win_is_valid(cbar_win) then
|
||||||
|
if vim.api.nvim_win_get_height(cbar_win) < 3 then
|
||||||
|
pcall(vim.api.nvim_win_set_height, cbar_win, 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function reposition_bars()
|
local function reposition_bars()
|
||||||
-- Just re-render bar content. We intentionally DON'T re-pin dapui
|
-- Just re-render bar content. We intentionally DON'T re-pin dapui
|
||||||
-- sizes here — the user may have manually resized panes, and
|
-- sizes here — the user may have manually resized panes, and
|
||||||
@@ -659,6 +689,10 @@ local function set_dapui_window_opts(win)
|
|||||||
vim.wo[win].foldcolumn = "0"
|
vim.wo[win].foldcolumn = "0"
|
||||||
-- make end-of-buffer `~` invisible by setting EOB fillchar to space
|
-- make end-of-buffer `~` invisible by setting EOB fillchar to space
|
||||||
vim.wo[win].fillchars = "eob: "
|
vim.wo[win].fillchars = "eob: "
|
||||||
|
-- Baseline winhighlight so WinBarNC:WinBar is in effect from the
|
||||||
|
-- first render (not just after the first WinEnter/WinLeave cycle).
|
||||||
|
-- setup_active_win_highlights overwrites on focus change.
|
||||||
|
vim.wo[win].winhighlight = DAPUI_WINHL_INACTIVE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1139,9 +1173,9 @@ function M.enter()
|
|||||||
if active then M._install_for_buf(args.buf) end
|
if active then M._install_for_buf(args.buf) end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
-- Editor resize: recreate any bar that got killed (shrink-below-min),
|
-- Editor resize: recreate any bar that got killed, un-squish any that
|
||||||
-- then re-render. dapui sizes are left alone so user manual resizes
|
-- got shrunk below 3 rows, then re-render. dapui sizes are left alone
|
||||||
-- stick.
|
-- so user manual resizes stick.
|
||||||
vim.api.nvim_create_autocmd("VimResized", {
|
vim.api.nvim_create_autocmd("VimResized", {
|
||||||
group = group,
|
group = group,
|
||||||
callback = function()
|
callback = function()
|
||||||
@@ -1154,33 +1188,33 @@ function M.enter()
|
|||||||
cbar_win, cbar_buf = nil, nil
|
cbar_win, cbar_buf = nil, nil
|
||||||
end
|
end
|
||||||
open_bars()
|
open_bars()
|
||||||
|
ensure_bar_heights()
|
||||||
render_bars()
|
render_bars()
|
||||||
end)
|
end)
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
-- WinResized fires on any geometry change: mouse-drags on split
|
-- WinResized fires on any geometry change: mouse-drags on split
|
||||||
-- borders, bufferline's tabline appearing/disappearing (which shifts
|
-- borders, bufferline's tabline appearing/disappearing (which
|
||||||
-- and can even KILL a bar when the grid gets too tight), `wincmd =`,
|
-- reclaims a row and squishes `winfixheight` bars down), `wincmd =`,
|
||||||
-- etc. Cheap O(1) check for missing bars — only does work when one
|
-- etc. Cheap O(1): validate and un-squish only when something
|
||||||
-- actually got nuked. Always finishes with a redraw! to clear stale
|
-- actually shifted. Always finishes with redraw! to clear stale
|
||||||
-- pixels from the shift.
|
-- pixels from the shift.
|
||||||
vim.api.nvim_create_autocmd("WinResized", {
|
vim.api.nvim_create_autocmd("WinResized", {
|
||||||
group = group,
|
group = group,
|
||||||
callback = function()
|
callback = function()
|
||||||
if not active then return end
|
if not active then return end
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
local need_recover = not (sbar_win and vim.api.nvim_win_is_valid(sbar_win))
|
if not (sbar_win and vim.api.nvim_win_is_valid(sbar_win)) then
|
||||||
or not (cbar_win and vim.api.nvim_win_is_valid(cbar_win))
|
sbar_win, sbar_buf = nil, nil
|
||||||
if need_recover then
|
|
||||||
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()
|
|
||||||
render_bars()
|
|
||||||
end
|
end
|
||||||
|
if not (cbar_win and vim.api.nvim_win_is_valid(cbar_win)) then
|
||||||
|
cbar_win, cbar_buf = nil, nil
|
||||||
|
end
|
||||||
|
if not (sbar_win and cbar_win) then
|
||||||
|
open_bars()
|
||||||
|
end
|
||||||
|
ensure_bar_heights()
|
||||||
|
render_bars()
|
||||||
pcall(vim.cmd, "redraw!")
|
pcall(vim.cmd, "redraw!")
|
||||||
end)
|
end)
|
||||||
end,
|
end,
|
||||||
|
|||||||
Reference in new issue
Block a user