sysml-validate 0.15.8 → 0.17.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 +1544 -92
- 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;
|
|
@@ -58642,6 +58649,18 @@ var DIAGNOSTIC_MESSAGES = {
|
|
|
58642
58649
|
// `first a then b;` and the `then` occurrence shorthand in a part), so the
|
|
58643
58650
|
// message names the construct it means rather than "action language".
|
|
58644
58651
|
SEM011_ACTION_ONLY_CONSTRUCT: (construct, clause, owner) => `${construct} may only be declared in the body of an action definition or usage (OMG SysML v2 Part 1 \xA7${clause}), not in ${owner}.`,
|
|
58652
|
+
// REQ-392 — issue #150. A `rep … language "sysml"` body is a legal representation
|
|
58653
|
+
// of the element it annotates (OMG KerML §7.4.3), so the two faults are "this is
|
|
58654
|
+
// not SysML" and "this is not THIS element". The parser's own wording is carried
|
|
58655
|
+
// through verbatim, because it names the token the reader has to go and fix.
|
|
58656
|
+
SYN076_EMBEDDED_REPRESENTATION_SYNTAX: (language, detail) => `This representation declares language "${language}", so its body must be valid ${language}: ${detail}`,
|
|
58657
|
+
SEM012_REPRESENTATION_WRONG_KIND: (expected, actual) => `A textual representation must represent the element it annotates. This body represents '${actual}', but it annotates '${expected}'.`,
|
|
58658
|
+
// REQ-392 — The body is resolved in the represented element's own context, so an
|
|
58659
|
+
// unresolvable name there is the same defect it would be in ordinary code. It is
|
|
58660
|
+
// reported under its own code so a team can tune representations separately.
|
|
58661
|
+
RES020_REPRESENTATION_UNRESOLVED: (name, language) => `'${name}' cannot be resolved from here, so this ${language} representation is not a valid representation of the element it annotates.`,
|
|
58662
|
+
RES020_REPRESENTATION_PATH_SEGMENT: (segment, owner, language) => `Feature path segment '${segment}' does not exist on '${owner}', so this ${language} representation is not a valid representation of the element it annotates.`,
|
|
58663
|
+
SEM012_REPRESENTATION_WRONG_NAME: (expected, actual) => `A textual representation must represent the element it annotates. This body represents '${actual}', but it annotates '${expected}'.`,
|
|
58645
58664
|
RES016_LIBRARY_SHADOWING: (name) => `'${name}' shadows a standard-library element of the same name; the library symbol is hidden in this scope.`,
|
|
58646
58665
|
// issue #183 — KerML namespace distinguishability (validateNamespaceDistinguishability).
|
|
58647
58666
|
// Owned siblings sharing a name/short name (or an alias clashing with an owned
|
|
@@ -58685,7 +58704,16 @@ var DIAGNOSTIC_MESSAGES = {
|
|
|
58685
58704
|
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
58705
|
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
58706
|
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
|
|
58707
|
+
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.`,
|
|
58708
|
+
// REQ-392 — a `sysml-format` comment the formatter cannot act on. Advisory:
|
|
58709
|
+
// the directive is inert, and saying so beats leaving the author to wonder
|
|
58710
|
+
// why their layout was reformatted anyway.
|
|
58711
|
+
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.",
|
|
58712
|
+
// REQ-345, REQ-393 — the formatter-settings refusal is shown to the user
|
|
58713
|
+
// through `window/showWarningMessage`, so its text lives here like every
|
|
58714
|
+
// other reviewable string rather than inline at the call site.
|
|
58715
|
+
FORMAT_SETTINGS_REFUSED: (detail) => `SysML formatter settings ignored (${detail}). The previous value is still in use.`,
|
|
58716
|
+
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
58717
|
};
|
|
58690
58718
|
var KEYWORD_TOOLTIPS = {
|
|
58691
58719
|
// Package / namespace
|
|
@@ -58882,6 +58910,107 @@ var OPERATOR_TOOLTIPS = {
|
|
|
58882
58910
|
"@@": { 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
58911
|
};
|
|
58884
58912
|
|
|
58913
|
+
// ../language-server/out/src/services/format-directives.js
|
|
58914
|
+
var DIRECTIVE_WORD = "sysml-format";
|
|
58915
|
+
var IGNORE_ARGUMENT = "ignore";
|
|
58916
|
+
var EMPTY_SCAN = { directives: [], protectedRanges: [] };
|
|
58917
|
+
function commentBody(text) {
|
|
58918
|
+
if (text.startsWith("//*") && text.endsWith("*/") && text.length >= 5)
|
|
58919
|
+
return text.slice(3, -2);
|
|
58920
|
+
if (text.startsWith("//"))
|
|
58921
|
+
return text.slice(2);
|
|
58922
|
+
if (text.startsWith("/*") && text.endsWith("*/") && text.length >= 4)
|
|
58923
|
+
return text.slice(2, -2);
|
|
58924
|
+
return void 0;
|
|
58925
|
+
}
|
|
58926
|
+
function classify(text) {
|
|
58927
|
+
const body = commentBody(text);
|
|
58928
|
+
if (body === void 0)
|
|
58929
|
+
return void 0;
|
|
58930
|
+
const trimmed = body.trim();
|
|
58931
|
+
if (trimmed !== DIRECTIVE_WORD && !trimmed.startsWith(`${DIRECTIVE_WORD} `) && !trimmed.startsWith(`${DIRECTIVE_WORD} `)) {
|
|
58932
|
+
return void 0;
|
|
58933
|
+
}
|
|
58934
|
+
const argument = trimmed.slice(DIRECTIVE_WORD.length).trim();
|
|
58935
|
+
return { kind: argument === IGNORE_ARGUMENT ? "ignore" : "malformed", argument };
|
|
58936
|
+
}
|
|
58937
|
+
function looksLikeFormatDirective(text) {
|
|
58938
|
+
return text.includes(DIRECTIVE_WORD);
|
|
58939
|
+
}
|
|
58940
|
+
function nodesByStartOffset(root3) {
|
|
58941
|
+
const byOffset = /* @__PURE__ */ new Map();
|
|
58942
|
+
for (const node of ast_utils_exports.streamAllContents(root3)) {
|
|
58943
|
+
const cst = node.$cstNode;
|
|
58944
|
+
if (!cst)
|
|
58945
|
+
continue;
|
|
58946
|
+
const existing = byOffset.get(cst.offset);
|
|
58947
|
+
if (!existing || (existing.$cstNode?.end ?? 0) < cst.end)
|
|
58948
|
+
byOffset.set(cst.offset, node);
|
|
58949
|
+
}
|
|
58950
|
+
return byOffset;
|
|
58951
|
+
}
|
|
58952
|
+
function mergeSlices(slices) {
|
|
58953
|
+
const sorted = [...slices].sort((a2, b) => a2.offset - b.offset || a2.end - b.end);
|
|
58954
|
+
const merged = [];
|
|
58955
|
+
for (const slice of sorted) {
|
|
58956
|
+
const last2 = merged.at(-1);
|
|
58957
|
+
if (last2 && slice.offset <= last2.end) {
|
|
58958
|
+
last2.end = Math.max(last2.end, slice.end);
|
|
58959
|
+
} else {
|
|
58960
|
+
merged.push({ ...slice });
|
|
58961
|
+
}
|
|
58962
|
+
}
|
|
58963
|
+
return merged;
|
|
58964
|
+
}
|
|
58965
|
+
function scanFormatDirectives(document) {
|
|
58966
|
+
const root3 = document.parseResult?.value;
|
|
58967
|
+
const rootCst = root3?.$cstNode;
|
|
58968
|
+
if (!root3 || !rootCst)
|
|
58969
|
+
return EMPTY_SCAN;
|
|
58970
|
+
const text = document.textDocument.getText();
|
|
58971
|
+
if (!looksLikeFormatDirective(text))
|
|
58972
|
+
return EMPTY_SCAN;
|
|
58973
|
+
const leaves = cst_utils_exports.flattenCst(rootCst).toArray();
|
|
58974
|
+
const directives = [];
|
|
58975
|
+
const protectedRanges = [];
|
|
58976
|
+
let byOffset;
|
|
58977
|
+
for (let index = 0; index < leaves.length; index++) {
|
|
58978
|
+
const leaf = leaves[index];
|
|
58979
|
+
if (!leaf.hidden)
|
|
58980
|
+
continue;
|
|
58981
|
+
const classified = classify(leaf.text);
|
|
58982
|
+
if (!classified)
|
|
58983
|
+
continue;
|
|
58984
|
+
const comment = { offset: leaf.offset, end: leaf.end };
|
|
58985
|
+
if (classified.kind === "malformed") {
|
|
58986
|
+
directives.push({ kind: "malformed", comment, argument: classified.argument });
|
|
58987
|
+
continue;
|
|
58988
|
+
}
|
|
58989
|
+
let next;
|
|
58990
|
+
for (let scan = index + 1; scan < leaves.length; scan++) {
|
|
58991
|
+
if (!leaves[scan].hidden) {
|
|
58992
|
+
next = leaves[scan];
|
|
58993
|
+
break;
|
|
58994
|
+
}
|
|
58995
|
+
}
|
|
58996
|
+
byOffset ??= nodesByStartOffset(root3);
|
|
58997
|
+
const target = next ? byOffset.get(next.offset)?.$cstNode : void 0;
|
|
58998
|
+
if (!target) {
|
|
58999
|
+
directives.push({ kind: "dangling", comment, argument: classified.argument });
|
|
59000
|
+
continue;
|
|
59001
|
+
}
|
|
59002
|
+
const slice = { offset: target.offset, end: target.end };
|
|
59003
|
+
directives.push({ kind: "ignore", comment, argument: classified.argument, target: slice });
|
|
59004
|
+
protectedRanges.push(slice);
|
|
59005
|
+
}
|
|
59006
|
+
if (directives.length === 0)
|
|
59007
|
+
return EMPTY_SCAN;
|
|
59008
|
+
return { directives, protectedRanges: mergeSlices(protectedRanges) };
|
|
59009
|
+
}
|
|
59010
|
+
function intersectsProtected(ranges, offset, end) {
|
|
59011
|
+
return ranges.some((slice) => offset < slice.end && end > slice.offset);
|
|
59012
|
+
}
|
|
59013
|
+
|
|
58885
59014
|
// ../language-server/out/src/services/v1-guardrail.js
|
|
58886
59015
|
var V1_KEYWORD_MAP = {
|
|
58887
59016
|
block: { v2: "part def", note: "SysML v2 replaces the v1 'Block' with 'part def'." },
|
|
@@ -59479,6 +59608,274 @@ function isNamespaceOnlyDecl(node) {
|
|
|
59479
59608
|
return node.isDef === true;
|
|
59480
59609
|
}
|
|
59481
59610
|
|
|
59611
|
+
// ../language-server/out/src/services/comment-body.js
|
|
59612
|
+
function sourceOf(node) {
|
|
59613
|
+
return node.$cstNode?.root.fullText;
|
|
59614
|
+
}
|
|
59615
|
+
function cleanBlockBodyLines(raw, rawOffset) {
|
|
59616
|
+
let cut = raw.length - raw.trimStart().length;
|
|
59617
|
+
let body = raw.slice(cut).trimEnd();
|
|
59618
|
+
if (body.startsWith("/*")) {
|
|
59619
|
+
body = body.slice(2);
|
|
59620
|
+
cut += 2;
|
|
59621
|
+
}
|
|
59622
|
+
if (body.endsWith("*/"))
|
|
59623
|
+
body = body.slice(0, -2);
|
|
59624
|
+
const lines = [];
|
|
59625
|
+
let cursor = cut;
|
|
59626
|
+
for (const rawLine of body.split("\n")) {
|
|
59627
|
+
const gutter = /^\s*\*+ ?/u.exec(rawLine);
|
|
59628
|
+
const skip = gutter ? gutter[0].length : 0;
|
|
59629
|
+
lines.push({
|
|
59630
|
+
text: rawLine.slice(skip).replace(/\s+$/u, ""),
|
|
59631
|
+
offset: rawOffset + cursor + skip
|
|
59632
|
+
});
|
|
59633
|
+
cursor += rawLine.length + 1;
|
|
59634
|
+
}
|
|
59635
|
+
while (lines.length > 0 && lines[0].text.trim() === "")
|
|
59636
|
+
lines.shift();
|
|
59637
|
+
while (lines.length > 0 && lines[lines.length - 1].text.trim() === "")
|
|
59638
|
+
lines.pop();
|
|
59639
|
+
if (lines.length > 0) {
|
|
59640
|
+
const lead = lines[0].text.length - lines[0].text.trimStart().length;
|
|
59641
|
+
if (lead > 0)
|
|
59642
|
+
lines[0] = { text: lines[0].text.slice(lead), offset: lines[0].offset + lead };
|
|
59643
|
+
}
|
|
59644
|
+
return lines;
|
|
59645
|
+
}
|
|
59646
|
+
function cleanBlockBody(inner) {
|
|
59647
|
+
return cleanBlockBodyLines(inner, 0).map((line) => line.text).join("\n");
|
|
59648
|
+
}
|
|
59649
|
+
function docCommentBody(node) {
|
|
59650
|
+
const raw = node.doc;
|
|
59651
|
+
if (typeof raw !== "string")
|
|
59652
|
+
return void 0;
|
|
59653
|
+
const start = raw.indexOf("/*");
|
|
59654
|
+
const end = raw.lastIndexOf("*/");
|
|
59655
|
+
if (start < 0 || end < start)
|
|
59656
|
+
return void 0;
|
|
59657
|
+
const body = cleanBlockBody(raw.slice(start, end + 2));
|
|
59658
|
+
return body.length > 0 ? body : void 0;
|
|
59659
|
+
}
|
|
59660
|
+
function trailingBlockBodySpan(node) {
|
|
59661
|
+
const cst = node.$cstNode;
|
|
59662
|
+
const text = sourceOf(node);
|
|
59663
|
+
if (!cst || text === void 0)
|
|
59664
|
+
return void 0;
|
|
59665
|
+
const rest = text.slice(cst.end);
|
|
59666
|
+
const open = rest.indexOf("/*");
|
|
59667
|
+
if (open < 0)
|
|
59668
|
+
return void 0;
|
|
59669
|
+
if (rest.slice(0, open).trim() !== "")
|
|
59670
|
+
return void 0;
|
|
59671
|
+
const close = rest.indexOf("*/", open + 2);
|
|
59672
|
+
if (close < 0)
|
|
59673
|
+
return void 0;
|
|
59674
|
+
const raw = rest.slice(open, close + 2);
|
|
59675
|
+
const lines = cleanBlockBodyLines(raw, cst.end + open);
|
|
59676
|
+
const body = lines.map((line) => line.text).join("\n");
|
|
59677
|
+
if (body.length === 0)
|
|
59678
|
+
return void 0;
|
|
59679
|
+
return { text: body, lines, open: cst.end + open, close: cst.end + close + 2 };
|
|
59680
|
+
}
|
|
59681
|
+
function trailingBlockBody(node) {
|
|
59682
|
+
return trailingBlockBodySpan(node)?.text;
|
|
59683
|
+
}
|
|
59684
|
+
function bodyOffsetAt(span, line, character) {
|
|
59685
|
+
const entry = Number.isInteger(line) ? span.lines[line] : void 0;
|
|
59686
|
+
if (!entry)
|
|
59687
|
+
return span.open;
|
|
59688
|
+
const column = Number.isFinite(character) ? character : 0;
|
|
59689
|
+
return entry.offset + Math.max(0, Math.min(column, entry.text.length));
|
|
59690
|
+
}
|
|
59691
|
+
|
|
59692
|
+
// ../language-server/out/src/services/embedded-representation.js
|
|
59693
|
+
var EMBEDDED_LANGUAGES = /* @__PURE__ */ new Set(["sysml", "kerml"]);
|
|
59694
|
+
var EXPRESSION_VALUED_OWNERS = /* @__PURE__ */ new Set([
|
|
59695
|
+
"AssertConstraintStmt",
|
|
59696
|
+
"AssumeConstraintStmt",
|
|
59697
|
+
"RequireConstraintStmt",
|
|
59698
|
+
"ConstraintDecl",
|
|
59699
|
+
"InvShorthand",
|
|
59700
|
+
"CalcDecl",
|
|
59701
|
+
"ExpressionDecl"
|
|
59702
|
+
]);
|
|
59703
|
+
function isEmbeddedLanguage(raw) {
|
|
59704
|
+
if (typeof raw !== "string")
|
|
59705
|
+
return false;
|
|
59706
|
+
return EMBEDDED_LANGUAGES.has(raw.replace(/^"|"$/gu, "").trim().toLowerCase());
|
|
59707
|
+
}
|
|
59708
|
+
function bareName(node) {
|
|
59709
|
+
const name = node?.name;
|
|
59710
|
+
return typeof name === "string" && name.length > 0 ? name.replace(/^'|'$/gu, "") : void 0;
|
|
59711
|
+
}
|
|
59712
|
+
function kindOf(node) {
|
|
59713
|
+
const decl = /^([A-Z][a-z]+)Decl$/.exec(node.$type);
|
|
59714
|
+
if (decl) {
|
|
59715
|
+
const base = decl[1].toLowerCase();
|
|
59716
|
+
return node.isDef ? `${base} def` : base;
|
|
59717
|
+
}
|
|
59718
|
+
switch (node.$type) {
|
|
59719
|
+
case "Package":
|
|
59720
|
+
return "package";
|
|
59721
|
+
case "InvShorthand":
|
|
59722
|
+
return "inv";
|
|
59723
|
+
case "AssertConstraintStmt":
|
|
59724
|
+
return "assert constraint";
|
|
59725
|
+
case "AssumeConstraintStmt":
|
|
59726
|
+
return "assume constraint";
|
|
59727
|
+
case "RequireConstraintStmt":
|
|
59728
|
+
return "require constraint";
|
|
59729
|
+
default:
|
|
59730
|
+
return node.$type;
|
|
59731
|
+
}
|
|
59732
|
+
}
|
|
59733
|
+
function hasErrors(result) {
|
|
59734
|
+
return result.lexerErrors.length > 0 || result.parserErrors.length > 0;
|
|
59735
|
+
}
|
|
59736
|
+
function describeParserError(error) {
|
|
59737
|
+
const image = error.token?.image ?? "";
|
|
59738
|
+
const expecting = /Expecting token of type '([^']+)'/u.exec(error.message ?? "");
|
|
59739
|
+
if (image === "") {
|
|
59740
|
+
return expecting ? `the body ends while '${expecting[1]}' is still expected.` : "the body ends before it is complete.";
|
|
59741
|
+
}
|
|
59742
|
+
if (error.name === "NotAllInputParsedException") {
|
|
59743
|
+
return `'${image}' is left over after the representation is already complete.`;
|
|
59744
|
+
}
|
|
59745
|
+
if (expecting)
|
|
59746
|
+
return `expected '${expecting[1]}' but found '${image}'.`;
|
|
59747
|
+
return `unexpected '${image}'.`;
|
|
59748
|
+
}
|
|
59749
|
+
function describeLexerError(error) {
|
|
59750
|
+
const character = /unexpected character: ->(.*?)<-/u.exec(error.message ?? "");
|
|
59751
|
+
return character ? `unexpected character '${character[1]}'.` : error.message ?? "the body cannot be read.";
|
|
59752
|
+
}
|
|
59753
|
+
function failureOf(result, span) {
|
|
59754
|
+
const endLine = Math.max(0, span.lines.length - 1);
|
|
59755
|
+
const end = { line: endLine, character: span.lines[endLine]?.text.length ?? 0 };
|
|
59756
|
+
const lexical = result.lexerErrors[0];
|
|
59757
|
+
const syntactic = result.parserErrors[0];
|
|
59758
|
+
if (lexical && Number.isFinite(lexical.line)) {
|
|
59759
|
+
return {
|
|
59760
|
+
line: lexical.line - 1,
|
|
59761
|
+
character: Math.max(0, (Number.isFinite(lexical.column) ? lexical.column : 1) - 1),
|
|
59762
|
+
message: describeLexerError(lexical)
|
|
59763
|
+
};
|
|
59764
|
+
}
|
|
59765
|
+
if (syntactic) {
|
|
59766
|
+
const token = syntactic.token;
|
|
59767
|
+
const message = describeParserError(syntactic);
|
|
59768
|
+
if (Number.isFinite(token?.startLine) && Number.isFinite(token?.startColumn)) {
|
|
59769
|
+
return { line: token.startLine - 1, character: token.startColumn - 1, message };
|
|
59770
|
+
}
|
|
59771
|
+
return { ...end, message };
|
|
59772
|
+
}
|
|
59773
|
+
return { ...end, message: lexical ? describeLexerError(lexical) : "the body is not valid SysML." };
|
|
59774
|
+
}
|
|
59775
|
+
function conformance(parsed, owner) {
|
|
59776
|
+
if (!parsed || !owner || owner.$type === "Document")
|
|
59777
|
+
return { status: "ok" };
|
|
59778
|
+
const sameKind = parsed.$type === owner.$type && Boolean(parsed.isDef) === Boolean(owner.isDef);
|
|
59779
|
+
if (!sameKind) {
|
|
59780
|
+
return { status: "mismatch", mismatch: { reason: "kind", expected: kindOf(owner), actual: kindOf(parsed) } };
|
|
59781
|
+
}
|
|
59782
|
+
const expected = bareName(owner);
|
|
59783
|
+
const actual = bareName(parsed);
|
|
59784
|
+
if (expected && actual && expected !== actual) {
|
|
59785
|
+
return { status: "mismatch", mismatch: { reason: "name", expected, actual } };
|
|
59786
|
+
}
|
|
59787
|
+
return { status: "ok" };
|
|
59788
|
+
}
|
|
59789
|
+
function nodesOf(root3) {
|
|
59790
|
+
return [root3, ...ast_utils_exports.streamAllContents(root3)];
|
|
59791
|
+
}
|
|
59792
|
+
function declaredNames(root3) {
|
|
59793
|
+
const names = /* @__PURE__ */ new Set();
|
|
59794
|
+
for (const node of nodesOf(root3)) {
|
|
59795
|
+
const name = node.name;
|
|
59796
|
+
if (typeof name === "string" && name.length > 0)
|
|
59797
|
+
names.add(name.replace(/^'|'$/gu, ""));
|
|
59798
|
+
}
|
|
59799
|
+
return names;
|
|
59800
|
+
}
|
|
59801
|
+
function graftInto(fragment, owner) {
|
|
59802
|
+
const mutable = fragment;
|
|
59803
|
+
mutable.$container = owner;
|
|
59804
|
+
mutable.$containerProperty = "members";
|
|
59805
|
+
}
|
|
59806
|
+
function firstSegment(refText) {
|
|
59807
|
+
return (refText.split("::")[0] ?? refText).replace(/^'|'$/gu, "").trim();
|
|
59808
|
+
}
|
|
59809
|
+
function unresolvedReferences(fragment, owner) {
|
|
59810
|
+
graftInto(fragment, owner);
|
|
59811
|
+
const own = declaredNames(fragment);
|
|
59812
|
+
const out = [];
|
|
59813
|
+
for (const node of nodesOf(fragment)) {
|
|
59814
|
+
for (const [property3, value] of Object.entries(node)) {
|
|
59815
|
+
if (property3.startsWith("$"))
|
|
59816
|
+
continue;
|
|
59817
|
+
for (const candidate of Array.isArray(value) ? value : [value]) {
|
|
59818
|
+
if (!isReference(candidate))
|
|
59819
|
+
continue;
|
|
59820
|
+
const reference = candidate;
|
|
59821
|
+
const refText = reference.$refText;
|
|
59822
|
+
if (!refText || own.has(firstSegment(refText)))
|
|
59823
|
+
continue;
|
|
59824
|
+
let resolved;
|
|
59825
|
+
try {
|
|
59826
|
+
resolved = reference.ref;
|
|
59827
|
+
} catch {
|
|
59828
|
+
return [];
|
|
59829
|
+
}
|
|
59830
|
+
if (resolved)
|
|
59831
|
+
continue;
|
|
59832
|
+
const range = reference.$refNode?.range;
|
|
59833
|
+
if (!range)
|
|
59834
|
+
continue;
|
|
59835
|
+
out.push({
|
|
59836
|
+
refText,
|
|
59837
|
+
line: range.start.line,
|
|
59838
|
+
character: range.start.character,
|
|
59839
|
+
length: Math.max(1, range.end.character - range.start.character)
|
|
59840
|
+
});
|
|
59841
|
+
}
|
|
59842
|
+
}
|
|
59843
|
+
}
|
|
59844
|
+
return out;
|
|
59845
|
+
}
|
|
59846
|
+
function evaluateRepresentation(parser, rep) {
|
|
59847
|
+
if (!isEmbeddedLanguage(rep.language))
|
|
59848
|
+
return void 0;
|
|
59849
|
+
const span = trailingBlockBodySpan(rep);
|
|
59850
|
+
if (!span)
|
|
59851
|
+
return void 0;
|
|
59852
|
+
const owner = rep.$container;
|
|
59853
|
+
const context = owner && owner.$type !== "Document" ? owner : void 0;
|
|
59854
|
+
const declaration = parser.parse(span.text, { rule: "NamespaceElement" });
|
|
59855
|
+
if (!hasErrors(declaration)) {
|
|
59856
|
+
return {
|
|
59857
|
+
span,
|
|
59858
|
+
verdict: conformance(declaration.value, owner),
|
|
59859
|
+
unresolved: context ? unresolvedReferences(declaration.value, context) : [],
|
|
59860
|
+
fragment: declaration.value,
|
|
59861
|
+
tier: "declaration"
|
|
59862
|
+
};
|
|
59863
|
+
}
|
|
59864
|
+
if (owner && EXPRESSION_VALUED_OWNERS.has(owner.$type)) {
|
|
59865
|
+
const expression = parser.parse(span.text, { rule: "Expr" });
|
|
59866
|
+
if (!hasErrors(expression)) {
|
|
59867
|
+
return {
|
|
59868
|
+
span,
|
|
59869
|
+
verdict: { status: "ok" },
|
|
59870
|
+
unresolved: context ? unresolvedReferences(expression.value, context) : [],
|
|
59871
|
+
fragment: expression.value,
|
|
59872
|
+
tier: "expression"
|
|
59873
|
+
};
|
|
59874
|
+
}
|
|
59875
|
+
}
|
|
59876
|
+
return { span, verdict: { status: "unparsed", failure: failureOf(declaration, span) }, unresolved: [] };
|
|
59877
|
+
}
|
|
59878
|
+
|
|
59482
59879
|
// ../language-server/out/src/services/conformance.js
|
|
59483
59880
|
var SPECIALIZATION_KINDS2 = /* @__PURE__ */ new Set([
|
|
59484
59881
|
":>",
|
|
@@ -59917,7 +60314,14 @@ var SysmlValidator = class _SysmlValidator {
|
|
|
59917
60314
|
langiumDocuments;
|
|
59918
60315
|
astNodeLocator;
|
|
59919
60316
|
featurePaths;
|
|
60317
|
+
// REQ-396 — One SysmlValidator instance is registered against BOTH the SysML and
|
|
60318
|
+
// the KerML services (sysml-module.ts), so the parser used to read an embedded
|
|
60319
|
+
// representation is looked up from the document being validated rather than
|
|
60320
|
+
// captured here. The two share one grammar today; this keeps that from becoming
|
|
60321
|
+
// load-bearing.
|
|
60322
|
+
serviceRegistry;
|
|
59920
60323
|
constructor(services) {
|
|
60324
|
+
this.serviceRegistry = services.shared.ServiceRegistry;
|
|
59921
60325
|
this.indexManager = services.shared.workspace.IndexManager;
|
|
59922
60326
|
this.langiumDocuments = services.shared.workspace.LangiumDocuments;
|
|
59923
60327
|
this.astNodeLocator = services.workspace.AstNodeLocator;
|
|
@@ -59933,23 +60337,38 @@ var SysmlValidator = class _SysmlValidator {
|
|
|
59933
60337
|
// Langium linker cannot diagnose a missing intermediate feature. Resolve the
|
|
59934
60338
|
// chain here and underline only the first invalid segment.
|
|
59935
60339
|
checkPathExpr(node, accept) {
|
|
60340
|
+
const fault = this.featurePathFault(node);
|
|
60341
|
+
if (!fault)
|
|
60342
|
+
return;
|
|
60343
|
+
accept(severity("RES001", "error"), DIAGNOSTIC_MESSAGES.RES001_FEATURE_PATH_SEGMENT(fault.segment, fault.owner), {
|
|
60344
|
+
node,
|
|
60345
|
+
range: fault.range,
|
|
60346
|
+
code: "RES001",
|
|
60347
|
+
data: { featurePathSegment: true }
|
|
60348
|
+
});
|
|
60349
|
+
}
|
|
60350
|
+
// REQ-317, REQ-396 — The one place that decides whether a written feature path
|
|
60351
|
+
// has a provably absent segment. Extracted so an embedded representation body is
|
|
60352
|
+
// judged by exactly this rule (issue #150 review): a body is grafted onto the
|
|
60353
|
+
// element it represents and is therefore unreachable from the document walk that
|
|
60354
|
+
// dispatches `checkPathExpr`, so without a shared decision the two would drift
|
|
60355
|
+
// and a body would quietly escape a diagnostic the surrounding model receives.
|
|
60356
|
+
featurePathFault(node) {
|
|
59936
60357
|
let declaration = node.$container;
|
|
59937
60358
|
while (declaration && !isPartDecl(declaration))
|
|
59938
60359
|
declaration = declaration.$container;
|
|
59939
60360
|
const modifiers2 = declaration?.modifiers ?? [];
|
|
59940
60361
|
if (!declaration || !modifiers2.includes("ref"))
|
|
59941
|
-
return;
|
|
60362
|
+
return void 0;
|
|
59942
60363
|
const resolution = this.featurePaths.resolve(node);
|
|
59943
60364
|
if (resolution.unresolvedIndex === void 0)
|
|
59944
|
-
return;
|
|
60365
|
+
return void 0;
|
|
59945
60366
|
const invalid = resolution.segments[resolution.unresolvedIndex];
|
|
59946
|
-
|
|
59947
|
-
|
|
59948
|
-
|
|
59949
|
-
range: invalid.cst.range
|
|
59950
|
-
|
|
59951
|
-
data: { featurePathSegment: true }
|
|
59952
|
-
});
|
|
60367
|
+
return {
|
|
60368
|
+
segment: invalid.text,
|
|
60369
|
+
owner: resolution.unresolvedIndex > 0 ? resolution.segments[resolution.unresolvedIndex - 1].text : "the current scope",
|
|
60370
|
+
range: invalid.cst.range
|
|
60371
|
+
};
|
|
59953
60372
|
}
|
|
59954
60373
|
// REQ-389 — Namespace qualification versus feature chaining in every written path
|
|
59955
60374
|
// issue #213 — RES019: `::` binds tighter than `.`. Each dot-separated link of
|
|
@@ -60180,6 +60599,7 @@ var SysmlValidator = class _SysmlValidator {
|
|
|
60180
60599
|
this.checkConnectorForms(node, accept);
|
|
60181
60600
|
this.checkActionStartSuccessions(node, accept);
|
|
60182
60601
|
this.checkAnnotationForms(node, accept);
|
|
60602
|
+
this.checkEmbeddedRepresentations(node, accept);
|
|
60183
60603
|
this.checkLanguageDisjointness(node, accept);
|
|
60184
60604
|
this.checkKermlWellFormedness(node, accept);
|
|
60185
60605
|
this.checkFilterExpressions(node, accept);
|
|
@@ -60268,6 +60688,23 @@ var SysmlValidator = class _SysmlValidator {
|
|
|
60268
60688
|
}
|
|
60269
60689
|
this.checkMixedIndentation(node, doc, accept);
|
|
60270
60690
|
this.checkAlphabetizedImportGroups(node, doc, accept);
|
|
60691
|
+
this.checkFormatDirectives(node, doc, accept);
|
|
60692
|
+
}
|
|
60693
|
+
// REQ-392 — STYL008 a `sysml-format` directive the formatter cannot act on.
|
|
60694
|
+
// A well-formed `sysml-format ignore` that found its element is silent; only
|
|
60695
|
+
// an unknown argument and one with nothing left to attach to are reported,
|
|
60696
|
+
// each on the comment itself so the fix is where the cursor lands.
|
|
60697
|
+
checkFormatDirectives(root3, doc, accept) {
|
|
60698
|
+
for (const directive of scanFormatDirectives(doc).directives) {
|
|
60699
|
+
if (directive.kind === "ignore")
|
|
60700
|
+
continue;
|
|
60701
|
+
const range = {
|
|
60702
|
+
start: doc.textDocument.positionAt(directive.comment.offset),
|
|
60703
|
+
end: doc.textDocument.positionAt(directive.comment.end)
|
|
60704
|
+
};
|
|
60705
|
+
const message = directive.kind === "dangling" ? DIAGNOSTIC_MESSAGES.STYL008_FORMAT_DIRECTIVE_UNATTACHED : DIAGNOSTIC_MESSAGES.STYL008_FORMAT_DIRECTIVE_UNKNOWN(directive.argument);
|
|
60706
|
+
accept(severity("STYL008", "info"), message, { node: root3, range, code: "STYL008" });
|
|
60707
|
+
}
|
|
60271
60708
|
}
|
|
60272
60709
|
// REQ-332 — STYL004 mixed tab + space indentation.
|
|
60273
60710
|
checkMixedIndentation(root3, doc, accept) {
|
|
@@ -60503,6 +60940,132 @@ ${baseIndent}}`;
|
|
|
60503
60940
|
}
|
|
60504
60941
|
}
|
|
60505
60942
|
}
|
|
60943
|
+
// REQ-396 — SYN076/SEM012 embedded textual representations (issue #150).
|
|
60944
|
+
// A `rep … language "sysml"` (or `"kerml"`, matched case-insensitively) is not
|
|
60945
|
+
// opaque content: OMG KerML §7.4.3 requires its body to be a legal representation
|
|
60946
|
+
// of the element it annotates. So the body is processed by the comment-body rules,
|
|
60947
|
+
// parsed, and compared against that element:
|
|
60948
|
+
// SYN076 — the body is not valid SysML, reported ON the offending character
|
|
60949
|
+
// inside the body rather than on the `rep` keyword.
|
|
60950
|
+
// SEM012 — the body parses but represents a different element (wrong kind, or
|
|
60951
|
+
// the right kind under a different name).
|
|
60952
|
+
// Every other language stays opaque and is never parsed — an `alf`, `ocl` or
|
|
60953
|
+
// `yaml` body is content, and the vendored OMG corpus holds only those.
|
|
60954
|
+
checkEmbeddedRepresentations(node, accept) {
|
|
60955
|
+
const doc = ast_utils_exports.getDocument(node);
|
|
60956
|
+
if (!doc?.textDocument)
|
|
60957
|
+
return;
|
|
60958
|
+
let parser;
|
|
60959
|
+
for (const child of ast_utils_exports.streamAllContents(node)) {
|
|
60960
|
+
if (child.$type !== "RepStmt")
|
|
60961
|
+
continue;
|
|
60962
|
+
if (!isEmbeddedLanguage(child.language))
|
|
60963
|
+
continue;
|
|
60964
|
+
parser ??= this.serviceRegistry.getServices(doc.uri).parser.LangiumParser;
|
|
60965
|
+
const evaluated = evaluateRepresentation(parser, child);
|
|
60966
|
+
if (!evaluated)
|
|
60967
|
+
continue;
|
|
60968
|
+
const { span, verdict } = evaluated;
|
|
60969
|
+
const language = String(child.language ?? "").replace(/^"|"$/gu, "");
|
|
60970
|
+
if (verdict.status === "unparsed") {
|
|
60971
|
+
const { line, character, message } = verdict.failure;
|
|
60972
|
+
const anchor = bodyOffsetAt(span, line, character);
|
|
60973
|
+
const after = bodyOffsetAt(span, line, character + 1);
|
|
60974
|
+
const from = after > anchor ? anchor : bodyOffsetAt(span, line, Math.max(0, character - 1));
|
|
60975
|
+
accept(severity("SYN076", "error"), DIAGNOSTIC_MESSAGES.SYN076_EMBEDDED_REPRESENTATION_SYNTAX(language, message), {
|
|
60976
|
+
node: child,
|
|
60977
|
+
range: {
|
|
60978
|
+
start: doc.textDocument.positionAt(from),
|
|
60979
|
+
end: doc.textDocument.positionAt(Math.max(after, anchor))
|
|
60980
|
+
},
|
|
60981
|
+
code: "SYN076"
|
|
60982
|
+
});
|
|
60983
|
+
continue;
|
|
60984
|
+
}
|
|
60985
|
+
for (const reference of evaluated.unresolved) {
|
|
60986
|
+
accept(severity("RES020", "warning"), DIAGNOSTIC_MESSAGES.RES020_REPRESENTATION_UNRESOLVED(reference.refText, language), {
|
|
60987
|
+
node: child,
|
|
60988
|
+
range: {
|
|
60989
|
+
start: doc.textDocument.positionAt(bodyOffsetAt(span, reference.line, reference.character)),
|
|
60990
|
+
end: doc.textDocument.positionAt(bodyOffsetAt(span, reference.line, reference.character + reference.length))
|
|
60991
|
+
},
|
|
60992
|
+
code: "RES020"
|
|
60993
|
+
});
|
|
60994
|
+
}
|
|
60995
|
+
if (evaluated.fragment) {
|
|
60996
|
+
this.checkEmbeddedDialect(child, evaluated.fragment, language, span, doc, accept);
|
|
60997
|
+
this.checkEmbeddedFeaturePaths(child, evaluated.fragment, language, span, doc, accept);
|
|
60998
|
+
}
|
|
60999
|
+
if (verdict.status === "ok")
|
|
61000
|
+
continue;
|
|
61001
|
+
const { reason, expected, actual } = verdict.mismatch;
|
|
61002
|
+
const owner = child.$container;
|
|
61003
|
+
accept(severity("SEM012", "warning"), reason === "kind" ? DIAGNOSTIC_MESSAGES.SEM012_REPRESENTATION_WRONG_KIND(expected, actual) : DIAGNOSTIC_MESSAGES.SEM012_REPRESENTATION_WRONG_NAME(expected, actual), {
|
|
61004
|
+
node: child,
|
|
61005
|
+
range: {
|
|
61006
|
+
start: doc.textDocument.positionAt(bodyOffsetAt(span, 0, 0)),
|
|
61007
|
+
end: doc.textDocument.positionAt(bodyOffsetAt(span, 0, span.lines[0]?.text.length ?? 0))
|
|
61008
|
+
},
|
|
61009
|
+
code: "SEM012",
|
|
61010
|
+
relatedInformation: owner ? [relatedInfo(owner, "The element this representation annotates.")].filter((info) => info !== void 0) : void 0
|
|
61011
|
+
});
|
|
61012
|
+
}
|
|
61013
|
+
}
|
|
61014
|
+
// REQ-396 — SYN100/SYN101 inside a representation body (issue #150 review).
|
|
61015
|
+
// The normative rule is that the body is legal in the language the representation
|
|
61016
|
+
// NAMES, which is not the language of the file it sits in: a `.sysml` file may
|
|
61017
|
+
// carry a `language "kerml"` representation, and its body may then use no
|
|
61018
|
+
// SysML-only construct. `checkLanguageDisjointness` answers a different question
|
|
61019
|
+
// — what may this FILE contain — from the host URI, so the same tables are read
|
|
61020
|
+
// here against the declared language instead.
|
|
61021
|
+
checkEmbeddedDialect(rep, fragment, language, span, doc, accept) {
|
|
61022
|
+
const dialect = language.trim().toLowerCase();
|
|
61023
|
+
const isKerml = dialect === "kerml";
|
|
61024
|
+
const forbidden = isKerml ? SYSML_ONLY_TYPES : KERML_ONLY_TYPES;
|
|
61025
|
+
const code = isKerml ? "SYN100" : "SYN101";
|
|
61026
|
+
for (const node of [fragment, ...ast_utils_exports.streamAllContents(fragment)]) {
|
|
61027
|
+
if (!forbidden.has(node.$type))
|
|
61028
|
+
continue;
|
|
61029
|
+
const range = node.$cstNode?.range;
|
|
61030
|
+
if (!range)
|
|
61031
|
+
continue;
|
|
61032
|
+
accept(severity(code, "error"), isKerml ? `'${declKeyword(node)}' is a SysML construct and is not available in a "${language}" representation.` : `'${declKeyword(node)}' is a KerML-only construct \u2014 use the SysML form in a "${language}" representation.`, {
|
|
61033
|
+
node: rep,
|
|
61034
|
+
range: this.bodyRange(span, doc, range),
|
|
61035
|
+
code
|
|
61036
|
+
});
|
|
61037
|
+
}
|
|
61038
|
+
}
|
|
61039
|
+
// REQ-396 — RES020 for a written feature path with a provably absent segment
|
|
61040
|
+
// (issue #150 review). `checkPathExpr` is registered as a node-level check, so
|
|
61041
|
+
// Langium dispatches it by walking the document AST and never reaches the grafted
|
|
61042
|
+
// fragment. The decision itself is shared with it through `featurePathFault`, so
|
|
61043
|
+
// a body is held to the same rule as the model around it rather than a parallel
|
|
61044
|
+
// one that could drift; only the code differs, keeping every fault found IN a
|
|
61045
|
+
// representation body under the one severity a team can tune.
|
|
61046
|
+
checkEmbeddedFeaturePaths(rep, fragment, language, span, doc, accept) {
|
|
61047
|
+
for (const node of [fragment, ...ast_utils_exports.streamAllContents(fragment)]) {
|
|
61048
|
+
if (!this.featurePaths.isPathExpr(node))
|
|
61049
|
+
continue;
|
|
61050
|
+
const fault = this.featurePathFault(node);
|
|
61051
|
+
if (!fault)
|
|
61052
|
+
continue;
|
|
61053
|
+
accept(severity("RES020", "warning"), DIAGNOSTIC_MESSAGES.RES020_REPRESENTATION_PATH_SEGMENT(fault.segment, fault.owner, language), {
|
|
61054
|
+
node: rep,
|
|
61055
|
+
range: this.bodyRange(span, doc, fault.range),
|
|
61056
|
+
code: "RES020"
|
|
61057
|
+
});
|
|
61058
|
+
}
|
|
61059
|
+
}
|
|
61060
|
+
// REQ-396 — A range inside a parsed body, expressed in document coordinates.
|
|
61061
|
+
// Fragment CST positions are line/character within the PROCESSED body, which is
|
|
61062
|
+
// exactly what the span's line table converts.
|
|
61063
|
+
bodyRange(span, doc, range) {
|
|
61064
|
+
return {
|
|
61065
|
+
start: doc.textDocument.positionAt(bodyOffsetAt(span, range.start.line, range.start.character)),
|
|
61066
|
+
end: doc.textDocument.positionAt(bodyOffsetAt(span, range.end.line, range.end.character))
|
|
61067
|
+
};
|
|
61068
|
+
}
|
|
60506
61069
|
// REQ-311 — SYN050/051 connector syntax, with a specific message in place of
|
|
60507
61070
|
// the grammar's generic parse error:
|
|
60508
61071
|
// SYN050 — `connect a with b;` (use `connect a to b;`).
|
|
@@ -62910,6 +63473,7 @@ var STYLE_ADVISORY_CODES = /* @__PURE__ */ new Set([
|
|
|
62910
63473
|
"STYL005",
|
|
62911
63474
|
"STYL006",
|
|
62912
63475
|
"STYL007",
|
|
63476
|
+
"STYL008",
|
|
62913
63477
|
"LEX009",
|
|
62914
63478
|
// trailing whitespace
|
|
62915
63479
|
"SYN024",
|
|
@@ -63376,52 +63940,6 @@ function outlineGroupForType(astType) {
|
|
|
63376
63940
|
return CATEGORY_TO_OUTLINE_GROUP[categoryForType(astType)] ?? "structure";
|
|
63377
63941
|
}
|
|
63378
63942
|
|
|
63379
|
-
// ../language-server/out/src/services/comment-body.js
|
|
63380
|
-
function sourceOf(node) {
|
|
63381
|
-
return node.$cstNode?.root.fullText;
|
|
63382
|
-
}
|
|
63383
|
-
function cleanBlockBody(inner) {
|
|
63384
|
-
let body = inner.trim();
|
|
63385
|
-
if (body.startsWith("/*"))
|
|
63386
|
-
body = body.slice(2);
|
|
63387
|
-
if (body.endsWith("*/"))
|
|
63388
|
-
body = body.slice(0, -2);
|
|
63389
|
-
const lines = body.split("\n").map((line) => line.replace(/^\s*\*+ ?/u, "").replace(/\s+$/u, ""));
|
|
63390
|
-
while (lines.length > 0 && lines[0].trim() === "")
|
|
63391
|
-
lines.shift();
|
|
63392
|
-
while (lines.length > 0 && lines[lines.length - 1].trim() === "")
|
|
63393
|
-
lines.pop();
|
|
63394
|
-
return lines.join("\n").trim();
|
|
63395
|
-
}
|
|
63396
|
-
function docCommentBody(node) {
|
|
63397
|
-
const raw = node.doc;
|
|
63398
|
-
if (typeof raw !== "string")
|
|
63399
|
-
return void 0;
|
|
63400
|
-
const start = raw.indexOf("/*");
|
|
63401
|
-
const end = raw.lastIndexOf("*/");
|
|
63402
|
-
if (start < 0 || end < start)
|
|
63403
|
-
return void 0;
|
|
63404
|
-
const body = cleanBlockBody(raw.slice(start, end + 2));
|
|
63405
|
-
return body.length > 0 ? body : void 0;
|
|
63406
|
-
}
|
|
63407
|
-
function trailingBlockBody(node) {
|
|
63408
|
-
const cst = node.$cstNode;
|
|
63409
|
-
const text = sourceOf(node);
|
|
63410
|
-
if (!cst || text === void 0)
|
|
63411
|
-
return void 0;
|
|
63412
|
-
const rest = text.slice(cst.end);
|
|
63413
|
-
const open = rest.indexOf("/*");
|
|
63414
|
-
if (open < 0)
|
|
63415
|
-
return void 0;
|
|
63416
|
-
if (rest.slice(0, open).trim() !== "")
|
|
63417
|
-
return void 0;
|
|
63418
|
-
const close = rest.indexOf("*/", open + 2);
|
|
63419
|
-
if (close < 0)
|
|
63420
|
-
return void 0;
|
|
63421
|
-
const body = cleanBlockBody(rest.slice(open, close + 2));
|
|
63422
|
-
return body.length > 0 ? body : void 0;
|
|
63423
|
-
}
|
|
63424
|
-
|
|
63425
63943
|
// ../language-server/out/src/services/document-symbol-provider.js
|
|
63426
63944
|
var SPECIALIZATION_KINDS4 = /* @__PURE__ */ new Set([":>", "specializes"]);
|
|
63427
63945
|
var REDEFINITION_KINDS = /* @__PURE__ */ new Set([":>>", "redefines"]);
|
|
@@ -63988,6 +64506,18 @@ function resolveEffectiveSubject(node) {
|
|
|
63988
64506
|
|
|
63989
64507
|
// ../language-server/out/src/services/hover-provider.js
|
|
63990
64508
|
var MAX_HOVER_LINES = 30;
|
|
64509
|
+
var MAX_DOC_DEPTH = 16;
|
|
64510
|
+
var MAX_DOC_NODES = 64;
|
|
64511
|
+
var MAX_DOC_SECTION_LINES = 14;
|
|
64512
|
+
var DOC_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
|
|
64513
|
+
":>",
|
|
64514
|
+
":>>",
|
|
64515
|
+
"::>",
|
|
64516
|
+
"specializes",
|
|
64517
|
+
"subsets",
|
|
64518
|
+
"redefines",
|
|
64519
|
+
"references"
|
|
64520
|
+
]);
|
|
63991
64521
|
function leafAtOffset(node, offset) {
|
|
63992
64522
|
if (isLeafCstNode(node)) {
|
|
63993
64523
|
return node.offset <= offset && offset < node.offset + node.length ? node : void 0;
|
|
@@ -64142,22 +64672,26 @@ function multiplicityPlainEnglish(node) {
|
|
|
64142
64672
|
return lo === 1 ? "Exactly 1 value" : `Exactly ${lo} values`;
|
|
64143
64673
|
return `Between ${lo} and ${hi ?? "*"} values`;
|
|
64144
64674
|
}
|
|
64145
|
-
function
|
|
64675
|
+
function localDocs(node) {
|
|
64676
|
+
const bodies = [];
|
|
64146
64677
|
const members = node.members;
|
|
64147
64678
|
if (Array.isArray(members)) {
|
|
64148
64679
|
for (const m of members) {
|
|
64149
64680
|
const body = docBody(m);
|
|
64150
64681
|
if (body)
|
|
64151
|
-
|
|
64682
|
+
bodies.push(body);
|
|
64152
64683
|
}
|
|
64153
64684
|
}
|
|
64685
|
+
if (bodies.length > 0)
|
|
64686
|
+
return bodies;
|
|
64154
64687
|
const siblings = node.$container?.members;
|
|
64155
64688
|
if (Array.isArray(siblings)) {
|
|
64156
64689
|
const index = siblings.indexOf(node);
|
|
64157
|
-
|
|
64158
|
-
|
|
64690
|
+
const preceding = index > 0 ? docBody(siblings[index - 1]) : void 0;
|
|
64691
|
+
if (preceding)
|
|
64692
|
+
bodies.push(preceding);
|
|
64159
64693
|
}
|
|
64160
|
-
return
|
|
64694
|
+
return bodies;
|
|
64161
64695
|
}
|
|
64162
64696
|
function docBody(node) {
|
|
64163
64697
|
const member = node;
|
|
@@ -64303,13 +64837,67 @@ function buildCommentHover(node) {
|
|
|
64303
64837
|
lines.push("", sourceFooter(nodeSource(node)));
|
|
64304
64838
|
return lines.join("\n");
|
|
64305
64839
|
}
|
|
64840
|
+
function uniqueLocalTarget(source, targetText) {
|
|
64841
|
+
const root3 = ast_utils_exports.getDocument(source).parseResult?.value;
|
|
64842
|
+
if (!root3)
|
|
64843
|
+
return void 0;
|
|
64844
|
+
let found;
|
|
64845
|
+
for (const candidate of ast_utils_exports.streamAllContents(root3)) {
|
|
64846
|
+
const named = candidate;
|
|
64847
|
+
if (named.name !== targetText && named.shortName?.name !== targetText)
|
|
64848
|
+
continue;
|
|
64849
|
+
if (found)
|
|
64850
|
+
return void 0;
|
|
64851
|
+
found = candidate;
|
|
64852
|
+
}
|
|
64853
|
+
return found;
|
|
64854
|
+
}
|
|
64855
|
+
function declarationLink(node) {
|
|
64856
|
+
const uri = ast_utils_exports.getDocument(node).uri.toString();
|
|
64857
|
+
const line = (node.$cstNode?.range.start.line ?? 0) + 1;
|
|
64858
|
+
return `[${shortUri(uri)}:${line}](${uri}#L${line})`;
|
|
64859
|
+
}
|
|
64860
|
+
function limitDocSection(value) {
|
|
64861
|
+
const lines = value.split(/\r?\n/u);
|
|
64862
|
+
if (lines.length <= MAX_DOC_SECTION_LINES)
|
|
64863
|
+
return value;
|
|
64864
|
+
const kept = lines.slice(0, MAX_DOC_SECTION_LINES);
|
|
64865
|
+
if (kept.filter((line) => line.startsWith("```")).length % 2 !== 0)
|
|
64866
|
+
kept.push("```");
|
|
64867
|
+
kept.push("\u2026");
|
|
64868
|
+
return kept.join("\n");
|
|
64869
|
+
}
|
|
64870
|
+
function renderDocs(bodies, provenance) {
|
|
64871
|
+
const body = limitDocSection(bodies.join("\n\n"));
|
|
64872
|
+
return provenance ? `${provenance}
|
|
64873
|
+
|
|
64874
|
+
${body}` : body;
|
|
64875
|
+
}
|
|
64876
|
+
function supertypeCandidates(node) {
|
|
64877
|
+
const named = node;
|
|
64878
|
+
const out = [];
|
|
64879
|
+
const typing = named.typing;
|
|
64880
|
+
if (typing?.type?.$refText)
|
|
64881
|
+
out.push({ refText: typing.type.$refText, ref: typing.type.ref });
|
|
64882
|
+
for (const more of typing?.moreTypes ?? []) {
|
|
64883
|
+
if (more.$refText)
|
|
64884
|
+
out.push({ refText: more.$refText, ref: more.ref });
|
|
64885
|
+
}
|
|
64886
|
+
for (const rel of allRelationships(named)) {
|
|
64887
|
+
if (!rel.kind || !DOC_SPECIALIZATION_KINDS.has(rel.kind))
|
|
64888
|
+
continue;
|
|
64889
|
+
for (const target of rel.targets ?? [])
|
|
64890
|
+
out.push({ refText: target });
|
|
64891
|
+
}
|
|
64892
|
+
return out;
|
|
64893
|
+
}
|
|
64306
64894
|
function buildContextualHover(node) {
|
|
64307
64895
|
const contextual = nearestAncestor(node, /* @__PURE__ */ new Set(["IfExpr", "MetadataAnnotation", "RepStmt", "CommentStmt"]));
|
|
64308
64896
|
if (!contextual)
|
|
64309
64897
|
return void 0;
|
|
64310
64898
|
return buildConditionalHover(contextual) ?? buildMetadataAnnotationHover(contextual) ?? buildRepresentationHover(contextual) ?? buildCommentHover(contextual);
|
|
64311
64899
|
}
|
|
64312
|
-
function buildHoverMarkdown(node, relationshipDetails) {
|
|
64900
|
+
function buildHoverMarkdown(node, relationshipDetails, documentation) {
|
|
64313
64901
|
const named = node;
|
|
64314
64902
|
const dir = directionLabel(node);
|
|
64315
64903
|
const kind = kindLabel2(node);
|
|
@@ -64318,7 +64906,7 @@ function buildHoverMarkdown(node, relationshipDetails) {
|
|
|
64318
64906
|
const mult = multiplicityLabel(node);
|
|
64319
64907
|
const multHint = multiplicityPlainEnglish(node);
|
|
64320
64908
|
const rels = relationshipLabel(allRelationships(named));
|
|
64321
|
-
const doc =
|
|
64909
|
+
const doc = documentation;
|
|
64322
64910
|
const members = memberList(node);
|
|
64323
64911
|
const headerLines = [`${dir}${kind} ${name}${typing}${mult}${rels}`];
|
|
64324
64912
|
if (members.length > 0) {
|
|
@@ -64671,11 +65259,14 @@ var SysmlHoverProvider = class {
|
|
|
64671
65259
|
astNodeLocator;
|
|
64672
65260
|
documents;
|
|
64673
65261
|
indexManager;
|
|
65262
|
+
/** REQ-394 — the linker's lazy standard-library load, reused rather than duplicated. */
|
|
65263
|
+
linker;
|
|
64674
65264
|
constructor(services) {
|
|
64675
65265
|
this.references = services.references.References;
|
|
64676
65266
|
this.astNodeLocator = services.workspace.AstNodeLocator;
|
|
64677
65267
|
this.documents = services.shared.workspace.LangiumDocuments;
|
|
64678
65268
|
this.indexManager = services.shared.workspace.IndexManager;
|
|
65269
|
+
this.linker = services.references.Linker;
|
|
64679
65270
|
}
|
|
64680
65271
|
// REQ-246, REQ-334, REQ-335, REQ-336, REQ-342, REQ-343 — Route hover requests to markdown tooltip builders
|
|
64681
65272
|
async getHoverContent(document, params) {
|
|
@@ -64749,7 +65340,50 @@ var SysmlHoverProvider = class {
|
|
|
64749
65340
|
};
|
|
64750
65341
|
}
|
|
64751
65342
|
async buildElementHover(node) {
|
|
64752
|
-
return buildHoverMarkdown(node, await this.relationshipDetailSection(node));
|
|
65343
|
+
return buildHoverMarkdown(node, await this.relationshipDetailSection(node), await this.documentationSection(node));
|
|
65344
|
+
}
|
|
65345
|
+
/**
|
|
65346
|
+
* REQ-394 — The documentation an element reads with.
|
|
65347
|
+
*
|
|
65348
|
+
* Its own `doc` members win, all of them, in declaration order. With none of
|
|
65349
|
+
* its own, the walk goes up the typing and specialization graph breadth
|
|
65350
|
+
* first, so the CLOSEST documented type wins over a more general ancestor,
|
|
65351
|
+
* and stops at the first documented one — naming it, and linking to it, so
|
|
65352
|
+
* the reader can see where the prose came from.
|
|
65353
|
+
*
|
|
65354
|
+
* The walk resolves through the same machinery as go-to-definition
|
|
65355
|
+
* (`resolveRelationshipTarget` — local nodes, then the workspace index, then
|
|
65356
|
+
* one lazily parsed library file), so cross-file and standard-library
|
|
65357
|
+
* documentation arrive without a second name resolver. A `visited` set plus
|
|
65358
|
+
* the depth and node budgets make a cyclic or malformed graph terminate.
|
|
65359
|
+
*/
|
|
65360
|
+
async documentationSection(node) {
|
|
65361
|
+
const own = localDocs(node);
|
|
65362
|
+
if (own.length > 0)
|
|
65363
|
+
return renderDocs(own);
|
|
65364
|
+
const visited = /* @__PURE__ */ new Set([node]);
|
|
65365
|
+
let frontier = [node];
|
|
65366
|
+
for (let depth = 0; depth < MAX_DOC_DEPTH && frontier.length > 0; depth++) {
|
|
65367
|
+
const next = [];
|
|
65368
|
+
for (const current2 of frontier) {
|
|
65369
|
+
for (const candidate of supertypeCandidates(current2)) {
|
|
65370
|
+
if (visited.size >= MAX_DOC_NODES)
|
|
65371
|
+
return void 0;
|
|
65372
|
+
const target = candidate.ref ?? this.resolveDocumentationTarget(current2, candidate.refText);
|
|
65373
|
+
if (!target || visited.has(target))
|
|
65374
|
+
continue;
|
|
65375
|
+
visited.add(target);
|
|
65376
|
+
const inherited = localDocs(target);
|
|
65377
|
+
if (inherited.length > 0) {
|
|
65378
|
+
const label = identificationLabel(target) ?? candidate.refText;
|
|
65379
|
+
return renderDocs(inherited, `*Documentation inherited from ${kindLabel2(target)} \`${label}\`* \u2014 ${declarationLink(target)}`);
|
|
65380
|
+
}
|
|
65381
|
+
next.push(target);
|
|
65382
|
+
}
|
|
65383
|
+
}
|
|
65384
|
+
frontier = next;
|
|
65385
|
+
}
|
|
65386
|
+
return void 0;
|
|
64753
65387
|
}
|
|
64754
65388
|
async buildOperatorHover(token) {
|
|
64755
65389
|
const qualifiedName = OPERATOR_FUNCTIONS[token];
|
|
@@ -64799,6 +65433,46 @@ var SysmlHoverProvider = class {
|
|
|
64799
65433
|
return lines.length > 0 ? `*Relationships:*
|
|
64800
65434
|
${lines.join("\n")}` : void 0;
|
|
64801
65435
|
}
|
|
65436
|
+
/**
|
|
65437
|
+
* REQ-394 — Resolve a supertype for the documentation walk without pulling a
|
|
65438
|
+
* document into the workspace.
|
|
65439
|
+
*
|
|
65440
|
+
* The card itself may lazily parse ONE indexed library target (REQ-251), and
|
|
65441
|
+
* that is the budget. Looking for an inherited `doc` must not add to it, so
|
|
65442
|
+
* this reuses the LINKER's own lazy load — the same path a cross-reference
|
|
65443
|
+
* takes, which parses a standard-library file off to the side and caches it
|
|
65444
|
+
* — instead of `getOrCreateDocument`, which would make every hover grow
|
|
65445
|
+
* `LangiumDocuments`.
|
|
65446
|
+
*
|
|
65447
|
+
* The match has to be UNAMBIGUOUS. This is not a scope-aware resolver — it
|
|
65448
|
+
* cannot apply imports or visibility — so it only accepts a name written
|
|
65449
|
+
* exactly as the index holds it, or a final segment that names exactly one
|
|
65450
|
+
* element in the whole index. Two `Vehicle`s in different packages, or an
|
|
65451
|
+
* unresolved reference that happens to share a final segment with something
|
|
65452
|
+
* unrelated, therefore yield NO documentation instead of another element's
|
|
65453
|
+
* prose picked by index order. Wrong documentation reads as authoritative;
|
|
65454
|
+
* none reads as none.
|
|
65455
|
+
*/
|
|
65456
|
+
resolveDocumentationTarget(source, targetText) {
|
|
65457
|
+
const local = uniqueLocalTarget(source, targetText);
|
|
65458
|
+
if (local)
|
|
65459
|
+
return local;
|
|
65460
|
+
const key = relationshipTargetKey(targetText);
|
|
65461
|
+
const exact = /* @__PURE__ */ new Map();
|
|
65462
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
65463
|
+
for (const description of this.indexManager.allElements()) {
|
|
65464
|
+
const id = `${description.documentUri.toString()}#${description.path}`;
|
|
65465
|
+
if (description.name === targetText)
|
|
65466
|
+
exact.set(id, description);
|
|
65467
|
+
else if (description.name === key)
|
|
65468
|
+
byKey.set(id, description);
|
|
65469
|
+
}
|
|
65470
|
+
const pool = exact.size > 0 ? exact : byKey;
|
|
65471
|
+
if (pool.size !== 1)
|
|
65472
|
+
return void 0;
|
|
65473
|
+
const chosen = pool.values().next().value;
|
|
65474
|
+
return this.linker.resolveIndexedNode?.(chosen) ?? chosen.node;
|
|
65475
|
+
}
|
|
64802
65476
|
async resolveRelationshipTarget(source, targetText) {
|
|
64803
65477
|
const local = this.findLocalRelationshipTarget(source, targetText);
|
|
64804
65478
|
if (local)
|
|
@@ -66671,6 +67345,19 @@ var SysmlLinker = class extends DefaultLinker {
|
|
|
66671
67345
|
const root3 = this.parsedLibraryDocument(nodeDescription.documentUri)?.parseResult?.value;
|
|
66672
67346
|
return root3 ? this.astNodeLocator.getAstNode(root3, nodeDescription.path) : void 0;
|
|
66673
67347
|
}
|
|
67348
|
+
/**
|
|
67349
|
+
* REQ-394 — Resolve an indexed description to a node WITHOUT adding a
|
|
67350
|
+
* document to the workspace.
|
|
67351
|
+
*
|
|
67352
|
+
* This is the same path a cross-reference takes (`loadAstNode`): an already
|
|
67353
|
+
* live node, otherwise the standard-library file parsed once off to the side
|
|
67354
|
+
* and cached here. Hover's documentation walk uses it so looking for an
|
|
67355
|
+
* inherited `doc` never pulls a library file into `LangiumDocuments`, and so
|
|
67356
|
+
* there is one lazy-library-load path rather than two.
|
|
67357
|
+
*/
|
|
67358
|
+
resolveIndexedNode(nodeDescription) {
|
|
67359
|
+
return this.loadAstNode(nodeDescription);
|
|
67360
|
+
}
|
|
66674
67361
|
/** Parse (once) the standard-library file a precomputed description points at.
|
|
66675
67362
|
* Prefers an already-live workspace document when one exists (runtime-loaded
|
|
66676
67363
|
* library, or the user opened the file) so there is never a second copy. */
|
|
@@ -67302,14 +67989,526 @@ function getNodeAtRange(document, params) {
|
|
|
67302
67989
|
return leafAtOffset2(root3, offset)?.astNode;
|
|
67303
67990
|
}
|
|
67304
67991
|
|
|
67992
|
+
// ../language-server/out/src/services/formatter-settings.js
|
|
67993
|
+
var FORMATTER_DEFAULTS = Object.freeze({
|
|
67994
|
+
indentStyle: "editor",
|
|
67995
|
+
indentSize: 0,
|
|
67996
|
+
blankLines: 1,
|
|
67997
|
+
lineWidth: 0,
|
|
67998
|
+
emptyBody: "preserve",
|
|
67999
|
+
quotedNames: "preserve",
|
|
68000
|
+
documentationIndent: "preserve",
|
|
68001
|
+
keywords: "preserve"
|
|
68002
|
+
});
|
|
68003
|
+
var sysmlFormatterSettings = { ...FORMATTER_DEFAULTS };
|
|
68004
|
+
var OPTION_PREFIX = "sysml.format.";
|
|
68005
|
+
var INDENT_STYLES = ["editor", "spaces", "tabs"];
|
|
68006
|
+
var EMPTY_BODY_STYLES = ["preserve", "semicolon", "braces"];
|
|
68007
|
+
var QUOTED_NAME_STYLES = ["preserve", "unquoteSafe"];
|
|
68008
|
+
var DOC_INDENT_STYLES = ["preserve", "align"];
|
|
68009
|
+
var KEYWORD_STYLES = ["preserve", "symbolic"];
|
|
68010
|
+
var MAX_INDENT_SIZE = 16;
|
|
68011
|
+
var MIN_LINE_WIDTH = 40;
|
|
68012
|
+
var MAX_LINE_WIDTH = 400;
|
|
68013
|
+
var MAX_BLANK_LINES = 2;
|
|
68014
|
+
function readEnum(name, value, allowed, fallback, issues) {
|
|
68015
|
+
if (value === void 0)
|
|
68016
|
+
return fallback;
|
|
68017
|
+
if (typeof value === "string" && allowed.includes(value))
|
|
68018
|
+
return value;
|
|
68019
|
+
issues.push({ setting: name, value, reason: `expected one of ${allowed.map((a2) => `'${a2}'`).join(", ")}` });
|
|
68020
|
+
return fallback;
|
|
68021
|
+
}
|
|
68022
|
+
function readInt(name, value, min2, max, fallback, issues, zeroMeans) {
|
|
68023
|
+
if (value === void 0)
|
|
68024
|
+
return fallback;
|
|
68025
|
+
if (typeof value === "number" && Number.isInteger(value) && (value === 0 && zeroMeans !== void 0 ? true : value >= min2 && value <= max)) {
|
|
68026
|
+
return value;
|
|
68027
|
+
}
|
|
68028
|
+
const range = zeroMeans !== void 0 ? `0 (${zeroMeans}) or ${min2}\u2013${max}` : `${min2}\u2013${max}`;
|
|
68029
|
+
issues.push({ setting: name, value, reason: `expected an integer ${range}` });
|
|
68030
|
+
return fallback;
|
|
68031
|
+
}
|
|
68032
|
+
function resolveFormatterSettings(raw, base = FORMATTER_DEFAULTS) {
|
|
68033
|
+
const issues = [];
|
|
68034
|
+
const source = raw && typeof raw === "object" ? raw : {};
|
|
68035
|
+
const settings = {
|
|
68036
|
+
indentStyle: readEnum("sysml.format.indentStyle", source.indentStyle, INDENT_STYLES, base.indentStyle, issues),
|
|
68037
|
+
indentSize: readInt("sysml.format.indentSize", source.indentSize, 1, MAX_INDENT_SIZE, base.indentSize, issues, "follow the editor"),
|
|
68038
|
+
blankLines: readInt("sysml.format.blankLines", source.blankLines, 0, MAX_BLANK_LINES, base.blankLines, issues),
|
|
68039
|
+
lineWidth: readInt("sysml.format.lineWidth", source.lineWidth, MIN_LINE_WIDTH, MAX_LINE_WIDTH, base.lineWidth, issues, "never wrap"),
|
|
68040
|
+
emptyBody: readEnum("sysml.format.emptyBody", source.emptyBody, EMPTY_BODY_STYLES, base.emptyBody, issues),
|
|
68041
|
+
quotedNames: readEnum("sysml.format.quotedNames", source.quotedNames, QUOTED_NAME_STYLES, base.quotedNames, issues),
|
|
68042
|
+
documentationIndent: readEnum("sysml.format.documentationIndent", source.documentationIndent, DOC_INDENT_STYLES, base.documentationIndent, issues),
|
|
68043
|
+
keywords: readEnum("sysml.format.keywords", source.keywords, KEYWORD_STYLES, base.keywords, issues)
|
|
68044
|
+
};
|
|
68045
|
+
return { settings, issues };
|
|
68046
|
+
}
|
|
68047
|
+
function resolveFormatterOptions(options, workspace = sysmlFormatterSettings) {
|
|
68048
|
+
const overrides = {};
|
|
68049
|
+
for (const [key, value] of Object.entries(options ?? {})) {
|
|
68050
|
+
if (key.startsWith(OPTION_PREFIX))
|
|
68051
|
+
overrides[key.slice(OPTION_PREFIX.length)] = value;
|
|
68052
|
+
}
|
|
68053
|
+
return resolveFormatterSettings(overrides, workspace);
|
|
68054
|
+
}
|
|
68055
|
+
function effectiveFormattingOptions(options, settings) {
|
|
68056
|
+
const insertSpaces = settings.indentStyle === "editor" ? options.insertSpaces : settings.indentStyle === "spaces";
|
|
68057
|
+
const tabSize = settings.indentSize === 0 ? options.tabSize : settings.indentSize;
|
|
68058
|
+
if (insertSpaces === options.insertSpaces && tabSize === options.tabSize)
|
|
68059
|
+
return options;
|
|
68060
|
+
return { ...options, insertSpaces, tabSize };
|
|
68061
|
+
}
|
|
68062
|
+
function indentUnit(options) {
|
|
68063
|
+
return options.insertSpaces ? " ".repeat(Math.max(1, options.tabSize)) : " ";
|
|
68064
|
+
}
|
|
68065
|
+
|
|
68066
|
+
// ../language-server/out/src/services/formatter-style.js
|
|
68067
|
+
var SYMBOLIC_KEYWORDS = Object.freeze({
|
|
68068
|
+
specializes: ":>",
|
|
68069
|
+
subsets: ":>",
|
|
68070
|
+
redefines: ":>>",
|
|
68071
|
+
references: "::>",
|
|
68072
|
+
crosses: "=>"
|
|
68073
|
+
});
|
|
68074
|
+
var TYPING_PHRASES = ["typed", "defined"];
|
|
68075
|
+
var BARE_ID2 = /^[_a-zA-Z]\w*$/u;
|
|
68076
|
+
var ESCAPED_NAME = /^'([^'\\]|\\.)*'$/u;
|
|
68077
|
+
function projectFormatted(document, edits) {
|
|
68078
|
+
const doc = document.textDocument;
|
|
68079
|
+
const source = doc.getText();
|
|
68080
|
+
const list = edits.map((edit) => ({
|
|
68081
|
+
start: doc.offsetAt(edit.range.start),
|
|
68082
|
+
end: doc.offsetAt(edit.range.end),
|
|
68083
|
+
newText: edit.newText
|
|
68084
|
+
})).sort((a2, b) => a2.start - b.start);
|
|
68085
|
+
let out = "";
|
|
68086
|
+
let cursor = 0;
|
|
68087
|
+
let delta = 0;
|
|
68088
|
+
const marks = [];
|
|
68089
|
+
for (const edit of list) {
|
|
68090
|
+
if (edit.start < cursor)
|
|
68091
|
+
continue;
|
|
68092
|
+
out += source.slice(cursor, edit.start) + edit.newText;
|
|
68093
|
+
delta += edit.newText.length - (edit.end - edit.start);
|
|
68094
|
+
marks.push({ at: edit.end, delta });
|
|
68095
|
+
cursor = edit.end;
|
|
68096
|
+
}
|
|
68097
|
+
out += source.slice(cursor);
|
|
68098
|
+
return {
|
|
68099
|
+
text: out,
|
|
68100
|
+
project(offset) {
|
|
68101
|
+
let low = 0;
|
|
68102
|
+
let high = marks.length - 1;
|
|
68103
|
+
let applied = 0;
|
|
68104
|
+
while (low <= high) {
|
|
68105
|
+
const mid = low + high >> 1;
|
|
68106
|
+
if (marks[mid].at <= offset) {
|
|
68107
|
+
applied = marks[mid].delta;
|
|
68108
|
+
low = mid + 1;
|
|
68109
|
+
} else {
|
|
68110
|
+
high = mid - 1;
|
|
68111
|
+
}
|
|
68112
|
+
}
|
|
68113
|
+
return offset + applied;
|
|
68114
|
+
}
|
|
68115
|
+
};
|
|
68116
|
+
}
|
|
68117
|
+
function lineAt(text, offset) {
|
|
68118
|
+
const start = text.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
|
|
68119
|
+
const nl = text.indexOf("\n", offset);
|
|
68120
|
+
const end = nl < 0 ? text.length : nl;
|
|
68121
|
+
const indent = /^[ \t]*/u.exec(text.slice(start, end))?.[0] ?? "";
|
|
68122
|
+
return { start, end, indent };
|
|
68123
|
+
}
|
|
68124
|
+
function leavesOf(document) {
|
|
68125
|
+
const rootCst = document.parseResult?.value?.$cstNode;
|
|
68126
|
+
return rootCst ? cst_utils_exports.flattenCst(rootCst).toArray() : [];
|
|
68127
|
+
}
|
|
68128
|
+
function nextLeaf(leaves, index, skipHidden) {
|
|
68129
|
+
for (let scan = index + 1; scan < leaves.length; scan++) {
|
|
68130
|
+
if (!skipHidden || !leaves[scan].hidden)
|
|
68131
|
+
return leaves[scan];
|
|
68132
|
+
}
|
|
68133
|
+
return void 0;
|
|
68134
|
+
}
|
|
68135
|
+
function keywordRewrites(leaves, out) {
|
|
68136
|
+
for (let index = 0; index < leaves.length; index++) {
|
|
68137
|
+
const leaf = leaves[index];
|
|
68138
|
+
if (leaf.hidden)
|
|
68139
|
+
continue;
|
|
68140
|
+
const symbol = SYMBOLIC_KEYWORDS[leaf.text];
|
|
68141
|
+
if (symbol) {
|
|
68142
|
+
out.push({ offset: leaf.offset, end: leaf.end, newText: symbol });
|
|
68143
|
+
continue;
|
|
68144
|
+
}
|
|
68145
|
+
if (TYPING_PHRASES.includes(leaf.text) && leaf.astNode?.$type === "Typing") {
|
|
68146
|
+
const by = nextLeaf(leaves, index, true);
|
|
68147
|
+
if (by?.text === "by" && by.astNode?.$type === "Typing") {
|
|
68148
|
+
out.push({ offset: leaf.offset, end: by.end, newText: ":" });
|
|
68149
|
+
}
|
|
68150
|
+
}
|
|
68151
|
+
}
|
|
68152
|
+
}
|
|
68153
|
+
var BRACE_ONLY_BODIES = /* @__PURE__ */ new Set([
|
|
68154
|
+
"InvShorthand",
|
|
68155
|
+
"ActionBlock",
|
|
68156
|
+
"EffectClause",
|
|
68157
|
+
"Import"
|
|
68158
|
+
]);
|
|
68159
|
+
function emptyBraces(node, leaves, source) {
|
|
68160
|
+
const cst = node.$cstNode;
|
|
68161
|
+
if (!cst)
|
|
68162
|
+
return void 0;
|
|
68163
|
+
let open;
|
|
68164
|
+
let close;
|
|
68165
|
+
for (const leaf of leaves) {
|
|
68166
|
+
if (leaf.offset < cst.offset || leaf.end > cst.end || leaf.astNode !== node)
|
|
68167
|
+
continue;
|
|
68168
|
+
if (leaf.text === "{" && !open)
|
|
68169
|
+
open = leaf;
|
|
68170
|
+
if (leaf.text === "}")
|
|
68171
|
+
close = leaf;
|
|
68172
|
+
}
|
|
68173
|
+
if (!open || !close || close.offset < open.end)
|
|
68174
|
+
return void 0;
|
|
68175
|
+
return source.slice(open.end, close.offset).trim().length === 0 ? { open, close } : void 0;
|
|
68176
|
+
}
|
|
68177
|
+
function terminatingSemicolon(node, leaves) {
|
|
68178
|
+
const cst = node.$cstNode;
|
|
68179
|
+
if (!cst)
|
|
68180
|
+
return void 0;
|
|
68181
|
+
for (let index = leaves.length - 1; index >= 0; index--) {
|
|
68182
|
+
const leaf = leaves[index];
|
|
68183
|
+
if (leaf.end !== cst.end)
|
|
68184
|
+
continue;
|
|
68185
|
+
return leaf.text === ";" && leaf.astNode === node ? leaf : void 0;
|
|
68186
|
+
}
|
|
68187
|
+
return void 0;
|
|
68188
|
+
}
|
|
68189
|
+
function emptyBodyRewrites(document, leaves, projection, settings, options, out) {
|
|
68190
|
+
const root3 = document.parseResult?.value;
|
|
68191
|
+
if (!root3)
|
|
68192
|
+
return;
|
|
68193
|
+
const source = document.textDocument.getText();
|
|
68194
|
+
const leafBefore = /* @__PURE__ */ new Map();
|
|
68195
|
+
for (let index = 1; index < leaves.length; index++)
|
|
68196
|
+
leafBefore.set(leaves[index].offset, leaves[index - 1]);
|
|
68197
|
+
for (const node of ast_utils_exports.streamAllContents(root3)) {
|
|
68198
|
+
const members = node.members;
|
|
68199
|
+
if (!Array.isArray(members) || members.length > 0)
|
|
68200
|
+
continue;
|
|
68201
|
+
if (BRACE_ONLY_BODIES.has(node.$type))
|
|
68202
|
+
continue;
|
|
68203
|
+
if (settings.emptyBody === "semicolon") {
|
|
68204
|
+
const braces = emptyBraces(node, leaves, source);
|
|
68205
|
+
if (!braces)
|
|
68206
|
+
continue;
|
|
68207
|
+
const previous = leafBefore.get(braces.open.offset);
|
|
68208
|
+
const from = previous && previous.end <= braces.open.offset ? previous.end : braces.open.offset;
|
|
68209
|
+
out.push({ offset: from, end: braces.close.end, newText: ";" });
|
|
68210
|
+
continue;
|
|
68211
|
+
}
|
|
68212
|
+
if (settings.emptyBody === "braces") {
|
|
68213
|
+
if (emptyBraces(node, leaves, source))
|
|
68214
|
+
continue;
|
|
68215
|
+
const semicolon = terminatingSemicolon(node, leaves);
|
|
68216
|
+
if (!semicolon)
|
|
68217
|
+
continue;
|
|
68218
|
+
const indent = lineAt(projection.text, projection.project(node.$cstNode.offset)).indent;
|
|
68219
|
+
out.push({
|
|
68220
|
+
offset: semicolon.offset,
|
|
68221
|
+
end: semicolon.end,
|
|
68222
|
+
newText: ` {
|
|
68223
|
+
${indent}}`
|
|
68224
|
+
});
|
|
68225
|
+
}
|
|
68226
|
+
}
|
|
68227
|
+
}
|
|
68228
|
+
function quotedNameRewrites(leaves, reservedKeywords, out) {
|
|
68229
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
68230
|
+
const declared = /* @__PURE__ */ new Set();
|
|
68231
|
+
const bareSpellings = /* @__PURE__ */ new Set();
|
|
68232
|
+
for (const leaf of leaves) {
|
|
68233
|
+
if (leaf.hidden)
|
|
68234
|
+
continue;
|
|
68235
|
+
if (BARE_ID2.test(leaf.text) && !reservedKeywords.has(leaf.text)) {
|
|
68236
|
+
bareSpellings.add(leaf.text);
|
|
68237
|
+
continue;
|
|
68238
|
+
}
|
|
68239
|
+
if (!ESCAPED_NAME.test(leaf.text))
|
|
68240
|
+
continue;
|
|
68241
|
+
const body = leaf.text.slice(1, -1);
|
|
68242
|
+
if (!BARE_ID2.test(body) || reservedKeywords.has(body))
|
|
68243
|
+
continue;
|
|
68244
|
+
const list = occurrences.get(body);
|
|
68245
|
+
if (list)
|
|
68246
|
+
list.push(leaf);
|
|
68247
|
+
else
|
|
68248
|
+
occurrences.set(body, [leaf]);
|
|
68249
|
+
if (grammar_utils_exports.findAssignment(leaf)?.feature === "name")
|
|
68250
|
+
declared.add(body);
|
|
68251
|
+
}
|
|
68252
|
+
for (const [body, list] of occurrences) {
|
|
68253
|
+
if (!declared.has(body) || bareSpellings.has(body))
|
|
68254
|
+
continue;
|
|
68255
|
+
for (const leaf of list)
|
|
68256
|
+
out.push({ offset: leaf.offset, end: leaf.end, newText: body });
|
|
68257
|
+
}
|
|
68258
|
+
}
|
|
68259
|
+
function documentationRewrites(document, leaves, projection, options, out) {
|
|
68260
|
+
for (const leaf of leaves) {
|
|
68261
|
+
if (leaf.hidden || !leaf.text.includes("\n"))
|
|
68262
|
+
continue;
|
|
68263
|
+
const owner = leaf.astNode?.$type;
|
|
68264
|
+
if (owner !== "DocCommentMember" && owner !== "CommentStmt")
|
|
68265
|
+
continue;
|
|
68266
|
+
if (!leaf.text.includes("/*"))
|
|
68267
|
+
continue;
|
|
68268
|
+
const projected = projection.project(leaf.offset);
|
|
68269
|
+
const line = lineAt(projection.text, projected);
|
|
68270
|
+
if (projection.text.slice(line.start, projected).trim().length > 0)
|
|
68271
|
+
continue;
|
|
68272
|
+
const target = line.indent;
|
|
68273
|
+
const bodyLines = leaf.text.split("\n");
|
|
68274
|
+
let common;
|
|
68275
|
+
for (let index = 1; index < bodyLines.length; index++) {
|
|
68276
|
+
const raw = bodyLines[index].replace(/\r$/u, "");
|
|
68277
|
+
if (raw.trim().length === 0)
|
|
68278
|
+
continue;
|
|
68279
|
+
const lead = /^[ \t]*/u.exec(raw)?.[0] ?? "";
|
|
68280
|
+
if (common === void 0 || lead.length < common.length)
|
|
68281
|
+
common = lead;
|
|
68282
|
+
}
|
|
68283
|
+
if (common === void 0)
|
|
68284
|
+
continue;
|
|
68285
|
+
let cursor = leaf.offset + bodyLines[0].length + 1;
|
|
68286
|
+
for (let index = 1; index < bodyLines.length; index++) {
|
|
68287
|
+
const raw = bodyLines[index];
|
|
68288
|
+
const lead = /^[ \t]*/u.exec(raw)?.[0] ?? "";
|
|
68289
|
+
if (raw.replace(/\r$/u, "").trim().length > 0) {
|
|
68290
|
+
const shifted = target + lead.slice(Math.min(common.length, lead.length));
|
|
68291
|
+
if (shifted !== lead)
|
|
68292
|
+
out.push({ offset: cursor, end: cursor + lead.length, newText: shifted });
|
|
68293
|
+
}
|
|
68294
|
+
cursor += raw.length + 1;
|
|
68295
|
+
}
|
|
68296
|
+
}
|
|
68297
|
+
}
|
|
68298
|
+
function wrapRewrites(leaves, projection, options, lineWidth, out) {
|
|
68299
|
+
const unit = indentUnit(options);
|
|
68300
|
+
for (let index = 0; index < leaves.length; index++) {
|
|
68301
|
+
const leaf = leaves[index];
|
|
68302
|
+
if (leaf.hidden || leaf.text !== ",")
|
|
68303
|
+
continue;
|
|
68304
|
+
const following = nextLeaf(leaves, index, false);
|
|
68305
|
+
if (!following)
|
|
68306
|
+
continue;
|
|
68307
|
+
const projected = projection.project(leaf.offset);
|
|
68308
|
+
const line = lineAt(projection.text, projected);
|
|
68309
|
+
if (line.end - line.start <= lineWidth)
|
|
68310
|
+
continue;
|
|
68311
|
+
if (projection.text.slice(projection.project(leaf.end), line.end).trim().length === 0)
|
|
68312
|
+
continue;
|
|
68313
|
+
if (projection.text.slice(projected, projection.project(following.offset)).includes("\n"))
|
|
68314
|
+
continue;
|
|
68315
|
+
out.push({
|
|
68316
|
+
offset: leaf.end,
|
|
68317
|
+
end: following.offset,
|
|
68318
|
+
newText: `
|
|
68319
|
+
${line.indent}${unit}`
|
|
68320
|
+
});
|
|
68321
|
+
}
|
|
68322
|
+
}
|
|
68323
|
+
function withinRange(document, rewrite, range) {
|
|
68324
|
+
if (!range)
|
|
68325
|
+
return true;
|
|
68326
|
+
const doc = document.textDocument;
|
|
68327
|
+
return doc.offsetAt(range.start) <= rewrite.offset && rewrite.end <= doc.offsetAt(range.end);
|
|
68328
|
+
}
|
|
68329
|
+
function styleEdits(input) {
|
|
68330
|
+
const { document, settings, options, baseEdits, protectedRanges, range, reservedKeywords } = input;
|
|
68331
|
+
const wantsRewrite = settings.emptyBody !== "preserve" || settings.quotedNames !== "preserve" || settings.documentationIndent !== "preserve" || settings.keywords !== "preserve" || settings.lineWidth > 0;
|
|
68332
|
+
if (!wantsRewrite)
|
|
68333
|
+
return [];
|
|
68334
|
+
const leaves = leavesOf(document);
|
|
68335
|
+
if (leaves.length === 0)
|
|
68336
|
+
return [];
|
|
68337
|
+
const projection = projectFormatted(document, baseEdits);
|
|
68338
|
+
const rewrites = [];
|
|
68339
|
+
if (settings.keywords === "symbolic")
|
|
68340
|
+
keywordRewrites(leaves, rewrites);
|
|
68341
|
+
if (settings.emptyBody !== "preserve") {
|
|
68342
|
+
emptyBodyRewrites(document, leaves, projection, settings, options, rewrites);
|
|
68343
|
+
}
|
|
68344
|
+
if (settings.quotedNames === "unquoteSafe")
|
|
68345
|
+
quotedNameRewrites(leaves, reservedKeywords, rewrites);
|
|
68346
|
+
if (settings.documentationIndent === "align") {
|
|
68347
|
+
documentationRewrites(document, leaves, projection, options, rewrites);
|
|
68348
|
+
}
|
|
68349
|
+
if (settings.lineWidth > 0)
|
|
68350
|
+
wrapRewrites(leaves, projection, options, settings.lineWidth, rewrites);
|
|
68351
|
+
const doc = document.textDocument;
|
|
68352
|
+
const source = doc.getText();
|
|
68353
|
+
const accepted = [];
|
|
68354
|
+
let lastEnd = -1;
|
|
68355
|
+
for (const rewrite of rewrites.sort((a2, b) => a2.offset - b.offset || a2.end - b.end)) {
|
|
68356
|
+
if (rewrite.offset < lastEnd)
|
|
68357
|
+
continue;
|
|
68358
|
+
if (source.slice(rewrite.offset, rewrite.end) === rewrite.newText)
|
|
68359
|
+
continue;
|
|
68360
|
+
if (intersectsProtected(protectedRanges, rewrite.offset, rewrite.end))
|
|
68361
|
+
continue;
|
|
68362
|
+
if (!withinRange(document, rewrite, range))
|
|
68363
|
+
continue;
|
|
68364
|
+
accepted.push(rewrite);
|
|
68365
|
+
lastEnd = rewrite.end;
|
|
68366
|
+
}
|
|
68367
|
+
return accepted.map((rewrite) => ({
|
|
68368
|
+
range: { start: doc.positionAt(rewrite.offset), end: doc.positionAt(rewrite.end) },
|
|
68369
|
+
newText: rewrite.newText
|
|
68370
|
+
}));
|
|
68371
|
+
}
|
|
68372
|
+
|
|
67305
68373
|
// ../language-server/out/src/services/formatter.js
|
|
67306
68374
|
var INDENT = Formatting.indent({ allowMore: true });
|
|
67307
68375
|
var DEDENT = Formatting.noIndent();
|
|
67308
68376
|
var NL = Formatting.newLine();
|
|
67309
|
-
var NL2 = Formatting.newLines(2);
|
|
67310
68377
|
var SPACE = Formatting.oneSpace();
|
|
67311
68378
|
var NO_SPACE = Formatting.noSpace();
|
|
67312
68379
|
var SysmlFormatter = class extends AbstractFormatter {
|
|
68380
|
+
services;
|
|
68381
|
+
/**
|
|
68382
|
+
* The settings in force for the request being served. Formatting is
|
|
68383
|
+
* synchronous from `formatDocument` through the AST pass, so a field is the
|
|
68384
|
+
* whole of the plumbing the base class does not offer.
|
|
68385
|
+
*/
|
|
68386
|
+
active = FORMATTER_DEFAULTS;
|
|
68387
|
+
keywordCache;
|
|
68388
|
+
constructor(services) {
|
|
68389
|
+
super();
|
|
68390
|
+
this.services = services;
|
|
68391
|
+
}
|
|
68392
|
+
// REQ-392, REQ-393 — Full-document formatting.
|
|
68393
|
+
formatDocument(document, params) {
|
|
68394
|
+
return this.formatWith(document, params.options, (options) => super.formatDocument(document, { ...params, options }));
|
|
68395
|
+
}
|
|
68396
|
+
// REQ-392, REQ-393 — Range formatting. A range that reaches into a preserved
|
|
68397
|
+
// element formats the rest of the range and leaves that element whole: the
|
|
68398
|
+
// base formatter narrows to the range, and the same filter then drops
|
|
68399
|
+
// whatever would have rewritten a frozen slice, so a partial rewrite is
|
|
68400
|
+
// impossible. The settings are resolved identically to the full-document
|
|
68401
|
+
// path, so the two cannot produce different style.
|
|
68402
|
+
formatDocumentRange(document, params) {
|
|
68403
|
+
return this.formatWith(document, params.options, (options) => super.formatDocumentRange(document, { ...params, options }), params.range);
|
|
68404
|
+
}
|
|
68405
|
+
// REQ-392, REQ-393 — Format-on-type formats the current line.
|
|
68406
|
+
formatDocumentOnType(document, params) {
|
|
68407
|
+
const line = { start: { line: params.position.line, character: 0 }, end: params.position };
|
|
68408
|
+
return this.formatWith(document, params.options, (options) => super.formatDocumentOnType(document, { ...params, options }), line);
|
|
68409
|
+
}
|
|
68410
|
+
/**
|
|
68411
|
+
* REQ-392, REQ-393 — One pipeline for all three entry points: resolve the
|
|
68412
|
+
* settings, run the base whitespace pass under them, drop whatever a
|
|
68413
|
+
* `sysml-format ignore` froze, then add the token-level rewrites the style
|
|
68414
|
+
* options ask for.
|
|
68415
|
+
*/
|
|
68416
|
+
formatWith(document, requested, base, range) {
|
|
68417
|
+
const { settings } = resolveFormatterOptions(requested);
|
|
68418
|
+
const options = effectiveFormattingOptions(requested, settings);
|
|
68419
|
+
this.active = settings;
|
|
68420
|
+
const protectedRanges = scanFormatDirectives(document).protectedRanges;
|
|
68421
|
+
const baseEdits = this.preserveIgnored(document, base(options), protectedRanges);
|
|
68422
|
+
const style = styleEdits({
|
|
68423
|
+
document,
|
|
68424
|
+
settings,
|
|
68425
|
+
options,
|
|
68426
|
+
baseEdits,
|
|
68427
|
+
protectedRanges,
|
|
68428
|
+
range,
|
|
68429
|
+
reservedKeywords: this.reservedKeywords()
|
|
68430
|
+
});
|
|
68431
|
+
return this.merge(document, baseEdits, style);
|
|
68432
|
+
}
|
|
68433
|
+
/**
|
|
68434
|
+
* REQ-392 — Drop every edit that reaches inside a preserved element.
|
|
68435
|
+
*
|
|
68436
|
+
* Langium only ever edits the whitespace BETWEEN two tokens, so an edit is
|
|
68437
|
+
* either wholly inside a preserved slice (dropped, leaving the slice
|
|
68438
|
+
* byte-for-byte identical, line endings included) or wholly outside it
|
|
68439
|
+
* (kept, so the element's own indentation and its surroundings are still
|
|
68440
|
+
* formatted).
|
|
68441
|
+
*/
|
|
68442
|
+
preserveIgnored(document, edits, ranges) {
|
|
68443
|
+
if (edits.length === 0 || ranges.length === 0)
|
|
68444
|
+
return edits;
|
|
68445
|
+
const text = document.textDocument;
|
|
68446
|
+
return edits.filter((edit) => !intersectsProtected(ranges, text.offsetAt(edit.range.start), text.offsetAt(edit.range.end)));
|
|
68447
|
+
}
|
|
68448
|
+
/**
|
|
68449
|
+
* REQ-393 — Combine the whitespace pass with the token rewrites. A token
|
|
68450
|
+
* rewrite wins where the two touch: `part def P { }` → `part def P;` has to
|
|
68451
|
+
* swallow the space the whitespace pass wanted to put before the brace.
|
|
68452
|
+
*/
|
|
68453
|
+
merge(document, baseEdits, style) {
|
|
68454
|
+
if (style.length === 0)
|
|
68455
|
+
return baseEdits;
|
|
68456
|
+
const text = document.textDocument;
|
|
68457
|
+
const spans = style.map((edit) => ({
|
|
68458
|
+
offset: text.offsetAt(edit.range.start),
|
|
68459
|
+
end: text.offsetAt(edit.range.end)
|
|
68460
|
+
}));
|
|
68461
|
+
const kept = baseEdits.filter((edit) => {
|
|
68462
|
+
const start = text.offsetAt(edit.range.start);
|
|
68463
|
+
const end = text.offsetAt(edit.range.end);
|
|
68464
|
+
return !spans.some((span) => start < span.end && end > span.offset);
|
|
68465
|
+
});
|
|
68466
|
+
return [...kept, ...style].sort((a2, b) => text.offsetAt(a2.range.start) - text.offsetAt(b.range.start));
|
|
68467
|
+
}
|
|
68468
|
+
/**
|
|
68469
|
+
* REQ-393 — Every keyword the grammar reserves. A quoted name may only lose
|
|
68470
|
+
* its quotes when the bare spelling would still be a name and not a keyword,
|
|
68471
|
+
* and the grammar is the only honest source for that list.
|
|
68472
|
+
*/
|
|
68473
|
+
reservedKeywords() {
|
|
68474
|
+
if (this.keywordCache)
|
|
68475
|
+
return this.keywordCache;
|
|
68476
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
68477
|
+
const grammar = this.services?.Grammar;
|
|
68478
|
+
if (grammar) {
|
|
68479
|
+
for (const node of ast_utils_exports.streamAllContents(grammar)) {
|
|
68480
|
+
if (node.$type === "Keyword") {
|
|
68481
|
+
const value = node.value;
|
|
68482
|
+
if (value && /^[_a-zA-Z]\w*$/u.test(value))
|
|
68483
|
+
keywords.add(value);
|
|
68484
|
+
}
|
|
68485
|
+
}
|
|
68486
|
+
}
|
|
68487
|
+
this.keywordCache = keywords;
|
|
68488
|
+
return keywords;
|
|
68489
|
+
}
|
|
68490
|
+
/**
|
|
68491
|
+
* REQ-392 — Keep the indentation level a comment interrupts.
|
|
68492
|
+
*
|
|
68493
|
+
* Langium 3.5's `createTextEdit` returns early for a hidden node and never
|
|
68494
|
+
* applies the formatting action's indentation move to the running context.
|
|
68495
|
+
* `{` carries the block's `indent` action, so a comment sitting between `{`
|
|
68496
|
+
* and the first member — which is exactly where a `sysml-format ignore`
|
|
68497
|
+
* directive goes — dropped the whole block back to column 0. Applying the
|
|
68498
|
+
* move ourselves restores the level a composite node would have kept, and
|
|
68499
|
+
* leaves every other hidden-node path untouched.
|
|
68500
|
+
*/
|
|
68501
|
+
createTextEdit(a2, b, formatting, context) {
|
|
68502
|
+
const edits = super.createTextEdit(a2, b, formatting, context);
|
|
68503
|
+
if (!b.hidden)
|
|
68504
|
+
return edits;
|
|
68505
|
+
const startRange = {
|
|
68506
|
+
start: { line: b.range.start.line, character: 0 },
|
|
68507
|
+
end: b.range.start
|
|
68508
|
+
};
|
|
68509
|
+
context.indentation += this.findFittingMove(startRange, formatting.moves, context)?.tabs ?? 0;
|
|
68510
|
+
return edits;
|
|
68511
|
+
}
|
|
67313
68512
|
// REQ-259 — Dispatch formatting from AST node kind
|
|
67314
68513
|
format(node) {
|
|
67315
68514
|
if (isDocument(node)) {
|
|
@@ -67417,13 +68616,28 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67417
68616
|
f.keyword(first2).append(SPACE);
|
|
67418
68617
|
f.keyword(second).prepend(SPACE);
|
|
67419
68618
|
}
|
|
68619
|
+
/**
|
|
68620
|
+
* REQ-393 — Top-level element separation is decided HERE and nowhere else.
|
|
68621
|
+
*
|
|
68622
|
+
* The first element gets `noSpace`, which erases leading blank lines rather
|
|
68623
|
+
* than adding two of them: the document-level rule and the element's own
|
|
68624
|
+
* `prepend` used to fire at the same offset, and two insertions at one
|
|
68625
|
+
* position stack, so every file that began with `package` gained blank
|
|
68626
|
+
* lines at the top on every format. `blankLines` decides the rest.
|
|
68627
|
+
*/
|
|
67420
68628
|
fmtDocument(node) {
|
|
67421
68629
|
const f = this.getNodeFormatter(node);
|
|
67422
|
-
|
|
68630
|
+
const separator = Formatting.newLines(1 + this.active.blankLines);
|
|
68631
|
+
for (let index = 0; index < node.elements.length; index++) {
|
|
68632
|
+
f.property("elements", index).prepend(index === 0 ? NO_SPACE : separator);
|
|
68633
|
+
}
|
|
67423
68634
|
}
|
|
67424
68635
|
fmtBlock(node, keyword) {
|
|
67425
68636
|
const f = this.getNodeFormatter(node);
|
|
67426
|
-
|
|
68637
|
+
if (!isDocument(node.$container)) {
|
|
68638
|
+
f.keyword(keyword).prepend(Formatting.newLines(1 + this.active.blankLines));
|
|
68639
|
+
}
|
|
68640
|
+
f.keyword(keyword).append(SPACE);
|
|
67427
68641
|
this.fmtBraces(f);
|
|
67428
68642
|
}
|
|
67429
68643
|
// REQ-355 — Filtered imports keep the `[@Meta]` condition tight against the path
|
|
@@ -67443,7 +68657,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67443
68657
|
// REQ-258, REQ-260 — Format one element per line with canonical keyword spacing
|
|
67444
68658
|
fmtElement(node, keyword) {
|
|
67445
68659
|
const f = this.getNodeFormatter(node);
|
|
67446
|
-
|
|
68660
|
+
if (!isDocument(node.$container))
|
|
68661
|
+
f.keyword(keyword).prepend(NL);
|
|
68662
|
+
f.keyword(keyword).append(SPACE);
|
|
67447
68663
|
if (node.isDef) {
|
|
67448
68664
|
f.keyword("def").prepend(SPACE).append(SPACE);
|
|
67449
68665
|
}
|
|
@@ -67457,7 +68673,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67457
68673
|
// REQ-260 — Keep multi-word `verification case` keyword phrase together
|
|
67458
68674
|
fmtVerificationCase(node) {
|
|
67459
68675
|
const f = this.getNodeFormatter(node);
|
|
67460
|
-
|
|
68676
|
+
if (!isDocument(node.$container))
|
|
68677
|
+
f.keyword("verification").prepend(NL);
|
|
68678
|
+
f.keyword("verification").append(SPACE);
|
|
67461
68679
|
f.keyword("case").append(SPACE);
|
|
67462
68680
|
if (node.isDef)
|
|
67463
68681
|
f.keyword("def").append(SPACE);
|
|
@@ -67468,7 +68686,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67468
68686
|
// REQ-260 — Keep multi-word `analysis case` keyword phrase together
|
|
67469
68687
|
fmtAnalysisCase(node) {
|
|
67470
68688
|
const f = this.getNodeFormatter(node);
|
|
67471
|
-
|
|
68689
|
+
if (!isDocument(node.$container))
|
|
68690
|
+
f.keyword("analysis").prepend(NL);
|
|
68691
|
+
f.keyword("analysis").append(SPACE);
|
|
67472
68692
|
f.keyword("case").append(SPACE);
|
|
67473
68693
|
if (node.isDef)
|
|
67474
68694
|
f.keyword("def").append(SPACE);
|
|
@@ -67479,7 +68699,9 @@ var SysmlFormatter = class extends AbstractFormatter {
|
|
|
67479
68699
|
// REQ-260 — Keep multi-word `use case` keyword phrase together
|
|
67480
68700
|
fmtUseCase(node) {
|
|
67481
68701
|
const f = this.getNodeFormatter(node);
|
|
67482
|
-
|
|
68702
|
+
if (!isDocument(node.$container))
|
|
68703
|
+
f.keyword("use").prepend(NL);
|
|
68704
|
+
f.keyword("use").append(SPACE);
|
|
67483
68705
|
f.keyword("case").append(SPACE);
|
|
67484
68706
|
if (node.isDef)
|
|
67485
68707
|
f.keyword("def").append(SPACE);
|
|
@@ -67668,14 +68890,87 @@ function typeAnchor(node, refText) {
|
|
|
67668
68890
|
}
|
|
67669
68891
|
return void 0;
|
|
67670
68892
|
}
|
|
68893
|
+
var EXPLICIT_SPECIALIZATION_KINDS = /* @__PURE__ */ new Set([
|
|
68894
|
+
":>",
|
|
68895
|
+
":>>",
|
|
68896
|
+
"::>",
|
|
68897
|
+
"=>",
|
|
68898
|
+
"specializes",
|
|
68899
|
+
"subsets",
|
|
68900
|
+
"redefines",
|
|
68901
|
+
"references",
|
|
68902
|
+
"crosses",
|
|
68903
|
+
"conjugates"
|
|
68904
|
+
]);
|
|
68905
|
+
var REDEFINITION_KINDS2 = /* @__PURE__ */ new Set([":>>", "redefines"]);
|
|
68906
|
+
var IMPLICIT_BASES = Object.freeze({
|
|
68907
|
+
PartDecl: { def: "Parts::Part", usage: "Parts::parts" },
|
|
68908
|
+
ItemDecl: { def: "Items::Item", usage: "Items::items" },
|
|
68909
|
+
PortDecl: { def: "Ports::Port", usage: "Ports::ports" },
|
|
68910
|
+
ActionDecl: { def: "Actions::Action", usage: "Actions::actions" },
|
|
68911
|
+
StateDecl: { def: "States::StateAction", usage: "States::stateActions" },
|
|
68912
|
+
ConnectionDecl: { def: "Connections::Connection", usage: "Connections::connections" },
|
|
68913
|
+
InterfaceDecl: { def: "Interfaces::Interface", usage: "Interfaces::interfaces" },
|
|
68914
|
+
CaseDecl: { def: "Cases::Case", usage: "Cases::cases" },
|
|
68915
|
+
UseCaseDecl: { def: "UseCases::UseCase", usage: "UseCases::useCases" },
|
|
68916
|
+
AnalysisCaseDecl: { def: "AnalysisCases::AnalysisCase", usage: "AnalysisCases::analysisCases" },
|
|
68917
|
+
VerificationCaseDecl: { def: "VerificationCases::VerificationCase", usage: "VerificationCases::verificationCases" },
|
|
68918
|
+
ViewDecl: { def: "Views::View", usage: "Views::views" },
|
|
68919
|
+
ViewpointDecl: { def: "Views::ViewpointCheck", usage: "Views::viewpointChecks" },
|
|
68920
|
+
RenderingDecl: { def: "Views::Rendering", usage: "Views::renderings" },
|
|
68921
|
+
RequirementDecl: { def: "Requirements::RequirementCheck", usage: "Requirements::requirementChecks" },
|
|
68922
|
+
ConcernDecl: { def: "Requirements::ConcernCheck", usage: "Requirements::concernChecks" },
|
|
68923
|
+
ConstraintDecl: { def: "Constraints::ConstraintCheck", usage: "Constraints::constraintChecks" },
|
|
68924
|
+
CalcDecl: { def: "Calculations::Calculation", usage: "Calculations::calculations" },
|
|
68925
|
+
AllocationDecl: { def: "Allocations::Allocation", usage: "Allocations::allocations" },
|
|
68926
|
+
AttributeDecl: { def: "Attributes::AttributeValue", usage: "Attributes::attributeValues" },
|
|
68927
|
+
OccurrenceDecl: { def: "Occurrences::Occurrence", usage: "Occurrences::occurrences" },
|
|
68928
|
+
MetadataDecl: { def: "Metadata::MetadataItem", usage: "Metadata::metadataItems" }
|
|
68929
|
+
});
|
|
68930
|
+
function markdown(value) {
|
|
68931
|
+
return { kind: import_vscode_languageserver20.MarkupKind.Markdown, value };
|
|
68932
|
+
}
|
|
68933
|
+
function allRelationships2(node) {
|
|
68934
|
+
return [...node.preRelationships ?? [], ...node.relationships ?? []];
|
|
68935
|
+
}
|
|
68936
|
+
function hasExplicitSpecialization(node) {
|
|
68937
|
+
if (node.typing?.type?.$refText)
|
|
68938
|
+
return true;
|
|
68939
|
+
return allRelationships2(node).some((rel) => rel.kind && EXPLICIT_SPECIALIZATION_KINDS.has(rel.kind));
|
|
68940
|
+
}
|
|
68941
|
+
function keywordLeaf(node, keyword) {
|
|
68942
|
+
const cst = node.$cstNode;
|
|
68943
|
+
if (!cst)
|
|
68944
|
+
return void 0;
|
|
68945
|
+
return cst_utils_exports.flattenCst(cst).find((leaf) => leaf.text === keyword && !leaf.hidden);
|
|
68946
|
+
}
|
|
68947
|
+
function descriptionRange(description) {
|
|
68948
|
+
const segment = description.nameSegment ?? description.selectionSegment;
|
|
68949
|
+
return segment?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
|
|
68950
|
+
}
|
|
67671
68951
|
var SysmlInlayHintProvider = class {
|
|
68952
|
+
services;
|
|
68953
|
+
/**
|
|
68954
|
+
* Resolved implicit bases, keyed by the qualified name in
|
|
68955
|
+
* {@link IMPLICIT_BASES}. The standard library does not change once it is
|
|
68956
|
+
* loaded, so this is built at most once for real; while it is still empty —
|
|
68957
|
+
* before the library has been indexed — the rebuild is over a tiny index and
|
|
68958
|
+
* costs nothing, which is what keeps the hint path free of a full index scan
|
|
68959
|
+
* on every request.
|
|
68960
|
+
*/
|
|
68961
|
+
baseCache = /* @__PURE__ */ new Map();
|
|
68962
|
+
constructor(services) {
|
|
68963
|
+
this.services = services;
|
|
68964
|
+
}
|
|
67672
68965
|
getInlayHints(document, _params) {
|
|
67673
68966
|
const root3 = document.parseResult?.value;
|
|
67674
|
-
if (!root3)
|
|
68967
|
+
if (!root3 || !sysmlInlayHintSettings.enabled)
|
|
67675
68968
|
return void 0;
|
|
68969
|
+
const settings = sysmlInlayHintSettings;
|
|
67676
68970
|
const hints = [];
|
|
67677
68971
|
for (const node of allAstNodes(root3)) {
|
|
67678
|
-
|
|
68972
|
+
const decl = node;
|
|
68973
|
+
if (settings.multiplicity && (isPartDecl(node) || isPortDecl(node) || isAttributeDecl(node)) && !decl.isDef && !decl.multiplicity && node.$cstNode && decl.name) {
|
|
67679
68974
|
hints.push({
|
|
67680
68975
|
position: multiplicityAnchor(node.$cstNode),
|
|
67681
68976
|
label: " [1]",
|
|
@@ -67683,7 +68978,65 @@ var SysmlInlayHintProvider = class {
|
|
|
67683
68978
|
paddingLeft: true
|
|
67684
68979
|
});
|
|
67685
68980
|
}
|
|
67686
|
-
if (!
|
|
68981
|
+
if (settings.visibility && isImport(node) && !decl.visibility) {
|
|
68982
|
+
const keyword = keywordLeaf(node, "import");
|
|
68983
|
+
if (keyword) {
|
|
68984
|
+
const at = keyword.range.start;
|
|
68985
|
+
hints.push({
|
|
68986
|
+
position: at,
|
|
68987
|
+
label: "private",
|
|
68988
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
68989
|
+
paddingRight: true,
|
|
68990
|
+
tooltip: markdown("An `import` that declares no visibility is **private**: the names it brings in are not re-exported from this namespace."),
|
|
68991
|
+
// Writing the modifier is mechanically safe — `private`
|
|
68992
|
+
// is exactly what the grammar accepts here, and the text
|
|
68993
|
+
// then says what it already meant.
|
|
68994
|
+
textEdits: [{ range: { start: at, end: at }, newText: "private " }]
|
|
68995
|
+
});
|
|
68996
|
+
}
|
|
68997
|
+
}
|
|
68998
|
+
if (settings.modifiers && isAttributeDecl(node) && !decl.isDef && !(decl.modifiers ?? []).includes("ref")) {
|
|
68999
|
+
const keyword = keywordLeaf(node, "attribute");
|
|
69000
|
+
if (keyword) {
|
|
69001
|
+
const at = keyword.range.start;
|
|
69002
|
+
hints.push({
|
|
69003
|
+
position: at,
|
|
69004
|
+
label: "ref",
|
|
69005
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
69006
|
+
paddingRight: true,
|
|
69007
|
+
tooltip: markdown("An attribute usage is always referential (`ref`); it is never a composite feature of its owner."),
|
|
69008
|
+
textEdits: [{ range: { start: at, end: at }, newText: "ref " }]
|
|
69009
|
+
});
|
|
69010
|
+
}
|
|
69011
|
+
}
|
|
69012
|
+
if (settings.specialization && node.$cstNode && decl.name && !hasExplicitSpecialization(decl)) {
|
|
69013
|
+
const entry = IMPLICIT_BASES[node.$type];
|
|
69014
|
+
const qualified = entry ? decl.isDef ? entry.def : entry.usage : void 0;
|
|
69015
|
+
const description = qualified ? this.resolveImplicitBase(qualified, decl.isDef !== true) : void 0;
|
|
69016
|
+
if (qualified && description) {
|
|
69017
|
+
hints.push({
|
|
69018
|
+
position: multiplicityAnchor(node.$cstNode),
|
|
69019
|
+
label: [{
|
|
69020
|
+
value: ` :> ${qualified}`,
|
|
69021
|
+
location: {
|
|
69022
|
+
uri: description.documentUri.toString(),
|
|
69023
|
+
range: descriptionRange(description)
|
|
69024
|
+
}
|
|
69025
|
+
}],
|
|
69026
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
69027
|
+
paddingLeft: true,
|
|
69028
|
+
tooltip: markdown(`Implicitly specializes \`${qualified}\` from the standard library.`)
|
|
69029
|
+
});
|
|
69030
|
+
}
|
|
69031
|
+
}
|
|
69032
|
+
if (settings.redefinition)
|
|
69033
|
+
hints.push(...this.parameterRedefinitionHints(decl));
|
|
69034
|
+
if (settings.effectiveNames) {
|
|
69035
|
+
const effective = effectiveNameHint(decl);
|
|
69036
|
+
if (effective)
|
|
69037
|
+
hints.push(effective);
|
|
69038
|
+
}
|
|
69039
|
+
if (!settings.dimension)
|
|
67687
69040
|
continue;
|
|
67688
69041
|
if (isNumericPrimary(node)) {
|
|
67689
69042
|
const symbol = node.unit?.$refText;
|
|
@@ -67699,7 +69052,7 @@ var SysmlInlayHintProvider = class {
|
|
|
67699
69052
|
continue;
|
|
67700
69053
|
}
|
|
67701
69054
|
if (isAttributeDecl(node)) {
|
|
67702
|
-
const refText =
|
|
69055
|
+
const refText = decl.typing?.type?.$refText;
|
|
67703
69056
|
const label = refText ? quantityTypeDimensionLabel(refText) : void 0;
|
|
67704
69057
|
const position = refText ? typeAnchor(node, refText) : void 0;
|
|
67705
69058
|
if (label && position) {
|
|
@@ -67714,7 +69067,102 @@ var SysmlInlayHintProvider = class {
|
|
|
67714
69067
|
}
|
|
67715
69068
|
return hints;
|
|
67716
69069
|
}
|
|
69070
|
+
/**
|
|
69071
|
+
* REQ-395 — Positional parameter correspondence.
|
|
69072
|
+
*
|
|
69073
|
+
* A usage typed by a definition redefines that definition's directed
|
|
69074
|
+
* parameters by position ([OMG SysML v2 Part 1 §7.16.4] — the same rule
|
|
69075
|
+
* `SSM025` checks). The definition has to be RESOLVED for the hint to exist,
|
|
69076
|
+
* and a parameter that already redefines something explicitly is left alone.
|
|
69077
|
+
*/
|
|
69078
|
+
parameterRedefinitionHints(node) {
|
|
69079
|
+
const definition = node.typing?.type?.ref;
|
|
69080
|
+
if (!definition || node.isDef)
|
|
69081
|
+
return [];
|
|
69082
|
+
const mine = directedParameters(node);
|
|
69083
|
+
if (mine.length === 0)
|
|
69084
|
+
return [];
|
|
69085
|
+
const theirs = directedParameters(definition);
|
|
69086
|
+
const hints = [];
|
|
69087
|
+
for (let index = 0; index < mine.length && index < theirs.length; index++) {
|
|
69088
|
+
const parameter = mine[index].node;
|
|
69089
|
+
if (!parameter.name || !parameter.$cstNode)
|
|
69090
|
+
continue;
|
|
69091
|
+
if (allRelationships2(parameter).some((rel) => rel.kind && REDEFINITION_KINDS2.has(rel.kind)))
|
|
69092
|
+
continue;
|
|
69093
|
+
const target = theirs[index].node;
|
|
69094
|
+
if (!target.name || target.name === parameter.name)
|
|
69095
|
+
continue;
|
|
69096
|
+
const uri = ast_utils_exports.getDocument(target).uri.toString();
|
|
69097
|
+
const range = target.$cstNode?.range ?? { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } };
|
|
69098
|
+
hints.push({
|
|
69099
|
+
position: multiplicityAnchor(parameter.$cstNode),
|
|
69100
|
+
label: [{ value: ` :>> ${target.name}`, location: { uri, range } }],
|
|
69101
|
+
kind: import_vscode_languageserver20.InlayHintKind.Type,
|
|
69102
|
+
paddingLeft: true,
|
|
69103
|
+
tooltip: markdown(`Parameter ${index + 1} implicitly redefines \`${target.name}\` of \`${node.typing?.type?.$refText ?? "its definition"}\`.`)
|
|
69104
|
+
});
|
|
69105
|
+
}
|
|
69106
|
+
return hints;
|
|
69107
|
+
}
|
|
69108
|
+
/**
|
|
69109
|
+
* REQ-395 — Find one implicit base in the index.
|
|
69110
|
+
*
|
|
69111
|
+
* A description whose name is the full qualified spelling wins outright. The
|
|
69112
|
+
* index otherwise keys library symbols by their simple name, so the fallback
|
|
69113
|
+
* requires BOTH a standard-library document and the right side of the
|
|
69114
|
+
* definition/usage split, which the precomputed index records — that is what
|
|
69115
|
+
* keeps `Parts::Part` from matching a `part Part` somewhere else.
|
|
69116
|
+
*/
|
|
69117
|
+
resolveImplicitBase(qualified, wantUsage) {
|
|
69118
|
+
const cached = this.baseCache.get(qualified);
|
|
69119
|
+
if (cached)
|
|
69120
|
+
return cached;
|
|
69121
|
+
const index = this.services?.shared.workspace.IndexManager;
|
|
69122
|
+
if (!index)
|
|
69123
|
+
return void 0;
|
|
69124
|
+
const simple = lastSegment3(qualified);
|
|
69125
|
+
let fallback;
|
|
69126
|
+
for (const description of index.allElements()) {
|
|
69127
|
+
if (description.name === qualified) {
|
|
69128
|
+
this.baseCache.set(qualified, description);
|
|
69129
|
+
return description;
|
|
69130
|
+
}
|
|
69131
|
+
if (fallback || description.name !== simple)
|
|
69132
|
+
continue;
|
|
69133
|
+
const uri = description.documentUri instanceof URI2 ? description.documentUri : URI2.parse(String(description.documentUri));
|
|
69134
|
+
if (!isStandardLibraryUri(uri))
|
|
69135
|
+
continue;
|
|
69136
|
+
if (description.isUsage === true !== wantUsage)
|
|
69137
|
+
continue;
|
|
69138
|
+
fallback = description;
|
|
69139
|
+
}
|
|
69140
|
+
if (fallback)
|
|
69141
|
+
this.baseCache.set(qualified, fallback);
|
|
69142
|
+
return fallback;
|
|
69143
|
+
}
|
|
67717
69144
|
};
|
|
69145
|
+
function effectiveNameHint(node) {
|
|
69146
|
+
if (node.name || node.shortName?.name || !node.$cstNode)
|
|
69147
|
+
return void 0;
|
|
69148
|
+
const redefinition = allRelationships2(node).find((rel) => rel.kind && REDEFINITION_KINDS2.has(rel.kind) && (rel.targets?.length ?? 0) > 0);
|
|
69149
|
+
const target = redefinition?.targets?.[0];
|
|
69150
|
+
if (!target)
|
|
69151
|
+
return void 0;
|
|
69152
|
+
const effective = lastSegment3(target);
|
|
69153
|
+
if (!effective)
|
|
69154
|
+
return void 0;
|
|
69155
|
+
const operator = cst_utils_exports.flattenCst(node.$cstNode).find((leaf) => !leaf.hidden && redefinition?.kind !== void 0 && leaf.text === redefinition.kind);
|
|
69156
|
+
if (!operator)
|
|
69157
|
+
return void 0;
|
|
69158
|
+
return {
|
|
69159
|
+
position: operator.range.start,
|
|
69160
|
+
label: effective,
|
|
69161
|
+
kind: import_vscode_languageserver20.InlayHintKind.Parameter,
|
|
69162
|
+
paddingRight: true,
|
|
69163
|
+
tooltip: markdown(`Effective name: \`${effective}\`, taken from the feature this one redefines.`)
|
|
69164
|
+
};
|
|
69165
|
+
}
|
|
67718
69166
|
|
|
67719
69167
|
// ../language-server/out/src/services/requirement-eval.js
|
|
67720
69168
|
var UNRESOLVED = { kind: "unresolved" };
|
|
@@ -68923,9 +70371,13 @@ var SysmlLspModule = {
|
|
|
68923
70371
|
// Epic G
|
|
68924
70372
|
RenameProvider: (services) => new SysmlRenameProvider(services),
|
|
68925
70373
|
CodeActionProvider: (services) => new SysmlCodeActionProvider(services),
|
|
68926
|
-
|
|
70374
|
+
// REQ-393 — the formatter reads the grammar's keyword set for the
|
|
70375
|
+
// quoted-name decision, so it is built with services.
|
|
70376
|
+
Formatter: (services) => new SysmlFormatter(services),
|
|
68927
70377
|
SemanticTokenProvider: (services) => new SysmlSemanticTokenProvider(services),
|
|
68928
|
-
|
|
70378
|
+
// REQ-395 — the provider resolves implicit standard-library bases
|
|
70379
|
+
// through the workspace/library index, so it is built with services.
|
|
70380
|
+
InlayHintProvider: (services) => new SysmlInlayHintProvider(services),
|
|
68929
70381
|
CodeLensProvider: (services) => new SysmlCodeLensProvider(services),
|
|
68930
70382
|
DocumentHighlightProvider: (services) => new SysmlDocumentHighlightProvider(services),
|
|
68931
70383
|
DefinitionProvider: (services) => new SysmlDefinitionProvider(services),
|
|
@@ -70024,7 +71476,7 @@ async function runValidation(command) {
|
|
|
70024
71476
|
}
|
|
70025
71477
|
|
|
70026
71478
|
// src/main.ts
|
|
70027
|
-
var VERSION2 = true ? "0.
|
|
71479
|
+
var VERSION2 = true ? "0.17.0" : "dev";
|
|
70028
71480
|
async function main(argv) {
|
|
70029
71481
|
const command = parseArgs(argv);
|
|
70030
71482
|
if (command.kind === "help") {
|