sysml-validate 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
@@ -57970,7 +57970,9 @@ var sysmlInlayHintSettings = {
57970
57970
  modifiers: false,
57971
57971
  specialization: false,
57972
57972
  redefinition: false,
57973
- effectiveNames: false
57973
+ effectiveNames: false,
57974
+ importedNames: false,
57975
+ parameterNames: false
57974
57976
  };
57975
57977
  function asDiagnosticLevel(value, fallback = "off") {
57976
57978
  return value === "off" || value === "warning" || value === "error" ? value : fallback;
@@ -69973,6 +69975,329 @@ var SysmlSemanticTokenProvider = class extends AbstractSemanticTokenProvider {
69973
69975
 
69974
69976
  // ../language-server/out/src/services/inlay-hint-provider.js
69975
69977
  var import_vscode_languageserver20 = __toESM(require_main4(), 1);
69978
+
69979
+ // ../language-server/out/src/services/name-lookup.js
69980
+ function unquoteName3(name) {
69981
+ return name.replace(/^'(.*)'$/u, "$1");
69982
+ }
69983
+ function pathSegments(path10) {
69984
+ const segments = [];
69985
+ let current2 = "";
69986
+ let quoted = false;
69987
+ for (let index = 0; index < path10.length; index++) {
69988
+ const char = path10[index];
69989
+ if (char === "'") {
69990
+ quoted = !quoted;
69991
+ current2 += char;
69992
+ continue;
69993
+ }
69994
+ if (!quoted && char === ":" && path10[index + 1] === ":") {
69995
+ segments.push(current2);
69996
+ current2 = "";
69997
+ index++;
69998
+ continue;
69999
+ }
70000
+ if (!quoted && char === ".") {
70001
+ segments.push(current2);
70002
+ current2 = "";
70003
+ continue;
70004
+ }
70005
+ current2 += char;
70006
+ }
70007
+ segments.push(current2);
70008
+ return segments;
70009
+ }
70010
+ function simpleNameOf2(name) {
70011
+ return pathSegments(name).at(-1) ?? name;
70012
+ }
70013
+ var ELEMENT_SEPARATOR = "\0";
70014
+ function elementKey2(description) {
70015
+ return `${description.documentUri.toString()}${ELEMENT_SEPARATOR}${description.path}`;
70016
+ }
70017
+ function isDeclaredSpelling(description) {
70018
+ const kind = description.derivedKind;
70019
+ if (kind === "reexport" || kind === "inherited")
70020
+ return false;
70021
+ return pathSegments(description.name).length === 1;
70022
+ }
70023
+ var SysmlNameLookup = class {
70024
+ shared;
70025
+ byName;
70026
+ /** Element key → the declared spellings the index holds for that element. */
70027
+ spellings;
70028
+ constructor(shared) {
70029
+ this.shared = shared;
70030
+ this.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, () => {
70031
+ this.byName = void 0;
70032
+ this.spellings = void 0;
70033
+ });
70034
+ }
70035
+ /** Every description indexed under `name`, in either spelling of an escaped name. */
70036
+ descriptions(name) {
70037
+ return this.index().get(name) ?? [];
70038
+ }
70039
+ /**
70040
+ * The ONE element `name` names, or `undefined` when the answer is not
70041
+ * certain: no element, or more than one. `accept` narrows the candidates
70042
+ * before ambiguity is judged, so "the only CALLABLE called `f`" is a
70043
+ * decidable question even where a part shares the name.
70044
+ */
70045
+ unique(name, accept) {
70046
+ const candidates = this.descriptions(name).filter((candidate) => !accept || accept(candidate));
70047
+ let found;
70048
+ let key;
70049
+ for (const candidate of candidates) {
70050
+ const candidateKey = elementKey2(candidate);
70051
+ if (found === void 0) {
70052
+ found = candidate;
70053
+ key = candidateKey;
70054
+ continue;
70055
+ }
70056
+ if (candidateKey !== key)
70057
+ return void 0;
70058
+ }
70059
+ return found;
70060
+ }
70061
+ /**
70062
+ * Resolve a written path: the spelling the index holds verbatim first, then
70063
+ * its final segment. Both readings must name exactly one element.
70064
+ */
70065
+ uniqueForPath(path10, accept) {
70066
+ return this.unique(path10, accept) ?? this.unique(simpleNameOf2(path10), accept);
70067
+ }
70068
+ /**
70069
+ * The declared spellings of the element `description` names — its regular
70070
+ * name and its `<short>` name — minus `written`.
70071
+ */
70072
+ otherNames(description, written) {
70073
+ const all = this.spellingIndex().get(elementKey2(description)) ?? [];
70074
+ const seen = unquoteName3(written);
70075
+ return all.filter((name) => unquoteName3(name) !== seen);
70076
+ }
70077
+ index() {
70078
+ if (!this.byName)
70079
+ this.build();
70080
+ return this.byName;
70081
+ }
70082
+ spellingIndex() {
70083
+ if (!this.spellings)
70084
+ this.build();
70085
+ return this.spellings;
70086
+ }
70087
+ /** One pass over the index feeds both maps; neither is worth a second. */
70088
+ build() {
70089
+ const byName = /* @__PURE__ */ new Map();
70090
+ const spellings = /* @__PURE__ */ new Map();
70091
+ const add = (key, description) => {
70092
+ const bucket = byName.get(key);
70093
+ if (bucket)
70094
+ bucket.push(description);
70095
+ else
70096
+ byName.set(key, [description]);
70097
+ };
70098
+ for (const description of this.shared.workspace.IndexManager.allElements()) {
70099
+ add(description.name, description);
70100
+ const unquoted = unquoteName3(description.name);
70101
+ if (unquoted !== description.name)
70102
+ add(unquoted, description);
70103
+ if (!isDeclaredSpelling(description))
70104
+ continue;
70105
+ const key = elementKey2(description);
70106
+ const names = spellings.get(key);
70107
+ if (names) {
70108
+ if (!names.includes(description.name))
70109
+ names.push(description.name);
70110
+ } else {
70111
+ spellings.set(key, [description.name]);
70112
+ }
70113
+ }
70114
+ this.byName = byName;
70115
+ this.spellings = spellings;
70116
+ }
70117
+ };
70118
+ var lookups = /* @__PURE__ */ new WeakMap();
70119
+ function nameLookupFor(shared) {
70120
+ if (!shared)
70121
+ return void 0;
70122
+ const existing = lookups.get(shared);
70123
+ if (existing)
70124
+ return existing;
70125
+ const created = new SysmlNameLookup(shared);
70126
+ lookups.set(shared, created);
70127
+ return created;
70128
+ }
70129
+
70130
+ // ../language-server/out/src/services/callable-resolver.js
70131
+ var CALLABLE_TYPES = /* @__PURE__ */ new Set([
70132
+ "CalcDecl",
70133
+ "ActionDecl",
70134
+ "ConstraintDecl",
70135
+ "FunctionDecl",
70136
+ "PredicateDecl",
70137
+ "BehaviorDecl",
70138
+ "InteractionDecl"
70139
+ ]);
70140
+ var CallableResolver = class {
70141
+ services;
70142
+ lookup;
70143
+ linker;
70144
+ constructor(services) {
70145
+ this.services = services;
70146
+ this.lookup = nameLookupFor(services?.shared);
70147
+ this.linker = services?.references.Linker;
70148
+ }
70149
+ /**
70150
+ * Every indexed callable that answers to `name`, so a caller can decide what
70151
+ * ambiguity costs it. Signature help is transient and follows the cursor, so
70152
+ * it takes the first; an inlay hint is a permanent annotation, so it takes a
70153
+ * candidate only when it is the ONLY element with that name.
70154
+ */
70155
+ candidates(name) {
70156
+ const simple = simpleNameOf2(name);
70157
+ return (this.lookup?.descriptions(simple) ?? []).filter((description) => CALLABLE_TYPES.has(description.type));
70158
+ }
70159
+ /**
70160
+ * The one callable `name` unambiguously names in the index, if any: the
70161
+ * written spelling first, then its final segment, and either reading must
70162
+ * name exactly one element.
70163
+ */
70164
+ uniqueCandidate(name) {
70165
+ return this.lookup?.uniqueForPath(name, (description) => CALLABLE_TYPES.has(description.type));
70166
+ }
70167
+ /** The callable declaration `name` invokes in `document`, resolved locally. */
70168
+ findLocal(document, name) {
70169
+ const root3 = document.parseResult?.value;
70170
+ if (!root3)
70171
+ return void 0;
70172
+ return collectCallables([root3, ...ast_utils_exports.streamAllContents(root3).toArray()]).get(unquoteName3(simpleNameOf2(name)));
70173
+ }
70174
+ /**
70175
+ * SYNCHRONOUS resolution: a qualified spelling the index holds verbatim,
70176
+ * then the document's own tree, then an unambiguous indexed callable —
70177
+ * resolved through the linker's off-to-the-side library parse. Nothing here
70178
+ * grows `LangiumDocuments`, and nothing here awaits, so it is safe on a
70179
+ * request VS Code re-issues on every scroll.
70180
+ */
70181
+ resolve(document, name, local) {
70182
+ if (name.includes("::") || name.includes(".")) {
70183
+ const exact = this.lookup?.unique(name, (description) => CALLABLE_TYPES.has(description.type));
70184
+ const node = exact ? this.nodeOf(exact) : void 0;
70185
+ if (node)
70186
+ return node;
70187
+ }
70188
+ const found = local ? local(unquoteName3(simpleNameOf2(name))) : this.findLocal(document, name);
70189
+ if (found)
70190
+ return found;
70191
+ const candidate = this.uniqueCandidate(name);
70192
+ return candidate ? this.nodeOf(candidate) : void 0;
70193
+ }
70194
+ /** An indexed description as a node, through the linker's lazy library parse. */
70195
+ nodeOf(description) {
70196
+ return this.linker?.resolveIndexedNode?.(description) ?? description.node;
70197
+ }
70198
+ /**
70199
+ * Resolution that may load a workspace document. Signature help uses it: it
70200
+ * runs on an explicit editor gesture, not on every scroll, and a callable in
70201
+ * a workspace file the user has not opened yet still deserves a signature.
70202
+ */
70203
+ // REQ-263 — Signature help on `(` / `,` / `->`
70204
+ async resolveAsync(document, name) {
70205
+ const direct = this.resolve(document, name);
70206
+ if (direct)
70207
+ return direct;
70208
+ const candidate = this.candidates(name).at(0);
70209
+ if (!candidate)
70210
+ return void 0;
70211
+ if (candidate.node)
70212
+ return candidate.node;
70213
+ const documents = this.services?.shared.workspace.LangiumDocuments;
70214
+ const locator = this.services?.workspace.AstNodeLocator;
70215
+ if (!documents || !locator)
70216
+ return void 0;
70217
+ const doc = documents.getDocument(candidate.documentUri) ?? await documents.getOrCreateDocument(candidate.documentUri);
70218
+ const root3 = doc?.parseResult?.value;
70219
+ return root3 ? locator.getAstNode(root3, candidate.path) : void 0;
70220
+ }
70221
+ /** {@link callableParameters} — the parameters, in written order. */
70222
+ parameters(node) {
70223
+ return callableParameters(node);
70224
+ }
70225
+ /**
70226
+ * The parameters a positional argument list binds to: the INPUTS, in written
70227
+ * order. An `out` parameter and the result are not written at the call site
70228
+ * (OMG KerML v1.0 binds an `InvocationExpression`'s arguments to the
70229
+ * invoked type's input features).
70230
+ */
70231
+ inputParameters(node) {
70232
+ return this.parameters(node).filter((parameter) => !parameter.isReturn && parameter.direction !== "out");
70233
+ }
70234
+ };
70235
+ function callableParameters(node) {
70236
+ const directed = new Map(directedParameters(node).map((entry) => [entry.node, entry.direction]));
70237
+ const result = [];
70238
+ for (const member of membersOf(node)) {
70239
+ const direction = directed.get(member);
70240
+ const isReturn = member.$type === "ReturnDecl";
70241
+ if (!direction && !isReturn)
70242
+ continue;
70243
+ const name = nodeName2(member);
70244
+ if (!name && !direction)
70245
+ continue;
70246
+ result.push({
70247
+ node: member,
70248
+ direction: direction ?? "return",
70249
+ name,
70250
+ type: typingText(member),
70251
+ multiplicity: multiplicityText(member),
70252
+ isReturn
70253
+ });
70254
+ }
70255
+ return result;
70256
+ }
70257
+ function collectCallables(nodes) {
70258
+ const callables = /* @__PURE__ */ new Map();
70259
+ const byDefinition = /* @__PURE__ */ new Set();
70260
+ for (const node of nodes) {
70261
+ if (!CALLABLE_TYPES.has(node.$type) && callableParameters(node).length === 0)
70262
+ continue;
70263
+ const isDefinition = node.isDef === true;
70264
+ for (const name of [nodeName2(node), shortNameOf2(node)]) {
70265
+ if (!name)
70266
+ continue;
70267
+ if (callables.has(name) && !(isDefinition && !byDefinition.has(name)))
70268
+ continue;
70269
+ callables.set(name, node);
70270
+ if (isDefinition)
70271
+ byDefinition.add(name);
70272
+ }
70273
+ }
70274
+ return callables;
70275
+ }
70276
+ function membersOf(node) {
70277
+ const members = node?.members;
70278
+ return Array.isArray(members) ? members.filter(isAstNode3) : [];
70279
+ }
70280
+ function isAstNode3(value) {
70281
+ return typeof value === "object" && value !== null && typeof value.$type === "string";
70282
+ }
70283
+ function nodeName2(node) {
70284
+ const value = node?.name;
70285
+ return typeof value === "string" && value.length > 0 ? unquoteName3(value) : void 0;
70286
+ }
70287
+ function shortNameOf2(node) {
70288
+ const value = node?.shortName?.name;
70289
+ return typeof value === "string" && value.length > 0 ? unquoteName3(value) : void 0;
70290
+ }
70291
+ function typingText(node) {
70292
+ const text = node?.typing?.type?.$refText;
70293
+ return typeof text === "string" && text.length > 0 ? text : void 0;
70294
+ }
70295
+ function multiplicityText(node) {
70296
+ const text = node?.multiplicity?.$cstNode?.text;
70297
+ return typeof text === "string" && text.length > 0 ? text.trim() : void 0;
70298
+ }
70299
+
70300
+ // ../language-server/out/src/services/inlay-hint-provider.js
69976
70301
  function multiplicityAnchor(node) {
69977
70302
  const leaves = cst_utils_exports.flattenCst(node).toArray();
69978
70303
  const bodyStart = leaves.find((leaf) => leaf.text === "{");
@@ -70031,6 +70356,50 @@ var EXPLICIT_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
70031
70356
  "conjugates"
70032
70357
  ]);
