@likec4/language-server 1.20.2 → 1.21.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.
Files changed (51) 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 +4230 -4096
  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/logger.d.ts +3 -0
  10. package/dist/logger.js +0 -4
  11. package/dist/lsp/CompletionProvider.js +2 -1
  12. package/dist/lsp/SemanticTokenProvider.js +50 -2
  13. package/dist/model/model-builder.js +57 -3
  14. package/dist/model/model-parser.d.ts +6 -0
  15. package/dist/model/model-parser.js +1 -0
  16. package/dist/model/parser/Base.js +11 -5
  17. package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
  18. package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
  19. package/dist/model/parser/DeploymentViewParser.js +3 -0
  20. package/dist/model/parser/FqnRefParser.d.ts +1 -0
  21. package/dist/model/parser/FqnRefParser.js +10 -0
  22. package/dist/model/parser/GlobalsParser.d.ts +1 -0
  23. package/dist/model/parser/ModelParser.d.ts +2 -1
  24. package/dist/model/parser/ModelParser.js +26 -1
  25. package/dist/model/parser/PredicatesParser.js +19 -1
  26. package/dist/model/parser/ViewsParser.d.ts +1 -0
  27. package/dist/references/scope-provider.d.ts +1 -1
  28. package/dist/references/scope-provider.js +1 -1
  29. package/dist/test/testServices.d.ts +1 -0
  30. package/dist/test/testServices.js +8 -0
  31. package/dist/utils/elementRef.d.ts +3 -3
  32. package/dist/validation/index.d.ts +1 -1
  33. package/package.json +12 -12
  34. package/src/ast.ts +55 -9
  35. package/src/formatting/LikeC4Formatter.ts +7 -1
  36. package/src/generated/ast.ts +200 -49
  37. package/src/generated/grammar.ts +1 -1
  38. package/src/like-c4.langium +45 -7
  39. package/src/logger.ts +3 -4
  40. package/src/lsp/CompletionProvider.ts +1 -0
  41. package/src/lsp/SemanticTokenProvider.ts +56 -1
  42. package/src/model/model-builder.ts +65 -6
  43. package/src/model/model-parser.ts +1 -0
  44. package/src/model/parser/Base.ts +11 -5
  45. package/src/model/parser/DeploymentViewParser.ts +3 -0
  46. package/src/model/parser/FqnRefParser.ts +11 -0
  47. package/src/model/parser/ModelParser.ts +30 -1
  48. package/src/model/parser/PredicatesParser.ts +19 -1
  49. package/src/references/scope-provider.ts +9 -9
  50. package/src/test/testServices.ts +18 -9
  51. package/src/utils/elementRef.ts +3 -3
@@ -125,10 +125,13 @@ ElementStringProperty:
125
125
  key=('title' | 'technology' | 'description') ':'? value=String ';'?;
126
126
 
127
127
  ExtendElement:
128
- 'extend' element=FqnElementRef body=ExtendElementBody
128
+ 'extend' element=StrictFqnElementRef body=ExtendElementBody
129
129
  ;
130
130
 
131
131
  ExtendElementBody: '{'
