feat: italic qualifiers on step controls + unified WinSeparator color
Control-bar step labels now show the standard debugger qualifier after the key mnemonic, styled italic: (s)tep over (was (s)tep) (d)escend into (was (d)escend) (r)eturn out (was (r)eturn) The qualifier is rendered with a new `TurboDebugBarItalic` highlight (Comment fg + italic). When the cbar can't fit all controls with qualifiers, `build_controls(true)` is retried as `build_controls(false)` and the qualifiers are dropped — the compact labels survive. Also overrides the global `WinSeparator` highlight to the bright TurboDebugBarSeparator color during debug mode, so every split divider in the editor — between dapui panes, between source and console, between our bars and their neighbors — renders one consistent chrome color. Original WinSeparator captured on first apply, restored byte- for-byte on M.exit.
This commit is contained in:
1 file changed
+98
-25
+98
-25
@@ -34,6 +34,15 @@ local installed = {}
|
||||
-- our initial proportions.
|
||||
local last_dapui_total_height = nil
|
||||
|
||||
-- The user's theme's WinSeparator highlight, captured on debug-mode enter
|
||||
-- before we override it to the bright TurboDebugBarSeparator color. We
|
||||
-- want EVERY horizontal/vertical split divider in the editor to match our
|
||||
-- bar separator style during a debug session — the dim default makes
|
||||
-- dapui's pane-to-pane boundaries look like a visual seam, while the
|
||||
-- bright override gives the whole debug UI a single consistent chrome
|
||||
-- color. Restored byte-for-byte on M.exit.
|
||||
local saved_winsep_hl = nil
|
||||
|
||||
local dapui_initialized = false
|
||||
local vt_initialized = false
|
||||
|
||||
@@ -163,6 +172,26 @@ local function define_highlights()
|
||||
hl(0, "TurboDebugBarReady", { default = true, link = "DiagnosticHint" })
|
||||
hl(0, "TurboDebugBarRunning", { default = true, link = "DiagnosticInfo" })
|
||||
hl(0, "TurboDebugBarPaused", { default = true, link = "DiagnosticError" })
|
||||
-- Italic qualifier words ("over", "into", "out") shown after the
|
||||
-- step/descend/return labels. Subordinate styling: Comment fg + italic,
|
||||
-- so the key label ("(s)tep") reads primary and the qualifier ("over")
|
||||
-- reads secondary. Can't combine link+italic via nvim_set_hl; derive
|
||||
-- fg from Comment and set italic explicitly.
|
||||
local function apply_italic_hl()
|
||||
local ok, src = pcall(vim.api.nvim_get_hl, 0, { name = "Comment", link = false })
|
||||
if not ok or not src or not src.fg then
|
||||
vim.api.nvim_set_hl(0, "TurboDebugBarItalic", { default = true, link = "Comment" })
|
||||
return
|
||||
end
|
||||
vim.api.nvim_set_hl(0, "TurboDebugBarItalic", {
|
||||
fg = src.fg, italic = true, default = true,
|
||||
})
|
||||
end
|
||||
apply_italic_hl()
|
||||
vim.api.nvim_create_autocmd("ColorScheme", {
|
||||
group = vim.api.nvim_create_augroup("TurboDebugBarItalicHL", { clear = true }),
|
||||
callback = apply_italic_hl,
|
||||
})
|
||||
|
||||
-- The key letter inside (c)ontinue etc. gets bold + Special's fg.
|
||||
-- No underline — the descender on lowercase q/g/p/y overlaps the
|
||||
@@ -191,6 +220,12 @@ local function define_highlights()
|
||||
-- the semantic we want for our bar edges. Theme-agnostic. If
|
||||
-- WinBar has no fg (very rare), fall back to WinSeparator so
|
||||
-- we at least match the theme's split-divider convention.
|
||||
--
|
||||
-- Also override the global WinSeparator to the same fg during
|
||||
-- debug mode, so every split divider in the editor matches our
|
||||
-- bar chrome (dapui pane-to-pane, source-to-console, our bars to
|
||||
-- their neighbors — all one consistent color). Original captured
|
||||
-- on first call, restored in M.exit.
|
||||
local function apply_sep_hl()
|
||||
local ok, src = pcall(vim.api.nvim_get_hl, 0, { name = "WinBar", link = false })
|
||||
if ok and src and src.fg then
|
||||
@@ -198,6 +233,11 @@ local function define_highlights()
|
||||
-- we set in the table-of-defaults above. default semantics
|
||||
-- would leave the placeholder in place.
|
||||
vim.api.nvim_set_hl(0, "TurboDebugBarSeparator", { fg = src.fg })
|
||||
if saved_winsep_hl == nil then
|
||||
local wok, ws = pcall(vim.api.nvim_get_hl, 0, { name = "WinSeparator", link = false })
|
||||
if wok then saved_winsep_hl = ws or {} end
|
||||
end
|
||||
vim.api.nvim_set_hl(0, "WinSeparator", { fg = src.fg })
|
||||
return
|
||||
end
|
||||
vim.api.nvim_set_hl(0, "TurboDebugBarSeparator", { link = "WinSeparator" })
|
||||
@@ -272,9 +312,12 @@ local function keyof(name, fallback)
|
||||
return fallback
|
||||
end
|
||||
|
||||
-- Build a label's byte-level representation: returns { text, hl_spans, key_col, key_end_col }
|
||||
-- Format: "<icon> (<key>)<tail>" e.g. ` (c)ontinue` with byte offsets for highlights.
|
||||
local function build_control_text(icon, key, tail)
|
||||
-- Build a label's byte-level representation.
|
||||
-- Format: "<icon> (<key>)<tail>[ <italic>]"
|
||||
-- e.g. ` (s)tep over` with `over` styled italic.
|
||||
-- Returns: text, key_col, key_end_col, italic_col, italic_end_col
|
||||
-- (italic_col/italic_end_col are nil when italic is not set).
|
||||
local function build_control_text(icon, key, tail, italic)
|
||||
local parts = {}
|
||||
local function add(s) parts[#parts + 1] = s end
|
||||
local function col() return #table.concat(parts) end
|
||||
@@ -286,8 +329,15 @@ local function build_control_text(icon, key, tail)
|
||||
local key_end_col = col()
|
||||
add(")")
|
||||
add(tail or "")
|
||||
local italic_col, italic_end_col
|
||||
if italic and italic ~= "" then
|
||||
add(" ")
|
||||
italic_col = col()
|
||||
add(italic)
|
||||
italic_end_col = col()
|
||||
end
|
||||
|
||||
return table.concat(parts), key_col, key_end_col
|
||||
return table.concat(parts), key_col, key_end_col, italic_col, italic_end_col
|
||||
end
|
||||
|
||||
local function render_sbar()
|
||||
@@ -378,36 +428,50 @@ local function render_cbar()
|
||||
local quit_tail = has_session and " terminate" or "uit"
|
||||
|
||||
local controls = {
|
||||
{ icon = continue_icon, key = keyof("continue", "c"), tail = continue_tail, fn = action("continue") },
|
||||
{ icon = ICON.step_over, key = keyof("step_over", "s"), tail = "tep", fn = action("step_over") },
|
||||
{ icon = ICON.step_into, key = keyof("step_into", "d"), tail = "escend", fn = action("step_into") },
|
||||
{ icon = ICON.step_out, key = keyof("step_out", "r"), tail = "eturn", fn = action("step_out") },
|
||||
{ icon = continue_icon, key = keyof("continue", "c"), tail = continue_tail, italic = nil, fn = action("continue") },
|
||||
{ icon = ICON.step_over, key = keyof("step_over", "s"), tail = "tep", italic = "over", fn = action("step_over") },
|
||||
{ icon = ICON.step_into, key = keyof("step_into", "d"), tail = "escend", italic = "into", fn = action("step_into") },
|
||||
{ icon = ICON.step_out, key = keyof("step_out", "r"), tail = "eturn", italic = "out", fn = action("step_out") },
|
||||
}
|
||||
-- (R)estart is only meaningful during an active session
|
||||
if has_session then
|
||||
controls[#controls + 1] = { icon = ICON.restart, key = keyof("restart", "R"), tail = "estart", fn = action("restart") }
|
||||
controls[#controls + 1] = { icon = ICON.restart, key = keyof("restart", "R"), tail = "estart", italic = nil, fn = action("restart") }
|
||||
end
|
||||
controls[#controls + 1] = { icon = ICON.stop, key = keyof("terminate", "q"), tail = quit_tail, fn = action("terminate") }
|
||||
controls[#controls + 1] = { icon = ICON.stop, key = keyof("terminate", "q"), tail = quit_tail, italic = nil, fn = action("terminate") }
|
||||
|
||||
local ctrl_parts = {}
|
||||
local ctrl_spans = {}
|
||||
local ctrl_key_spans = {}
|
||||
local ctrl_zones_bytes = {}
|
||||
local gap = " "
|
||||
for i, c in ipairs(controls) do
|
||||
local prefix_len = #table.concat(ctrl_parts)
|
||||
if i > 1 then
|
||||
ctrl_parts[#ctrl_parts + 1] = gap
|
||||
prefix_len = prefix_len + #gap
|
||||
local function build_controls(include_italic)
|
||||
local parts, spans, key_spans, italic_spans, zones = {}, {}, {}, {}, {}
|
||||
for i, c in ipairs(controls) do
|
||||
local prefix_len = #table.concat(parts)
|
||||
if i > 1 then
|
||||
parts[#parts + 1] = gap
|
||||
prefix_len = prefix_len + #gap
|
||||
end
|
||||
local italic_text = include_italic and c.italic or nil
|
||||
local txt, kcol, kend, icol, iend = build_control_text(c.icon, c.key, c.tail, italic_text)
|
||||
parts[#parts + 1] = txt
|
||||
spans[#spans + 1] = { prefix_len, prefix_len + #txt, "TurboDebugBarCtrl" }
|
||||
key_spans[#key_spans + 1] = { prefix_len + kcol, prefix_len + kend }
|
||||
if icol then
|
||||
italic_spans[#italic_spans + 1] = { prefix_len + icol, prefix_len + iend }
|
||||
end
|
||||
zones[#zones + 1] = { prefix_len, prefix_len + #txt, c.fn }
|
||||
end
|
||||
local txt, kcol, kend = build_control_text(c.icon, c.key, c.tail)
|
||||
ctrl_parts[#ctrl_parts + 1] = txt
|
||||
ctrl_spans[#ctrl_spans + 1] = { prefix_len, prefix_len + #txt, "TurboDebugBarCtrl" }
|
||||
ctrl_key_spans[#ctrl_key_spans + 1] = { prefix_len + kcol, prefix_len + kend }
|
||||
ctrl_zones_bytes[#ctrl_zones_bytes + 1] = { prefix_len, prefix_len + #txt, c.fn }
|
||||
return table.concat(parts), spans, key_spans, italic_spans, zones
|
||||
end
|
||||
local controls_text = table.concat(ctrl_parts)
|
||||
|
||||
-- Try with italic qualifiers first; fall back to the compact form if
|
||||
-- the label row would overflow the available width. Min 1-char padding
|
||||
-- on each side of the controls-plus-help block.
|
||||
local controls_text, ctrl_spans, ctrl_key_spans, ctrl_italic_spans, ctrl_zones_bytes =
|
||||
build_controls(true)
|
||||
local controls_dw = vim.fn.strdisplaywidth(controls_text)
|
||||
if controls_dw + help_dw + 2 > width then
|
||||
controls_text, ctrl_spans, ctrl_key_spans, ctrl_italic_spans, ctrl_zones_bytes =
|
||||
build_controls(false)
|
||||
controls_dw = vim.fn.strdisplaywidth(controls_text)
|
||||
end
|
||||
|
||||
-- controls centered between left edge and help (right anchor)
|
||||
local ctrl_zone_width = width - help_dw
|
||||
@@ -441,6 +505,11 @@ local function render_cbar()
|
||||
end_row = 0, end_col = ctrl_byte_offset + span[2], hl_group = "TurboDebugBarKey",
|
||||
})
|
||||
end
|
||||
for _, span in ipairs(ctrl_italic_spans) do
|
||||
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 0, ctrl_byte_offset + span[1], {
|
||||
end_row = 0, end_col = ctrl_byte_offset + span[2], hl_group = "TurboDebugBarItalic",
|
||||
})
|
||||
end
|
||||
|
||||
local help_byte_start = #content_line - #help_text
|
||||
pcall(vim.api.nvim_buf_set_extmark, cbar_buf, bar_ns, 0, help_byte_start, {
|
||||
@@ -1332,6 +1401,10 @@ function M.exit()
|
||||
close_bars()
|
||||
if vt_initialized then require("nvim-dap-virtual-text").disable() end
|
||||
last_dapui_total_height = nil
|
||||
if saved_winsep_hl ~= nil then
|
||||
pcall(vim.api.nvim_set_hl, 0, "WinSeparator", saved_winsep_hl)
|
||||
saved_winsep_hl = nil
|
||||
end
|
||||
end
|
||||
|
||||
function M.toggle()
|
||||
|
||||
Reference in new issue
Block a user