Skip to content

UI: theme, layout, hotkeys

The [ui] section configures the debugger window. It lives outside the profiles because it has nothing to do with launching: a project has many profiles and one window.

version = 1

[ui]
theme = "dark"
width = 1200
height = 800

[profiles.api]
adapter = "dlv"
program = "cmd/api"
Field Required What it is
theme no dark (default), light or system
width no initial window width in logical pixels; 800 by default
height no initial window height in logical pixels; 600 by default
layout no panel layout, below
keymaps no window hotkeys, below

Window size

width and height set the initial inner size of the native debugger window. They are independent: omitting one dimension leaves that dimension to the editor preference or the default. Values must be positive integers. Resizing the window later does not rewrite the project config.

The precedence for each dimension is:

  1. width or height in the project's [ui] section;
  2. gui_width or gui_height from the Neovim plugin;
  3. the default: 800 × 600 logical pixels.

The project value wins because it is shared by everyone opening the project.

Theme

The default is dark, not system. The debugger window opens next to your editor rather than instead of it, and it should follow the OS setting only when asked. That is what system is for, and with it the theme switches on a live session.

An unknown value is a config parse error that lists the allowed ones — not a silent fallback to the default.

Silence is not the same as theme = "dark"

A theme named in [ui] beats everyone: it is shared by everyone who opens the project. Not naming one leaves the choice to whoever opens the window.

Precedence:

  1. [ui] theme from the project config;
  2. --theme from the editor (the plugin's gui_theme becomes bugsaur gui --theme dark|light|system);
  3. the debugger's default — dark.

A launch with no project (--adapter / --program) reads no config, leaving only points 2 and 3.

Layout

The window is divided into a left column and a right area, each into two zones:

+----------------+-----------------------------+
|                |                             |
|   top_left     |            main             |
|                |                             |
+----------------+-----------------------------+   ← main_split
|                |                             |
|  bottom_left   |           bottom            |
|                |                             |
+----------------+-----------------------------+
        ↑ left_split          ↑ left
Field What it is Default
left share of the window width for the left column 0.25
left_split share of the column height for top_left 0.35
main_split share of the right area's height for main 0.35
top_left panels in the upper zone of the left column ["Threads"]
bottom_left panels in the lower zone of the left column []
main panels in the main zone everything else
bottom panels in the zone below the main one ["Call Stack"]

Panel names are written exactly as their tabs read: Threads, Call Stack, Variables, Watches, Evaluate, Logs, Inspector, Breakpoints. Panels in one zone become a tab stack in the order listed.

[ui.layout]
left = 0.3
left_split = 0.5
top_left = ["Threads"]
bottom_left = ["Call Stack"]
main = ["Variables", "Watches", "Inspector"]
bottom = ["Evaluate", "Logs"]

A panel named in no zone is not shown — that is how panels are removed. An empty zone takes up no space at all; an empty main gives its whole area to the left column.

An unnamed zone keeps its default contents

Moving a panel therefore requires clearing its previous zone too: main = [..., "Call Stack"] without bottom = [] is a "panel named twice" error, not a silent choice between the two zones.

Also errors: an unknown panel name, a share outside 0 to 1, and a layout that leaves no panels at all.

Precedence works like the theme's, but the section is taken whole. If [ui.layout] exists, it is used and the editor's gui_layout is not consulted at all; if not, the editor's layout is taken whole (bugsaur gui --layout <json>, same keys); if neither, the defaults above. Mixing zones from two sources would assemble a window nobody designed.

Panels dragged with the mouse are not saved: the layout is built from the config on every launch.

Hotkeys

A dictionary of action → chord:

[ui.keymaps]
continue = "Alt+R"
step_into = "F3"
step_over = "F2"
step_out = "F5"
terminate = "Alt+S"
pause = ""          # unbind

Actions:

  • executioncontinue, step_into, step_over, step_out, pause, terminate, restart;
  • panelsnext_panel, previous_panel, and jumping to one: focus_threads, focus_call_stack, focus_variables, focus_watches, focus_evaluate, focus_logs, focus_inspector, focus_breakpoints;
  • navigationnext_row, previous_row, top, bottom, collapse, expand, activate, delete_row, cancel;
  • search in Variablessearch, search_next, search_previous, search_continue;
  • otherhelp.

An empty chord unbinds. TOML has no null, and there would otherwise be no way for a config to remove a default. An action has exactly one chord: focus_variables = "Alt+V" also removes Ctrl+V.

A chord is the modifiers Cmd, Ctrl, Alt, Shift joined by +, plus a key name (F1F20, a letter, Enter, Space, ArrowDown, …). Modifier order does not matter. An unknown action, key or modifier stops the launch: a mistyped hotkey would otherwise simply never fire, giving no sign of itself.

Precedence is per action, not per block. What [ui.keymaps] names overrides gui_keymaps from Neovim; what it does not name stays as configured in the editor; what nothing names keeps the built-in default. Per-block would be awkward: bindings are independent of each other, and a project that overrides continue alone should not wipe out someone's personal setup.

The full stack, bottom to top:

  1. built-in defaults (F7, F8, Shift+F8, F9, Cmd+F2 / Ctrl+F2, H J K L, the arrows);
  2. <root>/.bugsaur-keys.json — a personal per-project file, chord → action, where null unbinds;
  3. gui_keymaps from the Neovim plugin's setup();
  4. [ui.keymaps] from this config.

F1 or ? in the window shows what all four layers produced.