Merge: the command is flan, and it knows what buffer you are in

This commit is contained in:
Joseph Ferano 2026-09-19 02:20:29 +07:00
commit 5c228d35dc
23 changed files with 827 additions and 759 deletions

View File

@ -1,5 +1,5 @@
* Stuff I've found
** Why do I need to call flan-dev to open another window?
** Why do I need to call flan to open another window?
let: flan: the program exited; restart flan dev
** I can't eval a toplevel defvar, need to eval-defun (C-c C-c)
For Flans intended live-program workflow, C-x C-e on any complete top-level form should do the natural thing:
@ -40,7 +40,7 @@ Globals are NOT reset between runs. That is the CL/Clojure semantics asked for:
the process never died, so a second (main) sees what the first one left.
** 2. C-x C-e on a top-level form — QUEUED behind 1
Same file as 1 (emacs/flan-dev.el), so it waits rather than merging by hand.
Same file as 1 (emacs/flan.el), so it waits rather than merging by hand.
No design questions; the note specifies it.
** 3. Runtime-loaded owning globals — DISPATCHED

12
NEXT.md
View File

@ -801,9 +801,9 @@ tip themselves. A lane that did not notice would produce plausible work against
### ~~From `HANDOFF-f2.md``pause` marking, the Emacs half~~
**Built.** All seven items landed: the `eval_expr` pause path, `flan-dev--eval` sending the position,
**Built.** All seven items landed: the `eval_expr` pause path, `flan--eval` sending the position,
`flan-eval-defun` taking `C-u`, the overlay and its face, `test/programs/dev-pause.flan`, the `test_dev.ml` block and
the `emacs/test-flan-dev.el` one. `HANDOFF-f2.md` is deleted; the reasoning is in `docs/BUILT.md`, "A breakpoint is a
the `emacs/test-flan.el` one. `HANDOFF-f2.md` is deleted; the reasoning is in `docs/BUILT.md`, "A breakpoint is a
function call, and the editor only says where".
### ~~From `HANDOFF-f1.md`~~ — the socket flake is fixed, and everything that followed it is done
@ -884,7 +884,7 @@ putting the old `?` back in `flan-watch-ghost-call-regexp` and watching exactly
**Closed.** The dead `n=0` branch in `watch_render_num` is deleted and the reader stays epoch-blind on purpose: a
stopped program does not sample, so an epoch-aware reader would blank the watch for the whole of a break loop, which
is exactly when the numbers are being read. What was actually wrong was in the editor — `flan-watch--tick` sent
`:reset t` five times a second at a program that could not answer it — and it is now guarded on `flan-dev--stopped`,
`:reset t` five times a second at a program that could not answer it — and it is now guarded on `flan--stopped`,
with the read still going out every tick. `emacs/test-flan-watch.el` pins both halves. `HANDOFF-f3.md` is deleted;
the reasoning is in `docs/BUILT.md`, "A hot loop keeps five numbers, and the window is the editor's".
@ -1440,7 +1440,7 @@ reader ✅ → parse ✅ → load ✅ → check ✅ → emit ✅ → clang ✅
| `lib/shim.ml` | **`declare-c` -> the generated C that flattens a struct crossing** |
| `vendor/raylib/` | **the raylib package: `raylib.flan` and `link`, and no C at all** |
| `vendor/agent/` | **the dev agent: one verb table, a loader thread, install at a frame boundary** |
| `emacs/` | **`flan-mode.el`, `flan-dev.el`, `flan-repl.el`: the editor half of the dev loop** |
| `emacs/` | **`flan-mode.el`, `flan.el`, `flan-repl.el`: the editor half of the dev loop** |
| `bin/main.ml` | `flan read \| parse \| check \| emit \| shim \| build \| run \| reload \| dev` |
| `test/test_flan.ml` | reader, parser and checker |
| `test/test_acceptance.ml` | expression/result pairs + whole programs + the traps |
@ -1564,7 +1564,7 @@ way, and such a frame is now refused by name. Kept separate from the slot finger
reads a frame whose locals are fine and whose global attribution is not.
**The break buffer opens by itself when the program stops.** Today a condition stops the program and the buffer appears
only when `C-c C-b` is typed. `flan-dev--absorb` already inspects every reply for `:stopped` and a poll covers the case
only when `C-c C-b` is typed. `flan--absorb` already inspects every reply for `:stopped` and a poll covers the case
where no reply is pending, so the client already knows the moment it happens and already moves the mode line from it —
this is a hook at a point that exists, not new plumbing.
@ -1842,7 +1842,7 @@ wrote Flan in the old spelling and their files want the same pass at merge:
```
python3 tools/unit-return.py .
python3 tools/unit-return.py --in-strings test/test_flan.ml test/test_acceptance.ml \
test/test_session.ml emacs/test-flan-dev.el emacs/test-flan-mode.el
test/test_session.ml emacs/test-flan.el emacs/test-flan-mode.el
python3 tools/unit-return.py --raw-ml lib/prelude.ml
python3 tools/unit-return.py --in-html web/index.html
```

View File

