diff --git a/FIX.org b/FIX.org index 91be8be..6fd894d 100644 --- a/FIX.org +++ b/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 diff --git a/NEXT.md b/NEXT.md index 76682ed..2c23b6e 100644 --- a/NEXT.md +++ b/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 ``` diff --git a/README.md b/README.md index 365c1fd..d52184c 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/docs/BUILT.md b/docs/BUILT.md index 410baff..165a105 100644 --- a/docs/BUILT.md +++ b/docs/BUILT.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. diff --git a/docs/DISCUSS.md b/docs/DISCUSS.md index 1f05819..e78b11d 100644 --- a/docs/DISCUSS.md +++ b/docs/DISCUSS.md @@ -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) )`. Nothing on the compiler side changes. diff --git a/docs/REFERENCES.md b/docs/REFERENCES.md index aec4ad2..1494fa7 100644 --- a/docs/REFERENCES.md +++ b/docs/REFERENCES.md @@ -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.** | diff --git a/docs/REVIEW-production-readiness.md b/docs/REVIEW-production-readiness.md index 5fb0e38..9c46fad 100644 --- a/docs/REVIEW-production-readiness.md +++ b/docs/REVIEW-production-readiness.md @@ -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` diff --git a/emacs/MANUAL.md b/emacs/MANUAL.md index 92db3c7..498906a 100644 --- a/emacs/MANUAL.md +++ b/emacs/MANUAL.md @@ -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 | diff --git a/emacs/flan-cnr.el b/emacs/flan-cnr.el index b8fe4ec..749f931 100644 --- a/emacs/flan-cnr.el +++ b/emacs/flan-cnr.el @@ -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. diff --git a/emacs/flan-dape.el b/emacs/flan-dape.el index 8371604..4ba767b 100644 --- a/emacs/flan-dape.el +++ b/emacs/flan-dape.el @@ -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. diff --git a/emacs/flan-inspect.el b/emacs/flan-inspect.el index 9e065bd..afcb736 100644 --- a/emacs/flan-inspect.el +++ b/emacs/flan-inspect.el @@ -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 diff --git a/emacs/flan-lower.el b/emacs/flan-lower.el index e289302..cbfa7ec 100644 --- a/emacs/flan-lower.el +++ b/emacs/flan-lower.el @@ -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)))) diff --git a/emacs/flan-mode.el b/emacs/flan-mode.el index 8309bd0..961c6e1 100644 --- a/emacs/flan-mode.el +++ b/emacs/flan-mode.el @@ -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' diff --git a/emacs/flan-repl.el b/emacs/flan-repl.el index deb2f65..2276442 100644 --- a/emacs/flan-repl.el +++ b/emacs/flan-repl.el @@ -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 "")) (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 diff --git a/emacs/flan-watch.el b/emacs/flan-watch.el index bc245ba..b73cb1f 100644 --- a/emacs/flan-watch.el +++ b/emacs/flan-watch.el @@ -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 diff --git a/emacs/flan-dev.el b/emacs/flan.el similarity index 83% rename from emacs/flan-dev.el rename to emacs/flan.el index 4898c13..aea5491 100644 --- a/emacs/flan-dev.el +++ b/emacs/flan.el @@ -1,4 +1,4 @@ -;;; flan-dev.el --- Talk to a running Flan program -*- lexical-binding: t; -*- +;;; flan.el --- Talk to a running Flan program -*- lexical-binding: t; -*- ;; Author: Joseph Ferano ;; Version: 0.1.0 @@ -21,9 +21,9 @@ ;; The editor half of Flan's dev loop. `flan dev program.flan' compiles the ;; program, launches it, and listens on .flan-dev.sock beside the source; this -;; connects to that socket and sends it forms. M-x flan-dev starts that +;; connects to that socket and sends it forms. M-x flan starts that ;; daemon from here and connects when it is serving, so the loop needs no -;; terminal — and M-x flan-dev-quit ends it, which ends the program, because +;; terminal — and M-x flan-quit ends it, which ends the program, because ;; the daemon is what owns the program's lifetime. ;; ;; C-c C-c recompiles the top-level form at point and installs it in the @@ -73,30 +73,30 @@ ;; the commands here and requires nothing back. (require 'flan-mode) -(defgroup flan-dev nil +(defgroup flan nil "Talking to a running Flan program." :group 'flan - :prefix "flan-dev-") + :prefix "flan-") -(defcustom flan-dev-socket-name ".flan-dev.sock" +(defcustom flan-socket-name ".flan-dev.sock" "Name of the socket `flan dev' listens on, looked for up from the buffer." :type 'string) -(defcustom flan-dev-echo-result t +(defcustom flan-echo-result t "Whether an accepted evaluation reports in the echo area. Turning this off makes a successful evaluation indistinguishable from one that quietly did nothing, which is why it is on." :type 'boolean) -(defcustom flan-dev-names-shown 4 +(defcustom flan-names-shown 4 "How many installed names to name before falling back to counting them." :type 'integer) -(defcustom flan-dev-output-buffer "*flan-output*" +(defcustom flan-output-buffer "*flan-output*" "Buffer the running program's own output is appended to." :type 'string) -(defcustom flan-dev-daemon-buffer "*flan-dev*" +(defcustom flan-daemon-buffer "*flan-dev*" "Buffer the daemon's own output goes to. This is where a build that failed says so: the daemon compiles the program before it binds its socket, so a program that does not compile produces no @@ -105,7 +105,7 @@ build that is merely slow can be watched, which is what the reply timeout below points at." :type 'string) -(defcustom flan-dev-reply-timeout 30 +(defcustom flan-reply-timeout 30 "Seconds to wait for one reply from the daemon before giving up. This bounds a single request, not a session. Thirty seconds is generous @@ -119,20 +119,20 @@ it again would install a body twice or run a side-effecting expression twice; reconnecting happens before a send and never after one." :type 'number) -(defvar flan-dev--connection nil +(defvar flan--connection nil "The open connection, or nil.") -(defvar flan-dev--socket nil - "Path of the socket `flan-dev--connection' is connected to.") +(defvar flan--socket nil + "Path of the socket `flan--connection' is connected to.") -(defvar flan-dev--stopped nil +(defvar flan--stopped nil "Name of the condition the program is stopped on, or nil if it is running. Set from every reply the daemon sends, which is how a stop that nothing asked about is noticed at all.") -(defvar flan-dev--parked nil +(defvar flan--parked nil "Non-nil when the program has finished and its process is waiting to re-run. -Set from every reply, as `flan-dev--stopped' is and for the same reason: a +Set from every reply, as `flan--stopped' is and for the same reason: a program finishes without announcing it, and the commonest way to finish is closing its window with the mouse. Nothing about the session has gone — `flan-rerun' runs `main' again, with the globals as the last run left them.") @@ -143,28 +143,28 @@ closing its window with the mouse. Nothing about the session has gone — ;; sessions. Kept up here with the rest of the client's state rather than ;; beside the code that starts a daemon, because `flan-connect' reads them ;; before it replaces anything. -(defvar flan-dev--file nil +(defvar flan--file nil "The program the daemon this Emacs started was started on, or nil. Kept so that it can be started again on the same program and the same -socket, which is what `flan-dev-restart-program' is.") +socket, which is what `flan-restart-program' is.") -(defvar flan-dev--daemon nil +(defvar flan--daemon nil "The `flan dev' process this Emacs started, or nil. A daemon started in a terminal is not here, and `flan-connect' still works for it — this is only what Emacs is responsible for killing.") -(defvar flan-dev--daemon-socket nil +(defvar flan--daemon-socket nil "Path of the socket the daemon this Emacs started is listening on, or nil. -`flan-dev--socket' is where the *connection* is, and the two part company the +`flan--socket' is where the *connection* is, and the two part company the moment `flan-connect' attaches to something else: a second program in another terminal is an ordinary thing to go and look at, and it does not stop this Emacs being responsible for the daemon it launched. Keeping both is what lets -`flan-dev-quit' tell one session from two.") +`flan-quit' tell one session from two.") -(defvar flan-dev--busy nil +(defvar flan--busy nil "Non-nil while a request is waiting for its reply. -`flan-dev--read-reply' runs `accept-process-output', which runs timers, so +`flan--read-reply' runs `accept-process-output', which runs timers, so without this the poll timer could fire inside another request and read the reply that request was waiting for.") @@ -177,7 +177,7 @@ reply that request was waiting for.") ;; a multibyte identifier would otherwise put the reply stream out of step by ;; exactly as many bytes as the payload has non-ASCII characters. -(defun flan-dev--bare (form) +(defun flan--bare (form) "FORM with every string stripped of its text properties. A propertized string prints as #(...) syntax, which the daemon's reader takes as a bare symbol followed by a stray list — the field silently stops @@ -185,20 +185,20 @@ being a string. Buffer text arrives propertized (the REPL's comint input does, for one), so the wire layer strips rather than trusting every caller to." (cond ((stringp form) (substring-no-properties form)) - ((consp form) (cons (flan-dev--bare (car form)) - (flan-dev--bare (cdr form)))) + ((consp form) (cons (flan--bare (car form)) + (flan--bare (cdr form)))) (t form))) -(defun flan-dev--send (proc form) +(defun flan--send (proc form) "Send FORM to PROC as one framed message." - (let* ((payload (encode-coding-string (prin1-to-string (flan-dev--bare form)) + (let* ((payload (encode-coding-string (prin1-to-string (flan--bare form)) 'utf-8 t))) (process-send-string proc (format "%d\n%s" (length payload) payload)))) -(defun flan-dev--take-reply (proc) +(defun flan--take-reply (proc) "Read one complete framed message out of PROC's buffer, or return nil. -Never waits. This is the half of `flan-dev--read-reply' that does not block, +Never waits. This is the half of `flan--read-reply' that does not block, split out for the watch timer: a timer that called `accept-process-output' would stall the UI every tick, which is exactly the mistake the Clojure original left a comment about. See `flan-watch--tick'." @@ -211,9 +211,9 @@ original left a comment about. See `flan-watch--tick'." ;; Present in full, or not yet — a partial body is not an error here, ;; it is the ordinary state between the send and the reply. (when (>= (- (position-bytes (point-max)) (position-bytes body-start)) n) - (flan-dev--extract-reply body-start n))))))) + (flan--extract-reply body-start n))))))) -(defun flan-dev--extract-reply (body-start n) +(defun flan--extract-reply (body-start n) "Read the N bytes at BODY-START as a reply and delete the frame. Point is in the process buffer, and the frame is known to be complete. @@ -234,7 +234,7 @@ stream in step for the request that follows." (delete-region (point-min) end) (car (read-from-string text)))) -(defun flan-dev--no-reply (proc) +(defun flan--no-reply (proc) "Signal that PROC has not answered, saying which of the two silences it is. Deliberately not retried, in either case. If the daemon took the request and @@ -248,18 +248,18 @@ Reconnecting happens before a send, never after one." ;; and the daemon's own log says which step it is on, so the message ;; names the buffer to look in and the setting to raise rather than ;; leaving both to be discovered. - (error "flan dev: no reply in %ss from %s; the daemon may still be building — see %s for its log, and raise `flan-dev-reply-timeout' if this build is simply long" - flan-dev-reply-timeout - (abbreviate-file-name (or flan-dev--socket "the daemon")) - flan-dev-daemon-buffer) + (error "flan dev: no reply in %ss from %s; the daemon may still be building — see %s for its log, and raise `flan-reply-timeout' if this build is simply long" + flan-reply-timeout + (abbreviate-file-name (or flan--socket "the daemon")) + flan-daemon-buffer) (error "flan dev: the daemon on %s closed the connection; not resent, because it may already have run" - (abbreviate-file-name (or flan-dev--socket "?"))))) + (abbreviate-file-name (or flan--socket "?"))))) -(defun flan-dev--read-reply (proc) +(defun flan--read-reply (proc) "Block until PROC sends one complete framed message, and read it." (with-current-buffer (process-buffer proc) - (let ((deadline (+ (float-time) flan-dev-reply-timeout))) + (let ((deadline (+ (float-time) flan-reply-timeout))) ;; The header first: digits up to a newline. (while (and (not (save-excursion (goto-char (point-min)) (re-search-forward "\\`\\([0-9]+\\)\n" nil t))) @@ -272,14 +272,14 @@ Reconnecting happens before a send, never after one." ;; that is merely slow into a stream out of step by however much of the ;; count had landed. (unless (re-search-forward "\\`\\([0-9]+\\)\n" nil t) - (flan-dev--no-reply proc)) + (flan--no-reply proc)) (let* ((n (string-to-number (match-string 1))) (body-start (point))) (while (and (< (- (position-bytes (point-max)) (position-bytes body-start)) n) (< (float-time) deadline)) (accept-process-output proc 0.05)) (if (>= (- (position-bytes (point-max)) (position-bytes body-start)) n) - (flan-dev--extract-reply body-start n) + (flan--extract-reply body-start n) ;; The body never came, so the count at the head of the buffer is a ;; promise about bytes that will never be made good: reading on from ;; here would take the *next* reply's header as this one's payload @@ -287,16 +287,16 @@ Reconnecting happens before a send, never after one." ;; The frame is dead — drop it, and the connection is in step again ;; for whatever a person does next. Testing the condition again ;; rather than trusting the loop is the whole fix: falling through - ;; to `flan-dev--extract-reply' with a short buffer signals a + ;; to `flan--extract-reply' with a short buffer signals a ;; wrong-type error from `byte-to-position', which says nothing ;; about a timeout to whoever reads it. (erase-buffer) - (flan-dev--no-reply proc)))))) + (flan--no-reply proc)))))) -(defun flan-dev--append-output (text) +(defun flan--append-output (text) "Append TEXT, the running program's own output, to its buffer." (when (and text (> (length text) 0)) - (with-current-buffer (get-buffer-create flan-dev-output-buffer) + (with-current-buffer (get-buffer-create flan-output-buffer) (let ((at-end (= (point) (point-max)))) (save-excursion (goto-char (point-max)) @@ -315,7 +315,7 @@ Reconnecting happens before a send, never after one." ;; The mode line and one echo-area line were the whole of it: the buffer that ;; says what happened and what can be done about it appeared only when `C-c -;; C-b' was typed. `flan-dev--absorb' below already knows the moment — it +;; C-b' was typed. `flan--absorb' below already knows the moment — it ;; reads `:stopped' off every reply, and the poll covers the case where no ;; reply is coming — so this is a hook at a point that exists rather than new ;; plumbing. @@ -328,7 +328,7 @@ Reconnecting happens before a send, never after one." ;; keystrokes somewhere they were not aimed, and `q' in a break buffer is not ;; what a half-typed word wanted to be. So `display-buffer': the buffer ;; appears, point does not move, and the window that had focus keeps it. -;; `flan-dev-break-on-stop' can be set to `focus' by anyone who disagrees, and +;; `flan-break-on-stop' can be set to `focus' by anyone who disagrees, and ;; to nil to go back to the mode line alone. ;; ;; **`(pause)' is not a special case.** It was worth asking — a breakpoint is @@ -350,7 +350,7 @@ Reconnecting happens before a send, never after one." ;; stopped and `C-c C-b' still works, so nothing is lost but the automatic ;; part. -(defcustom flan-dev-break-on-stop 'display +(defcustom flan-break-on-stop 'display "What to do when the program stops. `display' shows the break buffer without taking focus, `focus' shows it and selects its window, and nil leaves it to the mode line and `C-c C-b'." @@ -358,22 +358,22 @@ selects its window, and nil leaves it to the mode line and `C-c C-b'." (const :tag "Show it and go there" focus) (const :tag "Only the mode line" nil))) -(defun flan-dev--auto-break () +(defun flan--auto-break () "Show the break buffer, if the program is still stopped and it is safe to. -Runs from a timer, deliberately: `flan-dev--absorb' notices the stop in the -middle of reading a reply on the socket, with `flan-dev--busy' bound, and +Runs from a timer, deliberately: `flan--absorb' notices the stop in the +middle of reading a reply on the socket, with `flan--busy' bound, and `flan-cnr-show' asks the daemon three more questions. Issuing those from inside the read they were triggered by would interleave two conversations on one connection. The check is on the state rather than on the edge that scheduled this. By the time this runs the edge has been consumed, `flan-cnr-show''s own `break' has -been through `flan-dev--absorb' again, and the program may have been resumed in +been through `flan--absorb' again, and the program may have been resumed in between — so what matters is whether it is stopped *now*." - (when (and flan-dev--stopped - flan-dev-break-on-stop - (not flan-dev--busy) - (process-live-p flan-dev--connection) + (when (and flan--stopped + flan-break-on-stop + (not flan--busy) + (process-live-p flan--connection) ;; Someone is in the middle of answering a prompt. (not (active-minibuffer-window)) ;; A macro must do the same thing every time it is run. @@ -385,33 +385,33 @@ between — so what matters is whether it is stopped *now*." (ignore-errors (save-selected-window (let ((buf (flan-cnr-show))) - (when (and (eq flan-dev-break-on-stop 'focus) (buffer-live-p buf)) + (when (and (eq flan-break-on-stop 'focus) (buffer-live-p buf)) (let ((win (get-buffer-window buf))) (when win (select-window win))))))))) -(defun flan-dev--absorb (reply) +(defun flan--absorb (reply) "Take from REPLY the two things every reply carries, and return it. The program's output, and whether it is stopped. Both are read here rather -than at each call site because `flan-dev--report' signals on a rejection — +than at each call site because `flan--report' signals on a rejection — state attached to a reply that turns out to be an error would be thrown away with it, and a rejected evaluation is a likely moment to *become* stopped." - (flan-dev--append-output (plist-get reply :output)) - ;; The same edge treatment `flan-dev--stopped' gets below, and the message is + (flan--append-output (plist-get reply :output)) + ;; The same edge treatment `flan--stopped' gets below, and the message is ;; the reason: a program that finished is a program somebody is about to want ;; back, and the name of the command that does it is the whole of what they ;; need. Once, on the edge — the poll runs every second, and a line in the ;; echo area every second is a line nobody reads. - (let ((was flan-dev--parked) + (let ((was flan--parked) (now (and (plist-get reply :parked) t))) - (setq flan-dev--parked now) + (setq flan--parked now) (unless (eq was now) (force-mode-line-update t) (when now (message "flan: the program finished; C-c C-M-x runs it again")))) - (let ((was flan-dev--stopped) + (let ((was flan--stopped) (now (and (plist-get reply :stopped) (or (plist-get reply :condition) "a condition")))) - (setq flan-dev--stopped now) + (setq flan--stopped now) (unless (equal was now) (force-mode-line-update t) ;; Once, on the edge. A message every poll would bury whatever else the @@ -419,13 +419,13 @@ with it, and a rejected evaluation is a likely moment to *become* stopped." ;; there. (when now (message "flan: stopped on %s — C-c C-b to choose a restart" now) - ;; On the edge, and out of band. See `flan-dev--auto-break' for why + ;; On the edge, and out of band. See `flan--auto-break' for why ;; it cannot happen here: this runs inside the read of a reply on the ;; socket, and showing the buffer asks three more questions down it. - (run-at-time 0 nil #'flan-dev--auto-break)))) + (run-at-time 0 nil #'flan--auto-break)))) reply) -(defvar flan-dev-settle-hook nil +(defvar flan-settle-hook nil "Run before anything is sent, against the connection as it stands. The protocol is one reply per request on one connection, and that is the whole @@ -440,21 +440,21 @@ An outstanding reply belongs to the connection it was asked on; if the daemon has been restarted under Emacs, that connection is gone and no reply is coming on the new one. Running this first is what lets a hook see that for itself and drop its pending flag, rather than sitting out a full -`flan-dev-reply-timeout' waiting on a socket the question was never asked +`flan-reply-timeout' waiting on a socket the question was never asked down.") -(defun flan-dev--request (form) +(defun flan--request (form) "Send FORM to the connected program and return its reply." - ;; `flan-dev--busy' first of all, and around the reconnect as well as around - ;; the send: `flan-dev--live-connection' asks the new daemon what it + ;; `flan--busy' first of all, and around the reconnect as well as around + ;; the send: `flan--live-connection' asks the new daemon what it ;; defines, and `accept-process-output' runs timers, so a poll firing in the ;; middle of that would be a second conversation on the connection this one ;; just opened. - (let ((flan-dev--busy t)) - (run-hooks 'flan-dev-settle-hook) - (let ((proc (flan-dev--live-connection))) - (flan-dev--absorb (progn (flan-dev--send proc form) - (flan-dev--read-reply proc)))))) + (let ((flan--busy t)) + (run-hooks 'flan-settle-hook) + (let ((proc (flan--live-connection))) + (flan--absorb (progn (flan--send proc form) + (flan--read-reply proc)))))) ;;; Noticing that the program stopped @@ -472,81 +472,81 @@ down.") ;; anything — the ordinary way, 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 does not reconnect. `flan-dev--request' reopens a socket the +;; The timer does not reconnect. `flan--request' reopens a socket the ;; daemon restarted under it, which is right for something a person did and ;; wrong for a background timer: it would quietly erase the `lost' state that ;; exists to be seen. So the timer sends on the live process or does nothing. -(defcustom flan-dev-poll-interval 1.0 +(defcustom flan-poll-interval 1.0 "Seconds between background checks of whether the program has stopped. Set to nil to leave the program's state to whatever replies happen to say." :type '(choice number (const :tag "Never" nil))) -(defvar flan-dev--timer nil +(defvar flan--timer nil "The background poll, or nil.") -(defun flan-dev--poll () +(defun flan--poll () "Ask the daemon how the program is, if it is safe to ask right now." - (when (and (not flan-dev--busy) - (process-live-p flan-dev--connection)) - (let ((proc flan-dev--connection) - (flan-dev--busy t)) + (when (and (not flan--busy) + (process-live-p flan--connection)) + (let ((proc flan--connection) + (flan--busy t)) (ignore-errors ;; The same settle every other sender does, and for the same reason. - ;; `flan-dev--busy' is not enough on its own: the watch timer leaves a + ;; `flan--busy' is not enough on its own: the watch timer leaves a ;; request in flight and *clears* nothing, deliberately — it binds no ;; busy flag, because it never waits — so a poll that checked only the ;; flag would send `describe' with the watch's reply still coming and ;; read that instead. The two would then stay swapped for the rest of ;; the session, each consumer answering the other's question, which is - ;; exactly the interleaving `flan-dev-settle-hook' exists to prevent. - (run-hooks 'flan-dev-settle-hook) + ;; exactly the interleaving `flan-settle-hook' exists to prevent. + (run-hooks 'flan-settle-hook) ;; `describe' rather than `break': it is the cheap op, it is what ;; drains the program's output, and the state is on every reply anyway. ;; Asking `break' would fetch restart names nobody is choosing from. - (flan-dev--send proc '(:op "describe")) - (flan-dev--absorb (flan-dev--read-reply proc)))))) + (flan--send proc '(:op "describe")) + (flan--absorb (flan--read-reply proc)))))) -(defun flan-dev--start-polling () +(defun flan--start-polling () "Begin watching for the program stopping." - (flan-dev--stop-polling) - (when flan-dev-poll-interval - (setq flan-dev--timer - (run-with-timer flan-dev-poll-interval flan-dev-poll-interval - #'flan-dev--poll)))) + (flan--stop-polling) + (when flan-poll-interval + (setq flan--timer + (run-with-timer flan-poll-interval flan-poll-interval + #'flan--poll)))) -(defun flan-dev--stop-polling () +(defun flan--stop-polling () "Stop watching." - (when flan-dev--timer (cancel-timer flan-dev--timer)) - (setq flan-dev--timer nil)) + (when flan--timer (cancel-timer flan--timer)) + (setq flan--timer nil)) ;;; Connection -(defun flan-dev--find-socket () +(defun flan--find-socket () "Find the daemon's socket by walking up from the current buffer." (let ((dir (locate-dominating-file (or buffer-file-name default-directory) - flan-dev-socket-name))) - (and dir (expand-file-name flan-dev-socket-name dir)))) + flan-socket-name))) + (and dir (expand-file-name flan-socket-name dir)))) -(defun flan-dev--open (socket) +(defun flan--open (socket) "Open a connection to SOCKET and make it the current one." - (when (process-live-p flan-dev--connection) - (delete-process flan-dev--connection)) + (when (process-live-p flan--connection) + (delete-process flan--connection)) (let ((buf (get-buffer-create " *flan-dev*"))) ;; Unibyte, because the framing counts bytes and this buffer is where they ;; are counted. (with-current-buffer buf (erase-buffer) (set-buffer-multibyte nil)) - (setq flan-dev--connection + (setq flan--connection (make-network-process - :name "flan-dev" :buffer buf :family 'local :service socket + :name "flan" :buffer buf :family 'local :service socket :coding 'binary :noquery t)) - (setq flan-dev--socket socket)) - (setq flan-dev--stopped nil) - (setq flan-dev--parked nil) - (flan-dev--start-polling) + (setq flan--socket socket)) + (setq flan--stopped nil) + (setq flan--parked nil) + (flan--start-polling) (force-mode-line-update t) - flan-dev--connection) + flan--connection) ;; A daemon restarted while Emacs was not looking is the ordinary case, not an ;; exceptional one: `flan dev' ends when its program does, and a program under @@ -554,33 +554,33 @@ Set to nil to leave the program's state to whatever replies happen to say." ;; socket it was on rather than reported — but only *before* a request goes ;; out. Reconnecting after one has been sent and lost would be a retry, and a ;; retry of `eval-expr' runs the expression a second time. -(defun flan-dev--live-connection () +(defun flan--live-connection () "The open connection, reconnecting if the daemon has been restarted." - (unless (process-live-p flan-dev--connection) + (unless (process-live-p flan--connection) (cond - ((null flan-dev--socket) - (error "Not connected: M-x flan-dev to start a program, or M-x flan-connect")) - ((not (file-exists-p flan-dev--socket)) - (setq flan-dev--connection nil) + ((null flan--socket) + (error "Not connected: M-x flan to start a program, or M-x flan-connect")) + ((not (file-exists-p flan--socket)) + (setq flan--connection nil) (force-mode-line-update t) (error "flan dev: nothing is listening on %s; start `flan dev program.flan' again" - (abbreviate-file-name flan-dev--socket))) + (abbreviate-file-name flan--socket))) (t (condition-case err - (progn (flan-dev--open flan-dev--socket) + (progn (flan--open flan--socket) ;; A restarted daemon is a rebuilt program: everything known ;; about its names was about the last one. - (flan-dev--forget-defs) + (flan--forget-defs) ;; And asked again straight away. An empty cache is honest ;; but silent: eldoc would go quiet and M-. would fall through ;; to some other backend until the next install happened to ;; refill it. Safe to call from here — the connection is live ;; by now, so it does not come back through this function. - (ignore-errors (flan-dev-refresh-defs)) + (ignore-errors (flan-refresh-defs)) (message "flan dev: reconnected to %s" - (abbreviate-file-name flan-dev--socket))) + (abbreviate-file-name flan--socket))) (error - (setq flan-dev--connection nil) + (setq flan--connection nil) (force-mode-line-update t) ;; A refusal on a path that exists is a socket file outliving the ;; process that bound it -- a daemon killed outright rather than one @@ -593,34 +593,34 @@ Set to nil to leave the program's state to whatever replies happen to say." (if (string-match-p "[Cc]onnection refused" (error-message-string err)) (error "flan dev: %s is a leftover socket -- whatever bound it is \ gone; remove it and start `flan dev program.flan' again" - (abbreviate-file-name flan-dev--socket)) + (abbreviate-file-name flan--socket)) (error "flan dev: cannot reconnect to %s: %s" - (abbreviate-file-name flan-dev--socket) + (abbreviate-file-name flan--socket) (error-message-string err)))))))) - flan-dev--connection) + flan--connection) ;;;###autoload (defun flan-connect (&optional socket) "Connect to a `flan dev' daemon listening on SOCKET. -With no argument, look for `flan-dev-socket-name' up from this buffer." +With no argument, look for `flan-socket-name' up from this buffer." (interactive - (list (or (flan-dev--find-socket) + (list (or (flan--find-socket) (read-file-name "flan dev socket: ")))) - (unless socket (user-error "No %s found above this buffer" flan-dev-socket-name)) + (unless socket (user-error "No %s found above this buffer" flan-socket-name)) (setq socket (expand-file-name socket)) ;; Attaching to a second program is a thing people do on purpose — a daemon ;; running in a terminal is exactly what this command is for — but it leaves ;; two sessions where the client can only name one, and until it was said - ;; out loud the cost fell on `flan-dev-quit': "stop the daemon this Emacs + ;; out loud the cost fell on `flan-quit': "stop the daemon this Emacs ;; started" ended up closing whatever the connection happened to be pointing ;; at *and* killing the daemon, which by then were two different programs. ;; Naming both is most of the fix, because the situation is fine once it is - ;; known about; the refusal for a Lisp caller matches `flan-dev''s, where a + ;; known about; the refusal for a Lisp caller matches `flan''s, where a ;; running program is never discarded without someone saying so. - (when (and (process-live-p flan-dev--daemon) - flan-dev--daemon-socket - (not (equal socket flan-dev--daemon-socket))) - (let ((mine (abbreviate-file-name (or flan-dev--file flan-dev--daemon-socket))) + (when (and (process-live-p flan--daemon) + flan--daemon-socket + (not (equal socket flan--daemon-socket))) + (let ((mine (abbreviate-file-name (or flan--file flan--daemon-socket))) (theirs (abbreviate-file-name socket))) (if (called-interactively-p 'interactive) (unless (y-or-n-p @@ -630,27 +630,27 @@ With no argument, look for `flan-dev-socket-name' up from this buffer." (user-error "flan dev: %s is running from this Emacs; M-x flan-connect interactively to attach to %s as well" mine theirs)))) - (flan-dev--open socket) - (let ((r (flan-dev--request '(:op "describe")))) - (flan-dev-refresh-defs) + (flan--open socket) + (let ((r (flan--request '(:op "describe")))) + (flan-refresh-defs) (message "flan dev: connected to %s (%d functions, %d globals)" (abbreviate-file-name socket) (length (plist-get r :fns)) (length (plist-get r :globals)))) - flan-dev--connection) + flan--connection) (defun flan-disconnect () "Close the connection, which also ends the daemon and its program." (interactive) - (when (process-live-p flan-dev--connection) - (ignore-errors (flan-dev--request '(:op "close"))) - (delete-process flan-dev--connection)) - (setq flan-dev--connection nil) + (when (process-live-p flan--connection) + (ignore-errors (flan--request '(:op "close"))) + (delete-process flan--connection)) + (setq flan--connection nil) ;; Forgotten, not kept: this was a deliberate disconnect, so the next ;; request should say so rather than quietly reopening what was just closed. - (setq flan-dev--socket nil) - (setq flan-dev--stopped nil) - (flan-dev--stop-polling) - (flan-dev--forget-defs) + (setq flan--socket nil) + (setq flan--stopped nil) + (flan--stop-polling) + (flan--forget-defs) (force-mode-line-update t) (message "flan dev: disconnected")) @@ -670,49 +670,49 @@ With no argument, look for `flan-dev-socket-name' up from this buffer." ;; it works, until the timeout, or until the daemon exits — whichever comes ;; first. -(defcustom flan-dev-command "flan" +(defcustom flan-command "flan" "The Flan compiler, as `flan dev' is started from Emacs. A name is looked up on `exec-path'; a path is used as given." :type 'string) -;; `flan-dev-daemon-buffer' belongs to this section and is declared with the +;; `flan-daemon-buffer' belongs to this section and is declared with the ;; other buffer names at the top of the file instead, because the reply reader ;; -- which runs long before any of this -- names it in the message it gives ;; when a request times out, and the byte-compiler reads a file in order. -(defcustom flan-dev-start-timeout 60 +(defcustom flan-start-timeout 60 "Seconds to wait for a daemon started from Emacs to accept a connection. It builds the program first, which for a cold project is most of this." :type 'number) -;; The three variables that say what this Emacs started — `flan-dev--file', -;; `flan-dev--daemon' and `flan-dev--daemon-socket' — are declared with the +;; The three variables that say what this Emacs started — `flan--file', +;; `flan--daemon' and `flan--daemon-socket' — are declared with the ;; rest of the client's state at the top of the file for the same reason: ;; `flan-connect' reads all three before it replaces a connection, and the ;; byte-compiler reads a file in order. -(defun flan-dev--daemon-sentinel (proc event) +(defun flan--daemon-sentinel (proc event) "Say that the daemon PROC has gone, once, when it does. EVENT says how." (unless (process-live-p proc) - (when (eq proc flan-dev--daemon) - (setq flan-dev--daemon nil) - (setq flan-dev--daemon-socket nil) + (when (eq proc flan--daemon) + (setq flan--daemon nil) + (setq flan--daemon-socket nil) (force-mode-line-update t) ;; Named, because the daemon exits for two very different reasons — the ;; program finished, or it never built — and the buffer is where the ;; difference is written. (message "flan dev: the daemon exited (%s); see %s" - (string-trim (or event "")) flan-dev-daemon-buffer)))) + (string-trim (or event "")) flan-daemon-buffer)))) -(defun flan-dev--start-daemon (file socket) +(defun flan--start-daemon (file socket) "Start `flan dev' on FILE listening on SOCKET, and return the process." - (let ((buf (get-buffer-create flan-dev-daemon-buffer)) + (let ((buf (get-buffer-create flan-daemon-buffer)) ;; Expanded before `default-directory' moves, so that a command given ;; as a path is the path the user meant and not one relative to the ;; program's directory. A bare name is left alone for `exec-path'. - (cmd (if (file-name-directory flan-dev-command) - (expand-file-name flan-dev-command) - flan-dev-command)) + (cmd (if (file-name-directory flan-command) + (expand-file-name flan-command) + flan-command)) ;; The daemon runs where the program is, and so does the program it ;; launches — it inherits this. A game opening "assets/tiles.png" ;; means the project's directory, not whichever buffer Emacs happened @@ -725,60 +725,72 @@ It builds the program first, which for a cold project is most of this." (insert (format "%s dev %s -s %s\n\n" cmd file socket))) (setq default-directory (file-name-directory (expand-file-name file)))) (make-process - :name "flan-dev-daemon" :buffer buf + :name "flan-daemon" :buffer buf :command (list cmd "dev" file "-s" socket) ;; The daemon writes its ready line and the program's stderr to stderr, ;; and both belong in the same buffer in the order they happened. :connection-type 'pipe :noquery t - :sentinel #'flan-dev--daemon-sentinel))) + :sentinel #'flan--daemon-sentinel))) -(defun flan-dev--connect-when-ready (socket proc) +(defun flan--connect-when-ready (socket proc) "Connect to SOCKET once PROC is serving it, or say why that never happened." - (let ((deadline (+ (float-time) flan-dev-start-timeout)) + (let ((deadline (+ (float-time) flan-start-timeout)) (done nil)) (while (not done) (cond - ((condition-case nil (progn (flan-dev--open socket) t) (error nil)) + ((condition-case nil (progn (flan--open socket) t) (error nil)) (setq done t)) ((not (process-live-p proc)) ;; The likeliest failure by far: the program did not compile, so the ;; daemon died before binding. The reason is in its buffer and not in ;; anything this end can see — so show the buffer rather than name it ;; and leave someone to go and find it. - (display-buffer flan-dev-daemon-buffer) + (display-buffer flan-daemon-buffer) (user-error "flan dev: the daemon exited before it was ready; see %s" - flan-dev-daemon-buffer)) + flan-daemon-buffer)) ((> (float-time) deadline) - (display-buffer flan-dev-daemon-buffer) + (display-buffer flan-daemon-buffer) (user-error "flan dev: no socket on %s after %ss; see %s" - (abbreviate-file-name socket) flan-dev-start-timeout - flan-dev-daemon-buffer)) + (abbreviate-file-name socket) flan-start-timeout + flan-daemon-buffer)) (t (accept-process-output proc 0.05)))))) ;;;###autoload -(defun flan-dev (file &optional socket) +(defun flan (file &optional socket) "Start `flan dev' on FILE and connect to it when it is ready. -SOCKET defaults to `flan-dev-socket-name' beside FILE, which is where the +SOCKET defaults to `flan-socket-name' beside FILE, which is where the daemon puts it when it is not told otherwise. +Interactively, a buffer visiting a .flan file is started without asking; from +anywhere else, and under a prefix argument, the file is read from the +minibuffer. + When called interactively while a daemon this Emacs started is alive, asks before stopping it and switching programs. A noninteractive call still refuses: callers cannot silently discard a running program's state." (interactive ;; Starting a program is about the buffer the command was called from. - ;; Keeping [flan-dev--file] as DEFAULT here made a previous project win - ;; over the current buffer: after working on sand.flan, invoking this from + ;; Keeping [flan--file] as DEFAULT here made a previous project win over the + ;; current buffer: after working on sand.flan, invoking this from ;; ~/Development/gameboy/gameboy.flan still proposed (and could start) - ;; sand.flan. [flan-dev-restart-program] is the deliberate way to restart - ;; the previous program; an ordinary M-x command must not do that. + ;; sand.flan. [flan-restart-program] is the deliberate way to restart the + ;; previous program; an ordinary M-x command must not do that. + ;; + ;; A buffer already visiting a .flan file has answered the question, so it is + ;; taken rather than offered: the prompt had nothing to add but a keystroke + ;; and a chance to get it wrong. The prefix argument is how you say the + ;; buffer is not what you meant — and it still only changes what is asked, + ;; never which file the unasked case picks. (let ((file (and buffer-file-name (string-suffix-p ".flan" buffer-file-name) (expand-file-name buffer-file-name)))) - (list (read-file-name "flan dev: " - (and file (file-name-directory file)) - file t - (and file (file-name-nondirectory file)))))) - (when (process-live-p flan-dev--daemon) + (list (if (and file (not current-prefix-arg)) + file + (read-file-name "flan dev: " + (and file (file-name-directory file)) + file t + (and file (file-name-nondirectory file))))))) + (when (process-live-p flan--daemon) ;; `interactive' has already read FILE. Refusing only here used to make ;; that selection look as though it had been ignored: the old daemon kept ;; running, even though the minibuffer had just accepted a different @@ -788,35 +800,35 @@ refuses: callers cannot silently discard a running program's state." (if (y-or-n-p (format "Stop %s and start %s? " (abbreviate-file-name - (or flan-dev--file "the running program")) + (or flan--file "the running program")) (abbreviate-file-name (expand-file-name file)))) - (flan-dev-quit) + (flan-quit) (user-error "flan dev: keeping %s" (abbreviate-file-name - (or flan-dev--file "the running program")))) - (user-error "flan dev: already running on %s; M-x flan-dev-quit first" - (abbreviate-file-name (or flan-dev--socket "a socket"))))) + (or flan--file "the running program")))) + (user-error "flan dev: already running on %s; M-x flan-quit first" + (abbreviate-file-name (or flan--socket "a socket"))))) (let* ((file (expand-file-name file)) (socket (or socket - (expand-file-name flan-dev-socket-name + (expand-file-name flan-socket-name (file-name-directory file))))) (unless (file-exists-p file) (user-error "flan dev: no such file: %s" file)) - (setq flan-dev--file file) - (setq flan-dev--daemon-socket socket) - (setq flan-dev--daemon (flan-dev--start-daemon file socket)) - (flan-dev--connect-when-ready socket flan-dev--daemon) + (setq flan--file file) + (setq flan--daemon-socket socket) + (setq flan--daemon (flan--start-daemon file socket)) + (flan--connect-when-ready socket flan--daemon) ;; Connected by now, so the rest is what `flan-connect' does after opening: ;; learn what the program defines, and say what is on the other end. - (let ((r (flan-dev--request '(:op "describe")))) - (flan-dev-refresh-defs) + (let ((r (flan--request '(:op "describe")))) + (flan-refresh-defs) (message "flan dev: %s running (%d functions, %d globals)" (file-name-nondirectory file) (length (plist-get r :fns)) (length (plist-get r :globals)))) - flan-dev--daemon)) + flan--daemon)) ;;;###autoload -(defun flan-dev-quit () +(defun flan-quit () "Stop the daemon this Emacs started, and the program with it. `close' first, which is the daemon's own way out and lets it unlink its socket; the process is killed only if it does not take it. @@ -829,38 +841,38 @@ in another terminal turned one command into the end of two sessions — one of them belonging to somebody else's window. When the connection is not the daemon's, this closes the connection and says what it left running." (interactive) - (if (and (process-live-p flan-dev--daemon) - (process-live-p flan-dev--connection) - flan-dev--socket flan-dev--daemon-socket - (not (equal flan-dev--socket flan-dev--daemon-socket))) - (let ((theirs (abbreviate-file-name flan-dev--socket)) + (if (and (process-live-p flan--daemon) + (process-live-p flan--connection) + flan--socket flan--daemon-socket + (not (equal flan--socket flan--daemon-socket))) + (let ((theirs (abbreviate-file-name flan--socket)) (mine (abbreviate-file-name - (or flan-dev--file flan-dev--daemon-socket)))) + (or flan--file flan--daemon-socket)))) ;; `flan-disconnect' is exactly the right half: it closes, which the ;; daemon on the other end takes as the end of its session, and it ;; touches nothing this Emacs started. (flan-disconnect) (message - "flan dev: closed %s; %s is still running here — M-x flan-dev-quit again to stop it" + "flan dev: closed %s; %s is still running here — M-x flan-quit again to stop it" theirs mine)) - (unless (process-live-p flan-dev--daemon) + (unless (process-live-p flan--daemon) ;; A daemon started in a terminal is not this Emacs' to kill, and ;; `flan-disconnect' is the thing that ends one of those — it closes, which ;; the daemon takes as the end of the session. Saying so is better than ;; doing the same thing under a name that claims more than it did. (user-error "flan dev: no daemon started from Emacs%s" - (if (process-live-p flan-dev--connection) + (if (process-live-p flan--connection) "; M-x flan-disconnect ends the one you are connected to" ""))) - (let ((proc flan-dev--daemon)) - (when (process-live-p flan-dev--connection) - (ignore-errors (flan-dev--request '(:op "close"))) - (delete-process flan-dev--connection)) - (setq flan-dev--connection nil - flan-dev--socket nil - flan-dev--stopped nil) - (flan-dev--stop-polling) - (flan-dev--forget-defs) + (let ((proc flan--daemon)) + (when (process-live-p flan--connection) + (ignore-errors (flan--request '(:op "close"))) + (delete-process flan--connection)) + (setq flan--connection nil + flan--socket nil + flan--stopped nil) + (flan--stop-polling) + (flan--forget-defs) (when (process-live-p proc) ;; It has been told; give it a moment to go on its own before killing ;; it, so that it unlinks its socket and reaps its child itself. @@ -874,18 +886,18 @@ daemon's, this closes the connection and says what it left running." ;; saying "stopped" here would be the one place in this client where ;; a success message meant "probably". (delete-process proc) - (setq flan-dev--daemon nil - flan-dev--daemon-socket nil) + (setq flan--daemon nil + flan--daemon-socket nil) (force-mode-line-update t) (user-error "flan dev: the daemon would not close and was killed; its program may still be running"))) - (setq flan-dev--daemon nil - flan-dev--daemon-socket nil) + (setq flan--daemon nil + flan--daemon-socket nil) (force-mode-line-update t) (message "flan dev: stopped")))) ;;;###autoload -(defun flan-dev-restart-program () +(defun flan-restart-program () "Stop the program this Emacs started and start it again from source. For a change the running program cannot take: a struct whose layout moved, a @@ -901,27 +913,27 @@ The old daemon is waited for before the new one starts, because it unlinks the socket on its way out and would otherwise unlink the one its successor had just bound. If it will not close and has to be killed, this stops there rather than starting a second program on top of one that may still be -running — `flan-dev' then starts it again, on the same program." +running — `flan' then starts it again, on the same program." (interactive) - (unless (process-live-p flan-dev--daemon) + (unless (process-live-p flan--daemon) (user-error "flan dev: no daemon started from Emacs to restart")) ;; Refused rather than half-done: this ends a program and builds it again, ;; and with the connection attached to somebody else's daemon there is no ;; reading of it that leaves one session where there was one. - (when (and (process-live-p flan-dev--connection) - flan-dev--socket flan-dev--daemon-socket - (not (equal flan-dev--socket flan-dev--daemon-socket))) + (when (and (process-live-p flan--connection) + flan--socket flan--daemon-socket + (not (equal flan--socket flan--daemon-socket))) (user-error "flan dev: connected to %s, which is not the %s started here; M-x flan-connect to it first" - (abbreviate-file-name flan-dev--socket) - (abbreviate-file-name (or flan-dev--file flan-dev--daemon-socket)))) - (let ((file flan-dev--file) + (abbreviate-file-name flan--socket) + (abbreviate-file-name (or flan--file flan--daemon-socket)))) + (let ((file flan--file) ;; The daemon's socket and not the connection's: what is being ;; restarted is the program this Emacs started, and the connection may ;; by now be attached to a second one somewhere else. - (socket (or flan-dev--daemon-socket flan-dev--socket))) - (flan-dev-quit) ; returns only once it is really gone - (flan-dev file socket))) + (socket (or flan--daemon-socket flan--socket))) + (flan-quit) ; returns only once it is really gone + (flan file socket))) ;;; The modeline @@ -929,19 +941,19 @@ running — `flan-dev' then starts it again, on the same program." ;; permanent place on screen, because every other command in here is a lie ;; without it. Before this it was discovered by a command failing. -(defface flan-dev-live-face '((t :inherit success)) +(defface flan-live-face '((t :inherit success)) "Face for the modeline indicator when a program is connected." - :group 'flan-dev) + :group 'flan) -(defface flan-dev-lost-face '((t :inherit warning)) +(defface flan-lost-face '((t :inherit warning)) "Face for the modeline indicator when the daemon has gone away." - :group 'flan-dev) + :group 'flan) -(defface flan-dev-stopped-face '((t :inherit error)) +(defface flan-stopped-face '((t :inherit error)) "Face for the modeline indicator when the program is stopped at a break." - :group 'flan-dev) + :group 'flan) -(defun flan-dev-state () +(defun flan-state () "Whether a program is connected: `stopped', `parked', `live', `lost', or `off'. `lost' means there was one and the daemon is gone — a restart away, not a mistake, so it is distinguished from never having connected. `stopped' is a @@ -952,21 +964,21 @@ has *finished*: the process and every global in it are still there, and `stopped' wins over `parked' where both are somehow set, because a break is the state with something to answer in it." - (cond ((and (process-live-p flan-dev--connection) flan-dev--stopped) 'stopped) - ((and (process-live-p flan-dev--connection) flan-dev--parked) 'parked) - ((process-live-p flan-dev--connection) 'live) - (flan-dev--socket 'lost) + (cond ((and (process-live-p flan--connection) flan--stopped) 'stopped) + ((and (process-live-p flan--connection) flan--parked) 'parked) + ((process-live-p flan--connection) 'live) + (flan--socket 'lost) (t 'off))) -(defun flan-dev-mode-line () +(defun flan-mode-line () "The Flan connection indicator, for `mode-line-misc-info'." (when (derived-mode-p 'flan-mode 'flan-repl-mode) - (pcase (flan-dev-state) + (pcase (flan-state) ;; First, and it names the condition: a stopped program looks exactly ;; like a running one from anywhere else in Emacs, and the whole reason ;; the break loop is worth having is that someone notices it. - ('stopped (propertize (format " flan:stopped(%s)" flan-dev--stopped) - 'face 'flan-dev-stopped-face + ('stopped (propertize (format " flan:stopped(%s)" flan--stopped) + 'face 'flan-stopped-face 'help-echo "Stopped on an unhandled condition; C-c C-b to choose a restart")) ;; Before `live', and shown as its own word rather than as a shade of @@ -974,19 +986,19 @@ the state with something to answer in it." ;; a running one, and it is the indicator's job to be the thing that ;; notices. `stopped''s face is reused — both mean "connected, and not ;; going anywhere until you say so", which is what the colour is for. - ('parked (propertize " flan:parked" 'face 'flan-dev-stopped-face + ('parked (propertize " flan:parked" 'face 'flan-stopped-face 'help-echo "The program finished; C-c C-M-x runs it again, globals and all")) - ('live (propertize " flan:live" 'face 'flan-dev-live-face - 'help-echo (format "Connected to %s" flan-dev--socket))) - ('lost (propertize " flan:lost" 'face 'flan-dev-lost-face + ('live (propertize " flan:live" 'face 'flan-live-face + 'help-echo (format "Connected to %s" flan--socket))) + ('lost (propertize " flan:lost" 'face 'flan-lost-face 'help-echo (format "%s has gone away; the next command reconnects" - flan-dev--socket))) + flan--socket))) (_ (propertize " flan:off" 'face 'shadow 'help-echo "Not connected (C-c C-z)"))))) -;; Installed buffer-locally by `flan-dev-setup', not globally. A global entry +;; Installed buffer-locally by `flan-setup', not globally. A global entry ;; would evaluate on every redisplay of every buffer in the session — dired, ;; eshell, everything — to return nil, for someone who may never open a .flan ;; file at all. The `derived-mode-p' guard above stays anyway: cheap, and it @@ -1013,7 +1025,7 @@ the state with something to answer in it." ;; same decision: it is what you pick when none of the restarts is the answer. ;; It is last, and it is not the default. -(defun flan-dev--restart-candidates (restarts unreachable) +(defun flan--restart-candidates (restarts unreachable) "Label each of RESTARTS by its position, marking those in UNREACHABLE. An alist of label to index. The index leads the label because it is the identity: two entries may read the same and mean different frames." @@ -1027,27 +1039,27 @@ identity: two entries may read the same and mean different frames." i)) restarts))) -(defun flan-dev-restart-at (index name) +(defun flan-restart-at (index name) "Resume the stopped program at the restart at position INDEX. NAME is sent with it and is not the lookup: the program checks it against the name it holds at that position and refuses if the two have drifted apart, so a prompt cannot take a different restart than the one it showed." - (let ((r (flan-dev--request (list :op "restart-at" :index index :name name)))) + (let ((r (flan--request (list :op "restart-at" :index index :name name)))) (if (equal (plist-get r :status) "ok") (progn - ;; Accepted, not resumed — see `flan-dev-restart'. - (setq flan-dev--stopped nil) + ;; Accepted, not resumed — see `flan-restart'. + (setq flan--stopped nil) (force-mode-line-update t) (message "flan: %s — %s" name (or (plist-get r :note) "accepted"))) (user-error "flan: %s" (or (plist-get r :message) "refused"))))) -(defun flan-dev-restart (name) +(defun flan-restart (name) "Resume the stopped program at the restart called NAME. The first frame offering NAME, which is §4's own rule and therefore cannot reach a shadowed one. `flan-break' chooses by position instead; this is here for a name known in advance." - (interactive (list (completing-read "Restart: " (flan-dev-restarts) nil t))) - (let ((r (flan-dev--request (list :op "restart" :name name)))) + (interactive (list (completing-read "Restart: " (flan-restarts) nil t))) + (let ((r (flan--request (list :op "restart" :name name)))) (if (equal (plist-get r :status) "ok") (progn ;; Accepted, not resumed: the choice is validated against the stopped @@ -1057,7 +1069,7 @@ here for a name known in advance." ;; so until the poll after the one that agreed. Cleared here and ;; re-established by the next poll if the program is somehow still ;; there. - (setq flan-dev--stopped nil) + (setq flan--stopped nil) (force-mode-line-update t) (message "flan: %s — %s" name (or (plist-get r :note) "accepted"))) @@ -1072,7 +1084,7 @@ program opened a window, you closed it, `main' returned. The process did not go anywhere — it holds the compiler, the session and every global the run left — so this sends it round `main' once more and you get another window. It is what calling `(main)' at a Common Lisp or Clojure prompt does, -and it is the cheap counterpart of `flan-dev-restart-program', which throws +and it is the cheap counterpart of `flan-restart-program', which throws away the build and the state to get a new process. NOTHING IS RESET. The second run reads whatever the first left in the @@ -1083,33 +1095,33 @@ cannot be had back once this has zeroed something you wanted. Refused while the program is running, by the daemon, because two `main's in one process would be writing the same globals at once." (interactive) - (let ((r (flan-dev--request '(:op "rerun")))) + (let ((r (flan--request '(:op "rerun")))) (if (equal (plist-get r :status) "ok") (progn ;; Cleared here rather than waited for, exactly as a restart clears - ;; `flan-dev--stopped': the reply is written by the compiler thread + ;; `flan--stopped': the reply is written by the compiler thread ;; the instant it signals, and the modeline would otherwise say ;; `parked' until the poll after the one that agreed. - (setq flan-dev--parked nil) + (setq flan--parked nil) (force-mode-line-update t) (message "flan: %s" (or (plist-get r :note) "running again"))) (user-error "flan: %s" (or (plist-get r :message) "refused"))))) -(defun flan-dev-abort () +(defun flan-abort () "Let the stopped program die where it stopped. This ends `flan dev' too: the daemon owns the program's lifetime and has nothing left to serve once it has gone." (interactive) - (let ((r (flan-dev--request '(:op "abort")))) + (let ((r (flan--request '(:op "abort")))) (if (equal (plist-get r :status) "ok") - (progn (setq flan-dev--stopped nil) + (progn (setq flan--stopped nil) (force-mode-line-update t) (message "flan: %s" (or (plist-get r :note) "aborted"))) (user-error "flan: %s" (or (plist-get r :message) "refused"))))) -(defun flan-dev-restarts () +(defun flan-restarts () "The restart names the stopped program is offering, innermost first." - (let ((r (flan-dev--request '(:op "break")))) + (let ((r (flan--request '(:op "break")))) (unless (equal (plist-get r :status) "ok") (user-error "flan: %s" (or (plist-get r :message) "refused"))) (plist-get r :restarts))) @@ -1121,43 +1133,43 @@ Refuses while the program is running, by name: there is no restart stack to walk from a running program, and a prompt with nothing behind it is worse than being told so." (interactive) - (let* ((r (flan-dev--request '(:op "break")))) + (let* ((r (flan--request '(:op "break")))) (unless (equal (plist-get r :status) "ok") (user-error "flan: %s" (or (plist-get r :message) "refused"))) - (unless flan-dev--stopped + (unless flan--stopped (user-error "flan: the program is running; nothing is stopped")) (let* ((restarts (plist-get r :restarts)) (unreachable (append (plist-get r :unreachable) nil)) - (table (flan-dev--restart-candidates restarts unreachable)) + (table (flan--restart-candidates restarts unreachable)) (choice (completing-read - (format "flan: stopped on %s%s — " flan-dev--stopped + (format "flan: stopped on %s%s — " flan--stopped (if restarts "" " (no restarts are active)")) (append (mapcar #'car table) '("abort")) nil t)) (index (cdr (assoc choice table)))) (cond - ((equal choice "abort") (flan-dev-abort)) + ((equal choice "abort") (flan-abort)) ;; `require-match' over a table this built, so a choice outside it is ;; not something a person can type — but deriving the table wrongly ;; should say so rather than put nil on the wire as an index. ((null index) (user-error "flan: %s is not on the list" choice)) - (t (flan-dev-restart-at index (nth index restarts))))))) + (t (flan-restart-at index (nth index restarts))))))) ;;;###autoload (defun flan-show-output () "Show the running program's output, after collecting anything pending." (interactive) - (ignore-errors (flan-dev--request '(:op "describe"))) - (display-buffer (get-buffer-create flan-dev-output-buffer))) + (ignore-errors (flan--request '(:op "describe"))) + (display-buffer (get-buffer-create flan-output-buffer))) (defun flan-describe () "Report what the running program currently defines." (interactive) - (let ((r (flan-dev--request '(:op "describe")))) + (let ((r (flan--request '(:op "describe")))) ;; Three states and not two. `:alive' says whether there is still a ;; session; `:parked' says the program inside it has finished, which is a ;; thing "exited" used to be told and was wrong about — nothing exited, - ;; and saying so sent people to `flan-dev-restart-program' for something + ;; and saying so sent people to `flan-restart-program' for something ;; `flan-rerun' does without losing the build. (message "flan dev: %s, %d functions, %d globals" (cond ((plist-get r :parked) "parked") @@ -1176,7 +1188,7 @@ than being told so." ;; `forward-char' would also have walked into the next line on a column past ;; the end of a short one. -(defun flan-dev--parse-loc (loc) +(defun flan--parse-loc (loc) "Split LOC, a \"file:line:col\" string, into (FILE LINE COL), or nil." (when (and (stringp loc) (string-match "\\`\\(.*\\):\\([0-9]+\\):\\([0-9]+\\)\\'" loc)) @@ -1184,7 +1196,7 @@ than being told so." (string-to-number (match-string 2 loc)) (string-to-number (match-string 3 loc))))) -(defun flan-dev--position (line col) +(defun flan--position (line col) "Position of LINE and byte-column COL in the current buffer." (save-excursion (goto-char (point-min)) @@ -1198,23 +1210,23 @@ than being told so." ;; overshooting into the next line would point at innocent code. (min (or p eol) eol)))) -(defun flan-dev--wire-position (pos) +(defun flan--wire-position (pos) "POS as the (LINE COL) pair the daemon reads off a `:pause' field. -The inverse of `flan-dev--position', and byte-columns for the same reason: +The inverse of `flan--position', and byte-columns 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 at the position it was handed. The line is the buffer's own, which is what the daemon sees because -`flan-dev--text' pads the snippet back onto it." +`flan--text' pads the snippet back onto it." (list (line-number-at-pos pos) (save-excursion (goto-char pos) (1+ (- (position-bytes pos) (position-bytes (line-beginning-position))))))) -(defun flan-dev--buffer-visiting (file) +(defun flan--buffer-visiting (file) "The live buffer visiting FILE, or nil. Compared with `file-equal-p', so a symlinked or relative path still matches." (seq-find (lambda (b) @@ -1248,64 +1260,64 @@ Compared with `file-equal-p', so a symlinked or relative path still matches." ;; evaluation came from and a command somewhere else is not "doing something ;; else in that buffer". -(defface flan-dev-error-face +(defface flan-error-face '((t :inherit error :underline (:style wave))) "Face for the text an evaluation was rejected at." - :group 'flan-dev) + :group 'flan) -(defface flan-dev-error-message-face +(defface flan-error-message-face '((t :inherit error :height 0.9)) "Face for the message shown beside a rejected form." - :group 'flan-dev) + :group 'flan) -(defun flan-dev--error-overlays (&optional buffer) +(defun flan--error-overlays (&optional buffer) "The Flan error overlays in BUFFER, or in the current buffer." (with-current-buffer (or buffer (current-buffer)) - (seq-filter (lambda (o) (overlay-get o 'flan-dev-error)) + (seq-filter (lambda (o) (overlay-get o 'flan-error)) (overlays-in (point-min) (point-max))))) -(defun flan-dev-clear-errors (&optional buffer) +(defun flan-clear-errors (&optional buffer) "Remove Flan error overlays from BUFFER, or from the current buffer." (interactive) (with-current-buffer (or buffer (current-buffer)) - (remove-overlays (point-min) (point-max) 'flan-dev-error t) + (remove-overlays (point-min) (point-max) 'flan-error t) ;; With nothing left to clear there is nothing for the hook to do, and a ;; hook that stays installed after the last overlay is gone is the half of ;; this that quietly accumulates. - (remove-hook 'pre-command-hook #'flan-dev--clear-errors-on-command t))) + (remove-hook 'pre-command-hook #'flan--clear-errors-on-command t))) -(defun flan-dev--clear-errors-on-command () +(defun flan--clear-errors-on-command () "Take this buffer's error overlays down, as a `pre-command-hook'. Any command at all, because the overlay is about the evaluation that failed and not about the text: moving, typing and evaluating are all something else, and an overlay that survived a fix would be pointing at code that is no longer wrong." - (flan-dev-clear-errors)) + (flan-clear-errors)) -(defun flan-dev--show-error (loc msg) +(defun flan--show-error (loc msg) "Mark MSG at LOC, if LOC names a file some buffer is visiting. Returns non-nil when it put an overlay somewhere." - (let ((parts (flan-dev--parse-loc loc))) + (let ((parts (flan--parse-loc loc))) (when parts - (let ((buf (flan-dev--buffer-visiting (nth 0 parts)))) + (let ((buf (flan--buffer-visiting (nth 0 parts)))) (when buf (with-current-buffer buf - (flan-dev-clear-errors buf) - (let* ((beg (flan-dev--position (nth 1 parts) (nth 2 parts))) + (flan-clear-errors buf) + (let* ((beg (flan--position (nth 1 parts) (nth 2 parts))) (end (save-excursion (goto-char beg) (line-end-position))) (ov (make-overlay beg end buf t nil))) - (overlay-put ov 'flan-dev-error t) - (overlay-put ov 'face 'flan-dev-error-face) + (overlay-put ov 'flan-error t) + (overlay-put ov 'face 'flan-error-face) (overlay-put ov 'help-echo msg) (overlay-put ov 'evaporate nil) (overlay-put ov 'priority 100) (overlay-put ov 'after-string (propertize (concat " " msg) - 'face 'flan-dev-error-message-face)) + 'face 'flan-error-message-face)) ;; Local to this buffer, and installed only now that there is ;; something for it to remove. (add-hook 'pre-command-hook - #'flan-dev--clear-errors-on-command nil t) + #'flan--clear-errors-on-command nil t) ;; Point goes there too, but only in the buffer being looked at: ;; moving point in a buffer nobody is showing is a surprise the ;; next time it is visited. @@ -1331,35 +1343,35 @@ Returns non-nil when it put an overlay somewhere." ;; the whole buffer and therefore clears the whole buffer, which is right — ;; every declaration in it was just replaced. -(defface flan-dev-pause-face +(defface flan-pause-face ;; `warning', because `flan-cnr.el' already renders the `Pause' condition in ;; the conditions buffer as a warning and the two surfaces are about the same ;; stop. A breakpoint is not a failure. '((t :inherit warning :underline t)) "Face for a form the program will stop at." - :group 'flan-dev) + :group 'flan) -(defun flan-dev--pause-overlays (&optional buffer) +(defun flan--pause-overlays (&optional buffer) "The Flan pause overlays in BUFFER, or in the current buffer." (with-current-buffer (or buffer (current-buffer)) - (seq-filter (lambda (o) (overlay-get o 'flan-dev-pause)) + (seq-filter (lambda (o) (overlay-get o 'flan-pause)) (overlays-in (point-min) (point-max))))) -(defun flan-dev-clear-pause (&optional start end) +(defun flan-clear-pause (&optional start end) "Remove pause marks between START and END, or from the whole buffer. Interactively, the whole buffer: the point of asking is to be rid of them." (interactive) (remove-overlays (or start (point-min)) (or end (point-max)) - 'flan-dev-pause t)) + 'flan-pause t)) -(defun flan-dev--show-pause (beg end) +(defun flan--show-pause (beg end) "Mark BEG to END as a form the program will stop at." ;; The old mark first: re-marking a form that was already marked must leave ;; one overlay, not two stacked ones whose faces compound. - (flan-dev-clear-pause beg end) + (flan-clear-pause beg end) (let ((ov (make-overlay beg end nil t nil))) - (overlay-put ov 'flan-dev-pause t) - (overlay-put ov 'face 'flan-dev-pause-face) + (overlay-put ov 'flan-pause t) + (overlay-put ov 'face 'flan-pause-face) (overlay-put ov 'help-echo "flan: the program stops here; C-c C-c over it to clear") ;; Under the error overlays, which are about one command and should win @@ -1382,23 +1394,23 @@ Interactively, the whole buffer: the point of asking is to be rid of them." ;; accepted. A freshly installed `defn' completes immediately; nothing else ;; can have appeared in between, because this editor is the only client. -(defvar flan-dev--defs nil +(defvar flan--defs nil "What the running program defines: a list of (NAME KIND SIGNATURE LOC). LOC is the empty string where the daemon has none to give.") -(defun flan-dev--forget-defs () +(defun flan--forget-defs () "Drop what is known about the program's names." - (setq flan-dev--defs nil)) + (setq flan--defs nil)) -(defun flan-dev-refresh-defs () +(defun flan-refresh-defs () "Ask the running program what it defines, and remember it." (interactive) - (setq flan-dev--defs (plist-get (flan-dev--request '(:op "defs")) :defs)) + (setq flan--defs (plist-get (flan--request '(:op "defs")) :defs)) (when (called-interactively-p 'interactive) - (message "flan: %d names" (length flan-dev--defs))) - flan-dev--defs) + (message "flan: %d names" (length flan--defs))) + flan--defs) -(defun flan-dev--lookup (name) +(defun flan--lookup (name) "The entry for NAME, or nil. A name is looked up exactly first. Failing that, a buffer inside a package @@ -1407,21 +1419,21 @@ from the file's own package, which this end does not know — so a name that is the tail of exactly one program name resolves to it. Exactly one: several is ambiguous and resolving it by picking would be a guess about which function you meant." - (or (assoc name flan-dev--defs) + (or (assoc name flan--defs) (let ((tail (concat "/" name))) (let ((hits (seq-filter (lambda (d) (string-suffix-p tail (car d))) - flan-dev--defs))) + flan--defs))) (and (= 1 (length hits)) (car hits)))))) -(defun flan-dev--ambiguous (name) +(defun flan--ambiguous (name) "The entries whose name ends in NAME, when there is more than one." (let ((hits (seq-filter (lambda (d) (string-suffix-p (concat "/" name) (car d))) - flan-dev--defs))) + flan--defs))) (and (> (length hits) 1) hits))) ;;; eldoc -(defun flan-dev--enclosing-head () +(defun flan--enclosing-head () "The symbol heading the innermost form point is inside, or nil." (ignore-errors (save-excursion @@ -1430,13 +1442,13 @@ you meant." (goto-char (1+ open)) (and (looking-at "\\(?:\\sw\\|\\s_\\)+") (match-string-no-properties 0))))))) -(defun flan-dev-eldoc-function (callback &rest _) +(defun flan-eldoc-function (callback &rest _) "Give CALLBACK the signature of the name at point, from the running program. Falls back to the form point is inside, which is what you want while typing its arguments. Reads the cache only: eldoc runs on a timer and must not block on a socket or signal." - (let* ((name (or (thing-at-point 'symbol t) (flan-dev--enclosing-head))) - (d (and name (flan-dev--lookup name)))) + (let* ((name (or (thing-at-point 'symbol t) (flan--enclosing-head))) + (d (and name (flan--lookup name)))) (when d (funcall callback (concat (propertize (nth 2 d) 'face 'font-lock-function-name-face) @@ -1448,20 +1460,20 @@ block on a socket or signal." ;;; Completion -(defun flan-dev-completion-at-point () +(defun flan-completion-at-point () "Complete the name at point against the running program's own names. Nothing is offered when nothing is known — an empty table would look like \"no such name\" rather than \"not connected\"." - (when flan-dev--defs + (when flan--defs (let ((b (bounds-of-thing-at-point 'symbol))) (when b (list (car b) (cdr b) - (mapcar #'car flan-dev--defs) + (mapcar #'car flan--defs) :annotation-function - (lambda (n) (let ((d (assoc n flan-dev--defs))) + (lambda (n) (let ((d (assoc n flan--defs))) (and d (concat " " (nth 1 d))))) :company-docsig - (lambda (n) (let ((d (assoc n flan-dev--defs))) (and d (nth 2 d)))) + (lambda (n) (let ((d (assoc n flan--defs))) (and d (nth 2 d)))) ;; Not exclusive: dabbrev and the like still have something to ;; say about a name the program has not been told about yet. :exclusive 'no))))) @@ -1473,21 +1485,21 @@ Nothing is offered when nothing is known — an empty table would look like ;; global, and the prelude is a string in the compiler rather than a file, and ;; both of those must say so instead of opening an empty buffer. -(defun flan-dev-xref-backend () +(defun flan-xref-backend () "The xref backend for a buffer with a running Flan program behind it." - (and flan-dev--defs 'flan)) + (and flan--defs 'flan)) (cl-defmethod xref-backend-identifier-at-point ((_backend (eql flan))) (thing-at-point 'symbol t)) (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql flan))) - (mapcar #'car flan-dev--defs)) + (mapcar #'car flan--defs)) (cl-defmethod xref-backend-definitions ((_backend (eql flan)) identifier) - (let ((d (flan-dev--lookup identifier))) + (let ((d (flan--lookup identifier))) (cond ((null d) - (if-let ((hits (flan-dev--ambiguous identifier))) + (if-let ((hits (flan--ambiguous identifier))) (user-error "flan: %s could be %s; write the one you mean" identifier (string-join (mapcar #'car hits) " or ")) (user-error "flan: the running program defines no %s" identifier))) @@ -1498,7 +1510,7 @@ Nothing is offered when nothing is known — an empty table would look like (user-error "flan: %s is a %s, and the daemon reports no location for one" (car d) (nth 1 d))) (t - (let ((parts (flan-dev--parse-loc (nth 3 d)))) + (let ((parts (flan--parse-loc (nth 3 d)))) (cond ((null parts) (user-error "flan: the daemon gave %s an unreadable location: %s" @@ -1551,15 +1563,15 @@ Nothing is offered when nothing is known — an empty table would look like (defun flan-doc--goto (loc) "Visit LOC, a \"file:line:col\" the daemon gave for a definition." - (let ((parts (flan-dev--parse-loc loc))) + (let ((parts (flan--parse-loc loc))) (unless parts (user-error "flan: unreadable location: %s" loc)) (find-file-other-window (nth 0 parts)) - (goto-char (flan-dev--position (nth 1 parts) (nth 2 parts))))) + (goto-char (flan--position (nth 1 parts) (nth 2 parts))))) (defun flan-doc--where (d) "Insert where D is defined, or why that cannot be said." (let* ((loc (nth 3 d)) - (parts (and (not (equal loc "")) (flan-dev--parse-loc loc)))) + (parts (and (not (equal loc "")) (flan--parse-loc loc)))) (cond ;; Said, not omitted. A missing line reads as "it has no home"; the ;; truth is that Tast.global and Tast.extern carry no Loc, which is a @@ -1588,15 +1600,15 @@ not on one. Refuses a bare name that could be several packaged ones, in the same words `M-.' does: picking one would be a guess about which you meant." (interactive (list (or (thing-at-point 'symbol t) - (completing-read "Describe name: " (mapcar #'car flan-dev--defs) + (completing-read "Describe name: " (mapcar #'car flan--defs) nil t)))) - (unless flan-dev--defs + (unless flan--defs (user-error "flan: nothing is known about any name; connect first (C-c C-z)")) - (when (process-live-p flan-dev--connection) - (ignore-errors (flan-dev-refresh-defs))) - (let ((d (flan-dev--lookup name))) + (when (process-live-p flan--connection) + (ignore-errors (flan-refresh-defs))) + (let ((d (flan--lookup name))) (unless d - (if-let ((hits (flan-dev--ambiguous name))) + (if-let ((hits (flan--ambiguous name))) (user-error "flan: %s could be %s; write the one you mean" name (string-join (mapcar #'car hits) " or ")) (user-error "flan: the running program defines no %s" name))) @@ -1619,30 +1631,30 @@ same words `M-.' does: picking one would be a guess about which you meant." ;;; Wiring it into a buffer -(defun flan-dev-setup () +(defun flan-setup () "Give this buffer eldoc, completion and M-. against the running program. Installed from here rather than from `flan-mode', which must keep working for someone editing Flan with no program running and this file never loaded." - (add-hook 'completion-at-point-functions #'flan-dev-completion-at-point nil t) - (add-hook 'xref-backend-functions #'flan-dev-xref-backend nil t) + (add-hook 'completion-at-point-functions #'flan-completion-at-point nil t) + (add-hook 'xref-backend-functions #'flan-xref-backend nil t) ;; Registered, not switched on. Contributing a source is this file's ;; business; whether eldoc runs at all is the user's, and turning it on for ;; someone who has `global-eldoc-mode' off is overruling a decision they made ;; on purpose. It is on by default, so this is what almost everyone gets. - (add-hook 'eldoc-documentation-functions #'flan-dev-eldoc-function nil t) - (unless (member '(:eval (flan-dev-mode-line)) mode-line-misc-info) + (add-hook 'eldoc-documentation-functions #'flan-eldoc-function nil t) + (unless (member '(:eval (flan-mode-line)) mode-line-misc-info) ;; Appended rather than prepended: the least urgent thing in the line. (setq-local mode-line-misc-info - (append mode-line-misc-info '((:eval (flan-dev-mode-line))))))) + (append mode-line-misc-info '((:eval (flan-mode-line))))))) -(add-hook 'flan-mode-hook #'flan-dev-setup) +(add-hook 'flan-mode-hook #'flan-setup) ;; Buffers that were already in flan-mode when this file loaded: the client is ;; autoloaded on first use, so by the time it arrives the file being edited has ;; long since had its mode hooks run. (dolist (b (buffer-list)) (with-current-buffer b - (when (derived-mode-p 'flan-mode) (flan-dev-setup)))) + (when (derived-mode-p 'flan-mode) (flan-setup)))) ;;; Evaluating @@ -1652,17 +1664,17 @@ someone editing Flan with no program running and this file never loaded." ;; what was typed: the daemon is the one that knows which of them it installed, ;; and a `defvar' the program already had is not among them. -(defun flan-dev--names-phrase (names fallback) +(defun flan--names-phrase (names fallback) "NAMES as a phrase for the echo area, or FALLBACK when there are none. Long lists are counted and then sampled: an echo area truncated in the middle of the tenth name tells you neither how many there were nor which." (cond ((null names) fallback) - ((<= (length names) flan-dev-names-shown) (string-join names ", ")) + ((<= (length names) flan-names-shown) (string-join names ", ")) (t (format "%d names (%s, …)" (length names) - (string-join (seq-take names flan-dev-names-shown) ", "))))) + (string-join (seq-take names flan-names-shown) ", "))))) -(defun flan-dev--report (reply what) +(defun flan--report (reply what) "Report REPLY, describing WHAT was sent." (if (equal (plist-get reply :status) "ok") (let ((fns (plist-get reply :fns)) @@ -1674,11 +1686,11 @@ of the tenth name tells you neither how many there were nor which." ;; evaluation already did — and kept because it is the claim being ;; made, not the mechanism: an accepted evaluation is never left with a ;; rejection drawn over it. - (flan-dev-clear-errors) + (flan-clear-errors) ;; ...and a name that was just installed should complete, and have a ;; signature, from this moment rather than from the next connect. - (when (or fns names) (ignore-errors (flan-dev-refresh-defs))) - (when flan-dev-echo-result + (when (or fns names) (ignore-errors (flan-refresh-defs))) + (when flan-echo-result (cond ;; An expression's value, rendered inside the running program — ;; nothing was marshalled back, because nothing could be. @@ -1686,7 +1698,7 @@ of the tenth name tells you neither how many there were nor which." ;; The daemon accepted it and had nothing to send. Say so rather ;; than claiming an install that did not happen. (note (message "flan: %s — %s" - (flan-dev--names-phrase names what) note)) + (flan--names-phrase names what) note)) (t ;; `:fns' are the bodies that were installed and `:names' is ;; everything the evaluation declared; a buffer of five functions @@ -1702,22 +1714,22 @@ of the tenth name tells you neither how many there were nor which." ;; that `C-x C-e' reaches this path too, that sentence is also how ;; you tell an installed declaration from an expression's `=>'. (message "flan: %s installed in %.0f ms%s" - (flan-dev--names-phrase (or fns names) what) + (flan--names-phrase (or fns names) what) (or (plist-get reply :ms) 0) (let ((vars (and fns (seq-difference names fns)))) (if vars (format " (also %s)" - (flan-dev--names-phrase vars "")) + (flan--names-phrase vars "")) ""))))))) ;; The daemon reports where, so mark it there. This must not itself ;; signal: the error the caller is owed is the daemon's, and losing it to a ;; bad location would report the wrong thing entirely. (let ((loc (plist-get reply :loc)) (msg (plist-get reply :message))) - (ignore-errors (flan-dev--show-error loc (or msg "rejected"))) + (ignore-errors (flan--show-error loc (or msg "rejected"))) (user-error "flan: %s%s" (or msg "rejected") (if loc (format " (%s)" loc) ""))))) -(defun flan-dev--eval (code what &optional start end pause) +(defun flan--eval (code what &optional start end pause) "Send CODE to the running program. WHAT names it for the echo area. START and END, when given, are the region it came from, flashed on success. PAUSE, when given, is (BEG . END): the bounds of the form inside CODE the @@ -1726,15 +1738,15 @@ against the location the reader attached to that form — and END is what the mark is drawn over here. Nothing is inserted in the buffer; see docs/DISCUSS.md §9." (let ((reply - (flan-dev--request + (flan--request ;; buffer-file-name so an error points at the file being edited ;; rather than at the daemon's placeholder. (append (list :op "eval" :code code :file (or buffer-file-name "")) (when pause - (list :pause (flan-dev--wire-position (car pause)))))))) - (flan-dev--report reply what) - ;; `flan-dev--report' signals on a rejection, so reaching here means it + (list :pause (flan--wire-position (car pause)))))))) + (flan--report reply what) + ;; `flan--report' signals on a rejection, so reaching here means it ;; landed. Flashing the text that was sent answers "which form did that ;; take?" — the question the echo area cannot, because point may be nowhere ;; near the defn `beginning-of-defun' actually found. @@ -1747,11 +1759,11 @@ mark is drawn over here. Nothing is inserted in the buffer; see docs/DISCUSS.md ;; overlay is the only thing left claiming a breakpoint. (cond ((and pause (plist-get reply :pause)) - (flan-dev--show-pause (car pause) (cdr pause))) - ((and start end) (flan-dev-clear-pause start end))) + (flan--show-pause (car pause) (cdr pause))) + ((and start end) (flan-clear-pause start end))) reply)) -(defun flan-dev--text (start end) +(defun flan--text (start end) "The buffer text from START to END, on the line it is actually written on. The daemon reads what it is sent starting at line 1, so a form taken from the @@ -1763,10 +1775,10 @@ columns already were, because a top-level form starts at column 1." (concat (make-string (1- (line-number-at-pos start)) ?\n) (buffer-substring-no-properties start end))) -(defun flan-dev--text-at (start end) +(defun flan--text-at (start end) "The buffer text from START to END, on the line AND column it is written at. -`flan-dev--text' pads lines only, and says why it needs nothing more: a +`flan--text' pads lines only, and says why it needs nothing more: a top-level form starts at column 1, so the columns already agreed. A macro call does not. It is written somewhere inside a `defn', and a refusal the daemon reports against it — a macro that never settles is the one that @@ -1775,7 +1787,7 @@ the snippet and drawn at the start of the line. Leading newlines and leading spaces are both whitespace the reader skips, so padding with each is the whole fix. Byte columns, for the reason -`flan-dev--wire-position' gives: the reader walks the source a byte at a time, +`flan--wire-position' gives: the reader walks the source a byte at a time, and a space is one byte, so a byte count is exactly how many to write." (save-excursion (goto-char start) @@ -1785,7 +1797,7 @@ and a space is one byte, so a byte count is exactly how many to write." ?\s) (buffer-substring-no-properties start end)))) -(defun flan-dev--defun-bounds () +(defun flan--defun-bounds () "Bounds of the top-level form containing or preceding point, as (START . END)." (save-excursion (end-of-defun) @@ -1793,12 +1805,12 @@ and a space is one byte, so a byte count is exactly how many to write." (beginning-of-defun) (cons (point) end)))) -(defun flan-dev--defun-at-point () +(defun flan--defun-at-point () "The text of the top-level form containing or preceding point." - (let ((b (flan-dev--defun-bounds))) + (let ((b (flan--defun-bounds))) (buffer-substring-no-properties (car b) (cdr b)))) -(defun flan-dev--pause-bounds (b arg) +(defun flan--pause-bounds (b arg) "Bounds of the form to mark inside the defun B, for prefix ARG, or nil. Two of §9's three targets, off the same key. One `C-u' marks *the form point @@ -1832,9 +1844,9 @@ is told where the form is and splices the call in after parsing, so every location in the file stays where it was. The mark sticks until the same form is evaluated without a prefix." (interactive "P") - (let* ((b (flan-dev--defun-bounds)) - (pause (flan-dev--pause-bounds b arg))) - (flan-dev--eval (flan-dev--text (car b) (cdr b)) "form" (car b) (cdr b) + (let* ((b (flan--defun-bounds)) + (pause (flan--pause-bounds b arg))) + (flan--eval (flan--text (car b) (cdr b)) "form" (car b) (cdr b) pause))) ;;;###autoload @@ -1843,14 +1855,14 @@ is evaluated without a prefix." One module, not one per form: a var and the function that uses it have to arrive in the same load or the first refers to storage that does not exist." (interactive) - (flan-dev--eval (buffer-substring-no-properties (point-min) (point-max)) + (flan--eval (buffer-substring-no-properties (point-min) (point-max)) (buffer-name)) - ;; `flan-dev--eval' signals on a rejection, so reaching here means every + ;; `flan--eval' signals on a rejection, so reaching here means every ;; declaration in the buffer was just replaced by an unmarked one — and ;; therefore that every mark in it is gone. Done here rather than by passing ;; bounds, because those are also what gets flashed and pulsing a whole ;; buffer is not feedback, it is a flicker. - (flan-dev-clear-pause)) + (flan-clear-pause)) ;; Which of the two evaluators `C-x C-e' runs is decided by the form it would ;; send, not by where the cursor is sitting. Point inside a `defn' body, on @@ -1871,12 +1883,12 @@ arrive in the same load or the first refers to storage that does not exist." ;; the expression evaluator, which is the bug being fixed; a package ;; declaration is a statement about the file being read and not a change to ;; make to a running program. -(defconst flan-dev--declaration-heads +(defconst flan--declaration-heads '("defmacro" "defn" "defvar" "defconst" "defstruct" "defunion" "defenum" "defalias" "import" "declare" "declare-c") "Heads whose form is a declaration, and never an expression.") -(defun flan-dev--declaration-before-point () +(defun flan--declaration-before-point () "The top-level declaration `C-x C-e' would send, as (HEAD START END), or nil. Two questions, and both have to answer yes. The depth at START says whether @@ -1923,7 +1935,7 @@ make." (not (nth 4 state)) ; inside a comment (looking-at "([ \t\n]*\\(\\(?:\\sw\\|\\s_\\)+\\)") (member (match-string-no-properties 1) - flan-dev--declaration-heads) + flan--declaration-heads) (list (match-string-no-properties 1) start end)))) (scan-error nil))))) @@ -1933,14 +1945,14 @@ make." An expression is compiled into a thunk the program runs at its next frame boundary, and its value is shown. A top-level declaration — a `defvar', a -`defn', anything in `flan-dev--declaration-heads' — is compiled and installed +`defn', anything in `flan--declaration-heads' — is compiled and installed instead, and the reply names what changed. The compiler has always had both paths; this key used to reach only the first, so a `defvar' typed at the top of a file came back as \"defvar is a top-level declaration, not an expression\" and the only way to evaluate it was `C-c C-c'. That was an editor artifact and not a property of the language. -`flan-dev--declaration-before-point' decides which, off the form that would be +`flan--declaration-before-point' decides which, off the form that would be sent rather than off where point is; `C-c C-c' is unchanged and stays the explicit \"reload the definition I am standing in\" command, which is still the one to use from inside a body. @@ -1956,14 +1968,14 @@ An expression's mark does not stick and cannot — a thunk is built and thrown away, so there is no declaration for it to live in — while a declaration's does, until the same form is evaluated again without a prefix." (interactive "P") - (let ((decl (flan-dev--declaration-before-point))) + (let ((decl (flan--declaration-before-point))) (if decl (pcase-let ((`(,head ,start ,end) decl)) - ;; `flan-dev--text', not `flan-dev--text-at': a top-level form starts + ;; `flan--text', not `flan--text-at': a top-level form starts ;; at column 1, so the line padding is the whole fix and the columns ;; already agree. HEAD is what the echo area falls back to when the ;; daemon installed no bodies and declared no names. - (flan-dev--eval (flan-dev--text start end) head start end + (flan--eval (flan--text start end) head start end (and arg (cons start end)))) (let* ((start (save-excursion (condition-case nil (backward-sexp) (scan-error nil)) @@ -1979,8 +1991,8 @@ does, until the same form is evaluated again without a prefix." ;; file and the form meant was the one *after* it. (when (= start (point)) (user-error "flan: no form before point to evaluate")) - (flan-dev--report - (flan-dev--request + (flan--report + (flan--request (append (list :op "eval-expr" :code code :file (or buffer-file-name "")) (when arg (list :pause t)))) @@ -1990,7 +2002,7 @@ does, until the same form is evaluated again without a prefix." (defun flan-eval-region (start end) "Recompile the top-level forms between START and END." (interactive "r") - (flan-dev--eval (flan-dev--text start end) "region" start end)) + (flan--eval (flan--text start end) "region" start end)) ;;; Disassembly @@ -2048,25 +2060,25 @@ tail of exactly one packaged name, because a buffer inside a package writes (completing-read "Disassemble: " (mapcar #'car (seq-filter (lambda (d) (equal (nth 1 d) "fn")) - flan-dev--defs)) + flan--defs)) nil t nil nil (and (fboundp 'flan-current-defun-name) (flan-current-defun-name)))) current-prefix-arg)) - (when (process-live-p flan-dev--connection) - (ignore-errors (flan-dev-refresh-defs))) - (let* ((d (flan-dev--lookup name)) + (when (process-live-p flan--connection) + (ignore-errors (flan-refresh-defs))) + (let* ((d (flan--lookup name)) ;; Ambiguity is refused here rather than sent: the daemon would find ;; no such name and say so, which is true and useless — it is this end ;; that knows the buffer wrote a short name and that several program ;; names end in it. (_ (unless d - (when-let ((hits (flan-dev--ambiguous name))) + (when-let ((hits (flan--ambiguous name))) (user-error "flan: %s could be %s; write the one you mean" name (string-join (mapcar #'car hits) " or "))))) (full (if d (nth 0 d) name)) (form (if ir "ir" "asm")) - (r (flan-dev--request (list :op "disassemble" :name full :form form)))) + (r (flan--request (list :op "disassemble" :name full :form form)))) (unless (equal (plist-get r :status) "ok") (user-error "flan: %s" (or (plist-get r :message) "refused"))) (with-current-buffer (get-buffer-create flan-disassembly-buffer) @@ -2104,7 +2116,7 @@ that the IR half is findable by name rather than only by a modifier." (completing-read "LLVM IR for: " (mapcar #'car (seq-filter (lambda (d) (equal (nth 1 d) "fn")) - flan-dev--defs)) + flan--defs)) nil t)))) (flan-disassemble name t)) @@ -2143,7 +2155,7 @@ that the IR half is findable by name rather than only by a modifier." "Where `flan-macroexpand' draws." :type 'string) -(defvar flan-macroexpand-request-function #'flan-dev--request +(defvar flan-macroexpand-request-function #'flan--request "How the macroexpansion buffer reaches the daemon. One plist in, the reply plist out. A variable for `flan-cnr-request-function''s reason: so the renderer can be driven from a fixture.") @@ -2184,7 +2196,7 @@ sends padded text." ;; a ring, and both name a macro at a location — so mark it, exactly as ;; a refused evaluation is marked. This must not itself signal: the ;; error the caller is owed is the daemon's. - (ignore-errors (flan-dev--show-error loc (or msg "refused"))) + (ignore-errors (flan--show-error loc (or msg "refused"))) (user-error "flan: %s%s" (or msg "refused") (if loc (format " (%s)" loc) "")))) r)) @@ -2257,7 +2269,7 @@ rather than with what is typed." ;; Padded onto its own line *and column*, unlike `C-x C-e', because ;; the refusals this path can get name a location inside the snippet ;; and a macro call is written well inside a line. - (code (flan-dev--text-at (car b) (cdr b))) + (code (flan--text-at (car b) (cdr b))) (r (flan-macroexpand--ask code file all))) (flan-macroexpand--render r (buffer-substring-no-properties (car b) (cdr b)) file all) @@ -2378,7 +2390,7 @@ back to the daemon say why they will not. (defun flan-allocations--show (op title) "Ask the daemon for OP and show its rows under TITLE." - (let ((r (flan-dev--request (list :op op)))) + (let ((r (flan--request (list :op op)))) (unless (equal (plist-get r :status) "ok") (user-error "flan: %s" (or (plist-get r :message) "refused"))) (let ((rows (plist-get r :types)) @@ -2436,5 +2448,5 @@ can print the same breakdown to stderr under FLAN_DEV_LEAKS." (interactive) (flan-allocations--show "leaks" "Still held, by type")) -(provide 'flan-dev) -;;; flan-dev.el ends here +(provide 'flan) +;;; flan.el ends here diff --git a/emacs/test-flan-cider.el b/emacs/test-flan-cider.el index 3efd739..dcc18a1 100644 --- a/emacs/test-flan-cider.el +++ b/emacs/test-flan-cider.el @@ -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. diff --git a/emacs/test-flan-watch.el b/emacs/test-flan-watch.el index f2e18a9..31ff470 100644 --- a/emacs/test-flan-watch.el +++ b/emacs/test-flan-watch.el @@ -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)))) diff --git a/emacs/test-flan-dev.el b/emacs/test-flan.el similarity index 86% rename from emacs/test-flan-dev.el rename to emacs/test-flan.el index 89e60e0..d650ccc 100644 --- a/emacs/test-flan-dev.el +++ b/emacs/test-flan.el @@ -1,6 +1,6 @@ -;;; test-flan-dev.el --- Drive the client against a running program -*- lexical-binding: t; -*- +;;; test-flan.el --- Drive the client against a running program -*- lexical-binding: t; -*- -;; Run as: emacs -Q --batch -L emacs -l emacs/test-flan-dev.el -- +;; Run as: emacs -Q --batch -L emacs -l emacs/test-flan.el -- ;; ;; This is the client half of test_dev.ml. The OCaml test proves the daemon ;; answers correctly; this proves the elisp actually talks to it — the framing, @@ -11,7 +11,7 @@ ;;; Code: (require 'flan-mode) -(require 'flan-dev) +(require 'flan) (require 'flan-repl) (require 'flan-watch) (require 'flan-lower) @@ -52,53 +52,53 @@ is written instead — the real `message' call the real command makes." (test-flan--check "flan-mode is on for a .flan file" (eq major-mode 'flan-mode)) (test-flan--check "the modeline says so before connecting" - (and (eq (flan-dev-state) 'off) - (string-match-p "off" (flan-dev-mode-line)))) + (and (eq (flan-state) 'off) + (string-match-p "off" (flan-mode-line)))) (flan-connect socket) - (test-flan--check "connected" (process-live-p flan-dev--connection)) + (test-flan--check "connected" (process-live-p flan--connection)) (test-flan--check "the modeline says a program is there" - (and (eq (flan-dev-state) 'live) - (string-match-p "live" (flan-dev-mode-line)))) + (and (eq (flan-state) 'live) + (string-match-p "live" (flan-mode-line)))) (test-flan--check "and says nothing in a buffer that is not Flan's" - (with-temp-buffer (null (flan-dev-mode-line)))) + (with-temp-buffer (null (flan-mode-line)))) ;; Buffer-locally, so that someone who loads this and never opens a .flan ;; file is not evaluating it on every redisplay of every buffer they have. (test-flan--check "the indicator is in this buffer's modeline" - (member '(:eval (flan-dev-mode-line)) mode-line-misc-info)) + (member '(:eval (flan-mode-line)) mode-line-misc-info)) (test-flan--check "and not in everyone else's" (with-temp-buffer - (not (member '(:eval (flan-dev-mode-line)) + (not (member '(:eval (flan-mode-line)) mode-line-misc-info)))) ;; A daemon restarted while Emacs was not looking is the ordinary case. The ;; socket outlives this connection, so dropping the process and asking again ;; is the same situation the client meets after a restart, and it must come ;; back rather than fail. - (delete-process flan-dev--connection) + (delete-process flan--connection) (test-flan--check "a dead connection reads as lost, not as never-connected" - (and (eq (flan-dev-state) 'lost) - (string-match-p "lost" (flan-dev-mode-line)))) - (let ((r (flan-dev--request '(:op "describe")))) + (and (eq (flan-state) 'lost) + (string-match-p "lost" (flan-mode-line)))) + (let ((r (flan--request '(:op "describe")))) (test-flan--check "the next request reconnects on its own" - (and (process-live-p flan-dev--connection) + (and (process-live-p flan--connection) (member "step" (plist-get r :fns))))) ;; ...and knows the program's names again. An empty cache after a reconnect ;; is honest but silent: eldoc goes quiet and M-. falls through to another ;; backend, with nothing said about why. (test-flan--check "and knows the program's names again" - (assoc "step" flan-dev--defs)) + (assoc "step" flan--defs)) ;; But a socket nobody is listening on is refused by name, rather than ;; retried forever or reported as some other failure. - (let ((flan-dev--connection nil) - (flan-dev--socket "/nonexistent/flan-dev-not-here.sock") + (let ((flan--connection nil) + (flan--socket "/nonexistent/flan-not-here.sock") (raised nil)) - (condition-case err (flan-dev--request '(:op "describe")) + (condition-case err (flan--request '(:op "describe")) (error (setq raised (error-message-string err)))) (test-flan--check "a socket that is gone is refused by name" (and raised - (string-match-p "flan-dev-not-here.sock" raised) + (string-match-p "flan-not-here.sock" raised) (string-match-p "nothing is listening" raised)))) ;; ── The two frames a real daemon does not send ──────────────────────── @@ -122,15 +122,15 @@ is written instead — the real `message' call the real command makes." ;; buffer, and will never say anything of its own. (stand-in (start-process "flan-stand-in" buf "cat"))) (set-process-query-on-exit-flag stand-in nil) - ;; Unibyte, as `flan-dev--open' makes it: the framing counts bytes, and + ;; Unibyte, as `flan--open' makes it: the framing counts bytes, and ;; `position-bytes' on a multibyte buffer counts something else. (with-current-buffer buf (set-buffer-multibyte nil)) (unwind-protect - (let ((flan-dev-reply-timeout 0.3)) + (let ((flan-reply-timeout 0.3)) ;; Announced 40 bytes, wrote 12, went quiet. (with-current-buffer buf (insert "40\n(:status \"o")) (let ((raised nil)) - (condition-case err (flan-dev--read-reply stand-in) + (condition-case err (flan--read-reply stand-in) (error (setq raised (error-message-string err)))) (test-flan--check "a body that stalls is a timeout, said in words" (and raised (string-match-p "no reply in" raised))) @@ -146,17 +146,17 @@ is written instead — the real `message' call the real command makes." (insert "3\n(:a") (insert "14\n(:status \"ok\")")) (let ((raised nil)) - (condition-case err (flan-dev--take-reply stand-in) + (condition-case err (flan--take-reply stand-in) (error (setq raised (error-message-string err)))) (test-flan--check "a payload that will not read signals" raised) (test-flan--check "and is consumed, so the reply behind it arrives" - (equal (flan-dev--take-reply stand-in) + (equal (flan--take-reply stand-in) '(:status "ok"))))) (delete-process stand-in) (kill-buffer buf))) - (let ((r (flan-dev--request '(:op "describe")))) + (let ((r (flan--request '(:op "describe")))) (test-flan--check "describe lists the program's functions" (member "step" (plist-get r :fns))) (test-flan--check "describe lists the program's globals" @@ -167,41 +167,41 @@ is written instead — the real `message' call the real command makes." ;; `main' again. The client has to be able to *say* that, because from ;; anywhere else in Emacs a finished program and a running one look the same. ;; - ;; Driven through `flan-dev--absorb' with a made-up reply rather than by + ;; Driven through `flan--absorb' with a made-up reply rather than by ;; waiting for this program to finish. What is under test is the client's ;; reading of `:parked', which is a decision it makes about a plist; making ;; the real program park first would put the daemon, the agent and a ;; condition variable between the question and the answer, and test_dev.ml ;; already does that end to end. (let ((said (test-flan--said - (flan-dev--absorb '(:status "ok" :stopped nil :parked t))))) + (flan--absorb '(:status "ok" :stopped nil :parked t))))) (test-flan--check "a reply that says parked makes the client say so" - (and (eq (flan-dev-state) 'parked) - (string-match-p "parked" (flan-dev-mode-line)))) + (and (eq (flan-state) 'parked) + (string-match-p "parked" (flan-mode-line)))) ;; Once, on the edge, and naming the way out: the poll runs every second ;; and the whole point of the message is that it is read. (test-flan--check "and says how to get the program back, once" (and said (string-match-p "runs it again" said) (null (test-flan--said - (flan-dev--absorb + (flan--absorb '(:status "ok" :stopped nil :parked t))))))) ;; Stopped is not parked and must win where both arrive: a break has ;; restarts to choose and is the state with something to answer in it. - (flan-dev--absorb '(:status "ok" :stopped t :condition "Missing" :parked t)) + (flan--absorb '(:status "ok" :stopped t :condition "Missing" :parked t)) (test-flan--check "a stopped program reads as stopped even while parked is set" - (eq (flan-dev-state) 'stopped)) - (flan-dev--absorb '(:status "ok" :stopped nil :parked nil)) + (eq (flan-state) 'stopped)) + (flan--absorb '(:status "ok" :stopped nil :parked nil)) (test-flan--check "and both clear again" - (eq (flan-dev-state) 'live)) + (eq (flan-state) 'live)) ;; `flan-describe' had two words for three states, and "exited" was the ;; wrong one of them: nothing exited, and being told so is what sent people - ;; to `flan-dev-restart-program' for a thing `flan-rerun' does without + ;; to `flan-restart-program' for a thing `flan-rerun' does without ;; losing the build. (test-flan--check "flan-describe names the parked state rather than calling it exited" (let ((said (test-flan--said - (cl-letf (((symbol-function 'flan-dev--request) + (cl-letf (((symbol-function 'flan--request) (lambda (&rest _) '(:status "ok" :fns ("step") :globals ("ticks") :alive t :parked t)))) @@ -214,7 +214,7 @@ is written instead — the real `message' call the real command makes." (test-flan--check "a parked refusal reaches the user with the way out in it" (let ((raised nil)) - (cl-letf (((symbol-function 'flan-dev--request) + (cl-letf (((symbol-function 'flan--request) (lambda (&rest _) '(:status "error" :message "a backtrace is the frames of a stopped program, and a parked one has no frames at all; the program has finished and its process is parked — M-x flan-rerun starts it again")))) @@ -228,7 +228,7 @@ is written instead — the real `message' call the real command makes." ;; lists in it, so `read' on this side is doing something it does nowhere ;; else — and the daemon answers it without asking the program anything. (require 'flan-cnr) - (let ((flan-cnr-request-function #'flan-dev--request)) + (let ((flan-cnr-request-function #'flan--request)) (test-flan--check "a struct's fields come back named and typed" (equal (flan-cnr-layout "Missing") '(("id" "i32" nil)))) (test-flan--check "and a type the daemon cannot place is nil, not an error" @@ -243,7 +243,7 @@ is written instead — the real `message' call the real command makes." (save-excursion (search-forward "(+ ticks 1)") (replace-match "(+ ticks 41)")) - (let ((form (flan-dev--defun-at-point))) + (let ((form (flan--defun-at-point))) (test-flan--check "the form at point is the defn" (and (string-prefix-p "(defn step" (string-trim form)) (string-match-p "41" form)))) @@ -252,7 +252,7 @@ is written instead — the real `message' call the real command makes." ;; And an error: the daemon answers with a location, the client raises. (let ((raised nil)) (condition-case err - (flan-dev--eval "(defn step [] i64 nonsense)" "form") + (flan--eval "(defn step [] i64 nonsense)" "form") (user-error (setq raised (error-message-string err)))) (test-flan--check "a form that does not check is reported" (and raised (string-match-p "unknown name" raised)))) @@ -269,11 +269,11 @@ is written instead — the real `message' call the real command makes." (match-beginning 0))) (eol (save-excursion (goto-char (point-min)) (line-end-position)))) (test-flan--check "a byte column lands on the right character" - (= (flan-dev--position + (= (flan--position 2 (1+ (string-bytes "(defn wörld [] i64 "))) want)) (test-flan--check "a column past the end of a line is clamped to it" - (= (flan-dev--position 1 500) eol)))) + (= (flan--position 1 500) eol)))) ;; A rejected form is marked where it is, not only in the echo area. The ;; daemon numbers lines from the start of what it was sent, so a form taken @@ -287,7 +287,7 @@ is written instead — the real `message' call the real command makes." (search-forward "(+ ticks 41)") (replace-match "(+ ticks nonsense)")) (ignore-errors (flan-eval-defun)) - (let ((ovs (seq-filter (lambda (o) (overlay-get o 'flan-dev-error)) + (let ((ovs (seq-filter (lambda (o) (overlay-get o 'flan-error)) (overlays-in (point-min) (point-max))))) (test-flan--check "a rejected form gets exactly one error overlay" (= 1 (length ovs))) @@ -317,7 +317,7 @@ is written instead — the real `message' call the real command makes." ;; which names it installed. (let ((said (test-flan--said (flan-eval-defun)))) (test-flan--check "an accepted evaluation clears it" - (null (seq-filter (lambda (o) (overlay-get o 'flan-dev-error)) + (null (seq-filter (lambda (o) (overlay-get o 'flan-error)) (overlays-in (point-min) (point-max))))) (test-flan--check "and says which name landed" (and said (string-match-p "\\_" said))) @@ -327,7 +327,7 @@ is written instead — the real `message' call the real command makes." ;; A declaration the program already has installs nothing, and must say so ;; rather than reporting a time for a build that did not happen. (let ((said (test-flan--said - (flan-dev--eval "(defvar ticks i64)" "form")))) + (flan--eval "(defvar ticks i64)" "form")))) (test-flan--check "an evaluation with nothing to install says so" (and said (string-match-p "nothing to install" said) (not (string-match-p "installed" said))))) @@ -335,29 +335,29 @@ is written instead — the real `message' call the real command makes." ;; Many names are counted and sampled. An echo area truncated in the middle ;; of the tenth name says neither how many there were nor which. (test-flan--check "a long list of names is counted, not cut off" - (equal (flan-dev--names-phrase + (equal (flan--names-phrase '("a" "b" "c" "d" "e" "f") "fallback") "6 names (a, b, c, d, …)")) (test-flan--check "a short one is just named" - (equal (flan-dev--names-phrase '("a" "b") "fallback") "a, b")) + (equal (flan--names-phrase '("a" "b") "fallback") "a, b")) ;; `defs' is what eldoc, completion and M-. all read. One op answering all ;; three, cached, because eldoc fires on an idle timer and completion inside ;; redisplay, and neither may block on a socket. (test-flan--check "the program's names are known" - (assoc "step" flan-dev--defs)) + (assoc "step" flan--defs)) (test-flan--check "with a signature" - (equal (nth 2 (assoc "step" flan-dev--defs)) "step [] i64")) + (equal (nth 2 (assoc "step" flan--defs)) "step [] i64")) (test-flan--check "a global is known, and says it is one" - (equal (nth 1 (assoc "ticks" flan-dev--defs)) "var")) + (equal (nth 1 (assoc "ticks" flan--defs)) "var")) (test-flan--check "so is an imported package's extern" - (let ((d (assoc "agent/wait-raw" flan-dev--defs))) + (let ((d (assoc "agent/wait-raw" flan--defs))) (and d (equal (nth 1 d) "extern")))) ;; `step' was last installed from this buffer, so that is where the daemon ;; says it is — which is also the check that the client's line padding put it ;; on the line it is really on rather than on line 1. (test-flan--check "a fn carries where it is written" - (equal (nth 3 (assoc "step" flan-dev--defs)) + (equal (nth 3 (assoc "step" flan--defs)) (format "%s:%d:7" buffer-file-name (save-excursion (goto-char (point-min)) @@ -370,7 +370,7 @@ is written instead — the real `message' call the real command makes." (search-forward "(set ticks (step") (let ((said nil)) (test-flan--check "eldoc answers for the name at point" - (and (flan-dev-eldoc-function + (and (flan-eldoc-function (lambda (s &rest _) (setq said s))) said (string-match-p "step \\[\\] i64" said)))) (let ((said nil)) @@ -378,20 +378,20 @@ is written instead — the real `message' call the real command makes." (goto-char (point-min)) (search-forward "(set ticks (step)") (backward-char 1) ; inside (step ...), not on the name - (flan-dev-eldoc-function (lambda (s &rest _) (setq said s)))) + (flan-eldoc-function (lambda (s &rest _) (setq said s)))) (test-flan--check "and for the form point is inside" (and said (string-match-p "step" said)))) (let ((said nil)) (with-temp-buffer (insert "not-a-flan-name") - (flan-dev-eldoc-function (lambda (s &rest _) (setq said s)))) + (flan-eldoc-function (lambda (s &rest _) (setq said s)))) (test-flan--check "and says nothing about a name the program has not got" (null said))) ;; Completion: the running program's names, through `completion-at-point'. (goto-char (point-min)) (search-forward "(defn step") - (let* ((capf (flan-dev-completion-at-point)) + (let* ((capf (flan-completion-at-point)) (table (nth 2 capf))) (test-flan--check "completion offers the program's own names" (member "step" (all-completions "ste" table))) @@ -436,21 +436,21 @@ is written instead — the real `message' call the real command makes." (and raised (string-match-p "no no-such-name" raised)))) ;; A name installed now must complete now, not after the next connect. - (flan-dev--eval "(defn freshly-added [] i64 7)" "form") + (flan--eval "(defn freshly-added [] i64 7)" "form") (test-flan--check "a name just installed is known immediately" - (equal (nth 2 (assoc "freshly-added" flan-dev--defs)) + (equal (nth 2 (assoc "freshly-added" flan--defs)) "freshly-added [] i64")) ;; The session is not poisoned by that: a good form still lands. - (flan-dev--eval "(defn step [] i64 (set ticks (+ ticks 100)) ticks)" "form") + (flan--eval "(defn step [] i64 (set ticks (+ ticks 100)) ticks)" "form") ;; The program's own output arrives on replies and lands in its buffer, so ;; a long-running program is not writing into a terminal nobody is watching. - (flan-dev--eval "(defn step [] i64 (do (println \"HELLO\") ticks))" "form") + (flan--eval "(defn step [] i64 (do (println \"HELLO\") ticks))" "form") (let ((seen nil) (deadline (+ (float-time) 10))) (while (and (not seen) (< (float-time) deadline)) - (ignore-errors (flan-dev--request '(:op "describe"))) - (setq seen (with-current-buffer (get-buffer-create flan-dev-output-buffer) + (ignore-errors (flan--request '(:op "describe"))) + (setq seen (with-current-buffer (get-buffer-create flan-output-buffer) (string-match-p "HELLO" (buffer-string))))) (test-flan--check "the program's output reaches its buffer" seen)) @@ -466,23 +466,23 @@ is written instead — the real `message' call the real command makes." (goto-char (point-min)) (search-forward "(defvar ticks i64)") (test-flan--check "a top-level defvar takes the declaration path" - (equal (car (flan-dev--declaration-before-point)) "defvar")) + (equal (car (flan--declaration-before-point)) "defvar")) (goto-char (point-min)) (search-forward " ticks)") (test-flan--check "and so does a top-level defn" - (equal (car (flan-dev--declaration-before-point)) "defn")) + (equal (car (flan--declaration-before-point)) "defn")) ;; The case the whole "form, not cursor" rule exists for: this line is ;; inside `step', so the enclosing declaration is a `defn' — and evaluating ;; here must still mean this expression, not a reinstall of the function. (goto-char (point-min)) (search-forward "(+ ticks 41)") (test-flan--check "an expression inside a defn body does not" - (null (flan-dev--declaration-before-point))) + (null (flan--declaration-before-point))) (goto-char (point-max)) (let ((beg (point))) (insert "\n(+ 2 3)") (test-flan--check "nor does a bare expression at top level" - (null (flan-dev--declaration-before-point))) + (null (flan--declaration-before-point))) ;; ...and it still evaluates as one, which is the half of this key that ;; was already working and must not have moved. (let ((said (test-flan--said (flan-eval-last-sexp)))) @@ -504,11 +504,11 @@ is written instead — the real `message' call the real command makes." (goto-char beg) (search-forward "commented i64 1)") (test-flan--check "a declaration written in a comment is not one" - (null (flan-dev--declaration-before-point))) + (null (flan--declaration-before-point))) (goto-char beg) (search-forward "inside i64 1)") (test-flan--check "and neither is one written in a string" - (null (flan-dev--declaration-before-point))) + (null (flan--declaration-before-point))) (delete-region beg (point-max))) ;; And the top of the buffer, where there is nothing behind point at all. @@ -519,7 +519,7 @@ is written instead — the real `message' call the real command makes." ;; Nothing is sent now, from either path, and the refusal says why. (goto-char (point-min)) (test-flan--check "the form after point is not the form before it" - (null (flan-dev--declaration-before-point))) + (null (flan--declaration-before-point))) (test-flan--check "and C-x C-e at the top of a buffer refuses rather than sending nothing" (let ((raised nil)) (condition-case err (flan-eval-last-sexp) @@ -574,7 +574,7 @@ is written instead — the real `message' call the real command makes." ;; batch `insert' would leave bare. comint hands the sender the text ;; properties and all, and a propertized string prints as #(...), which ;; the daemon's reader takes as a symbol and a stray list rather than a - ;; string. So this line is the regression test for `flan-dev--bare'. + ;; string. So this line is the regression test for `flan--bare'. (insert (propertize "(+ 20 3)" 'fontified t 'face 'default)) (flan-repl-return) (let ((deadline (+ (float-time) 15))) @@ -599,13 +599,13 @@ is written instead — the real `message' call the real command makes." (insert "(println \"PRINTED\")") (flan-repl-return) (let ((deadline (+ (float-time) 15))) - (while (and (not (with-current-buffer flan-dev-output-buffer + (while (and (not (with-current-buffer flan-output-buffer (string-match-p "PRINTED" (buffer-string)))) (< (float-time) deadline)) - (ignore-errors (flan-dev--request '(:op "describe"))) + (ignore-errors (flan--request '(:op "describe"))) (accept-process-output nil 0.05))) (test-flan--check "printed text goes to the output buffer" - (with-current-buffer flan-dev-output-buffer + (with-current-buffer flan-output-buffer (string-match-p "PRINTED" (buffer-string)))) ;; ...and the prompt got the *value*, which for a call made for its effect ;; is Unit. The text it printed is not the value and does not belong here. @@ -633,31 +633,31 @@ is written instead — the real `message' call the real command makes." ;; checked below by calling it; that it is scheduled at all is checked here, ;; because a background discovery that nothing ever runs discovers nothing. (test-flan--check "a poll timer is running" - (and (timerp flan-dev--timer) - (eq (timer--function flan-dev--timer) #'flan-dev--poll))) + (and (timerp flan--timer) + (eq (timer--function flan--timer) #'flan--poll))) ;; Break it: `step' is called every time round the program's loop, so a body ;; that errors stops it on its own game thread, in a frame of its own — not ;; inside anything this client asked for. Nothing tells Emacs. - (flan-dev--eval + (flan--eval "(defn step [] i64 (restart-case (do (error (Missing {.id 7})) 0) (use-placeholder [] -1)))" "form") (let ((deadline (+ (float-time) 20))) - (while (and (not flan-dev--stopped) (< (float-time) deadline)) - (flan-dev--poll) + (while (and (not flan--stopped) (< (float-time) deadline)) + (flan--poll) (accept-process-output nil 0.05))) (test-flan--check "the client notices a stop nobody asked about" - (equal flan-dev--stopped "Missing")) + (equal flan--stopped "Missing")) (test-flan--check "and the modeline says so, with the condition" - (and (eq (flan-dev-state) 'stopped) - (string-match-p "stopped" (flan-dev-mode-line)) - (string-match-p "Missing" (flan-dev-mode-line)))) + (and (eq (flan-state) 'stopped) + (string-match-p "stopped" (flan-mode-line)) + (string-match-p "Missing" (flan-mode-line)))) ;; What the prompt would offer. `completing-read' is not driven here — a ;; minibuffer in a batch run is a hang waiting to happen — so the list it ;; reads and the two commands it dispatches to are exercised instead. (test-flan--check "the restarts on offer are the ones the frame declared" - (equal (flan-dev-restarts) '("use-placeholder"))) + (equal (flan-restarts) '("use-placeholder"))) ;; And what it would put in front of someone. The labels carry the position, ;; because the position is what gets chosen: two frames may offer the same @@ -665,15 +665,15 @@ is written instead — the real `message' call the real command makes." ;; so it is checked against the shapes a real daemon cannot easily be made to ;; produce as well as against the one it just did. (test-flan--check "the prompt numbers what it offers" - (equal (flan-dev--restart-candidates '("use-placeholder") nil) + (equal (flan--restart-candidates '("use-placeholder") nil) '(("0. use-placeholder" . 0)))) (test-flan--check "a shadowed name is two distinguishable choices" (equal (mapcar #'cdr - (flan-dev--restart-candidates + (flan--restart-candidates '("retry" "use-placeholder" "retry") nil)) '(0 1 2))) (test-flan--check "a restart below the break is shown, and shown as such" - (let ((table (flan-dev--restart-candidates + (let ((table (flan--restart-candidates '("retry" "use-placeholder") '(1)))) (and (not (string-match-p "cannot be taken" (caar table))) (string-match-p "cannot be taken" (car (nth 1 table))) @@ -696,7 +696,7 @@ is written instead — the real `message' call the real command makes." ;; replaced here, and the resume below returns into the old one for the last ;; time before every later call reaches the new body through its cell. (let ((said (test-flan--said - (flan-dev--eval "(defn step [] i64 (set ticks (+ ticks 1)) ticks)" + (flan--eval "(defn step [] i64 (set ticks (+ ticks 1)) ticks)" "form")))) (test-flan--check "a fix installs while the program is stopped" (and said (string-match-p "\\_" said)))) @@ -706,14 +706,14 @@ is written instead — the real `message' call the real command makes." ;; the next poll settle it. ;; By position, which is the path `C-c C-b' takes: the name goes with it as ;; the receipt the program checks, not as the lookup. - (flan-dev-restart-at 0 "use-placeholder") + (flan-restart-at 0 "use-placeholder") (let ((deadline (+ (float-time) 20))) - (while (and (not (eq (flan-dev-state) 'live)) (< (float-time) deadline)) - (flan-dev--poll) + (while (and (not (eq (flan-state) 'live)) (< (float-time) deadline)) + (flan--poll) (accept-process-output nil 0.05))) (test-flan--check "choosing a restart resumes the program" - (and (null flan-dev--stopped) - (eq (flan-dev-state) 'live))) + (and (null flan--stopped) + (eq (flan-state) 'live))) ;; ...and the client is an ordinary client again on the far side of it. (goto-char (point-max)) @@ -774,7 +774,7 @@ is written instead — the real `message' call the real command makes." ;; sends and does not wait, deliberately, because waiting on a 0.2s timer ;; stalls the UI. That leaves a reply in flight that the next C-c C-c would ;; read as its own — an evaluation reporting the watch table's answer, which - ;; is the exact bug `flan-dev-settle-hook' exists to make impossible. This + ;; is the exact bug `flan-settle-hook' exists to make impossible. This ;; program writes nothing into the table, which does not matter: the ;; interleaving is the claim. (flan-watch) @@ -789,34 +789,34 @@ is written instead — the real `message' call the real command makes." (test-flan--check "a tick leaves a request in flight rather than waiting for it" flan-watch--pending) ;; The background poll is a sender too, and it was the one sender that did - ;; not settle: it guarded on `flan-dev--busy' alone, which the watch timer + ;; not settle: it guarded on `flan--busy' alone, which the watch timer ;; deliberately does not bind — it never waits, so it has nothing to hold — ;; and sent `describe' straight into a connection that already owed a reply. ;; It then read the watch's answer as its own, and the two stayed swapped ;; for the rest of the session. The second check is where that would show: ;; a `describe' answered by the watch table has no `:fns' in it at all. - (flan-dev--poll) + (flan--poll) (test-flan--check "a poll settles the watch's reply rather than reading it as its own" (null flan-watch--pending)) (test-flan--check "and the request after it is still answered by its own reply" - (member "step" (plist-get (flan-dev--request '(:op "describe")) + (member "step" (plist-get (flan--request '(:op "describe")) :fns))) ;; And the same hook against a daemon restarted under an armed watch. The ;; reply the watch is owed was asked for on the connection that has gone, so ;; there is nothing to wait for — running the hook before the connection is ;; checked is what lets it see that. Asking after the reconnect meant a - ;; whole `flan-dev-reply-timeout' of frozen Emacs on the first thing anybody + ;; whole `flan-reply-timeout' of frozen Emacs on the first thing anybody ;; typed after a restart, which is why the wait itself is what is measured. (flan-watch--tick) - (delete-process flan-dev--connection) + (delete-process flan--connection) ;; The timeout and the assertion are deliberately different numbers: what is ;; being told apart is a request that waited one out from a request that did ;; not, and the wider the gap the less this depends on how loaded the machine ;; running the suite happens to be. A reconnect and a `describe' are ;; milliseconds of work. - (let ((flan-dev-reply-timeout 10) + (let ((flan-reply-timeout 10) (started (float-time))) - (let ((r (flan-dev--request '(:op "describe")))) + (let ((r (flan--request '(:op "describe")))) (test-flan--check "a request after a restart does not wait out a reply the old connection owed" (and (member "step" (plist-get r :fns)) (< (- (float-time) started) 3))) @@ -826,11 +826,11 @@ is written instead — the real `message' call the real command makes." (flan-watch--tick) ;; And now the interleaving, with a reply outstanding on purpose. If the ;; settle hook were not there this would return the watch table's plist and - ;; `flan-dev--report' would take its missing :status for a rejection. + ;; `flan--report' would take its missing :status for a rejection. ;; ;; Back in the source buffer first: `flan-doc' and `flan-watch' above both ;; display buffers of their own, and C-c C-c reads the buffer it is run in. - (pop-to-buffer (flan-dev--buffer-visiting file)) + (pop-to-buffer (flan--buffer-visiting file)) (goto-char (point-min)) (search-forward "(defn step") (goto-char (match-beginning 0)) @@ -858,13 +858,13 @@ is written instead — the real `message' call the real command makes." (flan-watch-stop) (test-flan--check "stopping cancels the timer" (null flan-watch--timer)) (test-flan--check "and takes the settle hook off with it" - (not (memq #'flan-watch--settle flan-dev-settle-hook))) + (not (memq #'flan-watch--settle flan-settle-hook))) (kill-buffer flan-watch-buffer) (flan-disconnect) - (test-flan--check "disconnected" (not (process-live-p flan-dev--connection))) + (test-flan--check "disconnected" (not (process-live-p flan--connection))) (test-flan--check "and the poll timer is cancelled with it" - (null flan-dev--timer)) + (null flan--timer)) ;; ── Starting the daemon from Emacs ──────────────────────────────────── ;; @@ -873,26 +873,26 @@ is written instead — the real `message' call the real command makes." ;; request went to. Its own socket for the same reason — and because ;; `flan dev' with no -s puts one beside the program, which for a test ;; program in /tmp is a path shared with every other thing running there. - (setq flan-dev-command flan) + (setq flan-command flan) (let ((socket2 (concat socket "-started-from-emacs"))) (ignore-errors (delete-file socket2)) (test-flan--check "nothing to quit before anything was started" (let ((raised nil)) - (condition-case err (flan-dev-quit) + (condition-case err (flan-quit) (user-error (setq raised (error-message-string err)))) (and raised (string-match-p "no daemon started" raised)))) - (flan-dev program socket2) - (test-flan--check "M-x flan-dev builds, launches and connects" - (and (process-live-p flan-dev--daemon) - (eq (flan-dev-state) 'live))) + (flan program socket2) + (test-flan--check "M-x flan builds, launches and connects" + (and (process-live-p flan--daemon) + (eq (flan-state) 'live))) (test-flan--check "and it is the program that was asked for" - (member "step" (plist-get (flan-dev--request '(:op "describe")) + (member "step" (plist-get (flan--request '(:op "describe")) :fns))) ;; Refused rather than silently restarted: a second daemon would take the ;; first one's program and everything in its memory with it. (test-flan--check "a second one is refused while the first is alive" (let ((raised nil)) - (condition-case err (flan-dev program socket2) + (condition-case err (flan program socket2) (user-error (setq raised (error-message-string err)))) (and raised (string-match-p "already running" raised)))) ;; The daemon owns the program's lifetime, so quitting has to actually end @@ -901,31 +901,31 @@ is written instead — the real `message' call the real command makes." ;; struct whose layout moved — where the answer is a new build, a new ;; process and the session that compiled it. Proved by what it throws ;; away: a name installed into the old program is not in the new one. - (flan-dev--eval "(defn only-in-the-old-program [] i64 1)" "form") + (flan--eval "(defn only-in-the-old-program [] i64 1)" "form") (test-flan--check "a name installed into the running program is there" - (assoc "only-in-the-old-program" flan-dev--defs)) - (let ((old flan-dev--daemon)) - (flan-dev-restart-program) + (assoc "only-in-the-old-program" flan--defs)) + (let ((old flan--daemon)) + (flan-restart-program) (test-flan--check "restarting gives a different daemon, connected" (and (not (process-live-p old)) - (process-live-p flan-dev--daemon) - (not (eq old flan-dev--daemon)) - (eq (flan-dev-state) 'live)))) + (process-live-p flan--daemon) + (not (eq old flan--daemon)) + (eq (flan-state) 'live)))) (test-flan--check "and a program built from source, without the addition" - (and (assoc "step" flan-dev--defs) + (and (assoc "step" flan--defs) (null (assoc "only-in-the-old-program" - flan-dev--defs)))) + flan--defs)))) - (let ((proc flan-dev--daemon)) - (flan-dev-quit) + (let ((proc flan--daemon)) + (flan-quit) ;; The process, not the variable: forgetting a daemon is not stopping ;; one, and a program left running with nothing attached to it is ;; exactly what the terminal loop used to leave behind. (test-flan--check "quitting ends the daemon" (and (not (process-live-p proc)) - (null flan-dev--daemon) - (not (process-live-p flan-dev--connection)) - (eq (flan-dev-state) 'off))) + (null flan--daemon) + (not (process-live-p flan--connection)) + (eq (flan-state) 'off))) ;; The daemon unlinks its socket on the way out, so this is the same ;; claim seen from the other side. (test-flan--check "and takes its socket with it" @@ -937,7 +937,7 @@ is written instead — the real `message' call the real command makes." ;; Attaching to a second program while a daemon of this Emacs' own is ;; running is an ordinary thing to do — the other one is in a terminal, and ;; `flan-connect' is the command for it — and it used to cost the first - ;; program its life: `flan-dev-quit' sent `close' down whichever connection + ;; program its life: `flan-quit' sent `close' down whichever connection ;; was current and then killed the daemon, which by then were two different ;; programs. Both halves are checked here. ;; @@ -956,12 +956,12 @@ is written instead — the real `message' call the real command makes." (set-process-query-on-exit-flag theirs nil) (with-current-buffer theirs-buf (set-buffer-multibyte nil)) (unwind-protect - (let ((flan-dev--daemon mine) - (flan-dev--daemon-socket "/tmp/flan-emacs-mine.sock") - (flan-dev--file "/tmp/flan-emacs-mine.flan") - (flan-dev--connection theirs) - (flan-dev--socket "/tmp/flan-emacs-theirs.sock") - (flan-dev--defs nil)) + (let ((flan--daemon mine) + (flan--daemon-socket "/tmp/flan-emacs-mine.sock") + (flan--file "/tmp/flan-emacs-mine.flan") + (flan--connection theirs) + (flan--socket "/tmp/flan-emacs-theirs.sock") + (flan--defs nil)) (test-flan--check "flan-connect elsewhere names both programs rather than dropping one" (let ((raised nil)) @@ -971,11 +971,11 @@ is written instead — the real `message' call the real command makes." (string-match-p "flan-emacs-mine" raised) (string-match-p "flan-emacs-elsewhere" raised)))) (test-flan--check "and the connection it refused to replace is untouched" - (eq flan-dev--connection theirs)) + (eq flan--connection theirs)) ;; And the command whose name promises one program. It closes the ;; connection it is on and leaves the daemon alone, saying so — ;; where it used to end both. - (let ((said (test-flan--said (flan-dev-quit)))) + (let ((said (test-flan--said (flan-quit)))) (test-flan--check "quit ends the session it is connected to" (not (process-live-p theirs))) (test-flan--check "and leaves the daemon this Emacs started running" @@ -993,10 +993,54 @@ is written instead — the real `message' call the real command makes." ;; distance and takes a compile to find out. (test-flan--check "a file that is not there is refused before anything starts" (let ((raised nil)) - (condition-case err (flan-dev "/nonexistent/nope.flan") + (condition-case err (flan "/nonexistent/nope.flan") (user-error (setq raised (error-message-string err)))) (and raised (string-match-p "no such file" raised)))) + ;; ── Which file M-x flan starts ──────────────────────────────────────── + ;; + ;; The `interactive' form by itself, evaluated the way the command loop + ;; evaluates it. Calling the command would build and launch a program, and + ;; none of that is in question here: the question is only which file the + ;; form arrives at, and whether it had to ask to get there. + (let* ((asked nil) + (answer "/nonexistent/answered.flan") + (probe (lambda (&rest _) (setq asked t) answer)) + (spec (cadr (interactive-form 'flan)))) + (advice-add 'read-file-name :override probe) + (unwind-protect + (progn + (with-temp-buffer + (setq buffer-file-name "/nonexistent/here.flan") + (setq asked nil) + (test-flan--check "a .flan buffer is started without a prompt" + (and (equal (eval spec t) '("/nonexistent/here.flan")) + (not asked)))) + (with-temp-buffer + (setq buffer-file-name "/nonexistent/notes.org") + (setq asked nil) + (test-flan--check "a buffer that is not Flan's is asked about" + (and (equal (eval spec t) (list answer)) asked))) + (with-temp-buffer + (setq asked nil) + (test-flan--check "and so is one visiting no file at all" + (and (equal (eval spec t) (list answer)) asked))) + ;; The escape hatch, for starting some other program from a .flan + ;; buffer — the one case the silent path would otherwise close off. + (with-temp-buffer + (setq buffer-file-name "/nonexistent/here.flan") + (setq asked nil) + (let ((current-prefix-arg '(4))) + (test-flan--check "a prefix argument asks from a .flan buffer too" + (and (equal (eval spec t) (list answer)) asked))))) + (advice-remove 'read-file-name probe))) + + ;; The old name is gone rather than deprecated, so nothing should still + ;; answer to it — an alias left behind is what keeps a rename from finishing. + (test-flan--check "the -dev- names are not defined any more" + (not (or (fboundp 'flan-dev) (fboundp 'flan-dev-quit) + (boundp 'flan-dev-socket-name)))) + ;; ── Navigating a file ───────────────────────────────────────────────── ;; ;; No daemon in any of this: imenu and which-function read the buffer, which @@ -1055,25 +1099,25 @@ is written instead — the real `message' call the real command makes." ;; hook is installed, in the right buffer and nowhere else, and that running ;; it clears the overlay and uninstalls itself. It does not prove Emacs runs ;; it, which is Emacs' own contract. - (let ((buf (flan-dev--buffer-visiting file))) + (let ((buf (flan--buffer-visiting file))) (with-current-buffer buf - (flan-dev-clear-errors) - (let ((marked (flan-dev--show-error (format "%s:2:1" file) "no such name"))) + (flan-clear-errors) + (let ((marked (flan--show-error (format "%s:2:1" file) "no such name"))) (test-flan--check "a rejection is marked in the buffer it came from" - (and marked (flan-dev--error-overlays))) + (and marked (flan--error-overlays))) (test-flan--check "and the buffer is armed to take it down again" - (memq #'flan-dev--clear-errors-on-command + (memq #'flan--clear-errors-on-command pre-command-hook)) ;; Buffer-local, or every buffer in the session runs this on every ;; keystroke for the sake of a buffer that had one bad evaluation. (test-flan--check "and nobody else is" - (not (memq #'flan-dev--clear-errors-on-command + (not (memq #'flan--clear-errors-on-command (default-value 'pre-command-hook)))) (run-hooks 'pre-command-hook) (test-flan--check "the next command in that buffer clears it" - (null (flan-dev--error-overlays))) + (null (flan--error-overlays))) (test-flan--check "and the hook goes with the last overlay" - (not (memq #'flan-dev--clear-errors-on-command + (not (memq #'flan--clear-errors-on-command pre-command-hook)))))) ;; ── Disassembly ─────────────────────────────────────────────────────── @@ -1083,9 +1127,9 @@ is written instead — the real `message' call the real command makes." ;; the thing that built the module and still has the .ll and the .so. (let ((socket3 (concat socket "-disasm"))) (ignore-errors (delete-file socket3)) - (flan-dev program socket3) + (flan program socket3) (test-flan--check "a daemon to disassemble against" - (process-live-p flan-dev--connection)) + (process-live-p flan--connection)) (when (executable-find "objdump") (flan-disassemble "step") @@ -1125,7 +1169,7 @@ is written instead — the real `message' call the real command makes." (user-error (setq raised (error-message-string err)))) (and raised (string-match-p "not a function" raised)))) - (flan-dev-quit) + (flan-quit) (ignore-errors (delete-file socket3))) ;; ── Every lowering of one function, in one buffer ───────────────────── @@ -1364,11 +1408,11 @@ is written instead — the real `message' call the real command makes." (goto-char (point-min)) (search-forward "(+ ticks 1)") (goto-char (1- (match-end 0))) ; inside the (+ ...), before its ")" - (let* ((b (flan-dev--defun-bounds)) - (inner (flan-dev--pause-bounds b '(4))) - (whole (flan-dev--pause-bounds b '(16)))) + (let* ((b (flan--defun-bounds)) + (inner (flan--pause-bounds b '(4))) + (whole (flan--pause-bounds b '(16)))) (test-flan--check "no prefix marks nothing" - (null (flan-dev--pause-bounds b nil))) + (null (flan--pause-bounds b nil))) (test-flan--check "C-u marks the form point is inside" (equal (buffer-substring-no-properties (car inner) (cdr inner)) @@ -1383,24 +1427,24 @@ is written instead — the real `message' call the real command makes." (goto-char (point-min)) (search-forward "(defn step") (goto-char (match-beginning 0)) - (let ((b (flan-dev--defun-bounds))) + (let ((b (flan--defun-bounds))) (test-flan--check "and a prefix with point not nested falls back to it" - (equal (flan-dev--pause-bounds b '(4)) b))) + (equal (flan--pause-bounds b '(4)) b))) ;; The column is a byte offset, because the reader walks the source a byte ;; at a time. Same rule as the `:loc' column, the other way round — and - ;; `flan-dev--position' is the inverse, so a round trip is the check. + ;; `flan--position' is the inverse, so a round trip is the check. (goto-char (point-min)) (search-forward "(+ ticks 1)") (let* ((pos (match-beginning 0)) - (lc (flan-dev--wire-position pos))) + (lc (flan--wire-position pos))) (test-flan--check "a marked position round-trips through the wire" - (= (flan-dev--position (nth 0 lc) (nth 1 lc)) pos))) + (= (flan--position (nth 0 lc) (nth 1 lc)) pos))) (with-temp-buffer (insert ";; héllo\n(defn wörld [] i64 (+ 1 1))\n") (goto-char (point-min)) (search-forward "(+ 1 1)") (test-flan--check "and counts bytes, not characters, past a non-ASCII one" - (equal (flan-dev--wire-position (match-beginning 0)) + (equal (flan--wire-position (match-beginning 0)) (list 2 (1+ (string-bytes "(defn wörld [] i64 "))))))) ;; A mark is an annotation on the running program and not feedback about one @@ -1412,29 +1456,29 @@ is written instead — the real `message' call the real command makes." (insert "(defn step [] i64 (+ 1 1))\n") (goto-char (point-min)) (search-forward "(+ 1 1)") - (flan-dev--show-pause (match-beginning 0) (match-end 0)) + (flan--show-pause (match-beginning 0) (match-end 0)) (test-flan--check "a mark is drawn over the form" - (= 1 (length (flan-dev--pause-overlays)))) + (= 1 (length (flan--pause-overlays)))) (run-hooks 'pre-command-hook) (test-flan--check "and survives the next command, where a rejection would not" - (= 1 (length (flan-dev--pause-overlays)))) + (= 1 (length (flan--pause-overlays)))) ;; Re-marking the same form leaves one, not two stacked overlays whose ;; faces compound into something that is not the face. - (flan-dev--show-pause (match-beginning 0) (match-end 0)) + (flan--show-pause (match-beginning 0) (match-end 0)) (test-flan--check "and marking it again leaves one mark, not two" - (= 1 (length (flan-dev--pause-overlays)))) - (flan-dev-clear-pause) + (= 1 (length (flan--pause-overlays)))) + (flan-clear-pause) (test-flan--check "and clearing takes it down" - (null (flan-dev--pause-overlays)))) + (null (flan--pause-overlays)))) ;; And once against a real daemon: the round trip, the overlay drawn off the ;; reply's `:pause' rather than off what was asked for, and the mark coming ;; down again when the same form is evaluated plainly. (let ((socket4 (concat socket "-pause"))) (ignore-errors (delete-file socket4)) - (flan-dev program socket4) + (flan program socket4) (test-flan--check "a daemon to mark a form in" - (process-live-p flan-dev--connection)) + (process-live-p flan--connection)) (with-temp-buffer (flan-mode) (insert "(defn step [] i64\n (set ticks (+ ticks 1))\n ticks)\n") @@ -1442,7 +1486,7 @@ is written instead — the real `message' call the real command makes." (search-forward "(+ ticks 1)") (goto-char (1- (match-end 0))) (flan-eval-defun '(4)) - (let ((ovs (flan-dev--pause-overlays))) + (let ((ovs (flan--pause-overlays))) (test-flan--check "C-u C-c C-c marks the form point is inside" (and (= 1 (length ovs)) (equal (buffer-substring-no-properties @@ -1452,16 +1496,16 @@ is written instead — the real `message' call the real command makes." (test-flan--check "and marks it as a pause, not as an error" (and ovs (eq (overlay-get (car ovs) 'face) - 'flan-dev-pause-face)))) + 'flan-pause-face)))) ;; The program calls `step' every few milliseconds, so it stops almost at ;; once — but "almost" is not "before this line", so wait for it. (let ((tries 400)) - (while (and (> tries 0) (not (eq (flan-dev-state) 'stopped))) + (while (and (> tries 0) (not (eq (flan-state) 'stopped))) (setq tries (1- tries)) - (flan-dev--request (list :op "describe")) + (flan--request (list :op "describe")) (sleep-for 0.005))) (test-flan--check "and the program stops there" - (eq (flan-dev-state) 'stopped)) + (eq (flan-state) 'stopped)) ;; A plain `C-c C-c' over the same form replaces the stored declaration ;; with an unmarked one, which is what makes the mark stop sticking — and ;; the overlay has to go with it or the buffer is claiming a breakpoint @@ -1469,19 +1513,19 @@ is written instead — the real `message' call the real command makes." ;; progress for `step'. (flan-eval-defun) (test-flan--check "and an ordinary C-c C-c takes the mark down again" - (null (flan-dev--pause-overlays))) + (null (flan--pause-overlays))) ;; Left running, because the next thing this file does is finish and the ;; daemon's program is killed with it — but a test that ends with the ;; program parked is one nobody can add anything after. - (flan-dev-restart "continue") + (flan-restart "continue") (let ((tries 400)) - (while (and (> tries 0) (eq (flan-dev-state) 'stopped)) + (while (and (> tries 0) (eq (flan-state) 'stopped)) (setq tries (1- tries)) - (flan-dev--request (list :op "describe")) + (flan--request (list :op "describe")) (sleep-for 0.005))) (test-flan--check "and it resumes when continue is taken" - (not (eq (flan-dev-state) 'stopped)))) - (flan-dev-quit) + (not (eq (flan-state) 'stopped)))) + (flan-quit) (ignore-errors (delete-file socket4))) ;; ── C-c C-m: what a macro call expands to ───────────────────────────── @@ -1493,15 +1537,15 @@ is written instead — the real `message' call the real command makes." ;; worth a real daemon — that a macro which never settles comes back as a ;; refusal drawn on the call, at its own line *and its own column*. ;; - ;; That last one is what `flan-dev--text-at' exists for. `C-x C-e' sends a - ;; raw substring and `flan-dev--text' pads lines only, because a top-level + ;; That last one is what `flan--text-at' exists for. `C-x C-e' sends a + ;; raw substring and `flan--text' pads lines only, because a top-level ;; form starts at column 1; a macro call is written well inside a line, and ;; a refusal against it would otherwise be drawn at the start of that line. (let ((socket5 (concat socket "-macro"))) (ignore-errors (delete-file socket5)) - (flan-dev program socket5) + (flan program socket5) (test-flan--check "a daemon to expand macros against" - (process-live-p flan-dev--connection)) + (process-live-p flan--connection)) (with-current-buffer (get-file-buffer file) (goto-char (point-max)) (insert "\n(defn expandable [] i32\n (unless false 1 2)\n (clamp 9 0 3))\n") @@ -1546,7 +1590,7 @@ is written instead — the real `message' call the real command makes." ;; comes back — and all the way is refused at the bound rather than ;; hanging the daemon, which is the failure that would wedge the editor ;; with the program still on screen. - (flan-dev--request + (flan--request (list :op "eval" :file file :code "(defmacro spinner [args] `(spinner ~@args))")) (goto-char (point-max)) @@ -1559,32 +1603,32 @@ is written instead — the real `message' call the real command makes." "one step of a macro that does not settle answers" (and (null (plist-get r :expanded)) (equal (plist-get r :macro) "spinner"))) - (flan-dev-clear-errors) + (flan-clear-errors) (goto-char call) (let ((said (condition-case e (progn (flan-macroexpand t) nil) (user-error (error-message-string e))))) (test-flan--check "and all the way is refused rather than hanging" (and said (string-match-p "did not settle" said))) - ;; The whole of `flan-dev--text-at': the refusal's location is the + ;; The whole of `flan--text-at': the refusal's location is the ;; call's own line and column, so the overlay lands on the call and ;; not at the start of the line it is written on. - (let ((ovs (flan-dev--error-overlays))) + (let ((ovs (flan--error-overlays))) (test-flan--check "and the refusal is drawn on the call, at its own column" (and (= 1 (length ovs)) (= (overlay-start (car ovs)) call))))) - (flan-dev-clear-errors)) + (flan-clear-errors)) ;; Expanding again in place, which is what makes one-step-by-default ;; usable rather than a thing you press once and lose. Two macros into ;; the session, the outer one quasiquoting a call to the inner: that is ;; the shape where one step leaves a macro call standing, and it is the ;; only shape where expanding in place has anything to do. - (flan-dev--request + (flan--request (list :op "eval" :file file :code "(defmacro m-inner [args] `(+ ~(at args 0) 1))")) - (flan-dev--request + (flan--request (list :op "eval" :file file :code "(defmacro m-outer [args] `(m-inner ~(at args 0)))")) (goto-char (point-max)) @@ -1617,12 +1661,12 @@ is written instead — the real `message' call the real command makes." (and (string-match-p (regexp-quote "(+ 5 1)") (buffer-string)) (not (string-match-p (regexp-quote "(m-inner 5)") (buffer-string))))))) - (flan-dev-quit) + (flan-quit) (ignore-errors (delete-file socket5))) (if (zerop test-flan--failures) - (message "flan-dev.el: all tests passed") + (message "flan.el: all tests passed") (message "\n%d failure(s)" test-flan--failures) (kill-emacs 1))) -;;; test-flan-dev.el ends here +;;; test-flan.el ends here diff --git a/lib/dev.ml b/lib/dev.ml index 77c2816..c0b4913 100644 --- a/lib/dev.ml +++ b/lib/dev.ml @@ -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. diff --git a/test/test_emacs.ml b/test/test_emacs.ml index c578f33..0ed08a9 100644 --- a/test/test_emacs.ml +++ b/test/test_emacs.ml @@ -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)) diff --git a/test/test_session.ml b/test/test_session.ml index 0962cbb..f9aefa5 100644 --- a/test/test_session.ml +++ b/test/test_session.ml @@ -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 "")`). Omitting it here + (flan.el's `:file (or buffer-file-name "")`). 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 diff --git a/web/index.html b/web/index.html index 1af28df..15637a1 100644 --- a/web/index.html +++ b/web/index.html @@ -1731,7 +1731,7 @@ retains three, permanently and correctly.

lisp-mode's syntax table, so sexp motion, paren matching and indentation are already right. It adds Flan's brackets — [ and { are brackets, not symbol characters, since every binding list and every type is written with them — and -the characters a Flan name may contain. emacs/flan-dev.el is the client; +the characters a Flan name may contain. emacs/flan.el is the client; there is no parser in it.

Setting up

@@ -1745,15 +1745,15 @@ the client, the REPL, the inspector and the conditions buffer all load on first

flan-dape.el is separate on purpose, so flan-mode works without dape installed. You also need flan on your PATH, or -flan-dev-command pointed at it.

+flan-command pointed at it.

Starting a program

-

M-x flan-dev runs flan dev on a file, waits for it to come up +

M-x flan runs flan dev on a file, waits for it to come up and connects. C-c C-z attaches to one that is already running, looking for .flan-dev.sock upward from the buffer, so from anywhere in the project it finds the one program you have going. C-c C-q disconnects and leaves it -running; M-x flan-dev-quit stops it, but only one this Emacs started — a +running; M-x flan-quit 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.

@@ -1782,8 +1782,8 @@ something surprising.

-

Some commands have no key. M-x flan-dev starts a program and -M-x flan-dev-quit stops it. M-x flan-watch opens the watch +

Some commands have no key. M-x flan starts a program and +M-x flan-quit stops it. M-x flan-watch opens the watch buffer, which shows values while the program runs rather than while it is stopped, and M-x flan-watch-ghost-mode shows the same values inline at the call that wrote each one. M-x flan-inspect-address roots an inspection at a @@ -1879,13 +1879,13 @@ answers with the outer binding.

- - - - - - - + + + + + + +
SettingDefaultWhat 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-resulttprint C-x C-e's value in the echo area
flan-dev-names-shown4how many names to list before counting them
flan-dev-output-buffer"*flan-output*"where the program's output goes
flan-dev-poll-interval1.0seconds between checks for whether it stopped
flan-dev-start-timeout60seconds 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-resulttprint C-x C-e's value in the echo area
flan-names-shown4how many names to list before counting them
flan-output-buffer"*flan-output*"where the program's output goes
flan-poll-interval1.0seconds between checks for whether it stopped
flan-start-timeout60seconds to wait for a program to come up