sysml-v2-lsp 0.34.0 → 0.35.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/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.35.0]
6
+
7
+ ### Added
8
+
9
+ - Add Copilot guidance to check generated and upstream-managed files during changes and reviews, fix their source of truth rather than editing overwritten artifacts, and validate fixes after regeneration.
10
+ - A projectId per workspace folder: before scanning a folder, the server asks the client for its `sysml.project` setting (`workspace/configuration`, scoped to the folder), `{ projectId }`. A top-level element of a document in that folder (the innermost one, if nested) gets the URL `urn:uuid:<projectId>/<name>`, so the same name in two folders with different projectIds gives two symbol IDs (KerML 9.1). A folder without one uses an enclosing folder's projectId, else `initializationOptions.projectId`; one whose `projectId` isn't a UUID is reported and left out. The projectIds are asked for again on `workspace/didChangeConfiguration`, and documents already indexed under another projectId get new symbol IDs. Each folder's prefix, and where it comes from, is logged when it changes (`Folder <uri>: top-level elements' URLs start with urn:uuid:<projectId>/ (its projectId)`).
11
+ - Follow workspace folders added or removed while running (`workspace/didChangeWorkspaceFolders`, now requested): an added folder is scanned, as at start, and the documents scanned from a removed folder are dropped.
12
+
13
+ ### Changed
14
+
15
+ - `sysml/model` and `sysml/elementLookup` answer only once the server's `initialized` phase is done (settings and each folder's projectId pulled from the client, library indexed, DFA loaded). A request sent earlier now waits, instead of being answered with symbol IDs that might change once the projectIds arrive. A client that declares the `workspace/configuration` capability must answer those requests.
16
+
17
+ ### Fixed
18
+
19
+ - A transition is typed by the payload its trigger accepts, read from the parse tree. The payload type ran into whatever followed it in the whitespace-free text (`accept cmd : Command if cmd.code == 0 then busy` gave `Commandifcmd`, `accept cmd : Command then busy` gave `Commandthenbusy`), reported as `unresolved-type`.
20
+ - A state's `entry`, `do` and `exit` actions (`exit action leave : WriteSafeState;`, `exit leave;`) get a symbol, as a `perform action` (the `StatePerformActionUsage` production). They had none, so they were missing from the outline, and an action definition that only typed one was reported as `unused-definition`.
21
+ - Concern definitions and usages (`concern def H { ... }`, `concern c : H { ... }`, and a requirement's framed concerns, `frame concern c : H { ... }`) get a symbol, of the new kinds `concern def` and `concern` (metaclasses `ConcernDefinition`, `ConcernUsage`). They had none, so their members were attributed to the enclosing namespace: two concerns, or two framed concerns of one requirement, that each declared `attribute limit` were reported as `ambiguous-namespace-name`, and concerns were missing from the outline and `sysml/model`.
22
+
5
23
  ## [0.34.0]
6
24
 
7
25
  ### Added
@@ -130252,6 +130252,10 @@ ${JSON.stringify(message, null, 4)}`);
130252
130252
  function canonicalUri(uri) {
130253
130253
  return uri.replace(/^file:\/\/\/([A-Za-z])(?::|%3[Aa])(?=\/|$)/, (_m, drive) => `file:///${drive.toLowerCase()}:`);
130254
130254
  }
130255
+ function isInFolder(uri, folderUri) {
130256
+ const folder = canonicalUri(folderUri);
130257
+ return canonicalUri(uri).startsWith(folder.endsWith("/") ? folder : `${folder}/`);
130258
+ }
130255
130259
  function isSameDocumentUri(a, b) {
130256
130260
  return a === b || canonicalUri(a) === canonicalUri(b);
130257
130261
  }
@@ -130410,6 +130414,7 @@ ${JSON.stringify(message, null, 4)}`);
130410
130414
  var NAMESPACE_URL = v5_default.URL;
130411
130415
  var DEFAULT_URL_PREFIX = "https://github.com/daltskin/sysml-v2-lsp/";
130412
130416
  var urlPrefix = DEFAULT_URL_PREFIX;
130417
+ var folderPrefixes = [];
130413
130418
  function setProjectId(projectId) {
130414
130419
  if (projectId === void 0) {
130415
130420
  urlPrefix = DEFAULT_URL_PREFIX;
@@ -130419,8 +130424,14 @@ ${JSON.stringify(message, null, 4)}`);
130419
130424
  urlPrefix = `urn:uuid:${projectId.toLowerCase()}/`;
130420
130425
  return true;
130421
130426
  }
130422
- function getUrlPrefix() {
130423
- return urlPrefix;
130427
+ function setFolderProjectIds(projectIds) {
130428
+ const rejected = projectIds.filter((p) => !validate_default(p.projectId));
130429
+ folderPrefixes = projectIds.filter((p) => validate_default(p.projectId)).map((p) => ({ folderUri: p.folderUri, prefix: `urn:uuid:${p.projectId.toLowerCase()}/` })).sort((a, b) => b.folderUri.length - a.folderUri.length);
130430
+ return rejected;
130431
+ }
130432
+ function getUrlPrefix(uri) {
130433
+ const match = uri === void 0 ? void 0 : folderPrefixes.find((f2) => isInFolder(uri, f2.folderUri));
130434
+ return match?.prefix ?? urlPrefix;
130424
130435
  }
130425
130436
  function uuidV5(namespace, name) {
130426
130437
  return v5_default(name, namespace);
@@ -130502,6 +130513,10 @@ ${JSON.stringify(message, null, 4)}`);
130502
130513
  return "RequirementDefinition";
130503
130514
  case "requirement" /* RequirementUsage */:
130504
130515
  return "RequirementUsage";
130516
+ case "concern def" /* ConcernDef */:
130517
+ return "ConcernDefinition";
130518
+ case "concern" /* ConcernUsage */:
130519
+ return "ConcernUsage";
130505
130520
  case "constraint def" /* ConstraintDef */:
130506
130521
  return "ConstraintDefinition";
130507
130522
  case "constraint" /* ConstraintUsage */:
@@ -130624,7 +130639,7 @@ ${JSON.stringify(message, null, 4)}`);
130624
130639
  declaration.parentId = owner?.symbolId;
130625
130640
  const path = declaration.qualifiedName !== void 0 ? this.qualifiedNameAsWritten(declaration) : owner ? `${this.paths.get(owner)}/${segmentOf(declaration)}` : segmentOf(declaration);
130626
130641
  const namespace = owner ? this.topLevelOf(owner).symbolId : NAMESPACE_URL;
130627
- const key = owner ? path : getUrlPrefix() + encodeURIComponent(path);
130642
+ const key = owner ? path : getUrlPrefix(declaration.uri) + encodeURIComponent(path);
130628
130643
  const groupId = this.uuidOf(namespace, key);
130629
130644
  this.groupKeys.set(groupId, { namespace, key, path });
130630
130645
  this.groupOf.set(declaration, groupId);
@@ -130809,6 +130824,12 @@ ${JSON.stringify(message, null, 4)}`);
130809
130824
  // 343
130810
130825
  [SysMLv2Parser.RULE_requirementDefinition, "requirement def" /* RequirementDef */],
130811
130826
  // 384
130827
+ [SysMLv2Parser.RULE_concernDefinition, "concern def" /* ConcernDef */],
130828
+ // 405
130829
+ [SysMLv2Parser.RULE_concernUsage, "concern" /* ConcernUsage */],
130830
+ // 406
130831
+ [SysMLv2Parser.RULE_framedConcernUsage, "concern" /* ConcernUsage */],
130832
+ // 394
130812
130833
  [SysMLv2Parser.RULE_constraintDefinition, "constraint def" /* ConstraintDef */],
130813
130834
  // 380
130814
130835
  [SysMLv2Parser.RULE_itemDefinition, "item def" /* ItemDef */],
@@ -130881,6 +130902,10 @@ ${JSON.stringify(message, null, 4)}`);
130881
130902
  // 268
130882
130903
  [SysMLv2Parser.RULE_performActionUsage, "perform action" /* PerformActionUsage */],
130883
130904
  // 298
130905
+ // A state's `entry`/`do`/`exit` action (`exit action a : A;`, `exit a;`): the
130906
+ // StatePerformActionUsage production, a PerformActionUsage without `perform`.
130907
+ [SysMLv2Parser.RULE_statePerformActionUsage, "perform action" /* PerformActionUsage */],
130908
+ // 352
130884
130909
  [SysMLv2Parser.RULE_exhibitStateUsage, "exhibit state" /* ExhibitStateUsage */],
130885
130910
  // 359
130886
130911
  [SysMLv2Parser.RULE_transitionUsage, "transition" /* TransitionUsage */],
