sysml-diagram 0.24.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
@@ -165264,16 +165264,16 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165264
165264
  };
165265
165265
  const performedActions = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? effectiveMembersOf(part).filter((m) => m.$type === "PerformStmt" && !!nameOf2(m)).map((act) => {
165266
165266
  const pinSource = this.performTargetOf(act, index2) ?? act;
165267
- 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)];
165268
165268
  return {
165269
165269
  act,
165270
165270
  pinSource,
165271
- pathSegments,
165272
- name: pathSegments.at(-1) ?? nameOf2(act),
165271
+ pathSegments: pathSegments2,
165272
+ name: pathSegments2.at(-1) ?? nameOf2(act),
165273
165273
  key: concretePerformPath(act, pinSource)
165274
165274
  };
165275
165275
  }).filter((entry, i, list) => list.findIndex((other) => other.key === entry.key) === i) : [];
165276
- const performedActionInfos = performedActions.map(({ act, pinSource, pathSegments, name, key }) => {
165276
+ const performedActionInfos = performedActions.map(({ act, pinSource, pathSegments: pathSegments2, name, key }) => {
165277
165277
  const id2 = `${instanceId}::__perform_${key}`;
165278
165278
  const inheritedFromDefinition = !!localUsage && !isAstDescendantOrSelf(act, localUsage);
165279
165279
  const syncDeclaration2 = this.synchronizationDeclarationOf(act, index2);
@@ -165281,7 +165281,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165281
165281
  act,
165282
165282
  name,
165283
165283
  id: id2,
165284
- pathSegments,
165284
+ pathSegments: pathSegments2,
165285
165285
  pins: this.actionPinPorts(pinSource, id2, index2, uri),
165286
165286
  meta: {
165287
165287
  ...ivEditMeta(act, id2, void 0, localUsage, [...localPath, name], uri),
@@ -170418,7 +170418,9 @@ var sysmlInlayHintSettings = {
170418
170418
  modifiers: false,
170419
170419
  specialization: false,
170420
170420
  redefinition: false,
170421
- effectiveNames: false
170421
+ effectiveNames: false,
170422
+ importedNames: false,
170423
+ parameterNames: false
170422
170424
  };
170423
170425
 
170424
170426
  // ../language-server/out/src/services/category-taxonomy.js
@@ -181119,6 +181121,329 @@ var SysmlSemanticTokenProvider = class extends AbstractSemanticTokenProvider {
181119
181121
 
181120
181122
  // ../language-server/out/src/services/inlay-hint-provider.js
181121
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
181122
181447
  function multiplicityAnchor(node) {
181123
181448
  const leaves = cst_utils_exports.flattenCst(node).toArray();
181124
181449
  const bodyStart = leaves.find((leaf) => leaf.text === "{");
@@ -181177,6 +181502,50 @@ var EXPLICIT_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
181177
181502
  "conjugates"
181178
181503
  ]);
181179
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"]);
181180
181549
  var IMPLICIT_BASES = Object.freeze({
181181
181550
  PartDecl: { def: "Parts::Part", usage: "Parts::parts" },
181182
181551
  ItemDecl: { def: "Items::Item", usage: "Items::items" },
@@ -181201,6 +181570,37 @@ var IMPLICIT_BASES = Object.freeze({
181201
181570
  OccurrenceDecl: { def: "Occurrences::Occurrence", usage: "Occurrences::occurrences" },
181202
181571
  MetadataDecl: { def: "Metadata::MetadataItem", usage: "Metadata::metadataItems" }
181203
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"]);
181204
181604
  function markdown(value) {
181205
181605
  return { kind: import_vscode_languageserver20.MarkupKind.Markdown, value };
181206
181606
  }
@@ -181222,19 +181622,130 @@ function descriptionRange(description) {
181222
181622
  const segment = description.nameSegment ?? description.selectionSegment;
181223
181623
  return segment?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
181224
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
+ }
181225
181700
  var SysmlInlayHintProvider = class {
181226
181701
  services;
181227
181702
  /**
181228
181703
  * 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.
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.
181234
181710
  */
181235
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();
181236
181739
  constructor(services) {
181237
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
+ });
181238
181749
  }
181239
181750
  getInlayHints(document2, _params) {
181240
181751
  const root4 = document2.parseResult?.value;
@@ -181242,7 +181753,14 @@ var SysmlInlayHintProvider = class {
181242
181753
  return void 0;
181243
181754
  const settings = sysmlInlayHintSettings;
181244
181755
  const hints = [];
181245
- 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) {
181246
181764
  const decl = node;
181247
181765
  if (settings.multiplicity && (isPartDecl(node) || isPortDecl(node) || isAttributeDecl(node)) && !decl.isDef && !decl.multiplicity && node.$cstNode && decl.name) {
181248
181766
  hints.push({
@@ -181269,6 +181787,11 @@ var SysmlInlayHintProvider = class {
181269
181787
  });
181270
181788
  }
181271
181789
  }
181790
+ if (settings.modifiers) {
181791
+ const constant2 = this.implicitConstantHint(decl);
181792
+ if (constant2)
181793
+ hints.push(constant2);
181794
+ }
181272
181795
  if (settings.modifiers && isAttributeDecl(node) && !decl.isDef && !(decl.modifiers ?? []).includes("ref")) {
181273
181796
  const keyword = keywordLeaf(node, "attribute");
181274
181797
  if (keyword) {
@@ -181310,6 +181833,17 @@ var SysmlInlayHintProvider = class {
181310
181833
  if (effective)
181311
181834
  hints.push(effective);
181312
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
+ }
181313
181847
  if (!settings.dimension)
181314
181848
  continue;
181315
181849
  if (isNumericPrimary(node)) {
@@ -181379,6 +181913,228 @@ var SysmlInlayHintProvider = class {
181379
181913
  }
181380
181914
  return hints;
181381
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
+ }
181382
182138
  /**
181383
182139
  * REQ-395 — Find one implicit base in the index.
181384
182140
  *
@@ -181392,25 +182148,19 @@ var SysmlInlayHintProvider = class {
181392
182148
  const cached = this.baseCache.get(qualified);
181393
182149
  if (cached)
181394
182150
  return cached;
181395
- const index2 = this.services?.shared.workspace.IndexManager;
181396
- if (!index2)
182151
+ if (!this.lookup)
181397
182152
  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;
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) => {
181407
182159
  const uri = description.documentUri instanceof URI2 ? description.documentUri : URI2.parse(String(description.documentUri));
181408
182160
  if (!isStandardLibraryUri(uri))
181409
- continue;
181410
- if (description.isUsage === true !== wantUsage)
181411
- continue;
181412
- fallback = description;
181413
- }
182161
+ return false;
182162
+ return description.isUsage === true === wantUsage;
182163
+ });
181414
182164
  if (fallback)
181415
182165
  this.baseCache.set(qualified, fallback);
181416
182166
  return fallback;
@@ -203957,7 +204707,7 @@ async function runExport(command) {
203957
204707
  }
203958
204708
 
203959
204709
  // src/main.ts
203960
- var VERSION2 = true ? "0.24.0" : "dev";
204710
+ var VERSION2 = true ? "0.25.0" : "dev";
203961
204711
  function display(file) {
203962
204712
  const rel2 = path9.relative(process.cwd(), file);
203963
204713
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;