sysml-v2-lsp 0.40.0 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.41.0]
6
+
7
+ ### Changed
8
+
9
+ - Update the pinned grammar to `v2026.09.1` (OMG release `2026-09.1`) and refresh the parser and DFA snapshot with `make update-grammar`. The lexer and parser grammars are unchanged from `v2026.09.0`; this introduces no language behavior changes.
10
+
11
+ ### Fixed
12
+
13
+ - `sysml/model` reports an element's prefix from its own prefix and multiplicity productions (SysML 8.2.2.6.2 `RefPrefix`, `BasicUsagePrefix`, `EndUsagePrefix`, 8.2.2.6.6 `MultiplicityPart`): `modifier` now includes `ref`, `constant` and `end`, in written order; `direction` is reported for every usage, not only ports; `ordered: true` and `nonunique: true` when written. A keyword in a member's or an owned feature's declaration (e.g. a transition's trigger payload or guard), in a name or reference spelled like a keyword (`var`), or in a comment is no longer credited to the element.
14
+ - `readonly`, which is not a keyword in the SysML v2 grammar (KERML_-57 renamed it to `constant`), is no longer reported in `modifier` when written; completion offers `constant` instead. Resolved features have `isConstant`.
15
+ - `sysml/model` reports the value of every kind of usage, not only attributes, read from its own `ValuePart` (SysML 8.2.2.6.2): the expression after `=`, `:=` or `default`, with `isDefault` and `isInitial` for a default and an initial value (SysML 7.13.4). A cross subsetting (`=> x`) is no longer reported as a value.
16
+ - `sysml/model` reports an element's specializations from its own declaration only (SysML 8.2.2.6.1, 8.2.2.6.2), one relationship per target with its `notation`: a usage's `:>` as `subsetting` (not `specializes`), every `:>>` target as `redefinition`, and new `referenceSubsetting` (`::>`, `perform a`) and `crossSubsetting` (`=>`). A definition's `:>`, an anonymous redefinition and a transition's trigger payload are no longer reported as typings, a target keeps `$::` and a conjugated port typing's `~`, and `resolvedTypes` covers each usage with specializations of its own, its `specializes` listing the same targets.
17
+
18
+ ### Deprecated
19
+
20
+ - The resolved features' `isReadonly`: use `isConstant` instead, which it always equals. It will be removed in a future release.
21
+
5
22
  ## [0.40.0]
6
23
 
7
24
  ### Security
@@ -130404,7 +130404,10 @@ ${JSON.stringify(message, null, 4)}`);
130404
130404
  return parts;
130405
130405
  }
130406
130406
  function joinQualifiedName(parts) {
130407
- return parts.map((part) => /::|['\\\r\n\t\b\f]|^\s|\s$/.test(part) ? quoteName(part) : part).join("::");
130407
+ return parts.map((part) => /::|^:|:$|['\\\r\n\t\b\f]|^\s|\s$|^\$?$/.test(part) ? quoteName(part) : part).join("::");
130408
+ }
130409
+ function joinReference(chain, conjugated = false) {
130410
+ return (conjugated ? "~" : "") + chain.map(({ global, names }) => (global ? "$::" : "") + names.map((name) => /\.|^~/.test(name) ? quoteName(name) : joinQualifiedName([name])).join("::")).join(".");
130408
130411
  }
130409
130412
  function readNameAt(text, start, allowIncomplete = false) {
130410
130413
  if (text[start] === "'") {
@@ -131830,12 +131833,44 @@ ${JSON.stringify(message, null, 4)}`);
131830
131833
  var BODY_RULE_INDICES = new Set(
131831
131834
  SysMLv2Parser.ruleNames.flatMap((ruleName, index2) => ruleName.endsWith("Body") ? [index2] : [])
131832
131835
  );
131836
+ var VALUE_DECLARATION_RULE_INDICES = /* @__PURE__ */ new Set([
131837
+ SysMLv2Parser.RULE_usage,
131838
+ SysMLv2Parser.RULE_usageCompletion,
131839
+ SysMLv2Parser.RULE_actionUsageDeclaration,
131840
+ SysMLv2Parser.RULE_performActionUsageDeclaration,
131841
+ SysMLv2Parser.RULE_constraintUsageDeclaration,
131842
+ SysMLv2Parser.RULE_interfaceUsageDeclaration,
131843
+ SysMLv2Parser.RULE_flowDeclaration,
131844
+ SysMLv2Parser.RULE_messageDeclaration
131845
+ ]);
131833
131846
  var FLOW_PART_RULE_INDICES = /* @__PURE__ */ new Set([
131834
131847
  SysMLv2Parser.RULE_payloadFeatureMember,
131835
131848
  SysMLv2Parser.RULE_flowPayloadFeatureMember,
131836
131849
  SysMLv2Parser.RULE_flowEndMember,
131837
131850
  SysMLv2Parser.RULE_messageEventMember
131838
131851
  ]);
