# Help and the inspector

Completion, quick doc, jump to definition, rename, the command palette, and the value inspector.

## Help

Completion, quick documentation, jump to definition and rename all resolve
in the namespace your program runs in, so a function Run just defined
shows up like a core one. The right-click menu and command palette reach
them too.

Ctrl+Space, Ctrl+Q and Ctrl+B mean Control specifically, even on a Mac. The
F-keys take no Control or Command (most Mac keyboards need Fn). What each
runtime supports is in [Known limits](known-limits.md).

### Completion

Ctrl+Space, in the editor or the console's input line, opens a popup of
candidates with a doc preview. One match is inserted straight away.

![The completion popup open over a partial word, with a candidate list and its doc preview](../images/completion.png)

- Up and Down move one row, PageUp and PageDown eight.
- Enter or Tab commits; Escape closes without changing the buffer.
- In the console, Up/Down and Enter belong to the popup while it's open, not
  to history or evaluation.

Candidates come from special forms, built-ins, the file's own definitions,
and whatever your program has defined. Each row is tagged with its source
(`special`, `builtin`, `buffer`, `user`, and for ClojureScript `core` or
`remote`). When a server answers completion, its names join the list as
they arrive.

### Quick documentation

Ctrl+Q or Shift+F1 shows the namespace, arglists and docstring for the
symbol at the caret. Escape or moving the caret closes it.

![The quick documentation popup over a call to a core function](../images/quick-doc.png)

### The docs panel

The popovers close as soon as you type, so they can't stay beside the code.
**Show the documentation in a panel**, in the palette or on Cmd/Ctrl+Alt+9, puts the doc of
the symbol at the caret in a panel on the right, where it stays while you
keep working. It asks the way full doc does: a dialect with a full doc
answers with that (JVM Clojure's includes ClojureDocs examples, each with
Run in Interactions and Copy to Definitions), and Jolt and ClojureDart show
the quick doc text. (Cmd/Ctrl+Alt+D would be easier to remember, but macOS
takes Cmd+Option+D to hide the Dock.) With no symbol at the caret the
command just brings the panel back and focuses it.

![The docs panel open on clojure.core/map after following the qualified name, with Back, Forward, Bookmarked and Close buttons above the page](../images/reader.png)

- **Following a symbol.** Click a symbol in the page and its doc becomes the
  next page. A symbol with no docs says so on the panel's footer and leaves
  the page as it was, and a drag that selects text isn't a click. The click
  waits out the double-click time, so a double-click that selects a word
  follows nothing. Guide
  pages work the same way.
- **History.** Back and Forward, or `[` and `]` while the panel has focus,
  move through the last 50 pages. Opening a doc after going back drops the
  pages that were ahead, as a browser does. Back and Forward show a page as
  it read when you first opened it, so Show the documentation in a panel on the same symbol
  asks again when you want a fresh one.
- **Bookmarks.** The Bookmark button, or **Bookmark This Page** in the
  palette, saves the page you're reading, and pressing it on a bookmarked
  page removes the bookmark. They live in the settings file under
  `:doc-bookmarks`, so every window and the next start have them. **Open a
  Bookmarked Page…** lists them in the palette and picking one opens it. A symbol
  is kept under the qualified name full doc gave (`clojure.core/map`), or as
  you typed it when the dialect has no full doc, and is looked up again from
  the tab you're in. If you edit `:doc-bookmarks` by hand into something
  Jonquil can't read whole (an entry that isn't a page, say), Bookmark and the
  bookmark list leave the file alone and say why on the panel's footer.
- **Guide pages.** **Open a Guide Page…** lists the pages of `docs/guide`
  beside the Jonquil source tree that is running. A page shows as plain
  text: headings underlined, links as their words, code indented. The
  installed app is one compiled file with no source tree, so there the
  command says the guide isn't installed.

**Switch to panel: Reader** brings the panel back once you've had it
open.

### Jump to definition

F12 or Ctrl+B jumps to the symbol's definition: the nearest local binding,
then a top-level `def` or `defn` in the file, then another file if the
runtime can say where it lives.

