Wiki source code of Macro Transformation
Last modified by Eleni Cojocariu on 2026/10/13 18:18
Hide last authors
| author | version | line-number | content |
|---|---|---|---|
![]() |
2.1 | 1 | The macro transformation is the [[rendering transformation>>doc:rendering:Main.Transformations]] that executes macros. It walks the XDOM a parser produced, looks up the component registered under each macro call, runs it, and puts the blocks it returned in the place of the call. [[Rendering Macros>>doc:documentation.xs.dev.rendering-macros.WebHome]] describes what a macro is made of; this page describes the engine that runs it. |
| 2 | |||
| 3 | == The Transformation That Runs First == | ||
| 4 | |||
| 5 | Several transformations can run over the same XDOM, and ##rendering.transformations## decides which, as [[Rendering Transformations>>doc:documentation.xs.admin.configuration.rendering-transformations.WebHome]] describes. They run by priority, and the macro transformation declares ##100##, low enough to be among the first, so that the transformations after it see the content the macros produced rather than the calls. | ||
| 6 | |||
| 7 | == Finding the Next Macro to Execute == | ||
| 8 | |||
| 9 | The transformation does not walk the tree in reading order. It collects the macro calls that have a macro registered for the syntax being rendered into a queue ordered by macro priority, and takes the lowest first. Each result goes back into the tree rather than into the output, so the queue can still grow while it is being drained. | ||
| 10 | |||
| 11 | What happens after each execution depends on the promise the macro made. A macro that declared its execution isolated cannot have inserted a call anywhere else, so only the blocks it returned are scanned and added to the queue. Any other macro forces a full rescan of the XDOM, because a call may now sit anywhere in it. [[Macro Execution Rules>>doc:documentation.xs.dev.rendering-macros.macro-execution-rules.WebHome]] covers that promise from the macro's side. | ||
| 12 | |||
| 13 | {{plantuml}} | ||
| 14 | @startuml | ||
| 15 | !theme bluegray | ||
| 16 | start | ||
| 17 | :Scan the XDOM for calls | ||
| 18 | that have a macro registered | ||
| 19 | for the current syntax; | ||
| 20 | :Order them in a queue | ||
| 21 | by macro priority; | ||
| 22 | repeat | ||
| 23 | :Take the first macro | ||
| 24 | off the queue and execute it; | ||
| 25 | :Replace the call by the blocks | ||
| 26 | it returned, wrapped in a | ||
| 27 | macro marker block; | ||
| 28 | if (execution declared isolated?) then (yes) | ||
| 29 | :Queue the calls the | ||
| 30 | returned blocks contain; | ||
| 31 | else (no) | ||
| 32 | :Scan the whole XDOM again; | ||
| 33 | endif | ||
| 34 | repeat while (a call left, under | ||
| 35 | the recursion limit?) is (yes) | ||
| 36 | stop | ||
| 37 | @enduml | ||
| 38 | {{/plantuml}} | ||
| 39 | |||
| 40 | The loop is bounded. The transformation counts the executions of macros that were themselves produced by another macro and stops after 1000 of them, so a macro generating itself leaves unexecuted content behind instead of hanging the request. | ||
| 41 | |||
| 42 | The component the transformation asks for each call is described in [[Macro Transformation API>>doc:documentation.xs.dev.rendering-macros.macro-transformation.api.WebHome]]. | ||
| 43 | |||
| 44 | == When a Macro Does Not Run == | ||
| 45 | |||
| 46 | Four things stop a macro, and each of them replaces the call by an error block in the page rather than failing the rendering. | ||
| 47 | |||
| 48 | |=What happened|=What the page shows | ||
| 49 | |No macro is registered under that name, for this syntax or for all syntaxes|An unknown macro error naming the id, so a misspelled macro name is visible where it was written | ||
| 50 | |The call sits inside a paragraph or a list item and ##supportsInlineMode## returns ##false##|A standalone macro error explaining that the macro needs a line of its own | ||
| 51 | |A parameter value cannot be converted into the parameters bean|An invalid parameters error carrying the conversion failure | ||
| 52 | |##execute## throws anything at all|A failed to execute error carrying the cause | ||
| 53 | |||
| 54 | One broken macro therefore costs its own block, never the page. The single exception is a macro that takes its own call out of the XDOM, as [[a deprecated macro handing over to its successor>>doc:documentation.xs.dev.rendering-macros.macro-execution-rules.WebHome]] does. | ||
| 55 | |||
| 56 | == The Context a Macro Receives == | ||
| 57 | |||
| 58 | ##execute## is given a ##MacroTransformationContext##, which carries what the macro cannot read from its own parameters and content. | ||
| 59 | |||
| 60 | |=Member|=What a macro reads it for | ||
| 61 | |##getCurrentMacroBlock()##|Its own call, to read a raw parameter or to find where in the page it sits | ||
| 62 | |##getXDOM()##|The whole document being transformed | ||
| 63 | |##isInline()##|Whether the call sits inside a paragraph, so the macro can return inline blocks | ||
| 64 | |##getTransformation()##|The running transformation, for a macro that has to transform content of its own, as the ##include## macro does | ||
| 65 | |##getTransformationContext()##|The syntax being rendered and the restricted flag | ||
| 66 | |||
| 67 | == Reaching the Rendering in Progress from Anywhere == | ||
| 68 | |||
| 69 | A component that is not a macro reads the same state through the ##RenderingContext## role: the syntax, the target syntax, the document being rendered and the macro call currently executing. It is the supported way for code outside a macro to know that a rendering is under way and in what state. | ||
| 70 | |||
| 71 | That context is set by the default ##TransformationManager## as it drives the transformations. Code that executes a ##Transformation## directly leaves it unset, and no public API sets it. Go through the ##TransformationManager## whenever a transformation has to be run. | ||
| 72 |