131852
+ var SPECIALIZATION_DECLARATION_RULE_INDICES = /* @__PURE__ */ new Set([
131853
+ SysMLv2Parser.RULE_definition,
131854
+ SysMLv2Parser.RULE_definitionDeclaration,
131855
+ SysMLv2Parser.RULE_usage,
131856
+ SysMLv2Parser.RULE_usageDeclaration,
131857
+ SysMLv2Parser.RULE_featureDeclaration,
131858
+ SysMLv2Parser.RULE_featureSpecializationPart,
131859
+ SysMLv2Parser.RULE_actionUsageDeclaration,
131860
+ SysMLv2Parser.RULE_performActionUsageDeclaration,
131861
+ SysMLv2Parser.RULE_constraintUsageDeclaration,
131862
+ SysMLv2Parser.RULE_interfaceUsageDeclaration,
131863
+ SysMLv2Parser.RULE_allocationUsageDeclaration,
131864
+ SysMLv2Parser.RULE_flowDeclaration,
131865
+ SysMLv2Parser.RULE_messageDeclaration
131866
+ ]);
131867
+ var REFERENCE_SHORTHAND_RULE_INDICES = /* @__PURE__ */ new Set([
131868
+ SysMLv2Parser.RULE_variantReference,
131869
+ SysMLv2Parser.RULE_performActionUsageDeclaration,
131870
+ SysMLv2Parser.RULE_exhibitStateUsage,
131871
+ SysMLv2Parser.RULE_framedConcernUsage,
131872
+ SysMLv2Parser.RULE_includeUseCaseUsage
131873
+ ]);
131839
131874
  var FLOW_DECLARATION_RULE_INDICES = [
131840
131875
  SysMLv2Parser.RULE_flowDeclaration,
131841
131876
  SysMLv2Parser.RULE_messageDeclaration
@@ -131941,6 +131976,50 @@ ${JSON.stringify(message, null, 4)}`);
131941
131976
  SysMLv2Parser.RULE_endFeaturePrefix
131942
131977
  // 86
131943
131978
  ]);
131979
+ var PREFIX_KEYWORD_TOKENS = /* @__PURE__ */ new Set([
131980
+ SysMLv2Lexer.IN,
131981
+ SysMLv2Lexer.OUT,
131982
+ SysMLv2Lexer.INOUT,
131983
+ SysMLv2Lexer.ABSTRACT,
131984
+ SysMLv2Lexer.VARIATION,
131985
+ SysMLv2Lexer.INDIVIDUAL,
131986
+ SysMLv2Lexer.DERIVED,
131987
+ SysMLv2Lexer.CONSTANT,
131988
+ SysMLv2Lexer.REF,
131989
+ SysMLv2Lexer.END,
131990
+ SysMLv2Lexer.ORDERED,
131991
+ SysMLv2Lexer.NONUNIQUE
131992
+ ]);
131993
+ var PREFIX_PATH_RULE_INDICES = /* @__PURE__ */ new Set([
131994
+ SysMLv2Parser.RULE_usagePrefix,
131995
+ SysMLv2Parser.RULE_unextendedUsagePrefix,
131996
+ SysMLv2Parser.RULE_basicUsagePrefix,
131997
+ SysMLv2Parser.RULE_endUsagePrefix,
131998
+ SysMLv2Parser.RULE_refPrefix,
131999
+ SysMLv2Parser.RULE_featureDirection,
132000
+ SysMLv2Parser.RULE_occurrenceUsagePrefix,
132001
+ SysMLv2Parser.RULE_controlNodePrefix,
132002
+ SysMLv2Parser.RULE_definitionPrefix,
132003
+ SysMLv2Parser.RULE_basicDefinitionPrefix,
132004
+ SysMLv2Parser.RULE_occurrenceDefinitionPrefix,
132005
+ SysMLv2Parser.RULE_usage,
132006
+ SysMLv2Parser.RULE_usageDeclaration,
132007
+ SysMLv2Parser.RULE_featureDeclaration,
132008
+ SysMLv2Parser.RULE_featureSpecializationPart,
132009
+ SysMLv2Parser.RULE_multiplicityPart,
132010
+ SysMLv2Parser.RULE_actionUsageDeclaration,
132011
+ SysMLv2Parser.RULE_performActionUsageDeclaration,
132012
+ SysMLv2Parser.RULE_constraintUsageDeclaration,
132013
+ SysMLv2Parser.RULE_interfaceUsageDeclaration,
132014
+ SysMLv2Parser.RULE_allocationUsageDeclaration,
132015
+ SysMLv2Parser.RULE_flowDeclaration,
132016
+ SysMLv2Parser.RULE_messageDeclaration
132017
+ ]);
132018
+ var OCCURRENCE_USAGE_ELEMENT_RULE_INDICES = /* @__PURE__ */ new Set([
132019
+ SysMLv2Parser.RULE_occurrenceUsageElement,
132020
+ SysMLv2Parser.RULE_structureUsageElement,
132021
+ SysMLv2Parser.RULE_behaviorUsageElement
132022
+ ]);
131944
132023
  var TYPE_SPECIALIZATION_RULE_INDICES = /* @__PURE__ */ new Set([
131945
132024
  SysMLv2Parser.RULE_specialization,
131946
132025
  // 66
@@ -132775,6 +132854,9 @@ ${JSON.stringify(message, null, 4)}`);
132775
132854
  const typeName = typeNames[0];
