sysml-v2-lsp 0.37.0 → 0.39.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.
@@ -118,28 +118,28 @@ var require_messages = __commonJS({
118
118
  Object.defineProperty(exports2, "__esModule", { value: true });
119
119
  exports2.Message = exports2.NotificationType9 = exports2.NotificationType8 = exports2.NotificationType7 = exports2.NotificationType6 = exports2.NotificationType5 = exports2.NotificationType4 = exports2.NotificationType3 = exports2.NotificationType2 = exports2.NotificationType1 = exports2.NotificationType0 = exports2.NotificationType = exports2.RequestType9 = exports2.RequestType8 = exports2.RequestType7 = exports2.RequestType6 = exports2.RequestType5 = exports2.RequestType4 = exports2.RequestType3 = exports2.RequestType2 = exports2.RequestType1 = exports2.RequestType = exports2.RequestType0 = exports2.AbstractMessageSignature = exports2.ParameterStructures = exports2.ResponseError = exports2.ErrorCodes = void 0;
120
120
  var is = require_is2();
121
- var ErrorCodes2;
122
- (function(ErrorCodes3) {
123
- ErrorCodes3.ParseError = -32700;
124
- ErrorCodes3.InvalidRequest = -32600;
125
- ErrorCodes3.MethodNotFound = -32601;
126
- ErrorCodes3.InvalidParams = -32602;
127
- ErrorCodes3.InternalError = -32603;
128
- ErrorCodes3.jsonrpcReservedErrorRangeStart = -32099;
129
- ErrorCodes3.serverErrorStart = -32099;
130
- ErrorCodes3.MessageWriteError = -32099;
131
- ErrorCodes3.MessageReadError = -32098;
132
- ErrorCodes3.PendingResponseRejected = -32097;
133
- ErrorCodes3.ConnectionInactive = -32096;
134
- ErrorCodes3.ServerNotInitialized = -32002;
135
- ErrorCodes3.UnknownErrorCode = -32001;
136
- ErrorCodes3.jsonrpcReservedErrorRangeEnd = -32e3;
137
- ErrorCodes3.serverErrorEnd = -32e3;
138
- })(ErrorCodes2 || (exports2.ErrorCodes = ErrorCodes2 = {}));
139
- var ResponseError2 = class _ResponseError extends Error {
121
+ var ErrorCodes3;
122
+ (function(ErrorCodes4) {
123
+ ErrorCodes4.ParseError = -32700;
124
+ ErrorCodes4.InvalidRequest = -32600;
125
+ ErrorCodes4.MethodNotFound = -32601;
126
+ ErrorCodes4.InvalidParams = -32602;
127
+ ErrorCodes4.InternalError = -32603;
128
+ ErrorCodes4.jsonrpcReservedErrorRangeStart = -32099;
129
+ ErrorCodes4.serverErrorStart = -32099;
130
+ ErrorCodes4.MessageWriteError = -32099;
131
+ ErrorCodes4.MessageReadError = -32098;
132
+ ErrorCodes4.PendingResponseRejected = -32097;
133
+ ErrorCodes4.ConnectionInactive = -32096;
134
+ ErrorCodes4.ServerNotInitialized = -32002;
135
+ ErrorCodes4.UnknownErrorCode = -32001;
136
+ ErrorCodes4.jsonrpcReservedErrorRangeEnd = -32e3;
137
+ ErrorCodes4.serverErrorEnd = -32e3;
138
+ })(ErrorCodes3 || (exports2.ErrorCodes = ErrorCodes3 = {}));
139
+ var ResponseError3 = class _ResponseError extends Error {
140
140
  constructor(code, message, data) {
141
141
  super(message);
142
- this.code = is.number(code) ? code : ErrorCodes2.UnknownErrorCode;
142
+ this.code = is.number(code) ? code : ErrorCodes3.UnknownErrorCode;
143
143
  this.data = data;
144
144
  Object.setPrototypeOf(this, _ResponseError.prototype);
145
145
  }
@@ -154,7 +154,7 @@ var require_messages = __commonJS({
154
154
  return result;
155
155
  }
156
156
  };
157
- exports2.ResponseError = ResponseError2;
157
+ exports2.ResponseError = ResponseError3;
158
158
  var ParameterStructures = class _ParameterStructures {
159
159
  constructor(kind) {
160
160
  this.kind = kind;
@@ -3552,8 +3552,8 @@ var require_main2 = __commonJS({
3552
3552
  }
3553
3553
  CodeDescription2.is = is;
3554
3554
  })(CodeDescription || (exports3.CodeDescription = CodeDescription = {}));
3555
- var Diagnostic6;
3556
- (function(Diagnostic7) {
3555
+ var Diagnostic7;
3556
+ (function(Diagnostic8) {
3557
3557
  function create(range, message, severity, code, source, relatedInformation) {
3558
3558
  var result = { range, message };
3559
3559
  if (Is.defined(severity)) {
@@ -3570,14 +3570,14 @@ var require_main2 = __commonJS({
3570
3570
  }
3571
3571
  return result;
3572
3572
  }
3573
- Diagnostic7.create = create;
3573
+ Diagnostic8.create = create;
3574
3574
  function is(value) {
3575
3575
  var _a;
3576
3576
  var candidate = value;
3577
3577
  return Is.defined(candidate) && Range6.is(candidate.range) && Is.string(candidate.message) && (Is.number(candidate.severity) || Is.undefined(candidate.severity)) && (Is.integer(candidate.code) || Is.string(candidate.code) || Is.undefined(candidate.code)) && (Is.undefined(candidate.codeDescription) || Is.string((_a = candidate.codeDescription) === null || _a === void 0 ? void 0 : _a.href)) && (Is.string(candidate.source) || Is.undefined(candidate.source)) && (Is.undefined(candidate.relatedInformation) || Is.typedArray(candidate.relatedInformation, DiagnosticRelatedInformation.is));
3578
3578
  }
3579
- Diagnostic7.is = is;
3580
- })(Diagnostic6 || (exports3.Diagnostic = Diagnostic6 = {}));
3579
+ Diagnostic8.is = is;
3580
+ })(Diagnostic7 || (exports3.Diagnostic = Diagnostic7 = {}));
3581
3581
  var Command;
3582
3582
  (function(Command2) {
3583
3583
  function create(title, command) {
@@ -4362,7 +4362,7 @@ var require_main2 = __commonJS({
4362
4362
  CodeActionContext2.create = create;
4363
4363
  function is(value) {
4364
4364
  var candidate = value;
4365
- return Is.defined(candidate) && Is.typedArray(candidate.diagnostics, Diagnostic6.is) && (candidate.only === void 0 || Is.typedArray(candidate.only, Is.string)) && (candidate.triggerKind === void 0 || candidate.triggerKind === CodeActionTriggerKind.Invoked || candidate.triggerKind === CodeActionTriggerKind.Automatic);
4365
+ return Is.defined(candidate) && Is.typedArray(candidate.diagnostics, Diagnostic7.is) && (candidate.only === void 0 || Is.typedArray(candidate.only, Is.string)) && (candidate.triggerKind === void 0 || candidate.triggerKind === CodeActionTriggerKind.Invoked || candidate.triggerKind === CodeActionTriggerKind.Automatic);
4366
4366
  }
4367
4367
  CodeActionContext2.is = is;
4368
4368
  })(CodeActionContext || (exports3.CodeActionContext = CodeActionContext = {}));
@@ -4387,7 +4387,7 @@ var require_main2 = __commonJS({
4387
4387
  CodeAction3.create = create;
4388
4388
  function is(value) {
4389
4389
  var candidate = value;
4390
- return candidate && Is.string(candidate.title) && (candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, Diagnostic6.is)) && (candidate.kind === void 0 || Is.string(candidate.kind)) && (candidate.edit !== void 0 || candidate.command !== void 0) && (candidate.command === void 0 || Command.is(candidate.command)) && (candidate.isPreferred === void 0 || Is.boolean(candidate.isPreferred)) && (candidate.edit === void 0 || WorkspaceEdit4.is(candidate.edit));
4390
+ return candidate && Is.string(candidate.title) && (candidate.diagnostics === void 0 || Is.typedArray(candidate.diagnostics, Diagnostic7.is)) && (candidate.kind === void 0 || Is.string(candidate.kind)) && (candidate.edit !== void 0 || candidate.command !== void 0) && (candidate.command === void 0 || Command.is(candidate.command)) && (candidate.isPreferred === void 0 || Is.boolean(candidate.isPreferred)) && (candidate.edit === void 0 || WorkspaceEdit4.is(candidate.edit));
4391
4391
  }
4392
4392
  CodeAction3.is = is;
4393
4393
  })(CodeAction2 || (exports3.CodeAction = CodeAction2 = {}));
@@ -8880,7 +8880,7 @@ var require_node3 = __commonJS({
8880
8880
  });
8881
8881
 
8882
8882
  // server/src/server.ts
8883
- var import_node14 = __toESM(require_node3());
8883
+ var import_node15 = __toESM(require_node3());
8884
8884
  var fs = __toESM(require("node:fs"));
8885
8885
  var path2 = __toESM(require("node:path"));
8886
8886
  var import_node_url2 = require("node:url");
@@ -124358,6 +124358,13 @@ var DocumentManager = class {
124358
124358
  * indexed yet" apart from "genuinely doesn't exist".
124359
124359
  */
124360
124360
  workspaceScanComplete = true;
124361
+ /**
124362
+ * Last version given to a stand-in text by `withTexts`. Caches keyed by
124363
+ * version assume one text per uri and version, so each stand-in gets a
124364
+ * version outside the LSP integer range (LSP 3.17, base types).
124365
+ * These internal positive versions are never sent to the client.
124366
+ */
124367
+ temporaryVersion = 2 ** 31 - 1;
124361
124368
  /**
124362
124369
  * Parse a document and cache the result.
124363
124370
  * Returns the cached result if the content hasn't changed.
@@ -124561,6 +124568,35 @@ var DocumentManager = class {
124561
124568
  this.wsSymbolTable.removeUri(uri);
124562
124569
  this.wsBuiltVersions.delete(uri);
124563
124570
  }
124571
+ /**
124572
+ * Runs `fn` with each text standing in for the document at its uri (or
124573
+ * added as a new document), all at once, then restores the previous
124574
+ * state. `fn` must be synchronous: no other request can run in between,
124575
+ * so no other request ever sees the temporary texts. Every text is
124576
+ * parsed before anything changes. The stand-ins get versions no real
124577
+ * document has, so caches keyed by version never confuse them with the
124578
+ * real documents.
124579
+ */
124580
+ withTexts(documents2, fn) {
124581
+ if (!Number.isSafeInteger(this.temporaryVersion + documents2.length)) {
124582
+ throw new RangeError("Temporary document version space exhausted.");
124583
+ }
124584
+ const parsed = documents2.map(({ uri, text }) => ({ uri, text, result: parseDocument(text) }));
124585
+ const previous = new Map(parsed.map(({ uri }) => [uri, this.cache.get(uri)]));
124586
+ for (const { uri, text, result } of parsed) {
124587
+ this.cache.set(uri, { version: ++this.temporaryVersion, text, result, needsParse: false });
124588
+ }
124589
+ try {
124590
+ return fn();
124591
+ } finally {
124592
+ for (const [uri, entry] of previous) {
124593
+ if (entry) this.cache.set(uri, entry);
124594
+ else this.cache.delete(uri);
124595
+ this.wsSymbolTable.removeUri(uri);
124596
+ this.wsBuiltVersions.delete(uri);
124597
+ }
124598
+ }
124599
+ }
124564
124600
  /**
124565
124601
  * Get all cached URIs.
124566
124602
  */
@@ -125382,12 +125418,19 @@ var MAX_NAMESPACE_NESTING_DEPTH = 64;
125382
125418
  function findConflictedQualifiedNames(allSymbols) {
125383
125419
  return groupConflicts(allSymbols.filter((s) => s.qualifiedName !== void 0), (s) => s.qualifiedName);
125384
125420
  }
125421
+ function memberNamesOf(symbol) {
125422
+ return symbol.shortName && symbol.shortName !== symbol.name ? [symbol.name, symbol.shortName] : [symbol.name];
125423
+ }
125385
125424
  function findConflictingDeclarations(allSymbols) {
125386
125425
  const conflicting = /* @__PURE__ */ new Map();
125387
125426
  const add = (groups) => {
125388
- for (const group of groups.values()) for (const s of group) conflicting.set(s, group);
125427
+ for (const group of groups) {
125428
+ for (const s of group) {
125429
+ conflicting.set(s, [.../* @__PURE__ */ new Set([...conflicting.get(s) ?? [], ...group])]);
125430
+ }
125431
+ }
125389
125432
  };
125390
- add(findConflictedQualifiedNames(allSymbols));
125433
+ add(findConflictedQualifiedNames(allSymbols).values());
125391
125434
  const membersOfAnonymous = allSymbols.filter((s) => s.qualifiedName === void 0 && !isAnonymous(s) && s.parentId !== void 0);
125392
125435
  const byOwner = /* @__PURE__ */ new Map();
125393
125436
  for (const s of membersOfAnonymous) {
@@ -125395,13 +125438,33 @@ function findConflictingDeclarations(allSymbols) {
125395
125438
  list.push(s);
125396
125439
  byOwner.set(s.parentId, list);
125397
125440
  }
125398
- for (const members of byOwner.values()) add(groupConflicts(members, (s) => s.name));
125441
+ for (const members of byOwner.values()) add(groupConflicts(members, (s) => s.name).values());
125442
+ add([...findConflictingShortNames(allSymbols).values()].map((group) => group.symbols));
125399
125443
  return conflicting;
125400
125444
  }
125445
+ function findConflictingShortNames(allSymbols) {
125446
+ const byId = new Map(allSymbols.map((s) => [s.symbolId, s]));
125447
+ const entries = [];
125448
+ const keysWithShortName = /* @__PURE__ */ new Set();
125449
+ for (const s of allSymbols) {
125450
+ if (isAnonymous(s)) continue;
125451
+ const owner = s.parentId ? byId.get(s.parentId) : void 0;
125452
+ const namespace = owner === void 0 ? "q:" : owner.qualifiedName !== void 0 ? `q:${owner.qualifiedName}` : `id:${owner.symbolId}`;
125453
+ for (const name of memberNamesOf(s)) {
125454
+ const key = `${namespace}\0${name}`;
125455
+ entries.push({ symbol: s, key });
125456
+ if (name === s.shortName) keysWithShortName.add(key);
125457
+ }
125458
+ }
125459
+ const groups = groupKeyedConflicts(entries.filter((e) => keysWithShortName.has(e.key)));
125460
+ return new Map([...groups].map(([key, symbols]) => [key, { name: key.slice(key.indexOf("\0") + 1), symbols }]));
125461
+ }
125401
125462
  function groupConflicts(symbols, keyOf) {
125463
+ return groupKeyedConflicts(symbols.map((symbol) => ({ symbol, key: keyOf(symbol) })));
125464
+ }
125465
+ function groupKeyedConflicts(entries) {
125402
125466
  const byKeyAndKind = /* @__PURE__ */ new Map();
125403
- for (const s of symbols) {
125404
- const key = keyOf(s);
125467
+ for (const { symbol: s, key } of entries) {
125405
125468
  let byKind = byKeyAndKind.get(key);
125406
125469
  if (!byKind) {
125407
125470
  byKind = /* @__PURE__ */ new Map();
@@ -125473,7 +125536,13 @@ function buildSymbolIndexes(allSymbols) {
125473
125536
  for (const s of allSymbols) {
125474
125537
  byId.set(s.symbolId, s);
125475
125538
  }
125476
- const indexes = { byName, byParent, byQualifiedName, byId, definitionsByName, portsByName };
125539
+ const clashingShortNames = /* @__PURE__ */ new Map();
125540
+ for (const { name, symbols } of findConflictingShortNames(allSymbols).values()) {
125541
+ const clashing = clashingShortNames.get(name) ?? /* @__PURE__ */ new Set();
125542
+ for (const s of symbols) clashing.add(s);
125543
+ clashingShortNames.set(name, clashing);
125544
+ }
125545
+ const indexes = { byName, byParent, byQualifiedName, byId, definitionsByName, portsByName, clashingShortNames };
125477
125546
  for (const s of allSymbols) {
125478
125547
  if (isAnonymous(s)) continue;
125479
125548
  const nameList = byName.get(s.name) ?? [];
@@ -125562,6 +125631,16 @@ var NamespaceResolver = class {
125562
125631
  resolveNameIn(namespace, name, indexes) {
125563
125632
  return this.resolveQualifiedNameFrom(namespace ? namespaceKeyOf(namespace) : "", name, indexes);
125564
125633
  }
125634
+ /** Resolve an editor reference without losing the reason a name is unresolved. */
125635
+ resolveReferenceIn(namespace, name, indexes) {
125636
+ return this.resolveQualifiedReferenceFrom(namespace ? namespaceKeyOf(namespace) : "", name, indexes);
125637
+ }
125638
+ /** Resolve a public qualified or feature-path member, without enclosing-scope fallback. */
125639
+ resolveMemberReference(owner, name, indexes) {
125640
+ const members = this.getResolvedMembers(namespaceKeyOf(owner), indexes).get(name);
125641
+ if (members && this.isAmbiguous(name, members, indexes)) return { ambiguous: true };
125642
+ return this.referenceTo(members?.find((member) => member.visibility === "public")?.symbol, indexes);
125643
+ }
125565
125644
  /**
125566
125645
  * Resolve a (possibly qualified) name to its symbol per §7.5.1, searching
125567
125646
  * outward from namespace `start` and its enclosing namespaces (its own resolved
@@ -125573,28 +125652,48 @@ var NamespaceResolver = class {
125573
125652
  * its own enclosing package already imported, per §7.5.1/§7.5.3).
125574
125653
  */
125575
125654
  resolveQualifiedNameFrom(start, name, indexes) {
125655
+ return this.resolveQualifiedReferenceFrom(start, name, indexes).symbol;
125656
+ }
125657
+ resolveQualifiedReferenceFrom(start, name, indexes) {
125576
125658
  const [first, ...rest] = splitQualifiedName(name);
125577
125659
  let resolved;
125578
125660
  for (const ancestor of this.namespaceAncestorsOf(start, indexes)) {
125579
125661
  const candidates = this.getResolvedMembers(ancestor, indexes).get(first);
125580
125662
  if (candidates && candidates.length > 0) {
125663
+ if (this.isAmbiguous(first, candidates, indexes)) return { ambiguous: true };
125581
125664
  resolved = candidates[0].symbol;
125582
125665
  break;
125583
125666
  }
125584
125667
  }
125585
- if (resolved === void 0) return void 0;
125668
+ if (resolved === void 0) return { ambiguous: false };
125586
125669
  const isWithinStart = (namespace) => this.namespaceAncestorsOf(start, indexes).has(namespace);
125587
125670
  for (const segment of rest) {
125588
125671
  const owner = resolved;
125589
125672
  const ownerKey = namespaceKeyOf(owner);
125590
125673
  const members = this.getResolvedMembers(ownerKey, indexes).get(segment);
125674
+ if (members && this.isAmbiguous(segment, members, indexes)) return { ambiguous: true };
125591
125675
  const visibleMember = members?.find(
125592
125676
  (m2) => m2.visibility === "public" || isWithinStart(ownerKey) || m2.visibility === "protected" && this.isProtectedVisibleFrom(start, owner, indexes)
125593
125677
  );
125594
- if (!visibleMember) return void 0;
125678
+ if (!visibleMember) return { ambiguous: false };
125595
125679
  resolved = visibleMember.symbol;
125596
125680
  }
125597
- return resolved.qualifiedName !== void 0 ? indexes.byQualifiedName.get(resolved.qualifiedName) : resolved;
125681
+ return this.referenceTo(resolved, indexes);
125682
+ }
125683
+ referenceTo(resolved, indexes) {
125684
+ if (!resolved) return { ambiguous: false };
125685
+ const symbol = resolved.qualifiedName !== void 0 ? indexes.byQualifiedName.get(resolved.qualifiedName) : resolved;
125686
+ return { symbol, ambiguous: symbol === void 0 };
125687
+ }
125688
+ /**
125689
+ * Whether `name` names more than one of `candidates` that clash on it
125690
+ * through a short name (`SymbolIndexes.clashingShortNames`), so it can't
125691
+ * be resolved to either (KerML 7.2.5.2). Like a conflicted qualifiedName,
125692
+ * it doesn't fall through to an enclosing namespace's member of that name.
125693
+ */
125694
+ isAmbiguous(name, candidates, indexes) {
125695
+ const clashing = indexes.clashingShortNames.get(name);
125696
+ return clashing !== void 0 && candidates.filter((c) => clashing.has(c.symbol)).length > 1;
125598
125697
  }
125599
125698
  /**
125600
125699
  * §7.5.3's protected-import exception: "A visibility of protected is the
@@ -128297,9 +128396,11 @@ var NameReferences = class {
128297
128396
  if (target && !path3.qualifiedNames.some((name) => name.segments.some((segment) => segment.name === target.name || segment.name === target.shortName))) continue;
128298
128397
  const namespace = namespaces.find((ns) => ns.body < path3.start && path3.end <= ns.end);
128299
128398
  let receiver;
128399
+ let receiverAmbiguous = false;
128300
128400
  for (let group = 0; group < path3.qualifiedNames.length; group++) {
128301
128401
  const qualified = path3.qualifiedNames[group];
128302
128402
  let resolved;
128403
+ let ambiguous = false;
128303
128404
  for (let index2 = 0; index2 < qualified.segments.length; index2++) {
128304
128405
  const segment = qualified.segments[index2];
128305
128406
  const range = import_node4.Range.create(doc.positionAt(segment.start), doc.positionAt(segment.end));
@@ -128308,17 +128409,27 @@ var NameReferences = class {
128308
128409
  while (before >= 0 && /\s/.test(text[before])) before--;
128309
128410
  while (after < text.length && /\s/.test(text[after])) after++;
128310
128411
  const declaration = symbols.find((symbol) => !isAnonymous(symbol) && (symbol.selectionRange.start.line === range.start.line && symbol.selectionRange.start.character === range.start.character && symbol.selectionRange.end.character === range.end.character || symbol.shortName === segment.name && text[before] === "<" && text[after] === ">" && doc.offsetAt(symbol.range.start) <= segment.start && segment.end < doc.offsetAt(symbol.selectionRange.start)));
128311
- if (declaration) resolved = declaration;
128312
- else if (index2 > 0) {
128313
- resolved = resolved ? this.resolver.getResolvedMembers(namespaceKeyOf(resolved), this.indexes).get(segment.name)?.find((entry) => entry.visibility === "public")?.symbol : void 0;
128412
+ if (declaration) {
128413
+ resolved = declaration;
128414
+ ambiguous = false;
128415
+ } else if (index2 > 0) {
128416
+ if (resolved) {
128417
+ const result = this.resolver.resolveMemberReference(resolved, segment.name, this.indexes);
128418
+ resolved = result.symbol;
128419
+ ambiguous = result.ambiguous;
128420
+ }
128314
128421
  } else if (group > 0) {
128315
- resolved = receiver ? this.resolveFeature(receiver, segment.name) : void 0;
128422
+ const result = receiver ? this.resolveFeature(receiver, segment.name) : { ambiguous: receiverAmbiguous };
128423
+ resolved = result.symbol;
128424
+ ambiguous = result.ambiguous;
128316
128425
  } else {
128317
- resolved = this.resolver.resolveNameIn(
128426
+ const result = this.resolver.resolveReferenceIn(
128318
128427
  namespace?.symbol,
128319
128428
  joinQualifiedName([segment.name]),
128320
128429
  this.indexes
128321
128430
  );
128431
+ resolved = result.symbol;
128432
+ ambiguous = result.ambiguous;
128322
128433
  }
128323
128434
  if (cursor !== void 0 && (cursor < segment.start || cursor > segment.end)) continue;
128324
128435
  if (target && segment.name !== target.name && segment.name !== target.shortName) continue;
@@ -128328,10 +128439,12 @@ var NameReferences = class {
128328
128439
  name: segment.name,
128329
128440
  qualifiedName: index2 === qualified.segments.length - 1 ? qualified.name : segment.name,
128330
128441
  symbol: resolved,
128331
- isDeclaration: declaration !== void 0
128442
+ isDeclaration: declaration !== void 0,
128443
+ isAmbiguous: ambiguous
128332
128444
  });
128333
128445
  }
128334
128446
  receiver = resolved;
128447
+ receiverAmbiguous = ambiguous;
128335
128448
  }
128336
128449
  }
128337
128450
  return refs;
@@ -128343,14 +128456,14 @@ var NameReferences = class {
128343
128456
  const owner = queue[index2];
128344
128457
  if (visited.has(owner)) continue;
128345
128458
  visited.add(owner);
128346
- const member = this.resolver.getResolvedMembers(namespaceKeyOf(owner), this.indexes).get(name)?.find((entry) => entry.visibility === "public")?.symbol;
128347
- if (member) return member;
128459
+ const member = this.resolver.resolveMemberReference(owner, name, this.indexes);
128460
+ if (member.symbol || member.ambiguous) return member;
128348
128461
  for (const typeName of owner.typeNames) {
128349
128462
  const type = this.resolver.resolveNameFrom(owner, typeName, this.indexes);
128350
128463
  if (type && !visited.has(type)) queue.push(type);
128351
128464
  }
128352
128465
  }
128353
- return void 0;
128466
+ return { ambiguous: false };
128354
128467
  }
128355
128468
  };
128356
128469
 
@@ -128367,6 +128480,7 @@ var DefinitionProvider = class {
128367
128480
  }
128368
128481
  const wsSymbolTable = this.documentManager.getWorkspaceSymbolTable();
128369
128482
  const reference = new NameReferences(this.documentManager).at(params.textDocument.uri, params.position);
128483
+ if (reference?.isAmbiguous) return null;
128370
128484
  if (reference?.symbol && !reference.isDeclaration) {
128371
128485
  return { uri: reference.symbol.uri, range: reference.symbol.selectionRange };
128372
128486
  }
@@ -128981,6 +129095,7 @@ var HoverProvider = class {
128981
129095
  }
128982
129096
  const text = this.documentManager.getText(params.textDocument.uri);
128983
129097
  const reference = new NameReferences(this.documentManager).at(params.textDocument.uri, params.position);
129098
+ if (reference?.isAmbiguous) return null;
128984
129099
  const word = reference?.qualifiedName ?? (text ? this.getWordAtPosition(text, params.position.line, params.position.character) : void 0);
128985
129100
  if (word && word.includes("::") && !reference?.symbol) {
128986
129101
  const libHover = this.buildLibraryHover(word);
@@ -131036,6 +131151,115 @@ var SemanticValidator = class _SemanticValidator {
131036
131151
  }
131037
131152
  };
131038
131153
 
131154
+ // server/src/providers/textValidationProvider.ts
131155
+ var import_node14 = __toESM(require_node3());
131156
+ var DEFAULT_TEXT_VALIDATION_SETTINGS = {
131157
+ enabled: false,
131158
+ maxDocuments: 5,
131159
+ maxLines: 200,
131160
+ maxCharacters: 8192,
131161
+ maxTotalCharacters: 16384
131162
+ };
131163
+ function readTextValidationSettings(section) {
131164
+ const raw = typeof section === "object" && section !== null ? section : {};
131165
+ const limit = (value, fallback) => Number.isSafeInteger(value) && value > 0 ? value : fallback;
131166
+ return {
131167
+ enabled: raw.enabled === true,
131168
+ maxDocuments: limit(raw.maxDocuments, DEFAULT_TEXT_VALIDATION_SETTINGS.maxDocuments),
131169
+ maxLines: limit(raw.maxLines, DEFAULT_TEXT_VALIDATION_SETTINGS.maxLines),
131170
+ maxCharacters: limit(raw.maxCharacters, DEFAULT_TEXT_VALIDATION_SETTINGS.maxCharacters),
131171
+ maxTotalCharacters: limit(raw.maxTotalCharacters, DEFAULT_TEXT_VALIDATION_SETTINGS.maxTotalCharacters)
131172
+ };
131173
+ }
131174
+ var TextValidationProvider = class {
131175
+ /**
131176
+ * `isReported` filters diagnostics as the server does before
131177
+ * publishing them (`sysml.validation.disabledCodes`), so a text gets
131178
+ * exactly what it would get as an open document.
131179
+ */
131180
+ constructor(documentManager2, semanticValidator2, isReported2 = () => true) {
131181
+ this.documentManager = documentManager2;
131182
+ this.semanticValidator = semanticValidator2;
131183
+ this.isReported = isReported2;
131184
+ }
131185
+ documentManager;
131186
+ semanticValidator;
131187
+ isReported;
131188
+ settings = { ...DEFAULT_TEXT_VALIDATION_SETTINGS };
131189
+ /** Applies the client's settings to the requests that follow. */
131190
+ configure(settings) {
131191
+ this.settings = { ...settings };
131192
+ }
131193
+ /**
131194
+ * The diagnostics each text would get as an open document, with all
131195
+ * texts in place at the same time. Answers `MethodNotFound` while
131196
+ * disabled, and `InvalidParams` for malformed parameters or parameters
131197
+ * over a limit.
131198
+ */
131199
+ validate(params) {
131200
+ if (!this.settings.enabled) {
131201
+ throw new import_node14.ResponseError(
131202
+ import_node14.ErrorCodes.MethodNotFound,
131203
+ "sysml/validateText is experimental and disabled: set sysml.experimental.validateText.enabled to true."
131204
+ );
131205
+ }
131206
+ const documents2 = this.checkDocuments(params?.documents);
131207
+ const known = documents2.map((document) => ({ uri: this.knownSpelling(document.uri), text: document.text }));
131208
+ const diagnostics = this.documentManager.withTexts(known, () => known.map(({ uri }) => this.diagnose(uri)));
131209
+ return { documents: documents2.map((document, index2) => ({ uri: document.uri, diagnostics: diagnostics[index2] })) };
131210
+ }
131211
+ /** `documents` checked: a non-empty array of `{ uri, text }` within the limits, each naming a different document. */
131212
+ checkDocuments(documents2) {
131213
+ if (!Array.isArray(documents2) || documents2.length === 0) invalid('"documents" must be a non-empty array of { uri, text }.');
131214
+ const { maxDocuments, maxLines, maxCharacters, maxTotalCharacters } = this.settings;
131215
+ if (documents2.length > maxDocuments) {
131216
+ invalid(`${documents2.length} documents, more than the ${maxDocuments} allowed (sysml.experimental.validateText.maxDocuments).`);
131217
+ }
131218
+ const checked = documents2;
131219
+ let totalCharacters = 0;
131220
+ checked.forEach((document, index2) => {
131221
+ if (typeof document?.text !== "string") invalid(`documents[${index2}] needs "text", the SysML text to validate.`);
131222
+ if (document.text.length > maxCharacters) {
131223
+ invalid(`documents[${index2}] has ${document.text.length} characters, more than the ${maxCharacters} allowed (sysml.experimental.validateText.maxCharacters).`);
131224
+ }
131225
+ totalCharacters += document.text.length;
131226
+ if (totalCharacters > maxTotalCharacters) {
131227
+ invalid(`${totalCharacters} total characters, more than the ${maxTotalCharacters} allowed (sysml.experimental.validateText.maxTotalCharacters).`);
131228
+ }
131229
+ const lines = lineCount(document.text);
131230
+ if (lines > maxLines) {
131231
+ invalid(`documents[${index2}] has ${lines} lines, more than the ${maxLines} allowed (sysml.experimental.validateText.maxLines).`);
131232
+ }
131233
+ if (!isUri(document.uri)) invalid(`documents[${index2}] needs "uri", the document the text is the content of.`);
131234
+ const earlier = checked.findIndex((other) => isSameDocumentUri(other.uri, document.uri));
131235
+ if (earlier !== index2) invalid(`documents[${earlier}] and documents[${index2}] name the same document.`);
131236
+ });
131237
+ return checked;
131238
+ }
131239
+ /** The syntax, keyword and semantic diagnostics of the document at `uri`, as published for an open document. */
131240
+ diagnose(uri) {
131241
+ const diagnostics = new DiagnosticsProvider(this.documentManager).getDiagnostics(uri);
131242
+ const parseResult = this.documentManager.get(uri);
131243
+ if (parseResult) diagnostics.push(...validateKeywords(parseResult));
131244
+ diagnostics.push(...this.semanticValidator.validate(uri));
131245
+ return diagnostics.filter(this.isReported);
131246
+ }
131247
+ /** The spelling under which the server knows the document `uri` names, else `uri` itself (see `isSameDocumentUri`). */
131248
+ knownSpelling(uri) {
131249
+ return this.documentManager.getUris().find((known) => isSameDocumentUri(known, uri)) ?? uri;
131250
+ }
131251
+ };
131252
+ function lineCount(text) {
131253
+ const breaks = text.match(/\r\n|\r|\n/g)?.length ?? 0;
131254
+ return text === "" || /[\r\n]$/.test(text) ? breaks : breaks + 1;
131255
+ }
131256
+ function isUri(value) {
131257
+ return typeof value === "string" && value.trim() !== "";
131258
+ }
131259
+ function invalid(message) {
131260
+ throw new import_node14.ResponseError(import_node14.ErrorCodes.InvalidParams, `sysml/validateText: ${message}`);
131261
+ }
131262
+
131039
131263
  // server/src/utils/fileDiscovery.ts
131040
131264
  var fsp = __toESM(require("node:fs/promises"));
131041
131265
  var path = __toESM(require("node:path"));
@@ -131110,7 +131334,7 @@ function toFsPath(uri) {
131110
131334
  return void 0;
131111
131335
  }
131112
131336
  var connection = createServerConnection();
131113
- var documents = new import_node14.TextDocuments(TextDocument);
131337
+ var documents = new import_node15.TextDocuments(TextDocument);
131114
131338
  var serverStartTime = Date.now();
131115
131339
  var documentManager = new DocumentManager();
131116
131340
  var modelProvider = new SysMLModelProvider(documentManager);
@@ -131127,6 +131351,7 @@ var renameProvider = new RenameProvider(documentManager);
131127
131351
  var codeActionProvider = new CodeActionProvider(documentManager);
131128
131352
  var formattingProvider = new FormattingProvider(documents);
131129
131353
  var semanticValidator = new SemanticValidator(documentManager);
131354
+ var textValidationProvider = new TextValidationProvider(documentManager, semanticValidator, isReported);
131130
131355
  hoverProvider.setSemanticValidator(semanticValidator);
131131
131356
  var hasConfigurationCapability = false;
131132
131357
  var hasWorkspaceFolderCapability = false;
@@ -131138,11 +131363,11 @@ function rootsOf(folderUris) {
131138
131363
  var isWorkspaceFile = false;
131139
131364
  var preloadOnOpen = "workspaceOnly";
131140
131365
  var disabledDiagnosticCodes = /* @__PURE__ */ new Set();
131366
+ function isReported(diagnostic) {
131367
+ return diagnostic.code === void 0 || !disabledDiagnosticCodes.has(String(diagnostic.code));
131368
+ }
131141
131369
  function publishDiagnostics(uri, diagnostics) {
131142
- connection.sendDiagnostics({
131143
- uri,
131144
- diagnostics: diagnostics.filter((diagnostic) => diagnostic.code === void 0 || !disabledDiagnosticCodes.has(String(diagnostic.code)))
131145
- });
131370
+ connection.sendDiagnostics({ uri, diagnostics: diagnostics.filter(isReported) });
131146
131371
  }
131147
131372
  var skipDirs = new Set(DEFAULT_SKIP_DIRS);
131148
131373
  var serverReady = false;
@@ -131275,7 +131500,7 @@ connection.onInitialize((params) => {
131275
131500
  };
131276
131501
  const result = {
131277
131502
  capabilities: {
131278
- textDocumentSync: import_node14.TextDocumentSyncKind.Incremental,
131503
+ textDocumentSync: import_node15.TextDocumentSyncKind.Incremental,
131279
131504
  // Completion
131280
131505
  completionProvider: {
131281
131506
  resolveProvider: true,
@@ -131303,7 +131528,7 @@ connection.onInitialize((params) => {
131303
131528
  // Code actions (quick fixes)
131304
131529
  codeActionProvider: {
131305
131530
  codeActionKinds: [
131306
- import_node14.CodeActionKind.QuickFix
131531
+ import_node15.CodeActionKind.QuickFix
131307
131532
  ]
131308
131533
  },
131309
131534
  // Document formatting
@@ -131324,7 +131549,7 @@ connection.onInitialize((params) => {
131324
131549
  connection.onInitialized(async () => {
131325
131550
  if (hasConfigurationCapability) {
131326
131551
  connection.client.register(
131327
- import_node14.DidChangeConfigurationNotification.type,
131552
+ import_node15.DidChangeConfigurationNotification.type,
131328
131553
  void 0
131329
131554
  );
131330
131555
  }
@@ -131385,6 +131610,8 @@ async function pullSettings() {
131385
131610
  const validation = await connection.workspace.getConfiguration("sysml.validation");
131386
131611
  const codes = validation?.disabledCodes;
131387
131612
  disabledDiagnosticCodes = new Set(Array.isArray(codes) ? codes.filter((code) => typeof code === "string") : []);
131613
+ const experimental = await connection.workspace.getConfiguration("sysml.experimental");
131614
+ textValidationProvider.configure(readTextValidationSettings(experimental?.validateText));
131388
131615
  }
131389
131616
  function applySettings(config) {
131390
131617
  if (!config) return;
@@ -131738,6 +131965,10 @@ connection.onRequest("sysml/elementLookup", async (params) => {
131738
131965
  await initialization;
131739
131966
  return elementLookupProvider.elementLookup(params);
131740
131967
  });
131968
+ connection.onRequest("sysml/validateText", async (params) => {
131969
+ await initialization;
131970
+ return textValidationProvider.validate(params);
131971
+ });
131741
131972
  connection.onRequest("sysml/serverStats", () => {
131742
131973
  const mem = typeof process !== "undefined" && typeof process.memoryUsage === "function" ? process.memoryUsage() : { heapUsed: 0, heapTotal: 0, rss: 0, external: 0 };
131743
131974
  const toMB = (bytes) => Math.round(bytes / 1024 / 1024);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "sysml-v2-lsp",
3
3
  "displayName": "SysML v2 Language Server",
4
4
  "description": "Language Server Protocol implementation for SysML v2, powered by ANTLR4",
5
- "version": "0.37.0",
5
+ "version": "0.39.0",
6
6
  "publisher": "JamieD",
7
7
  "license": "MIT",
8
8
  "repository": {
@@ -96,6 +96,40 @@
96
96
  "default": [],
97
97
  "description": "Diagnostic codes to suppress, such as missing-doc. Changes apply to open documents immediately."
98
98
  },
99
+ "sysml.experimental.validateText.enabled": {
100
+ "scope": "window",
101
+ "type": "boolean",
102
+ "default": false,
103
+ "markdownDescription": "Experimental: answer the custom `sysml/validateText` request, which validates SysML text against the workspace model without opening a document or publishing diagnostics. The request is meant for tools that generate or edit SysML text."
104
+ },
105
+ "sysml.experimental.validateText.maxDocuments": {
106
+ "scope": "window",
107
+ "type": "integer",
108
+ "minimum": 1,
109
+ "default": 5,
110
+ "markdownDescription": "Experimental: the most documents one `sysml/validateText` request may validate. Requests with more are rejected."
111
+ },
112
+ "sysml.experimental.validateText.maxLines": {
113
+ "scope": "window",
114
+ "type": "integer",
115
+ "minimum": 1,
116
+ "default": 200,
117
+ "markdownDescription": "Experimental: the most lines of code one document's text may have in a `sysml/validateText` request. Requests with a longer text are rejected. Character limits also apply."
118
+ },
119
+ "sysml.experimental.validateText.maxCharacters": {
120
+ "scope": "window",
121
+ "type": "integer",
122
+ "minimum": 1,
123
+ "default": 8192,
124
+ "markdownDescription": "Experimental: the most characters (UTF-16 code units) per document in a `sysml/validateText` request. Checked before parsing, including for single-line text."
125
+ },
126
+ "sysml.experimental.validateText.maxTotalCharacters": {
127
+ "scope": "window",
128
+ "type": "integer",
129
+ "minimum": 1,
130
+ "default": 16384,
131
+ "markdownDescription": "Experimental: the most characters (UTF-16 code units) across all documents in a `sysml/validateText` request. Checked before parsing."
132
+ },
99
133
  "sysml.maxNumberOfProblems": {
100
134
  "scope": "resource",
101
135
  "type": "number",