Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .reviewmark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,20 @@ reviews:
- "src/DemaConsulting.SysML2Tools.Core/Layout/Internal/LayoutWarnings.cs"
- "test/DemaConsulting.SysML2Tools.Tests/Layout/LayoutWarningsTests.cs"

- id: SysML2Tools-Core-Layout-Internal-ExposeScopeResolver
title: Review that DemaConsulting.SysML2Tools Layout Internal ExposeScopeResolver Implementation is Correct
context:
- docs/design/sysml2-tools-core.md
- docs/reqstream/sysml2-tools-core.yaml
- docs/design/sysml2-tools-core/layout.md
- docs/design/sysml2-tools-core/layout/internal.md
paths:
- "docs/reqstream/sysml2-tools-core/layout/internal/expose-scope-resolver.yaml"
- "docs/design/sysml2-tools-core/layout/internal/expose-scope-resolver.md"
- "docs/verification/sysml2-tools-core/layout/internal/expose-scope-resolver.md"
- "src/DemaConsulting.SysML2Tools.Core/Layout/Internal/ExposeScopeResolver.cs"
- "test/DemaConsulting.SysML2Tools.Tests/Layout/ExposeScopeResolverTests.cs"

- id: SysML2Tools-Core-Rendering-DiagramRenderer
title: Review that DemaConsulting.SysML2Tools Rendering DiagramRenderer Implementation is Correct
context:
Expand Down
22 changes: 0 additions & 22 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,28 +95,6 @@ new expression-evaluation component; `GeneralViewLayoutStrategy` filter applicat
**Visual gate:** a view with a `filter [<predicate>];` statement renders only the elements
satisfying the predicate, with no "not yet evaluated" warning.

### Expose-based scoping for the remaining layout strategies

`GeneralViewLayoutStrategy` implements `expose`-based subject-scoping (containment-subtree
filtering driven by a view's `expose` body statements — the only content-scoping mechanism a
view has), but `InterconnectionView`, `StateTransitionView`, `ActionFlowView`, `SequenceView`,
`GridView`, and `BrowserView` layout strategies do not yet honor `ViewContext.ViewNode`'s
`Expose` edges and continue to render their full applicable scope regardless of a view's
declared `expose` statements.

- Extend the same `ResolveExposedScope`/`IsInSubjectScope` containment-subtree idiom (or a
shared helper extracted from `GeneralViewLayoutStrategy`) to each of the six remaining layout
strategies, respecting the "no `Expose` edge → render everything unchanged" fallback used by
`GeneralViewLayoutStrategy`.
- Add regression tests per strategy mirroring `GeneralViewLayoutStrategyTests`'s expose-scoping,
expose-union, and no-expose-statement-regression scenarios.

**Scope:** `InterconnectionViewLayoutStrategy`, `StateTransitionViewLayoutStrategy`,
`ActionFlowViewLayoutStrategy`, `SequenceViewLayoutStrategy`, `GridViewLayoutStrategy`,
`BrowserViewLayoutStrategy`; corresponding test files.
**Visual gate:** each of the six views renders a scoped diagram when its view declares an
`expose <...>;` statement naming a resolvable target, unchanged when it does not.

### Support selecting rendering style via `render <renderingKind>;`

