sysml-v2-lsp 0.33.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,36 @@
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
+
23
+ ## [0.34.0]
24
+
25
+ ### Added
26
+
27
+ - Add `.github/copilot-instructions.md` requiring language-affecting changes to be validated against the latest OMG SysML and KerML specifications, with cited clauses or grammar productions and a regression test.
28
+
29
+ ### Fixed
30
+
31
+ - Report only an element's own multiplicity: a definition or usage no longer takes the multiplicity of a nested element (`part def Car { part : Engine[2]; }` gave `Car` the multiplicity `2`), and a unit in a default value (`default 0 [kg]`) is no longer reported as a multiplicity. SysML definitions cannot declare a multiplicity (`DefinitionDeclaration`); a usage's comes from its own `FeatureSpecializationPart`. `sysml/model` now reads multiplicity from the parse tree, so a usage whose multiplicity follows a specialization (`item :>> edges [3..4]`) reports it correctly.
32
+ - A connection or interface no longer reports the multiplicity of one of its ends (`connection c connect [2] a.p to [1] b.p;`): a `ConnectorEnd`'s cross multiplicity belongs to the end, not the connector.
33
+ - Elements named with an unreserved KerML keyword (`step`, `function`, `type`, `member`, `feature`, `behavior`, `var`, `multiplicity`, ...) keep their name, which the grammar allows (`name : IDENTIFIER | STRING | unreservedKeyword`). They were reported as anonymous, e.g. `perform action step[2];` was shown as `[2]`, and `alias multiplicity for degeneracy;` was named after its target.
34
+
5
35
  ## [0.33.0]
6
36
 
7
37
  ### 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 = [];
@@ -131838,7 +131884,7 @@ ${JSON.stringify(message, null, 4)}`);
131838
131884
  const child = ctx.getChild(i);
131839
131885
  if (child instanceof TerminalNode) {
131840
131886
  const token = child.symbol;
131841
- if (this.isIdentifierToken(token)) {
131887
+ if (this.isNameTerminal(child)) {
131842
131888
  return this.unquoteName(token.text ?? "");
131843
131889
  }
131844
131890
  }
@@ -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
  /**
@@ -131890,14 +131936,15 @@ ${JSON.stringify(message, null, 4)}`);
131890
131936
  /**
131891
131937
  * The first node of rule `ruleIndex` in `ctx`'s own declaration: never
131892
131938
  * inside a nested element, its body, its value, a flow's payload or ends,
131893
- * or a prefix such as `#metadata`.
131939
+ * a connector end's own multiplicity (`connect [2] a to b`), or a prefix
131940
+ * such as `#metadata`.
131894
131941
  */
131895
131942
  findOwnHeaderRule(ctx, ruleIndex) {
131896
131943
  for (let i = 0; i < ctx.getChildCount(); i++) {
131897
131944
  const child = ctx.getChild(i);
131898
131945
  if (!(child instanceof ParserRuleContext)) continue;
131899
131946
  if (child.ruleIndex === ruleIndex) return child;
131900
- if (RULE_INDEX_TO_KIND.has(child.ruleIndex) || BODY_RULE_INDICES.has(child.ruleIndex) || child.ruleIndex === SysMLv2Parser.RULE_valuePart || FLOW_PART_RULE_INDICES.has(child.ruleIndex) || this.isPrefixOrExtensionContext(child)) continue;
131947
+ if (RULE_INDEX_TO_KIND.has(child.ruleIndex) || BODY_RULE_INDICES.has(child.ruleIndex) || child.ruleIndex === SysMLv2Parser.RULE_valuePart || FLOW_PART_RULE_INDICES.has(child.ruleIndex) || child.ruleIndex === SysMLv2Parser.RULE_ownedCrossMultiplicityMember || this.isPrefixOrExtensionContext(child)) continue;
131901
131948
  const found = this.findOwnHeaderRule(child, ruleIndex);
131902
131949
  if (found) return found;
131903
131950
  }
@@ -132061,7 +132108,7 @@ ${JSON.stringify(message, null, 4)}`);
132061
132108
  extractNameRange(ctx) {
132062
132109
  for (let i = 0; i < ctx.getChildCount(); i++) {
132063
132110
  const child = ctx.getChild(i);
132064
- if (child instanceof TerminalNode && this.isIdentifierToken(child.symbol)) {
132111
+ if (child instanceof TerminalNode && this.isNameTerminal(child)) {
132065
132112
  return tokenToRange(child.symbol);
132066
132113
  }
132067
132114
  if (child instanceof ParserRuleContext) {
@@ -132246,12 +132293,12 @@ ${JSON.stringify(message, null, 4)}`);
132246
132293
  return lines.join("\n").trim();