@@ -131004,8 +131029,10 @@ ${JSON.stringify(message, null, 4)}`);
131004
131029
  // 210
131005
131030
  SysMLv2Parser.RULE_definition,
131006
131031
  // 192
131007
- SysMLv2Parser.RULE_usage
131032
+ SysMLv2Parser.RULE_usage,
131008
131033
  // 208
131034
+ SysMLv2Parser.RULE_payloadFeatureSpecializationPart
131035
+ // 157
131009
131036
  ]);
131010
131037
  var DOC_RULE_INDICES = /* @__PURE__ */ new Set([
131011
131038
  SysMLv2Parser.RULE_comment,
@@ -131630,7 +131657,7 @@ ${JSON.stringify(message, null, 4)}`);
131630
131657
  const shortName = transition || isFlowUsage(kind) && !declaredName ? void 0 : this.extractShortName(ctx);
131631
131658
  const qualifiedName = anonymous || owner && owner.qualifiedName === void 0 ? void 0 : owner ? `${owner.qualifiedName}::${name}` : name;
131632
131659
  const selectionRange = anonymous ? ctx.start ? tokenToRange(ctx.start) : range : this.extractNameRange(ctx) ?? range;
131633
- const typeNames = isFlowUsage(kind) ? this.extractFlowTypeNames(ctx) : this.extractTypeNames(ctx);
131660
+ const typeNames = isFlowUsage(kind) ? this.extractFlowTypeNames(ctx) : transition ? this.extractTransitionTypeNames(ctx) : this.extractTypeNames(ctx);
131634
131661
  const specializationNames = this.extractSpecializationNames(ctx);
131635
131662
  const typeName = typeNames[0];
131636
131663
  const documentation = this.extractDocumentation(ctx);
@@ -131745,6 +131772,25 @@ ${JSON.stringify(message, null, 4)}`);
131745
131772
  const typeDeclaration = declaration && (this.findOwnHeaderRule(declaration, SysMLv2Parser.RULE_featureDeclaration) ?? this.findOwnHeaderRule(declaration, SysMLv2Parser.RULE_usageDeclaration));
131746
131773
  return typeDeclaration ? this.extractTypeNames(typeDeclaration) : [];
131747
131774
  }
131775
+ /**
131776
+ * A transition's own typing, then the types of the payload its trigger
131777
+ * accepts (`accept cmd : Command`, `accept Command`; TriggerAction ->
131778
+ * AcceptParameterPart -> PayloadParameter). Read from the parse tree only:
131779
+ * the regex fallback of `extractTypeNames` runs on whitespace-free text, where
131780
+ * the payload type runs into the guard, effect or target that follows it
131781
+ * (`Commandifcmd`, `Commandthenbusy`).
131782
+ */
131783
+ extractTransitionTypeNames(ctx) {
131784
+ const names = [];
131785
+ this.collectTypeNamesFromTree(ctx, names, 0);
131786
+ const trigger = this.findOwnHeaderRule(ctx, SysMLv2Parser.RULE_triggerActionMember);
131787
+ const parameter = trigger && this.findRuleContext(trigger, SysMLv2Parser.RULE_payloadParameter);
131788
+ if (parameter) {
131789
+ const payload = this.findRuleContext(parameter, SysMLv2Parser.RULE_payloadFeature) ?? parameter;
131790
+ this.collectTypeNamesFromTree(payload, names, 0);
131791
+ }
131792
+ return names;
131793
+ }
131748
131794
  /** Find the first descendant of `ctx` with rule `ruleIndex`, in source order. */
131749
131795
  findRuleContext(ctx, ruleIndex) {
131750
131796
  const matches = [];
@@ -131870,7 +131916,7 @@ ${JSON.stringify(message, null, 4)}`);
131870
131916
  */
131871
131917
  anonymousDeclaration(ctx, kind, transition) {
131872
131918
  if (!isDefinition(kind) && !isUsage(kind)) return void 0;
131873
- if (transition && !(transition.source && transition.target) && this.extractTypeNames(ctx).length === 0) return void 0;
131919
+ if (transition && !(transition.source && transition.target) && this.extractTransitionTypeNames(ctx).length === 0) return void 0;
131874
131920
  return this.declarationText(ctx);
131875
131921
  }
131876
131922
  /**
@@ -132950,6 +132996,17 @@ ${JSON.stringify(message, null, 4)}`);
132950
132996
  }
132951
132997
  return this.wsSymbolTable;
132952
132998
  }
132999
+ /**
133000
+ * Rebuild the symbol tables of `uris` on next use, e.g. after their projectId
133001
+ * changed, which changes their symbol IDs (see `setFolderProjectIds`).
133002
+ */
133003
+ invalidateSymbols(uris) {
133004
+ for (const uri of uris) {
133005
+ const cached = this.cache.get(uri);
133006
+ if (cached) cached.symbolTable = void 0;
133007
+ this.wsBuiltVersions.delete(uri);
133008
+ }
133009
+ }
132953
133010
  /**
132954
133011
  * Cache semantic diagnostics for the current document version.
132955
133012
  */
@@ -139305,6 +139362,8 @@ ${childIndent}item def ${unresolvedType};
139305
139362
  // Requirements → Object
139306
139363
  case "requirement def" /* RequirementDef */:
139307
139364
  case "requirement" /* RequirementUsage */:
139365
+ case "concern def" /* ConcernDef */:
139366
+ case "concern" /* ConcernUsage */:
139308
139367
  case "actor" /* ActorUsage */:
139309
139368
  case "subject" /* SubjectUsage */:
139310
139369
  case "stakeholder" /* StakeholderUsage */:
@@ -141704,6 +141763,10 @@ ${info.documentation}`);
141704
141763
  var hasConfigurationCapability = false;
141705
141764
  var hasWorkspaceFolderCapability = false;
141706
141765
  var workspaceRoots = [];
141766
+ var workspaceFolderUris = [];
141767
+ function rootsOf(folderUris) {
141768
+ return folderUris.map((uri) => toFsPath(uri)).filter((p) => p !== void 0);
141769
+ }
141707
141770
  var isWorkspaceFile = false;
141708
141771
  var preloadOnOpen = "workspaceOnly";
141709
141772
  var disabledDiagnosticCodes = /* @__PURE__ */ new Set();
@@ -141715,6 +141778,10 @@ ${info.documentation}`);
141715
141778
  }
141716
141779
  var skipDirs = new Set(DEFAULT_SKIP_DIRS);
141717
141780
  var serverReady = false;
141781
+ var resolveInitialization;
141782
+ var initialization = new Promise((resolve) => {
141783
+ resolveInitialization = resolve;
141784
+ });
141718
141785
  var earlyOpenUris = /* @__PURE__ */ new Set();
141719
141786
  var workerRequestId = 0;
141720
141787
  var isBrowserServer = true;
@@ -141828,7 +141895,8 @@ ${info.documentation}`);
141828
141895
  connection.console.warn(`projectId is not a UUID: ${initOpts.projectId}; symbol IDs are unique only within this workspace`);
141829
141896
  }
141830
141897
  if (params.workspaceFolders) {
141831
- workspaceRoots = params.workspaceFolders.map((f2) => toFsPath(f2.uri)).filter((p) => p !== void 0);
141898
+ workspaceFolderUris = params.workspaceFolders.map((f2) => f2.uri);
141899
+ workspaceRoots = rootsOf(workspaceFolderUris);
141832
141900
  } else if (params.rootUri) {
141833
141901
  const root = toFsPath(params.rootUri);
141834
141902
  if (root) workspaceRoots = [root];
@@ -141878,7 +141946,8 @@ ${info.documentation}`);
141878
141946
  if (hasWorkspaceFolderCapability) {
141879
141947
  result.capabilities.workspace = {
141880
141948
  workspaceFolders: {
141881
- supported: true
141949
+ supported: true,
141950
+ changeNotifications: true
141882
141951
  }
141883
141952
  };
141884
141953
  }
@@ -141890,8 +141959,17 @@ ${info.documentation}`);
141890
141959
  import_node11.DidChangeConfigurationNotification.type,
141891
141960
  void 0
141892
141961
  );
141962
+ }
141963
+ if (hasWorkspaceFolderCapability) {
141964
+ connection.workspace.onDidChangeWorkspaceFolders((event) => {
141965
+ onWorkspaceFoldersChanged(event).catch((e) => connection.console.error(`Workspace folder change failed: ${e}`));
141966
+ });
141967
+ }
141968
+ if (hasConfigurationCapability) {
141893
141969
  await pullSettings().catch(() => {
141894
141970
  });
141971
+ await pullFolderProjectIds().catch(() => {
141972
+ });
141895
141973
  }
141896
141974
  const libCount = initLibraryIndex("/");
141897
141975
  connection.console.log(`SysML v2 Language Server initialized (${libCount} library packages indexed)`);
@@ -141907,14 +141985,13 @@ ${info.documentation}`);
141907
141985
  );