A view's `render <target>;` member names a rendering style/format usage per the SysML v2
Expand Down
2 changes: 2 additions & 0 deletions docs/design/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ system, subsystem, and unit levels:
- **GridViewLayoutStrategy** (Unit) — specialization/relationship matrix
- **BrowserViewLayoutStrategy** (Unit) — indented membership tree
- **LayoutWarnings** (Unit) — builder for layout diagnostic warning messages
- **ExposeScopeResolver** (Unit) — shared helper resolving a view's `expose`-statement
qualified-name containment-subtree scope, used by every strategy above
- **LayeredPlacement** (Unit) — thin helper that adapts the off-the-shelf
`DemaConsulting.Rendering.Layout` layered algorithm, returning placed rectangles and routed
polylines to the strategies
Expand Down
14 changes: 9 additions & 5 deletions docs/design/sysml2-tools-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,15 @@ N/A — not a safety-classified software item.
`RenderOptions`. For each view declared in the workspace it constructs a `ViewContext`
containing the view name, workspace reference, and (when available) the view's resolved
AST node.
2. `ILayoutStrategy.BuildLayout` is called with the `ViewContext` and `RenderOptions`. The
Layout subsystem produces a fully resolved `LayoutTree` by delegating geometric placement and
routing to the off-the-shelf `DemaConsulting.Rendering.Layout` layered algorithm (through the
`LayeredPlacement` helper), placing every node at absolute coordinates and routing every
connector as an orthogonal polyline. The Rendering subsystem then renders that tree.
2. `ILayoutStrategy.BuildLayout` is called with the `ViewContext` and `RenderOptions`. Before
building its placement input, every strategy resolves the view's `expose` scope via the shared
`ExposeScopeResolver` helper (`ResolveExposedScope`/`IsInSubjectScope`/`IsRootRelevantToScope`/
`IsMoreSpecificCandidate`), which scopes the returned `LayoutTree`'s content and, for the
single-root strategies, restricts root selection as well. The Layout subsystem then produces a
fully resolved `LayoutTree` by delegating geometric placement and routing to the off-the-shelf
`DemaConsulting.Rendering.Layout` layered algorithm (through the `LayeredPlacement` helper),
placing every node at absolute coordinates and routing every connector as an orthogonal
polyline. The Rendering subsystem then renders that tree.
3. `IRenderer.Render` is called with the `LayoutTree`, `RenderOptions`, and a fresh output
`Stream`. The renderer reads each `LayoutNode` in the tree, translates it to output-format
primitives, and writes bytes to the stream.
Expand Down
3 changes: 2 additions & 1 deletion docs/design/sysml2-tools-core/layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ chapter:

- **Internal** — the per-view layout strategies that map the semantic model to a
`LayoutTree` (general, interconnection, state transition, action flow, sequence, grid, and
browser views), plus `LayoutWarnings`. See the *Layout Internal Subsystem* chapter.
browser views), plus `LayoutWarnings`, plus the shared `ExposeScopeResolver` helper. See the
*Layout Internal Subsystem* chapter.
- **LayeredPlacement** — a thin helper that adapts the off-the-shelf
`DemaConsulting.Rendering.Layout` layered algorithm, returning placed rectangles and routed
polylines to the strategies. See its own unit chapter.
Expand Down
17 changes: 9 additions & 8 deletions docs/design/sysml2-tools-core/layout/internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ The subsystem contains one strategy per supported view type:
| `ActionFlowViewLayoutStrategy` | Lays out actions top-to-bottom with start/done markers and successions |

The subsystem also contains the `BrowserViewLayoutStrategy`, `GridViewLayoutStrategy`, and
`SequenceViewLayoutStrategy` strategies and the `LayoutWarnings` helper, each documented in its
own chapter.
`SequenceViewLayoutStrategy` strategies, the shared `ExposeScopeResolver` helper, and the
`LayoutWarnings` helper, each documented in its own chapter.

#### Interfaces

Expand All @@ -31,9 +31,10 @@ The renderers see only the returned tree.
#### Design

Each strategy follows the same shape: collect the relevant model elements (excluding
standard-library declarations), compute an intrinsic size for each box, use arithmetic placement
or delegate geometry through `LayeredPlacement`, and build the `LayoutNode` tree. When a
connector cannot be routed without crossing a box, the strategy records a layout warning through
`LayoutWarnings` rather than silently producing a misleading diagram. A view with no relevant
elements returns a minimal empty canvas. The detailed mapping and heuristics of each strategy are
described in its own unit chapter.
standard-library declarations), restrict that collection to the view's resolved `expose` scope
(via the shared `ExposeScopeResolver`) when one applies, compute an intrinsic size for each box,
use arithmetic placement or delegate geometry through `LayeredPlacement`, and build the
`LayoutNode` tree. When a connector cannot be routed without crossing a box, the strategy records
a layout warning through `LayoutWarnings` rather than silently producing a misleading diagram. A
view with no relevant elements returns a minimal empty canvas. The detailed mapping and
heuristics of each strategy are described in its own unit chapter.
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,28 @@ its computed box size; successions are carried as `(int From, int To)` index pai

###### `BuildLayout(ViewContext context, RenderOptions options)`

