sysml-diagram 0.23.0 → 0.25.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/out/main.js CHANGED
@@ -162474,6 +162474,35 @@ function connectionEndMultiplicity(node) {
162474
162474
  return void 0;
162475
162475
  return hi == null || hi === lo ? `[${lo}]` : `[${lo}..${hi}]`;
162476
162476
  }
162477
+ function isEmptyPrefixFlow(node) {
162478
+ const fm = node;
162479
+ return node.$type === "FlowStmt" && fm.target !== void 0 && fm.source === void 0;
162480
+ }
162481
+ function flowStmtName(node) {
162482
+ return isEmptyPrefixFlow(node) ? void 0 : effectiveNameOf(node);
162483
+ }
162484
+ function flowKeyword(fm) {
162485
+ return `${fm.succession === true ? "succession " : ""}${fm.flowKind === "message" ? "message" : "flow"}`;
162486
+ }
162487
+ function flowTransferredFeature(ends) {
162488
+ const sourceFeature = ends.source ? lastSeg(ends.source) : void 0;
162489
+ const targetFeature = ends.target ? lastSeg(ends.target) : void 0;
162490
+ return sourceFeature === targetFeature ? sourceFeature : sourceFeature ?? targetFeature;
162491
+ }
162492
+ function flowDecorationMeta(carried) {
162493
+ const drawn = carried.filter((flow) => !flow.message);
162494
+ if (!drawn.length)
162495
+ return {};
162496
+ return {
162497
+ flowDecorations: drawn.map(({ keyword, label, name, reversed, row }) => ({
162498
+ keyword,
162499
+ ...label ? { label } : {},
162500
+ ...name ? { name } : {},
162501
+ reversed,
162502
+ ...row.source ? { source: row.source } : {}
162503
+ }))
162504
+ };
162505
+ }
162477
162506
  var END_PROPERTY_ADORNMENTS = /* @__PURE__ */ new Set(["abstract", "derived", "readonly", "ordered", "nonunique"]);
162478
162507
  var uniqueStrings = (values2) => [...new Set([...values2].filter((value) => !!value))];
162479
162508
  function formatConnectionEndAdornment(notation) {
@@ -164919,6 +164948,27 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164919
164948
  }
164920
164949
  return { source: void 0, target: fm.target ? String(fm.target) : void 0 };
164921
164950
  }
164951
+ // REQ-401 — the `flows-compartment` row of one flow or message usage (OMG
164952
+ // SysML 8.2.3.16). Rebuilt from the declaration instead of copied from the
164953
+ // source text, because a flow may carry a BODY (`flow a.x to b.y { doc ... }`)
164954
+ // whose whole text would otherwise be inlined into one compartment row.
164955
+ // `ends` overrides the declared endpoints with the concrete ones a relationship
164956
+ // usage rebinds them to (REQ-402).
164957
+ // REQ-401 — Flow notation: on-connection decoration, port docking, and the flow usage node
164958
+ flowRowText(flow, ends) {
164959
+ const fm = flow;
164960
+ const declared = ends ?? this.flowStmtEnds(fm);
164961
+ const type = typeText(flow);
164962
+ const payload = this.flowPayloadText(fm);
164963
+ return [
164964
+ flowKeyword(fm),
164965
+ flowStmtName(flow),
164966
+ type ? `: ${type}` : void 0,
164967
+ payload ? `of ${payload}` : void 0,
164968
+ declared.source ? `from ${declared.source}` : void 0,
164969
+ declared.target ? `to ${declared.target}` : void 0
164970
+ ].filter(Boolean).join(" ");
164971
+ }
164922
164972
  // Payload text of a flow/message (`of Fuel` / `of f : Fuel`) per OMG SysML
164923
164973
  // 8.2.2.16 PayloadFeature.
164924
164974
  flowPayloadText(fm) {
@@ -164928,6 +164978,87 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164928
164978
  const typeRef = p.type?.$refText ?? p.typing?.type?.$refText;
164929
164979
  return typeRef ? lastSeg(typeRef) : p.name;
164930
164980
  }
164981
+ // REQ-402 — the flows a connection or interface usage CARRIES. A flow declared
164982
+ // in a `connection def` / `interface def` body (OMG SysML 8.2.3.16, and the
164983
+ // `Flow Interface Example` of the training corpus) is transported by every
164984
+ // usage of that definition; it is not a transfer of its own between two
164985
+ // unrelated features. Each end path is rebound through the usage's own end
164986
+ // bindings, so the definition-local `supplierPort.fuelSupply` reads as the
164987
+ // concrete `tankAssy.fuelTankPort.fuelSupply` this usage connects, and the
164988
+ // transfer direction is expressed relative to the usage's own end ordinals so
164989
+ // it survives whichever notation the relationship is drawn in.
164990
+ // REQ-402 — Flows carried by a connection or interface usage
164991
+ carriedFlowsOf(usage, ends, index2, uri) {
164992
+ const ordinalByRole = /* @__PURE__ */ new Map();
164993
+ const pathByRole = /* @__PURE__ */ new Map();
164994
+ for (const end of ends) {
164995
+ const role = end.notation.role;
164996
+ if (!role)
164997
+ continue;
164998
+ if (!ordinalByRole.has(role))
164999
+ ordinalByRole.set(role, end.ordinal);
165000
+ if (end.path && !pathByRole.has(role))
165001
+ pathByRole.set(role, end.path);
165002
+ }
165003
+ const rebind = (path10) => {
165004
+ if (!path10)
165005
+ return void 0;
165006
+ const segs = path10.split(".");
165007
+ const bound = pathByRole.get(segs[0]);
165008
+ return bound ? [bound, ...segs.slice(1)].join(".") : path10;
165009
+ };
165010
+ const carried = [];
165011
+ const seen = /* @__PURE__ */ new Set();
165012
+ for (const source of [usage, ...this.inheritedFeatureOwnersOf(usage, index2)]) {
165013
+ for (const member of membersOf(source)) {
165014
+ if (member.$type !== "FlowStmt" || member.isDef === true)
165015
+ continue;
165016
+ const fm = member;
165017
+ const flowEnds = this.flowStmtEnds(fm);
165018
+ const name = flowStmtName(member);
165019
+ const payload = this.flowPayloadText(fm);
165020
+ const key = name ?? `\0${flowEnds.source ?? ""}\0${flowEnds.target ?? ""}\0${payload ?? typeText(member) ?? ""}`;
165021
+ if (seen.has(key))
165022
+ continue;
165023
+ seen.add(key);
165024
+ const fromOrdinal = flowEnds.source ? ordinalByRole.get(flowEnds.source.split(".")[0]) : void 0;
165025
+ const from = rebind(flowEnds.source);
165026
+ const to = rebind(flowEnds.target);
165027
+ carried.push({
165028
+ flow: member,
165029
+ keyword: flowKeyword(fm),
165030
+ label: payload ?? typeText(member) ?? flowTransferredFeature(flowEnds),
165031
+ name,
165032
+ // The decoration rides the relationship, whose drawn direction
165033
+ // runs from its first end to its second.
165034
+ reversed: fromOrdinal !== void 0 && fromOrdinal > 0,
165035
+ inherited: source !== usage,
165036
+ message: fm.flowKind === "message",
165037
+ row: {
165038
+ text: this.flowRowText(member, { source: from, target: to }),
165039
+ source: sourceOf2(member, uri)
165040
+ }
165041
+ });
165042
+ }
165043
+ }
165044
+ return carried;
165045
+ }
165046
+ // REQ-402 — merge the transfers a relationship usage carries from its
165047
+ // definitions into the `flows` compartment its own body already contributes,
165048
+ // so one heading holds every transfer the usage transports.
165049
+ // REQ-402 — Flows carried by a connection or interface usage
165050
+ withCarriedFlowRows(compartments, carried) {
165051
+ const rows = carried.filter((flow) => flow.inherited).map((flow) => flow.row);
165052
+ if (!rows.length)
165053
+ return compartments;
165054
+ const out = [...compartments ?? []];
165055
+ const existing = out.findIndex((compartment) => compartment.title === "flows");
165056
+ if (existing >= 0)
165057
+ out[existing] = { ...out[existing], items: [...out[existing].items, ...rows] };
165058
+ else
165059
+ out.push({ title: "flows", items: rows });
165060
+ return out;
165061
+ }
164931
165062
  // REQ-192/360/363 — the ONE recursive Interconnection View renderer. Given the
164932
165063
  // top-level `roots` to draw, it renders each part instance and nests its
164933
165064
  // internals recursively, drilling into a usage's type DEFINITION (+
