Expand description
Cross-reference resolver (manifest §6 stage 3, MVP 1).
Walks a lowered [Document] and, in three passes:
- Assigns hierarchical
numberattributes to every [NodeKind::Section] ("1","1.1","1.2","2"), keyed off the existinglevelattribute. - Assigns flat document-order
numberattributes to every numbered [NodeKind::Figure] ("1","2","3") and stamps a visible"{supplement} N: …"label onto each captioned figure. Figures are not hierarchical, so the counter never resets. A figure can opt out withnumbered: false(skipped: no number, no caption prefix, does not advance the counter) or swap its supplement word withsupplement: "…"(issue #76). - Builds a
label → LabelTargetindex from every block carrying alabelattribute, then rewrites each [NodeKind::Reference]’stextattribute to its target’s resolved string.
The label index is typed: each entry records what kind of thing
the label points at (section, figure, or generic block). A section
reference renders as its bare hierarchical number ("1.2"); a figure
reference renders kind-aware as "{supplement} {n}" ("Figure 1" by
default) from the figure’s flat document-order number. Generic targets
(paragraphs, raw blocks, images, skipped figures, …) carry no counter
and render as the bare label, matching prior behavior.
Diagnostics:
MOS0030: a label is declared more than once. The first occurrence wins; later occurrences keep their numbering but are not added to the index. Each duplicate also carries a structured rename [Suggestion]; the next free{label}-N(N >= 2) that no other declaration or earlier suggestion already uses: over the duplicate label token span.MOS0033: a@labelreference targets a label that doesn’t exist. The reference’s text is left at its lowered placeholder (?label?) so it remains visible in the rendered output.
Manifest §6 stage 3 calls for a fixpoint loop because later stages (page references, TOC) can re-trigger resolution. MVP 1 only needs a single pass: section numbering doesn’t depend on layout, but the driver shape mirrors the manifest’s “internal fixpoint” anyway: the loop runs until no rewrite changes the document, with a hard cap to detect pathological cycles.
Every pass is idempotent: resolve is public and re-entrant, so
running it twice: inside the fixpoint above, or from a future
page-reference stage: must reproduce the same document rather than
compounding edits. Numbering overwrites attributes with the same
value; caption labelling re-derives from a preserved source instead
of re-reading the already-stamped text (which would nest the label
into "Figure 1: Figure 1: …").
Structs§
- Label
Target 🔒 - An entry in the label → target index.
Enums§
- Label
Target 🔒Kind - What a label points at, captured at index-build time.
Constants§
- MAX_
FIXPOINT_ 🔒ITERATIONS - Cap on resolver fixpoint iterations. MVP 1 always converges in one pass; the cap is a safety net against forward-reference loops once page numbering lands in MVP 3+.
Functions§
- build_
label_ 🔒index - Build the
label -> LabelTargetindex from every label-declaring block, reportingMOS0030for redeclarations. The first declaration of a label wins; later occurrences keep their numbering but are not indexed, and each carries a related note pointing at the first declaration plus a structured rename [Suggestion]; the next free{label}-N: over the duplicate label token span (see the module-level docs). Reads the document only, soresolvestays idempotent. - captured_
number 🔒 - Read a node’s resolved
numberattribute, or an empty string if it has none. Both section and figure numbering stash their counter there before the label index is built; an empty result means the numbering pass didn’t reach the node (a resolver/lowerer bug). - classify_
target 🔒 - Classify a labelled node into a
LabelTargetKind. Only nodes that actually declare a label reach this function: references are filtered out by the caller. - declared_
labels 🔒 - Collect every label declared anywhere in the document: any non-reference
block carrying a
labelattribute: regardless of document order or duplication. The duplicate-rename suggestion consults this set so it never proposes a name that some other declaration already uses. - edit_
distance 🔒 - Levenshtein edit distance between
aandbover their bytes. - figure_
caption_ 🔒text - Find the text node of a figure’s caption, if it has one. The lowerer
tags the caption paragraph with
role = "caption"and gives it a single [NodeKind::Text] child carrying the caption string. - figure_
is_ 🔒numbered - Whether a figure participates in the auto
Figure Ncounter. A figure opts out with#figure(numbered: false)(issue #76), recorded by the lowerer as anumbered = falseattribute; absence means numbered. - figure_
label_ 🔒prefix - Join a figure’s supplement word and number into the cohesive label
token used in both captions and references:
"Figure\u{00A0}1", non-breaking so the word never wraps off its number. An empty supplement renders the number alone ("1"), with no word and no leading space. - figure_
supplement 🔒 - The human-facing supplement word prefixed to a figure’s number in generated reference and caption text; the “Figure” in “Figure 1”.
- figure_
supplement_ 🔒attr - The supplement word for a figure’s caption and its references. An
explicit
#figure(supplement: …)value wins: including the empty string (supplement: ""/supplement: none), which means “number only, no word” (the “no visible prefix” form). Only an absent supplement falls back to the localizedfigure_supplementdefault ("Figure"). - is_
reference_ 🔒label - Whether
labelcan be spelled as an@reference: i.e. it is drawn from the reference grammar’s alphabet[A-Za-z0-9_:.-](mirrorsscan_label_charsinmos-parse).#figure(label: …)and#image(label: …)accept arbitrary strings, so the label index can hold names such as"intro x"or non-ASCII labels that an@…reference can never name; suggesting one would produce a fix that does not parse. - label_
span 🔒 - nearest_
label 🔒 - The single nearest resolvable label to
unknown, when one is a reasonable near-miss rather than an unrelated string; the candidate for a “did you mean@intro?” fix on an unknown reference. - nodes_
of_ 🔒kind - Collect the ids of every node of
kindin document order.nodes()iterates the arena by ascending [mos_core::NodeId] (allocation order), and the lowerer allocates nodes in source order, so the result is stable document order regardless of nesting depth. Shared by figure numbering andsection_orderso both passes agree on what “document order” means. - nonconflicting_
rename 🔒 - Pick a deterministic, collision-aware rename for a duplicated
label: the smallest integer suffixN >= 2whose{label}-{N}is not already indeclared. Boring and stable; no similarity ranking, but it steps over existing labels so the suggested fix never re-creates the clash it resolves. Among the firstdeclared.len() + 1candidates at least one is free (pigeonhole), so the bounded search always yields a name. - number_
figures 🔒 - Assign flat, document-order numbers to every figure (
"1","2","3", …) and stamp a visible"Figure N: …"label onto each captioned figure. Figures are not hierarchical, so the counter never resets. - number_
sections 🔒 - Walk the document depth-first and assign hierarchical numbers to
every section based on its
levelattribute. Sections without a readableleveldefault to depth 1. - read_
str_ 🔒attr - Read a string attribute off a node by id, cloning it out.
Noneif the node is missing or the attribute is absent or non-string. - render_
target 🔒 - Compute the display string for a reference to
target. - resolve
- Run the resolver pass over
documentin place. Returns any diagnostics produced; the document is modified regardless of whether errors are present so partial output is still renderable. - rewrite_
references 🔒 - Rewrite each
Referencenode’stextattribute to point at its target. Returns true if any node was mutated this iteration: callers use that signal to drive the §6 stage 3 fixpoint loop. - section_
order 🔒 - validate_
page_ 🔒references - Report an undeclared label in a
@page(label)reference asMOS0033, mirroring the@labelcross-reference check. A page reference resolves to a page number later, through the layout fixpoint (issue #72), but an unknown label is a lower-time error exactly like a bad@ref, and catching it here meansmos checkreports it without needing to lay the document out.