@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.
Files changed (50) hide show
  1. package/README.md +1 -1
  2. package/dist/ast.d.ts +16 -0
  3. package/dist/ast.js +42 -10
  4. package/dist/bundled.mjs +2641 -2876
  5. package/dist/formatting/LikeC4Formatter.js +6 -3
  6. package/dist/generated/ast.d.ts +76 -24
  7. package/dist/generated/ast.js +103 -25
  8. package/dist/generated/grammar.js +1 -1
  9. package/dist/generated-lib/icons.js +1 -0
  10. package/dist/lsp/CompletionProvider.js +2 -1
  11. package/dist/lsp/SemanticTokenProvider.js +50 -2
  12. package/dist/model/model-builder.js +57 -3
  13. package/dist/model/model-parser.d.ts +6 -0
  14. package/dist/model/model-parser.js +1 -0
  15. package/dist/model/parser/Base.js +11 -5
  16. package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
  17. package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
  18. package/dist/model/parser/DeploymentViewParser.js +3 -0
  19. package/dist/model/parser/FqnRefParser.d.ts +1 -0
  20. package/dist/model/parser/FqnRefParser.js +10 -0
  21. package/dist/model/parser/GlobalsParser.d.ts +1 -0
  22. package/dist/model/parser/ModelParser.d.ts +2 -1
  23. package/dist/model/parser/ModelParser.js +26 -1
  24. package/dist/model/parser/PredicatesParser.js +19 -1
  25. package/dist/model/parser/ViewsParser.d.ts +1 -0
  26. package/dist/references/scope-provider.d.ts +1 -1
  27. package/dist/references/scope-provider.js +1 -1
  28. package/dist/test/testServices.d.ts +1 -0
  29. package/dist/test/testServices.js +8 -0
  30. package/dist/utils/elementRef.d.ts +3 -3
  31. package/dist/validation/index.d.ts +1 -1
  32. package/package.json +13 -15
  33. package/src/ast.ts +55 -9
  34. package/src/formatting/LikeC4Formatter.ts +7 -1
  35. package/src/generated/ast.ts +200 -49
  36. package/src/generated/grammar.ts +1 -1
  37. package/src/generated-lib/icons.ts +1 -0
  38. package/src/like-c4.langium +45 -7
  39. package/src/lsp/CompletionProvider.ts +1 -0
  40. package/src/lsp/SemanticTokenProvider.ts +56 -1
  41. package/src/model/model-builder.ts +65 -6
  42. package/src/model/model-parser.ts +1 -0
  43. package/src/model/parser/Base.ts +11 -5
  44. package/src/model/parser/DeploymentViewParser.ts +3 -0
  45. package/src/model/parser/FqnRefParser.ts +11 -0
  46. package/src/model/parser/ModelParser.ts +30 -1
  47. package/src/model/parser/PredicatesParser.ts +19 -1
  48. package/src/references/scope-provider.ts +9 -9
  49. package/src/test/testServices.ts +18 -9
  50. package/src/utils/elementRef.ts +3 -3
@@ -342,6 +342,7 @@ export const LibIcons = `likec4lib { icons {
342
342
  azure:arc-data-services
343
343
  azure:arc-kubernetes
344
344
  azure:arc-machines
345
+ azure:arc-postgre-sql
345
346
  azure:arc-sql-managed-instance
346
347
  azure:arc-sql-server
347
348
  azure:atm-multistack
@@ -11,7 +11,8 @@ const STYLE_FIELDS = [
11
11
  "icon",
12
12
  "border",
13
13
  "opacity",
14
- "multiple"
14
+ "multiple",
15
+ "size"
15
16
  ].join(",");
16
17
  export class LikeC4CompletionProvider extends DefaultCompletionProvider {
17
18
  completionOptions = {
@@ -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.isFqnElementRef(node)) {
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
- multiple
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) => map(d.c4Elements, toModelElement(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;
@@ -42,6 +42,7 @@ export class LikeC4ModelParser {
42
42
  deployments: {}
43
43
  },
44
44
  c4Elements: [],
45
+ c4ExtendElements: [],
45
46
  c4Relations: [],
46
47
  c4Deployments: [],
47
48
  c4DeploymentRelations: [],
@@ -1,5 +1,5 @@
1
1
  import { invariant, isNonEmptyArray } from "@likec4/core";
2
- import { filter, flatMap, isNonNullish, isTruthy, mapToObj, pipe } from "remeda";
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
- return mapToObj(metadataAstNode.props, (p) => [p.key, removeIndent(p.value)]);
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
- const tags = [];
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 { DefaultScopeProvider, type ReferenceInfo, type Scope } from 'langium';
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.isFqnElementRef(container) && context.property === "el") {
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.FqnElementRef): any;
6
+ export declare function elementRef(node: ast.ElementRef | ast.StrictFqnElementRef): any;
7
7
  /**
8
- * Returns FQN of FqnElementRef
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.FqnElementRef): c4.Fqn;
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.FqnRefExpr | 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.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;
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.20.3",
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.20.3",
94
- "@likec4/icons": "1.20.3",
95
- "@likec4/layouts": "1.20.3",
96
- "@likec4/log": "1.20.3",
97
- "@likec4/tsconfig": "1.20.3",
98
- "@msgpack/msgpack": "^3.0.0-beta3",
99
- "@smithy/util-base64": "^3.0.0",
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": "^2.1.8",
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.2",
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.19.0",
112
+ "remeda": "^2.20.1",
115
113
  "strip-indent": "^4.0.0",
116
114
  "tsx": "~4.19.2",
117
- "turbo": "^2.3.4",
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": "^2.1.8",
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",