Entry point. Selects the root definition via `FindRoot`, collects its actions, resolves its
successions, lays the actions out in layers, adds the succession edges and the start/done markers,
and assembles the tree. Returns a minimal 200×100 empty `LayoutTree` when no root or no actions
are found.
Entry point. Resolves the view's `expose` scope via `ExposeScopeResolver.ResolveExposedScope`,
selects the root definition via `FindRoot(workspace, scope)`, collects its actions via
`CollectActions(root, theme, scope)`, resolves its successions, lays the actions out in layers,
adds the succession edges and the start/done markers, and assembles the tree. Returns a minimal
200×100 empty `LayoutTree` when no root or no actions are found.

###### `FindRoot(workspace)` and `CollectActions(root, theme)`
###### `FindRoot(workspace, scope)` and `CollectActions(root, theme, scope)`

`FindRoot` chooses the non-standard-library definition that scores highest on successions (then
actions). `CollectActions` gathers the declared `action` usages and any action named only by a
succession endpoint, building a name → index lookup.
actions), restricted — when a scope is resolved — to candidates for which
`ExposeScopeResolver.IsRootRelevantToScope` returns `true`. When multiple candidates are relevant
to a non-null scope (possible because a nested definition and its ancestor can both be relevant),
the most specific (deepest/longest qualified name) relevant candidate is preferred via
`ExposeScopeResolver.IsMoreSpecificCandidate`, with the succession/action score used only to break
ties among equally specific candidates; this ordering does not apply when `scope` is `null`. The
zero-successions-and-zero-actions exclusion guard (`successions > 0 || actions > 0`) is unaffected
by this change — it is applied regardless of specificity. `CollectActions` gathers the declared
`action` usages, excluding — when a scope is resolved — any declared action feature whose
qualified name fails `ExposeScopeResolver.IsInSubjectScope`; it then adds any additional action
named only by a succession endpoint **unconditionally** (this second pass has no independent
qualified name of its own to scope against, since it exists solely because a succession names it),
building a name → index lookup.

###### `ResolveSuccessions(root, index)`

Expand Down Expand Up @@ -58,6 +70,30 @@ and returns the number of successions whose polyline crosses a non-endpoint acti
centred over the actions with no incoming edge and a bullseye done marker centred under the actions
with no outgoing edge, joining each with a solid filled-arrow flow line.

##### Expose Scoping

Because this strategy renders exactly one selected root's actions, scoping restricts **which
root is selected** and then narrows **which of that root's actions are shown**, mirroring
`StateTransitionViewLayoutStrategy`'s approach. `FindRoot` only considers candidates
`ExposeScopeResolver.IsRootRelevantToScope` accepts, so exposing the current heuristic root
itself, an inner action of it, or a definition that itself contains the heuristic default all
correctly select a root, while exposing an unrelated definition yields no root and thus the
minimal empty canvas. When more than one candidate is relevant (a nested definition and an
ancestor definition can both be relevant to the same exposed subject),
`ExposeScopeResolver.IsMoreSpecificCandidate` prefers the most deeply nested candidate, so
exposing an inner action of a nested definition correctly selects that nested definition rather
than its ancestor, even when the ancestor has a higher succession/action score. `CollectActions`
then narrows the selected root's own **declared** action features to those within the resolved
scope; however, any declared-but-excluded action that is still referenced by an in-scope
succession is transparently re-added by the unconditional succession-endpoint pass, since that
pass has no independent qualified name to filter against — so expose-scoping only reliably drops
an action that is genuinely isolated (never referenced by any succession of the selected root).
`ResolveSuccessions`'s existing name-lookup approach naturally omits any succession whose endpoint
action was never added — no new edge-side logic was required. A view with no `expose` statement
(including the synthesized `--auto` view, whose `ViewNode` is `null`) resolves no scope, so
`FindRoot` considers every candidate and `CollectActions` keeps every action, unchanged from the
pre-scoping behavior.

##### Error Handling