132247
132294
  }
132248
132295
  /**
132249
- * Extract multiplicity from a context.
132250
- * Looks for MultiplicityBoundsContext in the subtree.
132296
+ * Extract multiplicity from a context's own declaration, never from a
132297
+ * nested element or its body.
132251
132298
  * Returns { multiplicity: "1..5", multiplicityRange: { lower: 1, upper: 5 } }
132252
132299
  */
132253
132300
  extractMultiplicity(ctx) {
132254
- const multCtx = this.findMultiplicityBounds(ctx);
132301
+ const multCtx = this.findOwnHeaderRule(ctx, SysMLv2Parser.RULE_multiplicityBounds);
132255
132302
  if (!multCtx) {
132256
132303
  return {};
132257
132304
  }
@@ -132305,24 +132352,6 @@ ${JSON.stringify(message, null, 4)}`);
132305
132352
  multiplicityRange: { lower, upper }
132306
132353
  };
132307
132354
  }
132308
- /**
132309
- * Recursively search for a MultiplicityBoundsContext in the subtree.
132310
- */
132311
- findMultiplicityBounds(ctx) {
132312
- if (ctx.ruleIndex === SysMLv2Parser.RULE_multiplicityBounds) {
132313
- return ctx;
132314
- }
132315
- for (let i = 0; i < ctx.getChildCount(); i++) {
132316
- const child = ctx.getChild(i);
132317
- if (child instanceof ParserRuleContext) {
132318
- const result = this.findMultiplicityBounds(child);
132319
- if (result) {
132320
- return result;
132321
- }
132322
- }
132323
- }
132324
- return void 0;
132325
- }
132326
132355
  /**
132327
132356
  * Whether a context is a prefix metadata or definition/usage prefix rule.
132328
132357
  * These contain annotation identifiers (e.g. from `#product`) that should
@@ -132364,6 +132393,14 @@ ${JSON.stringify(message, null, 4)}`);
132364
132393
  if (this.isQuotedName(text)) return true;
132365
132394
  return /^[a-zA-Z_]/.test(text) && !this.isKeyword(text);
132366
132395
  }
132396
+ /**
132397
+ * Whether a terminal is (part of) a name: an identifier, a quoted name, or
132398
+ * a KerML keyword such as `step` or `function` that the grammar parsed as a
132399
+ * name (`name : IDENTIFIER | STRING | unreservedKeyword`).
132400
+ */
132401
+ isNameTerminal(node) {
132402
+ return this.isIdentifierToken(node.symbol) || node.parent?.ruleIndex === SysMLv2Parser.RULE_unreservedKeyword;
132403
+ }
132367
132404
  /**
132368
132405
  * Check if text is a SysML quoted name (single-quoted string).
132369
132406
  */
@@ -132733,7 +132770,7 @@ ${JSON.stringify(message, null, 4)}`);
132733
132770
  const child = ctx.getChild(i);
