0060. Spacing between participants, and calls that hold what they cause
Status: accepted (sequence-spacing-and-nested-calls change, 28 October 2026). Extends decision 0019.
Context
Section titled “Context”The owner asked for two things in sequence diagrams. First, a way to set the distance between
participants: the layout put a fixed 40 units (scaled by the theme) between neighbouring heads,
gap could only add room before a participant, never take it away, and the first participant’s
gap was read and ignored. A head dragged sideways either reordered or snapped back, so the drag
the owner tried did nothing. Second, a way to say that a call happens inside another one: the
connection between a call, what it causes and its reply was only implied by an
activate/deactivate pair the person wrote and kept balanced by hand, and nothing linked a reply
to the call it answered. The grammar already parsed statements inside a message’s block; the model
reported anything but properties there.
Decision
Section titled “Decision”spacing <n>is a diagram property of sequence diagrams, 0 to 400 diagram units, the distance between neighbouring heads before their gaps; without it the layout keeps 40 scaled by the theme. A written value is not scaled, asgapis not.spaceandgaptake a side.space <event> <n> [before|after]andgap <participant> <n> [before|after],beforeby default; both sides of a pair add up, andspace … afteron the last event lengthens the lifelines. The model JSON writessideon every entry andproperties.spacing.- A
gapmay be negative, down to a floor: the halves of the two heads and 8 units, and the room the labels between them need. When the floor wins, the layout reportslayout.gap-floor(warning) on thegapstatement, naming the amount drawn. A negative gap on the outer side, before the first participant or after the last, isprop.invalid-value: there is nothing beyond it to bring closer. A negativespacestays an error: rows have no measured floor. - The first participant’s
gapdraws, as room left of it inside the drawing. - A head drag writes the gap. Dropped past a neighbour’s centre, the head reorders as before;
dropped short of it, the drag writes
gap <participant> <n>before it (never below 0 on the first participant), and the status line says what was written and what is drawn when the floor wins. A drag under the canvas’s 3-pixel threshold writes nothing. - A call’s block holds what the call causes. A message’s block in a sequence diagram may hold
events (messages, notes, fragments, activations, lifecycle events, dividers and delays) besides
its properties. The receiver is active for the block: the layout draws one bar from the call’s
arrow to the block’s last event, and
checkOrderrefuses adeactivatethat would end it. Explicit activations inside nest deeper, as twoactivates do. returnanswers the call around it.return ["Label"] [as <id>] [{ properties }]is a dashed reply from the callee to the caller, a relation like any other whose statement is thereturn, withreturnsnaming the call’s key in the model and the JSON. Its key is counted with the other relations of that pair, so no existing key moves.returnoutside a call, and an event after areturnin the same list, aresequence.return; areturnin a branch ends that branch.- Edits and the canvas.
nestInCallmoves the events right after a call into its block, their comments with them;unnestCallmoves them out and writes eachreturnas a written reply from the callee to the caller;moveEvententers and leaves call blocks,insertEventappends inside one, and removing a call removes its block with what names the events in it. The canvas binds Alt+N, Alt+Shift+N and Alt+R and lists Nest in the call, Unnest the call and Return to the caller after Activate the receiver; the inspector shows areturnread-only, and gains Gap after, Space after and Participant spacing. - The importers keep explicit pairs. The PlantUML and Mermaid importers write the
activate/deactivatepairs their sources write, and Alt+N and Alt+R make the block.
Alternatives considered
Section titled “Alternatives considered”- No
spacing, onlygaps (S2): tightening a whole diagram would mean onegapper pair. - Resize handles between columns (S3): a new overlay with its own hit-testing and keyboard access, where the head is what the owner drags.
- Coordinates for participants (S4): decision 0019’s rejection stands; positions would contradict the order.
- A theme token or a
placementstatement for the spacing: a theme is shared by many diagrams, and the spacing is about no one target. - Clamping a negative gap silently, or letting labels overlap: conflicts produce diagnostics, never silent moves, and nobody wants overlapping labels.
reply <call>on written replies (N2): every linked call would need an id, and nesting would be implied by position only.returnon explicit activations only (N3, PlantUML’s model): the link would be inferred from the activation stack, and a call without anactivatewould have no inside.++and--suffixes (N4):--is the solid line token, and the lexer takes the longest run; it would need new tokens and parser changes.- A
ReturnEventkind beside messages: every message feature (numbering,space, styles, animations, hit-testing, the inspector) would be written again for returns. - Importers that write call blocks: their snapshots and specs pin the pairs their sources write; the owner can nest with Alt+N afterwards.
Consequences
Section titled “Consequences”- The language stays 0.2:
spacing, the sides, the negativegap, call blocks andreturnare additive, and every valid file keeps its model, keys and drawing, except a first participant’sgap, which now draws (the defect it was, named in the changelog). Relation.nodeis a relation statement or areturnstatement;Relation.returns,MessageEvent.eventsandEventPlace’s{ message }are new public surface of the model, andnestInCallandunnestCallof the edit package.setRelationLineandreconnectRelationrefuse areturn.- New diagnostic codes:
layout.gap-floorandsequence.return. - The corpus gains
valid/sequence/calls.meltemi,invalid/layout.gap-floor.meltemiandinvalid/sequence.return.meltemi; every model JSON gainsreturns,sideandspacing. - Open for later: a row spacing property (
spacing 40 20), and nesting by dragging events onto a call. - Decision 0065 makes the floor of a negative
gapcount a label’s longest word instead of its whole width: message labels wrap at the room between their lifelines, so a head drag comes as close as the heads allow.