141908
141986
  }
141909
141987
  serverReady = true;
141988
+ resolveInitialization();
141910
141989
  spawnParseWorker();
141911
141990
  if (workspaceRoots.length > 0 && (preloadOnOpen === "always" || preloadOnOpen === "workspaceOnly" && isWorkspaceFile)) {
141912
- documentManager.setWorkspaceScanComplete(false);
141913
- scanWorkspaceFoldersAsync(workspaceRoots).then(({ fileCount, scanMs }) => {
141991
+ scanWorkspaceFolders(workspaceRoots).then(({ fileCount, scanMs }) => {
141914
141992
  connection.console.log(
141915
141993
  `Workspace scan: pre-parsed ${fileCount} .sysml files in ${scanMs} ms`
141916
141994
  );
141917
- documentManager.setWorkspaceScanComplete(true);
141918
141995
  revalidateOpenDocuments();
141919
141996
  });
141920
141997
  }
@@ -141949,9 +142026,79 @@ ${info.documentation}`);
141949
142026
  }
141950
142027
  }
141951
142028
  connection.onDidChangeConfiguration((_change) => {
141952
- pullSettings().then(() => revalidateOpenDocuments()).catch(() => {
142029
+ pullSettings().then(() => pullFolderProjectIds()).then(() => revalidateOpenDocuments()).catch(() => {
142030
+ });
142031
+ });
142032
+ function latestWins(task) {
142033
+ let calls = 0;
142034
+ let latest = Promise.resolve();
142035
+ return () => {
142036
+ const call = ++calls;
142037
+ const isSuperseded = () => call !== calls;
142038
+ const run = task(isSuperseded).then(() => isSuperseded() ? latest : void 0);
142039
+ latest = run;
142040
+ return run;
142041
+ };
142042
+ }
142043
+ var pullFolderProjectIds = latestWins(async (isSuperseded) => {
142044
+ if (!hasConfigurationCapability) return;
142045
+ const folderUris = [...workspaceFolderUris];
142046
+ const answers = folderUris.length === 0 ? [] : await connection.workspace.getConfiguration(folderUris.map((scopeUri) => ({ scopeUri, section: "sysml.project" })));
142047
+ if (isSuperseded()) return;
142048
+ const projectIds = folderUris.flatMap((folderUri, i) => {
142049
+ const projectId = answers[i]?.projectId;
142050
+ return typeof projectId === "string" ? [{ folderUri, projectId }] : [];
141953
142051
  });
142052
+ const uris = documentManager.getUris();
142053
+ const prefixBefore = new Map(uris.map((uri) => [uri, getUrlPrefix(uri)]));
142054
+ const rejected = setFolderProjectIds(projectIds);
142055
+ for (const entry of rejected) {
142056
+ connection.console.warn(`projectId of ${entry.folderUri} is not a UUID: ${entry.projectId}; ignored, so its documents use the projectId of an enclosing folder, else initializationOptions.projectId, else the default prefix`);
142057
+ }
142058
+ documentManager.invalidateSymbols(uris.filter((uri) => getUrlPrefix(uri) !== prefixBefore.get(uri)));
142059
+ logFolderPrefixes(new Set(projectIds.filter((p) => !rejected.includes(p)).map((p) => p.folderUri)));
141954
142060
  });
142061
+ var loggedFolderPrefixes = /* @__PURE__ */ new Map();
142062
+ function logFolderPrefixes(ownProjectIds) {
142063
+ for (const folderUri of workspaceFolderUris) {
142064
+ const prefix = getUrlPrefix(`${folderUri.endsWith("/") ? folderUri : `${folderUri}/`}_.sysml`);
142065
+ if (loggedFolderPrefixes.get(folderUri) === prefix) continue;
142066
+ loggedFolderPrefixes.set(folderUri, prefix);
142067
+ const source = ownProjectIds.has(folderUri) ? "its projectId" : prefix === getUrlPrefix() ? prefix === DEFAULT_URL_PREFIX ? "the default prefix, no projectId" : "initializationOptions.projectId" : "an enclosing folder's projectId";
142068
+ connection.console.info(`Folder ${folderUri}: top-level elements' URLs start with ${prefix} (${source})`);
142069
+ }
142070
+ for (const folderUri of loggedFolderPrefixes.keys()) {
142071
+ if (!workspaceFolderUris.includes(folderUri)) loggedFolderPrefixes.delete(folderUri);
142072
+ }
142073
+ }
142074
+ async function onWorkspaceFoldersChanged(event) {
142075
+ const initialScanPending = !serverReady;
142076
+ const removed = new Set(event.removed.map((f2) => f2.uri));
142077
+ workspaceFolderUris = [...workspaceFolderUris.filter((uri) => !removed.has(uri)), ...event.added.map((f2) => f2.uri)];
142078
+ workspaceRoots = rootsOf(workspaceFolderUris);
142079
+ dropDocumentsOutsideWorkspace(event.removed.map((f2) => f2.uri));
142080
+ await pullFolderProjectIds().catch(() => {
142081
+ });
142082
+ if (initialScanPending) return;
142083
+ const addedRoots = rootsOf(event.added.map((f2) => f2.uri).filter((uri) => workspaceFolderUris.includes(uri)));
142084
+ if (addedRoots.length > 0 && (preloadOnOpen === "always" || preloadOnOpen === "workspaceOnly" && isWorkspaceFile)) {
142085
+ const { fileCount, scanMs } = await scanWorkspaceFolders(addedRoots);
142086
+ connection.console.log(`Workspace scan: pre-parsed ${fileCount} .sysml files of ${addedRoots.length} added folder(s) in ${scanMs} ms`);
142087
+ }
142088
+ revalidateOpenDocuments();
142089
+ }
142090
+ function dropDocumentsOutsideWorkspace(folderUris) {
142091
+ for (const uri of documentManager.getUris()) {
142092
+ if (openDocumentFor(uri)) continue;
142093
+ if (folderUris.some((f2) => isInFolder(uri, f2)) && !isInWorkspace(uri)) {
142094
+ documentManager.remove(uri);
142095
+ modelProvider.removeUri(uri);
142096
+ }
142097
+ }
142098
+ }
142099
+ function isInWorkspace(uri) {
142100
+ return workspaceRoots.some((root) => isInFolder(uri, pathToFileURL(root).toString()));
142101
+ }
141955
142102
  function openDocumentFor(uri) {
141956
142103
  return documents.get(uri) ?? documents.all().find((d) => isSameDocumentUri(d.uri, uri));
141957
142104
  }
@@ -141976,6 +142123,16 @@ ${info.documentation}`);
141976
142123
  documentManager.parse(doc);
141977
142124
  return true;
141978
142125
  }
142126
+ var runningScans = 0;
142127
+ async function scanWorkspaceFolders(roots) {
142128
+ runningScans++;
142129
+ documentManager.setWorkspaceScanComplete(false);
142130
+ try {
142131
+ return await scanWorkspaceFoldersAsync(roots);
142132
+ } finally {
142133
+ if (--runningScans === 0) documentManager.setWorkspaceScanComplete(true);
142134
+ }
142135
+ }
141979
142136
  async function scanWorkspaceFoldersAsync(roots) {
141980
142137
  const scanT0 = Date.now();
141981
142138
  const fileArrays = await Promise.all(roots.map((r) => findSysMLFilesAsync(r, skipDirs)));
@@ -141987,6 +142144,7 @@ ${info.documentation}`);
141987
142144
  const filePath = allFiles[i];
141988
142145
  const uri = pathToFileURL(filePath).toString();
141989
142146
  if (openDocumentFor(uri)) continue;
142147
+ if (!isInWorkspace(uri)) continue;
141990
142148
  const content = fileContents.get(filePath);
141991
142149
  if (content === void 0) continue;
141992
142150
  documentManager.parseBatch(uri, 0, content);