Null `context` or `options` arguments throw `ArgumentNullException`. The absence of an eligible
Expand All @@ -73,6 +109,9 @@ surfaced through `LayoutWarnings`.
- `LayeredPlacement` (Layout Internal subsystem) — top-to-bottom placement and orthogonal routing
through `DemaConsulting.Rendering.Layout`.
- `StdlibFilter` (Rendering Internal subsystem) — standard-library exclusion.
- `ExposeScopeResolver` (Layout Internal subsystem) — `ResolveExposedScope`,
`IsRootRelevantToScope`, and `IsInSubjectScope` supply the shared `expose`-scoping used by
`BuildLayout`, `FindRoot`, and `CollectActions`.
- `SysmlWorkspace`, `SysmlDefinitionNode`, `SysmlFeatureNode`, `SysmlTransitionNode` (Semantic subsystem) — model input.
- `LayoutWarnings` (Layout Internal subsystem) — crossing-warning construction.
- The `LayoutTree`, `LayoutBox`, `LayoutBadge`, and `LayoutLine` data types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,39 @@ holding a node's qualified name, display label, optional keyword, and child node

Walks the membership forest and emits rows:

1. **Forest construction.** `BuildForest` takes the non-stdlib declarations in deterministic
(ordinal qualified-name) order so parents precede children, and links each element to the parent
identified by the prefix before its last `::` separator; elements with no known parent become
roots.
2. **Recursive emission.** `EmitNode` lays out each row left-to-right at an X derived from its depth
1. **Scope resolution.** `ExposeScopeResolver.ResolveExposedScope` resolves the view's `expose`
scope once (or `null` when none applies).
2. **Forest construction.** `BuildForest` takes the non-stdlib declarations in deterministic
(ordinal qualified-name) order so parents precede children — additionally excluding, when a
scope was resolved, any element whose qualified name is not within it per
`ExposeScopeResolver.IsInSubjectScope` — and links each remaining element to the parent
identified by the prefix before its last `::` separator; elements with no known parent (whether
because they are genuine workspace roots, or because scoping excluded their would-be parent)
become roots.
3. **Recursive emission.** `EmitNode` lays out each row left-to-right at an X derived from its depth
times a fixed indentation, advancing a shared Y cursor downward. Each row becomes a `LayoutBox`
whose label combines the element keyword and simple name and whose width fits the label.
3. **Connectors.** For every non-root row a `LayoutLine` is emitted from a vertical stem dropped from
4. **Connectors.** For every non-root row a `LayoutLine` is emitted from a vertical stem dropped from
the parent row down to the child's vertical centre and across to the child box, so the connector
never crosses the parent's own box or text.
4. **Canvas sizing.** The overall width follows the right-most box and the height follows the final
5. **Canvas sizing.** The overall width follows the right-most box and the height follows the final
Y cursor.

When there are no user-defined elements, a minimal empty `LayoutTree` with no nodes is returned.
When there are no user-defined elements (either because the workspace is empty or because scoping
excludes every element), a minimal empty `LayoutTree` with no nodes is returned.

##### Expose Scoping

`BuildForest` is the only place scoping applies: it is a direct, workspace-wide filter with no
single-root heuristic to restrict, so a resolved `expose` scope simply narrows the forest to the
elements within the exposed targets' containment subtrees (plus, via
`ExposeScopeResolver.ResolveExposedScope`'s usage-to-type fallback, an exposed feature usage's own
type). Because the parent-lookup step runs only over the narrowed set, an exposed target whose own
parent was filtered out is promoted to a forest root, so the diagram becomes one or more subtrees
rooted at the exposed target(s) rather than a single truncated tree. Multiple `expose` targets union
their subtrees, since `IsInSubjectScope` matches against every resolved subject. A view with no
`expose` statement (including the synthesized `--auto` view, whose `ViewNode` is `null`) resolves no
scope and renders the full membership forest, unchanged from the pre-scoping behavior.

##### Error Handling

Expand All @@ -49,6 +68,8 @@ throw: the strategy returns an empty diagram rather than failing.
- `SysmlWorkspace`, `SysmlNode`, `SysmlPackageNode`, `SysmlDefinitionNode`, `SysmlFeatureNode`, and
`SysmlViewNode` (Semantic subsystem).
- `StdlibFilter` (Rendering Internal subsystem) — standard-library exclusion.
- `ExposeScopeResolver` (Layout Internal subsystem) — `ResolveExposedScope` and
`IsInSubjectScope` supply the shared `expose`-scoping used by `BuildForest`.

##### Callers

Expand Down
Loading
Loading