F5 runs the program in front of you under a debugger: breakpoints,
stepping, the call stack, variables, watch expressions and a debug console.
Shift+F5 stops it. Settings > Debuggers has a card for each adapter with
its status, Install, Update or Remove where they apply, and an on or off
switch.
Supported debuggers
| Debugger | Languages | Install | What F5 can run |
|---|---|---|---|
| js-debug | JavaScript, TypeScript | Bundled. Needs node on your PATH. |
This file, a package script, or attach to a port |
| debugpy | Python | Tori installs it, from the card or the first time you press F5 | This file, a module, or pytest on this file |
| Delve | Go | go install github.com/go-delve/delve/cmd/dlv@latest |
This file’s package, or its tests |
| lldb-dap | Rust, C, C++, Objective-C | xcode-select --install (Apple’s Command Line Tools) or Xcode |
A Cargo binary, or any binary you pick |
If the debugger for a file is missing, F5 says so with an Install button. For debugpy that is Tori’s own install; for the others it runs the command above in a terminal. Press F5 again once it finishes.
Notes per debugger
- js-debug attaches to a process started with
node --inspect(port 9229 by default). A package script runs through the package manager your lockfile names, sopnpm run dev, notnpm run dev. - debugpy lives in a virtualenv Tori makes for it, under
~/.config/tori/debuggers/. Your program still runs on the project’s own Python: the nearest.venvorvenv, elsepython3on your PATH. The project does not need debugpy installed. - Delve installs into
$(go env GOPATH)/bin. If that is not on your PATH, add it, or point Tori at it with your own TOML (below). - lldb-dap is found through
xcrun. Homebrew’sllvmworks too once itsbinis on your PATH. A Rust target is built withcargo buildfirst, with the build output in the debug console, and gets Rust’s own pretty printers so aStringreads as text. Build C and C++ programs with-gyourself.
Starting a run
The file in front of you picks the debugger. The first F5 asks what to run; after that F5 replays the last target for that debugger in this workspace. A workspace with a Go service and a TypeScript frontend remembers one of each. The command palette has “Debug this file”, “Debug a package script” and “Attach the debugger to a port” for picking again.
Debugging runs the project’s code, so it only starts in a trusted project. F5 offers to trust it if needed.
Using the debugger
Set breakpoints
Click in the gutter left of the line numbers, or press F9 on a line. A dot
marks the breakpoint, and its tooltip says how far along it is:
| Dot | Means |
|---|---|
| Hollow, pending | The file has unsaved changes. Save it to arm the breakpoint |
| Hollow, set | Armed, but the debugger has not confirmed it yet |
| Filled, bound | The debugger will stop here |
Breakpoints follow their code as you edit, so adding lines above one does not leave it on the wrong line. They are kept per workspace and survive a relaunch.
Start and pick what to run
Press F5. The first time, What to debug asks, with choices for the
debugger behind the file in front of you:
| Debugger | Choices |
|---|---|
| js-debug | This file, a Package script from package.json, or attach to an Inspector port (node --inspect, 9229 by default) |
| debugpy | This file, a Module (python -m app.main), or pytest on this file |
| Delve | This package, which builds and runs it, or Package tests |
| lldb-dap | A Cargo binary, built with cargo build first, or any Program built with debug info |
After that, F5 runs the same target again. Debug this file, Debug a
package script… and Attach the debugger to a port… in the palette
pick again.
The Debug panel
The Debug panel in the right panel opens when a run starts. From the top:
- Controls: Continue (or Pause while running), Step over,
Step into, Step out, Restart and Stop.
Shift+F5stops too. Step controls are greyed out unless the program is paused. - Sessions: what is running, as a tree. A test runner can be several levels deep, one per worker.
- Call stack, while paused: one heading per paused session, paused on breakpoint (or exception, step…), then its frames. Click a frame to see its source and variables. The selected frame has an Ask button.
- Variables: the scopes of the selected frame, such as Local and Closure.
Expand a scope or an object to see what is inside. A scope the debugger marks
as expensive says slow. A long list shows Show N more. Where the
debugger allows it, click a value to change it, press
Enterto set it orEscapeto cancel. - Watch: expressions you want answered at every stop. Type one in
Expression to watch and press
Enter. Each row shows its value, or the debugger’s error if it cannot be read, and has move up, move down and remove buttons. Watches are kept per workspace, up to 50, and are there before a run starts. - Console: everything the program and the debugger printed, from every session, in the order it happened, each line tagged with its session.
- Evaluate an expression: a console prompt under the console. Type an
expression and press
Enterto evaluate it in the paused frame.ArrowUpandArrowDownwalk earlier entries.
Read values in the editor
While paused, hover a variable in the editor to see its value right there,
including expressions like res.status. Function calls are never evaluated on
hover, so hovering cannot change your program.
Ask the agent about a frame
Paused on a bad value, click Ask on the selected frame, or beside
Variables. Tori writes a message for the session selected in the sidebar:
which session is paused and why, where (@src/deliver.ts#L13), the call stack,
and the variables you have expanded. It lands in that chat’s composer for you to
add a question and send; it is never sent for you.
Long values and big scopes are cut down before they are written, and the message says what was left out, so one question never fills the agent’s context.
Turning one off
The card’s switch writes dap.disabled in ~/.config/tori/settings.json:
{ "dap": { "disabled": ["delve"] } }Changing how a debugger starts
Each debugger is a TOML file in ~/.config/tori/packs/dap/, but a file there
may not reuse a built in id, and F5 knows each built in debugger by its id.
So a file cannot change how a built in debugger starts. Put the program it
runs on your PATH instead: for Delve, add ~/go/bin to your PATH.
A debugger for a new language is different from the other tools: the file gets a card, but what F5 offers to run is built into Tori per debugger. So a new debugger needs a request rather than only a TOML.
Missing a debugger?
Request a debugger. Say which adapter, the language, and how
you start a debug session with it today (a VS Code launch.json is ideal).