Merge: the command is flan, and it knows what buffer you are in
This commit is contained in:
commit
5c228d35dc
4
FIX.org
4
FIX.org
@ -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 Flan’s 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
12
NEXT.md
@ -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
|
||||
```
|
||||
|
||||
@ -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).
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.** |
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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 |
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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))))
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
@ -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.
|
||||
|
||||
@ -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
@ -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.
|
||||
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 — <code>[</code> and <code>{</code> are brackets,
|
||||
not symbol characters, since every binding list and every type is written with them — 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 — a
|
||||
running; <code>M-x flan-quit</code> stops it, but only one this Emacs started — 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>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user