PlantUML Rendering Improvements - #499
Merged
Merged
Conversation
The dependency and special-character tests assembled their expected diagrams from string literals joined with Environment.NewLine, which hides the shape of the output. Record them as snapshots instead. Also pin how hand-built slices render today: slices sharing parent packages, a package-only slice with a colour, flat slices next to nested ones, C4-style boundaries and a nested slice with a hyperlink. These cover more than the fixture assembly does (several root packages, a component next to a deeper package), and show every shared package being re-opened once per slice and the hyperlink of a nested slice landing after its closing braces. Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
PlantUmlSlice rendered every nested slice on its own, together with the full chain of packages around it, so a diagram re-opened each shared parent package once per slice. PlantUML merges re-opened packages, so the drawing was fine, but the source read nothing like a hand-written diagram. PlantUmlDiagram now collects the nested slices into a PlantUmlSliceTree and renders each root package once, where its first slice used to be. Slices without a namespace keep their position. A package-only slice (as used by focus-on) colours its package node instead of re-opening it. The rendered diagrams are unchanged: the old and new snapshots produce the same clusters, entities, links and fills. While moving the component line, put the hyperlink in front of the colour. PlantUML rejects "[X] #color [[link]]" as a syntax error, and for nested slices the link used to be appended after the closing braces. Indent the contents of every package and boundary by two spaces per level, as a hand-written diagram would. Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
…ents PlantUML identifies a package by its bare name, not by where it is nested. The slice exporter writes every package by its last segment and points package-level arrows at that segment as well, so two packages called "Domain" under "Orders" and "Billing" are drawn as one: both "Model" components end up in the same "Domain", and the other parent is left as an empty package. An arrow into "Billing.Domain" is written as "-> Domain" and ends in whichever "Domain" PlantUML saw first. Add a SameNamedSubnamespaces fixture in which Orders and Billing both contain Domain.Model and Web depends on Billing.Domain, and pin what the exporter currently produces for it, in plain and C4 style. Pin the builder output for the same shape, the package arrows of every dependency type and an empty package, which the fix has to keep readable. Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
PlantUML identifies a package by its bare name, so the exporter merged
every two packages with the same last segment into one, and a package
arrow ("-> Domain") ended in whichever of them PlantUML registered
first.
Label every package with its own segment but identify it by its full
path ('package "Domain" as App.Billing.Domain', and the same for C4
boundary IDs), and point package arrows at that path. It is the slice
description the dependency already holds, so no lookup is needed. Both
have to change together: with aliased packages, an arrow to a bare name
no longer matches anything and PlantUML draws a free-floating entity
for it.
An empty package is drawn by PlantUML as a single node that shows its
alias above its label once the two differ. Give it a blank placeholder
label, so it stays a regular package that shows only its label. That
changes the look of the empty packages in the focus-on diagrams from a
flat tab to a package box. C4 boundaries do not show their ID and need
no placeholder.
Apart from that, the existing diagrams render the same clusters,
entities, links and fills as before; only the new SameNamedSubnamespaces
diagrams change, and now keep both Domain packages apart.
Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #499 +/- ##
==========================================
+ Coverage 86.31% 86.33% +0.02%
==========================================
Files 259 260 +1
Lines 12450 12496 +46
Branches 1210 1216 +6
==========================================
+ Hits 10746 10789 +43
- Misses 1370 1372 +2
- Partials 334 335 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
mak638
approved these changes
Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The slice exporter wrote every package by its last segment only. PlantUML identifies a package by that bare name, so two packages called
DomainunderOrdersandBillingwere drawn as one, and a package arrow (-> Domain) ended in whichever of them PlantUML registered first. This PR first emits the slice packages as a single tree and then identifies every package by its full path. Two test-only commits pin the output before each production change, so every change to the generated diagrams shows up as a snapshot diff. See the commit messages for details.NestedSliceWithHyperlinkTestis fixed by the next commit.BuildUmlByDependenciesWithObjectsWithNoDependenciesTestandSpecialCharactersInComponentNamesTestdeclare aclassand then draw component arrows ([a] --|> [b]). This already happens onmain, whose tests asserted the same strings. This PR doesn't address it.SameNamedSubnamespacesfixture in whichOrdersandBillingboth containDomain.ModelandWebdepends onBilling.Domain, and pins the broken output in plain and C4 style. The "Before" column below is what this commit pins: bothModelcomponents end up in oneDomain,Ordersis left as an empty package, and the extension arrow runs between the twoModels inside the merged package.package "Domain" as App.Billing.Domain), C4 boundary IDs use the same path, and package arrows point at it. BothDomainpackages now stay apart:The C4-style builder snapshot (
C4StyleSameNamedBoundariesUnderDifferentParentsTest) is fixed in the same way.One side effect is visible: once its alias and label differ, PlantUML shows an empty package's alias above its label. Empty packages therefore get a blank placeholder label inside them (
label " " as A.Y.__empty__), so they are drawn as a regular package box instead of a flat tab. C4 boundaries don't show their ID and need no placeholder.This affects the empty packages in the focus-on diagrams. Graphviz now routes the arrow into
Slice1around the outside of the diagram; the diagram still has the same packages, components and dependencies:Apart from these, the existing diagrams render exactly as before.
How the images were made
Each
.verified.txtsnapshot was rendered at the commit before and after the change, with PlantUML 1.2026.8 and Graphviz. The remote!includeof C4-PlantUML was replaced by PlantUML's bundled copy (!include <C4/C4_Container>). "Renders exactly as before" means the SVGs are identical once the attributes that only encode the diagram source (embedded source, source line numbers, qualified names and element IDs) are removed.