@@ -164976,7 +165107,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164976
165107
  for (const source of [part, ...this.inheritedFeatureOwnersOf(part, index2)]) {
164977
165108
  for (const member of membersOf(source)) {
164978
165109
  const relationship = member;
164979
- const emptyPrefixFlow = member.$type === "FlowStmt" && relationship.target !== void 0 && relationship.source === void 0;
165110
+ const emptyPrefixFlow = isEmptyPrefixFlow(member);
164980
165111
  const emptyPrefixInterface = isInterfaceDecl(member) && relationship.target !== void 0 && relationship.connect === void 0;
164981
165112
  const namedRelationship = isConnectorDecl(member) || isConnectionDecl(member) || isInterfaceDecl(member) && !emptyPrefixInterface || member.$type === "BindingDecl" || member.$type === "FlowStmt" && !emptyPrefixFlow;
164982
165113
  if (namedRelationship) {
@@ -165133,16 +165264,16 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165133
165264
  };
165134
165265
  const performedActions = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? effectiveMembersOf(part).filter((m) => m.$type === "PerformStmt" && !!nameOf2(m)).map((act) => {
165135
165266
  const pinSource = this.performTargetOf(act, index2) ?? act;
165136
- const pathSegments = this.isAnonymousBehaviorReference(act) ? this.featurePaths.resolveDeclarationPath(act).segments.map((segment) => segment.text) : [nameOf2(act)];
165267
+ const pathSegments2 = this.isAnonymousBehaviorReference(act) ? this.featurePaths.resolveDeclarationPath(act).segments.map((segment) => segment.text) : [nameOf2(act)];
165137
165268
  return {
165138
165269
  act,
165139
165270
  pinSource,
165140
- pathSegments,
165141
- name: pathSegments.at(-1) ?? nameOf2(act),
165271
+ pathSegments: pathSegments2,
165272
+ name: pathSegments2.at(-1) ?? nameOf2(act),
165142
165273
  key: concretePerformPath(act, pinSource)
165143
165274
  };
165144
165275
  }).filter((entry, i, list) => list.findIndex((other) => other.key === entry.key) === i) : [];
165145
- const performedActionInfos = performedActions.map(({ act, pinSource, pathSegments, name, key }) => {
165276
+ const performedActionInfos = performedActions.map(({ act, pinSource, pathSegments: pathSegments2, name, key }) => {
165146
165277
  const id2 = `${instanceId}::__perform_${key}`;
165147
165278
  const inheritedFromDefinition = !!localUsage && !isAstDescendantOrSelf(act, localUsage);
165148
165279
  const syncDeclaration2 = this.synchronizationDeclarationOf(act, index2);
@@ -165150,7 +165281,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165150
165281
  act,
165151
165282
  name,
165152
165283
  id: id2,
165153
- pathSegments,
165284
+ pathSegments: pathSegments2,
165154
165285
  pins: this.actionPinPorts(pinSource, id2, index2, uri),
165155
165286
  meta: {
165156
165287
  ...ivEditMeta(act, id2, void 0, localUsage, [...localPath, name], uri),
@@ -165334,6 +165465,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165334
165465
  // synthetic n-ary dot with that container's frame id).
165335
165466
  emitInterconnectEdges(members, resolveEnd, parts, index2, uri, nodes, edges, eRef, frameId, actionPinIds = /* @__PURE__ */ new Set(), structuralPortIds = /* @__PURE__ */ new Set(), memberOwner, memberOwnerInherited = false, endpointProvenance = /* @__PURE__ */ new Map()) {
165336
165467
  const memberList2 = [...members];
165468
+ const pendingFlows = [];
165469
+ const firstEdge = edges.length;
165337
165470
  const relationshipFields = (source) => {
165338
165471
  let directMember = source;
165339
165472
  while (directMember.$container && directMember.$container !== memberOwner) {
@@ -165488,22 +165621,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165488
165621
  }
165489
165622
  }
165490
165623
  } else if (m.$type === "FlowStmt") {
165491
- const fm = m;
165492
- if (fm.flowKind === "message")
165493
- continue;
165494
- const { source, target } = this.flowStmtEnds(fm);
165495
- const from = resolveEnd(source ?? "");
165496
- const to = resolveEnd(target ?? "");
165497
- if (from && to && supportsEndpoints("flow", [from, to])) {
165498
- edges.push({
165499
- id: `e${eRef.n++}`,
165500
- from,
165501
- to,
165502
- kind: "flow",
165503
- label: this.flowPayloadText(fm) ?? typeText(m) ?? nameOf2(m),
165504
- ...relationshipFields(m)
165505
- });
165506
- }
165624
+ if (m.isDef !== true)
165625
+ pendingFlows.push(m);
165507
165626
  } else if (m.$type === "BindStmt" || m.$type === "BindingConnectorStmt" || m.$type === "BindingDecl") {
165508
165627
  const bn = m;
165509
165628
  const leftEnd = bn.left ?? bn.source;
@@ -165591,6 +165710,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165591
165710
  }
165592
165711
  };
165593
165712
  });
165713
+ const carriedFlows = this.carriedFlowsOf(m, usageEnds, index2, uri);
165594
165714
  nodes.push({
165595
165715
  id: usageId,
165596
165716
  name: name ?? ANONYMOUS_INTERFACE_NAME,
@@ -165601,12 +165721,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165601
165721
  multiplicity: multText(m),
165602
165722
  ports,
165603
165723
  ...usageFrame ? { frame: usageFrame } : {},
165604
- compartments: this.compartmentsFor(m, uri, index2),
165724
+ compartments: this.withCarriedFlowRows(this.compartmentsFor(m, uri, index2), carriedFlows),
165605
165725
  source: usageSource,
165606
165726
  meta: {
165607
165727
  ...relationship.meta ?? {},
165608
165728
  interfaceUsage: true,
165609
- ...name ? { explicitRelationshipName: name } : {}
165729
+ ...name ? { explicitRelationshipName: name } : {},
165730
+ ...flowDecorationMeta(carriedFlows)
165610
165731
  }
165611
165732
  });
165612
165733
  for (const { end, ordinal, targetId } of visibleUsageEnds) {
@@ -165675,6 +165796,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165675
165796
  }
165676
165797
  };
165677
165798
  });
165799
+ const carriedFlows = this.carriedFlowsOf(m, usageEnds, index2, uri);
165678
165800
  nodes.push({
165679
165801
  id: usageId,
165680
165802
  name: name ?? "connection",
@@ -165684,12 +165806,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165684
165806
  type,
165685
165807
  ports,
165686
165808
  ...usageFrame ? { frame: usageFrame } : {},
165687
- compartments: this.compartmentsFor(m, uri, index2),
165809
+ compartments: this.withCarriedFlowRows(this.compartmentsFor(m, uri, index2), carriedFlows),
165688
165810
  source: usageSource,
165689
165811
  meta: {
165690
165812
  ...relationship.meta ?? {},
165691
165813
  connectionUsage: true,
165692
- ...name ? { explicitRelationshipName: name } : {}
165814
+ ...name ? { explicitRelationshipName: name } : {},
165815
+ ...flowDecorationMeta(carriedFlows)
165693
165816
  }
165694
165817
  });
165695
165818
  for (const { end, ordinal, targetId } of visibleUsageEnds) {
@@ -165717,6 +165840,92 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165717
165840
  }
165718
165841
  }
165719
165842
  }
165843
+ const resolveFlowEnd = (pathText) => {
165844
+ const direct = resolveEnd(pathText);
165845
+ if (direct)
165846
+ return { id: direct, docked: false };
165847
+ const parts2 = pathText.split(/(::|\.)/u).filter((part) => part.length > 0);
165848
+ for (let length2 = parts2.length - 2; length2 >= 1; length2 -= 2) {
165849
+ const carrier = resolveEnd(parts2.slice(0, length2).join(""));
165850
+ if (carrier)
165851
+ return { id: carrier, docked: true };
165852
+ }
165853
+ return void 0;
165854
+ };
165855
+ const transportFor = (from, to) => edges.slice(firstEdge).find((edge) => (edge.kind === "connect" || edge.kind === "interface") && (edge.from === from && edge.to === to || edge.from === to && edge.to === from));
165856
+ const flowUsageNodeId = (flow) => {
165857
+ const src = sourceOf2(flow, uri);
165858
+ const ownerId = frameId ?? (qnameOf(memberOwner ?? flow) || "iv");
165859
+ const suffix = (frameId ? flowStmtName(flow) : qnameOf(flow)) ?? (src ? `${src.range.start.line}_${src.range.start.character}` : `${eRef.n}`);
165860
+ return `${ownerId}::__flow_${suffix}`;
165861
+ };
165862
+ for (const m of pendingFlows) {
165863
+ const fm = m;
165864
+ const message = fm.flowKind === "message";
165865
+ const keyword = flowKeyword(fm);
165866
+ const { source, target } = this.flowStmtEnds(fm);
165867
+ const flowName = flowStmtName(m);
165868
+ const sourceEnd = message ? void 0 : resolveFlowEnd(source ?? "");
165869
+ const targetEnd = message ? void 0 : resolveFlowEnd(target ?? "");
165870
+ const selfDock = sourceEnd !== void 0 && sourceEnd.id === targetEnd?.id && (sourceEnd.docked || targetEnd.docked);
165871
+ const from = selfDock ? void 0 : sourceEnd?.id;
165872
+ const to = selfDock ? void 0 : targetEnd?.id;
165873
+ const transferred = flowTransferredFeature({ source, target });
165874
+ const label = this.flowPayloadText(fm) ?? typeText(m) ?? transferred ?? flowName;
165875
+ if (from && to && supportsEndpoints("flow", [from, to])) {
165876
+ const transport = transportFor(from, to);
165877
+ if (transport) {
165878
+ const decorations = Array.isArray(transport.meta?.flowDecorations) ? transport.meta.flowDecorations : [];
165879
+ transport.meta = {
165880
+ ...transport.meta ?? {},
165881
+ flowDecorations: [...decorations, {
165882
+ keyword,
165883
+ label,
165884
+ ...flowName ? { name: flowName } : {},
165885
+ reversed: transport.from === to,
165886
+ source: sourceOf2(m, uri)
165887
+ }]
165888
+ };
165889
+ continue;
165890
+ }
165891
+ edges.push({
165892
+ id: `e${eRef.n++}`,
165893
+ from,
165894
+ to,
165895
+ kind: "flow",
165896
+ label,
165897
+ ...relationshipFields(m)
165898
+ });
165899
+ continue;
165900
+ }
165901
+ if (message || !flowName)
165902
+ continue;
165903
+ const ownerNode = frameId ? nodes.find((node) => node.id === frameId) : void 0;
165904
+ const ends = [
165905
+ ...source ? [{ text: `from ${source}` }] : [],
165906
+ ...target ? [{ text: `to ${target}` }] : []
165907
+ ];
165908
+ nodes.push({
165909
+ id: flowUsageNodeId(m),
165910
+ name: flowName,
165911
+ keyword,
165912
+ isDef: false,
165913
+ shape: "box",
165914
+ type: typeText(m),
165915
+ multiplicity: multText(m),
165916
+ ...ownerNode ? { frame: ownerNode.frame } : frameId ? { frame: frameId } : {},
165917
+ compartments: [
165918
+ ...label ? [{ title: "payload", items: [{ text: label }] }] : [],
165919
+ ...ends.length ? [{ title: "ends", items: ends }] : []
165920
+ ],
165921
+ source: sourceOf2(m, uri),
165922
+ meta: {
165923
+ ...relationshipFields(m).meta ?? {},
165924
+ flowUsage: true,
165925
+ explicitRelationshipName: flowName
165926
+ }
165927
+ });
165928
+ }
165720
165929
  }