132776
132855
  const documentation = this.extractDocumentation(ctx);
132777
132856
  const visibility = this.extractVisibility(ctx);
132857
+ const featureValue = this.extractFeatureValue(ctx);
132858
+ const prefixKeywords = this.extractPrefixKeywords(ctx);
132859
+ const ownedSpecializations = this.extractOwnedSpecializations(ctx);
132778
132860
  const { multiplicity, multiplicityRange } = isUsage(kind) ? this.extractMultiplicity(ctx) : {};
132779
132861
  const metadataAnnotations = this.extractPrefixMetadataAnnotations(ctx);
132780
132862
  const isView = kind === "view" /* ViewUsage */ || kind === "view def" /* ViewDef */;
@@ -132807,6 +132889,9 @@ ${JSON.stringify(message, null, 4)}`);
132807
132889
  namingReference,
132808
132890
  documentation,
132809
132891
  visibility,
132892
+ featureValue,
132893
+ prefixKeywords: prefixKeywords.length > 0 ? prefixKeywords : void 0,
132894
+ ownedSpecializations: ownedSpecializations.length > 0 ? ownedSpecializations : void 0,
132810
132895
  source: transition?.source,
132811
132896
  target: transition?.target,
132812
132897
  flowDetails,
@@ -133190,7 +133275,23 @@ ${JSON.stringify(message, null, 4)}`);
133190
133275
  * between two of them (`connect a.p to b.p`, `connection : Conn`).
133191
133276
  */
