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
49 changes: 39 additions & 10 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,45 @@ gallery currently shows.
**Depends on:** "Additional relationship edges (General View)" above (needs the new edge kinds
implemented first).

### Annotating elements & compartment depth

- Render **Documentation/Comment** notes as `BoxShape.Note` (folded-corner) nodes attached to
their annotated element.
- Extend compartments to spec depth: enumeration values, constraint bodies, requirement
`subject`/`constraints`/`doc`, and a documentation compartment on definitions/usages.

**Scope:** semantic exposure of doc/comment + compartment content; `GeneralViewLayoutStrategy`
and renderers; possibly `LayoutLabel`/compartment tweaks.
**Visual gate:** a documented requirement/part renders its note and full compartments.
### Annotating elements & compartment depth — delivered

**Delivered.** `AstBuilder` now captures every content kind this item scoped: `VisitEnumeratedValue`
builds an `"enum value"`-keyword feature for each `enum def` literal (bare, value-assignment, and
redefinition-body forms), via a dedicated `CollectEnumerationBodyChildren` helper (needed because
`enumerationBody` uniquely alternates `annotatingMember`/`enumerationUsageMember` directly rather
than wrapping them in a single rule). `VisitRequirementDefinition`/`VisitConcernDefinition` and —
extending beyond a literal reading of this item, since the dominant real-corpus idiom nests these
inside a requirement/concern *usage* rather than a definition — `VisitRequirementUsage`/
`VisitConcernUsage` now capture `subject`/`actor`/`stakeholder` members (`VisitSubjectUsage`/
`VisitActorUsage`/`VisitStakeholderUsage`) and `require constraint`/`assume constraint` members
(`VisitRequirementConstraintMember`/`BuildConstraintFeatureNode`, capturing the raw expression text
into a new `SysmlFeatureNode.ExpressionText` field mirroring `SysmlTransitionNode.Guard`).
`VisitConstraintUsage` (previously entirely absent) and a rewritten `VisitConstraintDefinition`
capture standalone/definition-level constraint expressions the same way. A newly-introduced
`VisitRequirementVerificationMember`/`VisitFramedConcernMember` null-suppression safeguard
prevents a `verify`/`frame` member's own nested body from being spuriously hoisted onto the
enclosing requirement's `Children` now that body-collection reaches into `requirementBodyItem`.

`GeneralViewLayoutStrategy` now reads `Annotations`: `AddAnnotationNote` emits one `BoxShape.Note`
box per annotated definition (concatenating multiple `Documentation`/`Comment` annotations into a
single note, documented as a deliberate choice) connected by a plain solid line with no end
marker — implemented as ordinary `LayoutGraph` nodes/edges added during the existing single-pass
`BuildGraph` traversal, a documented deviation from a literal post-layout marker-decoration
approach (no equivalent decoration phase exists in this strategy). `Pluralize` titles
`"subject"`/`"assume constraint"`/`"require constraint"`/`"constraint"` compartments with the
guillemet stereotype form (reusing the pre-existing `«allocate»` edge-label convention), and
`FormatFeatureRow` renders a constraint-kind feature's raw expression text in place of the generic
`name : Type [multiplicity]` row shape.

The Quadcopter Drone gallery model (`docs/gallery/models/01-drone-general.sysml`) was extended
with a `doc`-annotated `Battery`, an `enum def FlightMode` with literal values, and a
`FlightTimeRequirement` with a `subject`/`require constraint` body, and regenerated.

**Explicitly deferred (not delivered here):** full expression-tree modeling of constraint bodies
(raw text capture only); a nested `doc` annotation inside a constraint's own `calculationBody`
(e.g. `assume constraint { doc /* ... */ ... }`, seen in the OMG training corpus) is intentionally
not captured — an accepted scope boundary, not an oversight; any other view kind besides General
View.