165721
165930
  // ── iv overview (REQ-360) — recursive nested Interconnection View over a
165722
165931
  // package. The hierarchy follows COMPOSITION, not text layout: the roots are
@@ -167077,9 +167286,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167077
167286
  const to = resolvePin(ends.target, occurrenceContext);
167078
167287
  if (from && to) {
167079
167288
  const kind = fm.succession === true ? "successionFlow" : "flow";
167080
- const sourceFeature = ends.source ? lastSeg(ends.source) : void 0;
167081
- const targetFeature = ends.target ? lastSeg(ends.target) : void 0;
167082
- const transferredFeature = sourceFeature === targetFeature ? sourceFeature : sourceFeature ?? targetFeature;
167289
+ const transferredFeature = flowTransferredFeature(ends);
167083
167290
  edges.push({
167084
167291
  id: `e${e++}`,
167085
167292
  from,
@@ -169777,6 +169984,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
169777
169984
  }
169778
169985
  add("connections", members.filter((member) => isConnectionDecl(member) && member.isDef !== true && isOrdinaryMember(member)).map((member) => item(member, this.featureText(member) || "(anonymous connection)")));
169779
169986
  add("interfaces", members.filter((m) => isInterfaceDecl(m) && m.isDef !== true && isOrdinaryMember(m)).map((m) => item(m, this.interfaceRowText(m))));
169987
+ add("flows", members.filter((m) => m.$type === "FlowStmt" && m.isDef !== true && isOrdinaryMember(m)).map((m) => item(m, this.flowRowText(m))));
169780
169988
  add("features", members.filter((m) => m.$type === "FeatureShorthand" || m.$type === "FeatureRedefinitionShorthand").filter(isOrdinaryMember).filter((m) => !isOccurrenceModified(m)).map((m) => item(m, canonicalFeatureText(m))));
169781
169989
  const undirected = (guard) => usages(guard);
169782
169990
  add("items", undirected(isItemDecl).filter(notPortion).map((m) => item(m)));
@@ -170210,7 +170418,9 @@ var sysmlInlayHintSettings = {
170210
170418
  modifiers: false,
170211
170419
  specialization: false,
170212
170420
  redefinition: false,
170213
- effectiveNames: false
170421
+ effectiveNames: false,
170422
+ importedNames: false,
170423
+ parameterNames: false
170214
170424
  };
170215
170425
 
170216
170426
  // ../language-server/out/src/services/category-taxonomy.js
@@ -180911,6 +181121,329 @@ var SysmlSemanticTokenProvider = class extends AbstractSemanticTokenProvider {
180911
181121
 
180912
181122
  // ../language-server/out/src/services/inlay-hint-provider.js
180913
181123
  var import_vscode_languageserver20 = __toESM(require_main4(), 1);
181124
+
181125
+ // ../language-server/out/src/services/name-lookup.js
181126
+ function unquoteName3(name) {
181127
+ return name.replace(/^'(.*)'$/u, "$1");
181128
+ }
181129
+ function pathSegments(path10) {
181130
+ const segments = [];
181131
+ let current2 = "";
181132
+ let quoted = false;
181133
+ for (let index2 = 0; index2 < path10.length; index2++) {
181134
+ const char = path10[index2];
181135
+ if (char === "'") {
181136
+ quoted = !quoted;
181137
+ current2 += char;
181138
+ continue;
181139
+ }
181140
+ if (!quoted && char === ":" && path10[index2 + 1] === ":") {
181141
+ segments.push(current2);
181142
+ current2 = "";
181143
+ index2++;
181144
+ continue;
181145
+ }
181146
+ if (!quoted && char === ".") {
181147
+ segments.push(current2);
181148
+ current2 = "";
181149
+ continue;
181150
+ }
181151
+ current2 += char;
181152
+ }
181153
+ segments.push(current2);
181154
+ return segments;
181155
+ }
181156
+ function simpleNameOf2(name) {
181157
+ return pathSegments(name).at(-1) ?? name;
181158
+ }
181159
+ var ELEMENT_SEPARATOR = "\0";
181160
+ function elementKey2(description) {
181161
+ return `${description.documentUri.toString()}${ELEMENT_SEPARATOR}${description.path}`;
181162
+ }
181163
+ function isDeclaredSpelling(description) {
181164
+ const kind = description.derivedKind;
181165
+ if (kind === "reexport" || kind === "inherited")
181166
+ return false;
181167
+ return pathSegments(description.name).length === 1;
181168
+ }
181169
+ var SysmlNameLookup = class {
181170
+ shared;
181171
+ byName;
181172
+ /** Element key → the declared spellings the index holds for that element. */
181173
+ spellings;
181174
+ constructor(shared) {
181175
+ this.shared = shared;
181176
+ this.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, () => {
181177
+ this.byName = void 0;
181178
+ this.spellings = void 0;
181179
+ });
181180
+ }
181181
+ /** Every description indexed under `name`, in either spelling of an escaped name. */
181182
+ descriptions(name) {
181183
+ return this.index().get(name) ?? [];
181184
+ }
181185
+ /**
181186
+ * The ONE element `name` names, or `undefined` when the answer is not
181187
+ * certain: no element, or more than one. `accept` narrows the candidates
181188
+ * before ambiguity is judged, so "the only CALLABLE called `f`" is a
181189
+ * decidable question even where a part shares the name.
181190
+ */
181191
+ unique(name, accept) {
181192
+ const candidates = this.descriptions(name).filter((candidate) => !accept || accept(candidate));
181193
+ let found;
181194
+ let key;
181195
+ for (const candidate of candidates) {
181196
+ const candidateKey = elementKey2(candidate);
181197
+ if (found === void 0) {
181198
+ found = candidate;
181199
+ key = candidateKey;
181200
+ continue;
181201
+ }
181202
+ if (candidateKey !== key)
181203
+ return void 0;
181204
+ }
181205
+ return found;
181206
+ }
181207
+ /**
181208
+ * Resolve a written path: the spelling the index holds verbatim first, then
181209
+ * its final segment. Both readings must name exactly one element.
181210
+ */
181211
+ uniqueForPath(path10, accept) {
181212
+ return this.unique(path10, accept) ?? this.unique(simpleNameOf2(path10), accept);
181213
+ }
181214
+ /**
181215
+ * The declared spellings of the element `description` names — its regular
181216
+ * name and its `<short>` name — minus `written`.
181217
+ */
181218
+ otherNames(description, written) {
181219
+ const all = this.spellingIndex().get(elementKey2(description)) ?? [];
181220
+ const seen = unquoteName3(written);
181221
+ return all.filter((name) => unquoteName3(name) !== seen);
181222
+ }
181223
+ index() {
181224
+ if (!this.byName)
181225
+ this.build();
181226
+ return this.byName;
181227
+ }
181228
+ spellingIndex() {
181229
+ if (!this.spellings)
181230
+ this.build();
181231
+ return this.spellings;
181232
+ }
181233
+ /** One pass over the index feeds both maps; neither is worth a second. */
181234
+ build() {
181235
+ const byName = /* @__PURE__ */ new Map();
181236
+ const spellings = /* @__PURE__ */ new Map();
181237
+ const add = (key, description) => {
181238
+ const bucket = byName.get(key);
181239
+ if (bucket)
181240
+ bucket.push(description);
181241
+ else
181242
+ byName.set(key, [description]);
181243
+ };
181244
+ for (const description of this.shared.workspace.IndexManager.allElements()) {
181245
+ add(description.name, description);
181246
+ const unquoted = unquoteName3(description.name);
181247
+ if (unquoted !== description.name)
181248
+ add(unquoted, description);
181249
+ if (!isDeclaredSpelling(description))
181250
+ continue;
181251
+ const key = elementKey2(description);
181252
+ const names = spellings.get(key);
181253
+ if (names) {
181254
+ if (!names.includes(description.name))
181255
+ names.push(description.name);
181256
+ } else {
181257
+ spellings.set(key, [description.name]);
181258
+ }
181259
+ }
181260
+ this.byName = byName;
181261
+ this.spellings = spellings;
181262
+ }
181263
+ };
181264
+ var lookups = /* @__PURE__ */ new WeakMap();
181265
+ function nameLookupFor(shared) {
181266
+ if (!shared)
181267
+ return void 0;
181268
+ const existing = lookups.get(shared);
181269
+ if (existing)
181270
+ return existing;
181271
+ const created = new SysmlNameLookup(shared);
181272
+ lookups.set(shared, created);
181273
+ return created;
181274
+ }
181275
+
181276
+ // ../language-server/out/src/services/callable-resolver.js
181277
+ var CALLABLE_TYPES = /* @__PURE__ */ new Set([
181278
+ "CalcDecl",
181279
+ "ActionDecl",
181280
+ "ConstraintDecl",
181281
+ "FunctionDecl",
181282
+ "PredicateDecl",
181283
+ "BehaviorDecl",
181284
+ "InteractionDecl"
181285
+ ]);
181286
+ var CallableResolver = class {
181287
+ services;
181288
+ lookup;
181289
+ linker;
181290
+ constructor(services) {
181291
+ this.services = services;
181292
+ this.lookup = nameLookupFor(services?.shared);
181293
+ this.linker = services?.references.Linker;
181294
+ }
181295
+ /**
181296
+ * Every indexed callable that answers to `name`, so a caller can decide what
181297
+ * ambiguity costs it. Signature help is transient and follows the cursor, so
181298
+ * it takes the first; an inlay hint is a permanent annotation, so it takes a
181299
+ * candidate only when it is the ONLY element with that name.
181300
+ */
181301
+ candidates(name) {
181302
+ const simple = simpleNameOf2(name);
181303
+ return (this.lookup?.descriptions(simple) ?? []).filter((description) => CALLABLE_TYPES.has(description.type));
181304
+ }
181305
+ /**
181306
+ * The one callable `name` unambiguously names in the index, if any: the
181307
+ * written spelling first, then its final segment, and either reading must
181308
+ * name exactly one element.
181309
+ */
181310
+ uniqueCandidate(name) {
181311
+ return this.lookup?.uniqueForPath(name, (description) => CALLABLE_TYPES.has(description.type));
181312
+ }
181313
+ /** The callable declaration `name` invokes in `document`, resolved locally. */
181314
+ findLocal(document2, name) {
181315
+ const root4 = document2.parseResult?.value;
181316
+ if (!root4)
181317
+ return void 0;
181318
+ return collectCallables([root4, ...ast_utils_exports.streamAllContents(root4).toArray()]).get(unquoteName3(simpleNameOf2(name)));
181319
+ }
181320
+ /**
181321
+ * SYNCHRONOUS resolution: a qualified spelling the index holds verbatim,
181322
+ * then the document's own tree, then an unambiguous indexed callable —
181323
+ * resolved through the linker's off-to-the-side library parse. Nothing here
181324
+ * grows `LangiumDocuments`, and nothing here awaits, so it is safe on a
181325
+ * request VS Code re-issues on every scroll.
181326
+ */
181327
+ resolve(document2, name, local) {
181328
+ if (name.includes("::") || name.includes(".")) {
181329
+ const exact = this.lookup?.unique(name, (description) => CALLABLE_TYPES.has(description.type));
181330
+ const node = exact ? this.nodeOf(exact) : void 0;
181331
+ if (node)
181332
+ return node;
181333
+ }
181334
+ const found = local ? local(unquoteName3(simpleNameOf2(name))) : this.findLocal(document2, name);
181335
+ if (found)
181336
+ return found;
181337
+ const candidate = this.uniqueCandidate(name);
181338
+ return candidate ? this.nodeOf(candidate) : void 0;
181339
+ }
181340
+ /** An indexed description as a node, through the linker's lazy library parse. */
181341
+ nodeOf(description) {
181342
+ return this.linker?.resolveIndexedNode?.(description) ?? description.node;
181343
+ }
181344
+ /**
181345
+ * Resolution that may load a workspace document. Signature help uses it: it
181346
+ * runs on an explicit editor gesture, not on every scroll, and a callable in
181347
+ * a workspace file the user has not opened yet still deserves a signature.
181348
+ */
181349
+ // REQ-263 — Signature help on `(` / `,` / `->`
181350
+ async resolveAsync(document2, name) {
181351
+ const direct = this.resolve(document2, name);
181352
+ if (direct)
181353
+ return direct;
181354
+ const candidate = this.candidates(name).at(0);
181355
+ if (!candidate)
181356
+ return void 0;
181357
+ if (candidate.node)
181358
+ return candidate.node;
181359
+ const documents = this.services?.shared.workspace.LangiumDocuments;
181360
+ const locator = this.services?.workspace.AstNodeLocator;
181361
+ if (!documents || !locator)
181362
+ return void 0;
181363
+ const doc = documents.getDocument(candidate.documentUri) ?? await documents.getOrCreateDocument(candidate.documentUri);
181364
+ const root4 = doc?.parseResult?.value;
181365
+ return root4 ? locator.getAstNode(root4, candidate.path) : void 0;
181366
+ }
181367
+ /** {@link callableParameters} — the parameters, in written order. */
181368
+ parameters(node) {
181369
+ return callableParameters(node);
181370
+ }
181371
+ /**
181372
+ * The parameters a positional argument list binds to: the INPUTS, in written
181373
+ * order. An `out` parameter and the result are not written at the call site
181374
+ * (OMG KerML v1.0 binds an `InvocationExpression`'s arguments to the
181375
+ * invoked type's input features).
181376
+ */
181377
+ inputParameters(node) {
181378
+ return this.parameters(node).filter((parameter) => !parameter.isReturn && parameter.direction !== "out");
181379
+ }
181380
+ };
181381
+ function callableParameters(node) {
181382
+ const directed = new Map(directedParameters(node).map((entry) => [entry.node, entry.direction]));
181383
+ const result = [];
181384
+ for (const member of membersOf2(node)) {
181385
+ const direction = directed.get(member);
181386
+ const isReturn = member.$type === "ReturnDecl";
181387
+ if (!direction && !isReturn)
181388
+ continue;
181389
+ const name = nodeName2(member);
181390
+ if (!name && !direction)
181391
+ continue;
181392
+ result.push({
181393
+ node: member,
181394
+ direction: direction ?? "return",
181395
+ name,
181396
+ type: typingText(member),
181397
+ multiplicity: multiplicityText2(member),
181398
+ isReturn
181399
+ });
181400
+ }
181401
+ return result;
181402
+ }
181403
+ function collectCallables(nodes) {
181404
+ const callables = /* @__PURE__ */ new Map();
181405
+ const byDefinition = /* @__PURE__ */ new Set();
181406
+ for (const node of nodes) {
181407
+ if (!CALLABLE_TYPES.has(node.$type) && callableParameters(node).length === 0)
181408
+ continue;
181409
+ const isDefinition = node.isDef === true;
181410
+ for (const name of [nodeName2(node), shortNameOf2(node)]) {
181411
+ if (!name)
181412
+ continue;
181413
+ if (callables.has(name) && !(isDefinition && !byDefinition.has(name)))
181414
+ continue;
181415
+ callables.set(name, node);
181416
+ if (isDefinition)
181417
+ byDefinition.add(name);
181418
+ }
181419
+ }
181420
+ return callables;
181421
+ }
181422
+ function membersOf2(node) {
181423
+ const members = node?.members;
181424
+ return Array.isArray(members) ? members.filter(isAstNode3) : [];
181425
+ }
181426
+ function isAstNode3(value) {
181427
+ return typeof value === "object" && value !== null && typeof value.$type === "string";
181428
+ }
181429
+ function nodeName2(node) {
181430
+ const value = node?.name;
181431
+ return typeof value === "string" && value.length > 0 ? unquoteName3(value) : void 0;
181432
+ }
181433
+ function shortNameOf2(node) {
181434
+ const value = node?.shortName?.name;
181435
+ return typeof value === "string" && value.length > 0 ? unquoteName3(value) : void 0;
181436
+ }
181437
+ function typingText(node) {
181438
+ const text = node?.typing?.type?.$refText;
181439
+ return typeof text === "string" && text.length > 0 ? text : void 0;
181440
+ }
181441
+ function multiplicityText2(node) {
181442
+ const text = node?.multiplicity?.$cstNode?.text;
181443
+ return typeof text === "string" && text.length > 0 ? text.trim() : void 0;
181444
+ }
181445
+
181446
+ // ../language-server/out/src/services/inlay-hint-provider.js
180914
181447
  function multiplicityAnchor(node) {
180915
181448
  const leaves = cst_utils_exports.flattenCst(node).toArray();
180916
181449
  const bodyStart = leaves.find((leaf) => leaf.text === "{");
@@ -180969,6 +181502,50 @@ var EXPLICIT_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
180969
181502
  "conjugates"
180970
181503
  ]);
180971
181504
  var REDEFINITION_KINDS2 = /* @__PURE__ */ new Set([":>>", "redefines"]);
181505
+ var CONSTANT_CARRYING_KINDS = /* @__PURE__ */ new Set([
181506
+ ":>",
181507
+ "subsets",
181508
+ ":>>",
181509
+ "redefines",
181510
+ "::>",
181511
+ "references",
181512
+ "=>",
181513
+ "crosses"
181514
+ ]);
181515
+ var CONSTANT_MODIFIERS = /* @__PURE__ */ new Set(["constant", "const"]);
181516
+ function constantKeyword(languageId) {
181517
+ return languageId === "kerml" ? "const" : "constant";
181518
+ }
181519
+ var PREFIXES_BEFORE_CONSTANT = /* @__PURE__ */ new Set([
181520
+ "public",
181521
+ "private",
181522
+ "protected",
181523
+ "in",
181524
+ "out",
181525
+ "inout",
181526
+ "abstract",
181527
+ "variation",
181528
+ "variant",
181529
+ "derived",
181530
+ "readonly",
181531
+ "composite",
181532
+ "portion",
181533
+ "ordered",
181534
+ "nonunique",
181535
+ "parallel"
181536
+ ]);
181537
+ var PORTION_MODIFIERS = /* @__PURE__ */ new Set(["portion", "snapshot", "timeslice"]);
181538
+ var ACTION_KIND_USAGES = /* @__PURE__ */ new Set([
181539
+ "ActionDecl",
181540
+ "StateDecl",
181541
+ "CalcDecl",
181542
+ "CaseDecl",
181543
+ "UseCaseDecl",
181544
+ "AnalysisCaseDecl",
181545
+ "VerificationCaseDecl"
181546
+ ]);
181547
+ var KERML_FEATURE_DECLS = /* @__PURE__ */ new Set(["FeatureDecl", "StepDecl", "ExpressionDecl"]);
181548
+ var REFERENCE_MODIFIERS = /* @__PURE__ */ new Set(["ref", "in", "out", "inout"]);
180972
181549
  var IMPLICIT_BASES = Object.freeze({
180973
181550
  PartDecl: { def: "Parts::Part", usage: "Parts::parts" },
180974
181551
  ItemDecl: { def: "Items::Item", usage: "Items::items" },
@@ -180993,6 +181570,37 @@ var IMPLICIT_BASES = Object.freeze({
180993
181570
  OccurrenceDecl: { def: "Occurrences::Occurrence", usage: "Occurrences::occurrences" },
180994
181571
  MetadataDecl: { def: "Metadata::MetadataItem", usage: "Metadata::metadataItems" }
180995
181572
  });
181573
+ var OCCURRENCE_KIND_OWNERS = /* @__PURE__ */ new Set([
181574
+ ...Object.keys(IMPLICIT_BASES).filter((kind) => kind !== "AttributeDecl"),
181575
+ // KerML fixes a library base per classifier kind too, in the same way and in
181576
+ // the same normative place: `Class` specializes `Occurrences::Occurrence`,
181577
+ // `Structure` specializes `Objects::Object`, `Metaclass` specializes
181578
+ // `Metaobjects::Metaobject`, `Behavior` specializes
181579
+ // `Performances::Performance`, `Function` and `Interaction` are Behaviors,
181580
+ // `Predicate` is a Function, `Step` specializes `Performances::performances`
181581
+ // and `Expression` is a Step. All of them reach `Occurrence`.
181582
+ "ClassDecl",
181583
+ "StructDecl",
181584
+ "MetaclassDecl",
181585
+ "BehaviorDecl",
181586
+ "InteractionDecl",
181587
+ "FunctionDecl",
181588
+ "PredicateDecl",
181589
+ "StepDecl",
181590
+ "ExpressionDecl"
181591
+ // Left out because their base is NOT an occurrence: `DatatypeDecl`
181592
+ // (`Base::DataValue`, declared disjoint from `Occurrence`) and plain
181593
+ // `AssociationDecl` (`Links::Link`, which specializes `Base::Anything`) —
181594
+ // `assoc struct` is handled separately, since it is a `LinkObject`.
181595
+ // Left out because their base is WRITTEN rather than fixed by the kind:
181596
+ // `TypeDecl`, `ClassifierDecl`, `FeatureDecl`.
181597
+ ]);
181598
+ function isOccurrenceKindOwner(owner) {
181599
+ if (owner.$type === "AssociationDecl")
181600
+ return owner.isStruct === true;
181601
+ return OCCURRENCE_KIND_OWNERS.has(owner.$type);
181602
+ }
181603
+ var NON_VARYING_LIBRARY_TYPES = /* @__PURE__ */ new Set(["SelfLink", "HappensLink"]);
180996
181604
  function markdown(value) {
180997
181605
  return { kind: import_vscode_languageserver20.MarkupKind.Markdown, value };
180998
181606
  }
@@ -181014,19 +181622,130 @@ function descriptionRange(description) {
181014
181622
  const segment = description.nameSegment ?? description.selectionSegment;
181015
181623
  return segment?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
181016
181624
  }
181625
+ function descriptionLocation(description) {
181626
+ return { uri: description.documentUri.toString(), range: descriptionRange(description) };
181627
+ }
181628
+ function nodeLocation(node) {
181629
+ const range = node.$cstNode?.range;
181630
+ if (!range)
181631
+ return void 0;
181632
+ return { uri: ast_utils_exports.getDocument(node).uri.toString(), range };
181633
+ }
181634
+ function specializesNonVaryingLibraryType(node) {
181635
+ const typing = node.typing?.type?.$refText;
181636
+ if (typing && NON_VARYING_LIBRARY_TYPES.has(lastSegment3(typing)))
181637
+ return true;
181638
+ return allRelationships2(node).flatMap((rel2) => rel2.targets ?? []).some((target) => NON_VARYING_LIBRARY_TYPES.has(lastSegment3(target)));
181639
+ }
181640
+ function constantAnchor(node) {
181641
+ const cst = node.$cstNode;
181642
+ if (!cst)
181643
+ return void 0;
181644
+ for (const leaf of cst_utils_exports.flattenCst(cst)) {
181645
+ if (leaf.hidden)
181646
+ continue;
181647
+ if (PREFIXES_BEFORE_CONSTANT.has(leaf.text))
181648
+ continue;
181649
+ return /^[a-z]+$/u.test(leaf.text) ? leaf : void 0;
181650
+ }
181651
+ return void 0;
181652
+ }
181653
+ function membershipImportPath(node) {
181654
+ if (isImport(node) && node.alias)
181655
+ return void 0;
181656
+ if (isExposePath(node) && node.dotSegs.length > 0)
181657
+ return void 0;
181658
+ if (node.segs.some((seg) => seg.star || seg.recursive || !seg.name))
181659
+ return void 0;
181660
+ const last2 = node.segs.at(-1);
181661
+ const written = last2?.name ?? node.head;
181662
+ const anchor = last2 ? grammar_utils_exports.findNodeForProperty(last2.$cstNode, "name") : grammar_utils_exports.findNodeForProperty(node.$cstNode, "head");
181663
+ if (!anchor)
181664
+ return void 0;
181665
+ return { path: [node.head, ...node.segs.map((seg) => seg.name)].join("::"), written, anchor };
181666
+ }
181667
+ function publicFeatures(type) {
181668
+ const members = type.members;
181669
+ if (!Array.isArray(members))
181670
+ return [];
181671
+ return members.filter((member) => {
181672
+ if (typeof member?.$type !== "string")
181673
+ return false;
181674
+ const decl = member;
181675
+ if (decl.isDef !== false && decl.$type !== "FeatureDecl")
181676
+ return false;
181677
+ return decl.visibility === void 0 || decl.visibility === "public";
181678
+ });
181679
+ }
181680
+ function calleeName(node) {
181681
+ if (node.call) {
181682
+ if (isPathExpr(node.target))
181683
+ return node.target.path;
181684
+ const target = node.target;
181685
+ if (isPostfixOp(target) && target.dot && target.field)
181686
+ return target.field;
181687
+ return void 0;
181688
+ }
181689
+ if (node.arrow && node.invoke)
181690
+ return node.invoke;
181691
+ return void 0;
181692
+ }
181693
+ function callArguments(node) {
181694
+ if (node.call)
181695
+ return { args: node.callArgs, firstParameter: 0 };
181696
+ if (node.arrow && node.invoke)
181697
+ return { args: node.args, firstParameter: 1 };
181698
+ return void 0;
181699
+ }
181017
181700
  var SysmlInlayHintProvider = class {
181018
181701
  services;
181019
181702
  /**
181020
181703
  * Resolved implicit bases, keyed by the qualified name in
181021
- * {@link IMPLICIT_BASES}. The standard library does not change once it is
181022
- * loaded, so this is built at most once for real; while it is still empty —
181023
- * before the library has been indexed the rebuild is over a tiny index and
181024
- * costs nothing, which is what keeps the hint path free of a full index scan
181025
- * on every request.
181704
+ * {@link IMPLICIT_BASES}. A base the index does not hold is NOT cached — the
181705
+ * library may still be loading and issue #240 made that miss cheap: two
181706
+ * lookups in the shared name index rather than the full index scan it used
181707
+ * to be, on a request VS Code re-issues on every scroll. A HIT is cached
181708
+ * until the index is rebuilt, so the description a label links to always
181709
+ * carries the ranges of the generation it was read from.
181026
181710
  */
181027
181711
  baseCache = /* @__PURE__ */ new Map();
181712
+ /**
181713
+ * REQ-395 — issue #240: the shared name lookup, built once per index
181714
+ * generation. Every category that resolves a WRITTEN name goes through it,
181715
+ * so no request puts an index scan on the hint path.
181716
+ */
181717
+ lookup;
181718
+ /** REQ-395 — issue #240: the callee resolution signature help also uses. */
181719
+ callables;
181720
+ /** REQ-395 — issue #240: the linker's lazy standard-library parse. */
181721
+ linker;
181722
+ /**
181723
+ * REQ-395 — issue #240: "does this element declare `constant`?", memoized by
181724
+ * element identity, because answering it may cost one lazily parsed library
181725
+ * file. Editing away the `constant` on the SUBSETTED feature changes the
181726
+ * answer without changing the key, so this is dropped whenever the index is
181727
+ * rebuilt, exactly like the shared name lookup.
181728
+ */
181729
+ constantCache = /* @__PURE__ */ new Map();
181730
+ /**
181731
+ * REQ-395 — issue #240: an element's own written names, memoized by element
181732
+ * identity. Reading them tells a `<short>` name from a regular one, which is
181733
+ * what lets the imported-name hint print the notation the source would have
181734
+ * written. It costs at most one lazily parsed library file, only for an
181735
+ * element that actually has two names, and it is dropped with the rest when
181736
+ * the index is rebuilt.
181737
+ */
181738
+ declaredNameCache = /* @__PURE__ */ new Map();
181028
181739
  constructor(services) {
181029
181740
  this.services = services;
181741
+ this.lookup = nameLookupFor(services?.shared);
181742
+ this.callables = new CallableResolver(services);
181743
+ this.linker = services?.references.Linker;
181744
+ services?.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, () => {
181745
+ this.baseCache.clear();
181746
+ this.constantCache.clear();
181747
+ this.declaredNameCache.clear();
181748
+ });
181030
181749
  }
181031
181750
  getInlayHints(document2, _params) {
181032
181751
  const root4 = document2.parseResult?.value;
@@ -181034,7 +181753,14 @@ var SysmlInlayHintProvider = class {
181034
181753
  return void 0;
181035
181754
  const settings = sysmlInlayHintSettings;
181036
181755
  const hints = [];
181037
- for (const node of allAstNodes(root4)) {
181756
+ const nodes = allAstNodes(root4);
181757
+ let localCallables;
181758
+ const localCallable = (simple) => {
181759
+ localCallables ??= collectCallables(nodes);
181760
+ return localCallables.get(simple);
181761
+ };
181762
+ const callees = /* @__PURE__ */ new Map();
181763
+ for (const node of nodes) {
181038
181764
  const decl = node;
181039
181765
  if (settings.multiplicity && (isPartDecl(node) || isPortDecl(node) || isAttributeDecl(node)) && !decl.isDef && !decl.multiplicity && node.$cstNode && decl.name) {
181040
181766
  hints.push({
@@ -181061,6 +181787,11 @@ var SysmlInlayHintProvider = class {
181061
181787
  });
181062
181788
  }
181063
181789
  }
181790
+ if (settings.modifiers) {
181791
+ const constant2 = this.implicitConstantHint(decl);
181792
+ if (constant2)
181793
+ hints.push(constant2);
181794
+ }
181064
181795
  if (settings.modifiers && isAttributeDecl(node) && !decl.isDef && !(decl.modifiers ?? []).includes("ref")) {
181065
181796
  const keyword = keywordLeaf(node, "attribute");
181066
181797
  if (keyword) {
@@ -181102,6 +181833,17 @@ var SysmlInlayHintProvider = class {
181102
181833
  if (effective)
181103
181834
  hints.push(effective);
181104
181835
  }
181836
+ if (settings.importedNames && (isImport(node) || isExposePath(node))) {
181837
+ const imported = this.importedNameHint(node);
181838
+ if (imported)
181839
+ hints.push(imported);
181840
+ }
181841
+ if (settings.parameterNames && isPostfixOp(node)) {
181842
+ hints.push(...this.argumentNameHints(node, document2, callees, localCallable));
181843
+ }
181844
+ if (settings.parameterNames && isNewExpr(node)) {
181845
+ hints.push(...this.constructorArgumentHints(node));
181846
+ }
181105
181847
  if (!settings.dimension)
181106
181848
  continue;
181107
181849
  if (isNumericPrimary(node)) {
@@ -181171,6 +181913,228 @@ var SysmlInlayHintProvider = class {
181171
181913
  }
181172
181914
  return hints;
181173
181915
  }
181916
+ /**
181917
+ * REQ-395 — issue #240: the other name a membership `import` brings in.
181918
+ *
181919
+ * An element may declare both a regular name and a `<short>` name, and an
181920
+ * import brings in BOTH — the one the path does not write is invisible in
181921
+ * the source, which is exactly what the hint is for. The written path is a
181922
+ * datatype string, not a cross-reference, so the element is found through
181923
+ * the shared name lookup: the spelling the index holds verbatim first, then
181924
+ * the final segment, and either reading must name exactly ONE element. A
181925
+ * name two packages both declare yields nothing rather than whichever the
181926
+ * index lists first.
181927
+ */
181928
+ importedNameHint(node) {
181929
+ const written = membershipImportPath(node);
181930
+ if (!written || !this.lookup)
181931
+ return void 0;
181932
+ const description = this.lookup.uniqueForPath(written.path);
181933
+ if (!description)
181934
+ return void 0;
181935
+ const others = this.lookup.otherNames(description, written.written);
181936
+ if (others.length === 0)
181937
+ return void 0;
181938
+ const declared = this.declaredNames(description);
181939
+ const shown = others.map((name) => declared?.shortName === name ? `<${name}>` : name);
181940
+ const kind = isImport(node) ? "import" : "expose";
181941
+ return {
181942
+ position: written.anchor.range.end,
181943
+ label: [{
181944
+ value: ` also ${shown.join(", ")}`,
181945
+ location: descriptionLocation(description)
181946
+ }],
181947
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
181948
+ paddingLeft: true,
181949
+ tooltip: markdown(`This \`${kind}\` also brings \`${shown.join("`, `")}\` into scope: an element is addressable by its regular name and by its short name, and a membership import carries both.`)
181950
+ };
181951
+ }
181952
+ /**
181953
+ * REQ-395 — issue #240: the names an indexed element declares for itself.
181954
+ *
181955
+ * The index records the spellings but not which of them is the `<short>`
181956
+ * one, so the declaration is resolved through the linker's off-to-the-side
181957
+ * library parse — the budget REQ-251 sets, spent only for an element that
181958
+ * has more than one name, and memoized until the index is rebuilt.
181959
+ */
181960
+ declaredNames(description) {
181961
+ const key = elementKey2(description);
181962
+ const cached = this.declaredNameCache.get(key);
181963
+ if (cached)
181964
+ return cached;
181965
+ const node = this.linker?.resolveIndexedNode?.(description) ?? description.node;
181966
+ if (!node)
181967
+ return void 0;
181968
+ const decl = node;
181969
+ const names = { name: decl.name, shortName: decl.shortName?.name };
181970
+ this.declaredNameCache.set(key, names);
181971
+ return names;
181972
+ }
181973
+ /**
181974
+ * REQ-395 — issue #240: the feature each `new Type(…)` argument binds to.
181975
+ *
181976
+ * A `ConstructorExpression` binds its arguments to the PUBLIC features of
181977
+ * the instantiated type, in order (OMG KerML v1.0, `ConstructorExpression`)
181978
+ * — not to input parameters, which is what an `InvocationExpression` binds.
181979
+ * The instantiated type IS a cross-reference here, so there is nothing to
181980
+ * resolve by name: an unresolved one simply yields no hints.
181981
+ */
181982
+ constructorArgumentHints(node) {
181983
+ if (node.args.length === 0)
181984
+ return [];
181985
+ if (node.args.every((argument) => argument.name !== void 0))
181986
+ return [];
181987
+ const instantiated = node.type?.ref;
181988
+ if (!instantiated)
181989
+ return [];
181990
+ const features = publicFeatures(instantiated);
181991
+ if (features.length === 0)
181992
+ return [];
181993
+ const hints = [];
181994
+ for (let index2 = 0; index2 < node.args.length; index2++) {
181995
+ const argument = node.args[index2];
181996
+ if (argument.name || !argument.$cstNode)
181997
+ continue;
181998
+ const feature = features[index2];
181999
+ const name = feature?.name;
182000
+ if (!feature || !name)
182001
+ continue;
182002
+ const location = nodeLocation(feature);
182003
+ hints.push({
182004
+ position: argument.$cstNode.range.start,
182005
+ label: [{ value: `${name} =`, ...location ? { location } : {} }],
182006
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
182007
+ paddingRight: true,
182008
+ tooltip: markdown(`Argument ${index2 + 1} binds to \`${name}\` of \`${node.type.$refText}\`.`)
182009
+ });
182010
+ }
182011
+ return hints;
182012
+ }
182013
+ /**
182014
+ * REQ-395 — issue #240: the parameter a POSITIONAL invocation argument binds
182015
+ * to.
182016
+ *
182017
+ * The callee of a call is an expression rather than a cross-reference, so
182018
+ * the name is resolved through the shared {@link CallableResolver} — the
182019
+ * same one signature help uses, which is why naming these arguments does not
182020
+ * add a second resolution path. An argument that writes its own name has
182021
+ * stated the correspondence, so it gets no hint.
182022
+ */
182023
+ argumentNameHints(node, document2, callees, localCallable) {
182024
+ const call = callArguments(node);
182025
+ const callee = calleeName(node);
182026
+ if (!call || !callee || call.args.length === 0)
182027
+ return [];
182028
+ if (call.args.every((argument) => argument.argName !== void 0))
182029
+ return [];
182030
+ let callable = callees.get(callee);
182031
+ if (callable === void 0 && !callees.has(callee)) {
182032
+ callable = this.callables.resolve(document2, callee, localCallable);
182033
+ callees.set(callee, callable);
182034
+ }
182035
+ if (!callable)
182036
+ return [];
182037
+ const parameters = this.callables.inputParameters(callable);
182038
+ if (parameters.length === 0)
182039
+ return [];
182040
+ const hints = [];
182041
+ for (let index2 = 0; index2 < call.args.length; index2++) {
182042
+ const argument = call.args[index2];
182043
+ if (argument.argName || !argument.$cstNode)
182044
+ continue;
182045
+ const parameter = parameters[index2 + call.firstParameter];
182046
+ if (!parameter?.name)
182047
+ continue;
182048
+ const location = nodeLocation(parameter.node);
182049
+ hints.push({
182050
+ position: argument.$cstNode.range.start,
182051
+ label: [{ value: `${parameter.name} =`, ...location ? { location } : {} }],
182052
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
182053
+ paddingRight: true,
182054
+ tooltip: markdown(`Argument ${index2 + 1} binds to \`${parameter.direction} ${parameter.name}\` of \`${callee}\`.`)
182055
+ });
182056
+ }
182057
+ return hints;
182058
+ }
182059
+ /**
182060
+ * REQ-395 — issue #240: implicit `constant`.
182061
+ *
182062
+ * OMG KerML v1.0 constrains `Subsetting`: `subsettedFeature.isConstant and
182063
+ * subsettingFeature.isVariable implies subsettingFeature.isConstant`. So a
182064
+ * usage that subsets or redefines a feature declared `constant` IS constant,
182065
+ * without writing it — provided it can vary at all, because `Feature` also
182066
+ * constrains `isConstant implies isVariable`.
182067
+ *
182068
+ * Whether it can vary is `Usage::mayTimeVary`, which OMG SysML v2 Part 1
182069
+ * derives as "owned by a type that specializes `Occurrences::Occurrence`,
182070
+ * and not a portion, a self/happens link, or a composite action". All four
182071
+ * are decided here from the declaration kinds and what the source writes
182072
+ * ({@link isOccurrenceKindOwner}, {@link PORTION_MODIFIERS},
182073
+ * {@link ACTION_KIND_USAGES} with {@link REFERENCE_MODIFIERS}, and
182074
+ * {@link specializesNonVaryingLibraryType}); an owner whose base is written
182075
+ * rather than fixed by its kind leaves the category quiet, as an
182076
+ * undecidable model must.
182077
+ */
182078
+ implicitConstantHint(node) {
182079
+ const isFeature = node.isDef === false || KERML_FEATURE_DECLS.has(node.$type);
182080
+ if (!isFeature)
182081
+ return void 0;
182082
+ const modifiers2 = node.modifiers ?? [];
182083
+ if (modifiers2.some((modifier) => CONSTANT_MODIFIERS.has(modifier)))
182084
+ return void 0;
182085
+ if (modifiers2.some((modifier) => PORTION_MODIFIERS.has(modifier)))
182086
+ return void 0;
182087
+ if (ACTION_KIND_USAGES.has(node.$type) && !modifiers2.some((modifier) => REFERENCE_MODIFIERS.has(modifier)))
182088
+ return void 0;
182089
+ const owner = node.$container;
182090
+ if (!owner || !isOccurrenceKindOwner(owner))
182091
+ return void 0;
182092
+ if (specializesNonVaryingLibraryType(node))
182093
+ return void 0;
182094
+ const carrier = allRelationships2(node).filter((rel2) => rel2.kind && CONSTANT_CARRYING_KINDS.has(rel2.kind)).flatMap((rel2) => rel2.targets ?? []).find((target) => this.isConstantFeature(target));
182095
+ if (!carrier)
182096
+ return void 0;
182097
+ const anchor = constantAnchor(node);
182098
+ const at = anchor?.range.start ?? node.$cstNode?.range.start;
182099
+ if (!at)
182100
+ return void 0;
182101
+ const keyword = constantKeyword(this.services?.LanguageMetaData.languageId);
182102
+ return {
182103
+ position: at,
182104
+ label: keyword,
182105
+ kind: import_vscode_languageserver20.InlayHintKind.Type,
182106
+ paddingRight: true,
182107
+ tooltip: markdown(`Implicitly \`${keyword}\`: this feature subsets \`${lastSegment3(carrier)}\`, which is declared constant, and a subsetting feature that may vary takes the constancy of what it subsets.`),
182108
+ // Writing the modifier is mechanically safe — it is a leading
182109
+ // declaration prefix, and OMG puts it exactly here, before `ref`
182110
+ // and the kind keyword.
182111
+ ...anchor ? { textEdits: [{ range: { start: at, end: at }, newText: `${keyword} ` }] } : {}
182112
+ };
182113
+ }
182114
+ /**
182115
+ * REQ-395 — issue #240: does the feature this path names declare `constant`?
182116
+ *
182117
+ * The path must name exactly one element, and that element is resolved
182118
+ * through the linker's off-to-the-side library parse — one lazily parsed
182119
+ * indexed target, the budget REQ-251 sets, and never a document added to the
182120
+ * workspace. The answer is memoized, so a scroll does not repeat it.
182121
+ */
182122
+ isConstantFeature(target) {
182123
+ if (!this.lookup)
182124
+ return false;
182125
+ const description = this.lookup.uniqueForPath(target);
182126
+ if (!description)
182127
+ return false;
182128
+ const key = elementKey2(description);
182129
+ const cached = this.constantCache.get(key);
182130
+ if (cached !== void 0)
182131
+ return cached;
182132
+ const node = this.linker?.resolveIndexedNode?.(description) ?? description.node;
182133
+ const modifiers2 = node?.modifiers ?? [];
182134
+ const answer = modifiers2.some((modifier) => CONSTANT_MODIFIERS.has(modifier));
182135
+ this.constantCache.set(key, answer);
182136
+ return answer;
182137
+ }
181174
182138
  /**
181175
182139
  * REQ-395 — Find one implicit base in the index.
181176
182140
  *
@@ -181184,25 +182148,19 @@ var SysmlInlayHintProvider = class {
181184
182148
  const cached = this.baseCache.get(qualified);
181185
182149
  if (cached)
181186
182150
  return cached;
181187
- const index2 = this.services?.shared.workspace.IndexManager;
181188
- if (!index2)
182151
+ if (!this.lookup)
181189
182152
  return void 0;
181190
- const simple = lastSegment3(qualified);
181191
- let fallback;
181192
- for (const description of index2.allElements()) {
181193
- if (description.name === qualified) {
181194
- this.baseCache.set(qualified, description);
181195
- return description;
181196
- }
181197
- if (fallback || description.name !== simple)
181198
- continue;
182153
+ const exact = this.lookup.descriptions(qualified).at(0);
182154
+ if (exact) {
182155
+ this.baseCache.set(qualified, exact);
182156
+ return exact;
182157
+ }
182158
+ const fallback = this.lookup.descriptions(lastSegment3(qualified)).find((description) => {
181199
182159
  const uri = description.documentUri instanceof URI2 ? description.documentUri : URI2.parse(String(description.documentUri));
181200
182160
  if (!isStandardLibraryUri(uri))
181201
- continue;
181202
- if (description.isUsage === true !== wantUsage)
181203
- continue;
181204
- fallback = description;
181205
- }
182161
+ return false;
182162
+ return description.isUsage === true === wantUsage;
182163
+ });
181206
182164
  if (fallback)
181207
182165
  this.baseCache.set(qualified, fallback);
181208
182166
  return fallback;
@@ -183835,7 +184793,7 @@ function compartmentWidth(node) {
183835
184793
  }
183836
184794
  function nodeForCanvas(node) {
183837
184795
  if (node.meta?.connectionUsage === true || node.meta?.interfaceUsage === true) {
183838
- const compartments2 = node.compartments?.filter((compartment) => compartment.title === "doc");
184796
+ const compartments2 = node.compartments?.filter((compartment) => compartment.title === "doc" || compartment.title === "flows");
183839
184797
  return {
183840
184798
  ...node,
183841
184799
  compartments: compartments2?.length ? compartments2 : void 0
@@ -184367,7 +185325,8 @@ function hideExplicitRelationshipBoxes(model, relationship) {
184367
185325
  bindings: explicitRelationshipBindings(usage, model),
184368
185326
  endCount: usage.ports?.length ?? 0
184369
185327
  }));
184370
- const retainedIds = new Set(projections.filter(({ usage, bindings, endCount }) => (nodeForCanvas(usage).compartments?.length ?? 0) > 0 || annotatedUsageIds.has(usage.id) || endCount < 2 || bindings.length !== endCount).map(({ usage }) => usage.id));
185328
+ const blocksCompaction = ({ usage, endCount }) => (nodeForCanvas(usage).compartments ?? []).some((compartment) => endCount !== 2 || compartment.title !== "flows");
185329
+ const retainedIds = new Set(projections.filter((projection2) => blocksCompaction(projection2) || annotatedUsageIds.has(projection2.usage.id) || projection2.endCount < 2 || projection2.bindings.length !== projection2.endCount).map(({ usage }) => usage.id));
184371
185330
  const hiddenIds = /* @__PURE__ */ new Set();
184372
185331
  for (const { usage } of projections) {
184373
185332
  if (retainedIds.has(usage.id)) continue;
@@ -184424,6 +185383,8 @@ function hideExplicitRelationshipBoxes(model, relationship) {
184424
185383
  });
184425
185384
  continue;
184426
185385
  }
185386
+ const hubMeta = { ...baseMeta };
185387
+ delete hubMeta.flowDecorations;
184427
185388
  const hubId = `${usage.id}::__compact_hub`;
184428
185389
  compactNodes.push({
184429
185390
  id: hubId,
@@ -184434,7 +185395,7 @@ function hideExplicitRelationshipBoxes(model, relationship) {
184434
185395
  ...usage.frame ? { frame: usage.frame } : {},
184435
185396
  source: usage.source,
184436
185397
  meta: {
184437
- ...baseMeta,
185398
+ ...hubMeta,
184438
185399
  naryConnectionHub: true,
184439
185400
  ...relationship === "connection" ? { compactConnectionUsageHub: true } : { compactInterfaceUsageHub: true }
184440
185401
  }
@@ -184454,7 +185415,7 @@ function hideExplicitRelationshipBoxes(model, relationship) {
184454
185415
  endAdornmentTo: binding.adornment,
184455
185416
  source: usage.source ?? binding.edge.source,
184456
185417
  meta: {
184457
- ...baseMeta,
185418
+ ...hubMeta,
184458
185419
  ...relationship === "connection" ? { compactConnectionUsageEnd: true } : { compactInterfaceUsageEnd: true },
184459
185420
  connectionEndOrdinal: binding.ordinal,
184460
185421
  ...binding.role ? { connectionEndRole: binding.role } : {}
@@ -198165,6 +199126,45 @@ function successionFlowLabelPoint(centre, mark) {
198165
199126
  y: markCoordinate(centre.y - mark.normal.y * 16)
198166
199127
  };
198167
199128
  }
199129
+ function flowDecorationsOf(edge) {
199130
+ const raw = edge.meta?.flowDecorations;
199131
+ if (!Array.isArray(raw)) return [];
199132
+ return raw.flatMap((entry) => {
199133
+ if (!entry || typeof entry !== "object") return [];
199134
+ const decoration = entry;
199135
+ return [{
199136
+ keyword: typeof decoration.keyword === "string" ? decoration.keyword : "flow",
199137
+ label: typeof decoration.label === "string" ? decoration.label : void 0,
199138
+ name: typeof decoration.name === "string" ? decoration.name : void 0,
199139
+ reversed: decoration.reversed === true
199140
+ }];
199141
+ });
199142
+ }
199143
+ var FLOW_DECORATION_STEP = 26;
199144
+ function flowDecorationGeometry(centre, points, source, target, index2, count, reversed) {
199145
+ const raw = midpointTangent(points?.length ? points : [source, target]) ?? { x: target.x - source.x, y: target.y - source.y };
199146
+ const length2 = Math.hypot(raw.x, raw.y);
199147
+ if (length2 <= 0) return void 0;
199148
+ const tangent = { x: raw.x / length2, y: raw.y / length2 };
199149
+ const normal = { x: -tangent.y, y: tangent.x };
199150
+ const shift = (index2 - (count - 1) / 2) * FLOW_DECORATION_STEP;
199151
+ const at = { x: centre.x + tangent.x * shift, y: centre.y + tangent.y * shift };
199152
+ const sign = reversed ? -1 : 1;
199153
+ const tip = { x: at.x + tangent.x * 5 * sign, y: at.y + tangent.y * 5 * sign };
199154
+ const back = { x: at.x - tangent.x * 4 * sign, y: at.y - tangent.y * 4 * sign };
199155
+ const corner = (side) => ({
199156
+ x: back.x + normal.x * 4 * side,
199157
+ y: back.y + normal.y * 4 * side
199158
+ });
199159
+ const wing = [corner(1), corner(-1)];
199160
+ return {
199161
+ chevron: [tip, wing[0], wing[1]].map((point) => `${markCoordinate(point.x)},${markCoordinate(point.y)}`).join(" "),
199162
+ label: {
199163
+ x: markCoordinate(at.x + normal.x * 13),
199164
+ y: markCoordinate(at.y + normal.y * 13)
199165
+ }
199166
+ };
199167
+ }
198168
199168
  function routePointNearSource(points, source, target, distance2) {
198169
199169
  const route = [source, ...points ?? [], target].filter((point, index2, all) => index2 === 0 || point.x !== all[index2 - 1].x || point.y !== all[index2 - 1].y);
198170
199170
  let remaining = distance2;
@@ -199194,6 +200194,13 @@ body {
199194
200194
  .dlink { stroke: var(--diagram-line-structural); stroke-width: 1.2; fill: none; }
199195
200195
  .dlink.successionFlow { stroke-width: 2; }
199196
200196
  .dsuccession-flow-mark { pointer-events: none; stroke-linecap: butt; }
200197
+ /* REQ-401 \u2014 OMG 8.2.3.16 flow-on-connection: the payload chevron rides the
200198
+ connector that transports it, in the item-flow hue, and never takes pointer
200199
+ events away from the connector it decorates. */
200200
+ .dflow-decoration {
200201
+ pointer-events: none; stroke: none;
200202
+ fill: var(--diagram-line-connection, #6cd1d9);
200203
+ }
199197
200204
  /* REQ-196: AFV control transitions (IR succession) use a dotted route so
199198
200205
  they remain visually distinct from continuous item and succession flows. */
199199
200206
  .dlink.succession { stroke-dasharray: 1 3; stroke-linecap: round; }
@@ -200373,6 +201380,24 @@ function buildDiagramSvg(options) {
200373
201380
  extendPoint(sequencingMark.x1, sequencingMark.y1, 2);
200374
201381
  extendPoint(sequencingMark.x2, sequencingMark.y2, 2);
200375
201382
  }
201383
+ const decorations = semantic ? flowDecorationsOf(semantic) : [];
201384
+ decorations.forEach((decoration, index2) => {
201385
+ const placement = flowDecorationGeometry(
201386
+ geometry.label,
201387
+ geometry.points ?? geometry.anchors,
201388
+ geometry.source,
201389
+ geometry.target,
201390
+ index2,
201391
+ decorations.length,
201392
+ decoration.reversed === true
201393
+ );
201394
+ if (!placement) return;
201395
+ edgeParts.push(`<polygon class="dflow-decoration ${decoration.keyword.replace(/\s+/gu, "-")}" points="${placement.chevron}"/>`);
201396
+ const text = [decoration.name, decoration.label].filter(Boolean).join(" : ");
201397
+ if (!text) return;
201398
+ labelParts.push(labelSvg(text, placement.label.x, placement.label.y, "elabel flow"));
201399
+ extendLabel(text, placement.label.x, placement.label.y);
201400
+ });
200376
201401
  extendPoint(geometry.source.x, geometry.source.y, MARKER_ALLOWANCE);
200377
201402
  extendPoint(geometry.target.x, geometry.target.y, MARKER_ALLOWANCE);
200378
201403
  for (const point of geometry.points ?? geometry.anchors) extendPoint(point.x, point.y, MARKER_ALLOWANCE);
@@ -203682,7 +204707,7 @@ async function runExport(command) {
203682
204707
  }
203683
204708
 
203684
204709
  // src/main.ts
203685
- var VERSION2 = true ? "0.23.0" : "dev";
204710
+ var VERSION2 = true ? "0.25.0" : "dev";
203686
204711
  function display(file) {
203687
204712
  const rel2 = path9.relative(process.cwd(), file);
203688
204713
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;