diff --git a/changes/unreleased/calc-parameter-pins.fixed.md b/changes/unreleased/calc-parameter-pins.fixed.md new file mode 100644 index 0000000000..b746b8f477 --- /dev/null +++ b/changes/unreleased/calc-parameter-pins.fixed.md @@ -0,0 +1 @@ +- A tree rendering (`-render '#tree:…'`, `%viz`, the Jupyter kernel's default) drew the directed parameters of a calculation, action or constraint definition or usage — `in x`, `out y`, `return` — as `attribute` members with membership edges, and an anonymous `return` as a bare `attribute`. They are now parameter pins of their owner's node, direction first (`in x : Integer`), the anonymous result named `result` as the standard names it; a usage typed by a definition shows each parameter once, the definition's as inherited. The text form lists them under the node, DOT and D2 draw them as pins, Mermaid and PlantUML list them in a `parameters` compartment. diff --git a/docs/project/view-rendering-forms.md b/docs/project/view-rendering-forms.md index bf7d7cdacb..1b4daab4c1 100644 --- a/docs/project/view-rendering-forms.md +++ b/docs/project/view-rendering-forms.md @@ -43,7 +43,18 @@ the usage's end — the glyphs of SysML v2 §8.2.3.6, the diamond read from the `Feature::isComposite` (`semantics.Model.UsageIsComposite`: a `ref`, an attribute, a port outside a port, an end, a directed or an enumeration usage is non-composite). The writers draw that edge in place of the containment line they draw to every other child, so a usage hangs off its owner by its -membership glyph alone. A usage listed in a compartment row instead of drawn as a node (the +membership glyph alone. The directed parameters of a calculation, action or constraint definition +or usage — `in`, `out`, `inout` and `return` — are no member nodes of a tree: `treeParameter` +(`tree_parameters.go`) pins each on its owner's node as a `Port` with `Parameter` set and the +direction `pinDirection` reads, where §8.2.3.17, §8.2.3.19 and §8.2.3.20 set a calc's, action's and +constraint's parameters (`param-l*`, `param-r*`, `param-b*`). A `return` declaring no name is the +`result` pin, named by the `Function::result` it redefines (§7.19.2); a usage typed by a definition +pins each parameter once, its own redefining ones as declared and the definition's others as +inherited (`^y`). The text form lists the pins under their node direction first (`in x : Integer`, +the type under `typings`), DOT and D2 draw them as pins, and Mermaid's flowchart and PlantUML's +class diagram, which have no pin on a tree node, list them in a `parameters` compartment +(`labeller.parameterLines`). The pins are the tree's model structure and stay whatever +`parameter-symbols` says, as its members do. A usage listed in a compartment row instead of drawn as a node (the compartment foldings, or a positioned tree's unplaced members) folds its membership and typing into one edge from its owner to each drawn definition, labelled with the usage's name and multiplicity at the definition's end — the property association a block definition diagram draws. Both @@ -451,7 +462,7 @@ table records what each rendering feature writes: | Feature | Mermaid syntax and behavior | | --- | --- | | Definitions, regions, package kinds | Square flowchart nodes, `n0["…"]`; cases use stadium nodes, actors and subjects use keyword-bearing rectangles, and objectives use note nodes. Other non-symbol leaves are rounded `n0("…")` nodes. Cameo follows the DOT skin's rounded rule. | -| Tree containment | Plain shaped nodes joined by `---`; tree nodes are never subgraphs and have no synthetic anchors. | +| Tree containment | Plain shaped nodes joined by `---`; tree nodes are never subgraphs and have no synthetic anchors. A calc's, action's or constraint's parameter pins are lines of the node's label under a `parameters` heading (`in x : Integer`). | | Initial, final, junction, fork and join | `f-circ` for initial and junction nodes, `fr-circ` for final nodes, and `fork` for fork/join bars. Only fork/join bars use the `control` class. Named fork/join nodes are listed in a notice because the bar draws no label. | | Decision, merge, choice and history | Diamonds; empty and synthesized decision names use a blank diamond. Shallow/deep history use `(("H"))` and `(("H*"))`. | | State pseudostates and final transitions | Mermaid state stereotypes (`<>`, `<>`, `<>`) and `[*]` for initial/final markers. A final in the same state body is implicit; cross-body final transitions retain the explicit final and receive a notice. | @@ -1203,7 +1214,8 @@ pseudostate shape only when nothing else is attached. Mermaid and DOT trees draw containment — a tree of edges, no nested containers — so the three forms show the same picture and a tree has no blocks. The Pilot's `comp def`/`comp usage` element kinds exist only in the PlantUML fork and are not emitted; every element is a standard - `class`. + `class`, a calc's, action's or constraint's parameter pins listed in its label under a + `parameters` heading, as the Mermaid tree lists them. - **`interconnection`** — nested `rectangle` blocks: a node with children is `rectangle "…" as n <> {` … `}`, indented two spaces a level, a leaf a one-line `rectangle`. A port is a nested rectangle inside its owner, not a `portin`/`portout`: the released jar's port grammar @@ -1391,8 +1403,8 @@ attribute a rendering in the model chooses it by: | Element | Attribute | What it draws | Optional by | | --- | --- | --- | --- | -| `parameter-symbols` | `showParameterSymbols` | A case's `actor`, `subject` and `stakeholder` parameters and an action's parameter pins as squares on the node's boundary. Without them a connection that ended at a parameter ends at the node that declares it: one line per connection, one node per actor. A tree lists a case's parameters as members, which stay. | §7.26.5; §8.2.3 `distinguished-parameter`, `distinguished-parameter-link` | -| `port-symbols` | `showPortSymbols` | A part's ports, and a constraint's parameters, as pins on its boundary. Without them a connection that ended at a port ends at the part, a binding that ended at a parameter at the constraint; a port drawn as a nested member of a tree is model structure and stays. Default. | §7.26.5; §8.2.3 `port-node` | +| `parameter-symbols` | `showParameterSymbols` | A case's `actor`, `subject` and `stakeholder` parameters and an action's parameter pins as squares on the node's boundary. Without them a connection that ended at a parameter ends at the node that declares it: one line per connection, one node per actor. A tree lists a case's parameters as members and a calc's, action's or constraint's as pins of their owner, all of which stay. | §7.26.5; §8.2.3 `distinguished-parameter`, `distinguished-parameter-link` | +| `port-symbols` | `showPortSymbols` | A part's ports, and a constraint's parameters, as pins on its boundary. Without them a connection that ended at a port ends at the part, a binding that ended at a parameter at the constraint; a port drawn as a nested member of a tree is model structure and stays, as are a tree's parameter pins. Default. | §7.26.5; §8.2.3 `port-node` | | `compartments` | `showCompartments` | The lists of nested features inside a node. Default. | §7.26.5; §8.2.3 `compartment` | | `edge-labels` | `showEdgeLabels` | The names of connections, flows, bindings and allocations along their lines. Without them no label box is left. A sequence diagram's message lines keep their labels: the label is the message. | §8.2.3 `edge-label` | | `keywords` | `showKeywords` | The `«keyword»` over each name (`«part def»`, `«part»`, `«enum def»`, …), as the notation's examples, the pilot and SysON draw every node. Default. | §8.2.3 `keyword-list` | diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 1485cc00a2..83e01e033e 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -859,6 +859,13 @@ pins in the edge's label (`n0 -- n1 : bread = b`) and the pins in a `' not repre draws the pins an edge ends at as nodes nested in their owner, and names the rest in a `# not represented:` notice. +A tree rendering draws the directed parameters of a calculation, action or constraint definition or +usage — `in`, `out`, `inout` and `return` — as parameter pins of their owner's node rather than as +nested members, a `return` declaring no name as the `result` output: `text` lists them under the +node direction first (`in x : Integer` with `typings` shown), `dot` and `d2` draw them as pins, and +`mermaid` and `plantuml` list them in a `parameters` compartment of the node's label. A usage typed +by a definition shows each parameter once, the definition's as inherited (`^y`). + Every `subgraph` of a Mermaid flowchart opens on a `direction` statement restating the flowchart's, because Mermaid lays out a subgraph that states none without regard to the flowchart's; a tree draws containment as edges, not subgraphs, so it carries none. diff --git a/internal/ir/view/d2.go b/internal/ir/view/d2.go index c6cf8cc9e8..97ce3d7b53 100644 --- a/internal/ir/view/d2.go +++ b/internal/ir/view/d2.go @@ -354,10 +354,10 @@ func (w *d2Writer) writeNode(node *Node, scope, indent string) { } // pins are the ports drawn inside a node: an interconnection's under the -// ports display, an action's the ones an edge ends at. +// ports display, a tree's parameter pins, an action's the ones an edge ends at. func (w *d2Writer) pins(node *Node) []Port { switch { - case w.ports.interconnection: + case w.ports.interconnection, w.ports.tree: return w.ports.of(node) case w.ports.action: var pins []Port @@ -373,9 +373,12 @@ func (w *d2Writer) pins(node *Node) []Port { // pinLabel is a pin's label: on an interconnection the port's stereotype over // `name : Type` under the full display and the name alone under the minimal, -// on an action the pin's name. +// on a tree the parameter's direction, name and type, on an action the pin's name. func (w *d2Writer) pinLabel(port Port) string { - if !w.ports.interconnection { + switch { + case w.ports.tree: + return parameterPinLine(port) + case !w.ports.interconnection: return port.displayName() } label := w.ports.pinLabel(port) diff --git a/internal/ir/view/dot_test.go b/internal/ir/view/dot_test.go index 8788fcdf45..4ae6bfe3bb 100644 --- a/internal/ir/view/dot_test.go +++ b/internal/ir/view/dot_test.go @@ -56,6 +56,8 @@ func TestGoldenDOT(t *testing.T) { {"glyphs-definition", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::definitionGlyphs", KindDefinition, false}, {"glyphs-package", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::packageGlyphs", KindPackage, false}, {"glyphs-tree", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::treeGlyphs", KindTree, false}, + {"calc-parameters", "calc-parameters.sysml", "ParameterCalcViews::calculations", KindTree, false}, + {"calc-parameter-definitions", "calc-parameters.sysml", "ParameterCalcViews::definitions", KindTree, false}, {"glyphs-interconnection", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::lineGlyphs", KindInterconnection, false}, {"glyphs-action", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::fillGlyphs", KindAction, false}, {"glyphs-mixed", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::mixedGlyphs", KindMixed, false}, diff --git a/internal/ir/view/geometry.go b/internal/ir/view/geometry.go index 96a7536c5d..53372fb99e 100644 --- a/internal/ir/view/geometry.go +++ b/internal/ir/view/geometry.go @@ -254,13 +254,20 @@ func (r *Renderer) memberDress(view, owner *symbols.Symbol, name string, node *N node.Style = &Style{Fill: s.Fill, Line: s.Line, Text: s.Text, Font: s.Font, FontSize: s.FontSize, Bold: s.Bold, Italic: s.Italic} } } + r.memberNotesOf(view, owner, name, node.ID, out) + return node +} + +// memberNotesOf adds to out the Notes about the member owner has under name in +// view, anchored to anchor: those naming it as owner's, and the declaring +// owner's — not one naming it as another inheriting owner's. +func (r *Renderer) memberNotesOf(view, owner *symbols.Symbol, name, anchor string, out *Rendering) { for _, site := range r.model.MemberNotesOf(view, owner, name) { r.noteLayoutProblems(site, owner, out) if n := site.Note; n != nil { - out.Notes = append(out.Notes, Note{Text: n.Text, Anchor: node.ID, X: n.X, Y: n.Y, Width: n.Width, Height: n.Height, HasSize: n.HasSize, Origin: site.Origin()}) + out.Notes = append(out.Notes, Note{Text: n.Text, Anchor: anchor, X: n.X, Y: n.Y, Width: n.Width, Height: n.Height, HasSize: n.HasSize, Origin: site.Origin()}) } } - return node } // routeOf is the waypoints the edge declared as elem follows in view, nil when diff --git a/internal/ir/view/label.go b/internal/ir/view/label.go index 89313f5bc3..10cf6dc129 100644 --- a/internal/ir/view/label.go +++ b/internal/ir/view/label.go @@ -17,6 +17,9 @@ type labeller struct { simple map[*Node]string // a node's distinguishing suffix, when names are drawn simple skin dotSkin // the DOT skin labels are composed and measured for shown Shown // the optional notation elements the labels carry + // pinLines lists a node's parameter pins as label lines, for a form that + // draws no pin on a tree node. + pinLines bool } // labelsOf finds the namespace the named roots share — the longest run of @@ -349,6 +352,7 @@ func (l labeller) lines(node *Node) []string { } lines = append(lines, l.headLines(node)...) lines = append(lines, l.details(node)...) + lines = append(lines, l.parameterLines(node)...) return append(lines, l.compartmentLines(node)...) } @@ -361,9 +365,24 @@ func (l labeller) titleLines(node *Node) []string { } lines = append(lines, head...) lines = append(lines, l.details(node)...) + lines = append(lines, l.parameterLines(node)...) return append(lines, l.compartmentLines(node)...) } +// parameterLines are a node's parameter pins as the notation's `parameters` +// compartment, `in x : Integer` a line under the heading, where pinLines asks +// for them; nothing for a node with no pin. +func (l labeller) parameterLines(node *Node) []string { + if !l.pinLines || len(node.Ports) == 0 { + return nil + } + lines := []string{"parameters"} + for _, port := range node.Ports { + lines = append(lines, parameterPinLine(port)) + } + return lines +} + // cameoStateDetails splits a state's detail into Cameo's compartment lines, one // per behaviour, and drops the `initial` marker the initial dot already draws. // The detail is split as notation, so a name quoting a comma stays one; the diff --git a/internal/ir/view/mermaid.go b/internal/ir/view/mermaid.go index 799a180a0c..d8f6016113 100644 --- a/internal/ir/view/mermaid.go +++ b/internal/ir/view/mermaid.go @@ -46,6 +46,7 @@ func (r *Rendering) MermaidWith(options Options) string { direction := options.Direction var b strings.Builder labels := labelsOf(r.Roots, false, nil, r.Shown) + labels.pinLines = r.Kind == KindTree labels.skin = skinOf(options.Style) ports := r.portView(options.Ports) r.writeMermaidFrontmatter(&b, labels, options, ports) @@ -1288,6 +1289,7 @@ func mermaidLabel(node *Node, labels labeller, lines []string, title bool) strin markdown = append(markdown, "**"+line+"**") } markdown = append(markdown, labels.details(node)...) + markdown = append(markdown, labels.parameterLines(node)...) markdown = append(markdown, labels.compartmentLines(node)...) return `"` + "`" + strings.Join(markdown, "\n") + "`" + `"` } diff --git a/internal/ir/view/narrow.go b/internal/ir/view/narrow.go index 476d7436c5..442f9a9b5d 100644 --- a/internal/ir/view/narrow.go +++ b/internal/ir/view/narrow.go @@ -125,7 +125,8 @@ func (n *narrowing) narrow(node *Node) { for _, port := range node.Ports { pinShown := n.shown.Has(ElementPortSymbols) if port.Parameter { - pinShown = n.shown.Has(ElementParameterSymbols) + // A tree's parameter pins are model structure, listed as its members are. + pinShown = n.shown.Has(ElementParameterSymbols) || n.kind == KindTree } if !pinShown { n.redirect[port.ID] = node.ID diff --git a/internal/ir/view/plantuml.go b/internal/ir/view/plantuml.go index 6514f466f7..7c1d5f0f33 100644 --- a/internal/ir/view/plantuml.go +++ b/internal/ir/view/plantuml.go @@ -51,6 +51,7 @@ func (r *Rendering) PlantUMLWith(options Options) (string, error) { w := &plantumlWriter{kind: r.Kind, borders: r.Kind.paletteBorders(), fills: familyFills{palette: options.Palette, tree: r.Kind == KindTree}, labels: labelsOf(r.Roots, false, nil, r.Shown), ports: r.portView(options.Ports), links: options.Links, writtenPorts: make(map[string]struct{}), nesting: r.nestingEdges()} + w.labels.pinLines = r.Kind == KindTree for _, root := range r.Roots { w.fills.collect(root) } @@ -707,6 +708,9 @@ func (w *plantumlWriter) plantumlLabel(node *Node) string { for _, line := range w.labels.details(node) { parts = append(parts, plantumlText(line)) } + for _, line := range w.labels.parameterLines(node) { + parts = append(parts, plantumlText(line)) + } for _, line := range w.labels.compartmentLines(node) { parts = append(parts, plantumlText(line)) } diff --git a/internal/ir/view/plantuml_test.go b/internal/ir/view/plantuml_test.go index ad4fc22260..3e3204746e 100644 --- a/internal/ir/view/plantuml_test.go +++ b/internal/ir/view/plantuml_test.go @@ -55,6 +55,8 @@ var plantumlGoldenCases = []struct { {"glyphs-definition", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::definitionGlyphs", KindDefinition}, {"glyphs-package", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::packageGlyphs", KindPackage}, {"glyphs-tree", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::treeGlyphs", KindTree}, + {"calc-parameters", "calc-parameters.sysml", "ParameterCalcViews::calculations", KindTree}, + {"calc-parameter-definitions", "calc-parameters.sysml", "ParameterCalcViews::definitions", KindTree}, {"glyphs-interconnection", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::lineGlyphs", KindInterconnection}, {"glyphs-action", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::fillGlyphs", KindAction}, {"glyphs-mixed", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::mixedGlyphs", KindMixed}, diff --git a/internal/ir/view/ports.go b/internal/ir/view/ports.go index 444b9880ab..b3369de15c 100644 --- a/internal/ir/view/ports.go +++ b/internal/ir/view/ports.go @@ -85,9 +85,12 @@ type portView struct { // whose pins text writes under their nodes. interconnection bool action bool - mixed bool - minimal bool - connected map[string]bool + // tree reports a tree rendering, whose pins are the parameters of its + // calculations, actions and constraints, written direction first. + tree bool + mixed bool + minimal bool + connected map[string]bool } // portView is the ports the rendering draws under display, the minimal @@ -99,7 +102,7 @@ func (r *Rendering) portView(display Ports) portView { return r.portViewOver(dis // no pin stands for a connector the form leaves out. func (r *Rendering) portViewOver(display Ports, edges []Edge) portView { v := portView{interconnection: r.Kind == KindInterconnection || r.Kind == KindMixed, action: r.Kind == KindAction, - mixed: r.Kind == KindMixed, minimal: display != PortsFull && r.Kind.SupportsPorts()} + tree: r.Kind == KindTree, mixed: r.Kind == KindMixed, minimal: display != PortsFull && r.Kind.SupportsPorts()} if !v.minimal { return v } diff --git a/internal/ir/view/render_test.go b/internal/ir/view/render_test.go index 05c7edc1f9..c3c9e4eda0 100644 --- a/internal/ir/view/render_test.go +++ b/internal/ir/view/render_test.go @@ -172,6 +172,8 @@ func TestGoldenRenderings(t *testing.T) { {"glyphs-definition", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::definitionGlyphs", KindDefinition}, {"glyphs-package", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::packageGlyphs", KindPackage}, {"glyphs-tree", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::treeGlyphs", KindTree}, + {"calc-parameters", "calc-parameters.sysml", "ParameterCalcViews::calculations", KindTree}, + {"calc-parameter-definitions", "calc-parameters.sysml", "ParameterCalcViews::definitions", KindTree}, {"glyphs-interconnection", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::lineGlyphs", KindInterconnection}, {"glyphs-action", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::fillGlyphs", KindAction}, {"glyphs-mixed", "notation-glyphs.sysml", "NotationGlyphs::Diagrams::mixedGlyphs", KindMixed}, diff --git a/internal/ir/view/testdata/calc-parameter-definitions.d2.golden b/internal/ir/view/testdata/calc-parameter-definitions.d2.golden new file mode 100644 index 0000000000..4aade7cee2 --- /dev/null +++ b/internal/ir/view/testdata/calc-parameter-definitions.d2.golden @@ -0,0 +1,29 @@ +# ParameterCalcViews::definitions — tree rendering (render asTreeDiagram) +classes: { + definition: { style: { fill: white; stroke: "#181818"; stroke-width: 1; font-color: black; font-size: 14 } } + pin: { style: { fill: white; stroke: "#181818"; stroke-width: 1; font-color: black; font-size: 10 } } +} +n0: "«calc def»\nAdd" { + class: definition + "n0.0": "in x" { class: pin } + "n0.1": "in y" { class: pin } + "n0.2": "out result" { class: pin } +} +n1: "«calc def»\nScale" { + class: definition + "n1.0": "in x" { class: pin } + "n1.1": "in factor" { class: pin } + "n1.2": "inout count" { class: pin } + "n1.3": "out doubled" { class: pin } + "n1.4": "out r" { class: pin } +} +n2: "«action def»\nHeat" { + class: definition + "n2.0": "in bread" { class: pin } + "n2.1": "out toast" { class: pin } +} +n3: "«constraint def»\nBetween" { + class: definition + "n3.0": "in low" { class: pin } + "n3.1": "in value" { class: pin } +} diff --git a/internal/ir/view/testdata/calc-parameter-definitions.dot.golden b/internal/ir/view/testdata/calc-parameter-definitions.dot.golden new file mode 100644 index 0000000000..0f8f933987 --- /dev/null +++ b/internal/ir/view/testdata/calc-parameter-definitions.dot.golden @@ -0,0 +1,13 @@ +// view: ParameterCalcViews::definitions +// kind: tree +// stated: render asTreeDiagram +// layout: dot +digraph "ParameterCalcViews::definitions" { + graph [fontname="Helvetica"]; + node [shape=box, style=filled, fillcolor=white, color="#181818", fontname="Helvetica", fontsize=14, penwidth=0.5]; + edge [color="#181818", fontname="Helvetica", fontsize=13, penwidth=1]; + "n0" [label=<
xy
«calc def»
Add
result
>]; + "n1" [label=<
xfactorcount
«calc def»
Scale
doubledr
>]; + "n2" [label=<
bread
«action def»
Heat
toast
>]; + "n3" [label=<
lowvalue
«constraint def»
Between
>]; +} diff --git a/internal/ir/view/testdata/calc-parameter-definitions.mermaid.golden b/internal/ir/view/testdata/calc-parameter-definitions.mermaid.golden new file mode 100644 index 0000000000..83d2437e42 --- /dev/null +++ b/internal/ir/view/testdata/calc-parameter-definitions.mermaid.golden @@ -0,0 +1,49 @@ +--- +config: + fontFamily: "Helvetica, Arial, sans-serif" + theme: base + themeCSS: ".edgeLabel rect { opacity: 1 !important; } .cluster-label .nodeLabel { text-align: center; }" + themeVariables: + fontFamily: "Helvetica, Arial, sans-serif" + fontSize: "14px" + primaryColor: "#FFFFFF" + secondaryColor: "#FFFFFF" + tertiaryColor: "#FFFFFF" + background: "#FFFFFF" + primaryBorderColor: "#181818" + primaryTextColor: "#000000" + lineColor: "#181818" + textColor: "#000000" + noteBkgColor: "#FEFFDD" + noteBorderColor: "#181818" + noteTextColor: "#000000" + clusterBkg: "#FFFFFF" + clusterBorder: "#181818" + edgeLabelBackground: "#FFFFFF" +--- +%% ParameterCalcViews::definitions — tree rendering (render asTreeDiagram) +flowchart TD + n0["`*«calc def»* +**Add** +parameters +in x +in y +out result`"] + n1["`*«calc def»* +**Scale** +parameters +in x +in factor +inout count +out doubled +out r`"] + n2["`*«action def»* +**Heat** +parameters +in bread +out toast`"] + n3["`*«constraint def»* +**Between** +parameters +in low +in value`"] diff --git a/internal/ir/view/testdata/calc-parameter-definitions.plantuml.golden b/internal/ir/view/testdata/calc-parameter-definitions.plantuml.golden new file mode 100644 index 0000000000..2a1a68467b --- /dev/null +++ b/internal/ir/view/testdata/calc-parameter-definitions.plantuml.golden @@ -0,0 +1,49 @@ +@startuml +' ParameterCalcViews::definitions — tree rendering (render asTreeDiagram) + +skinparam wrapWidth 300 +hide stereotype +hide circle +hide empty members +class "//«calc def»//\n**Add**\nparameters\nin x\nin y\nout result" as n0 <> +class "//«calc def»//\n**Scale**\nparameters\nin x\nin factor\ninout count\nout doubled\nout r" as n1 <> +class "//«action def»//\n**Heat**\nparameters\nin bread\nout toast" as n2 <> +class "//«constraint def»//\n**Between**\nparameters\nin low\nin value" as n3 <> +@enduml diff --git a/internal/ir/view/testdata/calc-parameter-definitions.text.golden b/internal/ir/view/testdata/calc-parameter-definitions.text.golden new file mode 100644 index 0000000000..582be1b594 --- /dev/null +++ b/internal/ir/view/testdata/calc-parameter-definitions.text.golden @@ -0,0 +1,18 @@ +ParameterCalcViews::definitions - tree rendering (render asTreeDiagram) + +calc def Add + in x + in y + out result +calc def Scale + in x + in factor + inout count + out doubled + out r +action def Heat + in bread + out toast +constraint def Between + in low + in value diff --git a/internal/ir/view/testdata/calc-parameters.d2.golden b/internal/ir/view/testdata/calc-parameters.d2.golden new file mode 100644 index 0000000000..b0c0beb94c --- /dev/null +++ b/internal/ir/view/testdata/calc-parameters.d2.golden @@ -0,0 +1,37 @@ +# ParameterCalcViews::calculations — tree rendering (render asTreeDiagram) +classes: { + definition: { style: { fill: white; stroke: "#181818"; stroke-width: 1; font-color: black; font-size: 14 } } + usage: { style: { fill: white; stroke: "#181818"; stroke-width: 1; font-color: black; font-size: 14; border-radius: 8 } } + pin: { style: { fill: white; stroke: "#181818"; stroke-width: 1; font-color: black; font-size: 10 } } + edge: { style: { stroke: "#181818"; font-size: 13; font-color: black; stroke-width: 1 } } + reference: { source-arrowhead: { shape: diamond; style: { filled: false } }; style: { stroke: "#181818"; font-size: 13; font-color: black; stroke-width: 1 } } +} +n0: "«calc»\nadd" { + class: usage + "n0.0": "in x" { class: pin } + "n0.1": "in y" { class: pin } + "n0.2": "out result" { class: pin } +} +n1: "«part def»\nSensor" { class: definition } +n2: "«attribute»\na" { class: usage } +n3: "«attribute»\nb" { class: usage } +n4: "«attribute»\ngain" { class: usage } +n5: "«calc»\nsum" { + class: usage + "n5.0": "in x" { class: pin } + "n5.1": "in ^y" { class: pin } + "n5.2": "out ^result" { class: pin } +} +n6: "«calc»\nscaled" { + class: usage + "n6.0": "in factor" { class: pin } + "n6.1": "in ^x" { class: pin } + "n6.2": "inout ^count" { class: pin } + "n6.3": "out ^doubled" { class: pin } + "n6.4": "out ^r" { class: pin } +} +n1 -- n5: { class: edge } +n1 -- n6: { class: edge } +n1 <- n2: { class: reference } +n1 <- n3: { class: reference } +n1 <- n4: { class: reference } diff --git a/internal/ir/view/testdata/calc-parameters.dot.golden b/internal/ir/view/testdata/calc-parameters.dot.golden new file mode 100644 index 0000000000..8d9e7d4217 --- /dev/null +++ b/internal/ir/view/testdata/calc-parameters.dot.golden @@ -0,0 +1,21 @@ +// view: ParameterCalcViews::calculations +// kind: tree +// stated: render asTreeDiagram +// layout: dot +digraph "ParameterCalcViews::calculations" { + graph [fontname="Helvetica"]; + node [shape=box, style=filled, fillcolor=white, color="#181818", fontname="Helvetica", fontsize=14, penwidth=0.5]; + edge [color="#181818", fontname="Helvetica", fontsize=13, penwidth=1]; + "n0" [style="rounded,filled", label=<
xy
«calc»
add
result
>]; + "n1" [label=<«part def»
Sensor>]; + "n2" [style="rounded,filled", label=<«attribute»
a>]; + "n3" [style="rounded,filled", label=<«attribute»
b>]; + "n4" [style="rounded,filled", label=<«attribute»
gain>]; + "n5" [style="rounded,filled", label=<
x^y
«calc»
sum
^result
>]; + "n1" -> "n5" [arrowhead=none]; + "n6" [style="rounded,filled", label=<
factor^x^count
«calc»
scaled
^doubled^r
>]; + "n1" -> "n6" [arrowhead=none]; + "n1" -> "n2" [dir=back, arrowtail=odiamond]; + "n1" -> "n3" [dir=back, arrowtail=odiamond]; + "n1" -> "n4" [dir=back, arrowtail=odiamond]; +} diff --git a/internal/ir/view/testdata/calc-parameters.mermaid.golden b/internal/ir/view/testdata/calc-parameters.mermaid.golden new file mode 100644 index 0000000000..8899d01595 --- /dev/null +++ b/internal/ir/view/testdata/calc-parameters.mermaid.golden @@ -0,0 +1,58 @@ +--- +config: + fontFamily: "Helvetica, Arial, sans-serif" + theme: base + themeCSS: ".edgeLabel rect { opacity: 1 !important; } .cluster-label .nodeLabel { text-align: center; }" + themeVariables: + fontFamily: "Helvetica, Arial, sans-serif" + fontSize: "14px" + primaryColor: "#FFFFFF" + secondaryColor: "#FFFFFF" + tertiaryColor: "#FFFFFF" + background: "#FFFFFF" + primaryBorderColor: "#181818" + primaryTextColor: "#000000" + lineColor: "#181818" + textColor: "#000000" + noteBkgColor: "#FEFFDD" + noteBorderColor: "#181818" + noteTextColor: "#000000" + clusterBkg: "#FFFFFF" + clusterBorder: "#181818" + edgeLabelBackground: "#FFFFFF" +--- +%% ParameterCalcViews::calculations — tree rendering (render asTreeDiagram) +flowchart TD + n0("`*«calc»* +**add** +parameters +in x +in y +out result`") + n1["`*«part def»* +**Sensor**`"] + n2("`*«attribute»* +**a**`") + n3("`*«attribute»* +**b**`") + n4("`*«attribute»* +**gain**`") + n5("`*«calc»* +**sum** +parameters +in x +in ^y +out ^result`") + n1 --- n5 + n6("`*«calc»* +**scaled** +parameters +in factor +in ^x +inout ^count +out ^doubled +out ^r`") + n1 --- n6 + n1 ---|"◇"| n2 + n1 ---|"◇"| n3 + n1 ---|"◇"| n4 diff --git a/internal/ir/view/testdata/calc-parameters.plantuml.golden b/internal/ir/view/testdata/calc-parameters.plantuml.golden new file mode 100644 index 0000000000..272057e020 --- /dev/null +++ b/internal/ir/view/testdata/calc-parameters.plantuml.golden @@ -0,0 +1,57 @@ +@startuml +' ParameterCalcViews::calculations — tree rendering (render asTreeDiagram) + +skinparam wrapWidth 300 +hide stereotype +hide circle +hide empty members +class "//«calc»//\n**add**\nparameters\nin x\nin y\nout result" as n0 <> <> +class "//«part def»//\n**Sensor**" as n1 <> +class "//«attribute»//\n**a**" as n2 <> <> +class "//«attribute»//\n**b**" as n3 <> <> +class "//«attribute»//\n**gain**" as n4 <> <> +class "//«calc»//\n**sum**\nparameters\nin x\nin ^y\nout ^result" as n5 <> <> +n1 -- n5 +class "//«calc»//\n**scaled**\nparameters\nin factor\nin ^x\ninout ^count\nout ^doubled\nout ^r" as n6 <> <> +n1 -- n6 +n1 o-- n2 +n1 o-- n3 +n1 o-- n4 +@enduml diff --git a/internal/ir/view/testdata/calc-parameters.sysml b/internal/ir/view/testdata/calc-parameters.sysml new file mode 100644 index 0000000000..35558f8f0b --- /dev/null +++ b/internal/ir/view/testdata/calc-parameters.sysml @@ -0,0 +1,67 @@ +// The directed parameters of calculations, actions and constraints: a tree draws them as +// parameter pins of their owner rather than as attribute members, and names the anonymous +// result parameter `result` as the standard names it. A calc usage typed by a calc def shows +// each parameter once: its own redefining one and the def's others as inherited. +package ParameterCalcs { + private import ScalarValues::*; + + // The reporter's shape: an anonymous result declared by `return`. + calc add { + in x : Integer; + in y : Integer; + return : Integer; + x + y + } + + calc def Add { + in x : Integer; + in y : Integer; + return : Integer = x + y; + } + + // Every direction, and a named result. + calc def Scale { + in x : Real; + in factor : Real; + inout count : Integer; + out doubled : Real = x + x; + return r : Real = x * factor; + } + + action def Heat { + in bread : Integer; + out toast : Integer; + } + + constraint def Between { + in attribute low : Real; + in attribute value : Real; + value >= low + } + + part def Sensor { + attribute a : Integer; + attribute b : Integer; + attribute gain : Real; + calc sum : Add { in x = a; } + calc scaled : Scale { in factor = gain; } + } +} + +package ParameterCalcViews { + private import Views::*; + + view calculations { + expose ParameterCalcs::add; + expose ParameterCalcs::Sensor; + render asTreeDiagram; + } + + view definitions { + expose ParameterCalcs::Add; + expose ParameterCalcs::Scale; + expose ParameterCalcs::Heat; + expose ParameterCalcs::Between; + render asTreeDiagram; + } +} diff --git a/internal/ir/view/testdata/calc-parameters.text.golden b/internal/ir/view/testdata/calc-parameters.text.golden new file mode 100644 index 0000000000..1b3639696f --- /dev/null +++ b/internal/ir/view/testdata/calc-parameters.text.golden @@ -0,0 +1,25 @@ +ParameterCalcViews::calculations - tree rendering (render asTreeDiagram) + +calc add + in x + in y + out result +part def Sensor + attribute a + attribute b + attribute gain + calc sum + in x + in ^y + out ^result + calc scaled + in factor + in ^x + inout ^count + out ^doubled + out ^r + +relationships: + Sensor o-- a + Sensor o-- b + Sensor o-- gain diff --git a/internal/ir/view/text.go b/internal/ir/view/text.go index fb56305045..6ab87d6f5c 100644 --- a/internal/ir/view/text.go +++ b/internal/ir/view/text.go @@ -245,7 +245,7 @@ func writeNodeText(b *strings.Builder, node *Node, depth int, labels map[string] b.WriteString(strings.Repeat(" ", depth+2) + rowLine(row) + "\n") } } - if ports.interconnection || ports.action { + if ports.interconnection || ports.action || ports.tree { for _, port := range ports.of(node) { labels[port.ID] = labels[node.ID] + "." + port.Name b.WriteString(strings.Repeat(" ", depth+1) + pinLine(port, ports) + "\n") @@ -289,10 +289,14 @@ func hiddenEdges(r *Rendering) []Edge { return edges } -// pinLine is a part port's `port