132734
132771
  if (child instanceof TerminalNode) {
132735
132772
  const text = child.symbol.text;
132736
- if (text && this.isIdentifierToken(child.symbol)) {
132773
+ if (text && this.isNameTerminal(child)) {
132737
132774
  parts.push(this.unquoteName(text));
132738
132775
  }
132739
132776
  } else if (child instanceof ParserRuleContext) {
@@ -132959,6 +132996,17 @@ ${JSON.stringify(message, null, 4)}`);
132959
132996
  }
132960
132997
  return this.wsSymbolTable;
132961
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
+ }
132962
133010
  /**
132963
133011
  * Cache semantic diagnostics for the current document version.
132964
133012
  */
@@ -137049,9 +137097,8 @@ ${JSON.stringify(message, null, 4)}`);
137049
137097
  if (direction) {
137050
137098
  attributes["direction"] = direction;
137051
137099
  }
137052
- const multiplicity = this.extractMultiplicity(symbol, lines);
137053
- if (multiplicity) {
137054
- attributes["multiplicity"] = multiplicity;
137100
+ if (symbol.multiplicity) {
137101
+ attributes["multiplicity"] = symbol.multiplicity;
137055
137102
  }
137056
137103
  const modifier = this.extractModifier(symbol, lines);
137057
137104
  if (modifier) {
@@ -137669,7 +137716,7 @@ ${JSON.stringify(message, null, 4)}`);
137669
137716
  name: c.name,
137670
137717
  kind: toMetaclassName(c.kind),
137671
137718
  type: c.typeNames.join(", ") || void 0,
137672
- multiplicity: c.multiplicity ?? this.extractMultiplicity(c, lines),
137719
+ multiplicity: c.multiplicity,
137673
137720
  direction,
137674
137721
  visibility: c.visibility,
137675
137722
  isDerived: modifier?.split(", ").includes("derived") ?? false,
@@ -137793,15 +137840,6 @@ ${JSON.stringify(message, null, 4)}`);
137793
137840
  if (containsWord(elementText, "out")) return "out";
137794
137841
  return void 0;
137795
137842
  }
137796
- /** Extract multiplicity (e.g., [4], [0..*]) from source text. */
137797
- extractMultiplicity(symbol, lines) {
137798
- const elementText = this.getElementText(symbol, lines);
137799
- const open = elementText.indexOf("[");
137800
- if (open < 0) return void 0;
137801
- const close = elementText.indexOf("]", open + 1);
137802
- if (close < 0) return void 0;
137803
- return elementText.substring(open + 1, close);
137804
- }
137805
137843
  /** Extract modifiers (abstract, readonly, derived, etc.) from source text. */
137806
137844
  extractModifier(symbol, lines) {
137807
137845
  const elementText = this.getElementText(symbol, lines);
@@ -139324,6 +139362,8 @@ ${childIndent}item def ${unresolvedType};
139324
139362
  // Requirements → Object
139325
139363
  case "requirement def" /* RequirementDef */:
139326
139364
  case "requirement" /* RequirementUsage */:
139365
+ case "concern def" /* ConcernDef */:
139366
+ case "concern" /* ConcernUsage */:
139327
139367
  case "actor" /* ActorUsage */:
139328
139368
  case "subject" /* SubjectUsage */:
139329
139369
  case "stakeholder" /* StakeholderUsage */:
@@ -141723,6 +141763,10 @@ ${info.documentation}`);
141723
141763
  var hasConfigurationCapability = false;
141724
141764
  var hasWorkspaceFolderCapability = false;
141725
141765
  var workspaceRoots = [];
141766
+ var workspaceFolderUris = [];
141767
+ function rootsOf(folderUris) {
141768
+ return folderUris.map((uri) => toFsPath(uri)).filter((p) => p !== void 0);
141769
+ }
141726
141770
  var isWorkspaceFile = false;
141727
141771
  var preloadOnOpen = "workspaceOnly";
141728
141772
  var disabledDiagnosticCodes = /* @__PURE__ */ new Set();
@@ -141734,6 +141778,10 @@ ${info.documentation}`);
141734
141778
  }
141735
141779
  var skipDirs = new Set(DEFAULT_SKIP_DIRS);
141736
141780
  var serverReady = false;
141781
+ var resolveInitialization;
141782
+ var initialization = new Promise((resolve) => {
141783
+ resolveInitialization = resolve;
141784
+ });
141737
141785
  var earlyOpenUris = /* @__PURE__ */ new Set();
141738
141786
  var workerRequestId = 0;
141739
141787
  var isBrowserServer = true;
@@ -141847,7 +141895,8 @@ ${info.documentation}`);
141847
141895
  connection.console.warn(`projectId is not a UUID: ${initOpts.projectId}; symbol IDs are unique only within this workspace`);
141848
141896
  }
141849
141897
  if (params.workspaceFolders) {
141850
- 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);
141851
141900
  } else if (params.rootUri) {
141852
141901
  const root = toFsPath(params.rootUri);
141853
141902
  if (root) workspaceRoots = [root];
@@ -141897,7 +141946,8 @@ ${info.documentation}`);
141897
141946
  if (hasWorkspaceFolderCapability) {
141898
141947
  result.capabilities.workspace = {
141899
141948
  workspaceFolders: {
141900
- supported: true
141949
+ supported: true,
141950
+ changeNotifications: true
141901
141951
  }
141902
141952
  };
141903
141953
  }
