Visualization & tooling¶
A statechart is worth drawing, and worth editing with help. harel renders any machine to a diagram and ships a language server plus a VSCode extension with a live preview.
Render to a diagram¶
The same Definition you run can be rendered — to PlantUML or to Mermaid — by walking the node
tree. No diagram server, no Java:
from harel import definition_from_dsl, render # render = PlantUML
from harel.viz import mermaid # the browser-friendly sibling
SOURCE = """
event Finish {}
machine order {
initial Cart
state Cart {}
final Done success {}
from Cart to Done on Finish
}
"""
defn = definition_from_dsl(SOURCE, "order")
print(render(defn))
print("---")
print(mermaid.render(defn))
[*] --> Cart
Cart --> Done: Finish
Done --> [*]
---
stateDiagram-v2
[*] --> Cart
state "Done" as Done
Done : outcome#58; success
Cart --> Done : Finish
Done --> [*]
(#58; is Mermaid’s entity for a colon, drawn as : — a bare one inside a state’s description is
a syntax error in Mermaid 11.)
PlantUML is good for docs and review; Mermaid (stateDiagram-v2) renders directly in a browser
— which is what the live preview below uses. Both walk the tree by reference and handle the full
language: composites carry their hooks, orthogonal regions become nested concurrent blocks, and
a transition’s guard is written out under its event — and/or/not and named guards
included (Pay<br/>[(tier == 'gold' or spend > 1000) and region == 'eu'] in Mermaid) — followed
by its set as a UML effect (/ context.n = context.n + 1), and a choose is drawn as a choice
node with each branch’s guard ([context.attempts >= 3]) and its else.
Highlighting states¶
mermaid.render(defn, active=[...]) highlights the states it is given, by full path — an
execution’s active_path, and its regions’ for an orthogonal state:
exe_paths = ["Cart"] # e.g. [exe.active_path] for a running execution
diagram = mermaid.render(defn, active=exe_paths)
assert diagram.splitlines()[-1] == "class Cart active"
It appends a classDef active … and a class … active line; active_style= sets the style (a
Mermaid classDef body), and a path the machine doesn’t have raises ValueError. For classes or
styles of your own, mermaid.node_id(defn, path) gives the id the diagram uses for a state.
The language server¶
harel.lsp is a DSL language server (install the lsp extra, run python -m harel.lsp
or the harel-lsp script). The pure analysis core, lsp.analyze(text), parses and validates
into a list of Diagnostics — the same validate findings from step 14,
mapped back to their source location. On top of that the server provides:
Diagnostics — parse errors (with a caret snippet) and validation findings as you type.
Hover, go-to-definition, completion — for events, guards, fragments, and
invoketargets, resolving acrossimports (go-to-definition oninvoke ns.reviewjumps into the imported file; go-to-definition on an import path opens that file).
The VSCode extension & live preview¶
editor/vscode/ is a VSCode extension: TextMate syntax highlighting, an LSP client, and a
live statechart preview. Open Statechart Preview renders the active .stm as a Mermaid
diagram and re-renders as you type, drawn by a vendored mermaid.min.js (offline, no CDN). The
preview pans and zooms, offers a picker across every machine/fragment/imported submachine in the
document, and keeps the last valid diagram on screen while you’re mid-edit. It even renders a
fragments-only file by wrapping the fragment in a synthetic preview machine.
The formatter¶
harel-fmt (or python -m harel.fmt) is a brace-aware reindenter — canonical 2-space
indentation, comment-preserving, idempotent. make fmt-stm formats every .stm in place;
make fmt-stm-check (folded into make lint) verifies formatting in CI.