clickable_plantuml
Sphinx extension that makes PlantUML diagrams clickable by injecting hyperlinks into rendered SVG/PNG diagrams.
Sphinx Integration
The extension hooks into the native Sphinx build lifecycle. URLs are computed by
app.builder.get_relative_uri(), which works for any builder and
output directory layout.
Sphinx build lifecycle clickable_plantuml hooks
═══════════════════════════════════ ═══════════════════════════════════════
builder-inited ───► on_builder_inited()
│ (one-time setup) Load all *plantuml_links.json files
│ from srcdir (recursive).
│ Store {puml_basename → alias_map}
│ in app.env.
│
├─ READ PHASE ──────────────────────────────────────────────────────────────
│ for each document:
│ env-purge-doc ───► on_env_purge_doc()
│ │ (incremental rebuild) Remove stale puml→docname entries
│ │ for the document being re-read.
│ │
│ parse RST → doctree
│ │
│ doctree-read ───► on_doctree_read()
│ (per document) Traverse the parsed doctree.
│ For every plantuml node that has a
│ filename attribute, record
│ {puml_basename → docname} in app.env.
│ Warn on basename collisions.
│
│ env-merge-info ───► on_env_merge_info()
│ (parallel builds only) Merge puml→docname maps gathered
│ by worker sub-processes into the
│ main environment.
│
├─ WRITE PHASE ─────────────────────────────────────────────────────────────
│ for each document:
│ post-transform / resolve
│ │
│ doctree-resolved ───► on_doctree_resolved()
│ (per document) For each plantuml node, look up the
│ alias_map from app.env.
│ Resolve target .puml → docname, then
│ call app.builder.get_relative_uri()
│ to get the correct relative URL.
│ Append url of <alias> is [[url]]
│ directives to node['uml'] before
│ sphinxcontrib-plantuml renders it.
│
build-finished
How It Works
Link discovery (
builder-inited) – Scans for*plantuml_links.jsonfiles in the Sphinx source directory.Diagram location mapping (
doctree-read) – As Sphinx reads each document, the extension traverses the parsed doctree to record whichdocnamecontains which.pumldiagram (keyed by basename). Basename collisions across documents are reported as warnings.URL resolution & link injection (
doctree-resolved) – For each plantuml node, resolves target.pumlreferences to the docname that contains the target diagram, generates a relative URL viaapp.builder.get_relative_uri(), and appendsurl of <alias> is [[url]]directives to the PlantUML source before rendering.Incremental / parallel support –
env-purge-docremoves stale entries when a document is re-read;env-merge-infomerges state from parallel worker processes.
Automatic JSON Generation (Bazel)
plantuml_links.json is generated by the architectural_design() rule.
The architectural_design() rule invokes //tools/plantuml/linker:linker on all
.fbs.bin FlatBuffers files produced by the PlantUML parser. See
Link Mapping Format for a detailed
description of which links are emitted.
Algorithm
Given the set of .fbs.bin files for one architectural_design() target:
Build a top-level index – For each diagram, collect every component whose
parent_idisNone(i.e. it is not nested inside another component). The index mapsalias → diagram file.Emit links – For every component in every diagram, look up its alias in the top-level index. If a different diagram defines that alias as a top-level component, emit a link entry:
source_file = diagram that contains the reference source_id = alias of the component target_file = diagram that defines it as a top-level component
Deduplicate – Sort and deduplicate so that each
(source_file, source_id)pair has exactly one target (first alphabetically). Duplicatesource_identries within the same source diagram are removed because PlantUML’surl of X is [[…]]directive supports only one URL per alias.
Concrete Example
' adas_overview.puml — subsystem context
@startuml
component ADAS
component BrakeController
component LaneKeepAssist
ADAS --> BrakeController
ADAS --> LaneKeepAssist
@enduml
' brake_controller.puml — component detail
@startuml
component BrakeController
interface BrakeDemandIF
interface WheelSpeedIF
BrakeController --> BrakeDemandIF
BrakeController <-- WheelSpeedIF
@enduml
Generated links — one in each direction:
{
"links": [
{
"source_file": "adas_overview.puml",
"source_id": "BrakeController",
"target_file": "brake_controller.puml"
},
{
"source_file": "brake_controller.puml",
"source_id": "BrakeController",
"target_file": "adas_overview.puml"
}
]
}
Clicking BrakeController in the overview navigates to its detail diagram;
clicking it in the detail diagram navigates back to the overview.
ADAS and LaneKeepAssist appear as top-level only in adas_overview.puml and
have no dedicated detail diagram, so no links are emitted for them.
Link Mapping Format
Place one or more *plantuml_links.json filesinside the Sphinx source directory:
{
"links": [
{
"source_file": "my_diagram.puml",
"source_id": "ComponentA",
"target_file": "other_diagram.puml"
}
]
}