132
+ tags=Tags?
133
+ props+=ExtendElementProperty*
134
+
132
135
  elements+=(
133
136
  Relation<true> |
134
137
  Element
@@ -136,9 +139,13 @@ ExtendElementBody: '{'
136
139
  '}'
137
140
  ;
138
141
 
142
+ ExtendElementProperty:
143
+ LinkProperty |
144
+ MetadataProperty;
145
+
139
146
  //
140
- FqnElementRef:
141
- el=[Element:Id] ({infer FqnElementRef.parent=current} StickyDot el=[Element:Id])*;
147
+ StrictFqnElementRef:
148
+ el=[Element:Id] ({infer StrictFqnElementRef.parent=current} StickyDot el=[Element:Id])*;
142
149
 
143
150
  ElementRef:
144
151
  el=[Element:Id] ({infer ElementRef.parent=current} StickyDot el=[Element:Id])*;
@@ -362,7 +369,7 @@ WhereElementNegation:
362
369
 
363
370
  WhereElement:
364
371
  {infer WhereElementTag} 'tag' EqOperator value=[Tag:TagId]? |
365
- {infer WhereElementKind} 'kind' EqOperator value=[ElementKind]?
372
+ {infer WhereElementKind} 'kind' EqOperator value=[DeploymentNodeOrElementKind]?
366
373
  ;
367
374
 
368
375
  ElementExpression:
@@ -627,7 +634,11 @@ DeploymentViewRulePredicateExpression:
627
634
 
628
635
  ExpressionV2:
629
636
  RelationPredicateOrWhereV2 |
630
- FqnExpr
637
+ ElementPredicateOrWhereV2
638
+ ;
639
+
640
+ ElementPredicateOrWhereV2:
641
+ FqnExpr ({infer ElementPredicateWhereV2.subject=current} 'where' where=WhereElementExpression?)?
631
642
  ;
632
643
 
633
644
  RelationPredicateOrWhereV2:
@@ -731,13 +742,40 @@ BorderStyleValue returns string:
731
742
  BorderProperty:
732
743
  key='border' ':'? value=BorderStyleValue ';'?;
733
744
 
745
+ SizeValue returns string:
746
+ 'xs' |
747
+ 'sm' |
748
+ 'md' |
749
+ 'lg' |
750
+ 'xl' |
751
+ 'xsmall' |
752
+ 'small' |
753
+ 'medium' |
754
+ 'large' |
755
+ 'xlarge';
756
+
757
+ ShapeSizeProperty:
758
+ key='size' ':'? value=SizeValue ';'?;
759
+
760
+ PaddingSizeProperty:
761
+ key='padding' ':'? value=SizeValue ';'?;
762
+
763
+ TextSizeProperty:
764
+ key='textSize' ':'? value=SizeValue ';'?;
765
+
766
+ type SizeProperty = ShapeSizeProperty | PaddingSizeProperty | TextSizeProperty;
767
+
734
768
  StyleProperty:
735
769
  ColorProperty |
736
770
  ShapeProperty |
737
771
  BorderProperty |
738
772
  OpacityProperty |
739
773
  IconProperty |
740
- MultipleProperty;
774
+ MultipleProperty |
775
+ ShapeSizeProperty |
776
+ PaddingSizeProperty |
777
+ TextSizeProperty
778
+ ;
741
779
 
742
780
  ElementStyleProperty:
743
781
  key='style' '{'
@@ -802,7 +840,7 @@ CustomColorId returns string:
802
840
  IdTerminal | ElementShape | ArrowType | LineOptions | 'element' | 'model';
803
841
 
804
842
  Id returns string:
805
- IdTerminal | ElementShape | ThemeColor | ArrowType | LineOptions | Participant | 'element' | 'model' | 'group' | 'node' | 'deployment' | 'instance';
843
+ IdTerminal | ElementShape | ThemeColor | ArrowType | LineOptions | Participant | SizeValue | 'element' | 'model' | 'group' | 'node' | 'deployment' | 'instance';
806
844
 
807
845
  fragment EqOperator:
808
846
  (
package/src/logger.ts CHANGED
@@ -10,11 +10,10 @@ export function logError(err: unknown): void {
10
10
  logger.error(err)
11
11
  }
12
12
 
13
+ /**
14
+ * Logs an error as warning (not critical)
15
+ */
13
16
  export function logWarnError(err: unknown): void {
14
- if (err instanceof Error) {
15
- logger.warn(err.stack ?? err.message)
16
- return
17
- }
18
17
  logger.warn(err)
19
18
  }
20
19
 
@@ -16,6 +16,7 @@ const STYLE_FIELDS = [
16
16
  'border',
17
17
  'opacity',
18
18
  'multiple',
19
+ 'size',
19
20
  ].join(',')
20
21
 
21
22
  export class LikeC4CompletionProvider extends DefaultCompletionProvider {
@@ -140,6 +140,60 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
140
140
  })
141
141
  return 'prune'
142
142
  }
