Reflow the note, autoload the client, and three todos

NEXT.md rewrapped to a wider column - a reflow, not a rewrite. The three TODO
entries in it are the substance: live disassembly of what is actually installed
in a cell, error overlays that vanish on the next thing you do rather than
surviving until an evaluation is accepted, and CL-style interactive recovery
where a stopped program offers a typed restart and the editor asks for the
value before invoking it.

flan-mode's declare-functions become real autoloads. A declare-function only
quiets the byte compiler; it does not load anything, so a user who had loaded
only flan-mode could not invoke M-x flan-dev at all.
This commit is contained in:
Joseph Ferano 2026-09-12 03:41:54 +07:00
parent a5980734dc
commit 5170746de5
4 changed files with 956 additions and 1327 deletions

2206
NEXT.md

File diff suppressed because it is too large Load Diff

View File

@ -427,9 +427,9 @@ for it — this is only what Emacs is responsible for killing.")
SOCKET defaults to `flan-dev-socket-name' beside FILE, which is where the
daemon puts it when it is not told otherwise.
Refuses while a daemon this Emacs started is still alive, by name: killing it
would take its program and everything that program has in memory with it,
which is the one thing a dev loop exists to avoid doing by accident."
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
;; The program last started, where there was one: a restart after a quit is
;; the common case, and it is rarely the buffer point happens to be in —
@ -439,8 +439,23 @@ which is the one thing a dev loop exists to avoid doing by accident."
(string-suffix-p ".flan" buffer-file-name)
(file-name-nondirectory buffer-file-name)))))
(when (process-live-p flan-dev--daemon)
(user-error "flan dev: already running on %s; M-x flan-dev-quit first"
(abbreviate-file-name (or flan-dev--socket "a socket"))))
;; `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
;; program. Keep the state-preserving default for Lisp callers, but let a
;; person explicitly choose to replace their own session.
(if (called-interactively-p 'interactive)
(if (y-or-n-p
(format "Stop %s and start %s? "
(abbreviate-file-name
(or flan-dev--file "the running program"))
(abbreviate-file-name (expand-file-name file))))
(flan-dev-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")))))
(let* ((file (expand-file-name file))
(socket (or socket
(expand-file-name flan-dev-socket-name

View File

@ -12,21 +12,22 @@
;; otherwise be made buffer-local before its own defvar had run.
(require 'imenu)
;; The keymap binds them; loading the client is what defines them, and a user
;; may well edit Flan without ever connecting to a running program.
(declare-function flan-eval-defun "flan-dev")
(declare-function flan-eval-buffer "flan-dev")
(declare-function flan-eval-last-sexp "flan-dev")
(declare-function flan-connect "flan-dev")
(declare-function flan-disconnect "flan-dev")
(declare-function flan-describe "flan-dev")
(declare-function flan-show-output "flan-dev")
(declare-function flan-repl "flan-repl")
(declare-function flan-break "flan-dev")
(declare-function flan-doc "flan-dev")
(declare-function flan-dev "flan-dev")
(declare-function flan-dev-quit "flan-dev")
(declare-function flan-dev-restart-program "flan-dev")
;; 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)
(autoload 'flan-repl "flan-repl" nil t)
(autoload 'flan-break "flan-dev" 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)
(defgroup flan nil
"Editing and evaluating Flan."

View File

@ -36,8 +36,8 @@
;;;; No raylib between here and the next banner, which is what the headless
;;;; driver imports this file for. The hash is over exactly this.
(defconst screen-width 1400)
(defconst screen-height 1000)
(defconst screen-width 900)
(defconst screen-height 600)
(defconst cell-size 5)
;; f32: velocity is [f32], and there is no implicit widening.
(defconst gravity f32 0.05)
@ -46,7 +46,7 @@
(defconst brush-size 10)
;; Packed 0xRRGGBBAA. A cell of 0 means empty, so no Option and no tag word.
(defconst colors [4 u32] [0xE6B800FF 0x3B6E8CFF 0xA83232FF 0xCC6B1FFF])
(defconst colors [4 u32] [0xFFF00FFF 0x3B6E8CFF 0xA83232FF 0xCC6B1FFF])
;; Flat, unboxed, statically sized. No headers, so these are exactly
;; rows*cols*4 bytes each — the same memory the Odin port has. Fixed arrays are
@ -283,6 +283,7 @@
(when (rl/mouse-button-released? :left) (next-color))
(step))
(defn draw-grid []
(dotimes [row rows]
(dotimes [col cols]
@ -438,10 +439,16 @@
;; Bare (defn main []) — argv and the i32 status are both optional.
;; Nothing in this loop allocates, so context/temp is never even touched.
(until (rl/window-should-close?)
;; The frame boundary, and the only place a redefinition becomes visible:
;; nothing that could be redefined is on the stack here.
(agent/poll)
(game-update)
;; The frame boundary, and the only place a redefinition becomes visible.
;; An error while installing/evaluating a dev form, or during the update,
;; leaves one CL-style escape hatch: choose `continue' in the editor to
;; abandon this frame and return to the next one with the game still live.
;; Keep drawing outside it. Skipping between BeginDrawing and EndDrawing
;; would leave raylib's frame unbalanced.
(restart-case
(do (agent/poll)
(game-update))
(continue [] (do)))
(rl/begin-drawing)
(game-draw)
(rl/end-drawing)))