swaits d062ac27ff chore: release 0.2.0
Major UI rework and stability release. Two floating chrome bars replace the
old single toolbar, every resize/tabline-toggle edge case is handled, and
every highlight now derives from the user's colorscheme.

BREAKING CHANGES
- `quit_exits_mode` option removed — `q` is now context-aware: if a DAP
  session is running, `q` terminates the session but keeps debug mode
  active so you can read the Console / inspect post-mortem state. Press
  `q` again to exit debug mode.
- `help_on_enter` default is now `false` (was `true`). Control bar always
  shows `(?) help`, so the auto-popup is no longer needed.
- Default layout changed: sidebar is Call Stack → Scopes → Watches →
  Breakpoints (all 25%). REPL is no longer in the default layout — press
  `E` during debug mode to open it as a floating window.
- Console pane sizing: bottom Console is `max(8 rows, 20% of editor
  lines)` (previously hardcoded). Configurable via `console_height`.

ADDED
- Two-bar floating chrome, independent of any statusline plugin:
  status bar (top, 2 rows, brand + live dap.status text + state chip),
  control bar (bottom, 2 rows, clickable flow buttons + `(?) help`
  right-anchored). Italic qualifiers on step controls auto-drop at
  narrow widths.
- Keymap-aware control bar — legend reflects user's actual remapped
  keys; disabled actions are omitted entirely.
- IP arrow markers on stopped lines: 👉 gutter sign (priority 500) +
  👈 REASON virtual text at EOL.
- Optional debug-mode colorscheme via `colorscheme` option.
- Theme-agnostic separator color (derives from WinBar.fg); global
  WinSeparator override during debug mode for consistent edge color.
- `clear_console_on_start` (default true) — wipe Console scrollback on
  session start while preserving the terminal channel.
- `collapsed_scopes` (default {"Registers"}) — auto-collapse noisy
  scopes that adapters don't flag expensive.
- `max_value_width` (default 2000) — per-line truncation for values in
  Scopes/Watches.
- `console_refresh_ms` (default 50) — trailing-edge Console repaint
  throttle.
- `sidebar` option — "left" (default) or "right".
- `active_window_highlight` (default true) — dim inactive dapui pane
  borders.
- `W` and `K` now work in visual mode (operate on the selection).
- Source window focused before DAP jumps on event_stopped.

CHANGED
- Bars are real splits (not overlapping floats).
- pin_dapui_sizes() only re-pins on external geometry changes (tabline
  toggle); user manual pane drags survive.
- ensure_dapui() runs ONCE per plugin lifetime, avoiding "Debug adapter
  didn't respond" timeouts on toggle.
- Bars clamp to exactly 2 rows on every resize (no stray EOB `~`).
- setup() is fully idempotent; plugin/turbo-debug.lua's deferred
  auto-setup yields to explicit user setup({...}).
- Copyright holder: Stephen Waits <steve@waits.net>.

FIXED
- Bars recreate if killed by shrink-below-minimum resize or tabline
  appear/disappear.
- Inherited winbar cleared on bar windows (no phantom separator rows).
- Layout re-asserts after `wincmd =`.
- Sidebar proportions stable across re-entry.
- Bar separators bright on both outward edges.
- Deferred dependency loading waits for vim.pack.add to finalize.

INTERNAL
- Extracted 10+ helper functions in mode.lua; deduped ~25 call sites.
- Data-driven sign definitions and Python interpreter probing.
- Dead code removed: unused reposition_bars, vestigial sbar_zones/
  handle_sbar_click.
- Added regression tests: test_render_bars, test_highlights_cs,
  test_helpers.
- stylua.toml added; all Lua code formatted.

See CHANGELOG.md for the full 0.2.0 entry.
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00
2026-04-12 20:00:59 -06:00

turbo-debug.nvim

A zero-config debugging experience for Neovim 0.12+. One plugin install, full IDE-class debugging: inline variable values, hover inspection, persistent breakpoints, watch expressions, and modal keybindings that just work.