70033
70358
  var REDEFINITION_KINDS2 = /* @__PURE__ */ new Set([":>>", "redefines"]);
70359
+ var CONSTANT_CARRYING_KINDS = /* @__PURE__ */ new Set([
70360
+ ":>",
70361
+ "subsets",
70362
+ ":>>",
70363
+ "redefines",
70364
+ "::>",
70365
+ "references",
70366
+ "=>",
70367
+ "crosses"
70368
+ ]);
70369
+ var CONSTANT_MODIFIERS = /* @__PURE__ */ new Set(["constant", "const"]);
70370
+ function constantKeyword(languageId) {
70371
+ return languageId === "kerml" ? "const" : "constant";
70372
+ }
70373
+ var PREFIXES_BEFORE_CONSTANT = /* @__PURE__ */ new Set([
70374
+ "public",
70375
+ "private",
70376
+ "protected",
70377
+ "in",
70378
+ "out",
70379
+ "inout",
70380
+ "abstract",
70381
+ "variation",
70382
+ "variant",
70383
+ "derived",
70384
+ "readonly",
70385
+ "composite",
70386
+ "portion",
70387
+ "ordered",
70388
+ "nonunique",
70389
+ "parallel"
70390
+ ]);
70391
+ var PORTION_MODIFIERS = /* @__PURE__ */ new Set(["portion", "snapshot", "timeslice"]);
70392
+ var ACTION_KIND_USAGES = /* @__PURE__ */ new Set([
70393
+ "ActionDecl",
70394
+ "StateDecl",
70395
+ "CalcDecl",
70396
+ "CaseDecl",
70397
+ "UseCaseDecl",
70398
+ "AnalysisCaseDecl",
70399
+ "VerificationCaseDecl"
70400
+ ]);
70401
+ var KERML_FEATURE_DECLS = /* @__PURE__ */ new Set(["FeatureDecl", "StepDecl", "ExpressionDecl"]);
70402
+ var REFERENCE_MODIFIERS = /* @__PURE__ */ new Set(["ref", "in", "out", "inout"]);
70034
70403
  var IMPLICIT_BASES = Object.freeze({
70035
70404
  PartDecl: { def: "Parts::Part", usage: "Parts::parts" },
70036
70405
  ItemDecl: { def: "Items::Item", usage: "Items::items" },
@@ -70055,6 +70424,37 @@ var IMPLICIT_BASES = Object.freeze({
70055
70424
  OccurrenceDecl: { def: "Occurrences::Occurrence", usage: "Occurrences::occurrences" },
70056
70425
  MetadataDecl: { def: "Metadata::MetadataItem", usage: "Metadata::metadataItems" }
70057
70426
  });
70427
+ var OCCURRENCE_KIND_OWNERS = /* @__PURE__ */ new Set([
70428
+ ...Object.keys(IMPLICIT_BASES).filter((kind) => kind !== "AttributeDecl"),
70429
+ // KerML fixes a library base per classifier kind too, in the same way and in
70430
+ // the same normative place: `Class` specializes `Occurrences::Occurrence`,
70431
+ // `Structure` specializes `Objects::Object`, `Metaclass` specializes
70432
+ // `Metaobjects::Metaobject`, `Behavior` specializes
70433
+ // `Performances::Performance`, `Function` and `Interaction` are Behaviors,
70434
+ // `Predicate` is a Function, `Step` specializes `Performances::performances`
70435
+ // and `Expression` is a Step. All of them reach `Occurrence`.
70436
+ "ClassDecl",
70437
+ "StructDecl",
70438
+ "MetaclassDecl",
70439
+ "BehaviorDecl",
70440
+ "InteractionDecl",
70441
+ "FunctionDecl",
70442
+ "PredicateDecl",
70443
+ "StepDecl",
70444
+ "ExpressionDecl"
70445
+ // Left out because their base is NOT an occurrence: `DatatypeDecl`
70446
+ // (`Base::DataValue`, declared disjoint from `Occurrence`) and plain
70447
+ // `AssociationDecl` (`Links::Link`, which specializes `Base::Anything`) —
70448
+ // `assoc struct` is handled separately, since it is a `LinkObject`.
70449
+ // Left out because their base is WRITTEN rather than fixed by the kind:
70450
+ // `TypeDecl`, `ClassifierDecl`, `FeatureDecl`.
70451
+ ]);
70452
+ function isOccurrenceKindOwner(owner) {
70453
+ if (owner.$type === "AssociationDecl")
70454
+ return owner.isStruct === true;
70455
+ return OCCURRENCE_KIND_OWNERS.has(owner.$type);
70456
+ }
70457
+ var NON_VARYING_LIBRARY_TYPES = /* @__PURE__ */ new Set(["SelfLink", "HappensLink"]);
70058
70458
  function markdown(value) {
70059
70459
  return { kind: import_vscode_languageserver20.MarkupKind.Markdown, value };
70060
70460
  }
@@ -70076,19 +70476,130 @@ function descriptionRange(description) {
70076
70476
  const segment = description.nameSegment ?? description.selectionSegment;
70077
70477
  return segment?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
70078
70478
  }
70479
+ function descriptionLocation(description) {
70480
+ return { uri: description.documentUri.toString(), range: descriptionRange(description) };
70481
+ }
70482
+ function nodeLocation(node) {
70483
+ const range = node.$cstNode?.range;
70484
+ if (!range)
70485
+ return void 0;
70486
+ return { uri: ast_utils_exports.getDocument(node).uri.toString(), range };
70487
+ }
70488
+ function specializesNonVaryingLibraryType(node) {
70489
+ const typing = node.typing?.type?.$refText;
70490
+ if (typing && NON_VARYING_LIBRARY_TYPES.has(lastSegment3(typing)))
70491
+ return true;
70492
+ return allRelationships2(node).flatMap((rel) => rel.targets ?? []).some((target) => NON_VARYING_LIBRARY_TYPES.has(lastSegment3(target)));
70493
+ }
70494
+ function constantAnchor(node) {
70495
+ const cst = node.$cstNode;
70496
+ if (!cst)
70497
+ return void 0;
70498
+ for (const leaf of cst_utils_exports.flattenCst(cst)) {
70499
+ if (leaf.hidden)
70500
+ continue;
70501
+ if (PREFIXES_BEFORE_CONSTANT.has(leaf.text))
70502
+ continue;
70503
+ return /^[a-z]+$/u.test(leaf.text) ? leaf : void 0;
70504
+ }
70505
+ return void 0;
70506
+ }
70507
+ function membershipImportPath(node) {
70508
+ if (isImport(node) && node.alias)
70509
+ return void 0;
70510
+ if (isExposePath(node) && node.dotSegs.length > 0)
70511
+ return void 0;
70512
+ if (node.segs.some((seg) => seg.star || seg.recursive || !seg.name))
70513
+ return void 0;
70514
+ const last2 = node.segs.at(-1);
70515
+ const written = last2?.name ?? node.head;
70516
+ const anchor = last2 ? grammar_utils_exports.findNodeForProperty(last2.$cstNode, "name") : grammar_utils_exports.findNodeForProperty(node.$cstNode, "head");
70517
+ if (!anchor)
70518
+ return void 0;
70519
+ return { path: [node.head, ...node.segs.map((seg) => seg.name)].join("::"), written, anchor };
70520
+ }
70521
+ function publicFeatures(type) {
70522
+ const members = type.members;
70523
+ if (!Array.isArray(members))
70524
+ return [];
70525
+ return members.filter((member) => {
70526
+ if (typeof member?.$type !== "string")
70527
+ return false;
70528
+ const decl = member;
70529
+ if (decl.isDef !== false && decl.$type !== "FeatureDecl")
70530
+ return false;
70531
+ return decl.visibility === void 0 || decl.visibility === "public";
70532
+ });
70533
+ }
70534
+ function calleeName(node) {
70535
+ if (node.call) {
70536
+ if (isPathExpr(node.target))
70537
+ return node.target.path;
70538
+ const target = node.target;
70539
+ if (isPostfixOp(target) && target.dot && target.field)
70540
+ return target.field;
70541
+ return void 0;
70542
+ }
70543
+ if (node.arrow && node.invoke)
70544
+ return node.invoke;
70545
+ return void 0;
70546
+ }
70547
+ function callArguments(node) {
70548
+ if (node.call)
70549
+ return { args: node.callArgs, firstParameter: 0 };
70550
+ if (node.arrow && node.invoke)
70551
+ return { args: node.args, firstParameter: 1 };
70552
+ return void 0;
70553
+ }
70079
70554
  var SysmlInlayHintProvider = class {
70080
70555
  services;
70081
70556
  /**
70082
70557
  * Resolved implicit bases, keyed by the qualified name in
70083
- * {@link IMPLICIT_BASES}. The standard library does not change once it is
70084
- * loaded, so this is built at most once for real; while it is still empty —
70085
- * before the library has been indexed the rebuild is over a tiny index and
70086
- * costs nothing, which is what keeps the hint path free of a full index scan
70087
- * on every request.
70558
+ * {@link IMPLICIT_BASES}. A base the index does not hold is NOT cached — the
70559
+ * library may still be loading and issue #240 made that miss cheap: two
70560
+ * lookups in the shared name index rather than the full index scan it used
70561
+ * to be, on a request VS Code re-issues on every scroll. A HIT is cached
70562
+ * until the index is rebuilt, so the description a label links to always
70563
+ * carries the ranges of the generation it was read from.
70088
70564
  */
70089
70565
  baseCache = /* @__PURE__ */ new Map();
70566
+ /**
70567
+ * REQ-395 — issue #240: the shared name lookup, built once per index
70568
+ * generation. Every category that resolves a WRITTEN name goes through it,
70569
+ * so no request puts an index scan on the hint path.
70570
+ */
70571
+ lookup;
70572
+ /** REQ-395 — issue #240: the callee resolution signature help also uses. */
70573
+ callables;
70574
+ /** REQ-395 — issue #240: the linker's lazy standard-library parse. */
70575
+ linker;
70576
+ /**
70577
+ * REQ-395 — issue #240: "does this element declare `constant`?", memoized by
70578
+ * element identity, because answering it may cost one lazily parsed library
70579
+ * file. Editing away the `constant` on the SUBSETTED feature changes the
70580
+ * answer without changing the key, so this is dropped whenever the index is
70581
+ * rebuilt, exactly like the shared name lookup.
70582
+ */
70583
+ constantCache = /* @__PURE__ */ new Map();
70584
+ /**
70585
+ * REQ-395 — issue #240: an element's own written names, memoized by element
70586
+ * identity. Reading them tells a `<short>` name from a regular one, which is
70587
+ * what lets the imported-name hint print the notation the source would have
70588
+ * written. It costs at most one lazily parsed library file, only for an
70589
+ * element that actually has two names, and it is dropped with the rest when
70590
+ * the index is rebuilt.
70591
+ */
70592
+ declaredNameCache = /* @__PURE__ */ new Map();
70090
70593
  constructor(services) {
70091
70594
  this.services = services;
70595
+ this.lookup = nameLookupFor(services?.shared);
70596
+ this.callables = new CallableResolver(services);
70597
+ this.linker = services?.references.Linker;
70598
+ services?.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.IndexedContent, () => {
70599
+ this.baseCache.clear();
70600
+ this.constantCache.clear();
70601
+ this.declaredNameCache.clear();
70602
+ });
70092
70603
  }
70093
70604
  getInlayHints(document, _params) {
70094
70605
  const root3 = document.parseResult?.value;
@@ -70096,7 +70607,14 @@ var SysmlInlayHintProvider = class {
70096
70607
  return void 0;
70097
70608
  const settings = sysmlInlayHintSettings;
70098
70609
  const hints = [];
70099
- for (const node of allAstNodes(root3)) {
70610
+ const nodes = allAstNodes(root3);
70611
+ let localCallables;
70612
+ const localCallable = (simple) => {
70613
+ localCallables ??= collectCallables(nodes);
70614
+ return localCallables.get(simple);
70615
+ };
70616
+ const callees = /* @__PURE__ */ new Map();
70617
+ for (const node of nodes) {
70100
70618
  const decl = node;
70101
70619
  if (settings.multiplicity && (isPartDecl(node) || isPortDecl(node) || isAttributeDecl(node)) && !decl.isDef && !decl.multiplicity && node.$cstNode && decl.name) {
70102
70620
  hints.push({
@@ -70123,6 +70641,11 @@ var SysmlInlayHintProvider = class {
70123
70641
  });
70124
70642
  }
70125
70643
  }
70644
+ if (settings.modifiers) {
70645
+ const constant2 = this.implicitConstantHint(decl);
70646
+ if (constant2)
70647
+ hints.push(constant2);
70648
+ }
70126
70649
  if (settings.modifiers && isAttributeDecl(node) && !decl.isDef && !(decl.modifiers ?? []).includes("ref")) {
70127
70650
  const keyword = keywordLeaf(node, "attribute");
70128
70651
  if (keyword) {
@@ -70164,6 +70687,17 @@ var SysmlInlayHintProvider = class {
70164
70687
  if (effective)
70165
70688
  hints.push(effective);
70166
70689
  }
70690
+ if (settings.importedNames && (isImport(node) || isExposePath(node))) {
70691
+ const imported = this.importedNameHint(node);
70692
+ if (imported)
70693
+ hints.push(imported);
70694
+ }
70695
+ if (settings.parameterNames && isPostfixOp(node)) {
70696
+ hints.push(...this.argumentNameHints(node, document, callees, localCallable));
70697
+ }
70698
+ if (settings.parameterNames && isNewExpr(node)) {
70699
+ hints.push(...this.constructorArgumentHints(node));
70700
+ }
70167
70701
  if (!settings.dimension)
70168
70702
  continue;
70169
70703
  if (isNumericPrimary(node)) {
@@ -70233,6 +70767,228 @@ var SysmlInlayHintProvider = class {
70233
70767
  }
70234
70768
  return hints;
70235
70769
  }
70770
+ /**
70771
+ * REQ-395 — issue #240: the other name a membership `import` brings in.
70772
+ *
70773
+ * An element may declare both a regular name and a `<short>` name, and an
70774
+ * import brings in BOTH — the one the path does not write is invisible in
70775
+ * the source, which is exactly what the hint is for. The written path is a
70776
+ * datatype string, not a cross-reference, so the element is found through
70777
+ * the shared name lookup: the spelling the index holds verbatim first, then
70778
+ * the final segment, and either reading must name exactly ONE element. A
70779
+ * name two packages both declare yields nothing rather than whichever the
70780
+ * index lists first.
70781
+ */
70782
+ importedNameHint(node) {
70783
+ const written = membershipImportPath(node);
70784
+ if (!written || !this.lookup)
70785
+ return void 0;
70786
+ const description = this.lookup.uniqueForPath(written.path);
70787
+ if (!description)
70788
+ return void 0;
70789
+ const others = this.lookup.otherNames(description, written.written);
70790
+ if (others.length === 0)
70791
+ return void 0;
70792
+ const declared = this.declaredNames(description);
70793
+ const shown = others.map((name) => declared?.shortName === name ? `<${name}>` : name);
70794
+ const kind = isImport(node) ? "import" : "expose";
70795
+ return {
70796
+ position: written.anchor.range.end,
70797
+ label: [{
70798
+ value: ` also ${shown.join(", ")}`,
70799
+ location: descriptionLocation(description)
70800
+ }],
70801
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
70802
+ paddingLeft: true,
70803
+ 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.`)
70804
+ };
70805
+ }
70806
+ /**
70807
+ * REQ-395 — issue #240: the names an indexed element declares for itself.
70808
+ *
70809
+ * The index records the spellings but not which of them is the `<short>`
70810
+ * one, so the declaration is resolved through the linker's off-to-the-side
70811
+ * library parse — the budget REQ-251 sets, spent only for an element that
70812
+ * has more than one name, and memoized until the index is rebuilt.
70813
+ */
70814
+ declaredNames(description) {
70815
+ const key = elementKey2(description);
70816
+ const cached = this.declaredNameCache.get(key);
70817
+ if (cached)
70818
+ return cached;
70819
+ const node = this.linker?.resolveIndexedNode?.(description) ?? description.node;
70820
+ if (!node)
70821
+ return void 0;
70822
+ const decl = node;
70823
+ const names = { name: decl.name, shortName: decl.shortName?.name };
70824
+ this.declaredNameCache.set(key, names);
70825
+ return names;
70826
+ }
70827
+ /**
70828
+ * REQ-395 — issue #240: the feature each `new Type(…)` argument binds to.
70829
+ *
70830
+ * A `ConstructorExpression` binds its arguments to the PUBLIC features of
70831
+ * the instantiated type, in order (OMG KerML v1.0, `ConstructorExpression`)
70832
+ * — not to input parameters, which is what an `InvocationExpression` binds.
70833
+ * The instantiated type IS a cross-reference here, so there is nothing to
70834
+ * resolve by name: an unresolved one simply yields no hints.
70835
+ */
70836
+ constructorArgumentHints(node) {
70837
+ if (node.args.length === 0)
70838
+ return [];
70839
+ if (node.args.every((argument) => argument.name !== void 0))
70840
+ return [];
70841
+ const instantiated = node.type?.ref;
70842
+ if (!instantiated)
70843
+ return [];
70844
+ const features = publicFeatures(instantiated);
70845
+ if (features.length === 0)
70846
+ return [];
70847
+ const hints = [];
70848
+ for (let index = 0; index < node.args.length; index++) {
70849
+ const argument = node.args[index];
70850
+ if (argument.name || !argument.$cstNode)
70851
+ continue;
70852
+ const feature = features[index];
70853
+ const name = feature?.name;
70854
+ if (!feature || !name)
70855
+ continue;
70856
+ const location = nodeLocation(feature);
70857
+ hints.push({
70858
+ position: argument.$cstNode.range.start,
70859
+ label: [{ value: `${name} =`, ...location ? { location } : {} }],
70860
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
70861
+ paddingRight: true,
70862
+ tooltip: markdown(`Argument ${index + 1} binds to \`${name}\` of \`${node.type.$refText}\`.`)
70863
+ });
70864
+ }
70865
+ return hints;
70866
+ }
70867
+ /**
70868
+ * REQ-395 — issue #240: the parameter a POSITIONAL invocation argument binds
70869
+ * to.
70870
+ *
70871
+ * The callee of a call is an expression rather than a cross-reference, so
70872
+ * the name is resolved through the shared {@link CallableResolver} — the
70873
+ * same one signature help uses, which is why naming these arguments does not
70874
+ * add a second resolution path. An argument that writes its own name has
70875
+ * stated the correspondence, so it gets no hint.
70876
+ */
70877
+ argumentNameHints(node, document, callees, localCallable) {
70878
+ const call = callArguments(node);
70879
+ const callee = calleeName(node);
70880
+ if (!call || !callee || call.args.length === 0)
70881
+ return [];
70882
+ if (call.args.every((argument) => argument.argName !== void 0))
70883
+ return [];
70884
+ let callable = callees.get(callee);
70885
+ if (callable === void 0 && !callees.has(callee)) {
70886
+ callable = this.callables.resolve(document, callee, localCallable);
70887
+ callees.set(callee, callable);
70888
+ }
70889
+ if (!callable)
70890
+ return [];
70891
+ const parameters = this.callables.inputParameters(callable);
70892
+ if (parameters.length === 0)
70893
+ return [];
70894
+ const hints = [];
70895
+ for (let index = 0; index < call.args.length; index++) {
70896
+ const argument = call.args[index];
70897
+ if (argument.argName || !argument.$cstNode)
70898
+ continue;
70899
+ const parameter = parameters[index + call.firstParameter];
70900
+ if (!parameter?.name)
70901
+ continue;
70902
+ const location = nodeLocation(parameter.node);
70903
+ hints.push({
70904
+ position: argument.$cstNode.range.start,
70905
+ label: [{ value: `${parameter.name} =`, ...location ? { location } : {} }],
70906
+ kind: import_vscode_languageserver20.InlayHintKind.Parameter,
70907
+ paddingRight: true,
70908
+ tooltip: markdown(`Argument ${index + 1} binds to \`${parameter.direction} ${parameter.name}\` of \`${callee}\`.`)
70909
+ });
70910
+ }
70911
+ return hints;
70912
+ }
70913
+ /**
70914
+ * REQ-395 — issue #240: implicit `constant`.
70915
+ *
70916
+ * OMG KerML v1.0 constrains `Subsetting`: `subsettedFeature.isConstant and
70917
+ * subsettingFeature.isVariable implies subsettingFeature.isConstant`. So a
70918
+ * usage that subsets or redefines a feature declared `constant` IS constant,
70919
+ * without writing it — provided it can vary at all, because `Feature` also
70920
+ * constrains `isConstant implies isVariable`.
70921
+ *
70922
+ * Whether it can vary is `Usage::mayTimeVary`, which OMG SysML v2 Part 1
70923
+ * derives as "owned by a type that specializes `Occurrences::Occurrence`,
70924
+ * and not a portion, a self/happens link, or a composite action". All four
70925
+ * are decided here from the declaration kinds and what the source writes
70926
+ * ({@link isOccurrenceKindOwner}, {@link PORTION_MODIFIERS},
70927
+ * {@link ACTION_KIND_USAGES} with {@link REFERENCE_MODIFIERS}, and
70928
+ * {@link specializesNonVaryingLibraryType}); an owner whose base is written
70929
+ * rather than fixed by its kind leaves the category quiet, as an
70930
+ * undecidable model must.
70931
+ */
70932
+ implicitConstantHint(node) {
70933
+ const isFeature = node.isDef === false || KERML_FEATURE_DECLS.has(node.$type);
70934
+ if (!isFeature)
70935
+ return void 0;
70936
+ const modifiers2 = node.modifiers ?? [];
70937
+ if (modifiers2.some((modifier) => CONSTANT_MODIFIERS.has(modifier)))
70938
+ return void 0;
70939
+ if (modifiers2.some((modifier) => PORTION_MODIFIERS.has(modifier)))
70940
+ return void 0;
70941
+ if (ACTION_KIND_USAGES.has(node.$type) && !modifiers2.some((modifier) => REFERENCE_MODIFIERS.has(modifier)))
70942
+ return void 0;
70943
+ const owner = node.$container;
70944
+ if (!owner || !isOccurrenceKindOwner(owner))
70945
+ return void 0;
70946
+ if (specializesNonVaryingLibraryType(node))
70947
+ return void 0;
70948
+ const carrier = allRelationships2(node).filter((rel) => rel.kind && CONSTANT_CARRYING_KINDS.has(rel.kind)).flatMap((rel) => rel.targets ?? []).find((target) => this.isConstantFeature(target));
70949
+ if (!carrier)
70950
+ return void 0;
70951
+ const anchor = constantAnchor(node);
70952
+ const at = anchor?.range.start ?? node.$cstNode?.range.start;
70953
+ if (!at)
70954
+ return void 0;
70955
+ const keyword = constantKeyword(this.services?.LanguageMetaData.languageId);
70956
+ return {
70957
+ position: at,
70958
+ label: keyword,
70959
+ kind: import_vscode_languageserver20.InlayHintKind.Type,
70960
+ paddingRight: true,
70961
+ 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.`),
70962
+ // Writing the modifier is mechanically safe — it is a leading
70963
+ // declaration prefix, and OMG puts it exactly here, before `ref`
70964
+ // and the kind keyword.
70965
+ ...anchor ? { textEdits: [{ range: { start: at, end: at }, newText: `${keyword} ` }] } : {}
70966
+ };
70967
+ }
70968
+ /**
70969
+ * REQ-395 — issue #240: does the feature this path names declare `constant`?
70970
+ *
70971
+ * The path must name exactly one element, and that element is resolved
70972
+ * through the linker's off-to-the-side library parse — one lazily parsed
70973
+ * indexed target, the budget REQ-251 sets, and never a document added to the
70974
+ * workspace. The answer is memoized, so a scroll does not repeat it.
70975
+ */
70976
+ isConstantFeature(target) {
70977
+ if (!this.lookup)
70978
+ return false;
70979
+ const description = this.lookup.uniqueForPath(target);
70980
+ if (!description)
70981
+ return false;
70982
+ const key = elementKey2(description);
70983
+ const cached = this.constantCache.get(key);
70984
+ if (cached !== void 0)
70985
+ return cached;
70986
+ const node = this.linker?.resolveIndexedNode?.(description) ?? description.node;
70987
+ const modifiers2 = node?.modifiers ?? [];
70988
+ const answer = modifiers2.some((modifier) => CONSTANT_MODIFIERS.has(modifier));
70989
+ this.constantCache.set(key, answer);
70990
+ return answer;
70991
+ }
70236
70992
  /**
70237
70993
  * REQ-395 — Find one implicit base in the index.
70238
70994
  *
@@ -70246,25 +71002,19 @@ var SysmlInlayHintProvider = class {
70246
71002
  const cached = this.baseCache.get(qualified);
70247
71003
  if (cached)
70248
71004
  return cached;
70249
- const index = this.services?.shared.workspace.IndexManager;
70250
- if (!index)
71005
+ if (!this.lookup)
70251
71006
  return void 0;
70252
- const simple = lastSegment3(qualified);
70253
- let fallback;
70254
- for (const description of index.allElements()) {
70255
- if (description.name === qualified) {
70256
- this.baseCache.set(qualified, description);
70257
- return description;
70258
- }
70259
- if (fallback || description.name !== simple)
70260
- continue;
71007
+ const exact = this.lookup.descriptions(qualified).at(0);
71008
+ if (exact) {
71009
+ this.baseCache.set(qualified, exact);
71010
+ return exact;
71011
+ }
71012
+ const fallback = this.lookup.descriptions(lastSegment3(qualified)).find((description) => {
70261
71013
  const uri = description.documentUri instanceof URI2 ? description.documentUri : URI2.parse(String(description.documentUri));
70262
71014
  if (!isStandardLibraryUri(uri))
70263
- continue;
70264
- if (description.isUsage === true !== wantUsage)
70265
- continue;
70266
- fallback = description;
70267
- }
71015
+ return false;
71016
+ return description.isUsage === true === wantUsage;
71017
+ });
70268
71018
  if (fallback)
70269
71019
  this.baseCache.set(qualified, fallback);
70270
71020
  return fallback;
@@ -72319,7 +73069,7 @@ async function runValidation(command) {
72319
73069
  }
72320
73070
 
72321
73071
  // src/main.ts
72322
- var VERSION2 = true ? "0.24.0" : "dev";
73072
+ var VERSION2 = true ? "0.26.0" : "dev";
72323
73073
  async function main(argv) {
72324
73074
  const command = parseArgs(argv);
72325
73075
  if (command.kind === "help") {