133192
133277
  declarationText(ctx) {
133193
- const tokens = this.declarationTokens(ctx);
133278
+ return this.tokensText(this.declarationTokens(ctx));
133279
+ }
133280
+ /** Every token of `ctx`, in source order: a whole expression, bodies and `;` included. */
133281
+ subtreeTokens(ctx, tokens = []) {
133282
+ for (let i = 0; i < ctx.getChildCount(); i++) {
133283
+ const child = ctx.getChild(i);
133284
+ if (child instanceof TerminalNode) {
133285
+ if (child.symbol.tokenIndex >= 0) tokens.push(child.symbol);
133286
+ } else if (child instanceof ParserRuleContext) this.subtreeTokens(child, tokens);
133287
+ }
133288
+ return tokens;
133289
+ }
133290
+ /**
133291
+ * Tokens joined as written, with one space wherever the source has
133292
+ * whitespace or a comment between two of them.
133293
+ */
133294
+ tokensText(tokens) {
133194
133295
  let text = "";
133195
133296
  tokens.forEach((token, i) => {
133196
133297
  if (i > 0 && token.start > tokens[i - 1].stop + 1) text += " ";
@@ -133296,6 +133397,139 @@ ${JSON.stringify(message, null, 4)}`);
133296
133397
  }
133297
133398
  return void 0;
133298
133399
  }
133400
+ /**
133401
+ * `ctx`'s own feature value (SysML 7.13.4, 8.2.2.6.2 `ValuePart`, `FeatureValue`):
133402
+ * its expression as written, and whether it is a default (`default`) and an
133403
+ * initial (`:=`) value. Never a nested element's value; a cross subsetting
133404
+ * (`=> x`) is not a value. The expression is the whole `OwnedExpression`,
133405
+ * expression bodies included, with whitespace and comments as one space.
133406
+ */
133407
+ extractFeatureValue(ctx) {
133408
+ const featureValueCtx = this.findOwnValuePart(ctx)?.getRuleContext(0, FeatureValueContext);
133409
+ const expression = featureValueCtx?.ownedExpression();
133410
+ const text = expression && this.tokensText(this.subtreeTokens(expression));
133411
+ if (!featureValueCtx || !text) return void 0;
133412
+ return {
133413
+ expression: text,
133414
+ isDefault: featureValueCtx.DEFAULT() !== null,
133415
+ isInitial: featureValueCtx.COLON_EQ() !== null
133416
+ };
133417
+ }
133418
+ /**
133419
+ * `ctx`'s own `ValuePart`: a direct child of its element rule, or of one of
133420
+ * its declaration rules (`VALUE_DECLARATION_RULE_INDICES`). Never one of an
133421
+ * inline-owned element, e.g. a transition's accept payload or effect action.
133422
+ */
133423
+ findOwnValuePart(ctx) {
133424
+ for (let i = 0; i < ctx.getChildCount(); i++) {
133425
+ const child = ctx.getChild(i);
133426
+ if (!(child instanceof ParserRuleContext)) continue;
133427
+ if (child.ruleIndex === SysMLv2Parser.RULE_valuePart) return child;
133428
+ if (!VALUE_DECLARATION_RULE_INDICES.has(child.ruleIndex)) continue;
133429
+ const valuePart = this.findOwnValuePart(child);
133430
+ if (valuePart) return valuePart;
133431
+ }
133432
+ return void 0;
133433
+ }
133434
+ /**
133435
+ * The specializations of `ctx`'s own declaration, one per target in written
133436
+ * order, read only from its `FeatureSpecialization`s (SysML v2 8.2.2.6.2),
133437
+ * `SubclassificationPart` (8.2.2.6.1) and a shorthand's reference
133438
+ * (`perform a`, `REFERENCE_SHORTHAND_RULE_INDICES`), reached through
133439
+ * `SPECIALIZATION_DECLARATION_RULE_INDICES`.
133440
+ */
133441
+ extractOwnedSpecializations(ctx) {
133442
+ const specializations = [];
133443
+ const add = (kind, targets, notation) => {
133444
+ for (const targetCtx of targets) {
133445
+ const target = targetCtx && this.specializationTarget(targetCtx);
133446
+ if (target) specializations.push(notation ? { kind, target, notation: notation.getText() } : { kind, target });
133447
+ }
133448
+ };
133449
+ const visit = (node) => {
133450
+ for (let i = 0; i < node.getChildCount(); i++) {
133451
+ const child = node.getChild(i);
133452
+ if (child instanceof FeatureSpecializationContext) {
133453
+ const typings = child.typings();
133454
+ const subsettings = child.subsettings();
133455
+ const references = child.references();
133456
+ const crosses = child.crosses();
133457
+ const redefinitions = child.redefinitions();
133458
+ if (typings) {
133459
+ const typedBy = typings.typedBy();
133460
+ add("typing", [typedBy?.featureTyping(), ...typings.featureTyping()].map((typing) => typing && (typing.ownedFeatureTyping() ?? typing.conjugatedPortTyping())));
133461
+ } else if (subsettings) {
133462
+ const subsets = subsettings.subsets();
133463
+ add("subsetting", [subsets?.ownedSubsetting(), ...subsettings.ownedSubsetting()], subsets && (subsets.COLON_GT() ?? subsets.SUBSETS()));
133464
+ } else if (references) {
133465
+ add("referenceSubsetting", [references.ownedReferenceSubsetting()], references.COLON_COLON_GT() ?? references.REFERENCES());
133466
+ } else if (crosses) {
133467
+ add("crossSubsetting", [crosses.ownedCrossSubsetting()], crosses.FAT_ARROW() ?? crosses.CROSSES());
133468
+ } else if (redefinitions) {
133469
+ const redefines = redefinitions.redefines();
133470
+ add("redefinition", [redefines?.ownedRedefinition(), ...redefinitions.ownedRedefinition()], redefines && (redefines.COLON_GT_GT() ?? redefines.REDEFINES()));
133471
+ }
133472
+ } else if (child instanceof OwnedReferenceSubsettingContext && REFERENCE_SHORTHAND_RULE_INDICES.has(node.ruleIndex)) {
133473
+ add("referenceSubsetting", [child]);
133474
+ } else if (child instanceof SubclassificationPartContext) {
133475
+ add("specializes", child.ownedSubclassification(), child.COLON_GT() ?? child.SPECIALIZES());
133476
+ } else if (child instanceof ParserRuleContext && SPECIALIZATION_DECLARATION_RULE_INDICES.has(child.ruleIndex)) {
133477
+ visit(child);
133478
+ }
133479
+ }
133480
+ };
133481
+ visit(ctx);
133482
+ return specializations;
133483
+ }
133484
+ /**
133485
+ * The general type or feature a specialization's target rule names: its
133486
+ * qualified name, or its feature chain (`v.engine`), with a global `$::`
133487
+ * (KerML 7.2.2) and a conjugated port typing's `~` (`~P`, SysML 8.3.12.3).
133488
+ * Its names are quoted as everywhere in the server (`joinReference`).
133489
+ * Undefined for a target missing after a syntax error.
133490
+ */
133491
+ specializationTarget(ctx) {
133492
+ const chain = ctx.getRuleContexts(QualifiedNameContext).map((qualifiedName) => ({
133493
+ global: qualifiedName.DOLLAR() !== null,
133494
+ names: qualifiedName.getRuleContexts(NameContext).map((name) => name.getText())
133495
+ }));
133496
+ if (chain.length === 0 || chain.some(({ names }) => names.length === 0 || names.some((name) => !name))) return void 0;
133497
+ return joinReference(
133498
+ chain.map(({ global, names }) => ({ global, names: names.map(unquoteName) })),
133499
+ ctx instanceof ConjugatedPortTypingContext
133500
+ );
133501
+ }
133502
+ /**
133503
+ * The keywords of `ctx`'s own prefix and multiplicity part, in written
133504
+ * order (`in`, `abstract`, `constant`, `ref`, `end`, `ordered`, ...),
133505
+ * read only from the rules in `PREFIX_PATH_RULE_INDICES`.
133506
+ */
133507
+ extractPrefixKeywords(ctx) {
133508
+ const keywords = [];
133509
+ const visit = (node) => {
133510
+ for (let i = 0; i < node.getChildCount(); i++) {
133511
+ const child = node.getChild(i);
133512
+ if (child instanceof TerminalNode) {
133513
+ if (PREFIX_KEYWORD_TOKENS.has(child.symbol.type)) keywords.push(child.getText());
133514
+ continue;
133515
+ }
133516
+ if (child instanceof ParserRuleContext && PREFIX_PATH_RULE_INDICES.has(child.ruleIndex)) visit(child);
133517
+ }
133518
+ };
133519
+ visit(ctx);
133520
+ return this.isEndOccurrence(ctx) ? ["end", ...keywords] : keywords;
133521
+ }
133522
+ /**
133523
+ * Whether `ctx` is the occurrence usage of an `endOccurrenceUsageElement`
133524
+ * (`end part a`), whose `end` precedes the usage's own rule.
133525
+ */
133526
+ isEndOccurrence(ctx) {
133527
+ let current = ctx.parent;
133528
+ while (current instanceof ParserRuleContext && OCCURRENCE_USAGE_ELEMENT_RULE_INDICES.has(current.ruleIndex)) {
133529
+ current = current.parent;
133530
+ }
133531
+ return current !== ctx.parent && current instanceof ParserRuleContext && current.ruleIndex === SysMLv2Parser.RULE_endOccurrenceUsageElement;
133532
+ }
133299
133533
  /**
133300
133534
  * Extract the range of just the name token.
133301
133535
  */
@@ -137633,22 +137867,6 @@ ${JSON.stringify(message, null, 4)}`);
137633
137867
  const start = skipWS(text, afterPos);
137634
137868
  return readIdent(text, start, allowDots);
137635
137869
  }
137636
- function readCommaSeparatedIdents(text, pos) {
137637
- const names = [];
137638
- let p = skipWS(text, pos);
137639
- while (p < text.length) {
137640
- const [name, end] = readIdent(text, p);
137641
- if (!name) break;
137642
- names.push(name);
137643
- p = skipWS(text, end);
137644
- if (p < text.length && text[p] === ",") {
137645
- p = skipWS(text, p + 1);
137646
- } else {
137647
- break;
137648
- }
137649
- }
137650
- return names;
137651
- }
137652
137870
  function symbolsById(symbols) {
137653
137871
  return new Map(symbols.map((s) => [s.symbolId, s]));
137654
137872
  }
@@ -137694,7 +137912,7 @@ ${JSON.stringify(message, null, 4)}`);
137694
137912
  result.activityDiagrams = this.extractActivityDiagrams(symbolTable, uri, lines);
137695
137913
  }
137696
137914
  if (scopeSet.has("resolvedTypes")) {
137697
- result.resolvedTypes = this.extractResolvedTypes(symbolTable, uri, lines);
137915
+ result.resolvedTypes = this.extractResolvedTypes(symbolTable, uri);
137698
137916
  }
137699
137917
  if (scopeSet.has("diagnostics")) {
137700
137918
  result.diagnostics = this.extractSemanticDiagnostics(symbolTable, uri);
@@ -137793,44 +138011,34 @@ ${JSON.stringify(message, null, 4)}`);
137793
138011
  if (symbol.viewRendering) {
137794
138012
  attributes["viewRendering"] = symbol.viewRendering;
137795
138013
  }