Inspired by Turbo Pascal — the first IDE where you could just hit a key and start debugging.

turbo-debug screenshot

Install

vim.pack.add("swaits/turbo-debug")

That's it. No other config needed. See :help turbo-debug for customization.

How It Works

turbo-debug is modal. Press <leader>dd and you enter debug mode — the dap-ui layout opens, keybindings activate, virtual text lights up, and you're debugging. Press <leader>dd again (or q) and everything disappears: the session terminates, the UI closes, and every keybinding is restored to exactly what it was.

This modal approach is inspired by debugmaster.nvim. During a debug session you navigate code and inspect state; you don't edit. So debug mode shadows editing keys (c, d, s, r, x) with debug actions and leaves all navigation keys untouched (h, j, k, l, w, b, e, f, /, n, etc.).

A nice text and GUI debugger

The modal keymap is deliberately left-hand optimized so the right hand stays on the mouse for interacting with the dap-ui panes:

  • Left hand on keyboard: c s d r C x X D W E q R fire the twelve rapid-use debug actions without leaving home row.
  • Right hand on mouse: click into Watches/Scopes/Stacks/Breakpoints panes to expand trees, jump to frames, add watch expressions, or toggle breakpoints from the GUI.

Only two modal keys live on the right hand, both by universal convention: K for hover (matches the LSP hover key) and ? for help.

Inside dap-ui panes, the native dap-ui mappings still work — d removes a watch, r sends to REPL, e edits. The modal d (step-into) and r (step-out) yield to dap-ui in those buffers so you can manage the GUI without exiting debug mode.

Keybindings

Everything lives under <leader>d (with which-key support if installed):

Key Action
<leader>dd Toggle debug mode
<leader>dx Toggle breakpoint
<leader>dX Conditional breakpoint
<leader>dD Clear all breakpoints

During debug mode, single-key controls:

Key Action Key Action
c Continue x Toggle breakpoint
s Step over X Conditional breakpoint
d Step into D Clear all breakpoints
r Step out W Watch expression
C Run to cursor K Hover / inspect
q Smart quit (terminate → exit) E Eval in REPL
R Restart session ? Help popup

q is context-aware: if a session is running, it terminates the session and leaves you in debug mode (so you can read the console and inspect the exit state). If no session is running, q exits debug mode entirely.

Press ? during debug mode for a quick reference that also lists dap-ui's native in-pane bindings (<CR>, e, o, d, r, t).

Extending keymaps

Every key is user-overridable. Remap, disable, or add bindings via setup():

require("turbo-debug").setup({
  keys = {
    step_over = "n",       -- remap a modal key
    watch = false,         -- disable a modal key
  },
  global_keys = {
    toggle = "<leader>D",  -- change the toggle chord
  },
  sidebar = "right",       -- "left" (default) or "right"
  help_on_enter = true,    -- show help popup on debug mode entry (default false)

  -- Optional theme swap for debug sessions. `nil` (default) keeps the
  -- current colorscheme; any string is passed to `:colorscheme` on enter
  -- and the original is restored on exit.
  colorscheme = nil,

  -- Auto stop-on-entry when no breakpoints are set (Turbo Pascal model):
  -- press `c` on an empty-BP project and the debugger pauses at main().
  -- Set false to always run until a breakpoint instead.
  stop_on_entry_when_no_breakpoints = true,

  -- Cap per-line value length in Scopes/Watches. Values longer than this
  -- get truncated with `…`. Default 2000 only catches pathological register
  -- dumps. Set 0 to disable truncation entirely.
  max_value_width = 2000,

  -- Console repaint throttle (ms). Max 1000/console_refresh_ms redraws per
  -- second when the debugged program spams stdout. Prevents editor DOS.
  console_refresh_ms = 50,

  -- Clear the Console pane on session start/restart (preserves the
  -- terminal channel — only wipes scrollback).
  clear_console_on_start = true,

  -- Scope names to default-collapse in Scopes. dapui auto-collapses any
  -- scope the adapter marks `expensive`, but codelldb doesn't flag
  -- Registers as expensive even though it's a huge dump.
  collapsed_scopes = { "Registers" },

  -- Dim inactive dapui pane borders so the focused pane is obvious.
  active_window_highlight = true,
})