### Action Flow View: control-node/successor AST correctness + fork/join/decision/merge shapes — delivered

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ box title and folder-tab geometry come from `BoxMetrics` in `DemaConsulting.Rend
`GeneralViewLayoutStrategy` has no instance state; all input arrives through the `BuildLayout`
parameters. Layout constants (`MinBoxWidth`, `CharWidthFactor`) are declared as `private const`
fields. Private records carry intermediate data: `DefBox` (a user definition with its computed
size, keyword, supertype names, memberships, and compartments), `ModelEdge` (a resolved
size, keyword, supertype names, memberships, compartments, and `Annotations` — the definition's
own `SysmlNode.Annotations` list, threaded through by `CollectDefinitions` so `AddAnnotationNote`
(below) can render them without a second workspace lookup), `ModelEdge` (a resolved
specialization/membership/attribute-typing/redefinition/subsetting/connect/allocate/dependency/
binding relationship expressed by qualified name, together with its target end marker, edge kind,
and an optional midpoint label), `Location` (a located definition's
Expand All @@ -33,10 +35,13 @@ carries the short kind label, raw source/target reference, and human-readable re
rendered boxes, feeding `LayoutWarnings.ForDroppedRelationshipEdges`. `FeatureMembership` (a private record) carries
each owned feature's keyword, raw type reference (`TypeName`, nullable — a feature may declare a
redefinition with no explicit type annotation), simple `Name`, raw
`RedefinedFeatureName` reference, and the raw `SubsettedFeatureNames` list (populated verbatim
from `SysmlFeatureNode.SupertypeNames` — a feature's `subsets`/`:>` targets, not a new AST field);
`CollectMemberships` includes a feature when `TypeName`, `RedefinedFeatureName`, or a non-empty
`SubsettedFeatureNames` is present. The private `EdgeKind` enumeration classifies each edge as
`RedefinedFeatureName` reference, the raw `SubsettedFeatureNames` list (populated verbatim
from `SysmlFeatureNode.SupertypeNames` — a feature's `subsets`/`:>` targets, not a new AST field),
and `ExpressionText` (verbatim from `SysmlFeatureNode.ExpressionText`, non-null only for
constraint-kind features such as `"require constraint"`/`"assume constraint"`/`"constraint"`);
`CollectMemberships` includes a feature when `TypeName`, `RedefinedFeatureName`,
`ExpressionText`, or a non-empty `SubsettedFeatureNames` is present. The private `EdgeKind`
enumeration classifies each edge as
`Specialization`, `Membership`, `Typing`, `Redefinition`, `Subsetting`, `Connect`, `Allocate`,
`Dependency`, or `Binding`; `Subsetting` is a purely view-layer classification — it does not
correspond to a public `SysmlEdgeKind` — reusing `SupertypeNames` and the same owner-resolution
Expand Down Expand Up @@ -87,10 +92,26 @@ the `LayoutTree with { Warnings = … }` record-copy idiom.
Iterates `workspace.Declarations`, keeping each `SysmlDefinitionNode` that is not a
standard-library element (per `StdlibFilter.IsStdlibElement`) and, when `scope` is non-null, is
within `scope` per `ExposeScopeResolver.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
compartments from the owned usage features (grouped by keyword, each formatted via
`FormatFeatureRow`), collects the typed memberships, and computes the box size from the title
and the longest compartment row.

Each compartment's title comes from `Pluralize(keyword)`, which special-cases a small set of
individually-significant single-member keywords to a guillemet-wrapped stereotype form instead of
the generic pluralized-keyword default: `"subject"` → `"«subject»"`, `"assume constraint"` →
`"«assume constraint»"`, `"require constraint"` → `"«require constraint»"`, and `"constraint"` →
`"«constraint»"`. This reuses the exact guillemet convention already established elsewhere in this
file for the `«allocate»` edge midpoint label (see `BuildModelEdges`), rather than inventing a new
one. Every other keyword introduced alongside this work — `"enum value"` (rendered as the
compartment title `"enum values"`), `"actor"`, `"stakeholder"` — falls through to the generic
pluralized-keyword default, matching the OMG spec figures' own compartment-heading conventions for
those kinds.

`FormatFeatureRow` renders a feature with a non-null, non-empty `ExpressionText` (constraint-kind
features only) as its raw expression text alone (unnamed) or as `"{name}: {expr}"` (named)
instead of the generic `"name : Type [multiplicity]"` shape used by every other feature kind,
since a constraint's defining content is its expression, not a type/multiplicity pair.

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

Groups definitions by the qualified-name prefix before the last `::`, preserving first-seen order.
Expand Down Expand Up @@ -244,6 +265,33 @@ map to reference and is silently dropped, exactly as before.
Adds one definition as a leaf node to the given scope (the root graph or a folder's `Children`),
carrying its `Label`, `Shape = Rectangle`, `Keyword`, and `Compartments`.

###### `AddAnnotationNote(scope, def, defNode, theme)`

Called immediately after `MakeDefNode` for every definition (both top-level and folder-scoped),
this no-ops when `def.Annotations` is empty; otherwise it adds one `BoxShape.Note` node (id
`note:{qualifiedName}`) to the same `scope` as `defNode`, plus a plain solid edge with no end
marker (id `note-edge:{qualifiedName}`, `TargetEnd = EndMarkerStyle.None`) from the note to the
definition. `BuildNoteText` concatenates every annotation's trimmed text with a blank-line
separator, so **one note box renders per annotated element**, never one box per individual
`comment`/`doc` — a definition with both a `doc` and two `comment`s still gets a single note.
`ComputeNoteBoxSize` sizes the note from the longest line and total line count using
`BoxMetrics.TitleAreaHeight(theme, hasLabel: false, hasKeyword: false)` for the (title-less) top
band plus per-line height and padding — deliberately simpler than the full `ComputeBoxSize` used
for definition boxes, since a note has no title/keyword band. The note's single
`LayoutCompartment` carries `Title = null` and one row per line of the concatenated text; no
`Label`/`Keyword` is set on the note node itself, matching conventional UML/SysML note rendering
(body text only).

This is implemented as ordinary `LayoutGraph` nodes and edges added during the same
single-pass `BuildGraph`/`CollectDefinitions` traversal that builds each definition's own box — a
deliberate, documented deviation from a literal "post-layout marker decoration" approach (the
pattern used by, e.g., `StateTransitionViewLayoutStrategy.AddInitialMarker`, which stamps a marker
onto an already-placed box after layout completes). This strategy has no equivalent
post-layout decoration phase — its entire graph, including folder containers and truncated-folder
placeholders, is built in one `BuildGraph` pass before layout ever runs — so adding the note as an
ordinary graph node/edge pair up front is the natural fit, and lets the layout engine route and
position it like any other element rather than requiring bespoke placement math.

###### `DecorateTruncatedFolders(tree, graph, truncated, theme)`

Replaces each truncated folder's placed box with one carrying its "+N more…" ellipsis label,
Expand Down
Loading
Loading