The manual says how a form is indented, and NEXT loses what landed

The indentation rules were written and tested but never described anywhere a
user would look. MANUAL.md had no section on editing at all — it starts at
`C-c C-c' and assumes the file is already written — so the rule that cost the
friction, a binding vector lining up name under name, was only visible by
trying it.

What is written down is what was checked, not what the port was aimed at: the
call fallback, the `handler-bind' clause vector, `defn' parameter alignment
with a return type after it, and `restart-case' clause bodies were each
reindented from scratch and the manual quotes the result.

NEXT.md keeps the half of the field-label handover that is still open. The
printer in render.ml has to move in the same commit as the inspector that
parses it, and that is the inspector lane's; the font-lock half is done here,
so only that half is struck.
This commit is contained in:
Joseph Ferano 2026-09-12 16:15:37 +07:00
parent c9e9d93a91
commit 64342c406e
2 changed files with 54 additions and 6 deletions

17
NEXT.md
View File

@ -286,9 +286,11 @@ BUILT.md, "The colon belongs to keys".
**What it left for the Emacs lane, both verified.** `render.ml` still *prints* a struct with colons, deliberately:
`emacs/flan-inspect.el:165` parses that output and hard-codes the colon when it reads a field out, so the printer
has to move in the same commit as its reader. And `flan-mode.el:61` font-locks `:name` as a constant with nothing
matching `.name`, so a field label is now unfontified where it used to be coloured. Neither is urgent; both belong
with whoever next opens `emacs/`.
has to move in the same commit as its reader. That half is still open and belongs with whoever next opens the
inspector. ~~And `flan-mode.el:61` font-locks `:name` as a constant with nothing matching `.name`, so a field label
is now unfontified where it used to be coloured.~~ **The font-lock half is done:** a field is drawn as a constant in
both of the spellings that exist while the corpus moves, so `{.x 1}` and the accessor `(.x v)` read alike, and the
keyword rule stayed where it was because the colon still means an enum member and a map key.
**7. `Map` follows Odin's implementation.** Read `base/runtime/dynamic_map_internal.odin` before writing any of it;
the checkout is at `~/Repositories/Odin`. Three properties are the ones worth copying, and they are stated in its own
@ -423,9 +425,12 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
whole of the resource-cleanup answer.
4. **The Emacs batch. Disjoint from the compiler, so it runs in parallel with anything above.** Globals in the break
buffer; the buffer opening itself when the program stops; the indentation rewrite with `clojure-mode` as the
reference; `#_`; hex, binary and addresses on primitives in the inspector. The indentation one is worth doing first
within this batch — it costs friction on every keystroke today.
buffer; the buffer opening itself when the program stops; ~~the indentation rewrite with `clojure-mode` as the
reference~~; ~~`#_`~~; hex, binary and addresses on primitives in the inspector. **The indentation rewrite and `#_`
are done.** The indenter is ported from `clojure-mode`'s source rather than derived from it — `flan-mode` still
requires nothing outside stock Emacs — and it aligns a binding vector name-under-name, which is the bug that cost
friction on every keystroke. `defn` parameter lists and `restart-case` clause parameters were the same shape and
came with it. What remains in this batch is the break buffer and the inspector, and they are independent.
5. **Union values, then the macro expander, then `Result`/`try`.** Promoted above `Handle` on the author's call —
macros are the thing most worth wanting, and unions are the only thing between here and them.

View File

@ -263,6 +263,49 @@ the inner binding.
---
## Writing it
`TAB` indents the line, and `C-M-q` the form under point. The rules are ported
from `clojure-mode`'s, because Clojure has the shapes Flan has and Emacs Lisp
does not — vectors that bind, bracket variety, and keys inside braces.
**A binding vector lines up name under name.** The second and later bindings of a
`let` sit under the first one's *name*, not under its value:
```
(let [vel (+ gravity (at velocity row col))
y (min (- rows 1) (+ row (i32 vel)))]
…)
```
The same rule draws `defn` parameter lists, `restart-case` and `handler-bind`
clause parameters, and both spellings of a struct literal — they are all a vector
or a brace read in pairs, so they are all indented as one.
**A body indents two.** `let`, `if`, `when`, `while`, `match`, `restart-case`,
`handler-bind`, `defn` and the `def…` forms all put their body two columns in
from the head. What differs between them is only how many forms come *before*
the body and stay on the head's line — a `let`'s binding vector, an `if`'s test,
a `restart-case`'s protected form — and the indenter knows that count per form.
A form it has no entry for is treated as a call: the arguments line up under the
first argument, not two in. That is the fallback, and it is what you want for
`(rl/draw-rectangle x y w h)` and for every function you write.
`defn` carries a return type between the parameter vector and the body, and it is
optional. The indenter does not need to know which — everything after the head
indents two, which is the right answer for the name, the parameters, a return type
if one is written, and the body alike.
**A field is drawn as a constant**, in the accessor `(.x v)` and as a label in
`{.x 1.0}`. The colon is still a constant too; it means an enum member, `:green`,
and a key in a map.
Nothing here needs a running program. Indentation and colouring are the major
mode's, so they work in a file you have only opened.
---
## Getting around
| Key | Does |