The wait for a reply is a setting, and the directory installs

The 30s deadline in the reply reader was a literal and its message said only
that nothing had arrived. It is flan-dev-reply-timeout now, and the message
names the daemon buffer to look in -- a first compile on a cold cache is the
case that legitimately runs long, and the build log is what says so -- and the
setting to raise. Still never resent: a request the daemon took and died on
may already have run.

Package headers on all eight client files, so package-install-file on the
directory works and the client is not reachable only by load-path. The
daemon-buffer defcustom moves up beside the other buffer names, because the
reply reader now names it and the byte-compiler reads a file in order.
This commit is contained in:
Joseph Ferano 2026-09-17 21:48:03 +07:00
parent 652361e169
commit 668268b6cc
9 changed files with 215 additions and 9 deletions

View File

@ -30,9 +30,34 @@ works without dape installed, and `C-c C-g` only exists once you load it.
(require 'flan-dape) ; only if you have dape
```
Or install it as a package, which gets you autoloads and byte-compiled files
without a `load-path` line. `package-install-file` names the package after the
directory, so the directory has to be called `flan-mode` — symlink it once and
point Emacs at the link:
```sh
ln -s ~/Development/flan/emacs ~/.emacs.d/flan-mode
```
```
M-x package-install-file RET ~/.emacs.d/flan-mode/ RET
```
Every file carries `Version:` and `Package-Requires: ((emacs "29.1"))`, which is
what package.el reads. dape is deliberately not in that list: `flan-dape.el`
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`.
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`
(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
you raise it.
---
## Starting a program

View File

@ -1,5 +1,24 @@
;;; flan-cnr.el --- What a stopped program is offering -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; `C-c C-b' is a `completing-read' over restart names. That is the whole UI
;; for the one moment the dev loop exists to make survivable, and it is thin in
;; a way that is worth being specific about: it shows the names and nothing

View File

@ -1,5 +1,24 @@
;;; flan-dape.el --- Debug a Flan program with dape and lldb-dap -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; 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
;; running while you change it; this is about stopping one and reading it.
;;

View File

@ -1,5 +1,24 @@
;;; flan-dev.el --- Talk to a running Flan program -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; 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
@ -77,6 +96,29 @@ that quietly did nothing, which is why it is on."
"Buffer the running program's own output is appended to."
:type 'string)
(defcustom flan-dev-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
socket at all and this buffer is the only account of why. It is also where a
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
"Seconds to wait for one reply from the daemon before giving up.
This bounds a single request, not a session. Thirty seconds is generous
for an evaluation and tight for a first compile of a large program on a
cold object cache, which is the one case that legitimately runs long so
it is a setting rather than a constant.
Giving up here never resends. If the daemon took the request and died
before replying the evaluation may well already have happened, and sending
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
"The open connection, or nil.")
@ -149,7 +191,7 @@ Point is in the process buffer, and the frame is known to be complete."
(defun flan-dev--read-reply (proc)
"Block until PROC sends one complete framed message, and read it."
(with-current-buffer (process-buffer proc)
(let ((deadline (+ (float-time) 30)))
(let ((deadline (+ (float-time) flan-dev-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)))
@ -161,9 +203,17 @@ Point is in the process buffer, and the frame is known to be complete."
;; before replying, the evaluation may well have happened — sending it
;; again would install it twice, or run a side-effecting expression
;; twice. Reconnecting happens before a send, never after one.
;; What a person does next, not just what failed. A live daemon that
;; has not answered is nearly always still working — a first compile
;; of a large program on a cold object cache is the case that runs
;; long — 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.
(if (process-live-p proc)
(error "flan dev: no reply in 30s from %s"
(abbreviate-file-name (or flan-dev--socket "the daemon")))
(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: the daemon on %s closed the connection; not resent, because it may already have run"
(abbreviate-file-name (or flan-dev--socket "?")))))
@ -511,12 +561,10 @@ With no argument, look for `flan-dev-socket-name' up from this buffer."
A name is looked up on `exec-path'; a path is used as given."
:type 'string)
(defcustom flan-dev-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
socket at all and this buffer is the only account of why."
:type 'string)
;; `flan-dev-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
"Seconds to wait for a daemon started from Emacs to accept a connection.

View File

@ -1,5 +1,24 @@
;;; flan-inspect.el --- Navigate a running program's values -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; `C-x C-e' renders a value once and puts it in the echo area. This is the
;; interactive version of the same walk: the fields laid out one per line, RET
;; to go into one, `l' to come back, `g' to read it again. CIDER's inspector,

View File

@ -1,5 +1,24 @@
;;; flan-lower.el --- Every lowering of one function, in one buffer -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; `spike/x86/dump.sh' prints four lowerings of one function side by side --
;; the LLVM IR the frontend emits, what `llc' makes of it at -O0 and at -O2,
;; and what the hand-written x86 backend emits. Reading one against another is

View File

@ -1,5 +1,24 @@
;;; flan-mode.el --- Major mode for Flan -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; Derived from `prog-mode', borrowing `lisp-mode''s machinery for the parts
;; that are simply s-expressions: sexp motion, paren matching and
;; `beginning-of-defun' already do the right thing.

View File

@ -1,5 +1,24 @@
;;; flan-repl.el --- A prompt for a running Flan program -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; A buffer to type expressions at, sent to the program `flan dev' is running
;; and answered with the value they had *there*. It adds no protocol and no
;; compiler support: every line goes through the same `eval-expr' request that

View File

@ -1,5 +1,24 @@
;;; flan-watch.el --- Watched values, in a pinned buffer or inline -*- lexical-binding: t; -*-
;; Author: Joseph Ferano <joseph@ferano.io>
;; Version: 0.1.0
;; Package-Requires: ((emacs "29.1"))
;; Keywords: languages, lisp, tools
;; The headers above are what make this directory installable. M-x
;; package-install-file on it reads them, and a file with no Version: is not a
;; package as far as package.el is concerned -- until now the client was
;; reachable only by adding it to load-path by hand, which is a thing to
;; explain to every person who wants to try it.
;;
;; 29.1 is the floor because it is the oldest Emacs any of this has been run
;; against, not because some function here is known to need it. dape, which
;; flan-dape drives, asks for 29.1 as well and is a soft dependency: it is
;; reached through declare-function, so the rest of the client loads and works
;; without it and it is deliberately not listed above. The compiler this talks
;; to is not an Emacs package and cannot be listed here either -- emacs/MANUAL.md
;; says what has to be on PATH.
;; A HUD for a running Flan program: a buffer that always shows the current
;; frame's values and nothing else. Ported from the author's Clojure
;; `clj-watch', with one thing kept and one thing inverted.