sysml-diagram 0.24.0 → 0.26.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
@@ -162649,7 +162649,12 @@ function keywordFor(node) {
162649
162649
  JoinNode: "join",
162650
162650
  TerminateNode: "terminate",
162651
162651
  PerformStmt: "perform action",
162652
- EventDecl: "event occurrence"
162652
+ EventDecl: "event occurrence",
162653
+ // REQ-198, issue #113: a state sub-action drawn as a state-transition
162654
+ // node keeps the keyword that says WHEN it runs (OMG 8.2.2.18.1).
162655
+ EntryAction: "entry action",
162656
+ DoAction: "do action",
162657
+ ExitAction: "exit action"
162653
162658
  };
162654
162659
  if (t === "ActionDecl" && n2.inlineBody?.$type === "AcceptNode") {
162655
162660
  return "accept action";
@@ -165264,16 +165269,16 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165264
165269
  };
165265
165270
  const performedActions = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? effectiveMembersOf(part).filter((m) => m.$type === "PerformStmt" && !!nameOf2(m)).map((act) => {
165266
165271
  const pinSource = this.performTargetOf(act, index2) ?? act;
165267
- const pathSegments = this.isAnonymousBehaviorReference(act) ? this.featurePaths.resolveDeclarationPath(act).segments.map((segment) => segment.text) : [nameOf2(act)];
165272
+ const pathSegments2 = this.isAnonymousBehaviorReference(act) ? this.featurePaths.resolveDeclarationPath(act).segments.map((segment) => segment.text) : [nameOf2(act)];
165268
165273
  return {
165269
165274
  act,
165270
165275
  pinSource,
165271
- pathSegments,
165272
- name: pathSegments.at(-1) ?? nameOf2(act),
165276
+ pathSegments: pathSegments2,
165277
+ name: pathSegments2.at(-1) ?? nameOf2(act),
165273
165278
  key: concretePerformPath(act, pinSource)
165274
165279
  };
165275
165280
  }).filter((entry, i, list) => list.findIndex((other) => other.key === entry.key) === i) : [];