- A file in your project opens in a tab; Cmd/Ctrl+[ goes back.
- Library code and files outside the project open in a read-only source
  panel on the right. In it, F12 follows definitions, Cmd/Ctrl+[ goes back,
  Escape closes it and Cmd/Ctrl+I swaps it for the inspector.
- Where a runtime can't say (or for built-ins), you get the var's
  documentation instead.

![F12 on clojure.string/join in a JVM Clojure window: the source panel shows string.clj from the Clojure jar, read-only, on join's definition](../images/clj-source.png)

### Rename

Shift+F6 or F2 opens a popup prefilled with the symbol. Enter applies,
Escape cancels. Only code changes: exact symbol tokens, never comments,
strings, regexes, keywords or longer names (renaming `greet` leaves
`greeting`). A blank or invalid name is explained in the popup. A rename is
one undo step.

![The rename popup, prefilled with the symbol at the caret](../images/rename.png)

### Right-click menus

Shift+F10 or a right click over the editor opens a menu with Run Form, Jump
to Definition, Rename Symbol, Quick Documentation, Inspect Last Result,
Inspect Form, Complete and the editing commands. Each runs the same
function as its key.

### The command palette

Cmd/Ctrl+Shift+P lists Jonquil's commands, with their keys and source
(built-in or plugin). Type words to narrow by description or id, Up and
Down to move, Return or a click to run, Escape to close.

The same words work at the Interactions prompt after a `!`, so you can run
a command without leaving the keyboard's place: `!clear the console` and
Return runs it, and the console names what ran. Words that match several
commands list them and run none, and a command whose description is
exactly the words wins over the longer ones. The line goes into the
history like any other, so Up brings it back. Only one line of words
counts, with a space somewhere after the `!`: a single word after it is
read as code, since `!x` and `!=` are Clojure symbols, so type `! clear`
for a one-word command. A line with brackets or quotes, or more than one
line, always goes to the evaluator.

### Switching panels

The panels on the right share one place, so opening one hides the one that
was there. The palette lists **Switch to panel: name** for each panel you've
had open in this window, the one you saw last first, leaving out the one on
screen. Cmd/Ctrl+Alt+1 to 8 each go to one panel: 1 the Inspector, 2
Source, 3 Locations, 4 Frames, 5 Trace, 6 Plugins, 7 Messages and 8
Processes (Cmd/Ctrl+Alt+9 shows a doc in [the docs panel](#the-docs-panel)). Both focus the panel
when it's showing. Otherwise Frames, Trace and Plugins open the way their own
commands do, asking for the last error, the recorded calls or the plugin
list as they are now. The Inspector, Source and Locations come back with
what they showed when it still holds. Until something fills them, their
keys say on the status bar what does. Switching to another dialect empties
the Inspector and Source, and closing the tab a locations list came from
clears that list, so those leave the palette's list too. A plugin's history
and an agent's proposal aren't offered, since each belongs to the plugin or
proposal it was opened from.

### Layouts

**Save the layout**, in the palette, keeps the window's arrangement as it
is: whether the file tree shows and how wide, which right-hand panel shows,
and where the editor and console split. The panel opens at its usual
width. It's named after what
shows, such as `File tree and Trace panel` or `Editor only`, so saving again
with the same parts showing replaces it. The palette then lists **Use
layout: name** for each one, which shows or hides the tree, opens the panel
the way its key does and sets the tree's width and the split. A panel with
nothing to show yet says so instead. **Forget every saved layout**
clears them. Layouts live in the settings file, so every window and the
next start see them.

### Messages

The Messages panel keeps a log of what happened in the background while you
were looking elsewhere, newest at the top. Every MCP tool call an agent makes
gets a row naming the agent by the name its client sends, so a
`jonquil_replace_text` from Claude Code reads "Claude Code replaced the text
of core.clj". When an open tab's file changes on disk, there's a row for that
too. That's the case to watch for, because agents usually edit files with
their own tools, which Jonquil never sees. The panel also logs an agent
attaching or leaving, an attached runtime losing its connection, and a
runtime starting or failing to start.

![The Messages panel with three rows: two agent rows where Claude Code replaced the text of demo.clj and core.clj, and a read row for the tab list](../images/messages.png)

The tick boxes above the list choose what shows. Agent, Files, Plugins,
Connections and Runtime are on to begin with. Reads (an agent reading a tab,
the console or the docs) and Status (everything the status bar has said, with
repeats merged into one row like "Ready ×3") are logged but hidden. While
there's anything unread of the kinds that start ticked, the status bar shows
"● 3 messages", and clicking it opens the panel. Showing the panel marks
everything read.

Enter on a row jumps to its tab, and on an agent's evaluation it opens the
code and the result below the row, cut at 20 lines. A row for a plugin
proposal opens the Review panel. The disk check covers files open in tabs,
about every two seconds. A tab with no unsaved edits follows its file, and
one with unsaved edits keeps them ([When the file changes on
disk](files-and-tabs.md#when-the-file-changes-on-disk)). The log keeps
the newest 1,000 entries, and the footer says how many older ones it dropped.

### Describing keys

Three palette commands explain the keys, from the live keymap, plugins
included. **Describe the next key you press** prints what the next key
does: the command it runs, its source, and any condition it needs, or that
it goes to GTK. That key is only described, never run. **List every key
binding** prints the window's keys, then the editor's, each with its
command's description. **Show the keys you pressed last** lists the last
50 chords with the command that ran for each, or that the window passed it
on to the editor or GTK. Typing, including Return, Tab and Backspace, shows
only as a count, never as the characters typed. A command's own keys are in the
palette's row for it.

When the editor's caret sits on a symbol, the status bar names the keys
that act on it, such as `Ctrl+Q doc · F12 jump · Shift+F6 rename`. They're
read from the live keymap, so your own bindings show, and the line follows
the caret a moment after it stops. **Show what keys do at the caret**, in
the palette, turns the line off or back on for this session.

### The locations list

Three palette commands list places in the buffer, in a panel on the right:
**List the uses of the symbol at the caret** (code only, never comments,
strings or keywords), **List the definitions of the symbol at the caret**
(a `defmulti` and each of its `defmethod`s, for example), and **List every
match of the find query, or of the selection** (the find bar's query when
it has one). A list stops at 1,000 places, and its title says when it did.
F8 or Cmd/Ctrl+. goes to the next place and Shift+F8 or Cmd/Ctrl+Shift+. to
the previous one, selecting it (on a Mac, F8 needs fn, see [Keys](keys.md)); a
click on a row goes there too, and F8 keeps working after the panel is
closed. Places follow ordinary edits, so a line added above one doesn't
move it off its text. A place whose text you deleted is reported gone.
Format All and reloading the file from disk replace the whole buffer, which
leaves the list out of date: F8 says so, and listing again fixes it.
Stepping past either end says so. A buffer list belongs to the tab it came
from: closing that tab clears it.

**Find Usages in Project** lists every use of the symbol at the caret in
the project's Clojure files: the folder the file tree shows, or the file's
own folder when there's no tree, minus what `.gitignore`, dot folders,
`target` and `node_modules` hide. An open tab counts with its unsaved text.
It works out which var the symbol at the caret names, from the file's `ns`
form (its aliases, `:refer` lists and `:refer-clojure` options), the file's
own definitions, the local bindings around the symbol and `clojure.core`,
and decides each place it finds the same way. So with the caret on
`str/join`, a `join` of your own in another namespace isn't listed, and
neither is a `let`-bound `join`. Two more check boxes under the kinds bring
the rest back: **Other vars** (another var, or a local, with the same name)
and **Unresolved**, for places a file can't decide from its own text, such
as a name brought in by `:refer :all`, `(:use ...)` or a top-level
`require` (in a file with `:refer :all` or `:use`, a `clojure.core` name
too, since one of those may shadow it), or one a macro defines. Each box shows how many places it holds,
and their rows say `other var` or `unresolved` after the kind. When the
symbol at the caret is itself a local, or can't be decided, the list
matches by name instead, so `str/join` and `clojure.string/join` both count
as uses of `join`, and the two boxes stay hidden. A `.cljc` file is read
for the window's dialect. Each row
reads `path:line: kind: text`, where the kind says how the name is used: a
`call` (the first thing in a list, `(join ...)` or `#(join ...)`), a `value`
(passed around, `(map join xs)`), a `def` (the name a `def` form defines),
or `ns` (inside the `ns` form, such as `:refer [join]`). The check boxes
above the list show or hide each kind. F8 steps through the shown rows and
opens each file in a tab when it gets there, and from then on that file's
places follow its edits as a buffer list's do. A file whose text changed
after the search, on disk or in its tab, isn't stepped into at the old
places: F8 says it changed, and searching again fixes it. Closing a tab
keeps the list, and F8 opens that file again. Like the other lists it
stops at 1,000 places. The search runs in the background, so typing
carries on while it looks.

![Find Usages in Project on a/join: the Locations panel titled Usages of demo.a/join with its definition and one call, and the Other vars (1) and Unresolved (1) check boxes under the kinds](../images/find-usages.png)

**Compare with the file on disk** and **Compare with git HEAD** list the
places where the tab differs from that version, one row per difference,
such as `4: 2 lines here, 1 line in HEAD: (def c 3)` with the other
version's first line. F8 steps through them like any list. **Take the
other version here** puts the compared version's lines in place of the
difference at the caret, as one step Cmd/Ctrl+Z undoes and with Parinfer
left out of it, and lists what's left. git runs as `git show HEAD:./name`
in the file's own folder, so a file outside a repository, or one git
doesn't track, gets git's reason on the status bar. Line endings and trailing
blank lines don't count as differences, and a Take keeps the tab's own line
endings.

**Compare with tab:** does the same against another open tab, as that tab
is right now, saved or not. The palette has one entry per open tab, such as
**Compare with tab: core.clj**, the tab you used most recently first. Take
reads that tab again, so an edit made there after the compare is what
comes back. If the tab has closed since, Take uses the text it had at the
compare.

**Compare with the last Run** compares the tab with what its last whole Run
sent, so you can see what you've changed since the code that's running was
loaded. Only a Run of the whole tab that ended without an error counts; Run
Form and Run Selection don't move it. Before the first such Run the status
bar says there's nothing to compare with yet.

**This form vs the file on disk**, **vs git HEAD** and **vs the last Run**
do the same for just the top-level form around the caret, so the other
forms' edits stay out of the list. The form is found in the other version
by its first two elements, such as `defn f` (three for a `defmethod`, whose
dispatch value tells its methods apart, and just `comment` for a comment
block), and by its place among forms that share them, so a second
`(comment ...)` block matches the second one there. The list's title names
the form, with its place when it isn't the first, Take works the same way,
and a version with no such form says so on the status bar. Adding or
removing a form that starts the same way, above or below it, moves its
place, so Take then asks you to compare again rather than guess. Typing
"this form" in the palette finds the three.

![This form vs the file on disk: the edited defn greet in the tab, and the Locations panel titled Differences in defn greet from disk with one row for it, though another form differs too](../images/compare-form.png)

### Freeing Ctrl+Space on macOS

macOS reserves Ctrl+Space for "Select the previous input source" (System
Settings > Keyboard > Keyboard Shortcuts > Input Sources). While it's on,
the keypress never reaches Jonquil. Turn it off or rebind it to use
completion.

## Inspecting values

Cmd/Ctrl+I opens the inspector on the last result (`*1`). Cmd/Ctrl+Shift+I
evaluates the form at the caret, as Run Form would, and inspects that. The
panel takes focus until you close it.

![The inspector panel open on a map, with a vector of same-shaped maps rendered as a table](../images/inspector.png)

The panel on the right shows a path, the value's type and size, a toolbar
(Back, Earlier, Later, Meta, Table, Def, Close), the rows, and a footer of keys. A
collection's rows read `label · preview`; maps sharing the same keys
become a table. The path starts with where the value came from: `*1` for the
last result, the printed line for a console result you clicked (its first 40
characters), a local's name, or a traced call such as `(demo.core/area 3)`.

- Up and Down move. Return or Right goes in; Left or Backspace comes out.
- Page Up and Page Down page through long collections.
- `m` toggles metadata; `v` switches a table to a list and back.
- `d` prompts for a name and defines that var from the current value.
- `[` and `]`, or Earlier and Later, go back and forward among the objects
  you inspected, each opened at its top; the footer then reads `object 2 of
  3`.
- Escape closes the panel (inside the `d` prompt, only the prompt).

The inspector remembers the last 10 objects it was opened on (an object
inspected twice in a row counts once), and opening a new one after going
back drops the later ones, as a browser does. It keeps the objects
themselves so they can be browsed again, so a large result stays in memory
until 10 newer inspections push it out. The history works with Jolt, in
process and out; a JVM server's inspector keeps only its current object, so
there Earlier and Later say "This server keeps only the object it shows
now."

Out of process, Jonquil loads the inspector into its Jolt child as nREPL
middleware, so stepping through a value never changes `*1`, `*2` or `*3`. Collections cap
at 1000 entries (`1000+ items`). Runtime support is in
[Known limits](known-limits.md).

<details>
<summary><strong>On the JVM</strong></summary>

- Before your first Run, Cmd/Ctrl+I says "Nothing to inspect yet: Jonquil
  isn't attached to the server. Run something first."
- If the server dies, the footer reads "Lost the connection to the nREPL
  server." and actions stay disabled until you reopen the panel.
- The server keeps one inspector view per connection, so another tool on the
  same server can replace yours.

![The inspector panel against a real JVM Clojure server, after Run and a REPL entry](../images/clj-inspector.png)

</details>

### Live results

Every result and error line the console prints keeps hold of its value, so
an older one stays inspectable after later entries. Click a result line and
the inspector opens on that value a moment later, once a double-click would
have come (on macOS that's the system's double-click speed); a double-click selects a word instead, and a drag that selects
text doesn't count as a click either. Right-click a line (which puts the caret there) or put the caret in
it, and the console's menu and the palette act on that result:

- **Inspect This Result** opens it in the inspector.
- **Copy This Result** copies the printed value, without the `=> `.
- **Define a Var for This Result** puts
  `(def  (jonquil.results-rt/value! 7 "…"))` at the prompt with the caret where
  the name goes, so the live value gets a name; Enter defines it. The second
  argument names the runtime that kept it.
- **Jump to This Var**, for a var such as `#'demo.core/area`, opens its
  definition as F12 does.
- **Quick Doc for This Var**, for a var, prints the doc quick doc would
  show into the console, as `;` comment lines under the result, so it
  stays there to scroll back to.

The kinds are a value, a var, a namespace and an exception. Each verb asks a
table of what each kind offers before it acts, so Jump to This Var or Quick
Doc for This Var on a number says the result isn't a var. The last 100 results are kept; an older
one, or one kept by a runtime that has since restarted, says it's gone, and
Define's form throws "That result is gone." for it rather than defining a
placeholder. Live results come from Jolt, in process or in a
project's Jolt child, and quiet evaluations (completion, help) keep nothing;
other runtimes print results as plain text.

A file and line in console output, such as `src/app/core.clj:12:3` in an
error's location or `(core.clj:12)` in a JVM stack frame, opens there with
a click, with the same wait for a double-click, or with **Open This File and
Line** from the console's menu or the palette, the caret in it. This works
for any runtime, since it reads the text. A path under the project folder,
or an absolute one, opens as written; a bare name opens the project file
that ends with it, and when several do, the namespace in a stack frame
(`demo.core$f.invoke` means `demo/core.clj`) picks one. Otherwise the status
bar says how many files could be meant and opens none. A project file opens
in a tab and any other file in the source panel, as a jump to a definition
does.

## Tracing a function

**Trace the function at the caret**, in the palette, wraps that function so
every call to it is recorded; run it again on the same name to stop.
**Show the calls of the traced functions** opens the Trace panel on the
right, one row per call, oldest first and indented by how deep it was
called from another traced function:

```
(user/tall 2) => [[0 0] [1 1]]
  (user/tpair 0) => [0 0]
  (user/tpair 1) => [1 1]
```

![The Trace panel after tracing sum-squares and square: the outer call with each square call indented under it and its value](../images/trace.png)

A call that threw reads `threw` and the error, and one still running reads
`(running)`. Enter on a row opens what that call returned in the inspector,
or its arguments when it threw. The panel refreshes after every evaluation
while it's open. Values print as the Frames panel's locals do, and a lazy
seq nothing has realized is never realized for the row, nor kept, so a
traced function can still hand out a seq too big to hold. Calls made while
a lazy result is realized happen after the call that returned it has
finished, so they show at the top level, not under it. The last 500 calls
are kept; **Clear the recorded calls** forgets them, and **Stop tracing every
function** unwraps everything.

Calls from functions compiled before the trace still go through it. Jolt
doesn't record a traced function's call to itself, only the outer call, and
redefining a traced function (Run again) replaces the wrapper, so the new
definition isn't traced. Tracing runs where your code runs, in process or in
a project's Jolt child, and doesn't touch `*1`; other runtimes say it needs
Jolt for now.

## The Processes panel

**Show the processes**, in the palette, or Cmd/Ctrl+Alt+8 opens the
Processes panel: the fibers your program spawned with `jolt.fibers/spawn`,
newest first, each with its number, its state as Jolt reports it
(`running`, `ready`, `parked`, `done` or `dead`), the function it runs and
how long ago it started. **Interrupt** raises an error in the selected fiber
("Interrupted from Jonquil's Processes panel"), which a `masked` region
holds off until it ends, and **Kill** ends it outright. **Forget finished**
drops the done and dead ones; Jonquil keeps the last 50 of those anyway.
While the panel shows, it refreshes every second.

![The Processes panel listing three fibers a Run spawned: two workers, one running and one ready, and one fiber that is done, with Interrupt, Kill, Forget finished and Close](../images/processes.png)

Jolt keeps no list of live fibers (jolt-lang/jolt#1606 asks for one), so
the first time the panel opens Jonquil starts watching `spawn`, the way
Trace wraps a function, and code compiled earlier goes through it too.
Fibers spawned before that aren't listed. Neither are the fibers core.async
starts itself, for `io-thread`, for `go` blocks on its fiber backend and for
flow processes, because they don't go through `spawn`. Threads (`future`,
agents) aren't fibers, so they're not listed either. It works in
process and in a project's Jolt child; other runtimes say it needs Jolt.

## Counting how often each line runs

**Count how often each line runs**, in the palette, turns on Jolt's line
counters for code compiled from then on. Run a saved file and the gutter
shows, beside each line that ran, how many times it did: `6`, `999`,
`1.2k`, `12k`. Each Run counts its own lines alone. The counts are for the
text that ran, so the first edit hides them until the next Run, and a Run
Form or Run Selection refreshes them only while the file still holds the
text its last Run did. An Untitled or unsaved tab has no file for Jolt to
count by, and the console says so. **Clear line counts** zeroes the
counters, and the toggle again turns counting off.

![spin.clj after a Run with line counts on: the gutter shows how many times each line ran, such as 6 beside the loop's test](../images/line-counts.png)

Jolt has one instrumentation mode at a time, so counting and Keep every
local (debug mode) take turns: turning one on turns the other off, and the
status bar says so. A Jolt child that restarts comes back without
counting, and the next Run says so on the console. Counting works in
process and in a project's Jolt child, on a Jolt with `jolt.profile`
(v0.8.24 or later); other runtimes say it needs Jolt.

## The last error's frames

After an error in Jolt, **Show the last error's frames and locals**, in the
palette, opens the Frames panel on the right. Its title is the error, such as
`ArithmeticException: Divide by zero`. Below it is one row per frame of the
call stack, innermost first, and under each frame its locals:

```
user/dbg-inner  line 2 (no file)
    n = 3
    xs = [1 2 3]
```

Frames stop where Jonquil's own evaluation code starts, so the list shows your
code. Return or a click on a frame opens its file at that line, in a tab
for a file of your project and in the source panel for anything else. Code
typed at the prompt or run from an unsaved buffer has no file, so its frame
shows the line within that text and says so instead. A Run of a saved file
keeps the file on a Jolt whose `eval-string` takes a source path (Jolt
v0.8.24 or later, jolt-lang/jolt#1476). Return or a click on a local opens its value in
the inspector, where it lives, so even a huge or lazy value is never copied out.
A local's row prints at most 8 items, 3 levels deep, and 100 characters. A lazy seq nothing has realized yet reads `<lazy seq, not realized>`, because printing it would run your code again (a `(map save! rows)` would save the rows again); opening it in the inspector realizes it.

### Evaluating in a frame

The field under the list evaluates a form in the selected row's frame (a
frame row, or one of its locals). The form sees that frame's locals by name
and resolves other names in the frame's namespace, so a private function of
that namespace works too, and Enter shows it in the console:

```
; in user/dbg-inner: (count xs)
=> 3
```

The console shows the code as soon as you press Enter, then what the forms
printed (the first 10000 characters) and the last one's value, at most 100
items and 10 levels deep. Several forms run in order. A form that throws
shows one line with the error, and Stop stops one that runs too long, saying
"Stopped.". Nothing resumes or unwinds: you are looking at a frame that has
already stopped, so the error stays the last error, and `*1` stays where it
was. The forms' own side effects still happen. At run time, `*ns*` is the
prompt's, not the frame's. In the Jolt child the window waits the usual 10
seconds for the answer; press Stop for a form that's still running there.
After a newer error, a panel still showing the older one refuses to inspect or
evaluate: "The panel shows an older error: show the frames again."

### Break on error

**Break on error**, in the palette, makes a Jolt evaluation stop where it
throws, before anything unwinds (run it again to turn it off; it starts off).
The console says `; Paused at ArithmeticException: Divide by zero.`, the
evaluation is still running, and the Frames panel opens on the live stack
with its title starting `Paused:` and a **Continue** button in its bar.

While it's paused, the field evaluates on the thread that threw. A form
sees the frame's locals, as after an error, and also everything that was
still in effect there: a `binding` the code was inside still holds, and no
`finally` or `with-open` has run yet, so a file the code had open is still
open. (A `try` whose `catch` didn't match the error has already unwound by
then, its `finally` included: Jolt rethrows from it.) **Continue** (or **Continue from the break** in the palette) lets the
error unwind as it would have, and the panel then shows its frames and the
restarts. Stop ends the evaluation instead, and Stop on an evaluation
blocked in a sleep or a wait stops it without pausing. Nothing can resume the code or
change a local.

Only errors that escape the evaluation pause: one your own code catches
doesn't, and neither do Jonquil's quiet evaluations (completion, help,
timing). A top-level expression typed at the prompt keeps no frame of its
own, so its locals show only inside a function it called; when no frame is
your code's, the panel says so instead of listing Jonquil's own. Break on error
works for Jolt, in process and in a project's Jolt child, where it needs the
child's Jolt to have `jolt.debug`, the jolt-lang/nrepl middleware Jonquil
starts it with, and `jolt.nrepl/*eval-wrapper*`, the hook Jolt's nREPL
server calls around each evaluation (Jolt v0.8.24 or later,
jolt-lang/jolt#1474). Turning it on says so when one of them is missing, and the other
runtimes say so too.

### Breakpoints

**Toggle Breakpoint** (F9, or the palette) puts a red dot in the gutter on
the caret's line, and pressing it there again takes the dot away. When code
reaches that line it pauses there, with nothing wrong: the Frames panel
opens titled `Breakpoint at line 3`, the console says
`; Paused at the breakpoint on line 3.`, and everything Break on error offers
works the same way. The field evaluates with the frame's locals, a local
opens in the inspector, **Continue** (the panel's button, or **Continue from
the break** in the palette) runs on, and Stop ends the evaluation. Break on
error doesn't need to be on.

![A red breakpoint dot in the gutter on line 3 of price.clj and the Frames panel titled Breakpoint at line 3, listing the locals price, qty and subtotal](../images/breakpoint.png)

A breakpoint goes before the code that starts on its line, so it needs
something there that runs:

- a body form of `defn`, `fn`, `let`, `loop`, `when`, `do`, `doseq`,
  `try` and the like: the breakpoint runs as a statement of its own before
  it;
- any other expression whose value is used, such as a call's argument, a
  `let` init or an `if` branch: it is wrapped as `(do <breakpoint> expr)`,
  which keeps its value.

A line with nothing that can pause (a parameter vector, a binding name, a
`catch` clause, a `case` test, quoted code, a tagged literal, the `ns` form,
a blank or comment line, anything inside a macro's arguments) moves the dot
to the next line that can hold one, and the status bar says
`Breakpoint moved to line N`. The dot is a text mark, so it moves with the
line when you edit above it.

Before it places a breakpoint, Toggle Breakpoint asks the runtime what each
form's head names in the buffer's namespace. Code inside a macro call is
left alone, since the macro may read it as data (`s/def`, `go-loop`, a
`defmacro` earlier in the same file), except for the core forms Jonquil
knows the shape of (`let`, `when`, `cond`, `try` and the like). A head that
names nothing loaded stays a call, unless the buffer's namespace isn't loaded
yet (before its first Run), since a name it refers could be a macro: then
every head Jonquil doesn't know counts as one, and the status bar says so, as
it does when the runtime can't answer at all. The answer is taken when you
toggle and again after each Run that ends well, and Runs in between reuse it:
until the next answer, a head typed since and every head after a change to
the `ns` form count as macros.
When a Run leaves a dot out because its line is now inside a macro (a name
typed since, or one the Run turned out to define), the console says so, and
after the Run the status bar names the dots that won't pause.

**It works at once.** Toggling sends the top-level form holding the line
again, the way Run Form does (the console shows it), with every breakpoint
it now holds, or without any once the last is gone. So after toggling a
breakpoint in a `defn`, an evaluation that calls that function pauses there,
whether you typed it at the console or it runs code in another namespace.
Only a form that just defines code is sent this way: `defn`, `defmacro`,
`defmethod`, `extend-type`, `extend-protocol`, and a `def` or `defonce` whose
value is literally `(fn ...)`, `#(...)` or `(reify ...)`. Toggling never runs
anything else, so on a form like `(def db (connect!))`, a top-level call, a
`deftype`, `defrecord`, `defprotocol` or `defmulti` (sending those again makes
a new class or dispatch table) or a form inside a `(comment ...)` block the
status bar says `it takes effect when you next run this form.` A form with
edits that haven't run isn't sent either, so the toggle never runs your
unfinished edit: after a Run that's what the changed-form mark shows, and in
a tab that never ran it's unsaved edits, or a form that differs from the
file on disk. The status bar then says
`this form has edits that haven't run, so Run Form to apply both.` If
sending the form again fails, the status bar says so and the dot stays.

Run, Run Form and Run Selection send the code with the breakpoints in their
text. Each inserted form is one line, so line numbers in errors and frames
stay right. `require :reload` or `load-file` of the file loads it as saved,
without breakpoints; toggling a breakpoint again puts them back. **Clear all
breakpoints** takes every dot in the tab away and sends the forms that held
them again without. A tab's breakpoints aren't saved across sessions.

Toggling turns Jolt's debug mode on when it's off, since a paused frame's
locals need it, and the status bar says so. One pause at a time: while one
is paused, another thread that reaches a breakpoint runs on, and so does an
error with Break on error on. Breakpoints work for Jolt, in process and in a
project's Jolt child, on a Jolt with `jolt.debug`; other dialects, and a
child whose Jolt lacks it, say why. A breakpoint pauses only during an
evaluation you started (a Run or a console entry):

- In process, only on that evaluation's own thread. A future, a quiet
  evaluation (completion, a plugin's) and the window's own thread run on,
  since pausing the window's thread would freeze it.
- In the Jolt child, on any of its threads while it runs, since that is how
  the pause reaches the window. A thread still paused when the evaluation
  ends is let go, because the window can't reach it after that.

Toggling before the Jolt child is running (or right after a switch to it)
keeps the dot: the first evaluation sent to the child arms the breakpoints.
If that child can't (its Jolt has no `jolt.debug`), every dot goes and the
status bar says why.

### Stepping

Paused at a breakpoint, three steps run on and pause again. Each has a key,
a palette entry and a button beside Continue in the Frames panel:

| Step | Key | Palette | Pauses at |
|---|---|---|---|
| **Step Into** | F7 | Step to the next line | the next line reached, inside a call too |
| **Step Over** | F10 | Step over the next line | the next line in this function or a caller, running a call on this line through |
| **Step Out** | Shift+F7 | Step out of this function | the caller's next line, once this function has returned |

The keys follow VS Code's F10 and F11 with F7 in F11's place, since macOS
keeps F11 for Show Desktop. The panel's title reads
`Stepped to line 3` for all three, and the console says
`; Stepped to line 3.`. A green
arrow in the gutter marks the paused line, in the tab the code came from,
and goes away on Continue, Step or Stop. When the tab was edited since its
breakpoints were sent, the line may have moved, so there's no arrow and the
status bar says so. The keys step only while a breakpoint's pause shows,
and pass through otherwise; at a Break on error pause Step does nothing and
says that it works at a breakpoint.

Stepping covers the forms that hold a breakpoint. When a top-level form (or
a form inside a `(comment ...)` block) holds a dot, a Run or a toggle sends
it with a step point on every other line that can hold a breakpoint, by the
same placement rules. A step point pauses only while stepping, which lasts
from a Step to the next pause. So:

- **Step Into** goes to the next line the run reaches in that form, or
  after a return to the caller's next line when the caller holds a
  breakpoint too. A call into a form with no breakpoint runs through
  without pausing, unless its tab is instrumented (below).
- **Step Over** and **Step Out** compare how deep the code is: Over pauses
  at the same depth or above, so a call on the line runs through, and Out
  pauses only above, once the function returned. Out from the outermost
  instrumented function runs to the next dot or the end, as Continue. A dot
  pauses whichever step is running. Step Over at the end of a function
  that a library loop calls (`mapv`, `reduce`, `run!`) pauses in that
  function's next call, since the loop itself has no frame to return to.
  Chrome's debugger does the same. Use Continue, or Step Out to leave the
  loop's function. An inline
  `fn` or `#()`, and a `for`, `lazy-seq`, `delay` or `with-redefs` body
  inside a defn, run as calls of their own, so Step Over steps over them,
  as JVM debuggers do.
- **Continue** turns stepping off and runs to the next breakpoint.
- **Stop** ends the run, as at any pause, and stepping with it.

A form with dots and nobody stepping pauses only at the dots, and its step
points cost next to nothing: each checks one flag and goes on, without
throwing. A Step belongs to the thread that was paused: another thread
reaching a step point meanwhile runs on, so the step lands on the line the
paused code reaches next. Break on error never turns stepping on.

To step into functions that hold no dot, run **Instrument this tab for
stepping** from the palette. It sends every top-level form in the tab that
only defines code (a `defn`, a `defmethod`, a `def` of a `fn`) again with
step points, so Step Into goes into any of them. Every later Run, Run Form
or Run Selection of that tab keeps the step points in. A form with edits
that haven't run isn't sent, and the status line counts it. Forms inside a
`(comment ...)` block and forms that do more than define code are never
sent. **Stop instrumenting this tab** sends the same forms again without
step points (a dot keeps its own). Clear All Breakpoints and closing the
tab end instrumenting too. Nothing is sent until you ask, and a function
value made before the command (one a `def` already holds) keeps no step
points.

### Restarts

After an evaluation fails, three restarts take it from there, in every
dialect, from the palette or from the bar of buttons at the top of the Frames
panel:

- **Retry the last failed evaluation** runs it again the same way: a REPL
  entry, Run Form or Run Selection sends the same code again, and a Run runs
  its tab again (its current text, as Run does).
- **Edit the last failed evaluation at the prompt** puts its code at the
  console prompt to change before Enter. For a Run it says to edit the file.
- **Define the name the last error couldn't resolve, then retry** is there
  when the error was `Unable to resolve symbol: foo`. It puts `(def foo )`
  and the code at the prompt, with the caret where the value goes; type the
  value and Enter defines the name and runs the code again, as one entry.

Code you edit or define at the prompt runs in the prompt's namespace, like
any REPL entry, which can differ from the buffer namespace a failed Run Form
used. A success clears the failure, so a restart always belongs to the error
you just saw. Nothing resumes the failed evaluation itself: by the time the
console shows the error, the stack has unwound, so there is nothing to abort
either.

### Keeping every local

Jolt keeps a frame's locals only when the code was compiled with them.
**Keep every local for the debugger** turns Jolt's debug mode on (run it again
to turn it off). Code you load or run afterwards keeps all its locals, so Run
again, or reload a namespace, after turning it on. Debug mode keeps tail calls
only for `recur`, so very deep recursion that isn't `recur` can run out of
stack while it's on: Jolt then runs out of memory, keeps no frames, and the
panel says so. Turn debug mode off and run it again. Without debug mode, a
frame shows the locals Jolt happened to keep, and code typed at the prompt
shows none.

The panel reads the frames after the error was caught, from the evaluator that
ran the code, in process or in the Jolt child. It needs a Jolt with
`jolt.debug` (jolt-lang/jolt#1465); on an older Jolt the panel says
"Frames need a Jolt with jolt.debug (newer than this one)." and debug mode
stays off.

In a JVM Clojure window the panel lists the last error's frames too, read from
its stack trace on your server: each Clojure fn once (the REPL's own frames
and Java's left out), and a frame opens its file from the server's classpath,
a jar's included. The JVM keeps no locals, so frames have none, and the field
and debug mode say they need Jolt. Showing the frames leaves `*1`, `*2`, `*3`
and `*e` as they were. The other dialects say "The debugger needs Jolt." for
now.