Chrome bars

When debug mode is active, two 2-row floating bars appear, completely independent of lualine, heirline, or any other statusline plugin:

Status bar (top) shows:

  • The turbo-debug brand
  • Live dap.status() progress text
  • Session state chip: DEBUG · READY / · RUNNING / · PAUSED (color-coded via DiagnosticHint / DiagnosticInfo / DiagnosticError)

Control bar (bottom, above your statusline) shows:

  • Clickable buttons: (c)ontinue / (s)tep over / (d)escend into / (r)eturn out / (R)estart (during session) / (q) terminate
  • (?) help right-anchored
  • Italic qualifiers (over, into, out) are inline when width allows; auto-dropped on narrow terminals

Both bars:

  • Recreate themselves if killed by layout changes (bufferline tabline toggles, wincmd =, etc.)
  • Re-render on every DAP state change (|User DapProgressUpdate|)
  • Close cleanly on exit

Design notes

  • Rounded borders are on every floating window this plugin opens: the help popup (?), the hover popup (K), and the debug-eval popup (E). Floating windows are the only windows Neovim draws borders around in the terminal grid. Splits (the Scopes/Watches/Stacks/Breakpoints/ Console panes) use the user's WinSeparator character — that's a terminal-rendering limit, not a plugin choice.

  • No hardcoded colors. Every highlight links to a standard group (DiagnosticError, Function, Comment, CursorLine, Visual, Search, Title, Special, WinBar, etc.) so the plugin automatically inherits your colorscheme on the fly. Switch themes mid-session (or set colorscheme = "high-contrast" in setup() for a dedicated debug theme) and everything re-paints.

  • Emoji (supplementary-plane, not VS16-dependent) for iconography: 🔴 🟡 ⭕ 👉 📝 for gutter signs; 🟢 🚀 👣 🔽 🔼 🔄 🛑 for control buttons; 🔎 👀 📚 🔴 💻 for pane titles. Colored emoji render consistently in modern terminals (Ghostty, Kitty, WezTerm, iTerm) without font-dependent rendering quirks.

Supported Languages

turbo-debug ships adapter configs for 12 languages. You install the adapter binary (via mason.nvim, your package manager, or manually) — turbo-debug handles all the configuration.

Language Adapter Language Adapter
Python debugpy C# netcoredbg
JavaScript / TS js-debug Java java-debug-adapter
Go delve Kotlin kotlin-debug
C / C++ codelldb PHP php-debug
Rust codelldb Ruby rdbg
Swift codelldb Lua (Neovim) osv

Rust gets special treatment: pressing c runs cargo build automatically and picks the binary via vim.ui.select.

What This Plugin Actually Does

turbo-debug is glue. It does not implement a debugger, a UI framework, virtual text rendering, or breakpoint persistence. It configures and wires together these excellent plugins into a single cohesive experience:

Plugin What it provides
nvim-dap DAP client — the core debug engine
nvim-dap-ui Scopes, stacks, watches, REPL, console UI
nvim-nio Async IO (required by nvim-dap-ui)
nvim-dap-virtual-text Inline variable values in source code
persistent-breakpoints.nvim Breakpoints that survive restarts

All five are loaded automatically via vim.pack.add. You never declare them.

Adapter configurations are inlined from the work of these projects (no runtime dependency, we just studied their configs and wrote plain Lua tables):

License

MIT

S
Description
All-in-one debugger setup for Neovim
Readme MIT
585 KiB
0 Stars 1 Watchers 0 Forks
Languages
Lua 100%