@@ -141909,8 +141959,17 @@ ${info.documentation}`);
141909
141959
  import_node11.DidChangeConfigurationNotification.type,
141910
141960
  void 0
141911
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) {
141912
141969
  await pullSettings().catch(() => {
141913
141970
  });
141971
+ await pullFolderProjectIds().catch(() => {
141972
+ });
141914
141973
  }
141915
141974
  const libCount = initLibraryIndex("/");
141916
141975
  connection.console.log(`SysML v2 Language Server initialized (${libCount} library packages indexed)`);
@@ -141926,14 +141985,13 @@ ${info.documentation}`);
141926
141985
  );
141927
141986
  }
141928
141987
  serverReady = true;
141988
+ resolveInitialization();
141929
141989
  spawnParseWorker();
141930
141990
  if (workspaceRoots.length > 0 && (preloadOnOpen === "always" || preloadOnOpen === "workspaceOnly" && isWorkspaceFile)) {
141931
- documentManager.setWorkspaceScanComplete(false);
141932
- scanWorkspaceFoldersAsync(workspaceRoots).then(({ fileCount, scanMs }) => {
141991
+ scanWorkspaceFolders(workspaceRoots).then(({ fileCount, scanMs }) => {
141933
141992
  connection.console.log(
141934
141993
  `Workspace scan: pre-parsed ${fileCount} .sysml files in ${scanMs} ms`
141935
141994
  );
141936
- documentManager.setWorkspaceScanComplete(true);
141937
141995
  revalidateOpenDocuments();
141938
141996
  });
141939
141997
  }
@@ -141968,9 +142026,79 @@ ${info.documentation}`);
141968
142026
  }
141969
142027
  }
141970
142028
  connection.onDidChangeConfiguration((_change) => {
141971
- 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 }] : [];
141972
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)));
141973
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
+ }
141974
142102
  function openDocumentFor(uri) {
141975
142103
  return documents.get(uri) ?? documents.all().find((d) => isSameDocumentUri(d.uri, uri));
141976
142104
  }
@@ -141995,6 +142123,16 @@ ${info.documentation}`);
141995
142123
  documentManager.parse(doc);
141996
142124
  return true;
141997
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
+ }
141998
142136
  async function scanWorkspaceFoldersAsync(roots) {
141999
142137
  const scanT0 = Date.now();
142000
142138
  const fileArrays = await Promise.all(roots.map((r) => findSysMLFilesAsync(r, skipDirs)));
@@ -142006,6 +142144,7 @@ ${info.documentation}`);
142006
142144
  const filePath = allFiles[i];
142007
142145
  const uri = pathToFileURL(filePath).toString();
142008
142146
  if (openDocumentFor(uri)) continue;
142147
+ if (!isInWorkspace(uri)) continue;
142009
142148
  const content = fileContents.get(filePath);
142010
142149
  if (content === void 0) continue;
142011
142150
  documentManager.parseBatch(uri, 0, content);
@@ -142218,14 +142357,16 @@ ${info.documentation}`);
142218
142357
  return formattingProvider.provideRangeFormatting(params);
142219
142358
  }
142220
142359
  );
142221
- connection.onRequest("sysml/model", (params) => {
142360
+ connection.onRequest("sysml/model", async (params) => {
142361
+ await initialization;
142222
142362
  return modelProvider.getModel(
142223
142363
  params.textDocument.uri,
142224
142364
  documentManager.getVersion(params.textDocument.uri),
142225
142365
  params.scope
142226
142366
  );
142227
142367
  });
142228
- connection.onRequest("sysml/elementLookup", (params) => {
142368
+ connection.onRequest("sysml/elementLookup", async (params) => {
142369
+ await initialization;
142229
142370
  return elementLookupProvider.elementLookup(params);
142230
142371
  });
142231
142372
  connection.onRequest("sysml/serverStats", () => {