137796
- const direction = this.extractDirection(symbol, lines);
137797
- if (direction) {
137798
- attributes["direction"] = direction;
138014
+ const prefix = prefixAttributesOf(symbol);
138015
+ if (prefix.direction) {
138016
+ attributes["direction"] = prefix.direction;
137799
138017
  }
137800
138018
  if (symbol.multiplicity) {
137801
138019
  attributes["multiplicity"] = symbol.multiplicity;
137802
138020
  }
137803
- const modifier = this.extractModifier(symbol, lines);
137804
- if (modifier) {
137805
- attributes["modifier"] = modifier;
138021
+ if (prefix.modifier) {
138022
+ attributes["modifier"] = prefix.modifier;
138023
+ }
138024
+ if (prefix.ordered) {
138025
+ attributes["ordered"] = true;
138026
+ }
138027
+ if (prefix.nonunique) {
138028
+ attributes["nonunique"] = true;
137806
138029
  }
137807
138030
  if (symbol.visibility) {
137808
138031
  attributes["visibility"] = symbol.visibility;
137809
138032
  }
137810
- const value = this.extractValue(symbol, lines);
137811
- if (value) {
137812
- attributes["value"] = value;
138033
+ if (symbol.featureValue) {
138034
+ attributes["value"] = symbol.featureValue.expression;
138035
+ if (symbol.featureValue.isDefault) attributes["isDefault"] = true;
138036
+ if (symbol.featureValue.isInitial) attributes["isInitial"] = true;
137813
138037
  }
137814
138038
  const relationships = [];
137815
- for (const tn of symbol.typeNames) {
137816
- relationships.push({
137817
- type: "typing",
137818
- source: symbol.name,
137819
- sourceId: symbol.symbolId,
137820
- target: tn
137821
- });
137822
- }
138039
+ relationships.push(...this.specializationRelationships(symbol));
137823
138040
  const flowRelationship = this.flowRelationship(symbol);
137824
138041
  if (flowRelationship) relationships.push(flowRelationship);
137825
- const specializations = this.extractSpecializations(symbol, lines);
137826
- for (const spec of specializations) {
137827
- relationships.push({
137828
- type: "specializes",
137829
- source: symbol.name,
137830
- sourceId: symbol.symbolId,
137831
- target: spec
137832
- });
137833
- }
137834
138042
  return {
137835
138043
  type: symbol.kind,
137836
138044
  name: symbol.name,
@@ -137842,6 +138050,20 @@ ${JSON.stringify(message, null, 4)}`);
137842
138050
  relationships
137843
138051
  };
137844
138052
  }
138053
+ /**
138054
+ * The typings, subclassifications, subsettings and redefinitions of the
138055
+ * element's own declaration, one per target in written order (SysML v2
138056
+ * 8.2.2.6.1, 8.2.2.6.2).
138057
+ */
138058
+ specializationRelationships(symbol) {
138059
+ return (symbol.ownedSpecializations ?? []).map(({ kind, target, notation }) => ({
138060
+ type: kind,
138061
+ source: symbol.name,
138062
+ sourceId: symbol.symbolId,
138063
+ target,
138064
+ ...notation ? { notation } : {}
138065
+ }));
138066
+ }
137845
138067
  flowRelationship(symbol) {
137846
138068
  const details = symbol.flowDetails;
137847
138069
  if (!isFlowUsage(symbol.kind) || !details?.source || !details.target) return void 0;
@@ -137875,25 +138097,7 @@ ${JSON.stringify(message, null, 4)}`);
137875
138097
  if (symbol.kind === "package" /* Package */ || symbol.kind === "import" /* Import */ || symbol.kind === "comment" /* Comment */ || symbol.kind === "doc" /* Doc */) {
137876
138098
  continue;
137877
138099
  }
137878
- if (symbol.typeNames.length > 0 && isUsage(symbol.kind)) {
137879
- for (const tn of symbol.typeNames) {
137880
- relationships.push({
137881
- type: "typing",
137882
- source: symbol.name,
137883
- sourceId: symbol.symbolId,
137884
- target: tn
137885
- });
137886
- }
137887
- }
137888
- const specializations = this.extractSpecializations(symbol, lines);
137889
- for (const spec of specializations) {
137890
- relationships.push({
137891
- type: "specializes",
137892
- source: symbol.name,
137893
- sourceId: symbol.symbolId,
137894
- target: spec
137895
- });
137896
- }
138100
+ relationships.push(...this.specializationRelationships(symbol));
137897
138101
  if (symbol.kind === "connection" /* ConnectionUsage */) {
137898
138102
  const connectionTargets = this.extractConnectionEndpoints(symbol, lines);
137899
138103
  if (connectionTargets.length === 2) {
@@ -138386,41 +138590,30 @@ ${JSON.stringify(message, null, 4)}`);
138386
138590
  * Because the LSP server doesn't yet have a full type resolver or library
138387
138591
  * index, we provide what's available from the parse tree.
138388
138592
  */
138389
- extractResolvedTypes(symbolTable, uri, lines) {
138593
+ extractResolvedTypes(symbolTable, uri) {
138390
138594
  const symbols = symbolTable.getSymbolsForUri(uri);
138391
138595
  const result = {};
138392
138596
  for (const symbol of symbols) {
138393
138597
  const qualifiedName = symbol.qualifiedName;
138394
138598
  if (qualifiedName === void 0) continue;
138395
- if (!isDefinition(symbol.kind) && symbol.typeNames.length === 0) {
138599
+ if (!isDefinition(symbol.kind) && !symbol.ownedSpecializations) {
138396
138600
  continue;
138397
138601
  }
138398
- const specializes = [];
138399
- const specList = this.extractSpecializations(symbol, lines);
138400
- for (const s of specList) {
138401
- if (!specializes.includes(s)) {
138402
- specializes.push(s);
138403
- }
138404
- }
138405
- for (const tn of symbol.typeNames) {
138406
- if (!specializes.includes(tn)) {
138407
- specializes.push(tn);
138408
- }
138409
- }
138602
+ const specializes = [...new Set((symbol.ownedSpecializations ?? []).map(({ target }) => target))];
138410
138603
  const specializationChain = [...specializes];
138411
138604
  const children = this.getChildSymbols(symbol, symbolTable);
138412
138605
  const features = children.filter((c) => isUsage(c.kind)).map((c) => {
138413
- const modifier = this.extractModifier(c, lines);
138414
- const direction = this.extractDirection(c, lines);
138606
+ const prefix = prefixAttributesOf(c);
138415
138607
  return {
138416
138608
  name: c.name,
138417
138609
  kind: toMetaclassName(c.kind),
138418
138610
  type: c.typeNames.join(", ") || void 0,
138419
138611
  multiplicity: c.multiplicity,
138420
- direction,
138612
+ direction: prefix.direction,
138421
138613
  visibility: c.visibility,
138422
- isDerived: modifier?.split(", ").includes("derived") ?? false,
138423
- isReadonly: modifier?.split(", ").includes("readonly") ?? false
138614
+ isDerived: prefix.keywords.includes("derived"),
138615
+ isConstant: prefix.isConstant,
138616
+ isReadonly: prefix.isConstant
138424
138617
  };
138425
138618
  });
138426
138619
  result[qualifiedName] = {
@@ -138537,67 +138730,6 @@ ${JSON.stringify(message, null, 4)}`);
138537
138730
  const endLine = Math.min(symbol.range.end.line, lines.length - 1);
138538
138731
  return lines.slice(startLine, endLine + 1).join("\n");
138539
138732
  }
138540
- /** Extract port direction (in | out | inout) from source text. */
138541
- extractDirection(symbol, lines) {
138542
- if (symbol.kind !== "port" /* PortUsage */ && symbol.kind !== "port def" /* PortDef */) {
138543
- return void 0;
138544
- }
138545
- const elementText = this.getElementText(symbol, lines);
138546
- if (containsWord(elementText, "inout")) return "inout";
138547
- if (containsWord(elementText, "in")) return "in";
138548
- if (containsWord(elementText, "out")) return "out";
138549
- return void 0;
138550
- }
138551
- /** Extract modifiers (abstract, readonly, derived, etc.) from source text. */
138552
- extractModifier(symbol, lines) {
138553
- const elementText = this.getElementText(symbol, lines);
138554
- const modifiers = [];
138555
- if (containsWord(elementText, "abstract")) modifiers.push("abstract");
138556
- if (containsWord(elementText, "readonly")) modifiers.push("readonly");
138557
- if (containsWord(elementText, "derived")) modifiers.push("derived");
138558
- if (containsWord(elementText, "variation")) modifiers.push("variation");
138559
- if (containsWord(elementText, "individual")) modifiers.push("individual");
138560
- return modifiers.length > 0 ? modifiers.join(", ") : void 0;
138561
- }
138562
- /** Extract default/assigned value from source text. */
138563
- extractValue(symbol, lines) {
138564
- if (symbol.kind !== "attribute" /* AttributeUsage */) {
138565
- return void 0;
138566
- }
138567
- const elementText = this.getElementText(symbol, lines);
138568
- const eqIdx = elementText.indexOf("=");
138569
- if (eqIdx < 0) return void 0;
138570
- let start = eqIdx + 1;
138571
- if (start < elementText.length && elementText[start] === "=") return void 0;
138572
- start = skipWS(elementText, start);
138573
- let end = start;
138574
- while (end < elementText.length) {
138575
- const ch = elementText[end];
138576
- if (ch === ";" || ch === "{" || ch === "}" || ch === "\n") break;
138577
- end++;
138578
- }
138579
- const val = elementText.substring(start, end).trim();
138580
- if (val && !val.includes("def") && val.length < 100) {
138581
- return val;
138582
- }
138583
- return void 0;
138584
- }
138585
- /** Extract all specialization targets from `:>` or `specializes` syntax. */
138586
- extractSpecializations(symbol, lines) {
138587
- const elementText = this.getElementText(symbol, lines);
138588
- const colonGtIdx = elementText.indexOf(":>");
138589
- if (colonGtIdx >= 0) {
138590
- if (colonGtIdx + 2 < elementText.length && elementText[colonGtIdx + 2] === ">") {
138591
- } else {
138592
- return readCommaSeparatedIdents(elementText, colonGtIdx + 2);
138593
- }
138594
- }
138595
- const specPositions = findWordPositions(elementText, "specializes");
138596
- if (specPositions.length > 0) {
138597
- return readCommaSeparatedIdents(elementText, specPositions[0].afterPos);
138598
- }
138599
- return [];
138600
- }
138601
138733
  /** Extract connection endpoints from `connect` syntax. */
138602
138734
  extractConnectionEndpoints(symbol, lines) {
138603
138735
  const elementText = this.getElementText(symbol, lines);
@@ -138661,16 +138793,6 @@ ${JSON.stringify(message, null, 4)}`);
138661
138793
  const fromElement = { source: elementName, sourceId: element.symbolId };
138662
138794
  elementText = stripComments(elementText);
138663
138795
  const rels = [];
138664
- const subsetsPositions = findWordPositions(elementText, "subsets");
138665
- if (subsetsPositions.length > 0) {
138666
- const [target] = readWordAfterKeyword(elementText, subsetsPositions[0].afterPos);
138667
- if (target) rels.push({ type: "subsetting", ...fromElement, target });
138668
- }
138669
- const redefPositions = findWordPositions(elementText, "redefines");
138670
- if (redefPositions.length > 0) {
138671
- const [target] = readWordAfterKeyword(elementText, redefPositions[0].afterPos);
138672
- if (target) rels.push({ type: "redefinition", ...fromElement, target });
138673
- }
138674
138796
  const satisfyPositions = findWordPositions(elementText, "satisfy");
138675
138797
  if (satisfyPositions.length > 0) {
138676
138798
  let pos = skipWS(elementText, satisfyPositions[0].afterPos);
@@ -138964,6 +139086,21 @@ ${this.followingBranchStatements(symbol, lines)}`;
138964
139086
  };
138965
139087
  }
138966
139088
  };
139089
+ var DIRECTIONS = /* @__PURE__ */ new Set(["in", "out", "inout"]);
139090
+ var MULTIPLICITY_KEYWORDS = /* @__PURE__ */ new Set(["ordered", "nonunique"]);
139091
+ function prefixAttributesOf(symbol) {
139092
+ const keywords = symbol.prefixKeywords ?? [];
139093
+ const direction = keywords.find((keyword) => DIRECTIONS.has(keyword));
139094
+ const modifiers = keywords.filter((keyword) => !DIRECTIONS.has(keyword) && !MULTIPLICITY_KEYWORDS.has(keyword));
139095
+ return {
139096
+ keywords,
139097
+ direction,
139098
+ modifier: modifiers.length > 0 ? modifiers.join(", ") : void 0,
139099
+ isConstant: keywords.includes("constant"),
139100
+ ordered: keywords.includes("ordered"),
139101
+ nonunique: keywords.includes("nonunique")
139102
+ };
139103
+ }
138967
139104
 
138968
139105
  // server/src/platform/connection.browser.ts
138969
139106
  var import_browser = __toESM(require_browser3());
@@ -139640,7 +139777,7 @@ ${childIndent}item def ${sourceName};
139640
139777
  { label: "satisfy", kind: import_node2.CompletionItemKind.Operator, detail: "Satisfaction", documentation: "Indicates requirement satisfaction", insertText: "satisfy ${1:requirement} by ${2:element};", insertTextFormat: import_node2.InsertTextFormat.Snippet },
139641
139778
  // Modifiers
139642
139779
  { label: "abstract", kind: import_node2.CompletionItemKind.Keyword, detail: "Abstract modifier", documentation: "Makes element abstract" },
139643
- { label: "readonly", kind: import_node2.CompletionItemKind.Keyword, detail: "Readonly modifier", documentation: "Makes attribute readonly" },
139780
+ { label: "constant", kind: import_node2.CompletionItemKind.Keyword, detail: "Constant modifier", documentation: "Makes feature constant" },
139644
139781
  { label: "derived", kind: import_node2.CompletionItemKind.Keyword, detail: "Derived modifier", documentation: "Indicates value is derived/calculated" },
139645
139782
  { label: "in", kind: import_node2.CompletionItemKind.Keyword, detail: "Input direction", documentation: "Port/parameter receives input" },
139646
139783
  { label: "out", kind: import_node2.CompletionItemKind.Keyword, detail: "Output direction", documentation: "Port/parameter provides output" },