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 +30 -0
- package/dist/server/browserServerMain.js +194 -53
- package/dist/server/mcpServer.js +143 -56
- package/dist/server/server.js +194 -53
- package/package.json +1 -1
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
|
|
130423
|
-
|
|
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.
|
|
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.
|
|
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
|
-
*
|
|
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.
|
|
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
|
-
*
|
|
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.
|
|
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.
|
|
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
|
-
|
|
137053
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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", () => {
|