143
+ if (
144
+ ast.isGlobalStyleGroup(node)
145
+ || ast.isGlobalStyle(node)
146
+ ) {
147
+ acceptor({
148
+ node,
149
+ property: 'id',
150
+ type: SemanticTokenTypes.variable,
151
+ modifier: [
152
+ SemanticTokenModifiers.definition,
153
+ SemanticTokenModifiers.readonly,
154
+ ],
155
+ })
156
+ return
157
+ }
158
+ if (ast.isViewRuleGlobalStyle(node)) {
159
+ acceptor({
160
+ node,
161
+ property: 'style',
162
+ type: SemanticTokenTypes.variable,
163
+ modifier: [
164
+ SemanticTokenModifiers.definition,
165
+ SemanticTokenModifiers.readonly,
166
+ ],
167
+ })
168
+ return
169
+ }
170
+ if (
171
+ ast.isGlobalPredicateGroup(node)
172
+ || ast.isGlobalDynamicPredicateGroup(node)
173
+ ) {
174
+ acceptor({
175
+ node,
176
+ property: 'name',
177
+ type: SemanticTokenTypes.variable,
178
+ modifier: [
179
+ SemanticTokenModifiers.definition,
180
+ SemanticTokenModifiers.readonly,
181
+ ],
182
+ })
183
+ return
184
+ }
185
+ if (ast.isViewRuleGlobalPredicateRef(node)) {
186
+ acceptor({
187
+ node,
188
+ property: 'predicate',
189
+ type: SemanticTokenTypes.variable,
190
+ modifier: [
191
+ SemanticTokenModifiers.definition,
192
+ SemanticTokenModifiers.readonly,
193
+ ],
194
+ })
195
+ return
196
+ }
143
197
  if (ast.isElementTagExpression(node) && isTruthy(node.tag)) {
144
198
  acceptor({
145
199
  node,
@@ -161,7 +215,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
161
215
  })
162
216
  return !node.parent ? 'prune' : undefined
163
217
  }