165276
- const performedActionInfos = performedActions.map(({ act, pinSource, pathSegments, name, key }) => {
165281
+ const performedActionInfos = performedActions.map(({ act, pinSource, pathSegments: pathSegments2, name, key }) => {
165277
165282
  const id2 = `${instanceId}::__perform_${key}`;
165278
165283
  const inheritedFromDefinition = !!localUsage && !isAstDescendantOrSelf(act, localUsage);
165279
165284
  const syncDeclaration2 = this.synchronizationDeclarationOf(act, index2);
@@ -165281,7 +165286,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165281
165286
  act,
165282
165287
  name,
165283
165288
  id: id2,
165284
- pathSegments,
165289
+ pathSegments: pathSegments2,
165285
165290
  pins: this.actionPinPorts(pinSource, id2, index2, uri),
165286
165291
  meta: {
165287
165292
  ...ivEditMeta(act, id2, void 0, localUsage, [...localPath, name], uri),
@@ -167625,7 +167630,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167625
167630
  const def = this.resolveType(holder, index2);
167626
167631
  return def && def !== holder && def.isDef === true ? def : void 0;
167627
167632
  };
167628
- const ownStates = (holder) => membersOf(holder).filter((member) => isStateDecl(member) && nameOf2(member) !== void 0);
167633
+ const ownStates = (holder) => membersOf(holder).map((member) => this.thenActionMemberNode(member) ?? member).filter((member) => isStateDecl(member) && nameOf2(member) !== void 0);
167629
167634
  const nestedStates = (holder) => {
167630
167635
  const out = ownStates(holder);
167631
167636
  const def = definitionOf(holder);
@@ -167664,9 +167669,28 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167664
167669
  drawnStates.push({ node: anchor, id: anchorFrameId });
167665
167670
  idByState.set(anchor, anchorFrameId);
167666
167671
  }
167672
+ const drawnRowKeys = /* @__PURE__ */ new Set();
167673
+ const rowKey = (source) => source ? `${source.uri} ${source.range.start.line} ${source.range.start.character}` : void 0;
167674
+ const registerDrawn = (member) => {
167675
+ const key = rowKey(sourceOf2(member, uri));
167676
+ if (key)
167677
+ drawnRowKeys.add(key);
167678
+ };
167679
+ const namesByFrame = /* @__PURE__ */ new Map();
167680
+ const namesIn = (frameId) => {
167681
+ const key = frameId ?? "";
167682
+ const existing = namesByFrame.get(key);
167683
+ if (existing)
167684
+ return existing;
167685
+ const created = /* @__PURE__ */ new Map();
167686
+ namesByFrame.set(key, created);
167687
+ return created;
167688
+ };
167667
167689
  const renderState = (s, parentFrame, depth, seen) => {
167668
167690
  const id2 = qnameOf(s) || nameOf2(s);
167669
167691
  byName.set(nameOf2(s), id2);
167692
+ namesIn(parentFrame).set(nameOf2(s), id2);
167693
+ registerDrawn(s);
167670
167694
  drawnStates.push({ node: s, id: id2 });
167671
167695
  idByState.set(s, id2);
167672
167696
  const def = s.isDef === true ? void 0 : this.resolveType(s, index2);
@@ -167704,6 +167728,75 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167704
167728
  };
167705
167729
  for (const s of topStates)
167706
167730
  renderState(s, anchorFrameId, 0, /* @__PURE__ */ new Set());
167731
+ const endpointName = (path10) => typeof path10 === "string" ? path10.split(/::|\./).pop() || void 0 : void 0;
167732
+ const controlNodeInfo = (m) => {
167733
+ switch (m.$type) {
167734
+ case "ActionDecl":
167735
+ return m.isDef === true || !nameOf2(m) ? void 0 : { shape: "action", label: nameOf2(m) };
167736
+ // A `perform` names the action it performs, which for a feature
167737
+ // chain (`perform machine.selfTest;`) is the LAST segment.
167738
+ case "PerformStmt": {
167739
+ const segments = [nameOf2(m), ...m.chainSegs ?? []].filter((segment) => !!segment);
167740
+ return { shape: "action", label: segments.at(-1) ?? typeText(m) ?? "perform" };
167741
+ }
167742
+ // User direction 2026-08-30: fork and join only. They express
167743
+ // concurrency, which a transition cannot say: nothing about a
167744
+ // guard or a trigger splits control into parallel regions or
167745
+ // gathers it again. `decide` and `merge` are dropped because a
167746
+ // branch between states IS a guarded transition, which this
167747
+ // view already draws and labels, so a diamond would say the
167748
+ // same thing a second time in the action-flow idiom.
167749
+ // `terminate` is dropped with them: the node carries no name,
167750
+ // so no succession can reach it and it could only ever stand
167751
+ // alone. All three remain in the OMG 8.2.3.18 node set.
167752
+ case "JoinNode":
167753
+ return { shape: "join", label: nameOf2(m) ?? "" };
167754
+ case "ForkNode":
167755
+ return { shape: "fork", label: nameOf2(m) ?? "" };
167756
+ default:
167757
+ return void 0;
167758
+ }
167759
+ };
167760
+ const isInitialMarker = (member) => {
167761
+ const entry = member;
167762
+ return !!entry && entry.$type === "EntryAction" && !entry.name && !entry.actionKw && !entry.send && !entry.accept && !entry.assign;
167763
+ };
167764
+ const machineNodeIds = /* @__PURE__ */ new Map();
167765
+ const machineNodeMembers = [];
167766
+ let anonymousNodes = 0;
167767
+ const emitMachineNodes = (holder, frameId) => {
167768
+ const members = membersOf(holder);
167769
+ const drawnHere = namesIn(frameId);
167770
+ for (const raw of members) {
167771
+ const member = this.thenActionMemberNode(raw) ?? raw;
167772
+ const info = controlNodeInfo(member);
167773
+ if (!info)
167774
+ continue;
167775
+ const name = nameOf2(member);
167776
+ if (name && drawnHere.has(name))
167777
+ continue;
167778
+ const id2 = name ? qnameOf(member) || `${frameId}::${name}` : `${frameId}::__stv_${member.$type}_${anonymousNodes++}__`;
167779
+ if (nodes.some((n2) => n2.id === id2) || frames.some((f) => f.id === id2))
167780
+ continue;
167781
+ if (name) {
167782
+ drawnHere.set(name, id2);
167783
+ if (!byName.has(name))
167784
+ byName.set(name, id2);
167785
+ }
167786
+ machineNodeIds.set(member, id2);
167787
+ machineNodeMembers.push({ member, id: id2 });
167788
+ nodes.push({
167789
+ id: id2,
167790
+ name: info.label,
167791
+ keyword: keywordFor(member),
167792
+ isDef: false,
167793
+ shape: info.shape,
167794
+ compartments: this.compartmentsFor(member, uri, index2),
167795
+ frame: frameId,
167796
+ source: sourceOf2(member, uri)
167797
+ });
167798
+ }
167799
+ };
167707
167800
  const edges = [];
167708
167801
  let e = 0;
167709
167802
  const resolveState = (nm) => {
@@ -167719,53 +167812,201 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167719
167812
  }
167720
167813
  return void 0;
167721
167814
  };
167722
- const transitionScope = [anchor, ...definitionOf(anchor) ? [definitionOf(anchor)] : []];
167723
- for (const t of transitionScope.flatMap((holder) => [...ast_utils_exports.streamAllContents(holder)])) {
167724
- if (t.$type !== "TransitionDecl")
167725
- continue;
167726
- const tn = t;
167727
- const from = resolveState(tn.from);
167728
- const to = resolveState(tn.to);
167815
+ const drawnEdges = /* @__PURE__ */ new Set();
167816
+ const stateLike = /* @__PURE__ */ new Set(["state", "initial", "final"]);
167817
+ const isStateEndpoint = (id2) => {
167818
+ const node = nodes.find((n2) => n2.id === id2);
167819
+ return node ? stateLike.has(node.shape) : frames.some((f) => f.id === id2);
167820
+ };
167821
+ const link = (from, to, label, src) => {
167729
167822
  if (!from || !to)
167730
- continue;
167823
+ return;
167824
+ const kind = isStateEndpoint(from) && isStateEndpoint(to) ? "transition" : "succession";
167825
+ const key = `${from}\0${to}\0${label ?? ""}`;
167826
+ if (drawnEdges.has(key))
167827
+ return;
167828
+ drawnEdges.add(key);
167829
+ if (src)
167830
+ registerDrawn(src);
167731
167831
  edges.push({
167732
167832
  id: `e${e++}`,
167733
167833
  from,
167734
167834
  to,
167735
- kind: "transition",
167736
- label: this.transitionLabel(t),
167737
- source: sourceOf2(t, uri)
167835
+ kind,
167836
+ label,
167837
+ source: src ? sourceOf2(src, uri) : void 0
167738
167838
  });
167839
+ };
167840
+ const guardText = (guard) => {
167841
+ const text = guard?.$cstNode?.text?.replace(/\s+/g, " ").trim();
167842
+ return text ? `[${text}]` : void 0;
167843
+ };
167844
+ const resolveEndpoint = (path10, initialId, frameId) => {
167845
+ if (typeof path10 !== "string")
167846
+ return void 0;
167847
+ const short = endpointName(path10);
167848
+ const local = namesByFrame.get(frameId ?? "");
167849
+ return (short ? local?.get(short) : void 0) ?? resolveState(path10) ?? resolveState(short) ?? (short === "start" ? initialId : void 0);
167850
+ };
167851
+ const transitionScope = [anchor, ...definitionOf(anchor) ? [definitionOf(anchor)] : []];
167852
+ for (const t of transitionScope.flatMap((holder) => [...ast_utils_exports.streamAllContents(holder)])) {
167853
+ if (t.$type !== "TransitionDecl")
167854
+ continue;
167855
+ const tn = t;
167856
+ link(resolveState(tn.from), resolveState(tn.to), this.transitionLabel(t), t);
167739
167857
  }
167740
- const designatedInitial = (owner) => {
167858
+ const designatedInitial = (owner, frameId) => {
167741
167859
  const members = owner.members;
167742
167860
  if (!Array.isArray(members))
167743
167861
  return void 0;
167744
- for (let i = 0; i < members.length - 1; i++) {
167745
- const entry = members[i];
167746
- if (entry.$type !== "EntryAction" || entry.name || entry.actionKw || entry.send || entry.accept || entry.assign)
167862
+ for (let i = 0; i < members.length; i++) {
167863
+ const member = members[i];
167864
+ if (member.$type === "FirstThenChain") {
167865
+ const chain = member;
167866
+ if (endpointName(chain.head) !== "start")
167867
+ continue;
167868
+ const target2 = resolveEndpoint((chain.steps ?? [])[0], void 0, frameId);
167869
+ if (target2) {
167870
+ registerDrawn(member);
167871
+ return target2;
167872
+ }
167873
+ continue;
167874
+ }
167875
+ if (!isInitialMarker(member))
167747
167876
  continue;
167748
167877
  const then = members[i + 1];
167749
167878
  if (then?.$type !== "ThenActionMember" || typeof then.target !== "string")
167750
167879
  continue;
167751
167880
  const target = resolveState(lastSeg(then.target));
167752
- if (target)
167881
+ if (target) {
167882
+ registerDrawn(then);
167753
167883
  return target;
167884
+ }
167754
167885
  }
167755
167886
  return void 0;
167756
167887
  };
167757
- const anchorDefinition = definitionOf(anchor);
167758
- const firstStateId = designatedInitial(anchor) ?? (anchorDefinition ? designatedInitial(anchorDefinition) : void 0) ?? (topStates.length ? byName.get(nameOf2(topStates[0])) : void 0);
167759
- if (firstStateId) {
167760
- edges.push({ id: `e${e++}`, from: "__initial__", to: firstStateId, kind: "transition" });
167888
+ const machineScope = (holder) => {
167889
+ const def = definitionOf(holder);
167890
+ return def ? [holder, def] : [holder];
167891
+ };
167892
+ if (anchorFrameId) {
167893
+ for (const holder of machineScope(anchor))
167894
+ emitMachineNodes(holder, anchorFrameId);
167895
+ }
167896
+ for (const { state, frameId } of compositeFrames) {
167897
+ for (const holder of machineScope(state))
167898
+ emitMachineNodes(holder, frameId);
167761
167899
  }
167900
+ const anchorDefinition = definitionOf(anchor);
167901
+ const firstStateId = designatedInitial(anchor, anchorFrameId) ?? (anchorDefinition ? designatedInitial(anchorDefinition, anchorFrameId) : void 0) ?? (topStates.length ? byName.get(nameOf2(topStates[0])) : void 0);
167902
+ if (firstStateId)
167903
+ link("__initial__", firstStateId, void 0, void 0);
167904
+ const compositeInitial = /* @__PURE__ */ new Map();
167762
167905
  for (const { state, frameId } of compositeFrames) {
167763
- const target = designatedInitial(state);
167906
+ const target = designatedInitial(state, frameId);
167764
167907
  if (!target)
167765
167908
  continue;
167766
167909
  const id2 = `__initial__${frameId}`;
167767
167910
  nodes.push({ id: id2, name: "", keyword: "initial", isDef: false, shape: "initial", frame: frameId });
167768
- edges.push({ id: `e${e++}`, from: id2, to: target, kind: "transition" });
167911
+ compositeInitial.set(state, id2);
167912
+ link(id2, target, void 0, void 0);
167913
+ }
167914
+ const sequentialTransitions = (holder, initialId, frameId) => {
167915
+ let cursor;
167916
+ const idOf = (member) => machineNodeIds.get(member) ?? idByState.get(member) ?? (nameOf2(member) ? resolveEndpoint(nameOf2(member), void 0, frameId) : void 0);
167917
+ const endpoint = (path10) => resolveEndpoint(path10, initialId, frameId);
167918
+ for (const member of membersOf(holder)) {
167919
+ switch (member.$type) {
167920
+ case "StateDecl": {
167921
+ cursor = idOf(member) ?? cursor;
167922
+ break;
167923
+ }
167924
+ // `entry;` IS the start pseudostate, so the succession written
167925
+ // after it leaves that pseudostate rather than the last state.
167926
+ case "EntryAction": {
167927
+ if (isInitialMarker(member)) {
167928
+ cursor = initialId;
167929
+ break;
167930
+ }
167931
+ cursor = machineNodeIds.get(member) ?? cursor;
167932
+ break;
167933
+ }
167934
+ // OMG 8.2.3.18 names this edge `st-succession`, so the
167935
+ // keyworded form is a first-class state-transition edge,
167936
+ // not action-language sugar the state view may skip.
167937
+ // Its ends are ConnectorEnds, not bare paths.
167938
+ case "SuccessionStmt": {
167939
+ const succession = member;
167940
+ let previous = endpoint(this.connectorEndPath(succession.first));
167941
+ let firstHop = true;
167942
+ for (const step of succession.steps ?? []) {
167943
+ const to = endpoint(this.connectorEndPath(step));
167944
+ link(previous, to, firstHop ? guardText(succession.guard) : void 0, member);
167945
+ previous = to;
167946
+ firstHop = false;
167947
+ }
167948
+ if (previous)
167949
+ cursor = previous;
167950
+ break;
167951
+ }
167952
+ case "FirstThenChain": {
167953
+ const chain = member;
167954
+ let previous = endpoint(chain.head);
167955
+ let firstHop = true;
167956
+ for (const step of chain.steps ?? []) {
167957
+ const to = endpoint(step);
167958
+ link(previous, to, firstHop ? guardText(chain.guard) : void 0, member);
167959
+ previous = to;
167960
+ firstHop = false;
167961
+ }
167962
+ if (previous)
167963
+ cursor = previous;
167964
+ break;
167965
+ }
167966
+ case "AcceptNode": {
167967
+ const accept = member;
167968
+ if (typeof accept.thenTarget !== "string")
167969
+ break;
167970
+ link(cursor, endpoint(accept.thenTarget), this.acceptShorthandLabel(member), member);
167971
+ break;
167972
+ }
167973
+ case "ThenActionMember": {
167974
+ const then = member;
167975
+ const declared = this.thenActionMemberNode(member);
167976
+ const to = (declared ? idOf(declared) : void 0) ?? endpoint(then.target);
167977
+ link(cursor, to, void 0, member);
167978
+ if (to)
167979
+ cursor = to;
167980
+ break;
167981
+ }
167982
+ // issue #113: a guarded or default target succession fans out
167983
+ // of the SAME source (a decision node, normally), so neither
167984
+ // branch may advance the cursor past its siblings.
167985
+ case "IfActionNode": {
167986
+ const branch = member;
167987
+ if (typeof branch.thenTarget !== "string")
167988
+ break;
167989
+ link(cursor, endpoint(branch.thenTarget), guardText(branch.cond), member);
167990
+ break;
167991
+ }
167992
+ case "ElseSuccessionMember": {
167993
+ link(cursor, endpoint(member.target), "else", member);
167994
+ break;
167995
+ }
167996
+ default: {
167997
+ cursor = machineNodeIds.get(member) ?? cursor;
167998
+ break;
167999
+ }
168000
+ }
168001
+ }
168002
+ };
168003
+ for (const holder of machineScope(anchor)) {
168004
+ sequentialTransitions(holder, hostsStateMachine ? "__initial__" : void 0, anchorFrameId);
168005
+ }
168006
+ for (const { state, frameId } of compositeFrames) {
168007
+ for (const holder of machineScope(state)) {
168008
+ sequentialTransitions(holder, compositeInitial.get(state), frameId);
168009
+ }
167769
168010
  }
167770
168011
  const exhibitorsByState = /* @__PURE__ */ new Map();
167771
168012
  const exhibitScope = this.packageScopeOf(anchor);
@@ -167847,6 +168088,37 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167847
168088
  edges.push({ id: `e${e++}`, from, to, kind: "causation", label: "\xABcausation\xBB", source: sourceOf2(c, uri) });
167848
168089
  }
167849
168090
  }
168091
+ const wired = /* @__PURE__ */ new Set();
168092
+ for (const edge of edges) {
168093
+ wired.add(edge.from);
168094
+ wired.add(edge.to);
168095
+ }
168096
+ const stranded = new Set(machineNodeMembers.filter(({ id: id2 }) => !wired.has(id2)).map(({ id: id2 }) => id2));
168097
+ if (stranded.size) {
168098
+ for (let at = nodes.length - 1; at >= 0; at--) {
168099
+ if (stranded.has(nodes[at].id))
168100
+ nodes.splice(at, 1);
168101
+ }
168102
+ }
168103
+ for (const { member, id: id2 } of machineNodeMembers) {
168104
+ if (!stranded.has(id2))
168105
+ registerDrawn(member);
168106
+ }
168107
+ for (const frame2 of frames) {
168108
+ const full = frame2.compartments;
168109
+ if (!full?.length)
168110
+ continue;
168111
+ const kept = full.map((compartment) => ({
168112
+ title: compartment.title,
168113
+ items: compartment.items.filter((row) => {
168114
+ const key = rowKey(row.source);
168115
+ return !key || !drawnRowKeys.has(key);
168116
+ })
168117
+ })).filter((compartment) => compartment.items.length > 0);
168118
+ if (kept.length === full.length && kept.every((c, at) => c.items.length === full[at].items.length))
168119
+ continue;
168120
+ frame2.compartments = kept.length ? kept : void 0;
168121
+ }
167850
168122
  const model = this.model("stv", anchor, nodes, edges);
167851
168123
  if (frames.length) {
167852
168124
  model.frames = frames;
@@ -170032,9 +170304,25 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
170032
170304
  return item(m, val ? `= ${val}` : "(value)");
170033
170305
  }));
170034
170306
  add("states", usages(isStateDecl).map((m) => item(m)));
170035
- const stateActions = members.map((m) => ({ m, kind: { EntryAction: "entry", DoAction: "do", ExitAction: "exit" }[m.$type] })).filter((x) => !!x.kind);
170036
- add("state actions", stateActions.map(({ m, kind }) => item(m, `${kind} ${nameOf2(m) ?? ""}`.trim())));
170307
+ const isInitialStateMarker = (m, at) => {
170308
+ const entry = m;
170309
+ if (m.$type !== "EntryAction" || entry.name || entry.actionKw || entry.send || entry.accept || entry.assign)
170310
+ return false;
170311
+ const next = members[at + 1];
170312
+ return next?.$type === "ThenActionMember" || next?.$type === "FirstThenChain";
170313
+ };
170314
+ const stateActionTitle = {
170315
+ EntryAction: "entry",
170316
+ DoAction: "during",
170317
+ ExitAction: "exit"
170318
+ };
170319
+ const stateActionText = (m) => nameOf2(m) ?? (m.$cstNode?.text ?? "").replace(/\s+/g, " ").replace(/;$/, "").replace(/^(entry|do|exit)\s+(action\s+)?/, "").trim();
170320
+ for (const title of ["entry", "during", "exit"]) {
170321
+ add(title, members.filter((m, at) => stateActionTitle[m.$type] === title && !isInitialStateMarker(m, at)).map((m) => item(m, stateActionText(m))).filter((row) => !!row.text));
170322
+ }
170037
170323
  add("state transition", members.filter((m) => m.$type === "TransitionDecl").map((m) => item(m, `${m.from ?? ""} \u2192 ${m.to ?? ""}`)));
170324
+ const successionText = (m) => (m.$cstNode?.text ?? "").replace(/\s+/g, " ").replace(/;$/, "").trim();
170325
+ add("successions", members.filter((m) => m.$type === "FirstThenChain" || m.$type === "SuccessionStmt" || m.$type === "ThenActionMember" && typeof m.target === "string" || m.$type === "ElseSuccessionMember" || (m.$type === "AcceptNode" || m.$type === "IfActionNode") && typeof m.thenTarget === "string").map((m) => item(m, successionText(m))).filter((row) => !!row.text));
170038
170326
  add("assert constraints", members.filter((m) => m.$type === "AssertConstraintStmt").map((m) => item(m, exprText3(m) || nameOf2(m) || "assert constraint")));
170039
170327
  add("invariants", members.filter((m) => m.$type === "InvShorthand").map((m) => {
170040
170328
  const inv = m;
@@ -170125,9 +170413,19 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
170125
170413
  // REQ-186/REQ-198 — transition label: `trigger [guard] / effect`.
170126
170414
  transitionLabel(t) {
170127
170415
  const tn = t;
170128
- const trigger = this.acceptTriggerLabel(tn.accept);
170129
- const guard = tn.guard?.$cstNode?.text?.replace(/\s+/g, " ").trim();
170130
- const effect = this.effectLabel(tn.effect);
170416
+ return this.transitionLabelOf(tn.accept, tn.guard, tn.effect);
170417
+ }
170418
+ // REQ-198: the label of a source-less transition shorthand (`accept Sig if g
170419
+ // do effect then t;`, OMG SysML 8.2.2.18.3). The accept node itself carries
170420
+ // the trigger, so it reads exactly like an explicit `transition`.
170421
+ acceptShorthandLabel(accept) {
170422
+ const node = accept;
170423
+ return this.transitionLabelOf(accept, node.guard, node.effect);
170424
+ }
170425
+ transitionLabelOf(accept, guardExpr, effectClause) {
170426
+ const trigger = this.acceptTriggerLabel(accept);
170427
+ const guard = guardExpr?.$cstNode?.text?.replace(/\s+/g, " ").trim();
170428
+ const effect = this.effectLabel(effectClause);
170131
170429
  const parts = [];
170132
170430
  if (trigger)
170133
170431
  parts.push(String(trigger));
@@ -170418,7 +170716,9 @@ var sysmlInlayHintSettings = {
170418
170716
  modifiers: false,
170419
170717
  specialization: false,
170420
170718
  redefinition: false,
170421
- effectiveNames: false
170719
+ effectiveNames: false,
170720
+ importedNames: false,
170721
+ parameterNames: false
170422
170722
  };
170423
170723
 
170424
170724
  // ../language-server/out/src/services/category-taxonomy.js
@@ -181119,6 +181419,329 @@ var SysmlSemanticTokenProvider = class extends AbstractSemanticTokenProvider {
181119
181419
 
181120
181420
  // ../language-server/out/src/services/inlay-hint-provider.js
181121
181421
  var import_vscode_languageserver20 = __toESM(require_main4(), 1);
181422
+
181423
+ // ../language-server/out/src/services/name-lookup.js
181424
+ function unquoteName3(name) {
181425
+ return name.replace(/^'(.*)'$/u, "$1");
181426
+ }
181427
+ function pathSegments(path10) {
181428
+ const segments = [];
181429
+ let current2 = "";
181430
+ let quoted = false;
181431
+ for (let index2 = 0; index2 < path10.length; index2++) {
181432
+ const char = path10[index2];
181433
+ if (char === "'") {
181434
+ quoted = !quoted;
181435
+ current2 += char;
181436
+ continue;
181437
+ }
181438
+ if (!quoted && char === ":" && path10[index2 + 1] === ":") {
181439
+ segments.push(current2);
181440
+ current2 = "";
181441
+ index2++;
181442
+ continue;
181443
+ }
181444
+ if (!quoted && char === ".") {
181445
+ segments.push(current2);
181446
+ current2 = "";
181447
+ continue;
181448
+ }
181449
+ current2 += char;
181450
+ }
181451
+ segments.push(current2);
181452
+ return segments;
181453
+ }
181454
+ function simpleNameOf2(name) {
181455
+ return pathSegments(name).at(-1) ?? name;
181456
+ }
181457
+ var ELEMENT_SEPARATOR = "\0";
181458
+ function elementKey2(description) {
181459
+ return `${description.documentUri.toString()}${ELEMENT_SEPARATOR}${description.path}`;
181460
+ }
181461
+ function isDeclaredSpelling(description) {
181462
+ const kind = description.derivedKind;
181463
+ if (kind === "reexport" || kind === "inherited")
181464
+ return false;
181465
+ return pathSegments(description.name).length === 1;
181466
+ }
181467
+ var SysmlNameLookup = class {
181468
+ shared;
181469
+ byName;
181470
+ /** Element key → the declared spellings the index holds for that element. */
181471
+ spellings;
181472
+ constructor(shared) {
181473
+ this.shared = shared;
181474
+ this.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, () => {
181475
+ this.byName = void 0;
181476
+ this.spellings = void 0;
181477
+ });
181478
+ }
181479
+ /** Every description indexed under `name`, in either spelling of an escaped name. */
181480
+ descriptions(name) {
181481
+ return this.index().get(name) ?? [];
181482
+ }
181483
+ /**
181484
+ * The ONE element `name` names, or `undefined` when the answer is not
181485
+ * certain: no element, or more than one. `accept` narrows the candidates
181486
+ * before ambiguity is judged, so "the only CALLABLE called `f`" is a
181487
+ * decidable question even where a part shares the name.
181488
+ */
181489
+ unique(name, accept) {
181490
+ const candidates = this.descriptions(name).filter((candidate) => !accept || accept(candidate));
181491
+ let found;
181492
+ let key;
181493
+ for (const candidate of candidates) {
181494
+ const candidateKey = elementKey2(candidate);
181495
+ if (found === void 0) {
181496
+ found = candidate;
181497
+ key = candidateKey;
181498
+ continue;
181499
+ }
181500
+ if (candidateKey !== key)
181501
+ return void 0;
181502
+ }
181503
+ return found;
181504
+ }
181505
+ /**
181506
+ * Resolve a written path: the spelling the index holds verbatim first, then
181507
+ * its final segment. Both readings must name exactly one element.
181508
+ */
181509
+ uniqueForPath(path10, accept) {
181510
+ return this.unique(path10, accept) ?? this.unique(simpleNameOf2(path10), accept);
181511
+ }
181512
+ /**
181513
+ * The declared spellings of the element `description` names — its regular
181514
+ * name and its `<short>` name — minus `written`.
181515
+ */
181516
+ otherNames(description, written) {
181517
+ const all = this.spellingIndex().get(elementKey2(description)) ?? [];
181518
+ const seen = unquoteName3(written);
181519
+ return all.filter((name) => unquoteName3(name) !== seen);
181520
+ }
181521
+ index() {
181522
+ if (!this.byName)
181523
+ this.build();
181524
+ return this.byName;
181525
+ }
181526
+ spellingIndex() {
181527
+ if (!this.spellings)
181528
+ this.build();
181529
+ return this.spellings;
181530
+ }
181531
+ /** One pass over the index feeds both maps; neither is worth a second. */
181532
+ build() {
181533
+ const byName = /* @__PURE__ */ new Map();
181534
+ const spellings = /* @__PURE__ */ new Map();
181535
+ const add = (key, description) => {
181536
+ const bucket = byName.get(key);
181537
+ if (bucket)
181538
+ bucket.push(description);
181539
+ else
181540
+ byName.set(key, [description]);
181541
+ };
181542
+ for (const description of this.shared.workspace.IndexManager.allElements()) {
181543
+ add(description.name, description);
181544
+ const unquoted = unquoteName3(description.name);
181545
+ if (unquoted !== description.name)
181546
+ add(unquoted, description);
181547
+ if (!isDeclaredSpelling(description))
181548
+ continue;
181549
+ const key = elementKey2(description);
181550
+ const names = spellings.get(key);
181551
+ if (names) {
181552
+ if (!names.includes(description.name))
181553
+ names.push(description.name);
181554
+ } else {
181555
+ spellings.set(key, [description.name]);
181556
+ }
181557
+ }
181558
+ this.byName = byName;
181559
+ this.spellings = spellings;
181560
+ }
181561
+ };
181562
+ var lookups = /* @__PURE__ */ new WeakMap();
181563
+ function nameLookupFor(shared) {
181564
+ if (!shared)
181565
+ return void 0;
181566
+ const existing = lookups.get(shared);
181567
+ if (existing)
181568
+ return existing;
181569
+ const created = new SysmlNameLookup(shared);
181570
+ lookups.set(shared, created);
181571
+ return created;
181572
+ }
181573
+
181574
+ // ../language-server/out/src/services/callable-resolver.js
181575
+ var CALLABLE_TYPES = /* @__PURE__ */ new Set([
181576
+ "CalcDecl",
181577
+ "ActionDecl",
181578
+ "ConstraintDecl",
181579
+ "FunctionDecl",
181580
+ "PredicateDecl",
181581
+ "BehaviorDecl",
181582
+ "InteractionDecl"
181583
+ ]);
181584
+ var CallableResolver = class {
181585
+ services;
181586
+ lookup;
181587
+ linker;
181588
+ constructor(services) {
181589
+ this.services = services;
181590
+ this.lookup = nameLookupFor(services?.shared);
181591
+ this.linker = services?.references.Linker;
181592
+ }
181593
+ /**
181594
+ * Every indexed callable that answers to `name`, so a caller can decide what
181595
+ * ambiguity costs it. Signature help is transient and follows the cursor, so
181596
+ * it takes the first; an inlay hint is a permanent annotation, so it takes a
181597
+ * candidate only when it is the ONLY element with that name.
181598
+ */
181599
+ candidates(name) {
181600
+ const simple = simpleNameOf2(name);
181601
+ return (this.lookup?.descriptions(simple) ?? []).filter((description) => CALLABLE_TYPES.has(description.type));
181602
+ }
181603
+ /**
181604
+ * The one callable `name` unambiguously names in the index, if any: the
181605
+ * written spelling first, then its final segment, and either reading must
181606
+ * name exactly one element.
181607
+ */
181608
+ uniqueCandidate(name) {
181609
+ return this.lookup?.uniqueForPath(name, (description) => CALLABLE_TYPES.has(description.type));
181610
+ }
181611
+ /** The callable declaration `name` invokes in `document`, resolved locally. */
181612
+ findLocal(document2, name) {
181613
+ const root4 = document2.parseResult?.value;
181614
+ if (!root4)
181615
+ return void 0;
181616
+ return collectCallables([root4, ...ast_utils_exports.streamAllContents(root4).toArray()]).get(unquoteName3(simpleNameOf2(name)));
181617
+ }
181618
+ /**
181619
+ * SYNCHRONOUS resolution: a qualified spelling the index holds verbatim,
181620
+ * then the document's own tree, then an unambiguous indexed callable —
181621
+ * resolved through the linker's off-to-the-side library parse. Nothing here
181622
+ * grows `LangiumDocuments`, and nothing here awaits, so it is safe on a
181623
+ * request VS Code re-issues on every scroll.
181624
+ */
181625
+ resolve(document2, name, local) {
181626
+ if (name.includes("::") || name.includes(".")) {
181627
+ const exact = this.lookup?.unique(name, (description) => CALLABLE_TYPES.has(description.type));
181628
+ const node = exact ? this.nodeOf(exact) : void 0;
181629
+ if (node)
181630
+ return node;
181631
+ }
181632
+ const found = local ? local(unquoteName3(simpleNameOf2(name))) : this.findLocal(document2, name);
181633
+ if (found)
181634
+ return found;
181635
+ const candidate = this.uniqueCandidate(name);
181636
+ return candidate ? this.nodeOf(candidate) : void 0;
181637
+ }
181638
+ /** An indexed description as a node, through the linker's lazy library parse. */
181639
+ nodeOf(description) {
181640
+ return this.linker?.resolveIndexedNode?.(description) ?? description.node;
181641
+ }
181642
+ /**
181643
+ * Resolution that may load a workspace document. Signature help uses it: it
181644
+ * runs on an explicit editor gesture, not on every scroll, and a callable in
181645
+ * a workspace file the user has not opened yet still deserves a signature.
181646
+ */
181647
+ // REQ-263 — Signature help on `(` / `,` / `->`
181648
+ async resolveAsync(document2, name) {
181649
+ const direct = this.resolve(document2, name);
181650
+ if (direct)
181651
+ return direct;
181652
+ const candidate = this.candidates(name).at(0);
181653
+ if (!candidate)
181654
+ return void 0;
181655
+ if (candidate.node)
181656
+ return candidate.node;
181657
+ const documents = this.services?.shared.workspace.LangiumDocuments;
181658
+ const locator = this.services?.workspace.AstNodeLocator;
181659
+ if (!documents || !locator)
181660
+ return void 0;
181661
+ const doc = documents.getDocument(candidate.documentUri) ?? await documents.getOrCreateDocument(candidate.documentUri);
181662
+ const root4 = doc?.parseResult?.value;
181663
+ return root4 ? locator.getAstNode(root4, candidate.path) : void 0;
181664
+ }
181665
+ /** {@link callableParameters} — the parameters, in written order. */
181666
+ parameters(node) {
181667
+ return callableParameters(node);
181668
+ }
181669
+ /**
181670
+ * The parameters a positional argument list binds to: the INPUTS, in written
181671
+ * order. An `out` parameter and the result are not written at the call site
181672
+ * (OMG KerML v1.0 binds an `InvocationExpression`'s arguments to the
181673
+ * invoked type's input features).
181674
+ */
181675
+ inputParameters(node) {
181676
+ return this.parameters(node).filter((parameter) => !parameter.isReturn && parameter.direction !== "out");
181677
+ }
181678
+ };
181679
+ function callableParameters(node) {
181680
+ const directed = new Map(directedParameters(node).map((entry) => [entry.node, entry.direction]));
181681
+ const result = [];
181682
+ for (const member of membersOf2(node)) {
181683
+ const direction = directed.get(member);
181684
+ const isReturn = member.$type === "ReturnDecl";
181685
+ if (!direction && !isReturn)
181686
+ continue;
181687
+ const name = nodeName2(member);
181688
+ if (!name && !direction)
181689
+ continue;
181690
+ result.push({
181691
+ node: member,
181692
+ direction: direction ?? "return",
181693
+ name,
181694
+ type: typingText(member),
181695
+ multiplicity: multiplicityText2(member),
181696
+ isReturn
181697
+ });
181698
+ }
181699
+ return result;
181700
+ }
181701
+ function collectCallables(nodes) {
181702
+ const callables = /* @__PURE__ */ new Map();
181703
+ const byDefinition = /* @__PURE__ */ new Set();
181704
+ for (const node of nodes) {
181705
+ if (!CALLABLE_TYPES.has(node.$type) && callableParameters(node).length === 0)
181706
+ continue;
181707
+ const isDefinition = node.isDef === true;
181708
+ for (const name of [nodeName2(node), shortNameOf2(node)]) {
181709
+ if (!name)
181710
+ continue;
181711
+ if (callables.has(name) && !(isDefinition && !byDefinition.has(name)))
181712
+ continue;
181713
+ callables.set(name, node);
181714
+ if (isDefinition)
181715
+ byDefinition.add(name);
181716
+ }
181717
+ }
181718
+ return callables;
181719
+ }
181720
+ function membersOf2(node) {
181721
+ const members = node?.members;
181722
+ return Array.isArray(members) ? members.filter(isAstNode3) : [];
181723
+ }
181724
+ function isAstNode3(value) {
181725
+ return typeof value === "object" && value !== null && typeof value.$type === "string";
181726
+ }
181727
+ function nodeName2(node) {
181728
+ const value = node?.name;
181729
+ return typeof value === "string" && value.length > 0 ? unquoteName3(value) : void 0;
181730
+ }
181731
+ function shortNameOf2(node) {
181732
+ const value = node?.shortName?.name;
181733
+ return typeof value === "string" && value.length > 0 ? unquoteName3(value) : void 0;
181734
+ }
181735
+ function typingText(node) {
181736
+ const text = node?.typing?.type?.$refText;
181737
+ return typeof text === "string" && text.length > 0 ? text : void 0;
181738
+ }
181739
+ function multiplicityText2(node) {
181740
+ const text = node?.multiplicity?.$cstNode?.text;
181741
+ return typeof text === "string" && text.length > 0 ? text.trim() : void 0;
181742
+ }
181743
+
181744
+ // ../language-server/out/src/services/inlay-hint-provider.js
181122
181745
  function multiplicityAnchor(node) {
181123
181746
  const leaves = cst_utils_exports.flattenCst(node).toArray();
181124
181747
  const bodyStart = leaves.find((leaf) => leaf.text === "{");
@@ -181177,6 +181800,50 @@ var EXPLICIT_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
181177
181800
  "conjugates"
181178
181801
  ]);
181179
181802
  var REDEFINITION_KINDS2 = /* @__PURE__ */ new Set([":>>", "redefines"]);
181803
+ var CONSTANT_CARRYING_KINDS = /* @__PURE__ */ new Set([
181804
+ ":>",
181805
+ "subsets",
181806
+ ":>>",
181807
+ "redefines",
181808
+ "::>",
181809
+ "references",
181810
+ "=>",
181811
+ "crosses"
181812
+ ]);
181813
+ var CONSTANT_MODIFIERS = /* @__PURE__ */ new Set(["constant", "const"]);
181814
+ function constantKeyword(languageId) {
181815
+ return languageId === "kerml" ? "const" : "constant";
181816
+ }
181817
+ var PREFIXES_BEFORE_CONSTANT = /* @__PURE__ */ new Set([
181818
+ "public",
181819
+ "private",
181820
+ "protected",
181821
+ "in",
181822
+ "out",
181823
+ "inout",
181824
+ "abstract",
181825
+ "variation",
181826
+ "variant",
181827
+ "derived",
181828
+ "readonly",
181829
+ "composite",
181830
+ "portion",
181831
+ "ordered",
181832
+ "nonunique",
181833
+ "parallel"
181834
+ ]);
181835
+ var PORTION_MODIFIERS = /* @__PURE__ */ new Set(["portion", "snapshot", "timeslice"]);
181836
+ var ACTION_KIND_USAGES = /* @__PURE__ */ new Set([
181837
+ "ActionDecl",
181838
+ "StateDecl",
181839
+ "CalcDecl",
181840
+ "CaseDecl",
181841
+ "UseCaseDecl",
181842
+ "AnalysisCaseDecl",
181843
+ "VerificationCaseDecl"
181844
+ ]);
181845
+ var KERML_FEATURE_DECLS = /* @__PURE__ */ new Set(["FeatureDecl", "StepDecl", "ExpressionDecl"]);
181846
+ var REFERENCE_MODIFIERS = /* @__PURE__ */ new Set(["ref", "in", "out", "inout"]);
181180
181847
  var IMPLICIT_BASES = Object.freeze({
181181
181848
  PartDecl: { def: "Parts::Part", usage: "Parts::parts" },
181182
181849
  ItemDecl: { def: "Items::Item", usage: "Items::items" },
@@ -181201,6 +181868,37 @@ var IMPLICIT_BASES = Object.freeze({
181201
181868
  OccurrenceDecl: { def: "Occurrences::Occurrence", usage: "Occurrences::occurrences" },
181202
181869
  MetadataDecl: { def: "Metadata::MetadataItem", usage: "Metadata::metadataItems" }
181203
181870
  });
181871
+ var OCCURRENCE_KIND_OWNERS = /* @__PURE__ */ new Set([
181872
+ ...Object.keys(IMPLICIT_BASES).filter((kind) => kind !== "AttributeDecl"),
181873
+ // KerML fixes a library base per classifier kind too, in the same way and in
181874
+ // the same normative place: `Class` specializes `Occurrences::Occurrence`,
181875
+ // `Structure` specializes `Objects::Object`, `Metaclass` specializes
181876
+ // `Metaobjects::Metaobject`, `Behavior` specializes
181877
+ // `Performances::Performance`, `Function` and `Interaction` are Behaviors,
181878
+ // `Predicate` is a Function, `Step` specializes `Performances::performances`
181879
+ // and `Expression` is a Step. All of them reach `Occurrence`.
181880
+ "ClassDecl",
181881
+ "StructDecl",
181882
+ "MetaclassDecl",
181883
+ "BehaviorDecl",
181884
+ "InteractionDecl",
181885
+ "FunctionDecl",
181886
+ "PredicateDecl",
181887
+ "StepDecl",
181888
+ "ExpressionDecl"
181889
+ // Left out because their base is NOT an occurrence: `DatatypeDecl`
181890
+ // (`Base::DataValue`, declared disjoint from `Occurrence`) and plain
181891
+ // `AssociationDecl` (`Links::Link`, which specializes `Base::Anything`) —
181892
+ // `assoc struct` is handled separately, since it is a `LinkObject`.
181893
+ // Left out because their base is WRITTEN rather than fixed by the kind:
181894
+ // `TypeDecl`, `ClassifierDecl`, `FeatureDecl`.
181895
+ ]);
181896
+ function isOccurrenceKindOwner(owner) {
181897
+ if (owner.$type === "AssociationDecl")
181898
+ return owner.isStruct === true;
181899
+ return OCCURRENCE_KIND_OWNERS.has(owner.$type);
181900
+ }
181901
+ var NON_VARYING_LIBRARY_TYPES = /* @__PURE__ */ new Set(["SelfLink", "HappensLink"]);
181204
181902
  function markdown(value) {
181205
181903
  return { kind: import_vscode_languageserver20.MarkupKind.Markdown, value };
181206
181904
  }
@@ -181222,19 +181920,130 @@ function descriptionRange(description) {
181222
181920
  const segment = description.nameSegment ?? description.selectionSegment;
181223
181921
  return segment?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
181224
181922
  }
181923
+ function descriptionLocation(description) {
181924
+ return { uri: description.documentUri.toString(), range: descriptionRange(description) };
181925
+ }
181926
+ function nodeLocation(node) {
181927
+ const range = node.$cstNode?.range;
181928
+ if (!range)
181929
+ return void 0;
181930
+ return { uri: ast_utils_exports.getDocument(node).uri.toString(), range };
181931
+ }
181932
+ function specializesNonVaryingLibraryType(node) {
181933
+ const typing = node.typing?.type?.$refText;
181934
+ if (typing && NON_VARYING_LIBRARY_TYPES.has(lastSegment3(typing)))
181935
+ return true;
181936
+ return allRelationships2(node).flatMap((rel2) => rel2.targets ?? []).some((target) => NON_VARYING_LIBRARY_TYPES.has(lastSegment3(target)));
181937
+ }
181938
+ function constantAnchor(node) {
181939
+ const cst = node.$cstNode;
181940
+ if (!cst)
181941
+ return void 0;
181942
+ for (const leaf of cst_utils_exports.flattenCst(cst)) {
181943
+ if (leaf.hidden)
181944
+ continue;
181945
+ if (PREFIXES_BEFORE_CONSTANT.has(leaf.text))
181946
+ continue;
181947
+ return /^[a-z]+$/u.test(leaf.text) ? leaf : void 0;
181948
+ }
181949
+ return void 0;
181950
+ }
181951
+ function membershipImportPath(node) {
181952
+ if (isImport(node) && node.alias)
181953
+ return void 0;
181954
+ if (isExposePath(node) && node.dotSegs.length > 0)
181955
+ return void 0;
181956
+ if (node.segs.some((seg) => seg.star || seg.recursive || !seg.name))
181957
+ return void 0;
181958
+ const last2 = node.segs.at(-1);
181959
+ const written = last2?.name ?? node.head;
181960
+ const anchor = last2 ? grammar_utils_exports.findNodeForProperty(last2.$cstNode, "name") : grammar_utils_exports.findNodeForProperty(node.$cstNode, "head");
181961
+ if (!anchor)
181962
+ return void 0;
181963
+ return { path: [node.head, ...node.segs.map((seg) => seg.name)].join("::"), written, anchor };
181964
+ }
181965
+ function publicFeatures(type) {
181966
+ const members = type.members;
181967
+ if (!Array.isArray(members))
181968
+ return [];
181969
+ return members.filter((member) => {
181970
+ if (typeof member?.$type !== "string")
181971
+ return false;
181972
+ const decl = member;
181973
+ if (decl.isDef !== false && decl.$type !== "FeatureDecl")
181974
+ return false;
181975
+ return decl.visibility === void 0 || decl.visibility === "public";
181976
+ });
181977
+ }
181978
+ function calleeName(node) {
181979
+ if (node.call) {
181980
+ if (isPathExpr(node.target))
181981
+ return node.target.path;
181982
+ const target = node.target;
181983
+ if (isPostfixOp(target) && target.dot && target.field)
181984
+ return target.field;
181985
+ return void 0;
181986
+ }
181987
+ if (node.arrow && node.invoke)
181988
+ return node.invoke;
181989
+ return void 0;
181990
+ }
181991
+ function callArguments(node) {
181992
+ if (node.call)
181993
+ return { args: node.callArgs, firstParameter: 0 };
181994
+ if (node.arrow && node.invoke)
181995
+ return { args: node.args, firstParameter: 1 };
181996
+ return void 0;
181997
+ }
181225
181998
  var SysmlInlayHintProvider = class {
181226
181999
  services;
181227
182000
  /**
181228
182001
  * Resolved implicit bases, keyed by the qualified name in
181229
- * {@link IMPLICIT_BASES}. The standard library does not change once it is
181230
- * loaded, so this is built at most once for real; while it is still empty —
181231
- * before the library has been indexed the rebuild is over a tiny index and
181232
- * costs nothing, which is what keeps the hint path free of a full index scan
181233
- * on every request.
182002
+ * {@link IMPLICIT_BASES}. A base the index does not hold is NOT cached — the
182003
+ * library may still be loading and issue #240 made that miss cheap: two
182004
+ * lookups in the shared name index rather than the full index scan it used
182005
+ * to be, on a request VS Code re-issues on every scroll. A HIT is cached
182006
+ * until the index is rebuilt, so the description a label links to always
182007
+ * carries the ranges of the generation it was read from.
181234
182008
  */
181235
182009
  baseCache = /* @__PURE__ */ new Map();
182010
+ /**
182011
+ * REQ-395 — issue #240: the shared name lookup, built once per index
182012
+ * generation. Every category that resolves a WRITTEN name goes through it,
182013
+ * so no request puts an index scan on the hint path.
182014
+ */
182015
+ lookup;
182016
+ /** REQ-395 — issue #240: the callee resolution signature help also uses. */
182017
+ callables;
182018
+ /** REQ-395 — issue #240: the linker's lazy standard-library parse. */
182019
+ linker;
182020
+ /**
182021
+ * REQ-395 — issue #240: "does this element declare `constant`?", memoized by
182022
+ * element identity, because answering it may cost one lazily parsed library
182023
+ * file. Editing away the `constant` on the SUBSETTED feature changes the
182024
+ * answer without changing the key, so this is dropped whenever the index is
182025
+ * rebuilt, exactly like the shared name lookup.
182026
+ */
182027
+ constantCache = /* @__PURE__ */ new Map();
182028
+ /**
182029
+ * REQ-395 — issue #240: an element's own written names, memoized by element
182030
+ * identity. Reading them tells a `<short>` name from a regular one, which is
182031
+ * what lets the imported-name hint print the notation the source would have
182032
+ * written. It costs at most one lazily parsed library file, only for an
182033
+ * element that actually has two names, and it is dropped with the rest when
182034
+ * the index is rebuilt.
182035
+ */
182036
+ declaredNameCache = /* @__PURE__ */ new Map();
181236
182037
  constructor(services) {
181237
182038
  this.services = services;
182039
+ this.lookup = nameLookupFor(services?.shared);
182040
+ this.callables = new CallableResolver(services);
182041
+ this.linker = services?.references.Linker;
182042
+ services?.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, () => {
182043
+ this.baseCache.clear();
182044
+ this.constantCache.clear();
182045
+ this.declaredNameCache.clear();
182046
+ });
181238
182047
  }
181239
182048
  getInlayHints(document2, _params) {
181240
182049
  const root4 = document2.parseResult?.value;
@@ -181242,7 +182051,14 @@ var SysmlInlayHintProvider = class {
181242
182051
  return void 0;
181243
182052
  const settings = sysmlInlayHintSettings;
181244
182053
  const hints = [];
181245
- for (const node of allAstNodes(root4)) {
182054
+ const nodes = allAstNodes(root4);
182055
+ let localCallables;
182056
+ const localCallable = (simple) => {
182057
+ localCallables ??= collectCallables(nodes);
182058
+ return localCallables.get(simple);
182059
+ };
182060
+ const callees = /* @__PURE__ */ new Map();
182061
+ for (const node of nodes) {
181246
182062
  const decl = node;
181247
182063
  if (settings.multiplicity && (isPartDecl(node) || isPortDecl(node) || isAttributeDecl(node)) && !decl.isDef && !decl.multiplicity && node.$cstNode && decl.name) {
181248
182064
  hints.push({
@@ -181269,6 +182085,11 @@ var SysmlInlayHintProvider = class {
181269
182085
  });
181270
182086
  }
181271
182087
  }
182088
+ if (settings.modifiers) {
182089
+ const constant2 = this.implicitConstantHint(decl);
182090
+ if (constant2)
182091
+ hints.push(constant2);
182092
+ }
181272
182093
  if (settings.modifiers && isAttributeDecl(node) && !decl.isDef && !(decl.modifiers ?? []).includes("ref")) {
181273
182094
  const keyword = keywordLeaf(node, "attribute");
181274
182095
  if (keyword) {
@@ -181310,6 +182131,17 @@ var SysmlInlayHintProvider = class {
181310
182131
  if (effective)
181311
182132
  hints.push(effective);
181312
182133
  }
182134
+ if (settings.importedNames && (isImport(node) || isExposePath(node))) {
182135
+ const imported = this.importedNameHint(node);
182136
+ if (imported)
182137
+ hints.push(imported);
182138
+ }
182139
+ if (settings.parameterNames && isPostfixOp(node)) {
182140
+ hints.push(...this.argumentNameHints(node, document2, callees, localCallable));
182141
+ }
182142
+ if (settings.parameterNames && isNewExpr(node)) {
182143
+ hints.push(...this.constructorArgumentHints(node));
182144
+ }
181313
182145
  if (!settings.dimension)
181314
182146
  continue;
181315
182147
  if (isNumericPrimary(node)) {
@@ -181379,6 +182211,228 @@ var SysmlInlayHintProvider = class {
181379
182211
  }
181380
182212
  return hints;
181381
182213
  }
182214
+ /**
182215
+ * REQ-395 — issue #240: the other name a membership `import` brings in.
182216
+ *
182217
+ * An element may declare both a regular name and a `<short>` name, and an
182218
+ * import brings in BOTH — the one the path does not write is invisible in
182219
+ * the source, which is exactly what the hint is for. The written path is a
182220
+ * datatype string, not a cross-reference, so the element is found through
182221
+ * the shared name lookup: the spelling the index holds verbatim first, then
182222
+ * the final segment, and either reading must name exactly ONE element. A
182223
+ * name two packages both declare yields nothing rather than whichever the
182224
+ * index lists first.
182225
+ */
182226
+ importedNameHint(node) {
182227
+ const written = membershipImportPath(node);
182228
+ if (!written || !this.lookup)
182229
+ return void 0;
182230
+ const description = this.lookup.uniqueForPath(written.path);
182231
+ if (!description)
182232
+ return void 0;
182233
+ const others = this.lookup.otherNames(description, written.written);
182234
+ if (others.length === 0)
182235
+ return void 0;
182236
+ const declared = this.declaredNames(description);
182237
+ const shown = others.map((name) => declared?.shortName === name ? `<${name}>` : name);
182238
+ const kind = isImport(node) ? "import" : "expose";
182239
+ return {
182240
+ position: written.anchor.range.end,
182241
+ label: [{
182242
+ value: ` also ${shown.join(", ")}`,
182243
+ location: descriptionLocation(description)
182244
+ }],
182245
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
182246
+ paddingLeft: true,
182247
+ 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.`)
182248
+ };
182249
+ }
182250
+ /**
182251
+ * REQ-395 — issue #240: the names an indexed element declares for itself.
182252
+ *
182253
+ * The index records the spellings but not which of them is the `<short>`
182254
+ * one, so the declaration is resolved through the linker's off-to-the-side
182255
+ * library parse — the budget REQ-251 sets, spent only for an element that
182256
+ * has more than one name, and memoized until the index is rebuilt.
182257
+ */
182258
+ declaredNames(description) {
182259
+ const key = elementKey2(description);
182260
+ const cached = this.declaredNameCache.get(key);
182261
+ if (cached)
182262
+ return cached;
182263
+ const node = this.linker?.resolveIndexedNode?.(description) ?? description.node;
182264
+ if (!node)
182265
+ return void 0;
182266
+ const decl = node;
182267
+ const names = { name: decl.name, shortName: decl.shortName?.name };
182268
+ this.declaredNameCache.set(key, names);
182269
+ return names;
182270
+ }
182271
+ /**
182272
+ * REQ-395 — issue #240: the feature each `new Type(…)` argument binds to.
182273
+ *
182274
+ * A `ConstructorExpression` binds its arguments to the PUBLIC features of
182275
+ * the instantiated type, in order (OMG KerML v1.0, `ConstructorExpression`)
182276
+ * — not to input parameters, which is what an `InvocationExpression` binds.
182277
+ * The instantiated type IS a cross-reference here, so there is nothing to
182278
+ * resolve by name: an unresolved one simply yields no hints.
182279
+ */
182280
+ constructorArgumentHints(node) {
182281
+ if (node.args.length === 0)
182282
+ return [];
182283
+ if (node.args.every((argument) => argument.name !== void 0))
182284
+ return [];
182285
+ const instantiated = node.type?.ref;
182286
+ if (!instantiated)
182287
+ return [];
182288
+ const features = publicFeatures(instantiated);
182289
+ if (features.length === 0)
182290
+ return [];
182291
+ const hints = [];
182292
+ for (let index2 = 0; index2 < node.args.length; index2++) {
182293
+ const argument = node.args[index2];
182294
+ if (argument.name || !argument.$cstNode)
182295
+ continue;
182296
+ const feature = features[index2];
182297
+ const name = feature?.name;
182298
+ if (!feature || !name)
182299
+ continue;
182300
+ const location = nodeLocation(feature);
182301
+ hints.push({
182302
+ position: argument.$cstNode.range.start,
182303
+ label: [{ value: `${name} =`, ...location ? { location } : {} }],
182304
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
182305
+ paddingRight: true,
182306
+ tooltip: markdown(`Argument ${index2 + 1} binds to \`${name}\` of \`${node.type.$refText}\`.`)
182307
+ });
182308
+ }
182309
+ return hints;
182310
+ }
182311
+ /**
182312
+ * REQ-395 — issue #240: the parameter a POSITIONAL invocation argument binds
182313
+ * to.
182314
+ *
182315
+ * The callee of a call is an expression rather than a cross-reference, so
182316
+ * the name is resolved through the shared {@link CallableResolver} — the
182317
+ * same one signature help uses, which is why naming these arguments does not
182318
+ * add a second resolution path. An argument that writes its own name has
182319
+ * stated the correspondence, so it gets no hint.
182320
+ */
182321
+ argumentNameHints(node, document2, callees, localCallable) {
182322
+ const call = callArguments(node);
182323
+ const callee = calleeName(node);
182324
+ if (!call || !callee || call.args.length === 0)
182325
+ return [];
182326
+ if (call.args.every((argument) => argument.argName !== void 0))
182327
+ return [];
182328
+ let callable = callees.get(callee);
182329
+ if (callable === void 0 && !callees.has(callee)) {
182330
+ callable = this.callables.resolve(document2, callee, localCallable);
182331
+ callees.set(callee, callable);
182332
+ }
182333
+ if (!callable)
182334
+ return [];
182335
+ const parameters = this.callables.inputParameters(callable);
182336
+ if (parameters.length === 0)
182337
+ return [];
182338
+ const hints = [];
182339
+ for (let index2 = 0; index2 < call.args.length; index2++) {
182340
+ const argument = call.args[index2];
182341
+ if (argument.argName || !argument.$cstNode)
182342
+ continue;
182343
+ const parameter = parameters[index2 + call.firstParameter];
182344
+ if (!parameter?.name)
182345
+ continue;
182346
+ const location = nodeLocation(parameter.node);
182347
+ hints.push({
182348
+ position: argument.$cstNode.range.start,
182349
+ label: [{ value: `${parameter.name} =`, ...location ? { location } : {} }],
182350
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
182351
+ paddingRight: true,
182352
+ tooltip: markdown(`Argument ${index2 + 1} binds to \`${parameter.direction} ${parameter.name}\` of \`${callee}\`.`)
182353
+ });
182354
+ }
182355
+ return hints;
182356
+ }
182357
+ /**
182358
+ * REQ-395 — issue #240: implicit `constant`.
182359
+ *
182360
+ * OMG KerML v1.0 constrains `Subsetting`: `subsettedFeature.isConstant and
182361
+ * subsettingFeature.isVariable implies subsettingFeature.isConstant`. So a
182362
+ * usage that subsets or redefines a feature declared `constant` IS constant,
182363
+ * without writing it — provided it can vary at all, because `Feature` also
182364
+ * constrains `isConstant implies isVariable`.
182365
+ *
182366
+ * Whether it can vary is `Usage::mayTimeVary`, which OMG SysML v2 Part 1
182367
+ * derives as "owned by a type that specializes `Occurrences::Occurrence`,
182368
+ * and not a portion, a self/happens link, or a composite action". All four
182369
+ * are decided here from the declaration kinds and what the source writes
182370
+ * ({@link isOccurrenceKindOwner}, {@link PORTION_MODIFIERS},
182371
+ * {@link ACTION_KIND_USAGES} with {@link REFERENCE_MODIFIERS}, and
182372
+ * {@link specializesNonVaryingLibraryType}); an owner whose base is written
182373
+ * rather than fixed by its kind leaves the category quiet, as an
182374
+ * undecidable model must.
182375
+ */
182376
+ implicitConstantHint(node) {
182377
+ const isFeature = node.isDef === false || KERML_FEATURE_DECLS.has(node.$type);
182378
+ if (!isFeature)
182379
+ return void 0;
182380
+ const modifiers2 = node.modifiers ?? [];
182381
+ if (modifiers2.some((modifier) => CONSTANT_MODIFIERS.has(modifier)))
182382
+ return void 0;
182383
+ if (modifiers2.some((modifier) => PORTION_MODIFIERS.has(modifier)))
182384
+ return void 0;
182385
+ if (ACTION_KIND_USAGES.has(node.$type) && !modifiers2.some((modifier) => REFERENCE_MODIFIERS.has(modifier)))
182386
+ return void 0;
182387
+ const owner = node.$container;
182388
+ if (!owner || !isOccurrenceKindOwner(owner))
182389
+ return void 0;
182390
+ if (specializesNonVaryingLibraryType(node))
182391
+ return void 0;
182392
+ const carrier = allRelationships2(node).filter((rel2) => rel2.kind && CONSTANT_CARRYING_KINDS.has(rel2.kind)).flatMap((rel2) => rel2.targets ?? []).find((target) => this.isConstantFeature(target));
182393
+ if (!carrier)
182394
+ return void 0;
182395
+ const anchor = constantAnchor(node);
182396
+ const at = anchor?.range.start ?? node.$cstNode?.range.start;
182397
+ if (!at)
182398
+ return void 0;
182399
+ const keyword = constantKeyword(this.services?.LanguageMetaData.languageId);
182400
+ return {
182401
+ position: at,
182402
+ label: keyword,
182403
+ kind: import_vscode_languageserver20.InlayHintKind.Type,
182404
+ paddingRight: true,
182405
+ 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.`),
182406
+ // Writing the modifier is mechanically safe — it is a leading
182407
+ // declaration prefix, and OMG puts it exactly here, before `ref`
182408
+ // and the kind keyword.
182409
+ ...anchor ? { textEdits: [{ range: { start: at, end: at }, newText: `${keyword} ` }] } : {}
182410
+ };
182411
+ }
182412
+ /**
182413
+ * REQ-395 — issue #240: does the feature this path names declare `constant`?
182414
+ *
182415
+ * The path must name exactly one element, and that element is resolved
182416
+ * through the linker's off-to-the-side library parse — one lazily parsed
182417
+ * indexed target, the budget REQ-251 sets, and never a document added to the
182418
+ * workspace. The answer is memoized, so a scroll does not repeat it.
182419
+ */
182420
+ isConstantFeature(target) {
182421
+ if (!this.lookup)
182422
+ return false;
182423
+ const description = this.lookup.uniqueForPath(target);
182424
+ if (!description)
182425
+ return false;
182426
+ const key = elementKey2(description);
182427
+ const cached = this.constantCache.get(key);
182428
+ if (cached !== void 0)
182429
+ return cached;
182430
+ const node = this.linker?.resolveIndexedNode?.(description) ?? description.node;
182431
+ const modifiers2 = node?.modifiers ?? [];
182432
+ const answer = modifiers2.some((modifier) => CONSTANT_MODIFIERS.has(modifier));
182433
+ this.constantCache.set(key, answer);
182434
+ return answer;
182435
+ }
181382
182436
  /**
181383
182437
  * REQ-395 — Find one implicit base in the index.
181384
182438
  *
@@ -181392,25 +182446,19 @@ var SysmlInlayHintProvider = class {
181392
182446
  const cached = this.baseCache.get(qualified);
181393
182447
  if (cached)
181394
182448
  return cached;
181395
- const index2 = this.services?.shared.workspace.IndexManager;
181396
- if (!index2)
182449
+ if (!this.lookup)
181397
182450
  return void 0;
181398
- const simple = lastSegment3(qualified);
181399
- let fallback;
181400
- for (const description of index2.allElements()) {
181401
- if (description.name === qualified) {
181402
- this.baseCache.set(qualified, description);
181403
- return description;
181404
- }
181405
- if (fallback || description.name !== simple)
181406
- continue;
182451
+ const exact = this.lookup.descriptions(qualified).at(0);
182452
+ if (exact) {
182453
+ this.baseCache.set(qualified, exact);
182454
+ return exact;
182455
+ }
182456
+ const fallback = this.lookup.descriptions(lastSegment3(qualified)).find((description) => {
181407
182457
  const uri = description.documentUri instanceof URI2 ? description.documentUri : URI2.parse(String(description.documentUri));
181408
182458
  if (!isStandardLibraryUri(uri))
181409
- continue;
181410
- if (description.isUsage === true !== wantUsage)
181411
- continue;
181412
- fallback = description;
181413
- }
182459
+ return false;
182460
+ return description.isUsage === true === wantUsage;
182461
+ });
181414
182462
  if (fallback)
181415
182463
  this.baseCache.set(qualified, fallback);
181416
182464
  return fallback;
@@ -196025,7 +197073,7 @@ var AFV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set([
196025
197073
  "succession",
196026
197074
  "successionFlow"
196027
197075
  ]);
196028
- var STV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set(["transition"]);
197076
+ var STV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set(["transition", "succession"]);
196029
197077
  function behaviorLayoutEdgeKinds(kind) {
196030
197078
  return kind === "afv" ? AFV_LAYOUT_EDGE_KINDS : kind === "stv" ? STV_LAYOUT_EDGE_KINDS : void 0;
196031
197079
  }
@@ -201234,6 +202282,10 @@ var TOOLBOX = {
201234
202282
  ],
201235
202283
  relations: [
201236
202284
  rel("transition", "transition", "\u2192", "click the source state, then the target state"),
202285
+ // issue #113 - a machine's action and control nodes are sequenced by
202286
+ // a succession, not by a transition: `transition` needs a state at
202287
+ // both ends (OMG SysML 8.2.2.18.3), while `first a then b` does not.
202288
+ rel("succession", "succession", "\u21E2", "click the source node, then the target node"),
201237
202289
  // issue #115 — nothing could mark the initial state, so a state
201238
202290
  // machine could not be STARTED from a blank canvas. The gesture runs
201239
202291
  // from the always-drawn initial pseudostate to the first state.
@@ -201392,7 +202444,11 @@ function normalizedElementKeyword(keyword) {
201392
202444
  "loop action",
201393
202445
  "for-loop action",
201394
202446
  "perform action",
201395
- "assign"
202447
+ "assign",
202448
+ // issue #113 — a state sub-action drawn as a node is an action node.
202449
+ "entry action",
202450
+ "do action",
202451
+ "exit action"
201396
202452
  ].includes(base)) {
201397
202453
  base = "action";
201398
202454
  } else if (["fork", "join", "merge", "decide", "decision"].includes(base)) {
@@ -201494,7 +202550,12 @@ function relationToolsForNode(keyword, viewKind, shape) {
201494
202550
  if (base === "port") return false;
201495
202551
  return base === "pin" && (tool.kind === "flow" || tool.kind === "successionFlow" || tool.kind === "bind");
201496
202552
  }
201497
- if (viewKind === "stv") return base === "state" && tool.kind !== "initial";
202553
+ if (viewKind === "stv") {
202554
+ if (base === "action" || base === "control") {
202555
+ return tool.kind === "succession" || tool.kind === TERMINAL_RELATION;
202556
+ }
202557
+ return base === "state" && tool.kind !== "initial";
202558
+ }
201498
202559
  if (viewKind === "sv") return shape === "lifeline" && (base === "part" || base === "lifeline");
201499
202560
  if (viewKind === "cv") {
201500
202561
  if (base === "actor") return tool.kind === "actor";
@@ -203957,7 +205018,7 @@ async function runExport(command) {
203957
205018
  }
203958
205019
 
203959
205020
  // src/main.ts
203960
- var VERSION2 = true ? "0.24.0" : "dev";
205021
+ var VERSION2 = true ? "0.26.0" : "dev";
203961
205022
  function display(file) {
203962
205023
  const rel2 = path9.relative(process.cwd(), file);
203963
205024
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;