2 Commits

Author SHA1 Message Date
Joseph Ferano
7118d6106d eldoc, completion and M-. off one cached reply
All three want the same three facts about a name — what it is, what it looks
like, and where it was written — so the daemon answers all three in one
`defs` reply and the client keeps the last one.

`defs` is its own op rather than more fields on `describe`. `describe` is what
an editor *polls*: it is how the program's output gets drained, and the
existing tests ask it in loops. Signatures riding on that would be paid for
every time anyone glanced at the output buffer. This is asked once on connect
and again after each accepted install, which is exactly when the answer can
have changed — so a `defn` typed a second ago completes.

It is a cache rather than a request per keystroke because of where these are
called from: eldoc fires on an idle timer and completion inside redisplay, and
neither may block on a socket or signal.

Three refusals rather than three guesses. A global has no location because
`Tast.global` carries no `Loc`, and searching the buffer for "(defvar ticks"
instead would find the wrong one in a program of several files. The prelude is
a string inside the compiler, so its location names a file nobody can visit. A
short name that could be several of the program's package-qualified ones is
ambiguous, and picking would be a guess about which function you meant — a
name that is the tail of exactly *one* is not a guess, and resolves.

Functions the checker invented — a lifted handler-bind clause, which carries
an `fparent` — are left out entirely: nobody wrote that name, so completing it
is noise and jumping to it is meaningless.

And the daemon now makes its own source path absolute before building, because
every location it reports derives from it. `flan dev src/game.flan` from a
project root answered `src/game.flan:12:7`, which an editor can only resolve by
guessing what it was relative to.

lib/dev.ml is the only compiler file touched: a `defs` op, its three list
builders, and the one `realpath` in `start`. Nothing existing changed shape —
`describe`, `eval` and `eval-expr` answer byte for byte what they did.
2026-09-11 17:56:37 +07:00
5993875539 A prompt on the running program
flan-repl.el is a comint buffer whose every line goes through the same
eval-expr request C-x C-e uses - no new protocol, no compiler support. Deriving
from comint rather than hand-rolling a prompt is the same call as deriving
flan-mode from lisp-mode: history, the input ring and kill/yank already exist
and are not worth rewriting. There is no subprocess behind it; the "process" is
a stub comint needs in order to have a prompt.

It is program-scoped: a name typed at the prompt resolves against the running
program's top-level namespace, so in sand you write sim/settle. A buffer
visiting a package's file gets the alias applied for it because the file says
which package it belongs to, and a prompt has no file to derive one from. RET
on a half-typed form opens a line instead of sending it, with balance checked
through the Flan syntax table so a paren inside a string does not count.

A value and the program's output are different things and arrive by different
routes: the value is the result of the request and appears at the prompt, while
anything printed rides along on the same reply into *flan-output*. Showing them
in one place would be convenient and wrong, so there is a test for the
separation - and it caught a real bug. The renderer's Unit case emitted () with
no evaluation at all, so (print-line "x"), the most ordinary thing anyone types
at a prompt, answered while nothing happened. A Unit expression is almost
always a call made for its effect; it is evaluated and then reported.
2026-09-11 07:21:37 +07:00