164
- if (ast.isElementRef(node) || ast.isFqnElementRef(node)) {
218
+ if (ast.isElementRef(node) || ast.isStrictFqnElementRef(node)) {
165
219
  acceptor({
166
220
  node,
167
221
  property: 'el',
@@ -276,6 +330,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
276
330
  || ast.isArrowProperty(node)
277
331
  || ast.isLineProperty(node)
278
332
  || ast.isBorderProperty(node)
333
+ || ast.isSizeProperty(node)
279
334
  ) {
280
335
  acceptor({
281
336
  node,
@@ -5,6 +5,7 @@ import {
5
5
  compareRelations,
6
6
  computeColorValues,
7
7
  DeploymentElement,
8
+ isNonEmptyArray,
8
9
  isScopedElementView,
9
10
  LikeC4Model,
10
11
  parentFqn,
@@ -18,6 +19,7 @@ import {
18
19
  filter,
19
20
  flatMap,
20
21
  groupBy,
22
+ hasAtLeast,
21
23
  indexBy,
22
24
  isBoolean,
23
25
  isDefined,
@@ -35,11 +37,13 @@ import {
35
37
  reduce,
36
38
  reverse,
37
39
  sort,
40
+ unique,
38
41
  values,
39
42
  } from 'remeda'
40
43
  import type {
41
44
  ParsedAstDeploymentRelation,
42
45
  ParsedAstElement,
46
+ ParsedAstExtendElement,
43
47
  ParsedAstRelation,
44
48
  ParsedAstSpecification,
45
49
  ParsedAstView,
@@ -117,7 +121,10 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
117
121
  icon,
118
122
  opacity,
119
123
  border,
124
+ size,
120
125
  multiple,
126
+ padding,
127
+ textSize,
121
128
  },
122
129
  id,
123
130
  kind,
@@ -140,6 +147,9 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
140
147
  border ??= __kind.style.border
141
148
  technology ??= __kind.technology
142
149
  multiple ??= __kind.style.multiple
150
+ size ??= __kind.style.size
151
+ padding ??= __kind.style.padding
152
+ textSize ??= __kind.style.textSize
143
153
  return {
144
154
  ...(color && { color }),
145
155
  ...(shape && { shape }),
@@ -148,6 +158,9 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
148
158
  ...(__kind.notation && { notation: __kind.notation }),
149
159
  style: {
150
160
  ...(border && { border }),
161
+ ...(size && { size }),
162
+ ...(padding && { padding }),
163
+ ...(textSize && { textSize }),
151
164
  ...(isBoolean(multiple) && { multiple }),
152
165
  ...(isNumber(opacity) && { opacity }),
153
166
  },
@@ -166,9 +179,55 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
166
179
  }
167
180
  }
168
181
 
182
+ const elementsExtendData = new Map<string, Pick<c4.Element, 'links' | 'tags' | 'metadata'>>()
183
+ function mergeAllC4ExtendElements(doc: ParsedLikeC4LangiumDocument) {
184
+ for (const el of doc.c4ExtendElements) {
185
+ let links = el.links ? resolveLinks(doc, el.links) : null
186
+ const existing = elementsExtendData.get(el.id)
187
+ if (existing) {
188
+ links = existing.links ? [...existing.links, ...(links ?? [])] : links
189
+
190
+ let tags: c4.Tag[] | null = [...(existing.tags ?? []), ...(el.tags ?? [])]
191
+ if (!hasAtLeast(tags, 1)) {
192
+ tags = null
193
+ }
194
+
195
+ elementsExtendData.set(el.id, {
196
+ tags,
197
+ links,
198
+ metadata: { ...existing.metadata, ...el.metadata },
199
+ })
200
+ } else {
201
+ elementsExtendData.set(el.id, {
202
+ tags: el.tags ?? null,
203
+ links,
204
+ metadata: { ...el.metadata },
205
+ })
206
+ }
207
+ }
208
+ }
209
+ function withExtendElementData(el: c4.Element): c4.Element {
210
+ const extendData = elementsExtendData.get(el.id)
211
+ if (extendData) {
212
+ const links = [...(el.links ?? []), ...(extendData.links ?? [])]
213
+ const tags = unique([...(el.tags ?? []), ...(extendData.tags ?? [])])
214
+ const metadata = { ...el.metadata, ...extendData.metadata }
215
+ return {
216
+ ...el,
217
+ tags: hasAtLeast(tags, 1) ? tags : null,
218
+ links: hasAtLeast(links, 1) ? links : null,
219
+ ...(!isEmpty(metadata) && { metadata }),
220
+ }
221
+ }
222
+ return el
223
+ }
224
+
169
225
  const elements = pipe(
170
226
  docs,
171
- flatMap(d => map(d.c4Elements, toModelElement(d))),
227
+ flatMap(d => {
228
+ mergeAllC4ExtendElements(d)
229
+ return map(d.c4Elements, toModelElement(d))
230
+ }),
172
231
  filter(isTruthy),
173
232
  // sort from root elements to nested, so that parent is always present
174
233
  // Import to preserve the order from the source
@@ -180,7 +239,7 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
180
239
  logWarnError(`No parent found for ${el.id}`)
181
240
  return acc
182
241
  }
183
- acc[el.id] = el
242
+ acc[el.id] = withExtendElementData(el)
184
243
  return acc
185
244
  },
186
245
  {} as c4.ParsedLikeC4Model['elements'],
@@ -219,7 +278,7 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
219
278
  } satisfies c4.ModelRelation
220
279
  }
221
280
  return {
222
- ...links && { links },
281
+ ...(links && { links }),
223
282
  ...model,
224
283
  source,
225
284
  target,
@@ -263,8 +322,8 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
263
322
  } = parsed
264
323
  return {
265
324
  ...parsed,
266
- ...notation && { notation },
267
- ...technology && { technology },
325
+ ...(notation && { notation }),
326
+ ...(technology && { technology }),
268
327
  style: {
269
328
  border: 'dashed',
270
329
  opacity: 10,
@@ -333,7 +392,7 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
333
392
  } satisfies c4.DeploymentRelation
334
393
  }
335
394
  return {
336
- ...links && { links },
395
+ ...(links && { links }),
337
396
  ...model,
338
397
  source,
339
398
  target,
@@ -51,6 +51,7 @@ export class LikeC4ModelParser {
51
51
  deployments: {},
52
52
  },
53
53
  c4Elements: [],
54
+ c4ExtendElements: [],
54
55
  c4Relations: [],
55
56
  c4Deployments: [],
56
57
  c4DeploymentRelations: [],
@@ -1,7 +1,7 @@
1
1
  import type * as c4 from '@likec4/core'
2
2
  import { invariant, isNonEmptyArray } from '@likec4/core'
3
3
  import type { AstNode } from 'langium'
4
- import { filter, flatMap, isNonNullish, isTruthy, mapToObj, pipe } from 'remeda'
4
+ import { filter, flatMap, fromEntries, isEmpty, isNonNullish, isTruthy, map, pipe, unique } from 'remeda'
5
5
  import stripIndent from 'strip-indent'
6
6
  import { type ParsedLikeC4LangiumDocument, type ParsedLink, ast } from '../../ast'
7
7
  import type { LikeC4Services } from '../../module'
@@ -49,10 +49,16 @@ export class BaseParser {
49
49
  }
50
50
 
51
51
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): { [key: string]: string } | undefined {
52
- if (!metadataAstNode || !this.isValid(metadataAstNode)) {
52
+ if (!metadataAstNode || !this.isValid(metadataAstNode) || isEmpty(metadataAstNode.props)) {
53
53
  return undefined
54
54
  }
55
- return mapToObj(metadataAstNode.props, p => [p.key, removeIndent(p.value)] as [string, string])
55
+ const data = pipe(
56
+ metadataAstNode.props,
57
+ map(p => [p.key, removeIndent(p.value)] as [string, string]),
58
+ filter(([_, value]) => isTruthy(value)),
59
+ fromEntries(),
60
+ )
61
+ return isEmpty(data) ? undefined : data
56
62
  }
57
63
 
58
64
  convertTags<E extends { tags?: ast.Tags }>(withTags?: E) {
@@ -63,7 +69,7 @@ export class BaseParser {
63
69
  if (!iter) {
64
70
  return null
65
71
  }
66
- const tags = [] as c4.Tag[]
72
+ let tags = [] as c4.Tag[]
67
73
  while (iter) {
68
74
  try {
69
75
  if (this.isValid(iter)) {
@@ -77,7 +83,7 @@ export class BaseParser {
77
83
  }
78
84
  iter = iter.prev
79
85
  }
80
- tags.reverse()
86
+ tags = unique(tags.reverse())
81
87
  return isNonEmptyArray(tags) ? tags : null
82
88
  }
83
89
 
@@ -84,6 +84,9 @@ export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploy
84
84
  case ast.isFqnExpr(expr):
85
85
  exprs.unshift(this.parseFqnExpr(expr))
86
86
  break
87
+ case ast.isElementPredicateWhereV2(expr):
88
+ exprs.unshift(this.parseElementWhereExpr(expr))
89
+ break
87
90
  case ast.isRelationExpr(expr):
88
91
  exprs.unshift(this.parseRelationExpr(expr))
89
92
  break
@@ -74,6 +74,17 @@ export function ExpressionV2Parser<TBase extends Base>(B: TBase) {
74
74
  }
75
75
  }
76
76
 
77
+ parseElementWhereExpr(astNode: ast.ElementPredicateWhereV2): c4.RelationExpr {
78
+ return {
79
+ where: {
80
+ expr: this.parseFqnExpr(astNode.subject as ast.FqnExpr),
81
+ condition: astNode.where ? parseWhereClause(astNode.where) : {
82
+ kind: { neq: '--always-true--' },
83
+ },
84
+ },
85
+ }
86
+ }
87
+
77
88
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[] {
78
89
  const exprs = [] as c4.FqnExpr[]
79
90
  let iter: ast.FqnExpressions['prev'] = astNode
@@ -1,8 +1,9 @@
1
1
  import type * as c4 from '@likec4/core'
2
2
  import { isNonEmptyArray, nonexhaustive, nonNullable } from '@likec4/core'
3
- import { filter, first, isNonNullish, isTruthy, map, mapToObj, pipe } from 'remeda'
3
+ import { filter, first, isEmpty, isNonNullish, isTruthy, map, mapToObj, pipe } from 'remeda'
4
4
  import {
5
5
  type ParsedAstElement,
6
+ type ParsedAstExtendElement,
6
7
  type ParsedAstRelation,
7
8
  ast,
8
9
  resolveRelationPoints,
@@ -32,6 +33,13 @@ export function ModelParser<TBase extends Base>(B: TBase) {
32
33
  }
33
34
  continue
34
35
  }
36
+ if (ast.isExtendElement(el)) {
37
+ const parsed = this.parseExtendElement(el)
38
+ if (parsed) {
39
+ doc.c4ExtendElements.push(parsed)
40
+ }
41
+ continue
42
+ }
35
43
  nonexhaustive(el)
36
44
  } catch (e) {
37
45
  logWarnError(e)
@@ -87,6 +95,27 @@ export function ModelParser<TBase extends Base>(B: TBase) {
87
95
  }
88
96
  }
89
97
 
98
+ parseExtendElement(astNode: ast.ExtendElement): ParsedAstExtendElement | null {
99
+ const isValid = this.isValid
100
+ const id = this.resolveFqn(astNode)
101
+ const tags = this.parseTags(astNode.body)
102
+ const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
103
+ const astPath = this.getAstNodePath(astNode)
104
+ const links = this.parseLinks(astNode.body) ?? []
105
+
106
+ if (!tags && isEmpty(metadata ?? {}) && isEmpty(links)) {
107
+ return null
108
+ }
109
+
110
+ return {
111
+ id,
112
+ astPath,
113
+ ...(metadata && { metadata }),
114
+ ...(tags && { tags }),
115
+ ...(links && isNonEmptyArray(links) && { links }),
116
+ }
117
+ }
118
+
90
119
  parseRelation(astNode: ast.Relation): ParsedAstRelation {
91
120
  const isValid = this.isValid
92
121
  const coupling = resolveRelationPoints(astNode)
@@ -1,7 +1,7 @@
1
1
  import type * as c4 from '@likec4/core'
2
2
  import { invariant, nonexhaustive } from '@likec4/core'
3
3
  import { isBoolean, isDefined, isTruthy } from 'remeda'
4
- import { ast, parseAstOpacityProperty, toColor } from '../../ast'
4
+ import { ast, parseAstOpacityProperty, parseAstSizeValue, toColor } from '../../ast'
5
5
  import { logWarnError } from '../../logger'
6
6
  import { elementRef } from '../../utils/elementRef'
7
7
  import { parseWhereClause } from '../model-parser-where'
@@ -180,6 +180,24 @@ export function PredicatesParser<TBase extends Base>(B: TBase) {
180
180
  }
181
181
  return acc
182
182
  }
183
+ if (ast.isShapeSizeProperty(prop)) {
184
+ if (isTruthy(prop.value)) {
185
+ acc.custom[prop.key] = parseAstSizeValue(prop)
186
+ }
187
+ return acc
188
+ }
189
+ if (ast.isTextSizeProperty(prop)) {
190
+ if (isTruthy(prop.value)) {
191
+ acc.custom[prop.key] = parseAstSizeValue(prop)
192
+ }
193
+ return acc
194
+ }
195
+ if (ast.isPaddingSizeProperty(prop)) {
196
+ if (isTruthy(prop.value)) {
197
+ acc.custom[prop.key] = parseAstSizeValue(prop)
198
+ }
199
+ return acc
200
+ }
183
201
  nonexhaustive(prop)
184
202
  },
185
203
  {
@@ -3,18 +3,18 @@ import { nonexhaustive } from '@likec4/core'
3
3
  import type { AstNode } from 'langium'
4
4
  import {
5
5
  type AstNodeDescription,
6
+ type ReferenceInfo,
7
+ type Scope,
8
+ type Stream,
6
9
  AstUtils,
7
10
  DefaultScopeProvider,
8
11
  DONE_RESULT,
9
12
  EMPTY_SCOPE,
10
13
  EMPTY_STREAM,
11
14
  MapScope,
12
- type ReferenceInfo,
13
- type Scope,
14
- type Stream,
15
15
  stream,
16
16
  StreamImpl,
17
- StreamScope
17
+ StreamScope,
18
18
  } from 'langium'
19
19
  import { ast } from '../ast'
20
20
  import { logger } from '../logger'
@@ -54,7 +54,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
54
54
  return iterator.next()
55
55
  }
56
56
  return DONE_RESULT
57
- }
57
+ },
58
58
  )
59
59
  }
60
60
 
@@ -99,7 +99,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
99
99
  return this.getGlobalScope(referenceType, context)
100
100
  }
101
101
 
102
- if (ast.isFqnElementRef(container) && context.property === 'el') {
102
+ if (ast.isStrictFqnElementRef(container) && context.property === 'el') {
103
103
  const parent = container.parent
104
104
  if (!parent) {
105
105
  return this.getGlobalScope(referenceType, context)
@@ -116,7 +116,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
116
116
  const closestElement = AstUtils.getContainerOfType(container, ast.isElement)
117
117
  if (closestElement) {
118
118
  return new MapScope([
119
- this.descriptions.createDescription(closestElement, context.reference.$refText)
119
+ this.descriptions.createDescription(closestElement, context.reference.$refText),
120
120
  ])
121
121
  } else {
122
122
  return EMPTY_SCOPE
@@ -146,8 +146,8 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
146
146
  // Third preference for elements if we are in deployment view
147
147
  AstUtils.hasContainerOfType(container, ast.isDeploymentView)
148
148
  ? this.computeScope(context, ast.Element)
149
- : EMPTY_SCOPE
150
- )
149
+ : EMPTY_SCOPE,
150
+ ),
151
151
  )
152
152
  }
153
153
  const parentRef = parent.value.ref
@@ -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'
@@ -16,7 +17,7 @@ export function createTestServices(workspace = 'file:///test/workspace') {
16
17
  const formatter = services.lsp.Formatter
17
18
  const workspaceFolder = {
18
19
  name: 'test',
19
- uri: workspaceUri.toString()
20
+ uri: workspaceUri.toString(),
20
21
  }
21
22
  let isInitialized = false
22
23
  let documentIndex = 1
@@ -32,7 +33,7 @@ export function createTestServices(workspace = 'file:///test/workspace') {
32
33
  capabilities: {},
33
34
  processId: null,
34
35
  rootUri: null,
35
- workspaceFolders: [workspaceFolder]
36
+ workspaceFolders: [workspaceFolder],
36
37
  })
37
38
  await services.shared.workspace.WorkspaceManager.initializeWorkspace([workspaceFolder])
38
39
  })