@@ -142199,14 +142357,16 @@ ${info.documentation}`);
142199
142357
  return formattingProvider.provideRangeFormatting(params);
142200
142358
  }
142201
142359
  );
142202
- connection.onRequest("sysml/model", (params) => {
142360
+ connection.onRequest("sysml/model", async (params) => {
142361
+ await initialization;
142203
142362
  return modelProvider.getModel(
142204
142363
  params.textDocument.uri,
142205
142364
  documentManager.getVersion(params.textDocument.uri),
142206
142365
  params.scope
142207
142366
  );
142208
142367
  });
142209
- connection.onRequest("sysml/elementLookup", (params) => {
142368
+ connection.onRequest("sysml/elementLookup", async (params) => {
142369
+ await initialization;
142210
142370
  return elementLookupProvider.elementLookup(params);
142211
142371
  });
142212
142372
  connection.onRequest("sysml/serverStats", () => {
@@ -42741,6 +42741,9 @@ var Protocol = class {
42741
42741
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
42742
42742
  const handleTaskResult = async () => {
42743
42743
  const taskId = request.params.taskId;
42744
+ if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
42745
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
42746
+ }
42744
42747
  if (this._taskMessageQueue) {
42745
42748
  let queuedMessage;
42746
42749
  while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
@@ -42771,12 +42774,12 @@ var Protocol = class {
42771
42774
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
42772
42775
  }
42773
42776
  if (!isTerminal(task.status)) {
42774
- await this._waitForTaskUpdate(taskId, extra.signal);
42777
+ await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
42775
42778
  return await handleTaskResult();
42776
42779
  }
42777
42780
  if (isTerminal(task.status)) {
42778
42781
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
42779
- this._clearTaskQueue(taskId);
42782
+ this._clearTaskQueue(taskId, extra.sessionId);
42780
42783
  return {
42781
42784
  ...result,
42782
42785
  _meta: {
@@ -42813,7 +42816,7 @@ var Protocol = class {
42813
42816
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
42814
42817
  }
42815
42818
  await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
42816
- this._clearTaskQueue(request.params.taskId);
42819
+ this._clearTaskQueue(request.params.taskId, extra.sessionId);
42817
42820
  const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
42818
42821
  if (!cancelledTask) {
42819
42822
  throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`);