@ -89,7 +89,7 @@ To set up the mode:
(require 'flan-mode)
```
Then use `M-x flan-dev` to start and attach, or `C-c C-z` to attach to a
Then use `M-x flan` to start and attach, or `C-c C-z` to attach to a
session started in a terminal. The editor workflow is documented in
[emacs/MANUAL.md](emacs/MANUAL.md).

View File

@ -1367,7 +1367,7 @@ the merged entry point uses to flush and park.
The park used to be `for (;;) pause()`, and that was a dead end with the process still standing: you ran a program, it
opened a window, you closed the window, `main` returned, and the only way to get another window was
`flan-dev-restart-program` — a new build, a new session, every global gone. Common Lisp and Clojure do not have that
`flan-restart-program` — a new build, a new session, every global gone. Common Lisp and Clojure do not have that
problem because the image outlives `main` and you call it again. The process here already outlived `main`; nothing
could wake it.
@ -1530,7 +1530,7 @@ s-expressions, so sexp motion, paren matching, `beginning-of-defun` and indentat
Flan's own bracket syntax (`[` and `{` are brackets, not symbol characters — every binding list and every type is
written with them), the characters a Flan name may contain (`-`, `?`, `/`, `.`), and its keywords.
`emacs/flan-dev.el` is the client. There is no parser in it, which is the point of the protocol choice: `prin1` writes a
`emacs/flan.el` is the client. There is no parser in it, which is the point of the protocol choice: `prin1` writes a
request and `read` reads a reply.
| | |
@ -1843,7 +1843,7 @@ errors — and that is a reply the client is already reading. Finding out a seco
out *after* the echo area had said the evaluation was fine.
- **And a timer asks anyway**, once a second, with `describe` — the cheap op, which is also how the output pipe is
drained. A program that stops in a frame of its own game loop produces no reply at all, and folding state into replies
that never come says nothing. The timer never *reconnects*: `flan-dev--request` reopens a socket a restarted daemon left
that never come says nothing. The timer never *reconnects*: `flan--request` reopens a socket a restarted daemon left
behind, which is right for something a person did and wrong for a background poll, because it would quietly erase the
`lost` state that exists to be seen. It also skips while another request is in flight — `accept-process-output` runs
timers, so a poll firing inside a read would eat the reply that read was waiting for.
@ -4358,7 +4358,7 @@ old spelling and their files want the same pass at merge:
```
python3 tools/unit-return.py .
python3 tools/unit-return.py --in-strings test/test_flan.ml test/test_acceptance.ml \
test/test_session.ml emacs/test-flan-dev.el emacs/test-flan-mode.el
test/test_session.ml emacs/test-flan.el emacs/test-flan-mode.el
python3 tools/unit-return.py --raw-ml lib/prelude.ml
python3 tools/unit-return.py --in-html web/index.html
```
@ -5143,7 +5143,7 @@ which is the honest answer to a question asked of a table that is never still.
**"At exit" is not a hook, and the honest reason is that a game is killed.** A program stopped by a signal runs no
`atexit` handler, no destructor, nothing — so no code written inside the program could report anything about the run
that matters most. The authoritative reader is therefore `(:op "leaks")`, which reads the same table over the agent
socket and can be asked at any moment, including the one before the kill. `flan-dev.el` does not ask on teardown
socket and can be asked at any moment, including the one before the kill. `flan.el` does not ask on teardown
either: that would put a request on a path that runs every time the editor closes, for an answer nobody asked for.
The hook exists for the other program — the one that returns from `main` — and it is two decisions:
@ -5201,7 +5201,7 @@ name in the table came from one of these call sites by construction — and the
**One reply, two pictures.** Ghost text does not poll. It is painted from `flan-watch--absorb`, the same function
that paints the buffer, from the same reply, so the two cannot disagree and there is no second `:op "watch"` in
flight — the one-request invariant `flan-dev-settle-hook` exists to keep. What had to change is that the **watch
flight — the one-request invariant `flan-settle-hook` exists to keep. What had to change is that the **watch
buffer used to be the subscription**: killing it cancelled the timer and disarmed the table. That was right while it
was the only consumer and wrong the moment it was not, so arming and the timer now hang off
`flan-watch--consumers`, and only the last consumer out turns the lights off.
@ -5309,7 +5309,7 @@ sample. It is the wrong trade, because **a stopped program does not sample**. An
watch for as long as the program sat in a break loop, and reading the numbers from the moment you stopped is the
entire point of stopping. The lazy clear gives exactly the right answer there. What was actually wrong was narrower
and lives in the editor: `flan-watch--tick` was sending `:reset t` five times a second at a program that could not
answer it. So the reset is now guarded on `flan-dev--stopped` — the read still goes out every tick, only the reset
answer it. So the reset is now guarded on `flan--stopped` — the read still goes out every tick, only the reset
field drops — and the runtime is untouched. That keeps the policy where the rest of this section already put it:
"since you last looked" is the editor's idea, not the table's. `watch_render_num`'s unreachable `n=0` arm is deleted
rather than commented, since the only way to reach it is the epoch check that was just rejected, and dead code is an
@ -5370,7 +5370,7 @@ the session would have been a second source of truth that drifts the first time
touching it, and clearing would have had to be written rather than falling out.
**`C-u C-x C-e` is a flag, not a position.** `flan-eval-last-sexp` sends a raw `buffer-substring` with no line
padding — unlike `flan-dev--text`, which pads a snippet back onto its own line — so buffer coordinates do not survive
padding — unlike `flan--text`, which pads a snippet back onto its own line — so buffer coordinates do not survive
that path. They are also not needed: the expression sent *is* the target, so `:pause t` says everything there is to
say, and `Session.eval_expr` wraps the parsed expression before `Check.expression`. It does not stick and cannot: a
thunk is built and thrown away, so there is no declaration for the mark to live in.
@ -5392,16 +5392,16 @@ reports because a nested break overwrites `condition_name` and restores it on th
`status` verb). A program already parked on a `Pause` is the one case this cannot tell apart, and nothing could: both
answers are "stopped at a pause".
**The editor's column is a byte offset.** `flan-dev--wire-position` is the inverse of `flan-dev--position` and has to
**The editor's column is a byte offset.** `flan--wire-position` is the inverse of `flan--position` and has to
count bytes for the same reason: the reader walks the source a byte at a time, so `current-column` would be short by
one per extra byte in every non-ASCII character earlier on the line and the daemon would find nothing where it was
pointed. The *line* is the buffer's own, which works because `flan-dev--text` pads the snippet with leading newlines.
pointed. The *line* is the buffer's own, which works because `flan--text` pads the snippet with leading newlines.
**One key, three targets.** `flan-eval-defun` takes `C-u` for the innermost form point is inside — `backward-up-list`,
falling back to the defun when point is not nested — and `C-u C-u` for the top-level form itself. With `C-u C-x C-e`
that is all three of §9's targets and no new binding; `flan-mode.el` did not change.
**The overlay is an annotation, not feedback.** `flan-dev-pause-face` is drawn over the marked form and deliberately
**The overlay is an annotation, not feedback.** `flan-pause-face` is drawn over the marked form and deliberately
does *not* copy the error overlays' lifetime. An error overlay is about the command that just failed and the next
keystroke takes it down; a pause mark is about the running program, and it has to survive `pre-command-hook` or the
buffer stops showing a breakpoint that is still there. What clears it is what clears the mark itself: an accepted
@ -5722,11 +5722,11 @@ The region is `C-x C-e`'s, plus one case it has no need of: the form *at* point
delimiter. A macro call is a form you put point on, and `backward-sexp` from an open paren takes the previous
sibling, which is never what was meant.
The text is padded onto its own line **and its own column**, which `flan-dev--text` does not do and says why it
The text is padded onto its own line **and its own column**, which `flan--text` does not do and says why it
does not: a top-level form starts at column 1, so the columns already agreed. A macro call does not — it is
written well inside a `defn` — and the refusal this path can get carries a column measured from the start of the
snippet, so an unpadded send would draw "did not settle" at the start of the line. Leading newlines and leading
spaces are both whitespace the reader skips. `test-flan-dev.el` checks the arithmetic the only way that proves it:
spaces are both whitespace the reader skips. `test-flan.el` checks the arithmetic the only way that proves it:
it evaluates a spinning `defmacro` into a live session, asks for the fixpoint of a call to it from a known buffer
position, and requires the error overlay to start at exactly that position.

View File

@ -316,9 +316,9 @@ cursor at `(+ 1| 1)` the program stops at that `(+ ...)`.
`continue` resumes at the call.
- **`flan-eval-defun`** takes the top-level form's bounds and sends the text; **`flan-eval-last-sexp`** sends the sexp
before point. Both hand a *string* to the daemon, which is the hook — the buffer never has to be modified.
- **`flan-dev--enclosing-head`** (`flan-dev.el:1009`) already walks out to the enclosing form to find its head, for
- **`flan--enclosing-head`** (`flan.el:1009`) already walks out to the enclosing form to find its head, for
eldoc. The third target's hard part is already written.
- `current-prefix-arg` is already read in one place (`flan-dev.el:1429`), so the `C-u` convention has precedent.
- `current-prefix-arg` is already read in one place (`flan.el:1429`), so the `C-u` convention has precedent.
**So the work is a source-to-source rewrite in Emacs before sending:** find the target form's bounds, send the
top-level form with that span replaced by `(do (pause) <span>)`. Nothing on the compiler side changes.

View File

@ -61,7 +61,7 @@ one borrows its memory model from.
|---|---|---|
| `farolero` | 2023-05-05 | Common Lisp conditions and restarts, portably — a smaller read than SBCL's for *semantics*. |
| `lem` | 2025-08-29 | An editor written in CL. Its own REPL and process model. |
| `cider` | 2026-09-10 | The Clojure editor protocol this repo's `flan-dev.el` is shaped after. |
| `cider` | 2026-09-10 | The Clojure editor protocol this repo's `flan.el` is shaped after. |
| `emacs` | 2026-04-18 | Overlays, faces, `pre-command-hook` — the C and Lisp behind the editor half. |
| `fennel-ls` | 2026-05-06 | A Lisp language server. |
| `Carp` | 2026-08-24 | A statically typed Lisp with no GC. **The closest thing to this language's premise.** |

View File

@ -172,7 +172,7 @@ The containers, strings/UTF-8, sequences, random, and printing layers are decent
- **No `-Wall -Wextra` on the runtime's C compile** (`lib/build.ml:827`, `:1069`).
- **Package visibility** — everything in a package is public except `main`
(`lib/load.ml:25-31`); `rl/get-color-raw` is the recorded symptom.
- **Emacs client**: 30s hard deadline with no retry on long builds (`flan-dev.el:155-170`);
- **Emacs client**: 30s hard deadline with no retry on long builds (`flan.el:155-170`);
`accept-process-output` loops can freeze Emacs up to 60s on a hung daemon (`:521`,
`:576-600`); no package headers, so not installable off MELPA or by path alone.
- **No CI** — README states it openly and records two silent-failure incidents. `@checks`

View File

@ -49,10 +49,10 @@ reaches it through `declare-function`, so everything else installs and works
without it.
You also need the `flan` binary on your `PATH`. If it is somewhere else, set
`flan-dev-command`.
`flan-command`.
Two settings worth knowing about before you need them. `flan-dev-start-timeout`
(60s) bounds the wait for a daemon to come up, and `flan-dev-reply-timeout`
Two settings worth knowing about before you need them. `flan-start-timeout`
(60s) bounds the wait for a daemon to come up, and `flan-reply-timeout`
(30s) bounds one request once it has. The second is the one a long first
compile can exhaust: the message says so and names `*flan-dev*`, where the
daemon's own build log is, so you can see whether it is still working before
@ -64,9 +64,15 @@ you raise it.
Two ways in, and they are different.
**`M-x flan-dev`** starts the program for you. It runs `flan dev` on a file,
**`M-x flan`** starts the program for you. It runs `flan dev` on a file,
waits for it to come up, and connects. This is the normal way.
From a buffer already visiting a `.flan` file it starts *that* file and asks
nothing — the buffer has answered the question. From anywhere else it reads the
file from the minibuffer, and `C-u M-x flan` reads it even from a `.flan`
buffer, which is how you start some other program without leaving the one you
are looking at.
**`C-c C-z`** (`flan-connect`) attaches to a program that is *already* running —
one you started in a terminal, say. It looks for a `.flan-dev.sock` file in the
current directory and upward, so from anywhere in the project it finds the one
@ -79,7 +85,7 @@ it found:
flan dev: connected to ~/Development/flan/.flan-dev.sock (47 functions, 2 globals)
```
**`C-c C-q`** disconnects without stopping the program. **`M-x flan-dev-quit`**
**`C-c C-q`** disconnects without stopping the program. **`M-x flan-quit`**
stops the program too — but only one this Emacs started. A daemon you launched
in a terminal is not Emacs' to kill, and it will say so rather than do something
surprising.
@ -716,7 +722,7 @@ next time that buffer's evaluation is accepted — so it disappears when you fix
the thing rather than when you dismiss it.
**"No .flan-dev.sock found above this buffer"** — nothing is running, or you are
outside the project. Start one with `M-x flan-dev`.
outside the project. Start one with `M-x flan`.
**The modeline says nothing about a program** — you are not connected. `C-c C-z`.
@ -757,7 +763,7 @@ Use `C-c C-g` if you need frames.
| `C-c C-x` | rebuild, relaunch, reconnect |
| `M-.` / `M-,` | where a name is written / back |
Commands with no key: `M-x flan-dev` (start a program), `M-x flan-dev-quit`
Commands with no key: `M-x flan` (start a program), `M-x flan-quit`
(stop it), `M-x flan-watch` (the watch buffer), `M-x flan-watch-stop`,
`M-x flan-watch-ghost-mode` (the same values inline),
`M-x flan-inspect-address` (what is at an address), `M-x flan-macroexpand-all`
@ -770,14 +776,14 @@ Commands with no key: `M-x flan-dev` (start a program), `M-x flan-dev-quit`
| Variable | Default | What it is |
|---|---|---|
| `flan-dev-command` | `"flan"` | the compiler binary |
| `flan-dev-socket-name` | `".flan-dev.sock"` | what `C-c C-z` searches for |
| `flan-dev-echo-result` | `t` | print `C-x C-e`'s value in the echo area |
| `flan-dev-names-shown` | `4` | how many names to list before summarising |
| `flan-dev-output-buffer` | `"*flan-output*"` | where the program's output goes |
| `flan-dev-poll-interval` | `1.0` | seconds between checks for whether it stopped |
| `flan-dev-daemon-buffer` | `"*flan-dev*"` | the daemon's own log |
| `flan-dev-start-timeout` | `60` | seconds to wait for a program to come up |
| `flan-command` | `"flan"` | the compiler binary |
| `flan-socket-name` | `".flan-dev.sock"` | what `C-c C-z` searches for |
| `flan-echo-result` | `t` | print `C-x C-e`'s value in the echo area |
| `flan-names-shown` | `4` | how many names to list before summarising |
| `flan-output-buffer` | `"*flan-output*"` | where the program's output goes |
| `flan-poll-interval` | `1.0` | seconds between checks for whether it stopped |
| `flan-daemon-buffer` | `"*flan-dev*"` | the daemon's own log |
| `flan-start-timeout` | `60` | seconds to wait for a program to come up |
| `flan-lower-buffer` | `"*flan-lowering*"` | where `C-c C-l` writes |
| `flan-lower-program` | `"flan"` | the compiler `C-c C-l` shells out to |
| `flan-lower-flags` | `nil` | flags for `flan emit``("--dev")` for the dev lowerings |
@ -786,6 +792,12 @@ Commands with no key: `M-x flan-dev` (start a program), `M-x flan-dev-quit`
| `flan-watch-interval` | `0.2` | seconds between repaints — not the watch rate |
| `flan-watch-ghost-call-regexp` | `"watch\(?:-[[:alnum:]]+\)?"` | the head of a call ghost text anchors on |
Every one of these used to be spelled `flan-dev-…`, and so did the commands:
`M-x flan-dev` is now `M-x flan`, `flan-dev-quit` is `flan-quit`, and so on
through the file. Nothing answers to the old names — there are no aliases —
so a `setq` or a keybinding in your config that names one will break, and the
fix is to delete `-dev` from it.
---
## The files
@ -793,7 +805,7 @@ Commands with no key: `M-x flan-dev` (start a program), `M-x flan-dev-quit`
| File | What it is |
|---|---|
| `flan-mode.el` | the major mode: syntax, indentation, imenu, the keymap |
| `flan-dev.el` | the client — the socket, evaluation, xref, eldoc, completion |
| `flan.el` | the client — the socket, evaluation, xref, eldoc, completion |
| `flan-repl.el` | the `*flan-repl*` buffer |
| `flan-watch.el` | watched values: the program pushes, this paints them in a buffer and inline |
| `flan-cnr.el` | the conditions-and-restarts buffer |

View File

@ -64,7 +64,7 @@
(require 'seq)
(require 'subr-x)
(declare-function flan-dev--request "flan-dev" (form))
(declare-function flan--request "flan" (form))
(declare-function flan-inspect "flan-inspect" (expr))
(declare-function flan-inspect-slot "flan-inspect" (frame slot name))
@ -77,10 +77,10 @@
"Where the break buffer draws."
:type 'string)
(defvar flan-cnr-request-function #'flan-dev--request
(defvar flan-cnr-request-function #'flan--request
"How this buffer reaches the daemon.
One plist in, the reply plist out. A variable so the renderers can be driven
from fixtures, and so `flan-dev.el' is named in one place.")
from fixtures, and so `flan.el' is named in one place.")
;;; What is not available, and why
@ -441,7 +441,7 @@ puts the likely culprit on top."
;; while the program is stopped, and re-asking on every fold would put a
;; round trip behind a keystroke that looks like pure redrawing.
;; Only when there is something to ask. The fixture tests render this
;; buffer from a literal state with no socket behind it and no `flan-dev'
;; buffer from a literal state with no socket behind it and no `flan'
;; loaded, and folding a frame must stay a display operation there — the
;; renderer already draws a reason for a frame whose locals are nil, which
;; is the same degradation every other section of this buffer uses.

View File

@ -19,7 +19,7 @@
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; The other half of the dev loop. flan-dev.el is about a program that keeps
;; The other half of the dev loop. flan.el is about a program that keeps
;; running while you change it; this is about stopping one and reading it.
;;
;; There is no DAP implementation here and there should not be. `flan build
@ -65,7 +65,7 @@
(defcustom flan-dape-command "flan"
"The flan executable used to build a program for debugging.
Its own option rather than flan-dev.el's `flan-dev-command', because this
Its own option rather than flan.el's `flan-command', because this
file is meant to load without that one: editing Flan, debugging Flan and
attaching to a running Flan are three independent things to want."
:type 'string)
@ -182,7 +182,7 @@ common case is one command rather than a config prompt."
;;; --dev and --debug are different builds
;;
;; `flan dev' — what flan-dev.el connects to — builds with --dev: every
;; `flan dev' — what flan.el connects to — builds with --dev: every
;; cross-function call goes through a cell so a redefinition can be installed,
;; and -rdynamic exports those cells. `flan build --debug' is a different
;; axis: -O0, DWARF, and no cells unless --dev is also passed.

View File

@ -99,7 +99,7 @@
(require 'seq)
(require 'subr-x)
(declare-function flan-dev--request "flan-dev" (form))
(declare-function flan--request "flan" (form))
(defgroup flan-inspect nil
"Navigating values in a running Flan program."
@ -110,12 +110,12 @@
"Where the inspector draws."
:type 'string)
(defvar flan-inspect-request-function #'flan-dev--request
(defvar flan-inspect-request-function #'flan--request
"How the inspector reaches the program.
Called with one plist a request and returning the reply plist. It is a
variable rather than a direct call so that a test can hand the renderers a
reply without a daemon behind them, and so that this file names
`flan-dev.el' in exactly one place.")
`flan.el' in exactly one place.")
;;; Reading what the renderer wrote

View File

@ -62,7 +62,7 @@
(require 'subr-x)
(require 'seq)
(require 'outline)
(require 'flan-dev)
(require 'flan)
(defgroup flan-lower nil
"Every lowering of one Flan function, side by side."
@ -616,7 +616,7 @@ was being read open."
(completing-read
"Lowerings of: "
(mapcar #'car (seq-filter (lambda (d) (equal (nth 1 d) "fn"))
flan-dev--defs))
flan--defs))
nil nil nil nil
(and (fboundp 'flan-current-defun-name)
(flan-current-defun-name))))

View File

@ -71,31 +71,31 @@
;; The keymap binds them, but do not load the client merely to edit a file.
;; These must be real autoloads, not just `declare-function`s: otherwise a
;; user who has loaded only flan-mode cannot invoke M-x flan-dev at all.
(autoload 'flan-eval-defun "flan-dev" nil t)
(autoload 'flan-eval-buffer "flan-dev" nil t)
(autoload 'flan-eval-last-sexp "flan-dev" nil t)
(autoload 'flan-connect "flan-dev" nil t)
(autoload 'flan-disconnect "flan-dev" nil t)
(autoload 'flan-describe "flan-dev" nil t)
(autoload 'flan-show-output "flan-dev" nil t)
;; user who has loaded only flan-mode cannot invoke M-x flan at all.
(autoload 'flan-eval-defun "flan" nil t)
(autoload 'flan-eval-buffer "flan" nil t)
(autoload 'flan-eval-last-sexp "flan" nil t)
(autoload 'flan-connect "flan" nil t)
(autoload 'flan-disconnect "flan" nil t)
(autoload 'flan-describe "flan" nil t)
(autoload 'flan-show-output "flan" nil t)
(autoload 'flan-repl "flan-repl" nil t)
(autoload 'flan-break "flan-dev" nil t)
(autoload 'flan-break "flan" nil t)
;; The two CIDER-shaped buffers. They reach the daemon through an indirection
;; of their own so that fixtures can drive them, so autoloading is all the
;; wiring they need.
(autoload 'flan-inspect "flan-inspect" nil t)
(autoload 'flan-cnr-show "flan-cnr" nil t)
(autoload 'flan-doc "flan-dev" nil t)
(autoload 'flan-dev "flan-dev" nil t)
(autoload 'flan-dev-quit "flan-dev" nil t)
(autoload 'flan-dev-restart-program "flan-dev" nil t)
(autoload 'flan-doc "flan" nil t)
(autoload 'flan "flan" nil t)
(autoload 'flan-quit "flan" nil t)
(autoload 'flan-restart-program "flan" nil t)
;; And the cheap counterpart, which needs an autoload for the reason above and
;; more than most: it is the command someone reaches for the moment a window
;; closes, which can be the first thing they ever ask the client to do.
(autoload 'flan-rerun "flan-dev" nil t)
(autoload 'flan-rerun "flan" nil t)
;; Bound below, like the rest, and it was the one missing an autoload.
(autoload 'flan-disassemble "flan-dev" nil t)
(autoload 'flan-disassemble "flan" nil t)
;; The other question about the same function, and the reason it is a second
;; command rather than a fifth argument to the first: `flan-disassemble' asks
;; the running program, and this compiles the file.
@ -103,8 +103,8 @@
;; C-c C-m and the half of it that is findable by name rather than by a
;; modifier. Real autoloads for the reason stated above: a `declare-function'
;; would leave M-x with nothing to load.
(autoload 'flan-macroexpand "flan-dev" nil t)
(autoload 'flan-macroexpand-all "flan-dev" nil t)
(autoload 'flan-macroexpand "flan" nil t)
(autoload 'flan-macroexpand-all "flan" nil t)
(defgroup flan nil
"Editing and evaluating Flan."
@ -196,7 +196,7 @@ line is off screen."
(defvar flan-mode-map
(let ((map (make-sparse-keymap)))
;; Autoloaded from flan-dev.el, so the client loads on first use.
;; Autoloaded from flan.el, so the client loads on first use.
(define-key map (kbd "C-c C-c") #'flan-eval-defun)
(define-key map (kbd "C-c C-k") #'flan-eval-buffer)
(define-key map (kbd "C-x C-e") #'flan-eval-last-sexp)
@ -236,7 +236,7 @@ line is off screen."
;; a full expansion is stamped with the outermost name only.
(define-key map (kbd "C-c C-m") #'flan-macroexpand)
;; The way out when a reload is refused: rebuild, relaunch, reconnect.
(define-key map (kbd "C-c C-x") #'flan-dev-restart-program)
(define-key map (kbd "C-c C-x") #'flan-restart-program)
;; And beside it the cheap one, which is the same question — "run this
;; program" — asked of a process that is already there: `main' again, with
;; the globals as the finished run left them. The pairing is `C-c C-b'

View File

@ -47,7 +47,7 @@
(require 'comint)
(require 'flan-mode)
(require 'flan-dev)
(require 'flan)
(defcustom flan-repl-buffer "*flan-repl*"
"Name of the Flan REPL buffer."
@ -104,7 +104,7 @@ a buffer-local value is not visible.")
;; The prompt gets completion, eldoc and M-. for the same names a buffer
;; does, and against the same program: they read the client's cache, which
;; is program-scoped, which is exactly what a prompt is.
(flan-dev-setup)
(flan-setup)
(setq-local font-lock-defaults '(flan-font-lock-keywords))
(setq-local comint-input-ring-size flan-repl-history-size)
(setq-local comint-input-ring-file-name
@ -250,7 +250,7 @@ should open a line, not send something the reader will reject."
((string-empty-p code) (flan-repl--output ""))
(t
(let ((reply (condition-case err
(flan-dev--request
(flan--request
(list :op "eval-expr" :code code :file "<repl>"))
(error (list :status "error"
:message (error-message-string err))))))
@ -287,7 +287,7 @@ should open a line, not send something the reader will reject."
"Open a prompt on the program `flan dev' is running.
Connects first if it has to."
(interactive)
(unless (and flan-dev--connection (process-live-p flan-dev--connection))
(unless (and flan--connection (process-live-p flan--connection))
(call-interactively #'flan-connect))
(let ((buf (get-buffer-create flan-repl-buffer)))
(with-current-buffer buf

View File

@ -78,7 +78,7 @@
;;; Code:
(require 'flan-dev)
(require 'flan)
(require 'subr-x)
(defgroup flan-watch nil
@ -191,7 +191,7 @@ was the only consumer and wrong the moment it was not.")
;; WHEN IT UPDATES. On the same timer, from the same reply. The two pictures
;; therefore cannot disagree — they are one table read, painted twice — and
;; there is no second `:op "watch"' in flight, which is the invariant
;; `flan-dev-settle-hook' exists to keep. As with the buffer, the timer decides
;; `flan-settle-hook' exists to keep. As with the buffer, the timer decides
;; how often the picture is repainted and not how fresh it is: the program
;; writes every frame regardless.
;;
@ -312,7 +312,7 @@ Guarded on the row's length so a short name cannot be claimed by accident."
(defun flan-watch--ghost-paint (rows overflow)
"Place inline overlays for ROWS. OVERFLOW means some name found no slot."
(flan-watch--ghost-clear)
(let ((stale (and flan-dev--stopped t)))
(let ((stale (and flan--stopped t)))
(dolist (buf (flan-watch--ghost-buffers))
(with-current-buffer buf
(let* ((sites (flan-watch--ghost-sites))
@ -365,24 +365,24 @@ Guarded on the row's length so a short name cannot be claimed by accident."
(defun flan-watch--settle ()
"Collect an outstanding watch reply, blocking if it has not arrived.
Hung on `flan-dev-settle-hook', so an ordinary request never reads the watch
Hung on `flan-settle-hook', so an ordinary request never reads the watch
timer's reply as its own. Blocking here is fine and blocking in the tick is
not: this runs inside something a person asked for, which already waits, and
what it waits for is a table read with nothing compiled behind it."
(when flan-watch--pending
(setq flan-watch--pending nil)
(when-let* ((proc flan-dev--connection))
(when-let* ((proc flan--connection))
(when (process-live-p proc)
(ignore-errors (flan-watch--absorb (flan-dev--read-reply proc)))))))
(ignore-errors (flan-watch--absorb (flan--read-reply proc)))))))
(defun flan-watch--tick ()
"Collect the last reply if it has come, then ask again. Never blocks.
Deliberately not `flan-dev--request', which waits for its answer: a
Deliberately not `flan--request', which waits for its answer: a
synchronous call on a 0.2s timer stalls Emacs's UI every tick, and a timer is
the one caller that must not. So this takes whatever has already arrived and
sends the next question, leaving at most one request in flight the invariant
`flan-dev-settle-hook' exists to keep."
`flan-settle-hook' exists to keep."
(cond
;; Killing the buffer cancels the buffer's half of the subscription, and
;; only that. When it was the only consumer this stops the timer and
@ -392,23 +392,23 @@ sends the next question, leaving at most one request in flight — the invariant
((and (memq 'buffer flan-watch--consumers)
(not (get-buffer flan-watch-buffer)))
(flan-watch--drop 'buffer))
((not (process-live-p flan-dev--connection))
((not (process-live-p flan--connection))
(flan-watch--paint "error:\nnot connected to a running program\n")
(flan-watch-stop))
;; Something else owns the connection this instant — an evaluation is
;; mid-flight. Skipping is right: its `flan-dev-settle-hook' has already
;; mid-flight. Skipping is right: its `flan-settle-hook' has already
;; taken any reply of ours, and the next tick is 0.2s away.
(flan-dev--busy nil)
(flan--busy nil)
(t
(when flan-watch--pending
;; Taken off the connection either way. `flan-dev--extract-reply'
;; Taken off the connection either way. `flan--extract-reply'
;; deletes a frame before it reads it, so a payload that will not read
;; has still been consumed — and a pending flag left standing after it
;; would wait for ever for a reply that is no longer in the buffer,
;; which stops the timer sending anything again. One bad reply costs
;; one tick, not the session.
(when-let* ((reply (condition-case nil
(flan-dev--take-reply flan-dev--connection)
(flan--take-reply flan--connection)
(error (setq flan-watch--pending nil) nil))))
(setq flan-watch--pending nil)
(flan-watch--absorb reply)))
@ -432,11 +432,11 @@ sends the next question, leaving at most one request in flight — the invariant
;; would ask for a new window five times a second that nothing can
;; fill. The guard lives here rather than in the runtime because
;; "since you last looked" is the editor's policy, not the table's.
;; `flan-dev--stopped' is the one place that state is tracked, and
;; `flan-dev.el''s background poll keeps it current whether or not
;; `flan--stopped' is the one place that state is tracked, and
;; `flan.el''s background poll keeps it current whether or not
;; anyone is evaluating.
(progn (flan-dev--send flan-dev--connection
(if flan-dev--stopped
(progn (flan--send flan--connection
(if flan--stopped
'(:op "watch")
'(:op "watch" :reset t)))
(setq flan-watch--pending t))
@ -456,14 +456,14 @@ writer, so it has to be told somebody is looking — and while nobody is, nothin
writes the table at all, which is what makes a watch call in a program nobody is
debugging a load and a not-taken branch. It is sent once for the first
consumer: two of them looking at one table is still one table."
(flan-dev--live-connection)
(flan--live-connection)
(unless flan-watch--consumers
(let ((r (flan-dev--request '(:op "watch-enable" :on t))))
(let ((r (flan--request '(:op "watch-enable" :on t))))
(unless (equal (plist-get r :status) "ok")
(user-error "flan: %s" (or (plist-get r :message) "watch refused")))))
(unless (memq consumer flan-watch--consumers)
(push consumer flan-watch--consumers))
(add-hook 'flan-dev-settle-hook #'flan-watch--settle)
(add-hook 'flan-settle-hook #'flan-watch--settle)
(when flan-watch--timer (cancel-timer flan-watch--timer))
(setq flan-watch--timer
(run-with-timer 0 flan-watch-interval #'flan-watch--tick)))
@ -520,16 +520,16 @@ Tears down both consumers. `flan-watch--drop' is the way to stop one of them."
(setq flan-watch--timer nil))
;; Settle before disarming, or the disarm request reads the tick's reply.
(flan-watch--settle)
(remove-hook 'flan-dev-settle-hook #'flan-watch--settle)
(remove-hook 'flan-settle-hook #'flan-watch--settle)
;; Only on a connection that is already live, and this is the important half.
;; `flan-dev--request' *reconnects* — which is right for something a person
;; `flan--request' *reconnects* — which is right for something a person
;; did and wrong here, because this is also called from the tick, and the
;; reason the tick calls it is that the connection has gone. Reconnecting
;; from a timer would quietly erase the `lost' state that exists to be seen,
;; which `flan-dev.el' already forbids for its own poll timer. And there is
;; which `flan.el' already forbids for its own poll timer. And there is
;; nothing to disarm anyway: the table went with the program.
(when (process-live-p flan-dev--connection)
(ignore-errors (flan-dev--request '(:op "watch-enable" :on nil)))))
(when (process-live-p flan--connection)
(ignore-errors (flan--request '(:op "watch-enable" :on nil)))))
;;; What ghost text still cannot show
@ -554,7 +554,7 @@ Tears down both consumers. `flan-watch--drop' is the way to stop one of them."
;; - The module in the running program can be older than the buffer. A site
;; you have typed but not yet installed with `C-c C-c' has no row, and one
;; you deleted but not yet installed still writes. Deliberately unreported:
;; the stale-caller machinery in `flan-dev.el' already tracks what needs
;; the stale-caller machinery in `flan.el' already tracks what needs
;; re-evaluating, and a second opinion about it here would be a worse one.
;;
;; Still genuinely blocked on the checker, and unchanged by any of this: a

File diff suppressed because it is too large Load Diff

View File

@ -18,9 +18,9 @@
(require 'flan-inspect)
(require 'flan-cnr)
;; For `flan-dev--auto-break', which is a decision about globals and windows
;; For `flan--auto-break', which is a decision about globals and windows
;; and needs neither a daemon nor a socket to be asked.
(require 'flan-dev)
(require 'flan)
(defvar test-flan--failures 0)
(defvar test-flan--ran 0)
@ -275,14 +275,14 @@
(message "\nthe registry listings")
(let* ((sent nil)
(flan-dev--request-stub
(flan--request-stub
(lambda (form)
(setq sent form)
'(:status "ok"
:types (("Enemy" 2 64) ("i32" 1 16))
:blocks 3 :bytes 80 :overflow nil
:note "every block the registry recorded"))))
(cl-letf (((symbol-function 'flan-dev--request) flan-dev--request-stub)
(cl-letf (((symbol-function 'flan--request) flan--request-stub)
((symbol-function 'display-buffer) #'ignore))
(let ((flan-allocations-buffer " *test-allocations*"))
(flan-allocations)
@ -301,7 +301,7 @@
;; An overflowed table has blocks in the program that are in nobody's row, so
;; every number under it is a floor. Said before the numbers, because a reader
;; who missed it would quote them as counts.
(cl-letf (((symbol-function 'flan-dev--request)
(cl-letf (((symbol-function 'flan--request)
(lambda (_) '(:status "ok" :types (("Enemy" 1 32)) :blocks 1 :bytes 32
:overflow t)))
((symbol-function 'display-buffer) #'ignore))
@ -315,7 +315,7 @@
;; And a release build, which records nothing and says so. The refusal comes
;; back as an ordinary error status and reaches the person, rather than an
;; empty listing that reads like a program holding nothing.
(cl-letf (((symbol-function 'flan-dev--request)
(cl-letf (((symbol-function 'flan--request)
(lambda (_) '(:status "error"
:message "the allocation registry is off; this is not a dev build")))
((symbol-function 'display-buffer) #'ignore))
@ -1044,7 +1044,7 @@ unwind would send the next one to a daemon that is not there."
;;; Opening the break buffer by itself
;; `flan-dev--auto-break' is a decision — given the state the client is in,
;; `flan--auto-break' is a decision — given the state the client is in,
;; does the break buffer appear — so it is asked here with the state bound
;; rather than by stopping a real program. Every guard in it exists because
;; the answer is no in some state a person is actually in.
@ -1052,7 +1052,7 @@ unwind would send the next one to a daemon that is not there."
(message "\nthe break buffer opening itself")
(defun test-flan--auto-break (&rest bindings)
"Non-nil if `flan-dev--auto-break' would show the buffer.
"Non-nil if `flan--auto-break' would show the buffer.
BINDINGS is a plist of extra state; the defaults are a live connection and a
stopped program, which is the case where it should fire."
(let ((shown nil))
@ -1063,16 +1063,16 @@ stopped program, which is the case where it should fire."
((symbol-function 'process-live-p)
(lambda (_) (if (plist-member bindings :live)
(plist-get bindings :live) t))))
(let ((flan-dev--stopped
(let ((flan--stopped
(if (plist-member bindings :stopped)
(plist-get bindings :stopped) "Missing"))
(flan-dev-break-on-stop
(flan-break-on-stop
(if (plist-member bindings :setting)
(plist-get bindings :setting) 'display))
(flan-dev--busy (plist-get bindings :busy))
(flan--busy (plist-get bindings :busy))
(executing-kbd-macro (plist-get bindings :macro))
(flan-dev--connection 'stub))
(flan-dev--auto-break)))
(flan--connection 'stub))
(flan--auto-break)))
shown))
(test-flan--check "a stopped program opens it"
@ -1081,7 +1081,7 @@ stopped program, which is the case where it should fire."
(not (test-flan--auto-break :stopped nil)))
(test-flan--check "nor does a connection that has gone"
(not (test-flan--auto-break :live nil)))
;; The reason it is deferred at all: `flan-dev--absorb' notices the stop in the
;; The reason it is deferred at all: `flan--absorb' notices the stop in the
;; middle of reading a reply, and three more requests down the same socket
;; would interleave two conversations.
(test-flan--check "nor while a request is still in flight"
@ -1118,7 +1118,7 @@ stopped program, which is the case where it should fire."
;;; The macroexpansion buffer
;; The same kind of thing again: a reply in, a buffer out. What the live test
;; in test-flan-dev.el cannot easily reach is the shape where *nothing*
;; in test-flan.el cannot easily reach is the shape where *nothing*
;; expanded — a head that is not a macro, and a macro whose answer is its own
;; call — because both have to be arranged in a program. Here they are a
;; plist.

View File

@ -141,7 +141,7 @@ what bounds its cost; a `with-temp-buffer' would be scanned by nothing."
"(watch-i64 \"ticks\" ticks)\n"
"(watch-i64 \"gone\" g)\n")))
(flan-watch--consumers '(ghost))
(flan-dev--stopped nil))
(flan--stopped nil))
(flan-watch--ghost-paint '(("ticks" . "42")) nil)
(test-flan--check
"a site whose name is in the table gets an overlay, and one that is not does not"
@ -163,14 +163,14 @@ what bounds its cost; a `with-temp-buffer' would be scanned by nothing."
(equal (nth 2 (test-flan-watch--strings))
" => no slot: the table is full"))
(setq flan-dev--stopped "BoundsError")
(setq flan--stopped "BoundsError")
(flan-watch--ghost-paint '(("ticks" . "42")) nil)
(test-flan--check
"stopping the program marks every overlay, not the buffer's modeline only"
(equal (test-flan-watch--strings)
'(" => 42 one slot, 2 sites last frame"
" => 42 one slot, 2 sites last frame")))
(setq flan-dev--stopped nil)
(setq flan--stopped nil)
;; The whole answer to invalidating an overlay whose line moved: no overlay
;; outlives a repaint, so there is nothing to invalidate.
@ -219,24 +219,24 @@ what bounds its cost; a `with-temp-buffer' would be scanned by nothing."
;; paused it. So the tick must drop `:reset' while stopped and keep reading.
;;
;; Asserted at the level the rest of this file works at: no daemon, no socket.
;; The tick is a function from `flan-dev--stopped' to the form it puts on the
;; wire, and that is the whole claim, so `flan-dev--send' and `process-live-p'
;; The tick is a function from `flan--stopped' to the form it puts on the
;; wire, and that is the whole claim, so `flan--send' and `process-live-p'
;; are stubs. What this cannot reach is the daemon actually honouring the
;; absent field; `test/test_dev.ml' drives a real program for that.
(defun test-flan-watch--tick-form (stopped)
"The form `flan-watch--tick' sends with the program STOPPED or not."
(let ((sent nil)
(flan-dev--stopped stopped)
(flan-dev--connection 'a-process)
(flan-dev--busy nil)
(flan--stopped stopped)
(flan--connection 'a-process)
(flan--busy nil)
(flan-watch--pending nil)
;; Not `buffer': that consumer checks for a live watch buffer first and
;; would drop the subscription instead of ticking.
(flan-watch--consumers '(ghost)))
(cl-letf (((symbol-function 'process-live-p) (lambda (_) t))
((symbol-function 'flan-dev--take-reply) (lambda (_) nil))
((symbol-function 'flan-dev--send)
((symbol-function 'flan--take-reply) (lambda (_) nil))
((symbol-function 'flan--send)
(lambda (_proc form) (setq sent form))))
(flan-watch--tick)
(list sent flan-watch--pending))))

File diff suppressed because it is too large Load Diff

View File

@ -2322,7 +2322,7 @@ let abort t =
The complaint it answers, in the words it was made in: you run a program
under [flan dev], it opens a window, you close the window, main returns
and there is no way to get a new window back short of tearing down the whole
session with [flan-dev-restart-program], which throws away the build, the
session with [flan-restart-program], which throws away the build, the
session and every global. In Common Lisp or Clojure the image outlives main,
so you call it again. The process here already outlived main; it simply had
nothing that could wake it.
@ -3074,7 +3074,7 @@ let serve t fd =
two-process daemon that would lose the session; in the merged build it kills
the program, the compiler and the listener together, and leaves the socket
file behind for the next client to get ECONNREFUSED on. That is not a
theoretical shape: `M-x flan-dev' reconnects a dead connection, Emacs tears
theoretical shape: `M-x flan' reconnects a dead connection, Emacs tears
the old process down when it does, and a reply already on its way out lands
in the gap.

View File

@ -112,7 +112,7 @@ let () =
let code =
Sys.command
(Printf.sprintf
"emacs -Q --batch -L ../../../emacs -l ../../../emacs/test-flan-dev.el \
"emacs -Q --batch -L ../../../emacs -l ../../../emacs/test-flan.el \
-- %s %s %s %s 2>&1"
(Filename.quote sock) (Filename.quote buf) (Filename.quote flan_abs)
(Filename.quote program))

View File

@ -290,7 +290,7 @@ let () =
let t, _ = Session.create ~file:"../sand.flan" () in
let src = In_channel.with_open_bin "../sand.flan" In_channel.input_all in
(* [~origin] is the buffer's own path and both editor paths send it
(flan-dev.el's `:file (or buffer-file-name "<buffer>")`). Omitting it here
(flan.el's `:file (or buffer-file-name "<buffer>")`). Omitting it here
was testing a request the editor never sends. It used to matter to this
case for a second reason sand.flan embedded brush.png, and an embedded
path resolves relative to the file the form is written in, so the default

View File

@ -1731,7 +1731,7 @@ retains three, permanently and correctly.</p>
<code>lisp-mode</code>'s syntax table, so sexp motion, paren matching and indentation are
already right. It adds Flan's brackets &mdash; <code>[</code> and <code>{</code> are brackets,
not symbol characters, since every binding list and every type is written with them &mdash; and
the characters a Flan name may contain. <code>emacs/flan-dev.el</code> is the client;
the characters a Flan name may contain. <code>emacs/flan.el</code> is the client;
there is no parser in it.</p>
<h3>Setting up</h3>
@ -1745,15 +1745,15 @@ the client, the REPL, the inspector and the conditions buffer all load on first
<p><code>flan-dape.el</code> is separate on purpose, so <code>flan-mode</code> works
without dape installed. You also need <code>flan</code> on your <code>PATH</code>, or
<code>flan-dev-command</code> pointed at it.</p>
<code>flan-command</code> pointed at it.</p>
<h3>Starting a program</h3>
<p><code>M-x flan-dev</code> runs <code>flan dev</code> on a file, waits for it to come up
<p><code>M-x flan</code> runs <code>flan dev</code> on a file, waits for it to come up
and connects. <kbd>C-c C-z</kbd> attaches to one that is already running, looking for
<code>.flan-dev.sock</code> upward from the buffer, so from anywhere in the project it
finds the one program you have going. <kbd>C-c C-q</kbd> disconnects and leaves it
running; <code>M-x flan-dev-quit</code> stops it, but only one this Emacs started &mdash; a
running; <code>M-x flan-quit</code> stops it, but only one this Emacs started &mdash; a
daemon you launched in a terminal is not Emacs' to kill, and it says so rather than doing
something surprising.</p>
@ -1782,8 +1782,8 @@ something surprising.</p>
</table>
</div>
<p>Some commands have no key. <code>M-x flan-dev</code> starts a program and
<code>M-x flan-dev-quit</code> stops it. <code>M-x flan-watch</code> opens the watch
<p>Some commands have no key. <code>M-x flan</code> starts a program and
<code>M-x flan-quit</code> stops it. <code>M-x flan-watch</code> opens the watch
buffer, which shows values <em>while the program runs</em> rather than while it is
stopped, and <code>M-x flan-watch-ghost-mode</code> shows the same values inline at the
call that wrote each one. <code>M-x flan-inspect-address</code> roots an inspection at a
@ -1879,13 +1879,13 @@ answers with the outer binding.</p>
<div class="scroll">
<table>
<tr><th>Setting</th><th>Default</th><th>What it is</th></tr>
<tr><td><code>flan-dev-command</code></td><td><code>"flan"</code></td><td>the compiler binary</td></tr>
<tr><td><code>flan-dev-socket-name</code></td><td><code>".flan-dev.sock"</code></td><td>what <kbd>C-c C-z</kbd> searches for</td></tr>
<tr><td><code>flan-dev-echo-result</code></td><td><code>t</code></td><td>print <kbd>C-x C-e</kbd>'s value in the echo area</td></tr>
<tr><td><code>flan-dev-names-shown</code></td><td><code>4</code></td><td>how many names to list before counting them</td></tr>
<tr><td><code>flan-dev-output-buffer</code></td><td><code>"*flan-output*"</code></td><td>where the program's output goes</td></tr>
<tr><td><code>flan-dev-poll-interval</code></td><td><code>1.0</code></td><td>seconds between checks for whether it stopped</td></tr>
<tr><td><code>flan-dev-start-timeout</code></td><td><code>60</code></td><td>seconds to wait for a program to come up</td></tr>
<tr><td><code>flan-command</code></td><td><code>"flan"</code></td><td>the compiler binary</td></tr>
<tr><td><code>flan-socket-name</code></td><td><code>".flan-dev.sock"</code></td><td>what <kbd>C-c C-z</kbd> searches for</td></tr>
<tr><td><code>flan-echo-result</code></td><td><code>t</code></td><td>print <kbd>C-x C-e</kbd>'s value in the echo area</td></tr>
<tr><td><code>flan-names-shown</code></td><td><code>4</code></td><td>how many names to list before counting them</td></tr>
<tr><td><code>flan-output-buffer</code></td><td><code>"*flan-output*"</code></td><td>where the program's output goes</td></tr>
<tr><td><code>flan-poll-interval</code></td><td><code>1.0</code></td><td>seconds between checks for whether it stopped</td></tr>
<tr><td><code>flan-start-timeout</code></td><td><code>60</code></td><td>seconds to wait for a program to come up</td></tr>
</table>
</div>