sysml-validate 0.15.8 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/main.js +1106 -37
- package/out/main.js.map +4 -4
- package/package.json +1 -1
- package/resources/sysml.dimension-table.json +1 -1
- package/resources/sysml.library.index.json +1 -1
package/out/main.js
CHANGED
|
@@ -4108,21 +4108,21 @@ var require_main2 = __commonJS({
|
|
|
4108
4108
|
}
|
|
4109
4109
|
TextDocumentItem3.is = is;
|
|
4110
4110
|
})(TextDocumentItem2 || (exports3.TextDocumentItem = TextDocumentItem2 = {}));
|
|
4111
|
-
var
|
|
4112
|
-
(function(
|
|
4113
|
-
|
|
4114
|
-
|
|
4111
|
+
var MarkupKind4;
|
|
4112
|
+
(function(MarkupKind5) {
|
|
4113
|
+
MarkupKind5.PlainText = "plaintext";
|
|
4114
|
+
MarkupKind5.Markdown = "markdown";
|
|
4115
4115
|
function is(value) {
|
|
4116
4116
|
var candidate = value;
|
|
4117
|
-
return candidate ===
|
|
4117
|
+
return candidate === MarkupKind5.PlainText || candidate === MarkupKind5.Markdown;
|
|
4118
4118
|
}
|
|
4119
|
-
|
|
4120
|
-
})(
|
|
4119
|
+
MarkupKind5.is = is;
|
|
4120
|
+
})(MarkupKind4 || (exports3.MarkupKind = MarkupKind4 = {}));
|
|
4121
4121
|
var MarkupContent2;
|
|
4122
4122
|
(function(MarkupContent3) {
|
|
4123
4123
|
function is(value) {
|
|
4124
4124
|
var candidate = value;
|
|
4125
|
-
return Is2.objectLiteral(value) &&
|
|
4125
|
+
return Is2.objectLiteral(value) && MarkupKind4.is(candidate.kind) && Is2.string(candidate.value);
|
|
4126
4126
|
}
|
|
4127
4127
|
MarkupContent3.is = is;
|
|
4128
4128
|
})(MarkupContent2 || (exports3.MarkupContent = MarkupContent2 = {}));
|
|
@@ -24188,14 +24188,14 @@ var TextDocumentItem;
|
|
|
24188
24188
|
TextDocumentItem2.is = is;
|
|
24189
24189
|
})(TextDocumentItem || (TextDocumentItem = {}));
|
|
24190
24190
|
var MarkupKind;
|
|
24191
|
-
(function(
|
|
24192
|
-
|
|
24193
|
-
|
|
24191
|
+
(function(MarkupKind4) {
|
|
24192
|
+
MarkupKind4.PlainText = "plaintext";
|
|
24193
|
+
MarkupKind4.Markdown = "markdown";
|
|
24194
24194
|
function is(value) {
|
|
24195
24195
|
const candidate = value;
|
|
24196
|
-
return candidate ===
|
|
24196
|
+
return candidate === MarkupKind4.PlainText || candidate === MarkupKind4.Markdown;
|
|
24197
24197
|
}
|
|
24198
|
-
|
|
24198
|
+
MarkupKind4.is = is;
|
|
24199
24199
|
})(MarkupKind || (MarkupKind = {}));
|
|
24200
24200
|
var MarkupContent;
|
|
24201
24201
|
(function(MarkupContent2) {
|
|
@@ -57803,7 +57803,14 @@ var sysmlOutlineSettings = {
|
|
|
57803
57803
|
hiddenCategories: []
|
|
57804
57804
|
};
|
|
57805
57805
|
var sysmlInlayHintSettings = {
|
|
57806
|
-
|
|
57806
|
+
enabled: true,
|
|
57807
|
+
multiplicity: true,
|
|
57808
|
+
dimension: false,
|
|
57809
|
+
visibility: false,
|
|
57810
|
+
modifiers: false,
|
|
57811
|
+
specialization: false,
|
|
57812
|
+
redefinition: false,
|
|
57813
|
+
effectiveNames: false
|
|
57807
57814
|
};
|
|
57808
57815
|
function asDiagnosticLevel(value, fallback = "off") {
|
|
57809
57816
|
return value === "off" || value === "warning" || value === "error" ? value : fallback;
|
|
@@ -58685,7 +58692,16 @@ var DIAGNOSTIC_MESSAGES = {
|
|
|
58685
58692
|
SSM025_ACTION_PARAMETER_COUNT: (usage, declared, typeName, available) => `'${usage}' declares ${declared} directed parameter${declared === 1 ? "" : "s"} but its definition '${typeName}' has ${available}. An action usage's parameters correspond positionally to its definition's, so there is no ${available === 0 ? "parameter" : `${available + 1}th parameter`} to correspond to.`,
|
|
58686
58693
|
SSM025_ACTION_PARAMETER_DIRECTION: (position, mine, typeName, theirs) => `Parameter ${position} is '${mine}' but the corresponding parameter of '${typeName}' is '${theirs}'. Positional correspondence redefines that parameter, and a redefinition cannot reverse a parameter's direction.`,
|
|
58687
58694
|
SSM026_REDEFINITION_DIRECTION: (feature, mine, theirs) => `Direction '${mine}' reverses the redefined feature '${feature}', which is '${theirs}'. A redefinition refines a feature \u2014 it cannot turn an input into an output.`,
|
|
58688
|
-
SSM027_INTERFACE_END_TYPE: (position, endType, definition, expected) => `End ${position} is a '${endType}', but '${definition}' declares that end as '${expected}'. An interface usage connects through the ends its definition declares
|
|
58695
|
+
SSM027_INTERFACE_END_TYPE: (position, endType, definition, expected) => `End ${position} is a '${endType}', but '${definition}' declares that end as '${expected}'. An interface usage connects through the ends its definition declares.`,
|
|
58696
|
+
// REQ-392 — a `sysml-format` comment the formatter cannot act on. Advisory:
|
|
58697
|
+
// the directive is inert, and saying so beats leaving the author to wonder
|
|
58698
|
+
// why their layout was reformatted anyway.
|
|
58699
|
+
STYL008_FORMAT_DIRECTIVE_UNATTACHED: "This `sysml-format ignore` directive has no element after it to attach to, so nothing is preserved. Move it directly above the element whose layout you want to keep.",
|
|
58700
|
+
// REQ-345, REQ-393 — the formatter-settings refusal is shown to the user
|
|
58701
|
+
// through `window/showWarningMessage`, so its text lives here like every
|
|
58702
|
+
// other reviewable string rather than inline at the call site.
|
|
58703
|
+
FORMAT_SETTINGS_REFUSED: (detail) => `SysML formatter settings ignored (${detail}). The previous value is still in use.`,
|
|
58704
|
+
STYL008_FORMAT_DIRECTIVE_UNKNOWN: (argument) => `Unknown formatter directive '${argument || "(none)"}'. The only directive is 'sysml-format ignore', which preserves the next element's layout.`
|
|
58689
58705
|
};
|
|
58690
58706
|
var KEYWORD_TOOLTIPS = {
|
|
58691
58707
|
// Package / namespace
|
|
@@ -58882,6 +58898,107 @@ var OPERATOR_TOOLTIPS = {
|
|
|
58882
58898
|
"@@": { desc: "**Metaclass application** \u2014 applies a metaclass annotation.", precedence: 0, example: "@@StandardProfile", cite: "OMG SysML v2.0 Part 1 \xA78.2.2.27 (Metadata)" }
|
|
58883
58899
|
};
|
|
58884
58900
|
|
|
58901
|
+
// ../language-server/out/src/services/format-directives.js
|
|
58902
|
+
var DIRECTIVE_WORD = "sysml-format";
|
|
58903
|
+
var IGNORE_ARGUMENT = "ignore";
|
|
58904
|
+
var EMPTY_SCAN = { directives: [], protectedRanges: [] };
|
|
58905
|
+
function commentBody(text) {
|
|
58906
|
+
if (text.startsWith("//*") && text.endsWith("*/") && text.length >= 5)
|
|
58907
|
+
return text.slice(3, -2);
|
|
58908
|
+
if (text.startsWith("//"))
|
|
58909
|
+
return text.slice(2);
|
|
58910
|
+
if (text.startsWith("/*") && text.endsWith("*/") && text.length >= 4)
|
|
58911
|
+
return text.slice(2, -2);
|
|
58912
|
+
return void 0;
|
|
58913
|
+
}
|
|
58914
|
+
function classify(text) {
|
|
58915
|
+
const body = commentBody(text);
|
|
58916
|
+
if (body === void 0)
|
|
58917
|
+
return void 0;
|
|
58918
|
+
const trimmed = body.trim();
|
|
58919
|
+
if (trimmed !== DIRECTIVE_WORD && !trimmed.startsWith(`${DIRECTIVE_WORD} `) && !trimmed.startsWith(`${DIRECTIVE_WORD} `)) {
|
|
58920
|
+
return void 0;
|
|
58921
|
+
}
|
|
58922
|
+
const argument = trimmed.slice(DIRECTIVE_WORD.length).trim();
|
|
58923
|
+
return { kind: argument === IGNORE_ARGUMENT ? "ignore" : "malformed", argument };
|
|
58924
|
+
}
|
|
58925
|
+
function looksLikeFormatDirective(text) {
|
|
58926
|
+
return text.includes(DIRECTIVE_WORD);
|
|
58927
|
+
}
|
|
58928
|
+
function nodesByStartOffset(root3) {
|
|
58929
|
+
const byOffset = /* @__PURE__ */ new Map();
|
|
58930
|
+
for (const node of ast_utils_exports.streamAllContents(root3)) {
|
|
58931
|
+
const cst = node.$cstNode;
|
|
58932
|
+
if (!cst)
|
|
58933
|
+
continue;
|
|
58934
|
+
const existing = byOffset.get(cst.offset);
|
|
58935
|
+
if (!existing || (existing.$cstNode?.end ?? 0) < cst.end)
|
|
58936
|
+
byOffset.set(cst.offset, node);
|
|
58937
|
+
}
|
|
58938
|
+
return byOffset;
|
|
58939
|
+
}
|
|
58940
|
+
function mergeSlices(slices) {
|
|
58941
|
+
const sorted = [...slices].sort((a2, b) => a2.offset - b.offset || a2.end - b.end);
|
|
58942
|
+
const merged = [];
|
|
58943
|
+
for (const slice of sorted) {
|
|
58944
|
+
const last2 = merged.at(-1);
|
|
58945
|
+
if (last2 && slice.offset <= last2.end) {
|
|
58946
|
+
last2.end = Math.max(last2.end, slice.end);
|
|
58947
|
+
} else {
|
|
58948
|
+
merged.push({ ...slice });
|
|
58949
|
+
}
|
|
58950
|
+
}
|
|
58951
|
+
return merged;
|
|
58952
|
+
}
|
|
58953
|
+
function scanFormatDirectives(document) {
|
|
58954
|
+
const root3 = document.parseResult?.value;
|
|
58955
|
+
const rootCst = root3?.$cstNode;
|
|
58956
|
+
if (!root3 || !rootCst)
|
|
58957
|
+
return EMPTY_SCAN;
|
|
58958
|
+
const text = document.textDocument.getText();
|
|
58959
|
+
if (!looksLikeFormatDirective(text))
|
|
58960
|
+
return EMPTY_SCAN;
|
|
58961
|
+
const leaves = cst_utils_exports.flattenCst(rootCst).toArray();
|
|
58962
|
+
const directives = [];
|
|
58963
|
+
const protectedRanges = [];
|
|
58964
|
+
let byOffset;
|
|
58965
|
+
for (let index = 0; index < leaves.length; index++) {
|
|
58966
|
+
const leaf = leaves[index];
|
|
58967
|
+
if (!leaf.hidden)
|
|
58968
|
+
continue;
|
|
58969
|
+
const classified = classify(leaf.text);
|
|
58970
|
+
if (!classified)
|
|
58971
|
+
continue;
|
|
58972
|
+
const comment = { offset: leaf.offset, end: leaf.end };
|
|
58973
|
+
if (classified.kind === "malformed") {
|
|
58974
|
+
directives.push({ kind: "malformed", comment, argument: classified.argument });
|
|
58975
|
+
continue;
|
|
58976
|
+
}
|
|
58977
|
+
let next;
|
|
58978
|
+
for (let scan = index + 1; scan < leaves.length; scan++) {
|
|
58979
|
+
if (!leaves[scan].hidden) {
|
|
58980
|
+
next = leaves[scan];
|
|
58981
|
+
break;
|
|
58982
|
+
}
|
|
58983
|
+
}
|
|
58984
|
+
byOffset ??= nodesByStartOffset(root3);
|
|
58985
|
+
const target = next ? byOffset.get(next.offset)?.$cstNode : void 0;
|
|
58986
|
+
if (!target) {
|
|
58987
|
+
directives.push({ kind: "dangling", comment, argument: classified.argument });
|
|
58988
|
+
continue;
|
|
58989
|
+
}
|
|
58990
|
+
const slice = { offset: target.offset, end: target.end };
|
|
58991
|
+
directives.push({ kind: "ignore", comment, argument: classified.argument, target: slice });
|
|
58992
|
+
protectedRanges.push(slice);
|
|
58993
|
+
}
|
|
58994
|
+
if (directives.length === 0)
|
|
58995
|
+
return EMPTY_SCAN;
|
|
58996
|
+
return { directives, protectedRanges: mergeSlices(protectedRanges) };
|
|
58997
|
+
}
|
|
58998
|
+
function intersectsProtected(ranges, offset, end) {
|
|
58999
|
+
return ranges.some((slice) => offset < slice.end && end > slice.offset);
|
|
59000
|
+
}
|
|
59001
|
+
|
|
58885
59002
|
// ../language-server/out/src/services/v1-guardrail.js
|
|
58886
59003
|
var V1_KEYWORD_MAP = {
|
|
58887
59004
|
block: { v2: "part def", note: "SysML v2 replaces the v1 'Block' with 'part def'." },
|
|
@@ -60268,6 +60385,23 @@ var SysmlValidator = class _SysmlValidator {
|
|
|
60268
60385
|
}
|
|
60269
60386
|
this.checkMixedIndentation(node, doc, accept);
|
|
60270
60387
|
this.checkAlphabetizedImportGroups(node, doc, accept);
|
|
60388
|
+
this.checkFormatDirectives(node, doc, accept);
|
|
60389
|
+
}
|
|
60390
|
+
// REQ-392 — STYL008 a `sysml-format` directive the formatter cannot act on.
|
|
60391
|
+
// A well-formed `sysml-format ignore` that found its element is silent; only
|
|
60392
|
+
// an unknown argument and one with nothing left to attach to are reported,
|
|
60393
|
+
// each on the comment itself so the fix is where the cursor lands.
|
|
60394
|
+
checkFormatDirectives(root3, doc, accept) {
|
|
60395
|
+
for (const directive of scanFormatDirectives(doc).directives) {
|
|
60396
|
+
if (directive.kind === "ignore")
|
|
60397
|
+
continue;
|
|
60398
|
+
const range = {
|
|
60399
|
+
start: doc.textDocument.positionAt(directive.comment.offset),
|
|
60400
|
+
end: doc.textDocument.positionAt(directive.comment.end)
|
|
60401
|
+
};
|
|
60402
|
+
const message = directive.kind === "dangling" ? DIAGNOSTIC_MESSAGES.STYL008_FORMAT_DIRECTIVE_UNATTACHED : DIAGNOSTIC_MESSAGES.STYL008_FORMAT_DIRECTIVE_UNKNOWN(directive.argument);
|
|
60403
|
+
accept(severity("STYL008", "info"), message, { node: root3, range, code: "STYL008" });
|
|
60404
|
+
}
|
|
60271
60405
|
}
|
|
60272
60406
|
// REQ-332 — STYL004 mixed tab + space indentation.
|
|
60273
60407
|
checkMixedIndentation(root3, doc, accept) {
|
|
@@ -62910,6 +63044,7 @@ var STYLE_ADVISORY_CODES = /* @__PURE__ */ new Set([
|
|
|
62910
63044
|
"STYL005",
|
|
62911
63045
|
"STYL006",
|
|
62912
63046
|
"STYL007",
|
|
63047
|
+
"STYL008",
|
|
62913
63048
|
"LEX009",
|
|
62914
63049
|
// trailing whitespace
|
|
62915
63050
|
"SYN024",
|
|
@@ -63988,6 +64123,18 @@ function resolveEffectiveSubject(node) {
|
|
|
63988
64123
|
|
|
63989
64124
|
// ../language-server/out/src/services/hover-provider.js
|
|
63990
64125
|
var MAX_HOVER_LINES = 30;
|
|
64126
|
+
var MAX_DOC_DEPTH = 16;
|
|
64127
|
+
var MAX_DOC_NODES = 64;
|
|
64128
|
+
var MAX_DOC_SECTION_LINES = 14;
|
|
64129
|
+
var DOC_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
|
|
64130
|
+
":>",
|
|
64131
|
+
":>>",
|
|
64132
|
+
"::>",
|
|
64133
|
+
"specializes",
|
|
64134
|
+
"subsets",
|
|
64135
|
+
"redefines",
|
|
64136
|
+
"references"
|
|
64137
|
+
]);
|
|
63991
64138
|
function leafAtOffset(node, offset) {
|
|
63992
64139
|
if (isLeafCstNode(node)) {
|
|
63993
64140
|
return node.offset <= offset && offset < node.offset + node.length ? node : void 0;
|
|
@@ -64142,22 +64289,26 @@ function multiplicityPlainEnglish(node) {
|
|
|
64142
64289
|
return lo === 1 ? "Exactly 1 value" : `Exactly ${lo} values`;
|
|
64143
64290
|
return `Between ${lo} and ${hi ?? "*"} values`;
|
|
64144
64291
|
}
|
|
64145
|
-
function
|
|
64292
|
+
function localDocs(node) {
|
|
64293
|
+
const bodies = [];
|
|
64146
64294
|
const members = node.members;
|
|
64147
64295
|
if (Array.isArray(members)) {
|
|
64148
64296
|
for (const m of members) {
|
|
64149
64297
|
const body = docBody(m);
|
|
64150
64298
|
if (body)
|
|
64151
|
-
|
|
64299
|
+
bodies.push(body);
|
|
64152
64300
|
}
|
|
64153
64301
|
}
|
|
64302
|
+
if (bodies.length > 0)
|
|
64303
|
+
return bodies;
|
|
64154
64304
|
const siblings = node.$container?.members;
|
|
64155
64305
|
if (Array.isArray(siblings)) {
|
|
64156
64306
|
const index = siblings.indexOf(node);
|
|
64157
|
-
|
|
64158
|
-
|
|
64307
|
+
const preceding = index > 0 ? docBody(siblings[index - 1]) : void 0;
|
|
64308
|
+
if (preceding)
|
|
64309
|
+
bodies.push(preceding);
|
|
64159
64310
|
}
|
|
64160
|
-
return
|
|
64311
|
+
return bodies;
|
|
64161
64312
|
}
|
|
64162
64313
|
function docBody(node) {
|
|
64163
64314
|
const member = node;
|
|
@@ -64303,13 +64454,67 @@ function buildCommentHover(node) {
|
|
|
64303
64454
|
lines.push("", sourceFooter(nodeSource(node)));
|
|
64304
64455
|
return lines.join("\n");
|
|
64305
64456
|
}
|
|
64457
|
+
function uniqueLocalTarget(source, targetText) {
|
|
64458
|
+
const root3 = ast_utils_exports.getDocument(source).parseResult?.value;
|
|
64459
|
+
if (!root3)
|
|
64460
|
+
return void 0;
|
|
64461
|
+
let found;
|
|
64462
|
+
for (const candidate of ast_utils_exports.streamAllContents(root3)) {
|
|
64463
|
+
const named = candidate;
|
|
64464
|
+
if (named.name !== targetText && named.shortName?.name !== targetText)
|
|
64465
|
+
continue;
|
|
64466
|
+
if (found)
|
|
64467
|
+
return void 0;
|
|
64468
|
+
found = candidate;
|
|
64469
|
+
}
|
|
64470
|
+
return found;
|
|
64471
|
+
}
|
|
64472
|
+
function declarationLink(node) {
|
|
64473
|
+
const uri = ast_utils_exports.getDocument(node).uri.toString();
|
|
64474
|
+
const line = (node.$cstNode?.range.start.line ?? 0) + 1;
|
|
64475
|
+
return `[${shortUri(uri)}:${line}](${uri}#L${line})`;
|
|
64476
|
+
}
|
|
64477
|
+
function limitDocSection(value) {
|
|
64478
|
+
const lines = value.split(/\r?\n/u);
|
|
64479
|
+
if (lines.length <= MAX_DOC_SECTION_LINES)
|
|
64480
|
+
return value;
|
|
64481
|
+
const kept = lines.slice(0, MAX_DOC_SECTION_LINES);
|
|
64482
|
+
if (kept.filter((line) => line.startsWith("```")).length % 2 !== 0)
|
|
64483
|
+
kept.push("```");
|
|
64484
|
+
kept.push("\u2026");
|
|
64485
|
+
return kept.join("\n");
|
|
64486
|
+
}
|
|
64487
|
+
function renderDocs(bodies, provenance) {
|
|
64488
|
+
const body = limitDocSection(bodies.join("\n\n"));
|
|
64489
|
+
return provenance ? `${provenance}
|
|
64490
|
+
|
|
64491
|
+
${body}` : body;
|
|
64492
|
+
}
|
|
64493
|
+
function supertypeCandidates(node) {
|
|
64494
|
+
const named = node;
|
|
64495
|
+
const out = [];
|
|
64496
|
+
const typing = named.typing;
|
|
64497
|
+
if (typing?.type?.$refText)
|
|
64498
|
+
out.push({ refText: typing.type.$refText, ref: typing.type.ref });
|
|
64499
|
+
for (const more of typing?.moreTypes ?? []) {
|
|
64500
|
+
if (more.$refText)
|
|
64501
|
+
out.push({ refText: more.$refText, ref: more.ref });
|
|
64502
|
+
}
|
|
64503
|
+
for (const rel of allRelationships(named)) {
|
|
64504
|
+
if (!rel.kind || !DOC_SPECIALIZATION_KINDS.has(rel.kind))
|
|
64505
|
+
continue;
|
|
64506
|
+
for (const target of rel.targets ?? [])
|
|
64507
|
+
out.push({ refText: target });
|
|
64508
|
+
}
|
|
64509
|
+
return out;
|
|
64510
|
+
}
|
|
64306
64511
|
function buildContextualHover(node) {
|
|
64307
64512
|
const contextual = nearestAncestor(node, /* @__PURE__ */ new Set(["IfExpr", "MetadataAnnotation", "RepStmt", "CommentStmt"]));
|
|
64308
64513
|
if (!contextual)
|
|
64309
64514
|
return void 0;
|
|
64310
64515
|
return buildConditionalHover(contextual) ?? buildMetadataAnnotationHover(contextual) ?? buildRepresentationHover(contextual) ?? buildCommentHover(contextual);
|
|
64311
64516
|
}
|
|
64312
|
-
function buildHoverMarkdown(node, relationshipDetails) {
|
|
64517
|
+
function buildHoverMarkdown(node, relationshipDetails, documentation) {
|
|
64313
64518
|
const named = node;
|
|
64314
64519
|
const dir = directionLabel(node);
|
|
64315
64520
|
const kind = kindLabel2(node);
|
|
@@ -64318,7 +64523,7 @@ function buildHoverMarkdown(node, relationshipDetails) {
|
|
|
64318
64523
|
const mult = multiplicityLabel(node);
|
|
64319
64524
|
const multHint = multiplicityPlainEnglish(node);
|
|
64320
64525
|
const rels = relationshipLabel(allRelationships(named));
|
|
64321
|
-
const doc =
|
|
64526
|
+
const doc = documentation;
|
|
64322
64527
|
const members = memberList(node);
|
|
64323
64528
|
const headerLines = [`${dir}${kind} ${name}${typing}${mult}${rels}`];
|
|
64324
64529
|
if (members.length > 0) {
|
|
@@ -64671,11 +64876,14 @@ var SysmlHoverProvider = class {
|
|
|
64671
64876
|
astNodeLocator;
|
|
64672
64877
|
documents;
|
|
64673
64878
|
indexManager;
|
|
64879
|
+
/** REQ-394 — the linker's lazy standard-library load, reused rather than duplicated. */
|
|
64880
|
+
linker;
|
|
64674
64881
|
constructor(services) {
|
|
64675
64882
|
this.references = services.references.References;
|
|
64676
64883
|
this.astNodeLocator = services.workspace.AstNodeLocator;
|
|
64677
64884
|
this.documents = services.shared.workspace.LangiumDocuments;
|
|
64678
64885
|
this.indexManager = services.shared.workspace.IndexManager;
|
|
64886
|
+
this.linker = services.references.Linker;
|
|
64679
64887
|
}
|
|
64680
64888
|
// REQ-246, REQ-334, REQ-335, REQ-336, REQ-342, REQ-343 — Route hover requests to markdown tooltip builders
|
|
64681
64889
|
async getHoverContent(document, params) {
|
|
@@ -64749,7 +64957,50 @@ var SysmlHoverProvider = class {
|
|
|
64749
64957
|
};
|
|
64750
64958
|
}
|
|
64751
64959
|
async buildElementHover(node) {
|
|
64752
|
-
return buildHoverMarkdown(node, await this.relationshipDetailSection(node));
|
|
64960
|
+
return buildHoverMarkdown(node, await this.relationshipDetailSection(node), await this.documentationSection(node));
|
|
64961
|
+
}
|
|
64962
|
+
/**
|
|
64963
|
+
* REQ-394 — The documentation an element reads with.
|
|
64964
|
+
*
|
|
64965
|
+
* Its own `doc` members win, all of them, in declaration order. With none of
|
|
64966
|
+
* its own, the walk goes up the typing and specialization graph breadth
|
|
64967
|
+
* first, so the CLOSEST documented type wins over a more general ancestor,
|
|
64968
|
+
* and stops at the first documented one — naming it, and linking to it, so
|
|
64969
|
+
* the reader can see where the prose came from.
|
|
64970
|
+
*
|
|
64971
|
+
* The walk resolves through the same machinery as go-to-definition
|
|
64972
|
+
* (`resolveRelationshipTarget` — local nodes, then the workspace index, then
|
|
64973
|
+
* one lazily parsed library file), so cross-file and standard-library
|
|
64974
|
+
* documentation arrive without a second name resolver. A `visited` set plus
|
|
64975
|
+
* the depth and node budgets make a cyclic or malformed graph terminate.
|
|
64976
|
+
*/
|
|
64977
|
+
async documentationSection(node) {
|
|
64978
|
+
const own = localDocs(node);
|
|
64979
|
+
if (own.length > 0)
|
|
64980
|
+
return renderDocs(own);
|
|
64981
|
+
const visited = /* @__PURE__ */ new Set([node]);
|
|
64982
|
+
let frontier = [node];
|
|
64983
|
+
for (let depth = 0; depth < MAX_DOC_DEPTH && frontier.length > 0; depth++) {
|
|
64984
|
+
const next = [];
|
|
64985
|
+
for (const current2 of frontier) {
|
|
64986
|
+
for (const candidate of supertypeCandidates(current2)) {
|
|
64987
|
+
if (visited.size >= MAX_DOC_NODES)
|
|
64988
|
+
return void 0;
|
|
64989
|
+
const target = candidate.ref ?? this.resolveDocumentationTarget(current2, candidate.refText);
|
|
64990
|
+
if (!target || visited.has(target))
|
|
64991
|
+
continue;
|
|
64992
|
+
visited.add(target);
|
|
64993
|
+
const inherited = localDocs(target);
|
|
64994
|
+
if (inherited.length > 0) {
|
|
64995
|
+
const label = identificationLabel(target) ?? candidate.refText;
|
|
64996
|
+
return renderDocs(inherited, `*Documentation inherited from ${kindLabel2(target)} \`${label}\`* \u2014 ${declarationLink(target)}`);
|
|
64997
|
+
}
|
|
64998
|
+
next.push(target);
|
|
64999
|
+
}
|
|
65000
|
+
}
|
|
65001
|
+
frontier = next;
|
|
65002
|
+
}
|
|
65003
|
+
return void 0;
|
|
64753
65004
|
}
|
|
64754
65005
|
async buildOperatorHover(token) {
|
|
64755
65006
|
const qualifiedName = OPERATOR_FUNCTIONS[token];
|
|
@@ -64799,6 +65050,46 @@ var SysmlHoverProvider = class {
|
|
|
64799
65050
|
return lines.length > 0 ? `*Relationships:*
|
|
64800
65051
|
${lines.join("\n")}` : void 0;
|
|
64801
65052
|
}
|
|
65053
|
+
/**
|
|
65054
|
+
* REQ-394 — Resolve a supertype for the documentation walk without pulling a
|
|
65055
|
+
* document into the workspace.
|
|
65056
|
+
*
|
|
65057
|
+
* The card itself may lazily parse ONE indexed library target (REQ-251), and
|
|
65058
|
+
* that is the budget. Looking for an inherited `doc` must not add to it, so
|
|
65059
|
+
* this reuses the LINKER's own lazy load — the same path a cross-reference
|
|
65060
|
+
* takes, which parses a standard-library file off to the side and caches it
|
|
65061
|
+
* — instead of `getOrCreateDocument`, which would make every hover grow
|
|
65062
|
+
* `LangiumDocuments`.
|
|
65063
|
+
*
|
|
65064
|
+
* The match has to be UNAMBIGUOUS. This is not a scope-aware resolver — it
|
|
65065
|
+
* cannot apply imports or visibility — so it only accepts a name written
|
|
65066
|
+
* exactly as the index holds it, or a final segment that names exactly one
|
|
65067
|
+
* element in the whole index. Two `Vehicle`s in different packages, or an
|
|
65068
|
+
* unresolved reference that happens to share a final segment with something
|
|
65069
|
+
* unrelated, therefore yield NO documentation instead of another element's
|
|
65070
|
+
* prose picked by index order. Wrong documentation reads as authoritative;
|
|
65071
|
+
* none reads as none.
|
|
65072
|
+
*/
|
|
65073
|
+
resolveDocumentationTarget(source, targetText) {
|
|
65074
|
+
const local = uniqueLocalTarget(source, targetText);
|
|
65075
|
+
if (local)
|
|
65076
|
+
return local;
|
|
65077
|
+
const key = relationshipTargetKey(targetText);
|
|
65078
|
+
const exact = /* @__PURE__ */ new Map();
|
|
65079
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
65080
|
+
for (const description of this.indexManager.allElements()) {
|
|
65081
|
+
const id = `${description.documentUri.toString()}#${description.path}`;
|
|
65082
|
+
if (description.name === targetText)
|
|
65083
|
+
exact.set(id, description);
|
|
65084
|
+
else if (description.name === key)
|
|
65085
|
+
byKey.set(id, description);
|
|
65086
|
+
}
|
|
65087
|
+
const pool = exact.size > 0 ? exact : byKey;
|
|
65088
|
+
if (pool.size !== 1)
|
|
65089
|
+
return void 0;
|
|
65090
|
+
const chosen = pool.values().next().value;
|
|
65091
|
+
return this.linker.resolveIndexedNode?.(chosen) ?? chosen.node;
|
|
65092
|
+
}
|
|
64802
65093
|
async resolveRelationshipTarget(source, targetText) {
|
|
64803
65094
|
const local = this.findLocalRelationshipTarget(source, targetText);
|
|
64804
65095
|
if (local)
|
|
@@ -66671,6 +66962,19 @@ var SysmlLinker = class extends DefaultLinker {
|
|
|
66671
66962
|
const root3 = this.parsedLibraryDocument(nodeDescription.documentUri)?.parseResult?.value;
|
|
66672
66963
|
return root3 ? this.astNodeLocator.getAstNode(root3, nodeDescription.path) : void 0;
|
|
66673
66964
|
}
|
|
66965
|
+
/**
|
|
66966
|
+
* REQ-394 — Resolve an indexed description to a node WITHOUT adding a
|
|
66967
|
+
* document to the workspace.
|
|
66968
|
+
*
|
|
66969
|
+
* This is the same path a cross-reference takes (`loadAstNode`): an already
|
|
66970
|
+
* live node, otherwise the standard-library file parsed once off to the side
|
|
66971
|
+
* and cached here. Hover's documentation walk uses it so looking for an
|
|
66972
|
+
* inherited `doc` never pulls a library file into `LangiumDocuments`, and so
|
|
66973
|
+
* there is one lazy-library-load path rather than two.
|
|
66974
|
+
*/
|
|
66975
|
+
resolveIndexedNode(nodeDescription) {
|
|
66976
|
+
return this.loadAstNode(nodeDescription);
|
|
66977
|
+
}
|
|
66674
66978
|
/** Parse (once) the standard-library file a precomputed description points at.
|
|
66675
66979
|
* Prefers an already-live workspace document when one exists (runtime-loaded
|
|
66676
66980
|
* library, or the user opened the file) so there is never a second copy. */
|
|
@@ -67302,14 +67606,526 @@ function getNodeAtRange(document, params) {
|
|
|
67302
67606
|
return leafAtOffset2(root3, offset)?.astNode;
|
|
67303
67607
|
}
|
|
67304
67608
|
|
|
67609
|
+
// ../language-server/out/src/services/formatter-settings.js
|
|
67610
|
+
var FORMATTER_DEFAULTS = Object.freeze({
|
|
67611
|
+
indentStyle: "editor",
|
|
67612
|
+
indentSize: 0,
|
|
67613
|
+
blankLines: 1,
|
|
67614
|
+
lineWidth: 0,
|
|
67615
|
+
emptyBody: "preserve",
|
|
67616
|
+
quotedNames: "preserve",
|
|
67617
|
+
documentationIndent: "preserve",
|
|
67618
|
+
keywords: "preserve"
|
|
67619
|
+
});
|
|
67620
|
+
var sysmlFormatterSettings = { ...FORMATTER_DEFAULTS };
|
|
67621
|
+
var OPTION_PREFIX = "sysml.format.";
|
|
67622
|
+
var INDENT_STYLES = ["editor", "spaces", "tabs"];
|
|
67623
|
+
var EMPTY_BODY_STYLES = ["preserve", "semicolon", "braces"];
|
|
67624
|
+
var QUOTED_NAME_STYLES = ["preserve", "unquoteSafe"];
|
|
67625
|
+
var DOC_INDENT_STYLES = ["preserve", "align"];
|
|
67626
|
+
var KEYWORD_STYLES = ["preserve", "symbolic"];
|
|
67627
|
+
var MAX_INDENT_SIZE = 16;
|
|
67628
|
+
var MIN_LINE_WIDTH = 40;
|
|
67629
|
+
var MAX_LINE_WIDTH = 400;
|
|
67630
|
+
var MAX_BLANK_LINES = 2;
|
|
67631
|
+
function readEnum(name, value, allowed, fallback, issues) {
|
|
67632
|
+
if (value === void 0)
|
|
67633
|
+
return fallback;
|
|
67634
|
+
if (typeof value === "string" && allowed.includes(value))
|
|
67635
|
+
return value;
|
|
67636
|
+
issues.push({ setting: name, value, reason: `expected one of ${allowed.map((a2) => `'${a2}'`).join(", ")}` });
|
|
67637
|
+
return fallback;
|
|
67638
|
+
}
|
|
67639
|
+
function readInt(name, value, min2, max, fallback, issues, zeroMeans) {
|
|
67640
|
+
if (value === void 0)
|
|
67641
|
+
return fallback;
|
|
67642
|
+
if (typeof value === "number" && Number.isInteger(value) && (value === 0 && zeroMeans !== void 0 ? true : value >= min2 && value <= max)) {
|
|
67643
|
+
return value;
|
|
67644
|
+
}
|
|
67645
|
+
const range = zeroMeans !== void 0 ? `0 (${zeroMeans}) or ${min2}\u2013${max}` : `${min2}\u2013${max}`;
|
|
67646
|
+
issues.push({ setting: name, value, reason: `expected an integer ${range}` });
|
|
67647
|
+
return fallback;
|
|
67648
|
+
}
|
|
67649
|
+
function resolveFormatterSettings(raw, base = FORMATTER_DEFAULTS) {
|
|
67650
|
+
const issues = [];
|
|
67651
|
+
const source = raw && typeof raw === "object" ? raw : {};
|
|
67652
|
+
const settings = {
|
|
67653
|
+
indentStyle: readEnum("sysml.format.indentStyle", source.indentStyle, INDENT_STYLES, base.indentStyle, issues),
|
|
67654
|
+
indentSize: readInt("sysml.format.indentSize", source.indentSize, 1, MAX_INDENT_SIZE, base.indentSize, issues, "follow the editor"),
|
|
67655
|
+
blankLines: readInt("sysml.format.blankLines", source.blankLines, 0, MAX_BLANK_LINES, base.blankLines, issues),
|
|
67656
|
+
lineWidth: readInt("sysml.format.lineWidth", source.lineWidth, MIN_LINE_WIDTH, MAX_LINE_WIDTH, base.lineWidth, issues, "never wrap"),
|
|
67657
|
+
emptyBody: readEnum("sysml.format.emptyBody", source.emptyBody, EMPTY_BODY_STYLES, base.emptyBody, issues),
|
|
67658
|
+
quotedNames: readEnum("sysml.format.quotedNames", source.quotedNames, QUOTED_NAME_STYLES, base.quotedNames, issues),
|
|
67659
|
+
documentationIndent: readEnum("sysml.format.documentationIndent", source.documentationIndent, DOC_INDENT_STYLES, base.documentationIndent, issues),
|
|
67660
|
+
keywords: readEnum("sysml.format.keywords", source.keywords, KEYWORD_STYLES, base.keywords, issues)
|
|
67661
|
+
};
|
|
67662
|
+
return { settings, issues };
|
|
67663
|
+
}
|
|
67664
|
+
function resolveFormatterOptions(options, workspace = sysmlFormatterSettings) {
|
|
67665
|
+
const overrides = {};
|
|
67666
|
+
for (const [key, value] of Object.entries(options ?? {})) {
|
|
67667
|
+
if (key.startsWith(OPTION_PREFIX))
|
|
67668
|
+
overrides[key.slice(OPTION_PREFIX.length)] = value;
|
|
67669
|
+
}
|
|
67670
|
+
return resolveFormatterSettings(overrides, workspace);
|
|
67671
|
+
}
|
|
67672
|
+
function effectiveFormattingOptions(options, settings) {
|
|
67673
|
+
const insertSpaces = settings.indentStyle === "editor" ? options.insertSpaces : settings.indentStyle === "spaces";
|
|
67674
|
+
const tabSize = settings.indentSize === 0 ? options.tabSize : settings.indentSize;
|
|
67675
|
+
if (insertSpaces === options.insertSpaces && tabSize === options.tabSize)
|
|
67676
|
+
return options;
|
|
67677
|
+
return { ...options, insertSpaces, tabSize };
|
|
67678
|
+
}
|
|
67679
|
+
function indentUnit(options) {
|
|
67680
|
+
return options.insertSpaces ? " ".repeat(Math.max(1, options.tabSize)) : " ";
|
|
67681
|
+
}
|
|
67682
|
+
|
|
67683
|
+
// ../language-server/out/src/services/formatter-style.js
|
|
67684
|
+
var SYMBOLIC_KEYWORDS = Object.freeze({
|
|
67685
|
+
specializes: ":>",
|
|
67686
|
+
subsets: ":>",
|
|
67687
|
+
redefines: ":>>",
|
|
67688
|
+
references: "::>",
|
|
67689
|
+
crosses: "=>"
|
|
67690
|
+
});
|
|
67691
|
+
var TYPING_PHRASES = ["typed", "defined"];
|
|
67692
|
+
var BARE_ID2 = /^[_a-zA-Z]\w*$/u;
|
|
67693
|
+
var ESCAPED_NAME = /^'([^'\\]|\\.)*'$/u;
|
|
67694
|
+
function projectFormatted(document, edits) {
|
|
67695
|
+
const doc = document.textDocument;
|
|
67696
|
+
const source = doc.getText();
|
|
67697
|
+
const list = edits.map((edit) => ({
|
|
67698
|
+
start: doc.offsetAt(edit.range.start),
|
|
67699
|
+
end: doc.offsetAt(edit.range.end),
|
|
67700
|
+
newText: edit.newText
|
|
67701
|
+
})).sort((a2, b) => a2.start - b.start);
|
|
67702
|
+
let out = "";
|
|
67703
|
+
let cursor = 0;
|
|
67704
|
+
let delta = 0;
|
|
67705
|
+
const marks = [];
|
|
67706
|
+
for (const edit of list) {
|
|
67707
|
+
if (edit.start < cursor)
|
|
67708
|
+
continue;
|
|
67709
|
+
out += source.slice(cursor, edit.start) + edit.newText;
|
|
67710
|
+
delta += edit.newText.length - (edit.end - edit.start);
|
|
67711
|
+
marks.push({ at: edit.end, delta });
|
|
67712
|
+
cursor = edit.end;
|
|
67713
|
+
}
|
|
67714
|
+
out += source.slice(cursor);
|
|
67715
|
+
return {
|
|
67716
|
+
text: out,
|
|
67717
|
+
project(offset) {
|
|
67718
|
+
let low = 0;
|
|
67719
|
+
let high = marks.length - 1;
|
|
67720
|
+
let applied = 0;
|
|
67721
|
+
while (low <= high) {
|
|
67722
|
+
const mid = low + high >> 1;
|
|
67723
|
+
if (marks[mid].at <= offset) {
|
|
67724
|
+
applied = marks[mid].delta;
|
|
67725
|
+
low = mid + 1;
|
|
67726
|
+
} else {
|
|
67727
|
+
high = mid - 1;
|
|
67728
|
+
}
|
|
67729
|
+
}
|
|
67730
|
+
return offset + applied;
|
|
67731
|
+
}
|
|
67732
|
+
};
|
|
67733
|
+
}
|
|
67734
|
+
function lineAt(text, offset) {
|
|
67735
|
+
const start = text.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
|
|
67736
|
+
const nl = text.indexOf("\n", offset);
|
|
67737
|
+
const end = nl < 0 ? text.length : nl;
|
|
67738
|
+
const indent = /^[ \t]*/u.exec(text.slice(start, end))?.[0] ?? "";
|
|
67739
|
+
return { start, end, indent };
|
|
67740
|
+
}
|
|
67741
|
+
function leavesOf(document) {
|
|
67742
|
+
const rootCst = document.parseResult?.value?.$cstNode;
|
|
67743
|
+
return rootCst ? cst_utils_exports.flattenCst(rootCst).toArray() : [];
|
|
67744
|
+
}
|
|
67745
|
+
function nextLeaf(leaves, index, skipHidden) {
|
|
67746
|
+
for (let scan = index + 1; scan < leaves.length; scan++) {
|
|
67747
|
+
if (!skipHidden || !leaves[scan].hidden)
|
|
67748
|
+
return leaves[scan];
|
|
67749
|
+
}
|
|
67750
|
+
return void 0;
|
|
67751
|
+
}
|
|
67752
|
+
function keywordRewrites(leaves, out) {
|
|
67753
|
+
for (let index = 0; index < leaves.length; index++) {
|
|
67754
|
+
const leaf = leaves[index];
|
|
67755
|
+
if (leaf.hidden)
|
|
67756
|
+
continue;
|
|
67757
|
+
const symbol = SYMBOLIC_KEYWORDS[leaf.text];
|
|
67758
|
+
if (symbol) {
|
|
67759
|
+
out.push({ offset: leaf.offset, end: leaf.end, newText: symbol });
|
|
67760
|
+
continue;
|
|
67761
|
+
}
|
|
67762
|
+
if (TYPING_PHRASES.includes(leaf.text) && leaf.astNode?.$type === "Typing") {
|
|
67763
|
+
const by = nextLeaf(leaves, index, true);
|
|
67764
|
+
if (by?.text === "by" && by.astNode?.$type === "Typing") {
|
|
67765
|
+
out.push({ offset: leaf.offset, end: by.end, newText: ":" });
|
|
67766
|
+
}
|
|
67767
|
+
}
|
|
67768
|
+
}
|
|
67769
|
+
}
|
|
67770
|
+
var BRACE_ONLY_BODIES = /* @__PURE__ */ new Set([
|
|
67771
|
+
"InvShorthand",
|
|
67772
|
+
"ActionBlock",
|
|
67773
|
+
"EffectClause",
|
|
67774
|
+
"Import"
|
|
67775
|
+
]);
|
|
67776
|
+
function emptyBraces(node, leaves, source) {
|
|
67777
|
+
const cst = node.$cstNode;
|
|
67778
|
+
if (!cst)
|
|
67779
|
+
return void 0;
|
|
67780
|
+
let open;
|
|
67781
|
+
let close;
|
|
67782
|
+
for (const leaf of leaves) {
|
|
67783
|
+
if (leaf.offset < cst.offset || leaf.end > cst.end || leaf.astNode !== node)
|
|
67784
|
+
continue;
|
|
67785
|
+
if (leaf.text === "{" && !open)
|
|
67786
|
+
open = leaf;
|
|
67787
|
+
if (leaf.text === "}")
|
|
67788
|
+
close = leaf;
|
|
67789
|
+
}
|
|
67790
|
+
if (!open || !close || close.offset < open.end)
|
|
67791
|
+
return void 0;
|
|
67792
|
+
return source.slice(open.end, close.offset).trim().length === 0 ? { open, close } : void 0;
|
|
67793
|
+
}
|
|
67794
|
+
function terminatingSemicolon(node, leaves) {
|
|
67795
|
+
const cst = node.$cstNode;
|
|
67796
|
+
if (!cst)
|
|
67797
|
+
return void 0;
|
|
67798
|
+
for (let index = leaves.length - 1; index >= 0; index--) {
|
|
67799
|
+
const leaf = leaves[index];
|
|
67800
|
+
if (leaf.end !== cst.end)
|
|
67801
|
+
continue;
|
|
67802
|
+
return leaf.text === ";" && leaf.astNode === node ? leaf : void 0;
|
|
67803
|
+
}
|
|
67804
|
+
return void 0;
|
|
67805
|
+
}
|
|
67806
|
+
function emptyBodyRewrites(document, leaves, projection, settings, options, out) {
|
|
67807
|
+
const root3 = document.parseResult?.value;
|
|
67808
|
+
if (!root3)
|
|
67809
|
+
return;
|
|
67810
|
+
const source = document.textDocument.getText();
|
|
67811
|
+
const leafBefore = /* @__PURE__ */ new Map();
|
|
67812
|
+
for (let index = 1; index < leaves.length; index++)
|
|
67813
|
+
leafBefore.set(leaves[index].offset, leaves[index - 1]);
|
|
67814
|
+
for (const node of ast_utils_exports.streamAllContents(root3)) {
|
|
67815
|
+
const members = node.members;
|
|
67816
|
+
if (!Array.isArray(members) || members.length > 0)
|
|
67817
|
+
continue;
|
|
67818
|
+
if (BRACE_ONLY_BODIES.has(node.$type))
|
|
67819
|
+
continue;
|
|
67820
|
+
if (settings.emptyBody === "semicolon") {
|
|
67821
|
+
const braces = emptyBraces(node, leaves, source);
|
|
67822
|
+
if (!braces)
|
|
67823
|
+
continue;
|
|
67824
|
+
const previous = leafBefore.get(braces.open.offset);
|
|
67825
|
+
const from = previous && previous.end <= braces.open.offset ? previous.end : braces.open.offset;
|
|
67826
|
+
out.push({ offset: from, end: braces.close.end, newText: ";" });
|
|
67827
|
+
continue;
|
|
67828
|
+
}
|
|
67829
|
+
if (settings.emptyBody === "braces") {
|
|
67830
|
+
if (emptyBraces(node, leaves, source))
|
|
67831
|
+
continue;
|
|
67832
|
+
const semicolon = terminatingSemicolon(node, leaves);
|
|
67833
|
+
if (!semicolon)
|
|
67834
|
+
continue;
|
|
67835
|
+
const indent = lineAt(projection.text, projection.project(node.$cstNode.offset)).indent;
|
|
67836
|
+
out.push({
|
|
67837
|
+
offset: semicolon.offset,
|
|
67838
|
+
end: semicolon.end,
|
|
67839
|
+
newText: ` {
|
|
67840
|
+
${indent}}`
|
|
67841
|
+
});
|
|
67842
|
+
}
|
|
67843
|
+
}
|
|
67844
|
+
}
|
|
67845
|
+
function quotedNameRewrites(leaves, reservedKeywords, out) {
|
|
67846
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
67847
|
+
const declared = /* @__PURE__ */ new Set();
|
|
67848
|
+
const bareSpellings = /* @__PURE__ */ new Set();
|
|
67849
|
+
for (const leaf of leaves) {
|
|
67850
|
+
if (leaf.hidden)
|
|
67851
|
+
continue;
|
|
67852
|
+
if (BARE_ID2.test(leaf.text) && !reservedKeywords.has(leaf.text)) {
|
|
67853
|
+
bareSpellings.add(leaf.text);
|
|
67854
|
+
continue;
|
|
67855
|
+
}
|
|
67856
|
+
if (!ESCAPED_NAME.test(leaf.text))
|
|
67857
|
+
continue;
|
|
67858
|
+
const body = leaf.text.slice(1, -1);
|
|
67859
|
+
if (!BARE_ID2.test(body) || reservedKeywords.has(body))
|
|
67860
|
+
continue;
|
|
67861
|
+
const list = occurrences.get(body);
|
|
67862
|
+
if (list)
|
|
67863
|
+
list.push(leaf);
|
|
67864
|
+
else
|
|
67865
|
+
occurrences.set(body, [leaf]);
|
|
67866
|
+
if (grammar_utils_exports.findAssignment(leaf)?.feature === "name")
|
|
67867
|
+
declared.add(body);
|
|
67868
|
+
}
|
|
67869
|
+
for (const [body, list] of occurrences) {
|
|
67870
|
+
if (!declared.has(body) || bareSpellings.has(body))
|
|
67871
|
+
continue;
|
|
67872
|
+
for (const leaf of list)
|
|
67873
|
+
out.push({ offset: leaf.offset, end: leaf.end, newText: body });
|
|
67874
|
+
}
|
|
67875
|
+
}
|
|
67876
|
+
function documentationRewrites(document, leaves, projection, options, out) {
|
|
67877
|
+
for (const leaf of leaves) {
|
|
67878
|
+
if (leaf.hidden || !leaf.text.includes("\n"))
|
|
67879
|
+
continue;
|
|
67880
|
+
const owner = leaf.astNode?.$type;
|
|
67881
|
+
if (owner !== "DocCommentMember" && owner !== "CommentStmt")
|
|
67882
|
+
continue;
|
|
67883
|
+
if (!leaf.text.includes("/*"))
|
|
67884
|
+
continue;
|
|
67885
|
+
const projected = projection.project(leaf.offset);
|
|
67886
|
+
const line = lineAt(projection.text, projected);
|
|
67887
|
+
if (projection.text.slice(line.start, projected).trim().length > 0)
|
|
67888
|
+
continue;
|
|
67889
|
+
const target = line.indent;
|
|
67890
|
+
const bodyLines = leaf.text.split("\n");
|
|
67891
|
+
let common;
|
|
67892
|
+
for (let index = 1; index < bodyLines.length; index++) {
|
|
67893
|
+
const raw = bodyLines[index].replace(/\r$/u, "");
|
|
67894
|
+
if (raw.trim().length === 0)
|
|
67895
|
+
continue;
|
|
67896
|
+
const lead = /^[ \t]*/u.exec(raw)?.[0] ?? "";
|
|
67897
|
+
if (common === void 0 || lead.length < common.length)
|
|
67898
|
+
common = lead;
|
|
67899
|
+
}
|
|
67900
|
+
if (common === void 0)
|
|
67901
|
+
continue;
|
|
67902
|
+
let cursor = leaf.offset + bodyLines[0].length + 1;
|
|
67903
|
+
for (let index = 1; index < bodyLines.length; index++) {
|
|
67904
|
+
const raw = bodyLines[index];
|
|
67905
|
+
const lead = /^[ \t]*/u.exec(raw)?.[0] ?? "";
|
|
67906
|
+
if (raw.replace(/\r$/u, "").trim().length > 0) {
|
|
67907
|
+
const shifted = target + lead.slice(Math.min(common.length, lead.length));
|
|
67908
|
+
if (shifted !== lead)
|
|
67909
|
+
out.push({ offset: cursor, end: cursor + lead.length, newText: shifted });
|
|
67910
|
+
}
|
|
67911
|
+
cursor += raw.length + 1;
|
|
67912
|
+
}
|
|
67913
|
+
}
|
|
67914
|
+
}
|
|
67915
|
+
function wrapRewrites(leaves, projection, options, lineWidth, out) {
|
|
67916
|
+
const unit = indentUnit(options);
|
|
67917
|
+
for (let index = 0; index < leaves.length; index++) {
|
|
67918
|
+
const leaf = leaves[index];
|
|
67919
|
+
if (leaf.hidden || leaf.text !== ",")
|
|
67920
|
+
continue;
|
|
67921
|
+
const following = nextLeaf(leaves, index, false);
|
|
67922
|
+
if (!following)
|
|
67923
|
+
continue;
|
|
67924
|
+
const projected = projection.project(leaf.offset);
|
|
67925
|
+
const line = lineAt(projection.text, projected);
|
|
67926
|
+
if (line.end - line.start <= lineWidth)
|
|
67927
|
+
continue;
|
|
67928
|
+
if (projection.text.slice(projection.project(leaf.end), line.end).trim().length === 0)
|
|
67929
|
+
continue;
|
|
67930
|
+
if (projection.text.slice(projected, projection.project(following.offset)).includes("\n"))
|
|
67931
|
+
continue;
|
|
67932
|
+
out.push({
|
|
67933
|
+
offset: leaf.end,
|
|
67934
|
+
end: following.offset,
|
|
67935
|
+
newText: `
|
|
67936
|
+
${line.indent}${unit}`
|
|
67937
|
+
});
|
|
67938
|
+
}
|
|
67939
|
+
}
|
|
67940
|
+
function withinRange(document, rewrite, range) {
|
|
67941
|
+
if (!range)
|
|
67942
|
+
return true;
|
|
67943
|
+
const doc = document.textDocument;
|
|
67944
|
+
return doc.offsetAt(range.start) <= rewrite.offset && rewrite.end <= doc.offsetAt(range.end);
|
|
67945
|
+
}
|
|
67946
|
+
function styleEdits(input) {
|
|
67947
|
+
const { document, settings, options, baseEdits, protectedRanges, range, reservedKeywords } = input;
|
|
67948
|
+
const wantsRewrite = settings.emptyBody !== "preserve" || settings.quotedNames !== "preserve" || settings.documentationIndent !== "preserve" || settings.keywords !== "preserve" || settings.lineWidth > 0;
|
|
67949
|
+
if (!wantsRewrite)
|
|
67950
|
+
return [];
|
|
67951
|
+
const leaves = leavesOf(document);
|
|
67952
|
+
if (leaves.length === 0)
|
|
67953
|
+
return [];
|
|
67954
|
+
const projection = projectFormatted(document, baseEdits);
|
|
67955
|
+
const rewrites = [];
|
|
67956
|
+
if (settings.keywords === "symbolic")
|
|
67957
|
+
keywordRewrites(leaves, rewrites);
|
|
67958
|
+
if (settings.emptyBody !== "preserve") {
|
|
67959
|
+
emptyBodyRewrites(document, leaves, projection, settings, options, rewrites);
|
|
67960
|
+
}
|
|
67961
|
+
if (settings.quotedNames === "unquoteSafe")
|
|
67962
|
+
quotedNameRewrites(leaves, reservedKeywords, rewrites);
|
|
67963
|
+
if (settings.documentationIndent === "align") {
|
|
67964
|
+
documentationRewrites(document, leaves, projection, options, rewrites);
|
|
67965
|
+
}
|
|
67966
|
+
if (settings.lineWidth > 0)
|
|
67967
|
+
wrapRewrites(leaves, projection, options, settings.lineWidth, rewrites);
|
|
67968
|
+
const doc = document.textDocument;
|
|
67969
|
+
const source = doc.getText();
|
|
67970
|
+
const accepted = [];
|
|
67971
|
+
let lastEnd = -1;
|
|
67972
|
+
for (const rewrite of rewrites.sort((a2, b) => a2.offset - b.offset || a2.end - b.end)) {
|
|
67973
|
+
if (rewrite.offset < lastEnd)
|
|
67974
|
+
continue;
|
|
67975
|
+
if (source.slice(rewrite.offset, rewrite.end) === rewrite.newText)
|
|
67976
|
+
continue;
|
|
67977
|
+
if (intersectsProtected(protectedRanges, rewrite.offset, rewrite.end))
|
|
67978
|
+
continue;
|
|
67979
|
+
if (!withinRange(document, rewrite, range))
|
|
67980
|
+
continue;
|
|
67981
|
+
accepted.push(rewrite);
|
|
67982
|
+
lastEnd = rewrite.end;
|
|
67983
|
+
}
|
|
67984
|
+
return accepted.map((rewrite) => ({
|
|
67985
|
+
range: { start: doc.positionAt(rewrite.offset), end: doc.positionAt(rewrite.end) },
|
|
67986
|
+
newText: rewrite.newText
|
|
67987
|
+
}));
|
|
67988
|
+
}
|
|
67989
|
+
|
|
67305
67990
|
// ../language-server/out/src/services/formatter.js
|
|
67306
67991
|
var INDENT = Formatting.indent({ allowMore: true });
|
|
67307
67992
|
var DEDENT = Formatting.noIndent();
|
|
67308
67993
|
var NL = Formatting.newLine();
|
|
67309
|
-
var NL2 = Formatting.newLines(2);
|
|
67310
67994
|
var SPACE = Formatting.oneSpace();
|
|
67311
67995
|
var NO_SPACE = Formatting.noSpace();
|
|
67312
67996
|
var SysmlFormatter = class extends AbstractFormatter {
|
|
67997
|
+
services;
|
|
67998
|
+
/**
|
|
67999
|
+
* The settings in force for the request being served. Formatting is
|
|
68000
|
+
* synchronous from `formatDocument` through the AST pass, so a field is the
|
|
68001
|
+
* whole of the plumbing the base class does not offer.
|
|
68002
|
+
*/
|
|
68003
|
+
active = FORMATTER_DEFAULTS;
|
|
68004
|
+
keywordCache;
|
|
68005
|
+
constructor(services) {
|
|
68006
|
+
super();
|
|
68007
|
+
this.services = services;
|
|
68008
|
+
}
|
|
68009
|
+
// REQ-392, REQ-393 — Full-document formatting.
|
|
68010
|
+
formatDocument(document, params) {
|
|
68011
|
+
return this.formatWith(document, params.options, (options) => super.formatDocument(document, { ...params, options }));
|
|
68012
|
+
}
|
|
68013
|
+
// REQ-392, REQ-393 — Range formatting. A range that reaches into a preserved
|
|
68014
|
+
// element formats the rest of the range and leaves that element whole: the
|
|
68015
|
+
// base formatter narrows to the range, and the same filter then drops
|
|
68016
|
+
// whatever would have rewritten a frozen slice, so a partial rewrite is
|
|
68017
|
+
// impossible. The settings are resolved identically to the full-document
|
|
68018
|
+
// path, so the two cannot produce different style.
|
|
68019
|
+
formatDocumentRange(document, params) {
|
|
68020
|
+
return this.formatWith(document, params.options, (options) => super.formatDocumentRange(document, { ...params, options }), params.range);
|
|
68021
|
+
}
|
|
68022
|
+
// REQ-392, REQ-393 — Format-on-type formats the current line.
|
|
68023
|
+
formatDocumentOnType(document, params) {
|
|
68024
|
+
const line = { start: { line: params.position.line, character: 0 }, end: params.position };
|
|
68025
|
+
return this.formatWith(document, params.options, (options) => super.formatDocumentOnType(document, { ...params, options }), line);
|
|
68026
|
+
}
|
|
68027
|
+
/**
|
|
68028
|
+
* REQ-392, REQ-393 — One pipeline for all three entry points: resolve the
|
|
68029
|
+
* settings, run the base whitespace pass under them, drop whatever a
|
|
68030
|
+
* `sysml-format ignore` froze, then add the token-level rewrites the style
|
|
68031
|
+
* options ask for.
|
|
68032
|
+
*/
|
|
68033
|
+
formatWith(document, requested, base, range) {
|
|
68034
|
+
const { settings } = resolveFormatterOptions(requested);
|
|
68035
|
+
const options = effectiveFormattingOptions(requested, settings);
|
|
68036
|
+
this.active = settings;
|
|
68037
|
+
const protectedRanges = scanFormatDirectives(document).protectedRanges;
|
|
68038
|
+
const baseEdits = this.preserveIgnored(document, base(options), protectedRanges);
|
|
68039
|
+
const style = styleEdits({
|
|
68040
|
+
document,
|
|
68041
|
+
settings,
|
|
68042
|
+
options,
|
|
68043
|
+
baseEdits,
|
|
68044
|
+
protectedRanges,
|
|
68045
|
+
range,
|
|
68046
|
+
reservedKeywords: this.reservedKeywords()
|
|
68047
|
+
});
|
|
68048
|
+
return this.merge(document, baseEdits, style);
|
|
68049
|
+
}
|
|
68050
|
+
/**
|
|
68051
|
+
* REQ-392 — Drop every edit that reaches inside a preserved element.
|
|
68052
|
+
*
|
|
68053
|
+
* Langium only ever edits the whitespace BETWEEN two tokens, so an edit is
|
|
68054
|
+
* either wholly inside a preserved slice (dropped, leaving the slice
|
|
68055
|
+
* byte-for-byte identical, line endings included) or wholly outside it
|
|
68056
|
+
* (kept, so the element's own indentation and its surroundings are still
|
|
68057
|
+
* formatted).
|
|
68058
|
+
*/
|
|
68059
|
+
preserveIgnored(document, edits, ranges) {
|
|
68060
|
+
if (edits.length === 0 || ranges.length === 0)
|
|
68061
|
+
return edits;
|
|
68062
|
+
const text = document.textDocument;
|
|
68063
|
+
return edits.filter((edit) => !intersectsProtected(ranges, text.offsetAt(edit.range.start), text.offsetAt(edit.range.end)));
|
|
68064
|
+
}
|
|
68065
|
+
/**
|
|
68066
|
+
* REQ-393 — Combine the whitespace pass with the token rewrites. A token
|
|
68067
|
+
* rewrite wins where the two touch: `part def P { }` → `part def P;` has to
|
|
68068
|
+
* swallow the space the whitespace pass wanted to put before the brace.
|
|
68069
|
+
*/
|
|
68070
|
+
merge(document, baseEdits, style) {
|
|
68071
|
+
if (style.length === 0)
|
|
68072
|
+
return baseEdits;
|
|
68073
|
+
const text = document.textDocument;
|
|
68074
|
+
const spans = style.map((edit) => ({
|
|
68075
|
+
offset: text.offsetAt(edit.range.start),
|
|
68076
|
+
end: text.offsetAt(edit.range.end)
|
|
68077
|
+
}));
|
|
68078
|
+
const kept = baseEdits.filter((edit) => {
|
|
68079
|
+
const start = text.offsetAt(edit.range.start);
|
|
68080
|
+
const end = text.offsetAt(edit.range.end);
|
|
68081
|
+
return !spans.some((span) => start < span.end && end > span.offset);
|
|
68082
|
+
});
|
|
68083
|
+
return [...kept, ...style].sort((a2, b) => text.offsetAt(a2.range.start) - text.offsetAt(b.range.start));
|
|
68084
|
+
}
|
|
68085
|
+
/**
|
|
68086
|
+
* REQ-393 — Every keyword the grammar reserves. A quoted name may only lose
|
|
68087
|
+
* its quotes when the bare spelling would still be a name and not a keyword,
|
|
68088
|
+
* and the grammar is the only honest source for that list.
|
|
68089
|
+
*/
|
|
68090
|
+
reservedKeywords() {
|
|
68091
|
+
if (this.keywordCache)
|
|
68092
|
+
return this.keywordCache;
|
|
68093
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
68094
|
+
const grammar = this.services?.Grammar;
|
|
68095
|
+
if (grammar) {
|
|
68096
|
+
for (const node of ast_utils_exports.streamAllContents(grammar)) {
|
|
68097
|
+
if (node.$type === "Keyword") {
|
|
68098
|
+
const value = node.value;
|
|
68099
|
+
if (value && /^[_a-zA-Z]\w*$/u.test(value))
|
|
68100
|
+
keywords.add(value);
|
|
68101
|
+
}
|
|
68102
|
+
}
|
|
68103
|
+
}
|
|
68104
|
+
this.keywordCache = keywords;
|
|
68105
|
+
return keywords;
|
|
68106
|
+
}
|
|
68107
|
+
/**
|
|
68108
|
+
* REQ-392 — Keep the indentation level a comment interrupts.
|
|
68109
|
+
*
|
|
68110
|
+
* Langium 3.5's `createTextEdit` returns early for a hidden node and never
|
|
68111
|
+
* applies the formatting action's indentation move to the running context.
|
|
68112
|
+
* `{` carries the block's `indent` action, so a comment sitting between `{`
|
|
68113
|
+
* and the first member — which is exactly where a `sysml-format ignore`
|
|
68114
|
+
* directive goes — dropped the whole block back to column 0. Applying the
|
|
68115
|
+
* move ourselves restores the level a composite node would have kept, and
|
|
68116
|
+
* leaves every other hidden-node path untouched.
|
|
68117
|
+
*/
|
|
68118
|
+
createTextEdit(a2, b, formatting, context) {
|
|
68119
|
+
const edits = super.createTextEdit(a2, b, formatting, context);
|
|
68120
|
+
if (!b.hidden)
|
|
68121
|
+
return edits;
|
|
68122
|
+
const startRange = {
|
|
68123
|
+
start: { line: b.range.start.line, character: 0 },
|
|
68124
|
+
end: b.range.start
|
|
68125
|
+
};
|
|
68126
|
+
context.indentation += this.findFittingMove(startRange, formatting.moves, context)?.tabs ?? 0;
|
|
68127
|
+
return edits;
|
|
68128
|
+
}
|
|
67313
68129
|
// REQ-259 — Dispatch formatting from AST node kind
|
|
67314
68130
|
format(node) {
|
|
67315
68131
|
if (isDocument(node)) {
|
|
@@ -67417,13 +68233,28 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67417
68233
|
f.keyword(first2).append(SPACE);
|
|
67418
68234
|
f.keyword(second).prepend(SPACE);
|
|
67419
68235
|
}
|
|
68236
|
+
/**
|
|
68237
|
+
* REQ-393 — Top-level element separation is decided HERE and nowhere else.
|
|
68238
|
+
*
|
|
68239
|
+
* The first element gets `noSpace`, which erases leading blank lines rather
|
|
68240
|
+
* than adding two of them: the document-level rule and the element's own
|
|
68241
|
+
* `prepend` used to fire at the same offset, and two insertions at one
|
|
68242
|
+
* position stack, so every file that began with `package` gained blank
|
|
68243
|
+
* lines at the top on every format. `blankLines` decides the rest.
|
|
68244
|
+
*/
|
|
67420
68245
|
fmtDocument(node) {
|
|
67421
68246
|
const f = this.getNodeFormatter(node);
|
|
67422
|
-
|
|
68247
|
+
const separator = Formatting.newLines(1 + this.active.blankLines);
|
|
68248
|
+
for (let index = 0; index < node.elements.length; index++) {
|
|
68249
|
+
f.property("elements", index).prepend(index === 0 ? NO_SPACE : separator);
|
|
68250
|
+
}
|
|
67423
68251
|
}
|
|
67424
68252
|
fmtBlock(node, keyword) {
|
|
67425
68253
|
const f = this.getNodeFormatter(node);
|
|
67426
|
-
|
|
68254
|
+
if (!isDocument(node.$container)) {
|
|
68255
|
+
f.keyword(keyword).prepend(Formatting.newLines(1 + this.active.blankLines));
|
|
68256
|
+
}
|
|
68257
|
+
f.keyword(keyword).append(SPACE);
|
|
67427
68258
|
this.fmtBraces(f);
|
|
67428
68259
|
}
|
|
67429
68260
|
// REQ-355 — Filtered imports keep the `[@Meta]` condition tight against the path
|
|
@@ -67443,7 +68274,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67443
68274
|
// REQ-258, REQ-260 — Format one element per line with canonical keyword spacing
|
|
67444
68275
|
fmtElement(node, keyword) {
|
|
67445
68276
|
const f = this.getNodeFormatter(node);
|
|
67446
|
-
|
|
68277
|
+
if (!isDocument(node.$container))
|
|
68278
|
+
f.keyword(keyword).prepend(NL);
|
|
68279
|
+
f.keyword(keyword).append(SPACE);
|
|
67447
68280
|
if (node.isDef) {
|
|
67448
68281
|
f.keyword("def").prepend(SPACE).append(SPACE);
|
|
67449
68282
|
}
|
|
@@ -67457,7 +68290,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67457
68290
|
// REQ-260 — Keep multi-word `verification case` keyword phrase together
|
|
67458
68291
|
fmtVerificationCase(node) {
|
|
67459
68292
|
const f = this.getNodeFormatter(node);
|
|
67460
|
-
|
|
68293
|
+
if (!isDocument(node.$container))
|
|
68294
|
+
f.keyword("verification").prepend(NL);
|
|
68295
|
+
f.keyword("verification").append(SPACE);
|
|
67461
68296
|
f.keyword("case").append(SPACE);
|
|
67462
68297
|
if (node.isDef)
|
|
67463
68298
|
f.keyword("def").append(SPACE);
|
|
@@ -67468,7 +68303,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67468
68303
|
// REQ-260 — Keep multi-word `analysis case` keyword phrase together
|
|
67469
68304
|
fmtAnalysisCase(node) {
|
|
67470
68305
|
const f = this.getNodeFormatter(node);
|
|
67471
|
-
|
|
68306
|
+
if (!isDocument(node.$container))
|
|
68307
|
+
f.keyword("analysis").prepend(NL);
|
|
68308
|
+
f.keyword("analysis").append(SPACE);
|
|
67472
68309
|
f.keyword("case").append(SPACE);
|
|
67473
68310
|
if (node.isDef)
|
|
67474
68311
|
f.keyword("def").append(SPACE);
|
|
@@ -67479,7 +68316,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67479
68316
|
// REQ-260 — Keep multi-word `use case` keyword phrase together
|
|
67480
68317
|
fmtUseCase(node) {
|
|
67481
68318
|
const f = this.getNodeFormatter(node);
|
|
67482
|
-
|
|
68319
|
+
if (!isDocument(node.$container))
|
|
68320
|
+
f.keyword("use").prepend(NL);
|
|
68321
|
+
f.keyword("use").append(SPACE);
|
|
67483
68322
|
f.keyword("case").append(SPACE);
|
|
67484
68323
|
if (node.isDef)
|
|
67485
68324
|
f.keyword("def").append(SPACE);
|
|
@@ -67668,14 +68507,87 @@ function typeAnchor(node, refText) {
|
|
|
67668
68507
|
}
|
|
67669
68508
|
return void 0;
|
|
67670
68509
|
}
|
|
68510
|
+
var EXPLICIT_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
|
|
68511
|
+
":>",
|
|
68512
|
+
":>>",
|
|
68513
|
+
"::>",
|
|
68514
|
+
"=>",
|
|
68515
|
+
"specializes",
|
|
68516
|
+
"subsets",
|
|
68517
|
+
"redefines",
|
|
68518
|
+
"references",
|
|
68519
|
+
"crosses",
|
|
68520
|
+
"conjugates"
|
|
68521
|
+
]);
|
|
68522
|
+
var REDEFINITION_KINDS2 = /* @__PURE__ */ new Set([":>>", "redefines"]);
|
|
68523
|
+
var IMPLICIT_BASES = Object.freeze({
|
|
68524
|
+
PartDecl: { def: "Parts::Part", usage: "Parts::parts" },
|
|
68525
|
+
ItemDecl: { def: "Items::Item", usage: "Items::items" },
|
|
68526
|
+
PortDecl: { def: "Ports::Port", usage: "Ports::ports" },
|
|
68527
|
+
ActionDecl: { def: "Actions::Action", usage: "Actions::actions" },
|
|
68528
|
+
StateDecl: { def: "States::StateAction", usage: "States::stateActions" },
|
|
68529
|
+
ConnectionDecl: { def: "Connections::Connection", usage: "Connections::connections" },
|
|
68530
|
+
InterfaceDecl: { def: "Interfaces::Interface", usage: "Interfaces::interfaces" },
|
|
68531
|
+
CaseDecl: { def: "Cases::Case", usage: "Cases::cases" },
|
|
68532
|
+
UseCaseDecl: { def: "UseCases::UseCase", usage: "UseCases::useCases" },
|
|
68533
|
+
AnalysisCaseDecl: { def: "AnalysisCases::AnalysisCase", usage: "AnalysisCases::analysisCases" },
|
|
68534
|
+
VerificationCaseDecl: { def: "VerificationCases::VerificationCase", usage: "VerificationCases::verificationCases" },
|
|
68535
|
+
ViewDecl: { def: "Views::View", usage: "Views::views" },
|
|
68536
|
+
ViewpointDecl: { def: "Views::ViewpointCheck", usage: "Views::viewpointChecks" },
|
|
68537
|
+
RenderingDecl: { def: "Views::Rendering", usage: "Views::renderings" },
|
|
68538
|
+
RequirementDecl: { def: "Requirements::RequirementCheck", usage: "Requirements::requirementChecks" },
|
|
68539
|
+
ConcernDecl: { def: "Requirements::ConcernCheck", usage: "Requirements::concernChecks" },
|
|
68540
|
+
ConstraintDecl: { def: "Constraints::ConstraintCheck", usage: "Constraints::constraintChecks" },
|
|
68541
|
+
CalcDecl: { def: "Calculations::Calculation", usage: "Calculations::calculations" },
|
|
68542
|
+
AllocationDecl: { def: "Allocations::Allocation", usage: "Allocations::allocations" },
|
|
68543
|
+
AttributeDecl: { def: "Attributes::AttributeValue", usage: "Attributes::attributeValues" },
|
|
68544
|
+
OccurrenceDecl: { def: "Occurrences::Occurrence", usage: "Occurrences::occurrences" },
|
|
68545
|
+
MetadataDecl: { def: "Metadata::MetadataItem", usage: "Metadata::metadataItems" }
|
|
68546
|
+
});
|
|
68547
|
+
function markdown(value) {
|
|
68548
|
+
return { kind: import_vscode_languageserver20.MarkupKind.Markdown, value };
|
|
68549
|
+
}
|
|
68550
|
+
function allRelationships2(node) {
|
|
68551
|
+
return [...node.preRelationships ?? [], ...node.relationships ?? []];
|
|
68552
|
+
}
|
|
68553
|
+
function hasExplicitSpecialization(node) {
|
|
68554
|
+
if (node.typing?.type?.$refText)
|
|
68555
|
+
return true;
|
|
68556
|
+
return allRelationships2(node).some((rel) => rel.kind && EXPLICIT_SPECIALIZATION_KINDS.has(rel.kind));
|
|
68557
|
+
}
|
|
68558
|
+
function keywordLeaf(node, keyword) {
|
|
68559
|
+
const cst = node.$cstNode;
|
|
68560
|
+
if (!cst)
|
|
68561
|
+
return void 0;
|
|
68562
|
+
return cst_utils_exports.flattenCst(cst).find((leaf) => leaf.text === keyword && !leaf.hidden);
|
|
68563
|
+
}
|
|
68564
|
+
function descriptionRange(description) {
|
|
68565
|
+
const segment = description.nameSegment ?? description.selectionSegment;
|
|
68566
|
+
return segment?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
|
|
68567
|
+
}
|
|
67671
68568
|
var SysmlInlayHintProvider = class {
|
|
68569
|
+
services;
|
|
68570
|
+
/**
|
|
68571
|
+
* Resolved implicit bases, keyed by the qualified name in
|
|
68572
|
+
* {@link IMPLICIT_BASES}. The standard library does not change once it is
|
|
68573
|
+
* loaded, so this is built at most once for real; while it is still empty —
|
|
68574
|
+
* before the library has been indexed — the rebuild is over a tiny index and
|
|
68575
|
+
* costs nothing, which is what keeps the hint path free of a full index scan
|
|
68576
|
+
* on every request.
|
|
68577
|
+
*/
|
|
68578
|
+
baseCache = /* @__PURE__ */ new Map();
|
|
68579
|
+
constructor(services) {
|
|
68580
|
+
this.services = services;
|
|
68581
|
+
}
|
|
67672
68582
|
getInlayHints(document, _params) {
|
|
67673
68583
|
const root3 = document.parseResult?.value;
|
|
67674
|
-
if (!root3)
|
|
68584
|
+
if (!root3 || !sysmlInlayHintSettings.enabled)
|
|
67675
68585
|
return void 0;
|
|
68586
|
+
const settings = sysmlInlayHintSettings;
|
|
67676
68587
|
const hints = [];
|
|
67677
68588
|
for (const node of allAstNodes(root3)) {
|
|
67678
|
-
|
|
68589
|
+
const decl = node;
|
|
68590
|
+
if (settings.multiplicity && (isPartDecl(node) || isPortDecl(node) || isAttributeDecl(node)) && !decl.isDef && !decl.multiplicity && node.$cstNode && decl.name) {
|
|
67679
68591
|
hints.push({
|
|
67680
68592
|
position: multiplicityAnchor(node.$cstNode),
|
|
67681
68593
|
label: " [1]",
|
|
@@ -67683,7 +68595,65 @@ var SysmlInlayHintProvider = class {
|
|
|
67683
68595
|
paddingLeft: true
|
|
67684
68596
|
});
|
|
67685
68597
|
}
|
|
67686
|
-
if (!
|
|
68598
|
+
if (settings.visibility && isImport(node) && !decl.visibility) {
|
|
68599
|
+
const keyword = keywordLeaf(node, "import");
|
|
68600
|
+
if (keyword) {
|
|
68601
|
+
const at = keyword.range.start;
|
|
68602
|
+
hints.push({
|
|
68603
|
+
position: at,
|
|
68604
|
+
label: "private",
|
|
68605
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
68606
|
+
paddingRight: true,
|
|
68607
|
+
tooltip: markdown("An `import` that declares no visibility is **private**: the names it brings in are not re-exported from this namespace."),
|
|
68608
|
+
// Writing the modifier is mechanically safe — `private`
|
|
68609
|
+
// is exactly what the grammar accepts here, and the text
|
|
68610
|
+
// then says what it already meant.
|
|
68611
|
+
textEdits: [{ range: { start: at, end: at }, newText: "private " }]
|
|
68612
|
+
});
|
|
68613
|
+
}
|
|
68614
|
+
}
|
|
68615
|
+
if (settings.modifiers && isAttributeDecl(node) && !decl.isDef && !(decl.modifiers ?? []).includes("ref")) {
|
|
68616
|
+
const keyword = keywordLeaf(node, "attribute");
|
|
68617
|
+
if (keyword) {
|
|
68618
|
+
const at = keyword.range.start;
|
|
68619
|
+
hints.push({
|
|
68620
|
+
position: at,
|
|
68621
|
+
label: "ref",
|
|
68622
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
68623
|
+
paddingRight: true,
|
|
68624
|
+
tooltip: markdown("An attribute usage is always referential (`ref`); it is never a composite feature of its owner."),
|
|
68625
|
+
textEdits: [{ range: { start: at, end: at }, newText: "ref " }]
|
|
68626
|
+
});
|
|
68627
|
+
}
|
|
68628
|
+
}
|
|
68629
|
+
if (settings.specialization && node.$cstNode && decl.name && !hasExplicitSpecialization(decl)) {
|
|
68630
|
+
const entry = IMPLICIT_BASES[node.$type];
|
|
68631
|
+
const qualified = entry ? decl.isDef ? entry.def : entry.usage : void 0;
|
|
68632
|
+
const description = qualified ? this.resolveImplicitBase(qualified, decl.isDef !== true) : void 0;
|
|
68633
|
+
if (qualified && description) {
|
|
68634
|
+
hints.push({
|
|
68635
|
+
position: multiplicityAnchor(node.$cstNode),
|
|
68636
|
+
label: [{
|
|
68637
|
+
value: ` :> ${qualified}`,
|
|
68638
|
+
location: {
|
|
68639
|
+
uri: description.documentUri.toString(),
|
|
68640
|
+
range: descriptionRange(description)
|
|
68641
|
+
}
|
|
68642
|
+
}],
|
|
68643
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
68644
|
+
paddingLeft: true,
|
|
68645
|
+
tooltip: markdown(`Implicitly specializes \`${qualified}\` from the standard library.`)
|
|
68646
|
+
});
|
|
68647
|
+
}
|
|
68648
|
+
}
|
|
68649
|
+
if (settings.redefinition)
|
|
68650
|
+
hints.push(...this.parameterRedefinitionHints(decl));
|
|
68651
|
+
if (settings.effectiveNames) {
|
|
68652
|
+
const effective = effectiveNameHint(decl);
|
|
68653
|
+
if (effective)
|
|
68654
|
+
hints.push(effective);
|
|
68655
|
+
}
|
|
68656
|
+
if (!settings.dimension)
|
|
67687
68657
|
continue;
|
|
67688
68658
|
if (isNumericPrimary(node)) {
|
|
67689
68659
|
const symbol = node.unit?.$refText;
|
|
@@ -67699,7 +68669,7 @@ var SysmlInlayHintProvider = class {
|
|
|
67699
68669
|
continue;
|
|
67700
68670
|
}
|
|
67701
68671
|
if (isAttributeDecl(node)) {
|
|
67702
|
-
const refText =
|
|
68672
|
+
const refText = decl.typing?.type?.$refText;
|
|
67703
68673
|
const label = refText ? quantityTypeDimensionLabel(refText) : void 0;
|
|
67704
68674
|
const position = refText ? typeAnchor(node, refText) : void 0;
|
|
67705
68675
|
if (label && position) {
|
|
@@ -67714,7 +68684,102 @@ var SysmlInlayHintProvider = class {
|
|
|
67714
68684
|
}
|
|
67715
68685
|
return hints;
|
|
67716
68686
|
}
|
|
68687
|
+
/**
|
|
68688
|
+
* REQ-395 — Positional parameter correspondence.
|
|
68689
|
+
*
|
|
68690
|
+
* A usage typed by a definition redefines that definition's directed
|
|
68691
|
+
* parameters by position ([OMG SysML v2 Part 1 §7.16.4] — the same rule
|
|
68692
|
+
* `SSM025` checks). The definition has to be RESOLVED for the hint to exist,
|
|
68693
|
+
* and a parameter that already redefines something explicitly is left alone.
|
|
68694
|
+
*/
|
|
68695
|
+
parameterRedefinitionHints(node) {
|
|
68696
|
+
const definition = node.typing?.type?.ref;
|
|
68697
|
+
if (!definition || node.isDef)
|
|
68698
|
+
return [];
|
|
68699
|
+
const mine = directedParameters(node);
|
|
68700
|
+
if (mine.length === 0)
|
|
68701
|
+
return [];
|
|
68702
|
+
const theirs = directedParameters(definition);
|
|
68703
|
+
const hints = [];
|
|
68704
|
+
for (let index = 0; index < mine.length && index < theirs.length; index++) {
|
|
68705
|
+
const parameter = mine[index].node;
|
|
68706
|
+
if (!parameter.name || !parameter.$cstNode)
|
|
68707
|
+
continue;
|
|
68708
|
+
if (allRelationships2(parameter).some((rel) => rel.kind && REDEFINITION_KINDS2.has(rel.kind)))
|
|
68709
|
+
continue;
|
|
68710
|
+
const target = theirs[index].node;
|
|
68711
|
+
if (!target.name || target.name === parameter.name)
|
|
68712
|
+
continue;
|
|
68713
|
+
const uri = ast_utils_exports.getDocument(target).uri.toString();
|
|
68714
|
+
const range = target.$cstNode?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
|
|
68715
|
+
hints.push({
|
|
68716
|
+
position: multiplicityAnchor(parameter.$cstNode),
|
|
68717
|
+
label: [{ value: ` :>> ${target.name}`, location: { uri, range } }],
|
|
68718
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
68719
|
+
paddingLeft: true,
|
|
68720
|
+
tooltip: markdown(`Parameter ${index + 1} implicitly redefines \`${target.name}\` of \`${node.typing?.type?.$refText ?? "its definition"}\`.`)
|
|
68721
|
+
});
|
|
68722
|
+
}
|
|
68723
|
+
return hints;
|
|
68724
|
+
}
|
|
68725
|
+
/**
|
|
68726
|
+
* REQ-395 — Find one implicit base in the index.
|
|
68727
|
+
*
|
|
68728
|
+
* A description whose name is the full qualified spelling wins outright. The
|
|
68729
|
+
* index otherwise keys library symbols by their simple name, so the fallback
|
|
68730
|
+
* requires BOTH a standard-library document and the right side of the
|
|
68731
|
+
* definition/usage split, which the precomputed index records — that is what
|
|
68732
|
+
* keeps `Parts::Part` from matching a `part Part` somewhere else.
|
|
68733
|
+
*/
|
|
68734
|
+
resolveImplicitBase(qualified, wantUsage) {
|
|
68735
|
+
const cached = this.baseCache.get(qualified);
|
|
68736
|
+
if (cached)
|
|
68737
|
+
return cached;
|
|
68738
|
+
const index = this.services?.shared.workspace.IndexManager;
|
|
68739
|
+
if (!index)
|
|
68740
|
+
return void 0;
|
|
68741
|
+
const simple = lastSegment3(qualified);
|
|
68742
|
+
let fallback;
|
|
68743
|
+
for (const description of index.allElements()) {
|
|
68744
|
+
if (description.name === qualified) {
|
|
68745
|
+
this.baseCache.set(qualified, description);
|
|
68746
|
+
return description;
|
|
68747
|
+
}
|
|
68748
|
+
if (fallback || description.name !== simple)
|
|
68749
|
+
continue;
|
|
68750
|
+
const uri = description.documentUri instanceof URI2 ? description.documentUri : URI2.parse(String(description.documentUri));
|
|
68751
|
+
if (!isStandardLibraryUri(uri))
|
|
68752
|
+
continue;
|
|
68753
|
+
if (description.isUsage === true !== wantUsage)
|
|
68754
|
+
continue;
|
|
68755
|
+
fallback = description;
|
|
68756
|
+
}
|
|
68757
|
+
if (fallback)
|
|
68758
|
+
this.baseCache.set(qualified, fallback);
|
|
68759
|
+
return fallback;
|
|
68760
|
+
}
|
|
67717
68761
|
};
|
|
68762
|
+
function effectiveNameHint(node) {
|
|
68763
|
+
if (node.name || node.shortName?.name || !node.$cstNode)
|
|
68764
|
+
return void 0;
|
|
68765
|
+
const redefinition = allRelationships2(node).find((rel) => rel.kind && REDEFINITION_KINDS2.has(rel.kind) && (rel.targets?.length ?? 0) > 0);
|
|
68766
|
+
const target = redefinition?.targets?.[0];
|
|
68767
|
+
if (!target)
|
|
68768
|
+
return void 0;
|
|
68769
|
+
const effective = lastSegment3(target);
|
|
68770
|
+
if (!effective)
|
|
68771
|
+
return void 0;
|
|
68772
|
+
const operator = cst_utils_exports.flattenCst(node.$cstNode).find((leaf) => !leaf.hidden && redefinition?.kind !== void 0 && leaf.text === redefinition.kind);
|
|
68773
|
+
if (!operator)
|
|
68774
|
+
return void 0;
|
|
68775
|
+
return {
|
|
68776
|
+
position: operator.range.start,
|
|
68777
|
+
label: effective,
|
|
68778
|
+
kind: import_vscode_languageserver20.InlayHintKind.Parameter,
|
|
68779
|
+
paddingRight: true,
|
|
68780
|
+
tooltip: markdown(`Effective name: \`${effective}\`, taken from the feature this one redefines.`)
|
|
68781
|
+
};
|
|
68782
|
+
}
|
|
67718
68783
|
|
|
67719
68784
|
// ../language-server/out/src/services/requirement-eval.js
|
|
67720
68785
|
var UNRESOLVED = { kind: "unresolved" };
|
|
@@ -68923,9 +69988,13 @@ var SysmlLspModule = {
|
|
|
68923
69988
|
// Epic G
|
|
68924
69989
|
RenameProvider: (services) => new SysmlRenameProvider(services),
|
|
68925
69990
|
CodeActionProvider: (services) => new SysmlCodeActionProvider(services),
|
|
68926
|
-
|
|
69991
|
+
// REQ-393 — the formatter reads the grammar's keyword set for the
|
|
69992
|
+
// quoted-name decision, so it is built with services.
|
|
69993
|
+
Formatter: (services) => new SysmlFormatter(services),
|
|
68927
69994
|
SemanticTokenProvider: (services) => new SysmlSemanticTokenProvider(services),
|
|
68928
|
-
|
|
69995
|
+
// REQ-395 — the provider resolves implicit standard-library bases
|
|
69996
|
+
// through the workspace/library index, so it is built with services.
|
|
69997
|
+
InlayHintProvider: (services) => new SysmlInlayHintProvider(services),
|
|
68929
69998
|
CodeLensProvider: (services) => new SysmlCodeLensProvider(services),
|
|
68930
69999
|
DocumentHighlightProvider: (services) => new SysmlDocumentHighlightProvider(services),
|
|
68931
70000
|
DefinitionProvider: (services) => new SysmlDefinitionProvider(services),
|
|
@@ -70024,7 +71093,7 @@ async function runValidation(command) {
|
|
|
70024
71093
|
}
|
|
70025
71094
|
|
|
70026
71095
|
// src/main.ts
|
|
70027
|
-
var VERSION2 = true ? "0.
|
|
71096
|
+
var VERSION2 = true ? "0.16.0" : "dev";
|
|
70028
71097
|
async function main(argv) {
|
|
70029
71098
|
const command = parseArgs(argv);
|
|
70030
71099
|
if (command.kind === "help") {
|