@@ -40,11 +41,11 @@ export function createTestServices(workspace = 'file:///test/workspace') {
40
41
  const docUri = Utils.resolvePath(
41
42
  workspaceUri,
42
43
  './src/',
43
- uri ?? `${documentIndex++}${metaData.fileExtensions[0]}`
44
+ uri ?? `${documentIndex++}${metaData.fileExtensions[0]}`,
44
45
  )
45
46
  const document = services.shared.workspace.LangiumDocumentFactory.fromString(
46
47
  stripIndent(input),
47
- docUri
48
+ docUri,
48
49
  )
49
50
  langiumDocuments.addDocument(document)
50
51
  await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
@@ -65,7 +66,7 @@ export function createTestServices(workspace = 'file:///test/workspace') {
65
66
  document,
66
67
  diagnostics,
67
68
  warnings,
68
- errors
69
+ errors,
69
70
  }
70
71
  }
71
72
 
@@ -79,8 +80,8 @@ export function createTestServices(workspace = 'file:///test/workspace') {
79
80
  document,
80
81
  {
81
82
  options: { tabSize: 2, insertSpaces: true },
82
- textDocument: { uri: document.uri.toString() }
83
- }
83
+ textDocument: { uri: document.uri.toString() },
84
+ },
84
85
  )
85
86
 
86
87
  return TextDocument.applyEdits(document.textDocument, edits ?? [])
@@ -106,7 +107,7 @@ export function createTestServices(workspace = 'file:///test/workspace') {
106
107
  return {
107
108
  diagnostics,
108
109
  errors,
109
- warnings
110
+ warnings,
110
111
  }
111
112
  }
112
113
 
@@ -117,6 +118,13 @@ export function createTestServices(workspace = 'file:///test/workspace') {
117
118
  return model
118
119
  }
119
120
 
121
+ const buildLikeC4Model = async () => {
122
+ await validateAll()
123
+ const model = await modelBuilder.buildComputedModel()
124
+ if (!model) throw new Error('No model found')
125
+ return LikeC4Model.create(model)
126
+ }
127
+
120
128
  /**
121
129
  * This will clear all documents
122
130
  */
@@ -133,8 +141,9 @@ export function createTestServices(workspace = 'file:///test/workspace') {
133
141
  validate,
134
142
  validateAll,
135
143
  buildModel,
144
+ buildLikeC4Model,
136
145
  resetState,
137
- format
146
+ format,
138
147
  }
139
148
  }
140
149