Formatter.Render

Render a Logical Printing Tree to its final formatted source string.

renderRoot walks the RootBox's top-level children, renders each one through the Box renderer (Formatter.Render.MakeRenderBox), and joins the results with newlines. Box is the only backend: every construct goes through Formatter.Render.Box's Line/Box IR, and one the renderer does not handle is an Err that fails the format rather than falling back to anything.

This module is also the doorway through the stage barrier. It is the only one on the render side that names LPNode: it calls Formatter.RenderTree.lower on the way in, and everything under Formatter/Render/ sees the position-free RenderNode instead. That is what makes "no source rows are read at render time" a compile error rather than a convention -- see Formatter.RenderTree.

lptToBoxJson : LPNode -> Result String String

Serialise the Box each top-level child renders to as a JSON array (one entry per declaration), for the --box debug flag — the Box analogue of the --lpt LPT dump. An Err from any child (an unhandled construct) aborts.

renderRoot : LPNode -> Result String String

Render every top-level child of the RootBox and join them with newlines.

renderRootChildren : LPNode -> Result String (Array String)

The same render, one string per top-level child instead of one string for the file.

renderRoot is these joined with newlines. They are needed apart by Formatter.Audit.DecisionTrace, which has to say which declaration's output moved between two formats: a decision that flipped inside a declaration whose output is unchanged caused nothing, and reporting it beside the ones that did would bury them.

renderLoweredChildren : RenderNode -> Result String (Array String)

renderRootChildren for a caller that has already lowered the tree.

Formatter.Audit.DecisionTrace is the one such caller, and it needs both halves at once: this render, and the render tree itself, which it walks to read the decisions back. Going through renderRootChildren made it lower the whole tree twice per formatting pass -- four times per --decisions run, which formats twice. Lowering is O(n) and allocates a node per node, so that is not free, and the second copy answers a question the first already answered.

renderRoot and lptToBoxJson keep taking the LPNode: they lower once and have no use for the result afterwards, which is the normal case.