@@ -42941,6 +42944,19 @@ var Protocol = class {
42941
42944
  const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
42942
42945
  const capturedTransport = this._transport;
42943
42946
  const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
42947
+ const sessionId = capturedTransport?.sessionId;
42948
+ const store = this._taskStore;
42949
+ let relatedTaskFound = true;
42950
+ let relatedTaskLookup;
42951
+ if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
42952
+ relatedTaskFound = false;
42953
+ relatedTaskLookup = (async () => {
42954
+ if (!await store.getTask(relatedTaskId, sessionId)) {
42955
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
42956
+ }
42957
+ relatedTaskFound = true;
42958
+ })();
42959
+ }
42944
42960
  if (handler === void 0) {
42945
42961
  const errorResponse = {
42946
42962
  jsonrpc: "2.0",
@@ -42950,7 +42966,10 @@ var Protocol = class {
42950
42966
  message: "Method not found"
42951
42967
  }
42952
42968
  };
42953
- if (relatedTaskId && this._taskMessageQueue) {
42969
+ if (relatedTaskId && relatedTaskLookup) {
42970
+ const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
42971
+ relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error62) => this._onerror(new Error(`Failed to send an error response: ${error62}`)));
42972
+ } else if (relatedTaskId && this._taskMessageQueue) {
42954
42973
  this._enqueueTaskMessage(relatedTaskId, {
42955
42974
  type: "error",
42956
42975
  message: errorResponse,
@@ -43001,7 +43020,10 @@ var Protocol = class {
43001
43020
  closeSSEStream: extra?.closeSSEStream,
43002
43021
  closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
43003
43022
  };
43004
- Promise.resolve().then(() => {
43023
+ (relatedTaskLookup ?? Promise.resolve()).then(() => {
43024
+ if (relatedTaskLookup && abortController.signal.aborted) {
43025
+ throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
43026
+ }
43005
43027
  if (taskCreationParams) {
43006
43028
  this.assertTaskHandlerCapability(request.method);
43007
43029
  }
@@ -43036,7 +43058,7 @@ var Protocol = class {
43036
43058
  ...error62["data"] !== void 0 && { data: error62["data"] }
43037
43059
  }
43038
43060
  };
43039
- if (relatedTaskId && this._taskMessageQueue) {
43061
+ if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
43040
43062
  await this._enqueueTaskMessage(relatedTaskId, {
43041
43063
  type: "error",
43042
43064
  message: errorResponse,
@@ -43516,7 +43538,7 @@ var Protocol = class {
43516
43538
  throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
43517
43539
  }
43518
43540
  const maxQueueSize = this._options?.maxTaskQueueSize;
43519
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
43541
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
43520
43542
  }
43521
43543
  /**
43522
43544
  * Clears the message queue for a task and rejects any pending request resolvers.
@@ -43545,12 +43567,13 @@ var Protocol = class {
43545
43567
  * Uses polling to check for updates at the task's configured poll interval.
43546
43568
  * @param taskId The task ID to wait for
43547
43569
  * @param signal Abort signal to cancel the wait
43570
+ * @param sessionId Session of the request that waits, passed to the task store
43548
43571
  * @returns Promise that resolves when an update occurs or rejects if aborted
43549
43572
  */
43550
- async _waitForTaskUpdate(taskId, signal) {
43573
+ async _waitForTaskUpdate(taskId, signal, sessionId) {
43551
43574
  let interval = this._options?.defaultTaskPollInterval ?? 1e3;
43552
43575
  try {
43553
- const task = await this._taskStore?.getTask(taskId);
43576
+ const task = await this._taskStore?.getTask(taskId, sessionId);
43554
43577
  if (task?.pollInterval) {
43555
43578
  interval = task.pollInterval;
43556
43579
  }
@@ -44429,6 +44452,42 @@ var ExperimentalMcpServerTasks = class {
44429
44452
  };
44430
44453
 
44431
44454
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
44455
+ function toolInputElementCount(value, max) {
44456
+ let count = 0;
44457
+ const stack = [value];
44458
+ while (stack.length > 0) {
44459
+ const node2 = stack.pop();
44460
+ if (node2 === null || typeof node2 !== "object")
44461
+ continue;
44462
+ if (Array.isArray(node2)) {
44463
+ for (const child of node2) {
44464
+ if (++count > max)
44465
+ return count;
44466
+ if (child !== null && typeof child === "object")
44467
+ stack.push(child);
44468
+ }
44469
+ } else {
44470
+ for (const key in node2) {
44471
+ if (!Object.prototype.hasOwnProperty.call(node2, key))
44472
+ continue;
44473
+ if (++count > max)
44474
+ return count;
44475
+ const child = node2[key];
44476
+ if (child !== null && typeof child === "object")
44477
+ stack.push(child);
44478
+ }
44479
+ }
44480
+ }
44481
+ return count;
44482
+ }
44483
+ function resolveMaxToolInputElements(value) {
44484
+ if (value === void 0 || value === Infinity)
44485
+ return void 0;
44486
+ if (typeof value !== "number" || Number.isNaN(value) || value < 1) {
44487
+ throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
44488
+ }
44489
+ return value;
44490
+ }
44432
44491
  var McpServer = class {
44433
44492
  constructor(serverInfo, options) {
44434
44493
  this._registeredResources = {};
@@ -44440,6 +44499,7 @@ var McpServer = class {
44440
44499
  this._resourceHandlersInitialized = false;
44441
44500
  this._promptHandlersInitialized = false;
44442
44501
  this.server = new Server(serverInfo, options);
44502
+ this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
44443
44503
  }
44444
44504
  /**
44445
44505
  * Access experimental features.
@@ -44570,12 +44630,15 @@ var McpServer = class {
44570
44630
  * Validates tool input arguments against the tool's input schema.
44571
44631
  */
44572
44632
  async validateToolInput(tool, args, toolName) {
44633
+ if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) {
44634
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
44635
+ }
44573
44636
  if (!tool.inputSchema) {
44574
44637
  return void 0;
44575
44638
  }
44576
44639
  const inputObj = normalizeObjectSchema(tool.inputSchema);
44577
44640
  const schemaToParse = inputObj ?? tool.inputSchema;
44578
- const parseResult = await safeParseAsync2(schemaToParse, args);
44641
+ const parseResult = await safeParseAsync2(schemaToParse, args ?? {});
44579
44642
  if (!parseResult.success) {
44580
44643
  const error62 = "error" in parseResult ? parseResult.error : "Unknown error";
44581
44644
  const errorMessage = getParseErrorMessage(error62);
@@ -44813,7 +44876,7 @@ var McpServer = class {
44813
44876
  }
44814
44877
  if (prompt.argsSchema) {
44815
44878
  const argsObj = normalizeObjectSchema(prompt.argsSchema);
44816
- const parseResult = await safeParseAsync2(argsObj, request.params.arguments);
44879
+ const parseResult = await safeParseAsync2(argsObj, request.params.arguments ?? {});
44817
44880
  if (!parseResult.success) {
44818
44881
  const error62 = "error" in parseResult ? parseResult.error : "Unknown error";
44819
44882
  const errorMessage = getParseErrorMessage(error62);
@@ -45355,6 +45418,8 @@ var SysMLElementKind = /* @__PURE__ */ ((SysMLElementKind2) => {
45355
45418
  SysMLElementKind2["TransitionUsage"] = "transition";
45356
45419
  SysMLElementKind2["RequirementDef"] = "requirement def";
45357
45420
  SysMLElementKind2["RequirementUsage"] = "requirement";
45421
+ SysMLElementKind2["ConcernDef"] = "concern def";
45422
+ SysMLElementKind2["ConcernUsage"] = "concern";
45358
45423
  SysMLElementKind2["ConstraintDef"] = "constraint def";
45359
45424
  SysMLElementKind2["ConstraintUsage"] = "constraint";
45360
45425
  SysMLElementKind2["ItemDef"] = "item def";
@@ -45466,6 +45531,10 @@ function toMetaclassName(kind) {
45466
45531
  return "RequirementDefinition";
45467
45532
  case "requirement" /* RequirementUsage */:
45468
45533
  return "RequirementUsage";
45534
+ case "concern def" /* ConcernDef */:
45535
+ return "ConcernDefinition";
45536
+ case "concern" /* ConcernUsage */:
45537
+ return "ConcernUsage";
45469
45538
  case "constraint def" /* ConstraintDef */:
45470
45539
  return "ConstraintDefinition";
45471
45540
  case "constraint" /* ConstraintUsage */:
@@ -158884,6 +158953,10 @@ function getLibraryPackageNames() {
158884
158953
  function canonicalUri(uri) {
158885
158954
  return uri.replace(/^file:\/\/\/([A-Za-z])(?::|%3[Aa])(?=\/|$)/, (_m, drive) => `file:///${drive.toLowerCase()}:`);
158886
158955
  }
158956
+ function isInFolder(uri, folderUri) {
158957
+ const folder = canonicalUri(folderUri);
158958
+ return canonicalUri(uri).startsWith(folder.endsWith("/") ? folder : `${folder}/`);
158959
+ }
158887
158960
  function isSameDocumentUri(a, b) {
158888
158961
  return a === b || canonicalUri(a) === canonicalUri(b);
158889
158962
  }
@@ -159474,6 +159547,7 @@ var NAMESPACE_URL = v5_default.URL;
159474
159547
  var DEFAULT_URL_PREFIX = "https://github.com/daltskin/sysml-v2-lsp/";
159475
159548
  var PROJECT_ID_ENV_VAR = "SYSML_PROJECT_ID";
159476
159549
  var urlPrefix = DEFAULT_URL_PREFIX;
159550
+ var folderPrefixes = [];
159477
159551
  function setProjectId(projectId2) {
159478
159552
  if (projectId2 === void 0) {
159479
159553
  urlPrefix = DEFAULT_URL_PREFIX;
@@ -159483,8 +159557,9 @@ function setProjectId(projectId2) {
159483
159557
  urlPrefix = `urn:uuid:${projectId2.toLowerCase()}/`;
159484
159558
  return true;
159485
159559
  }
159486
- function getUrlPrefix() {
159487
- return urlPrefix;
159560
+ function getUrlPrefix(uri) {
159561
+ const match = uri === void 0 ? void 0 : folderPrefixes.find((f) => isInFolder(uri, f.folderUri));
159562
+ return match?.prefix ?? urlPrefix;
159488
159563
  }
159489
159564
  function uuidV5(namespace, name) {
159490
159565
  return v5_default(name, namespace);
@@ -159544,7 +159619,7 @@ var IdRegistry = class {
159544
159619
  declaration.parentId = owner?.symbolId;
159545
159620
  const path = declaration.qualifiedName !== void 0 ? this.qualifiedNameAsWritten(declaration) : owner ? `${this.paths.get(owner)}/${segmentOf(declaration)}` : segmentOf(declaration);
159546
159621
  const namespace = owner ? this.topLevelOf(owner).symbolId : NAMESPACE_URL;
159547
- const key = owner ? path : getUrlPrefix() + encodeURIComponent(path);
159622
+ const key = owner ? path : getUrlPrefix(declaration.uri) + encodeURIComponent(path);
159548
159623
  const groupId = this.uuidOf(namespace, key);
159549
159624
  this.groupKeys.set(groupId, { namespace, key, path });
159550
159625
  this.groupOf.set(declaration, groupId);
@@ -159729,6 +159804,12 @@ var RULE_INDEX_TO_KIND = /* @__PURE__ */ new Map([
159729
159804
  // 343
159730
159805
  [SysMLv2Parser.RULE_requirementDefinition, "requirement def" /* RequirementDef */],
159731
159806
  // 384
159807
+ [SysMLv2Parser.RULE_concernDefinition, "concern def" /* ConcernDef */],
159808
+ // 405
159809
+ [SysMLv2Parser.RULE_concernUsage, "concern" /* ConcernUsage */],
159810
+ // 406
159811
+ [SysMLv2Parser.RULE_framedConcernUsage, "concern" /* ConcernUsage */],
159812
+ // 394
159732
159813
  [SysMLv2Parser.RULE_constraintDefinition, "constraint def" /* ConstraintDef */],
159733
159814
  // 380
159734
159815
  [SysMLv2Parser.RULE_itemDefinition, "item def" /* ItemDef */],
@@ -159801,6 +159882,10 @@ var RULE_INDEX_TO_KIND = /* @__PURE__ */ new Map([
159801
159882
  // 268
159802
159883
  [SysMLv2Parser.RULE_performActionUsage, "perform action" /* PerformActionUsage */],
159803
159884
  // 298
159885
+ // A state's `entry`/`do`/`exit` action (`exit action a : A;`, `exit a;`): the
159886
+ // StatePerformActionUsage production, a PerformActionUsage without `perform`.
159887
+ [SysMLv2Parser.RULE_statePerformActionUsage, "perform action" /* PerformActionUsage */],
159888
+ // 352
159804
159889
  [SysMLv2Parser.RULE_exhibitStateUsage, "exhibit state" /* ExhibitStateUsage */],
159805
159890
  // 359
159806
159891
  [SysMLv2Parser.RULE_transitionUsage, "transition" /* TransitionUsage */],
@@ -159924,8 +160009,10 @@ var TYPE_RECURSE_RULE_INDICES = /* @__PURE__ */ new Set([
159924
160009
  // 210
159925
160010
  SysMLv2Parser.RULE_definition,
159926
160011
  // 192
159927
- SysMLv2Parser.RULE_usage
160012
+ SysMLv2Parser.RULE_usage,
159928
160013
  // 208
160014
+ SysMLv2Parser.RULE_payloadFeatureSpecializationPart
160015
+ // 157
159929
160016
  ]);
159930
160017
  var DOC_RULE_INDICES = /* @__PURE__ */ new Set([
159931
160018
  SysMLv2Parser.RULE_comment,
@@ -160550,7 +160637,7 @@ var SymbolTable = class {
160550
160637
  const shortName = transition || isFlowUsage(kind) && !declaredName ? void 0 : this.extractShortName(ctx2);
160551
160638
  const qualifiedName = anonymous || owner && owner.qualifiedName === void 0 ? void 0 : owner ? `${owner.qualifiedName}::${name}` : name;
160552
160639
  const selectionRange = anonymous ? ctx2.start ? tokenToRange(ctx2.start) : range : this.extractNameRange(ctx2) ?? range;
160553
- const typeNames = isFlowUsage(kind) ? this.extractFlowTypeNames(ctx2) : this.extractTypeNames(ctx2);
160640
+ const typeNames = isFlowUsage(kind) ? this.extractFlowTypeNames(ctx2) : transition ? this.extractTransitionTypeNames(ctx2) : this.extractTypeNames(ctx2);
160554
160641
  const specializationNames = this.extractSpecializationNames(ctx2);
160555
160642
  const typeName = typeNames[0];
160556
160643
  const documentation = this.extractDocumentation(ctx2);
@@ -160665,6 +160752,25 @@ var SymbolTable = class {
160665
160752
  const typeDeclaration = declaration && (this.findOwnHeaderRule(declaration, SysMLv2Parser.RULE_featureDeclaration) ?? this.findOwnHeaderRule(declaration, SysMLv2Parser.RULE_usageDeclaration));
160666
160753
  return typeDeclaration ? this.extractTypeNames(typeDeclaration) : [];
160667
160754
  }
160755
+ /**
160756
+ * A transition's own typing, then the types of the payload its trigger
160757
+ * accepts (`accept cmd : Command`, `accept Command`; TriggerAction ->
160758
+ * AcceptParameterPart -> PayloadParameter). Read from the parse tree only:
160759
+ * the regex fallback of `extractTypeNames` runs on whitespace-free text, where
160760
+ * the payload type runs into the guard, effect or target that follows it
160761
+ * (`Commandifcmd`, `Commandthenbusy`).
160762
+ */
160763
+ extractTransitionTypeNames(ctx2) {
160764
+ const names = [];
160765
+ this.collectTypeNamesFromTree(ctx2, names, 0);
160766
+ const trigger = this.findOwnHeaderRule(ctx2, SysMLv2Parser.RULE_triggerActionMember);
160767
+ const parameter = trigger && this.findRuleContext(trigger, SysMLv2Parser.RULE_payloadParameter);
160768
+ if (parameter) {
160769
+ const payload = this.findRuleContext(parameter, SysMLv2Parser.RULE_payloadFeature) ?? parameter;
160770
+ this.collectTypeNamesFromTree(payload, names, 0);
160771
+ }
160772
+ return names;
160773
+ }
160668
160774
  /** Find the first descendant of `ctx` with rule `ruleIndex`, in source order. */
160669
160775
  findRuleContext(ctx2, ruleIndex) {
160670
160776
  const matches = [];
@@ -160790,7 +160896,7 @@ var SymbolTable = class {
160790
160896
  */
160791
160897
  anonymousDeclaration(ctx2, kind, transition) {
160792
160898
  if (!isDefinition(kind) && !isUsage(kind)) return void 0;
160793
- if (transition && !(transition.source && transition.target) && this.extractTypeNames(ctx2).length === 0) return void 0;
160899
+ if (transition && !(transition.source && transition.target) && this.extractTransitionTypeNames(ctx2).length === 0) return void 0;
160794
160900
  return this.declarationText(ctx2);
160795
160901
  }
160796
160902
  /**
@@ -121638,6 +121638,10 @@ var SYSML_KEYWORDS = new Set(SYSML_KEYWORDS_ARRAY);
121638
121638
  function canonicalUri(uri) {
121639
121639
  return uri.replace(/^file:\/\/\/([A-Za-z])(?::|%3[Aa])(?=\/|$)/, (_m, drive) => `file:///${drive.toLowerCase()}:`);
121640
121640
  }
121641
+ function isInFolder(uri, folderUri) {
121642
+ const folder = canonicalUri(folderUri);
121643
+ return canonicalUri(uri).startsWith(folder.endsWith("/") ? folder : `${folder}/`);
121644
+ }
121641
121645
  function isSameDocumentUri(a, b) {
121642
121646
  return a === b || canonicalUri(a) === canonicalUri(b);
121643
121647
  }
@@ -121740,6 +121744,7 @@ var v5_default = v5;
121740
121744
  var NAMESPACE_URL = v5_default.URL;
121741
121745
  var DEFAULT_URL_PREFIX = "https://github.com/daltskin/sysml-v2-lsp/";
121742
121746
  var urlPrefix = DEFAULT_URL_PREFIX;
121747
+ var folderPrefixes = [];
121743
121748
  function setProjectId(projectId) {
121744
121749
  if (projectId === void 0) {
121745
121750
  urlPrefix = DEFAULT_URL_PREFIX;
@@ -121749,8 +121754,14 @@ function setProjectId(projectId) {
121749
121754
  urlPrefix = `urn:uuid:${projectId.toLowerCase()}/`;
121750
121755
  return true;
121751
121756
  }
121752
- function getUrlPrefix() {
121753
- return urlPrefix;
121757
+ function setFolderProjectIds(projectIds) {
121758
+ const rejected = projectIds.filter((p) => !validate_default(p.projectId));
121759
+ folderPrefixes = projectIds.filter((p) => validate_default(p.projectId)).map((p) => ({ folderUri: p.folderUri, prefix: `urn:uuid:${p.projectId.toLowerCase()}/` })).sort((a, b) => b.folderUri.length - a.folderUri.length);
121760
+ return rejected;
121761
+ }
121762
+ function getUrlPrefix(uri) {
121763
+ const match = uri === void 0 ? void 0 : folderPrefixes.find((f) => isInFolder(uri, f.folderUri));
121764
+ return match?.prefix ?? urlPrefix;
121754
121765
  }
121755
121766
  function uuidV5(namespace, name) {
121756
121767
  return v5_default(name, namespace);
@@ -121832,6 +121843,10 @@ function toMetaclassName(kind) {
121832
121843
  return "RequirementDefinition";
121833
121844
  case "requirement" /* RequirementUsage */:
121834
121845
  return "RequirementUsage";
121846
+ case "concern def" /* ConcernDef */:
121847
+ return "ConcernDefinition";
121848
+ case "concern" /* ConcernUsage */:
121849
+ return "ConcernUsage";
121835
121850
  case "constraint def" /* ConstraintDef */:
121836
121851
  return "ConstraintDefinition";
121837
121852
  case "constraint" /* ConstraintUsage */:
@@ -121954,7 +121969,7 @@ var IdRegistry = class {
121954
121969
  declaration.parentId = owner?.symbolId;
121955
121970
  const path3 = declaration.qualifiedName !== void 0 ? this.qualifiedNameAsWritten(declaration) : owner ? `${this.paths.get(owner)}/${segmentOf(declaration)}` : segmentOf(declaration);
121956
121971
  const namespace = owner ? this.topLevelOf(owner).symbolId : NAMESPACE_URL;
121957
- const key = owner ? path3 : getUrlPrefix() + encodeURIComponent(path3);
121972
+ const key = owner ? path3 : getUrlPrefix(declaration.uri) + encodeURIComponent(path3);
121958
121973
  const groupId = this.uuidOf(namespace, key);
121959
121974
  this.groupKeys.set(groupId, { namespace, key, path: path3 });
121960
121975
  this.groupOf.set(declaration, groupId);
@@ -122139,6 +122154,12 @@ var RULE_INDEX_TO_KIND = /* @__PURE__ */ new Map([
122139
122154
  // 343
122140
122155
  [SysMLv2Parser.RULE_requirementDefinition, "requirement def" /* RequirementDef */],
122141
122156
  // 384
122157
+ [SysMLv2Parser.RULE_concernDefinition, "concern def" /* ConcernDef */],
122158
+ // 405
122159
+ [SysMLv2Parser.RULE_concernUsage, "concern" /* ConcernUsage */],
122160
+ // 406
122161
+ [SysMLv2Parser.RULE_framedConcernUsage, "concern" /* ConcernUsage */],
122162
+ // 394
122142
122163
  [SysMLv2Parser.RULE_constraintDefinition, "constraint def" /* ConstraintDef */],
122143
122164
  // 380
122144
122165
  [SysMLv2Parser.RULE_itemDefinition, "item def" /* ItemDef */],
@@ -122211,6 +122232,10 @@ var RULE_INDEX_TO_KIND = /* @__PURE__ */ new Map([
122211
122232
  // 268
122212
122233
  [SysMLv2Parser.RULE_performActionUsage, "perform action" /* PerformActionUsage */],
122213
122234
  // 298
122235
+ // A state's `entry`/`do`/`exit` action (`exit action a : A;`, `exit a;`): the
122236
+ // StatePerformActionUsage production, a PerformActionUsage without `perform`.
122237
+ [SysMLv2Parser.RULE_statePerformActionUsage, "perform action" /* PerformActionUsage */],
122238
+ // 352
122214
122239
  [SysMLv2Parser.RULE_exhibitStateUsage, "exhibit state" /* ExhibitStateUsage */],
122215
122240
  // 359
122216
122241
  [SysMLv2Parser.RULE_transitionUsage, "transition" /* TransitionUsage */],
@@ -122334,8 +122359,10 @@ var TYPE_RECURSE_RULE_INDICES = /* @__PURE__ */ new Set([
122334
122359
  // 210
122335
122360
  SysMLv2Parser.RULE_definition,
122336
122361
  // 192
122337
- SysMLv2Parser.RULE_usage
122362
+ SysMLv2Parser.RULE_usage,
122338
122363
  // 208
122364
+ SysMLv2Parser.RULE_payloadFeatureSpecializationPart
122365
+ // 157
122339
122366
  ]);
122340
122367
  var DOC_RULE_INDICES = /* @__PURE__ */ new Set([
122341
122368
  SysMLv2Parser.RULE_comment,
@@ -122960,7 +122987,7 @@ var SymbolTable = class {
122960
122987
  const shortName = transition || isFlowUsage(kind) && !declaredName ? void 0 : this.extractShortName(ctx);
122961
122988
  const qualifiedName = anonymous || owner && owner.qualifiedName === void 0 ? void 0 : owner ? `${owner.qualifiedName}::${name}` : name;
122962
122989
  const selectionRange = anonymous ? ctx.start ? tokenToRange(ctx.start) : range : this.extractNameRange(ctx) ?? range;
122963
- const typeNames = isFlowUsage(kind) ? this.extractFlowTypeNames(ctx) : this.extractTypeNames(ctx);
122990
+ const typeNames = isFlowUsage(kind) ? this.extractFlowTypeNames(ctx) : transition ? this.extractTransitionTypeNames(ctx) : this.extractTypeNames(ctx);
122964
122991
  const specializationNames = this.extractSpecializationNames(ctx);
122965
122992
  const typeName = typeNames[0];
122966
122993
  const documentation = this.extractDocumentation(ctx);
@@ -123075,6 +123102,25 @@ var SymbolTable = class {
123075
123102
  const typeDeclaration = declaration && (this.findOwnHeaderRule(declaration, SysMLv2Parser.RULE_featureDeclaration) ?? this.findOwnHeaderRule(declaration, SysMLv2Parser.RULE_usageDeclaration));
123076
123103
  return typeDeclaration ? this.extractTypeNames(typeDeclaration) : [];
123077
123104
  }
123105
+ /**
123106
+ * A transition's own typing, then the types of the payload its trigger
123107
+ * accepts (`accept cmd : Command`, `accept Command`; TriggerAction ->
123108
+ * AcceptParameterPart -> PayloadParameter). Read from the parse tree only:
123109
+ * the regex fallback of `extractTypeNames` runs on whitespace-free text, where
123110
+ * the payload type runs into the guard, effect or target that follows it
123111
+ * (`Commandifcmd`, `Commandthenbusy`).
123112
+ */
123113
+ extractTransitionTypeNames(ctx) {
123114
+ const names = [];
123115
+ this.collectTypeNamesFromTree(ctx, names, 0);
123116
+ const trigger = this.findOwnHeaderRule(ctx, SysMLv2Parser.RULE_triggerActionMember);
123117
+ const parameter = trigger && this.findRuleContext(trigger, SysMLv2Parser.RULE_payloadParameter);
123118
+ if (parameter) {
123119
+ const payload = this.findRuleContext(parameter, SysMLv2Parser.RULE_payloadFeature) ?? parameter;
123120
+ this.collectTypeNamesFromTree(payload, names, 0);
123121
+ }
123122
+ return names;
123123
+ }
123078
123124
  /** Find the first descendant of `ctx` with rule `ruleIndex`, in source order. */
123079
123125
  findRuleContext(ctx, ruleIndex) {
123080
123126
  const matches = [];
@@ -123200,7 +123246,7 @@ var SymbolTable = class {
123200
123246
  */
123201
123247
  anonymousDeclaration(ctx, kind, transition) {
123202
123248
  if (!isDefinition(kind) && !isUsage(kind)) return void 0;
123203
- if (transition && !(transition.source && transition.target) && this.extractTypeNames(ctx).length === 0) return void 0;
123249
+ if (transition && !(transition.source && transition.target) && this.extractTransitionTypeNames(ctx).length === 0) return void 0;
123204
123250
  return this.declarationText(ctx);
123205
123251
  }
123206
123252
  /**
@@ -124280,6 +124326,17 @@ var DocumentManager = class {
124280
124326
  }
124281
124327
  return this.wsSymbolTable;
124282
124328
  }
124329
+ /**
124330
+ * Rebuild the symbol tables of `uris` on next use, e.g. after their projectId
124331
+ * changed, which changes their symbol IDs (see `setFolderProjectIds`).
124332
+ */
124333
+ invalidateSymbols(uris) {
124334
+ for (const uri of uris) {
124335
+ const cached = this.cache.get(uri);
124336
+ if (cached) cached.symbolTable = void 0;
124337
+ this.wsBuiltVersions.delete(uri);
124338
+ }
124339
+ }
124283
124340
  /**
124284
124341
  * Cache semantic diagnostics for the current document version.
124285
124342
  */
@@ -128273,6 +128330,8 @@ function toSysMLSymbolKind(kind) {
128273
128330
  // Requirements → Object
128274
128331
  case "requirement def" /* RequirementDef */:
128275
128332
  case "requirement" /* RequirementUsage */:
128333
+ case "concern def" /* ConcernDef */:
128334
+ case "concern" /* ConcernUsage */:
128276
128335
  case "actor" /* ActorUsage */:
128277
128336
  case "subject" /* SubjectUsage */:
128278
128337
  case "stakeholder" /* StakeholderUsage */:
@@ -130666,6 +130725,10 @@ hoverProvider.setSemanticValidator(semanticValidator);
130666
130725
  var hasConfigurationCapability = false;
130667
130726
  var hasWorkspaceFolderCapability = false;
130668
130727
  var workspaceRoots = [];
130728
+ var workspaceFolderUris = [];
130729
+ function rootsOf(folderUris) {
130730
+ return folderUris.map((uri) => toFsPath(uri)).filter((p) => p !== void 0);
130731
+ }
130669
130732
  var isWorkspaceFile = false;
130670
130733
  var preloadOnOpen = "workspaceOnly";
130671
130734
  var disabledDiagnosticCodes = /* @__PURE__ */ new Set();
@@ -130677,6 +130740,10 @@ function publishDiagnostics(uri, diagnostics) {
130677
130740
  }
130678
130741
  var skipDirs = new Set(DEFAULT_SKIP_DIRS);
130679
130742
  var serverReady = false;
130743
+ var resolveInitialization;
130744
+ var initialization = new Promise((resolve2) => {
130745
+ resolveInitialization = resolve2;
130746
+ });
130680
130747
  var earlyOpenUris = /* @__PURE__ */ new Set();
130681
130748
  var workerRequestId = 0;
130682
130749
  var isBrowserServer = typeof __SYSML_BROWSER_SERVER__ !== "undefined" && __SYSML_BROWSER_SERVER__;
@@ -130790,7 +130857,8 @@ connection.onInitialize((params) => {
130790
130857
  connection.console.warn(`projectId is not a UUID: ${initOpts.projectId}; symbol IDs are unique only within this workspace`);
130791
130858
  }
130792
130859
  if (params.workspaceFolders) {
130793
- workspaceRoots = params.workspaceFolders.map((f) => toFsPath(f.uri)).filter((p) => p !== void 0);
130860
+ workspaceFolderUris = params.workspaceFolders.map((f) => f.uri);
130861
+ workspaceRoots = rootsOf(workspaceFolderUris);
130794
130862
  } else if (params.rootUri) {
130795
130863
  const root = toFsPath(params.rootUri);
130796
130864
  if (root) workspaceRoots = [root];
@@ -130840,7 +130908,8 @@ connection.onInitialize((params) => {
130840
130908
  if (hasWorkspaceFolderCapability) {
130841
130909
  result.capabilities.workspace = {
130842
130910
  workspaceFolders: {
130843
- supported: true
130911
+ supported: true,
130912
+ changeNotifications: true
130844
130913
  }
130845
130914
  };
130846
130915
  }
@@ -130852,8 +130921,17 @@ connection.onInitialized(async () => {
130852
130921
  import_node12.DidChangeConfigurationNotification.type,
130853
130922
  void 0
130854
130923
  );
130924
+ }
130925
+ if (hasWorkspaceFolderCapability) {
130926
+ connection.workspace.onDidChangeWorkspaceFolders((event) => {
130927
+ onWorkspaceFoldersChanged(event).catch((e) => connection.console.error(`Workspace folder change failed: ${e}`));
130928
+ });
130929
+ }
130930
+ if (hasConfigurationCapability) {
130855
130931
  await pullSettings().catch(() => {
130856
130932
  });
130933
+ await pullFolderProjectIds().catch(() => {
130934
+ });
130857
130935
  }
130858
130936
  const libCount = initLibraryIndex(__dirname);
130859
130937
  connection.console.log(`SysML v2 Language Server initialized (${libCount} library packages indexed)`);
@@ -130869,14 +130947,13 @@ connection.onInitialized(async () => {
130869
130947
  );
130870
130948
  }
130871
130949
  serverReady = true;
130950
+ resolveInitialization();
130872
130951
  spawnParseWorker();
130873
130952
  if (workspaceRoots.length > 0 && (preloadOnOpen === "always" || preloadOnOpen === "workspaceOnly" && isWorkspaceFile)) {
130874
- documentManager.setWorkspaceScanComplete(false);
130875
- scanWorkspaceFoldersAsync(workspaceRoots).then(({ fileCount, scanMs }) => {
130953
+ scanWorkspaceFolders(workspaceRoots).then(({ fileCount, scanMs }) => {
130876
130954
  connection.console.log(
130877
130955
  `Workspace scan: pre-parsed ${fileCount} .sysml files in ${scanMs} ms`
130878
130956
  );
130879
- documentManager.setWorkspaceScanComplete(true);
130880
130957
  revalidateOpenDocuments();
130881
130958
  });
130882
130959
  }
@@ -130911,9 +130988,79 @@ function applySettings(config) {
130911
130988
  }
130912
130989
  }
130913
130990
  connection.onDidChangeConfiguration((_change) => {
130914
- pullSettings().then(() => revalidateOpenDocuments()).catch(() => {
130991
+ pullSettings().then(() => pullFolderProjectIds()).then(() => revalidateOpenDocuments()).catch(() => {
130915
130992
  });
130916
130993
  });
130994
+ function latestWins(task) {
130995
+ let calls = 0;
130996
+ let latest = Promise.resolve();
130997
+ return () => {
130998
+ const call = ++calls;
130999
+ const isSuperseded = () => call !== calls;
131000
+ const run = task(isSuperseded).then(() => isSuperseded() ? latest : void 0);
131001
+ latest = run;
131002
+ return run;
131003
+ };
131004
+ }
131005
+ var pullFolderProjectIds = latestWins(async (isSuperseded) => {
131006
+ if (!hasConfigurationCapability) return;
131007
+ const folderUris = [...workspaceFolderUris];
131008
+ const answers = folderUris.length === 0 ? [] : await connection.workspace.getConfiguration(folderUris.map((scopeUri) => ({ scopeUri, section: "sysml.project" })));
131009
+ if (isSuperseded()) return;
131010
+ const projectIds = folderUris.flatMap((folderUri, i) => {
131011
+ const projectId = answers[i]?.projectId;
131012
+ return typeof projectId === "string" ? [{ folderUri, projectId }] : [];
131013
+ });
131014
+ const uris = documentManager.getUris();
131015
+ const prefixBefore = new Map(uris.map((uri) => [uri, getUrlPrefix(uri)]));
131016
+ const rejected = setFolderProjectIds(projectIds);
131017
+ for (const entry of rejected) {
131018
+ connection.console.warn(`projectId of ${entry.folderUri} is not a UUID: ${entry.projectId}; ignored, so its documents use the projectId of an enclosing folder, else initializationOptions.projectId, else the default prefix`);
131019
+ }
131020
+ documentManager.invalidateSymbols(uris.filter((uri) => getUrlPrefix(uri) !== prefixBefore.get(uri)));
131021
+ logFolderPrefixes(new Set(projectIds.filter((p) => !rejected.includes(p)).map((p) => p.folderUri)));
131022
+ });
131023
+ var loggedFolderPrefixes = /* @__PURE__ */ new Map();
131024
+ function logFolderPrefixes(ownProjectIds) {
131025
+ for (const folderUri of workspaceFolderUris) {
131026
+ const prefix = getUrlPrefix(`${folderUri.endsWith("/") ? folderUri : `${folderUri}/`}_.sysml`);
131027
+ if (loggedFolderPrefixes.get(folderUri) === prefix) continue;
131028
+ loggedFolderPrefixes.set(folderUri, prefix);
131029
+ const source = ownProjectIds.has(folderUri) ? "its projectId" : prefix === getUrlPrefix() ? prefix === DEFAULT_URL_PREFIX ? "the default prefix, no projectId" : "initializationOptions.projectId" : "an enclosing folder's projectId";
131030
+ connection.console.info(`Folder ${folderUri}: top-level elements' URLs start with ${prefix} (${source})`);
131031
+ }
131032
+ for (const folderUri of loggedFolderPrefixes.keys()) {
131033
+ if (!workspaceFolderUris.includes(folderUri)) loggedFolderPrefixes.delete(folderUri);
131034
+ }
131035
+ }
131036
+ async function onWorkspaceFoldersChanged(event) {
131037
+ const initialScanPending = !serverReady;
131038
+ const removed = new Set(event.removed.map((f) => f.uri));
131039
+ workspaceFolderUris = [...workspaceFolderUris.filter((uri) => !removed.has(uri)), ...event.added.map((f) => f.uri)];
131040
+ workspaceRoots = rootsOf(workspaceFolderUris);
131041
+ dropDocumentsOutsideWorkspace(event.removed.map((f) => f.uri));
131042
+ await pullFolderProjectIds().catch(() => {
131043
+ });
131044
+ if (initialScanPending) return;
131045
+ const addedRoots = rootsOf(event.added.map((f) => f.uri).filter((uri) => workspaceFolderUris.includes(uri)));
131046
+ if (addedRoots.length > 0 && (preloadOnOpen === "always" || preloadOnOpen === "workspaceOnly" && isWorkspaceFile)) {
131047
+ const { fileCount, scanMs } = await scanWorkspaceFolders(addedRoots);
131048
+ connection.console.log(`Workspace scan: pre-parsed ${fileCount} .sysml files of ${addedRoots.length} added folder(s) in ${scanMs} ms`);
131049
+ }
131050
+ revalidateOpenDocuments();
131051
+ }
131052
+ function dropDocumentsOutsideWorkspace(folderUris) {
131053
+ for (const uri of documentManager.getUris()) {
131054
+ if (openDocumentFor(uri)) continue;
131055
+ if (folderUris.some((f) => isInFolder(uri, f)) && !isInWorkspace(uri)) {
131056
+ documentManager.remove(uri);
131057
+ modelProvider.removeUri(uri);
131058
+ }
131059
+ }
131060
+ }
131061
+ function isInWorkspace(uri) {
131062
+ return workspaceRoots.some((root) => isInFolder(uri, (0, import_node_url2.pathToFileURL)(root).toString()));
131063
+ }
130917
131064
  function openDocumentFor(uri) {
130918
131065
  return documents.get(uri) ?? documents.all().find((d) => isSameDocumentUri(d.uri, uri));
130919
131066
  }
@@ -130938,6 +131085,16 @@ function parseWorkspaceFile(filePath, uri = (0, import_node_url2.pathToFileURL)(
130938
131085
  documentManager.parse(doc);
130939
131086
  return true;
130940
131087
  }
131088
+ var runningScans = 0;
131089
+ async function scanWorkspaceFolders(roots) {
131090
+ runningScans++;
131091
+ documentManager.setWorkspaceScanComplete(false);
131092
+ try {
131093
+ return await scanWorkspaceFoldersAsync(roots);
131094
+ } finally {
131095
+ if (--runningScans === 0) documentManager.setWorkspaceScanComplete(true);
131096
+ }
131097
+ }
130941
131098
  async function scanWorkspaceFoldersAsync(roots) {
130942
131099
  const scanT0 = Date.now();
130943
131100
  const fileArrays = await Promise.all(roots.map((r) => findSysMLFilesAsync(r, skipDirs)));
@@ -130949,6 +131106,7 @@ async function scanWorkspaceFoldersAsync(roots) {
130949
131106
  const filePath = allFiles[i];
130950
131107
  const uri = (0, import_node_url2.pathToFileURL)(filePath).toString();
130951
131108
  if (openDocumentFor(uri)) continue;
131109
+ if (!isInWorkspace(uri)) continue;
130952
131110
  const content = fileContents.get(filePath);
130953
131111
  if (content === void 0) continue;
130954
131112
  documentManager.parseBatch(uri, 0, content);
@@ -131161,14 +131319,16 @@ connection.onDocumentRangeFormatting(
131161
131319
  return formattingProvider.provideRangeFormatting(params);
131162
131320
  }
131163
131321
  );
131164
- connection.onRequest("sysml/model", (params) => {
131322
+ connection.onRequest("sysml/model", async (params) => {
131323
+ await initialization;
131165
131324
  return modelProvider.getModel(
131166
131325
  params.textDocument.uri,
131167
131326
  documentManager.getVersion(params.textDocument.uri),
131168
131327
  params.scope
131169
131328
  );
131170
131329
  });
131171
- connection.onRequest("sysml/elementLookup", (params) => {
131330
+ connection.onRequest("sysml/elementLookup", async (params) => {
131331
+ await initialization;
131172
131332
  return elementLookupProvider.elementLookup(params);
131173
131333
  });
131174
131334
  connection.onRequest("sysml/serverStats", () => {
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.34.0",
5
+ "version": "0.35.0",
6
6
  "publisher": "JamieD",
7
7
  "license": "MIT",
8
8
  "repository": {