@likec4/language-server 1.20.3 → 1.21.1
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/README.md +1 -1
- package/dist/ast.d.ts +16 -0
- package/dist/ast.js +42 -10
- package/dist/bundled.mjs +2641 -2876
- package/dist/formatting/LikeC4Formatter.js +6 -3
- package/dist/generated/ast.d.ts +76 -24
- package/dist/generated/ast.js +103 -25
- package/dist/generated/grammar.js +1 -1
- package/dist/generated-lib/icons.js +1 -0
- package/dist/lsp/CompletionProvider.js +2 -1
- package/dist/lsp/SemanticTokenProvider.js +50 -2
- package/dist/model/model-builder.js +57 -3
- package/dist/model/model-parser.d.ts +6 -0
- package/dist/model/model-parser.js +1 -0
- package/dist/model/parser/Base.js +11 -5
- package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
- package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
- package/dist/model/parser/DeploymentViewParser.js +3 -0
- package/dist/model/parser/FqnRefParser.d.ts +1 -0
- package/dist/model/parser/FqnRefParser.js +10 -0
- package/dist/model/parser/GlobalsParser.d.ts +1 -0
- package/dist/model/parser/ModelParser.d.ts +2 -1
- package/dist/model/parser/ModelParser.js +26 -1
- package/dist/model/parser/PredicatesParser.js +19 -1
- package/dist/model/parser/ViewsParser.d.ts +1 -0
- package/dist/references/scope-provider.d.ts +1 -1
- package/dist/references/scope-provider.js +1 -1
- package/dist/test/testServices.d.ts +1 -0
- package/dist/test/testServices.js +8 -0
- package/dist/utils/elementRef.d.ts +3 -3
- package/dist/validation/index.d.ts +1 -1
- package/package.json +13 -15
- package/src/ast.ts +55 -9
- package/src/formatting/LikeC4Formatter.ts +7 -1
- package/src/generated/ast.ts +200 -49
- package/src/generated/grammar.ts +1 -1
- package/src/generated-lib/icons.ts +1 -0
- package/src/like-c4.langium +45 -7
- package/src/lsp/CompletionProvider.ts +1 -0
- package/src/lsp/SemanticTokenProvider.ts +56 -1
- package/src/model/model-builder.ts +65 -6
- package/src/model/model-parser.ts +1 -0
- package/src/model/parser/Base.ts +11 -5
- package/src/model/parser/DeploymentViewParser.ts +3 -0
- package/src/model/parser/FqnRefParser.ts +11 -0
- package/src/model/parser/ModelParser.ts +30 -1
- package/src/model/parser/PredicatesParser.ts +19 -1
- package/src/references/scope-provider.ts +9 -9
- package/src/test/testServices.ts +18 -9
- package/src/utils/elementRef.ts +3 -3
|
@@ -134,6 +134,54 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
134
134
|
});
|
|
135
135
|
return "prune";
|
|
136
136
|
}
|
|
137
|
+
if (ast.isGlobalStyleGroup(node) || ast.isGlobalStyle(node)) {
|
|
138
|
+
acceptor({
|
|
139
|
+
node,
|
|
140
|
+
property: "id",
|
|
141
|
+
type: SemanticTokenTypes.variable,
|
|
142
|
+
modifier: [
|
|
143
|
+
SemanticTokenModifiers.definition,
|
|
144
|
+
SemanticTokenModifiers.readonly
|
|
145
|
+
]
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (ast.isViewRuleGlobalStyle(node)) {
|
|
150
|
+
acceptor({
|
|
151
|
+
node,
|
|
152
|
+
property: "style",
|
|
153
|
+
type: SemanticTokenTypes.variable,
|
|
154
|
+
modifier: [
|
|
155
|
+
SemanticTokenModifiers.definition,
|
|
156
|
+
SemanticTokenModifiers.readonly
|
|
157
|
+
]
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (ast.isGlobalPredicateGroup(node) || ast.isGlobalDynamicPredicateGroup(node)) {
|
|
162
|
+
acceptor({
|
|
163
|
+
node,
|
|
164
|
+
property: "name",
|
|
165
|
+
type: SemanticTokenTypes.variable,
|
|
166
|
+
modifier: [
|
|
167
|
+
SemanticTokenModifiers.definition,
|
|
168
|
+
SemanticTokenModifiers.readonly
|
|
169
|
+
]
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (ast.isViewRuleGlobalPredicateRef(node)) {
|
|
174
|
+
acceptor({
|
|
175
|
+
node,
|
|
176
|
+
property: "predicate",
|
|
177
|
+
type: SemanticTokenTypes.variable,
|
|
178
|
+
modifier: [
|
|
179
|
+
SemanticTokenModifiers.definition,
|
|
180
|
+
SemanticTokenModifiers.readonly
|
|
181
|
+
]
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
137
185
|
if (ast.isElementTagExpression(node) && isTruthy(node.tag)) {
|
|
138
186
|
acceptor({
|
|
139
187
|
node,
|
|
@@ -155,7 +203,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
155
203
|
});
|
|
156
204
|
return !node.parent ? "prune" : void 0;
|
|
157
205
|
}
|
|
158
|
-
if (ast.isElementRef(node) || ast.
|
|
206
|
+
if (ast.isElementRef(node) || ast.isStrictFqnElementRef(node)) {
|
|
159
207
|
acceptor({
|
|
160
208
|
node,
|
|
161
209
|
property: "el",
|
|
@@ -254,7 +302,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
254
302
|
}
|
|
255
303
|
return "prune";
|
|
256
304
|
}
|
|
257
|
-
if (ast.isColorProperty(node) || ast.isShapeProperty(node) || ast.isArrowProperty(node) || ast.isLineProperty(node) || ast.isBorderProperty(node)) {
|
|
305
|
+
if (ast.isColorProperty(node) || ast.isShapeProperty(node) || ast.isArrowProperty(node) || ast.isLineProperty(node) || ast.isBorderProperty(node) || ast.isSizeProperty(node)) {
|
|
258
306
|
acceptor({
|
|
259
307
|
node,
|
|
260
308
|
property: "key",
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
filter,
|
|
15
15
|
flatMap,
|
|
16
16
|
groupBy,
|
|
17
|
+
hasAtLeast,
|
|
17
18
|
indexBy,
|
|
18
19
|
isBoolean,
|
|
19
20
|
isDefined,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
reduce,
|
|
32
33
|
reverse,
|
|
33
34
|
sort,
|
|
35
|
+
unique,
|
|
34
36
|
values
|
|
35
37
|
} from "remeda";
|
|
36
38
|
import { isParsedLikeC4LangiumDocument } from "../ast.js";
|
|
@@ -97,7 +99,10 @@ function buildModel(services, docs) {
|
|
|
97
99
|
icon,
|
|
98
100
|
opacity,
|
|
99
101
|
border,
|
|
100
|
-
|
|
102
|
+
size,
|
|
103
|
+
multiple,
|
|
104
|
+
padding,
|
|
105
|
+
textSize
|
|
101
106
|
},
|
|
102
107
|
id,
|
|
103
108
|
kind,
|
|
@@ -120,6 +125,9 @@ function buildModel(services, docs) {
|
|
|
120
125
|
border ??= __kind.style.border;
|
|
121
126
|
technology ??= __kind.technology;
|
|
122
127
|
multiple ??= __kind.style.multiple;
|
|
128
|
+
size ??= __kind.style.size;
|
|
129
|
+
padding ??= __kind.style.padding;
|
|
130
|
+
textSize ??= __kind.style.textSize;
|
|
123
131
|
return {
|
|
124
132
|
...color && { color },
|
|
125
133
|
...shape && { shape },
|
|
@@ -128,6 +136,9 @@ function buildModel(services, docs) {
|
|
|
128
136
|
...__kind.notation && { notation: __kind.notation },
|
|
129
137
|
style: {
|
|
130
138
|
...border && { border },
|
|
139
|
+
...size && { size },
|
|
140
|
+
...padding && { padding },
|
|
141
|
+
...textSize && { textSize },
|
|
131
142
|
...isBoolean(multiple) && { multiple },
|
|
132
143
|
...isNumber(opacity) && { opacity }
|
|
133
144
|
},
|
|
@@ -145,9 +156,52 @@ function buildModel(services, docs) {
|
|
|
145
156
|
return null;
|
|
146
157
|
};
|
|
147
158
|
}
|
|
159
|
+
const elementsExtendData = /* @__PURE__ */ new Map();
|
|
160
|
+
function mergeAllC4ExtendElements(doc) {
|
|
161
|
+
for (const el of doc.c4ExtendElements) {
|
|
162
|
+
let links = el.links ? resolveLinks(doc, el.links) : null;
|
|
163
|
+
const existing = elementsExtendData.get(el.id);
|
|
164
|
+
if (existing) {
|
|
165
|
+
links = existing.links ? [...existing.links, ...links ?? []] : links;
|
|
166
|
+
let tags = [...existing.tags ?? [], ...el.tags ?? []];
|
|
167
|
+
if (!hasAtLeast(tags, 1)) {
|
|
168
|
+
tags = null;
|
|
169
|
+
}
|
|
170
|
+
elementsExtendData.set(el.id, {
|
|
171
|
+
tags,
|
|
172
|
+
links,
|
|
173
|
+
metadata: { ...existing.metadata, ...el.metadata }
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
elementsExtendData.set(el.id, {
|
|
177
|
+
tags: el.tags ?? null,
|
|
178
|
+
links,
|
|
179
|
+
metadata: { ...el.metadata }
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function withExtendElementData(el) {
|
|
185
|
+
const extendData = elementsExtendData.get(el.id);
|
|
186
|
+
if (extendData) {
|
|
187
|
+
const links = [...el.links ?? [], ...extendData.links ?? []];
|
|
188
|
+
const tags = unique([...el.tags ?? [], ...extendData.tags ?? []]);
|
|
189
|
+
const metadata = { ...el.metadata, ...extendData.metadata };
|
|
190
|
+
return {
|
|
191
|
+
...el,
|
|
192
|
+
tags: hasAtLeast(tags, 1) ? tags : null,
|
|
193
|
+
links: hasAtLeast(links, 1) ? links : null,
|
|
194
|
+
...!isEmpty(metadata) && { metadata }
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return el;
|
|
198
|
+
}
|
|
148
199
|
const elements = pipe(
|
|
149
200
|
docs,
|
|
150
|
-
flatMap((d) =>
|
|
201
|
+
flatMap((d) => {
|
|
202
|
+
mergeAllC4ExtendElements(d);
|
|
203
|
+
return map(d.c4Elements, toModelElement(d));
|
|
204
|
+
}),
|
|
151
205
|
filter(isTruthy),
|
|
152
206
|
// sort from root elements to nested, so that parent is always present
|
|
153
207
|
// Import to preserve the order from the source
|
|
@@ -159,7 +213,7 @@ function buildModel(services, docs) {
|
|
|
159
213
|
logWarnError(`No parent found for ${el.id}`);
|
|
160
214
|
return acc;
|
|
161
215
|
}
|
|
162
|
-
acc[el.id] = el;
|
|
216
|
+
acc[el.id] = withExtendElementData(el);
|
|
163
217
|
return acc;
|
|
164
218
|
},
|
|
165
219
|
{}
|
|
@@ -59,6 +59,7 @@ declare const DocumentParserFromMixins: {
|
|
|
59
59
|
parseFqnRef(astNode: import("../generated/ast").FqnRef): invariant;
|
|
60
60
|
parseFqnExpr(astNode: import("../generated/ast").FqnExpr): invariant;
|
|
61
61
|
parseFqnRefExpr(astNode: import("../generated/ast").FqnRefExpr): invariant;
|
|
62
|
+
parseElementWhereExpr(astNode: import("../generated/ast").ElementPredicateWhereV2): invariant;
|
|
62
63
|
parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): invariant[];
|
|
63
64
|
parseRelationWhereExpr(astNode: import("../generated/ast").RelationPredicateWhereV2): invariant;
|
|
64
65
|
parseRelationExpr(astNode: import("../generated/ast").RelationExpr): invariant;
|
|
@@ -117,6 +118,7 @@ declare const DocumentParserFromMixins: {
|
|
|
117
118
|
parseFqnRef(astNode: import("../generated/ast").FqnRef): invariant;
|
|
118
119
|
parseFqnExpr(astNode: import("../generated/ast").FqnExpr): invariant;
|
|
119
120
|
parseFqnRefExpr(astNode: import("../generated/ast").FqnRefExpr): invariant;
|
|
121
|
+
parseElementWhereExpr(astNode: import("../generated/ast").ElementPredicateWhereV2): invariant;
|
|
120
122
|
parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): invariant[];
|
|
121
123
|
parseRelationWhereExpr(astNode: import("../generated/ast").RelationPredicateWhereV2): invariant;
|
|
122
124
|
parseRelationExpr(astNode: import("../generated/ast").RelationExpr): invariant;
|
|
@@ -184,6 +186,7 @@ declare const DocumentParserFromMixins: {
|
|
|
184
186
|
parseFqnRef(astNode: import("../generated/ast").FqnRef): invariant;
|
|
185
187
|
parseFqnExpr(astNode: import("../generated/ast").FqnExpr): invariant;
|
|
186
188
|
parseFqnRefExpr(astNode: import("../generated/ast").FqnRefExpr): invariant;
|
|
189
|
+
parseElementWhereExpr(astNode: import("../generated/ast").ElementPredicateWhereV2): invariant;
|
|
187
190
|
parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): invariant[];
|
|
188
191
|
parseRelationWhereExpr(astNode: import("../generated/ast").RelationPredicateWhereV2): invariant;
|
|
189
192
|
parseRelationExpr(astNode: import("../generated/ast").RelationExpr): invariant;
|
|
@@ -217,6 +220,7 @@ declare const DocumentParserFromMixins: {
|
|
|
217
220
|
parseFqnRef(astNode: import("../generated/ast").FqnRef): invariant;
|
|
218
221
|
parseFqnExpr(astNode: import("../generated/ast").FqnExpr): invariant;
|
|
219
222
|
parseFqnRefExpr(astNode: import("../generated/ast").FqnRefExpr): invariant;
|
|
223
|
+
parseElementWhereExpr(astNode: import("../generated/ast").ElementPredicateWhereV2): invariant;
|
|
220
224
|
parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): invariant[];
|
|
221
225
|
parseRelationWhereExpr(astNode: import("../generated/ast").RelationPredicateWhereV2): invariant;
|
|
222
226
|
parseRelationExpr(astNode: import("../generated/ast").RelationExpr): invariant;
|
|
@@ -241,6 +245,7 @@ declare const DocumentParserFromMixins: {
|
|
|
241
245
|
new (...args: any[]): {
|
|
242
246
|
parseModel(): void;
|
|
243
247
|
parseElement(astNode: import("../generated/ast").Element): import("../ast").ParsedAstElement;
|
|
248
|
+
parseExtendElement(astNode: import("../generated/ast").ExtendElement): import("../ast").ParsedAstExtendElement | null;
|
|
244
249
|
parseRelation(astNode: import("../generated/ast").Relation): import("../ast").ParsedAstRelation;
|
|
245
250
|
isValid: import("../validation").IsValidFn;
|
|
246
251
|
readonly services: LikeC4Services;
|
|
@@ -264,6 +269,7 @@ declare const DocumentParserFromMixins: {
|
|
|
264
269
|
parseFqnRef(astNode: import("../generated/ast").FqnRef): invariant;
|
|
265
270
|
parseFqnExpr(astNode: import("../generated/ast").FqnExpr): invariant;
|
|
266
271
|
parseFqnRefExpr(astNode: import("../generated/ast").FqnRefExpr): invariant;
|
|
272
|
+
parseElementWhereExpr(astNode: import("../generated/ast").ElementPredicateWhereV2): invariant;
|
|
267
273
|
parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): invariant[];
|
|
268
274
|
parseRelationWhereExpr(astNode: import("../generated/ast").RelationPredicateWhereV2): invariant;
|
|
269
275
|
parseRelationExpr(astNode: import("../generated/ast").RelationExpr): invariant;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { invariant, isNonEmptyArray } from "@likec4/core";
|
|
2
|
-
import { filter, flatMap, isNonNullish, isTruthy,
|
|
2
|
+
import { filter, flatMap, fromEntries, isEmpty, isNonNullish, isTruthy, map, pipe, unique } from "remeda";
|
|
3
3
|
import stripIndent from "strip-indent";
|
|
4
4
|
import { ast } from "../../ast.js";
|
|
5
5
|
import { getFqnElementRef } from "../../utils/elementRef.js";
|
|
@@ -32,10 +32,16 @@ export class BaseParser {
|
|
|
32
32
|
return this.services.workspace.AstNodeLocator.getAstNodePath(node);
|
|
33
33
|
}
|
|
34
34
|
getMetadata(metadataAstNode) {
|
|
35
|
-
if (!metadataAstNode || !this.isValid(metadataAstNode)) {
|
|
35
|
+
if (!metadataAstNode || !this.isValid(metadataAstNode) || isEmpty(metadataAstNode.props)) {
|
|
36
36
|
return void 0;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
const data = pipe(
|
|
39
|
+
metadataAstNode.props,
|
|
40
|
+
map((p) => [p.key, removeIndent(p.value)]),
|
|
41
|
+
filter(([_, value]) => isTruthy(value)),
|
|
42
|
+
fromEntries()
|
|
43
|
+
);
|
|
44
|
+
return isEmpty(data) ? void 0 : data;
|
|
39
45
|
}
|
|
40
46
|
convertTags(withTags) {
|
|
41
47
|
return this.parseTags(withTags);
|
|
@@ -45,7 +51,7 @@ export class BaseParser {
|
|
|
45
51
|
if (!iter) {
|
|
46
52
|
return null;
|
|
47
53
|
}
|
|
48
|
-
|
|
54
|
+
let tags = [];
|
|
49
55
|
while (iter) {
|
|
50
56
|
try {
|
|
51
57
|
if (this.isValid(iter)) {
|
|
@@ -58,7 +64,7 @@ export class BaseParser {
|
|
|
58
64
|
}
|
|
59
65
|
iter = iter.prev;
|
|
60
66
|
}
|
|
61
|
-
tags.reverse();
|
|
67
|
+
tags = unique(tags.reverse());
|
|
62
68
|
return isNonEmptyArray(tags) ? tags : null;
|
|
63
69
|
}
|
|
64
70
|
convertLinks(source) {
|
|
@@ -11,6 +11,7 @@ export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B:
|
|
|
11
11
|
parseFqnRef(astNode: ast.FqnRef): c4.FqnRef;
|
|
12
12
|
parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr;
|
|
13
13
|
parseFqnRefExpr(astNode: ast.FqnRefExpr): c4.FqnExpr.NonWildcard;
|
|
14
|
+
parseElementWhereExpr(astNode: ast.ElementPredicateWhereV2): c4.RelationExpr;
|
|
14
15
|
parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
|
|
15
16
|
parseRelationWhereExpr(astNode: ast.RelationPredicateWhereV2): c4.RelationExpr;
|
|
16
17
|
parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr;
|
|
@@ -12,6 +12,7 @@ export declare function DeploymentViewParser<TBase extends WithExpressionV2 & Wi
|
|
|
12
12
|
parseFqnRef(astNode: ast.FqnRef): c4.FqnRef;
|
|
13
13
|
parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr;
|
|
14
14
|
parseFqnRefExpr(astNode: ast.FqnRefExpr): c4.FqnExpr.NonWildcard;
|
|
15
|
+
parseElementWhereExpr(astNode: ast.ElementPredicateWhereV2): c4.RelationExpr;
|
|
15
16
|
parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
|
|
16
17
|
parseRelationWhereExpr(astNode: ast.RelationPredicateWhereV2): c4.RelationExpr;
|
|
17
18
|
parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr;
|
|
@@ -67,6 +67,9 @@ export function DeploymentViewParser(B) {
|
|
|
67
67
|
case ast.isFqnExpr(expr):
|
|
68
68
|
exprs.unshift(this.parseFqnExpr(expr));
|
|
69
69
|
break;
|
|
70
|
+
case ast.isElementPredicateWhereV2(expr):
|
|
71
|
+
exprs.unshift(this.parseElementWhereExpr(expr));
|
|
72
|
+
break;
|
|
70
73
|
case ast.isRelationExpr(expr):
|
|
71
74
|
exprs.unshift(this.parseRelationExpr(expr));
|
|
72
75
|
break;
|
|
@@ -7,6 +7,7 @@ export declare function ExpressionV2Parser<TBase extends Base>(B: TBase): {
|
|
|
7
7
|
parseFqnRef(astNode: ast.FqnRef): c4.FqnRef;
|
|
8
8
|
parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr;
|
|
9
9
|
parseFqnRefExpr(astNode: ast.FqnRefExpr): c4.FqnExpr.NonWildcard;
|
|
10
|
+
parseElementWhereExpr(astNode: ast.ElementPredicateWhereV2): c4.RelationExpr;
|
|
10
11
|
parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
|
|
11
12
|
parseRelationWhereExpr(astNode: ast.RelationPredicateWhereV2): c4.RelationExpr;
|
|
12
13
|
parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr;
|
|
@@ -65,6 +65,16 @@ export function ExpressionV2Parser(B) {
|
|
|
65
65
|
return { ref };
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
parseElementWhereExpr(astNode) {
|
|
69
|
+
return {
|
|
70
|
+
where: {
|
|
71
|
+
expr: this.parseFqnExpr(astNode.subject),
|
|
72
|
+
condition: astNode.where ? parseWhereClause(astNode.where) : {
|
|
73
|
+
kind: { neq: "--always-true--" }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
68
78
|
parseFqnExpressions(astNode) {
|
|
69
79
|
const exprs = [];
|
|
70
80
|
let iter = astNode;
|
|
@@ -56,6 +56,7 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
|
|
|
56
56
|
parseFqnRef(astNode: ast.FqnRef): c4.FqnRef;
|
|
57
57
|
parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr;
|
|
58
58
|
parseFqnRefExpr(astNode: ast.FqnRefExpr): c4.FqnExpr.NonWildcard;
|
|
59
|
+
parseElementWhereExpr(astNode: ast.ElementPredicateWhereV2): c4.RelationExpr;
|
|
59
60
|
parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
|
|
60
61
|
parseRelationWhereExpr(astNode: ast.RelationPredicateWhereV2): c4.RelationExpr;
|
|
61
62
|
parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type * as c4 from '@likec4/core';
|
|
2
|
-
import { type ParsedAstElement, type ParsedAstRelation, ast } from '../../ast';
|
|
2
|
+
import { type ParsedAstElement, type ParsedAstExtendElement, type ParsedAstRelation, ast } from '../../ast';
|
|
3
3
|
import { type Base } from './Base';
|
|
4
4
|
export type WithModel = ReturnType<typeof ModelParser>;
|
|
5
5
|
export declare function ModelParser<TBase extends Base>(B: TBase): {
|
|
6
6
|
new (...args: any[]): {
|
|
7
7
|
parseModel(): void;
|
|
8
8
|
parseElement(astNode: ast.Element): ParsedAstElement;
|
|
9
|
+
parseExtendElement(astNode: ast.ExtendElement): ParsedAstExtendElement | null;
|
|
9
10
|
parseRelation(astNode: ast.Relation): ParsedAstRelation;
|
|
10
11
|
isValid: import("../../validation").IsValidFn;
|
|
11
12
|
readonly services: import("../..").LikeC4Services;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isNonEmptyArray, nonexhaustive, nonNullable } from "@likec4/core";
|
|
2
|
-
import { filter, first, isNonNullish, isTruthy, map, mapToObj, pipe } from "remeda";
|
|
2
|
+
import { filter, first, isEmpty, isNonNullish, isTruthy, map, mapToObj, pipe } from "remeda";
|
|
3
3
|
import {
|
|
4
4
|
ast,
|
|
5
5
|
resolveRelationPoints,
|
|
@@ -26,6 +26,13 @@ export function ModelParser(B) {
|
|
|
26
26
|
}
|
|
27
27
|
continue;
|
|
28
28
|
}
|
|
29
|
+
if (ast.isExtendElement(el)) {
|
|
30
|
+
const parsed = this.parseExtendElement(el);
|
|
31
|
+
if (parsed) {
|
|
32
|
+
doc.c4ExtendElements.push(parsed);
|
|
33
|
+
}
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
29
36
|
nonexhaustive(el);
|
|
30
37
|
} catch (e) {
|
|
31
38
|
logWarnError(e);
|
|
@@ -72,6 +79,24 @@ export function ModelParser(B) {
|
|
|
72
79
|
style
|
|
73
80
|
};
|
|
74
81
|
}
|
|
82
|
+
parseExtendElement(astNode) {
|
|
83
|
+
const isValid = this.isValid;
|
|
84
|
+
const id = this.resolveFqn(astNode);
|
|
85
|
+
const tags = this.parseTags(astNode.body);
|
|
86
|
+
const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
|
|
87
|
+
const astPath = this.getAstNodePath(astNode);
|
|
88
|
+
const links = this.parseLinks(astNode.body) ?? [];
|
|
89
|
+
if (!tags && isEmpty(metadata ?? {}) && isEmpty(links)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
id,
|
|
94
|
+
astPath,
|
|
95
|
+
...metadata && { metadata },
|
|
96
|
+
...tags && { tags },
|
|
97
|
+
...links && isNonEmptyArray(links) && { links }
|
|
98
|
+
};
|
|
99
|
+
}
|
|
75
100
|
parseRelation(astNode) {
|
|
76
101
|
const isValid = this.isValid;
|
|
77
102
|
const coupling = resolveRelationPoints(astNode);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { invariant, nonexhaustive } from "@likec4/core";
|
|
2
2
|
import { isBoolean, isDefined, isTruthy } from "remeda";
|
|
3
|
-
import { ast, parseAstOpacityProperty, toColor } from "../../ast.js";
|
|
3
|
+
import { ast, parseAstOpacityProperty, parseAstSizeValue, toColor } from "../../ast.js";
|
|
4
4
|
import { logWarnError } from "../../logger.js";
|
|
5
5
|
import { elementRef } from "../../utils/elementRef.js";
|
|
6
6
|
import { parseWhereClause } from "../model-parser-where.js";
|
|
@@ -171,6 +171,24 @@ export function PredicatesParser(B) {
|
|
|
171
171
|
}
|
|
172
172
|
return acc;
|
|
173
173
|
}
|
|
174
|
+
if (ast.isShapeSizeProperty(prop)) {
|
|
175
|
+
if (isTruthy(prop.value)) {
|
|
176
|
+
acc.custom[prop.key] = parseAstSizeValue(prop);
|
|
177
|
+
}
|
|
178
|
+
return acc;
|
|
179
|
+
}
|
|
180
|
+
if (ast.isTextSizeProperty(prop)) {
|
|
181
|
+
if (isTruthy(prop.value)) {
|
|
182
|
+
acc.custom[prop.key] = parseAstSizeValue(prop);
|
|
183
|
+
}
|
|
184
|
+
return acc;
|
|
185
|
+
}
|
|
186
|
+
if (ast.isPaddingSizeProperty(prop)) {
|
|
187
|
+
if (isTruthy(prop.value)) {
|
|
188
|
+
acc.custom[prop.key] = parseAstSizeValue(prop);
|
|
189
|
+
}
|
|
190
|
+
return acc;
|
|
191
|
+
}
|
|
174
192
|
nonexhaustive(prop);
|
|
175
193
|
},
|
|
176
194
|
{
|
|
@@ -54,6 +54,7 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
|
|
|
54
54
|
parseFqnRef(astNode: ast.FqnRef): c4.FqnRef;
|
|
55
55
|
parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr;
|
|
56
56
|
parseFqnRefExpr(astNode: ast.FqnRefExpr): c4.FqnExpr.NonWildcard;
|
|
57
|
+
parseElementWhereExpr(astNode: ast.ElementPredicateWhereV2): c4.RelationExpr;
|
|
57
58
|
parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
|
|
58
59
|
parseRelationWhereExpr(astNode: ast.RelationPredicateWhereV2): c4.RelationExpr;
|
|
59
60
|
parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ReferenceInfo, type Scope, DefaultScopeProvider } from 'langium';
|
|
2
2
|
import { ast } from '../ast';
|
|
3
3
|
import type { LikeC4Services } from '../module';
|
|
4
4
|
export declare class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
@@ -73,7 +73,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
73
73
|
if (referenceType !== ast.Element) {
|
|
74
74
|
return this.getGlobalScope(referenceType, context);
|
|
75
75
|
}
|
|
76
|
-
if (ast.
|
|
76
|
+
if (ast.isStrictFqnElementRef(container) && context.property === "el") {
|
|
77
77
|
const parent = container.parent;
|
|
78
78
|
if (!parent) {
|
|
79
79
|
return this.getGlobalScope(referenceType, context);
|
|
@@ -14,6 +14,7 @@ export declare function createTestServices(workspace?: string): {
|
|
|
14
14
|
warnings: any;
|
|
15
15
|
}>;
|
|
16
16
|
buildModel: () => Promise<any>;
|
|
17
|
+
buildLikeC4Model: () => Promise<any>;
|
|
17
18
|
resetState: () => Promise<void>;
|
|
18
19
|
format: (input: string | LikeC4LangiumDocument, uri?: string) => Promise<any>;
|
|
19
20
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LikeC4Model } from "@likec4/core";
|
|
1
2
|
import { DocumentState, EmptyFileSystem, TextDocument } from "langium";
|
|
2
3
|
import * as assert from "node:assert";
|
|
3
4
|
import stripIndent from "strip-indent";
|
|
@@ -101,6 +102,12 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
101
102
|
if (!model) throw new Error("No model found");
|
|
102
103
|
return model;
|
|
103
104
|
};
|
|
105
|
+
const buildLikeC4Model = async () => {
|
|
106
|
+
await validateAll();
|
|
107
|
+
const model = await modelBuilder.buildComputedModel();
|
|
108
|
+
if (!model) throw new Error("No model found");
|
|
109
|
+
return LikeC4Model.create(model);
|
|
110
|
+
};
|
|
104
111
|
const resetState = async () => {
|
|
105
112
|
await services.shared.workspace.WorkspaceLock.write(async (cancelToken) => {
|
|
106
113
|
const docs = langiumDocuments.all.toArray().map((doc) => doc.uri);
|
|
@@ -113,6 +120,7 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
113
120
|
validate,
|
|
114
121
|
validateAll,
|
|
115
122
|
buildModel,
|
|
123
|
+
buildLikeC4Model,
|
|
116
124
|
resetState,
|
|
117
125
|
format
|
|
118
126
|
};
|
|
@@ -3,9 +3,9 @@ import type { ast } from '../ast';
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns referenced AST Element
|
|
5
5
|
*/
|
|
6
|
-
export declare function elementRef(node: ast.ElementRef | ast.
|
|
6
|
+
export declare function elementRef(node: ast.ElementRef | ast.StrictFqnElementRef): any;
|
|
7
7
|
/**
|
|
8
|
-
* Returns FQN of
|
|
8
|
+
* Returns FQN of StrictFqnElementRef
|
|
9
9
|
* a.b.c.d - for c node returns a.b.c
|
|
10
10
|
*/
|
|
11
|
-
export declare function getFqnElementRef(node: ast.
|
|
11
|
+
export declare function getFqnElementRef(node: ast.StrictFqnElementRef): c4.Fqn;
|
|
@@ -3,7 +3,7 @@ import { type LikeC4LangiumDocument, ast } from '../ast';
|
|
|
3
3
|
import type { LikeC4Services } from '../module';
|
|
4
4
|
type Guard<N extends AstNode> = (n: AstNode) => n is N;
|
|
5
5
|
type Guarded<G> = G extends Guard<infer N> ? N : never;
|
|
6
|
-
declare const isValidatableAstNode: (n: AstNode) => n is ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.ElementDescedantsExpression | ast.ElementKindExpression | ast.ElementRef | ast.ElementTagExpression | ast.ExpandElementExpression | ast.WildcardExpression | ast.ElementPredicateWhere | ast.ElementPredicateWith | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.LinkProperty | ast.MetadataBody | ast.
|
|
6
|
+
declare const isValidatableAstNode: (n: AstNode) => n is ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentViewRulePredicate | ast.DeploymentViewRuleStyle | ast.ViewRuleAutoLayout | ast.DynamicViewGlobalPredicateRef | ast.DynamicViewIncludePredicate | ast.ViewRuleGlobalStyle | ast.ViewRuleStyle | ast.ElementDescedantsExpression | ast.ElementKindExpression | ast.ElementRef | ast.ElementTagExpression | ast.ExpandElementExpression | ast.WildcardExpression | ast.ElementPredicateWhere | ast.ElementPredicateWith | ast.ElementPredicateWhereV2 | ast.FqnRefExpr | ast.ElementStringProperty | ast.ElementStyleProperty | ast.IconProperty | ast.LinkProperty | ast.MetadataBody | ast.DirectedRelationExpr | ast.InOutRelationExpr | ast.IncomingRelationExpr | ast.OutgoingRelationExpr | ast.RelationPredicateWhereV2 | ast.Element | ast.ExtendElement | ast.DeploymentView | ast.DynamicView | ast.ElementView | ast.DirectedRelationExpression | ast.InOutRelationExpression | ast.IncomingRelationExpression | ast.OutgoingRelationExpression | ast.RelationPredicateWhere | ast.RelationPredicateWith | ast.RelationStringProperty | ast.ArrowProperty | ast.ColorProperty | ast.LineProperty | ast.PaddingSizeProperty | ast.ShapeSizeProperty | ast.TextSizeProperty | ast.MetadataAttribute | ast.NotationProperty | ast.NotesProperty | ast.SpecificationElementStringProperty | ast.SpecificationRelationshipStringProperty | ast.ViewStringProperty | ast.BorderProperty | ast.MultipleProperty | ast.OpacityProperty | ast.ShapeProperty | ast.ViewRuleGlobalPredicateRef | ast.ViewRuleGroup | ast.ExcludePredicate | ast.IncludePredicate | ast.SpecificationRelationshipKind | ast.GlobalStyle | ast.SpecificationColor | ast.NavigateToProperty | ast.DynamicViewStep | ast.Tags | ast.DeploymentRelation | ast.SpecificationDeploymentNodeKind | ast.DynamicViewParallelSteps | ast.GlobalDynamicPredicateGroup | ast.DynamicViewPredicateIterator | ast.Relation | ast.SpecificationElementKind | ast.GlobalPredicateGroup | ast.Globals | ast.GlobalStyleGroup | ast.SpecificationRule | ast.SpecificationTag;
|
|
7
7
|
type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
|
|
8
8
|
export declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
|
|
9
9
|
isValid: (n: ValidatableAstNode) => boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/language-server",
|
|
3
3
|
"description": "LikeC4 Language Server",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.21.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -90,36 +90,34 @@
|
|
|
90
90
|
"test:watch": "vitest"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@likec4/core": "1.
|
|
94
|
-
"@likec4/icons": "1.
|
|
95
|
-
"@likec4/layouts": "1.
|
|
96
|
-
"@likec4/log": "1.
|
|
97
|
-
"@likec4/tsconfig": "1.
|
|
98
|
-
"@msgpack/msgpack": "^3.0.0-
|
|
99
|
-
"@smithy/util-base64": "^
|
|
93
|
+
"@likec4/core": "1.21.1",
|
|
94
|
+
"@likec4/icons": "1.21.1",
|
|
95
|
+
"@likec4/layouts": "1.21.1",
|
|
96
|
+
"@likec4/log": "1.21.1",
|
|
97
|
+
"@likec4/tsconfig": "1.21.1",
|
|
98
|
+
"@msgpack/msgpack": "^3.0.0-beta4",
|
|
99
|
+
"@smithy/util-base64": "^4.0.0",
|
|
100
100
|
"@types/node": "^20.17.7",
|
|
101
101
|
"@types/which": "^3.0.4",
|
|
102
|
-
"@vitest/coverage-v8": "^
|
|
102
|
+
"@vitest/coverage-v8": "^3.0.4",
|
|
103
103
|
"esm-env": "^1.2.2",
|
|
104
|
-
"execa": "^9.3.1",
|
|
105
104
|
"fast-equals": "^5.2.2",
|
|
106
|
-
"fdir": "^6.4.
|
|
105
|
+
"fdir": "^6.4.3",
|
|
107
106
|
"indent-string": "^5.0.0",
|
|
108
107
|
"json5": "^2.2.3",
|
|
109
108
|
"langium": "3.3.1",
|
|
110
109
|
"langium-cli": "3.3.0",
|
|
111
110
|
"natural-compare-lite": "^1.4.0",
|
|
112
|
-
"npm-run-all2": "^7.0.1",
|
|
113
111
|
"p-debounce": "^4.0.0",
|
|
114
|
-
"remeda": "^2.
|
|
112
|
+
"remeda": "^2.20.1",
|
|
115
113
|
"strip-indent": "^4.0.0",
|
|
116
114
|
"tsx": "~4.19.2",
|
|
117
|
-
"turbo": "^2.
|
|
115
|
+
"turbo": "^2.4.0",
|
|
118
116
|
"type-fest": "4.28.1",
|
|
119
117
|
"typescript": "^5.7.3",
|
|
120
118
|
"ufo": "^1.5.4",
|
|
121
119
|
"unbuild": "^3.3.1",
|
|
122
|
-
"vitest": "^
|
|
120
|
+
"vitest": "^3.0.4",
|
|
123
121
|
"vscode-jsonrpc": "8.2.0",
|
|
124
122
|
"vscode-languageserver": "9.0.1",
|
|
125
123
|
"vscode-languageserver-types": "3.17.5",
|