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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ section above).
| Multiple views, `--view <name>` | Render only the named view |
| `--view <name>` names a view that does not exist | Error: lists available view names and exits non-zero |

For the General View diagram strategy (the diagram produced when no more specialized view kind
applies), a view's body `expose <...>;` statements now scope the rendered diagram to the union of
the exposed names' containment subtrees, instead of always rendering the full workspace; a view
with no `expose` statement continues to render the full workspace. `render <target>;` names a
rendering style (not yet honored) and has no effect on scope. This scoping is not yet extended to
the other layout strategies — see the *Introduction* user guide for details.

## NuGet Packages

| Package | Description |
Expand Down
71 changes: 71 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,77 @@ primitives (bar, diamond, pentagon, note). `LayoutActivation`/`LayoutBand` alrea
**Visual gate:** sequence shows activation bars + a fragment; action flow shows a fork/join and
a decision/merge with correct shapes.

### View `filter [<expr>];` expression evaluation

`GeneralViewLayoutStrategy` now scopes a rendered diagram to a view's `expose <...>;` subject
subtree, but a view's `filter [<expr>];` body statement is only parsed into
`SysmlViewNode.FilterExpressionText` (raw source text) — it is never evaluated, and a
layout warning ("parsed but not yet evaluated") is emitted in its place. SysML v2 view filtering
is part of the standard's view/viewpoint mechanism for selectively including/excluding elements
from a rendered diagram by predicate, beyond simple subject-subtree containment scoping (for
example, "only elements satisfying a given requirement" or "only elements with a given
stereotype"); without evaluation, a modeler's `filter` statement is silently ineffective beyond
the warning.

- Design and implement an expression evaluator for the bracketed filter expression grammar
(boolean/membership predicates over the resolved scope's elements), reusing/aligning with
existing expression-parsing infrastructure where practical.
- Apply the evaluated predicate as an additional filter over the resolved (expose) scope in
`GeneralViewLayoutStrategy`, removing the "not yet evaluated" warning once a filter
expression is present and successfully evaluated.
- Surface a diagnostic for filter expressions that fail to parse or evaluate, mirroring the
unresolved-reference diagnostic pattern already used for `expose`.

**Scope:** `AstBuilder`/`SysmlViewNode` (expression AST capture, if warranted, beyond raw text);
new expression-evaluation component; `GeneralViewLayoutStrategy` filter application.
**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
grammar (e.g. `asTreeDiagram`, `asElementTable`, `asTextualNotation`, `asTextualNotationTable` —
`rendering` usages, a distinct usage/definition kind) — it is captured verbatim on
`SysmlViewNode.RenderTargetName` but currently has no effect on rendering; every view renders
through the single `DiagramTypeRouter` → `ILayoutStrategy` selection regardless of its declared
`render` member. This is a distinct future capability from content scoping, which is `expose`'s
exclusive role.

- Design a mapping from recognized rendering-style names (`asTreeDiagram`, `asElementTable`,
and so on, once corresponding layout/rendering strategies exist) to an `ILayoutStrategy`/
renderer selection, honored when a view declares a `render` member naming one.
- Leave `RenderTargetName` un-honored (as today) for rendering-style names with no corresponding
strategy, with no diagnostic — an unrecognized rendering-style name is not an error, since
`render` selects presentation, not content.

**Scope:** `DiagramTypeRouter`/`RenderCommand` (rendering-style selection); new layout/rendering
strategies for tree-diagram/element-table/textual-notation styles, if not already covered by an
existing strategy.
**Visual gate:** a view declaring `render asTreeDiagram;` (once a tree-diagram strategy exists)
renders using that style instead of the default `GeneralView` layout.

---

## Release & packaging
Expand Down
3 changes: 2 additions & 1 deletion docs/design/sysml2-tools-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ N/A — not a safety-classified software item.

1. `DiagramRenderer.RenderWorkspace` receives a `SysmlWorkspace`, an `IRenderer`, and
`RenderOptions`. For each view declared in the workspace it constructs a `ViewContext`
containing the view name and workspace reference.
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,53 @@ from the structural relationships.

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

Entry point. Calls `CollectDefinitions` to gather user definitions; returns a minimal 200×100 empty
`LayoutTree` when none are found. Otherwise groups the definitions by package with `GroupByPackage`,
resolves the specialization/membership/attribute-typing relationships into qualified-name edges with
Entry point. First resolves the view's exposed-name scope via `ResolveExposedScope`,
then calls `CollectDefinitions` to gather user definitions restricted to that scope (or every
definition when no scope applies); returns a minimal 200×100 empty `LayoutTree` when none are
found. Otherwise groups the definitions by package with `GroupByPackage`, resolves the
specialization/membership/attribute-typing relationships into qualified-name edges with
`BuildModelEdges`, builds the single input `LayoutGraph` with `BuildGraph`, and places the whole
graph with one `HierarchicalLayoutAlgorithm().Apply(graph, LayoutOptions.ForAlgorithm("containment"))`
call — passing the desired root-scope leaf algorithm through the options parameter (not
`graph.Set(CoreOptions.Algorithm, …)`) so a caller going through `LayoutEngine.Layout(graph)` later
is never misled into skipping the hierarchical engine. When any package folder was depth-truncated,
`DecorateTruncatedFolders` stamps each truncated folder's "+N more…" ellipsis label onto its placed
box before returning.

###### `CollectDefinitions(workspace, theme)`
box. Finally, when `context.ViewNode?.FilterExpressionText` is non-null, attaches the
"parsed but not yet evaluated" warning (from `LayoutWarnings.ForUnevaluatedFilter`) to the returned
tree's `Warnings` via the `LayoutTree with { Warnings = … }` record-copy idiom, leaving the
resolved (unfiltered) scope's content unchanged.

###### `ResolveExposedScope(SysmlWorkspace workspace, SysmlViewNode? viewNode)`

Resolves the qualified-name containment-subtree scope a view's `expose` statements restrict the
diagram to — the only content-scoping mechanism a view has (`render <target>;` names a
rendering style/format, e.g. `asTreeDiagram`/`asElementTable`, per the SysML v2 grammar, never
content, so `RenderTargetName` never affects this decision). Returns `null` — meaning "render
everything", byte-identical to the pre-scoping behavior — whenever the view has no resolved
`Expose`-kind `ResolvedEdges` entries: covering a `null` `viewNode` (the `--auto` synthesized
view, which never carries expose/render/filter data), a view with no `expose` statement, and a
view whose every `expose` entry failed to resolve, uniformly. Otherwise, for each resolved
`Expose` edge's target qualified name, adds that name to the scope; when
`workspace.Declarations` resolves the target to a `SysmlFeatureNode` (a usage, e.g.
`part myVehicle : Vehicle;`) rather than a `SysmlDefinitionNode`, additionally resolves the
usage's own `Typing`-kind `ResolvedEdges` entry (if any) and adds *that* type's qualified name to
the scope too — the usage-to-type resolution fix for the containment gap where a usage's own
(typically empty) subtree would otherwise silently produce zero content.

###### `IsInSubjectScope(qualifiedName, subjects)`

Returns `true` when `qualifiedName` equals one of `subjects` or lies within one of their
containment subtrees (a `"{subject}::"` prefix match) — the same qualified-name-prefix idiom
`StdlibFilter.IsStdlibElement` already uses for stdlib-prefix matching. Generic over any subject
list; unchanged by this fix.

###### `CollectDefinitions(workspace, theme, scope)`

Iterates `workspace.Declarations`, keeping each `SysmlDefinitionNode` that is not a
standard-library element (per `StdlibFilter.IsStdlibElement`). For each kept definition it builds
the compartments from the owned usage features (grouped by keyword, each formatted as a
`name : Type [n]` row), collects the typed memberships, and computes the box size from the title and
the longest compartment row.
standard-library element (per `StdlibFilter.IsStdlibElement`) and, when `scope` is non-null, is
within `scope` per `IsInSubjectScope`. For each kept definition it builds the compartments from
the owned usage features (grouped by keyword, each formatted as a `name : Type [n]` row), collects
the typed memberships, and computes the box size from the title and the longest compartment row.

###### `GroupByPackage(defs)`

Expand Down Expand Up @@ -128,6 +157,10 @@ produces valid geometry, so no crossing warnings are emitted.
- `BoxMetrics` (`DemaConsulting.Rendering.Abstractions`) — box title-area and folder-tab geometry.
- `StdlibFilter` (Rendering Internal subsystem) — standard-library exclusion.
- `SysmlWorkspace`, `SysmlDefinitionNode`, `SysmlFeatureNode` (Semantic subsystem) — model input.
- `SysmlViewNode`, `SysmlEdge`, `SysmlEdgeKind` (Semantic subsystem) — a view's resolved
`expose` data, read by `ResolveExposedScope`.
- `LayoutWarnings` (Layout Internal subsystem) — `ForUnevaluatedFilter` supplies the
"parsed but not yet evaluated" filter-expression warning text.
- The `LayoutTree`, `LayoutBox`, `LayoutCompartment`, `LayoutLine`, `LayoutLabel`, and `Point2D` data
types (`DemaConsulting.Rendering`).
- `FeatureMembership` (private record) — carries the keyword and type reference of one owned feature.
Expand Down
16 changes: 15 additions & 1 deletion docs/design/sysml2-tools-core/layout/internal/layout-warnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ Returns the warnings for a view:
is rendered in singular form for a count of one and plural form otherwise, and the count is
formatted with the invariant culture.

###### `ForUnevaluatedFilter(viewName, filterExpressionText)`

Returns the warnings for a view's declared filter expression:

1. When `filterExpressionText` is `null` (the view has no `filter [<expr>];` member), an empty
list is returned.
2. Otherwise a single warning string is produced naming the view: `"View '{viewName}' declares a
filter expression, which is parsed but not yet evaluated; all elements in the resolved scope
are rendered unfiltered."` The raw expression text itself is not interpolated into the message
(only its presence matters) — full filter expression evaluation is deferred future work (see
ROADMAP.md).

##### Error Handling

N/A - the method performs no validation and does not throw; a non-positive count simply yields an
Expand All @@ -36,4 +48,6 @@ empty list and any string view name is accepted.
##### Callers

View layout strategies that route connectors call `LayoutWarnings.ForCrossings` to attach
crossing warnings to the `LayoutTree` they produce.
crossing warnings to the `LayoutTree` they produce. `GeneralViewLayoutStrategy` calls
`LayoutWarnings.ForUnevaluatedFilter` to attach the "not yet evaluated" filter warning when a
view's `FilterExpressionText` is non-null.
24 changes: 20 additions & 4 deletions docs/design/sysml2-tools-core/rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ flowchart TD

- *Type*: Sealed record.
- *Role*: Data transfer object.
- *Contract*: `string ViewName`, `SysmlWorkspace Workspace`.
- *Contract*: `string ViewName`, `SysmlWorkspace Workspace`, `SysmlViewNode? ViewNode = null`.
`ViewNode` is the view's resolved AST node, giving a layout strategy access to the view's
declared `render`/`expose`/`filter` body statements. Of these, only `ExposedNames` (and its
resolved `Expose` edges) drives content scoping; `RenderTargetName` names a rendering
style/format per the SysML v2 grammar and is captured as inert metadata only (it never
affects scope or resolution); `FilterExpressionText` is captured as raw text and not yet
evaluated. `ViewNode` is `null` for the `--auto` synthesized view, which carries no AST node
of its own.

**Theme**: Visual configuration record.

Expand Down Expand Up @@ -127,9 +134,17 @@ flowchart TD
that tree and the options to `IRenderer.Render`. Each rendered stream is wrapped in a
`RenderOutput` and collected into the return list.

2. `ILayoutStrategy.BuildLayout` receives a `ViewContext` containing the workspace and the view
name, plus `RenderOptions` for size and scale hints. It produces a fully resolved
`LayoutTree` with all waypoints in absolute canvas coordinates.
2. `ILayoutStrategy.BuildLayout` receives a `ViewContext` containing the workspace, the view
name, and (when available) the view's resolved AST node, plus `RenderOptions` for size and
scale hints. It produces a fully resolved `LayoutTree` with all waypoints in absolute canvas
coordinates. `GeneralViewLayoutStrategy` is the only strategy that currently reads
`ViewContext.ViewNode` to scope its diagram: when the view has one or more resolved `Expose`
edges, the diagram is scoped to the union of the exposed targets' containment subtrees
(resolving through a usage's type to its definition's subtree where needed); a view with no
`Expose` edges renders the full workspace, unchanged from prior behavior. The view's
`render`/`filter` statements never affect this scope. Every other strategy ignores
`ViewContext.ViewNode` and renders as before (see the Layout subsystem's
`general-view-layout-strategy` design doc for the scoping algorithm).

3. `IRenderer.Render` receives the `LayoutTree` and `RenderOptions` and writes all rendered
bytes to the supplied `Stream`. It must not perform any layout computation; it only reads
Expand Down Expand Up @@ -160,6 +175,7 @@ flowchart TD
| SysML2Tools-Core-Rendering-ThemeDepthWrap | `Theme.DepthFillColors` with modulo indexing documented in `Theme` |
| SysML2Tools-Core-Rendering-RenderOptions | `RenderOptions` record with default values |
| SysML2Tools-Core-Rendering-ILayoutStrategy | `ILayoutStrategy` interface and `ViewContext` record |
| SysML2Tools-Core-Rendering-ViewContextViewNode | `ViewContext.ViewNode` flows to `DiagramRenderer.RenderWorkspace` |
| SysML2Tools-Core-Rendering-DiagramRenderer | `DiagramRenderer.RenderWorkspace`; `DiagramTypeRouter`; `StdlibFilter` |
| SysML2Tools-Core-Rendering-RenderOutput | `RenderOutput` record |
| SysML2Tools-Core-Rendering-BuiltinThemes | `Themes.Light`, `Themes.Dark`, `Themes.Print` |
8 changes: 5 additions & 3 deletions docs/design/sysml2-tools-core/rendering/diagram-renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ off-the-shelf `RenderOptions` and `RenderOutput` records from `DemaConsulting.Re

For each declaration in the workspace it skips non-view nodes and standard-library views (via
`StdlibFilter`), routes the view to an `ILayoutStrategy` (via `DiagramTypeRouter`), skips views with
no supporting strategy or that do not match `viewFilter`, builds the `LayoutTree`, renders it to an
in-memory stream, and collects a `RenderOutput` with a sanitized file name and the layout warnings.
Returns an empty list when the workspace declares no renderable views.
no supporting strategy or that do not match `viewFilter`, constructs a `ViewContext` carrying the
view's own `SysmlViewNode` (so a strategy such as `GeneralViewLayoutStrategy` can read its resolved
`render`/`expose`/`filter` data), builds the `LayoutTree`, renders it to an in-memory stream, and
collects a `RenderOutput` with a sanitized file name and the layout warnings. Returns an empty list
when the workspace declares no renderable views.

##### `GetViewNames(workspace)`

Expand Down
5 changes: 3 additions & 2 deletions docs/design/sysml2-tools-language/semantic.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ optionally seeded with a pre-populated symbol table.
- *Role*: Data container.
- *Contract*: Exposes `IReadOnlyList<string> Files`, `IReadOnlySet<string> StdlibNames`,
`IReadOnlyDictionary<string, SysmlNode> Declarations` mapping qualified names to declaration
nodes, and `SemanticIndex Index` — a reverse-lookup index over resolved supertype, typing,
and import edges (see Semantic Model Subsystem).
nodes, and `SemanticIndex Index` — a reverse-lookup index over all resolved edge kinds
(supertype, typing, import, satisfy, verify, allocate, connect, transition, and expose; see
Semantic Model Subsystem).

### Design

Expand Down
2 changes: 1 addition & 1 deletion docs/design/sysml2-tools-language/semantic/model.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ over resolved edges.
| --- | --- |
| `AstBuilder` | Visits ANTLR4 CST; builds typed AST nodes with qualified names and supertype lists |
| `SymbolTable` | Registry mapping fully-qualified names to their AST nodes |
| `ReferenceResolver` | Resolves supertype/typing/import/satisfy/verify/allocate/connect/transition refs; builds index |
| `ReferenceResolver` | Resolves supertype/typing/import/satisfy/verify/allocate/connect/transition/expose refs |
| `SupertypeWalker` | Walks specialization chains; detects cyclic specialization |
| `SysmlNode` | Public abstract base record (and subtypes) modeling one parsed AST element |
| `SysmlEdge` | Public record modeling one resolved reference (Supertype/Typing/Import/Satisfy/Verify/Allocate/etc.) |
Expand Down
Loading
Loading