@likec4/language-server 1.19.1 → 1.20.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.
@@ -413,11 +413,13 @@ WhereRelationNegation:
413
413
 
414
414
  WhereRelation:
415
415
  {infer WhereRelationTag} 'tag' EqOperator value=[Tag:TagId]? |
416
- {infer WhereRelationKind} 'kind' EqOperator value=[RelationshipKind:Id]?
416
+ {infer WhereRelationKind} 'kind' EqOperator value=[RelationshipKind:Id]? |
417
+ {infer WhereRelationParticipantTag} participant=Participant StickyDot 'tag' EqOperator value=[Tag:TagId]? |
418
+ {infer WhereRelationParticipantKind} participant=Participant StickyDot 'kind' EqOperator value=[DeploymentNodeOrElementKind:Id]?
417
419
  ;
418
-
419
- type WhereTagEqual = WhereElementTag | WhereRelationTag;
420
- type WhereKindEqual = WhereElementKind | WhereRelationKind;
420
+ type DeploymentNodeOrElementKind = ElementKind | DeploymentNodeKind
421
+ type WhereTagEqual = WhereElementTag | WhereRelationTag | WhereRelationParticipantTag;
422
+ type WhereKindEqual = WhereElementKind | WhereRelationKind | WhereRelationParticipantKind;
421
423
 
422
424
  type WhereExpression = WhereElementExpression | WhereRelationExpression;
423
425
 
@@ -620,10 +622,13 @@ DeploymentViewRulePredicateExpression:
620
622
  ;
621
623
 
622
624
  ExpressionV2:
623
- RelationExpr |
625
+ RelationPredicateOrWhereV2 |
624
626
  FqnExpr
625
627
  ;
626
628
 
629
+ RelationPredicateOrWhereV2:
630
+ RelationExpr ({infer RelationPredicateWhereV2.subject=current} 'where' where=WhereRelationExpression?)?
631
+ ;
627
632
 
628
633
  FqnExpr:
629
634
  {infer WildcardExpression} isWildcard?='*' |
@@ -766,6 +771,8 @@ ThemeColor returns string:
766
771
  'primary' | 'secondary' | 'muted' | 'slate' | 'blue' | 'indigo' | 'sky' | 'red' | 'gray' | 'green' | 'amber';
767
772
  ElementShape returns string:
768
773
  'rectangle' | 'person' | 'browser' | 'mobile' | 'cylinder' | 'storage' | 'queue';
774
+ Participant returns string:
775
+ 'source' | 'target';
769
776
 
770
777
  CustomColorValue returns string:
771
778
  Hash (Id | Hex | Number);
@@ -786,7 +793,7 @@ CustomColorId returns string:
786
793
  IdTerminal | ElementShape | ArrowType | LineOptions | 'element' | 'model';
787
794
 
788
795
  Id returns string:
789
- IdTerminal | ElementShape | ThemeColor | ArrowType | LineOptions | 'element' | 'model' | 'group' | 'node' | 'deployment' | 'instance';
796
+ IdTerminal | ElementShape | ThemeColor | ArrowType | LineOptions | Participant | 'element' | 'model' | 'group' | 'node' | 'deployment' | 'instance';
790
797
 
791
798
  fragment EqOperator:
792
799
  (
@@ -1,5 +1,5 @@
1
1
  import type { AstNode } from 'langium'
2
- import { AbstractSemanticTokenProvider, type SemanticTokenAcceptor } from 'langium/lsp'
2
+ import { type SemanticTokenAcceptor, AbstractSemanticTokenProvider } from 'langium/lsp'
3
3
  import { isTruthy } from 'remeda'
4
4
  import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver-types'
5
5
  import { ast } from '../ast'
@@ -8,7 +8,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
8
8
  // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
9
9
  protected override highlightElement(
10
10
  node: AstNode,
11
- acceptor: SemanticTokenAcceptor
11
+ acceptor: SemanticTokenAcceptor,
12
12
  ): void | undefined | 'prune' {
13
13
  if (ast.isElement(node) || ast.isDeploymentNode(node)) {
14
14
  return this.highlightNameAndKind(node, acceptor)
@@ -21,8 +21,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
21
21
  type: SemanticTokenTypes.variable,
22
22
  modifier: [
23
23
  SemanticTokenModifiers.definition,
24
- SemanticTokenModifiers.readonly
25
- ]
24
+ SemanticTokenModifiers.readonly,
25
+ ],
26
26
  })
27
27
  }
28
28
  return
@@ -34,7 +34,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
34
34
  return acceptor({
35
35
  node,
36
36
  property: 'name',
37
- type: SemanticTokenTypes.function
37
+ type: SemanticTokenTypes.function,
38
38
  })
39
39
  }
40
40
  if (ast.isLibIcon(node)) {
@@ -42,7 +42,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
42
42
  node,
43
43
  property: 'name',
44
44
  type: SemanticTokenTypes.type,
45
- modifier: [SemanticTokenModifiers.definition]
45
+ modifier: [SemanticTokenModifiers.definition],
46
46
  })
47
47
  return 'prune'
48
48
  }
@@ -51,7 +51,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
51
51
  acceptor({
52
52
  node,
53
53
  property: 'kind',
54
- type: SemanticTokenTypes.function
54
+ type: SemanticTokenTypes.function,
55
55
  })
56
56
  return
57
57
  }
@@ -59,14 +59,14 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
59
59
  acceptor({
60
60
  node,
61
61
  property: 'kind',
62
- type: SemanticTokenTypes.function
62
+ type: SemanticTokenTypes.function,
63
63
  })
64
64
  }
65
65
  if (ast.isNavigateToProperty(node) || ast.isRelationNavigateToProperty(node)) {
66
66
  acceptor({
67
67
  node,
68
68
  property: 'key',
69
- type: SemanticTokenTypes.property
69
+ type: SemanticTokenTypes.property,
70
70
  })
71
71
  acceptor({
72
72
  node,
@@ -74,8 +74,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
74
74
  type: SemanticTokenTypes.variable,
75
75
  modifier: [
76
76
  SemanticTokenModifiers.definition,
77
- SemanticTokenModifiers.readonly
78
- ]
77
+ SemanticTokenModifiers.readonly,
78
+ ],
79
79
  })
80
80
  return 'prune'
81
81
  }
@@ -85,8 +85,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
85
85
  type: SemanticTokenTypes.variable,
86
86
  modifier: [
87
87
  SemanticTokenModifiers.definition,
88
- SemanticTokenModifiers.readonly
89
- ]
88
+ SemanticTokenModifiers.readonly,
89
+ ],
90
90
  })
91
91
  return 'prune'
92
92
  }
@@ -98,8 +98,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
98
98
  type: SemanticTokenTypes.variable,
99
99
  modifier: [
100
100
  SemanticTokenModifiers.definition,
101
- SemanticTokenModifiers.readonly
102
- ]
101
+ SemanticTokenModifiers.readonly,
102
+ ],
103
103
  })
104
104
  }
105
105
  return
@@ -108,7 +108,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
108
108
  acceptor({
109
109
  node,
110
110
  property: 'value',
111
- type: SemanticTokenTypes.function
111
+ type: SemanticTokenTypes.function,
112
112
  })
113
113
  return
114
114
  }
@@ -119,17 +119,24 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
119
119
  type: SemanticTokenTypes.type,
120
120
  modifier: [
121
121
  SemanticTokenModifiers.definition,
122
- SemanticTokenModifiers.readonly
123
- ]
122
+ SemanticTokenModifiers.readonly,
123
+ ],
124
124
  })
125
125
  return
126
126
  }
127
+ if ((ast.isWhereRelationParticipantKind(node) || ast.isWhereRelationParticipantTag(node))) {
128
+ acceptor({
129
+ node,
130
+ property: 'participant',
131
+ type: SemanticTokenTypes.keyword,
132
+ })
133
+ }
127
134
  if (ast.isElementKindExpression(node) && isTruthy(node.kind)) {
128
135
  acceptor({
129
136
  node,
130
137
  property: 'kind',
131
138
  type: SemanticTokenTypes.type,
132
- modifier: [SemanticTokenModifiers.definition]
139
+ modifier: [SemanticTokenModifiers.definition],
133
140
  })
134
141
  return 'prune'
135
142
  }
@@ -138,7 +145,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
138
145
  node,
139
146
  property: 'tag',
140
147
  type: SemanticTokenTypes.type,
141
- modifier: [SemanticTokenModifiers.definition]
148
+ modifier: [SemanticTokenModifiers.definition],
142
149
  })
143
150
  return 'prune'
144
151
  }
@@ -149,8 +156,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
149
156
  type: node.parent ? SemanticTokenTypes.property : SemanticTokenTypes.variable,
150
157
  modifier: [
151
158
  SemanticTokenModifiers.definition,
152
- SemanticTokenModifiers.readonly
153
- ]
159
+ SemanticTokenModifiers.readonly,
160
+ ],
154
161
  })
155
162
  return !node.parent ? 'prune' : undefined
156
163
  }
@@ -161,8 +168,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
161
168
  type: node.parent ? SemanticTokenTypes.property : SemanticTokenTypes.variable,
162
169
  modifier: [
163
170
  SemanticTokenModifiers.definition,
164
- SemanticTokenModifiers.readonly
165
- ]
171
+ SemanticTokenModifiers.readonly,
172
+ ],
166
173
  })
167
174
  return !node.parent ? 'prune' : undefined
168
175
  }
@@ -170,7 +177,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
170
177
  acceptor({
171
178
  node,
172
179
  keyword: 'color',
173
- type: SemanticTokenTypes.keyword
180
+ type: SemanticTokenTypes.keyword,
174
181
  })
175
182
  acceptor({
176
183
  node,
@@ -178,8 +185,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
178
185
  type: SemanticTokenTypes.type,
179
186
  modifier: [
180
187
  SemanticTokenModifiers.declaration,
181
- SemanticTokenModifiers.readonly
182
- ]
188
+ SemanticTokenModifiers.readonly,
189
+ ],
183
190
  })
184
191
  return
185
192
  }
@@ -193,15 +200,15 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
193
200
  type: SemanticTokenTypes.type,
194
201
  modifier: [
195
202
  SemanticTokenModifiers.declaration,
196
- SemanticTokenModifiers.readonly
197
- ]
203
+ SemanticTokenModifiers.readonly,
204
+ ],
198
205
  })
199
206
  }
200
207
  if (ast.isTags(node)) {
201
208
  return acceptor({
202
209
  node,
203
210
  property: 'values',
204
- type: SemanticTokenTypes.interface
211
+ type: SemanticTokenTypes.interface,
205
212
  })
206
213
  }
207
214
  if (ast.isTag(node)) {
@@ -209,7 +216,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
209
216
  node,
210
217
  property: 'name',
211
218
  type: SemanticTokenTypes.type,
212
- modifier: [SemanticTokenModifiers.definition]
219
+ modifier: [SemanticTokenModifiers.definition],
213
220
  })
214
221
  }
215
222
  if (
@@ -219,19 +226,19 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
219
226
  acceptor({
220
227
  node,
221
228
  property: 'key',
222
- type: SemanticTokenTypes.property
229
+ type: SemanticTokenTypes.property,
223
230
  })
224
231
  }
225
232
  if (ast.isOpacityProperty(node)) {
226
233
  acceptor({
227
234
  node,
228
235
  property: 'key',
229
- type: SemanticTokenTypes.property
236
+ type: SemanticTokenTypes.property,
230
237
  })
231
238
  acceptor({
232
239
  node,
233
240
  property: 'value',
234
- type: SemanticTokenTypes.number
241
+ type: SemanticTokenTypes.number,
235
242
  })
236
243
  return 'prune'
237
244
  }
@@ -243,14 +250,14 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
243
250
  acceptor({
244
251
  node,
245
252
  property: 'key',
246
- type: SemanticTokenTypes.property
253
+ type: SemanticTokenTypes.property,
247
254
  })
248
255
  if (ast.isIconProperty(node) && (node.libicon || node.value === 'none')) {
249
256
  acceptor({
250
257
  node,
251
258
  property: node.libicon ? 'libicon' : 'value',
252
259
  type: SemanticTokenTypes.enum,
253
- modifier: [SemanticTokenModifiers.defaultLibrary]
260
+ modifier: [SemanticTokenModifiers.defaultLibrary],
254
261
  })
255
262
  return 'prune'
256
263
  }
@@ -258,7 +265,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
258
265
  acceptor({
259
266
  node,
260
267
  property: 'value',
261
- type: SemanticTokenTypes.string
268
+ type: SemanticTokenTypes.string,
262
269
  })
263
270
  }
264
271
  return 'prune'
@@ -273,13 +280,13 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
273
280
  acceptor({
274
281
  node,
275
282
  property: 'key',
276
- type: SemanticTokenTypes.property
283
+ type: SemanticTokenTypes.property,
277
284
  })
278
285
  if ('value' in node) {
279
286
  acceptor({
280
287
  node,
281
288
  property: 'value',
282
- type: SemanticTokenTypes.enum
289
+ type: SemanticTokenTypes.enum,
283
290
  })
284
291
  }
285
292
  return 'prune'
@@ -293,14 +300,14 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
293
300
  type: SemanticTokenTypes.variable,
294
301
  modifier: [
295
302
  SemanticTokenModifiers.declaration,
296
- SemanticTokenModifiers.readonly
297
- ]
303
+ SemanticTokenModifiers.readonly,
304
+ ],
298
305
  })
299
306
  acceptor({
300
307
  node,
301
308
  property: 'kind',
302
309
  type: SemanticTokenTypes.keyword,
303
- modifier: []
310
+ modifier: [],
304
311
  })
305
312
  }
306
313
 
@@ -313,8 +320,8 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
313
320
  modifier: [
314
321
  SemanticTokenModifiers.declaration,
315
322
  SemanticTokenModifiers.readonly,
316
- 'local'
317
- ]
323
+ 'local',
324
+ ],
318
325
  })
319
326
  }
320
327
  }
@@ -5,40 +5,48 @@ import { ast } from '../ast'
5
5
 
6
6
  const parseEquals = (
7
7
  { operator, not }: ast.WhereKindEqual | ast.WhereTagEqual,
8
- value: string
8
+ value: string,
9
9
  ): c4.EqualOperator<string> => {
10
10
  if (operator.startsWith('!=')) {
11
11
  return {
12
- neq: value
12
+ neq: value,
13
13
  }
14
14
  }
15
15
  if (operator.startsWith('=')) {
16
16
  return {
17
- eq: value
17
+ eq: value,
18
18
  }
19
19
  }
20
20
  return not ? { neq: value } : { eq: value }
21
21
  }
22
22
 
23
+ function parseParticipant(astNode: ast.WhereExpression): ast.Participant | null {
24
+ if (!ast.isWhereRelationParticipantKind(astNode) && !ast.isWhereRelationParticipantTag(astNode)) {
25
+ return null
26
+ }
27
+
28
+ return astNode.participant
29
+ }
30
+
23
31
  export function parseWhereClause(astNode: ast.WhereExpression): c4.WhereOperator<string, string> {
24
32
  switch (true) {
25
33
  case ast.isWhereTagEqual(astNode): {
26
34
  const tag = astNode.value?.ref?.name
35
+ const participant = parseParticipant(astNode)
27
36
  invariant(tag, 'Expected tag name')
28
- return {
29
- tag: parseEquals(astNode, tag)
30
- }
37
+ const tagOperator = { tag: parseEquals(astNode, tag) }
38
+ return participant ? { participant, operator: tagOperator } : tagOperator
31
39
  }
32
40
  case ast.isWhereKindEqual(astNode): {
33
41
  const kind = astNode.value?.ref?.name
42
+ const participant = parseParticipant(astNode)
34
43
  invariant(kind, 'Expected kind name')
35
- return {
36
- kind: parseEquals(astNode, kind)
37
- }
44
+ const kindOperator = { kind: parseEquals(astNode, kind) }
45
+ return participant ? { participant, operator: kindOperator } : kindOperator
38
46
  }
39
47
  case ast.isWhereElementNegation(astNode) || ast.isWhereRelationNegation(astNode): {
40
48
  return {
41
- not: parseWhereClause(astNode.value)
49
+ not: parseWhereClause(astNode.value),
42
50
  }
43
51
  }
44
52
  case ast.isWhereBinaryExpression(astNode): {
@@ -49,21 +57,21 @@ export function parseWhereClause(astNode: ast.WhereExpression): c4.WhereOperator
49
57
  case 'and': {
50
58
  const operands = [
51
59
  isAndOperator(left) ? left.and : left,
52
- isAndOperator(right) ? right.and : right
60
+ isAndOperator(right) ? right.and : right,
53
61
  ].flat()
54
62
  invariant(isNonEmptyArray(operands), 'Expected non-empty array')
55
63
  return {
56
- and: operands
64
+ and: operands,
57
65
  }
58
66
  }
59
67
  case 'or': {
60
68
  const operands = [
61
69
  isOrOperator(left) ? left.or : left,
62
- isOrOperator(right) ? right.or : right
70
+ isOrOperator(right) ? right.or : right,
63
71
  ].flat()
64
72
  invariant(isNonEmptyArray(operands), 'Expected non-empty array')
65
73
  return {
66
- or: operands
74
+ or: operands,
67
75
  }
68
76
  }
69
77
  default:
@@ -1,7 +1,7 @@
1
1
  import type * as c4 from '@likec4/core'
2
- import { FqnExpr, invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core'
2
+ import { invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core'
3
3
  import { isNonNullish } from 'remeda'
4
- import { ast, type ParsedAstDeploymentView, toAutoLayout, toElementStyle, ViewOps } from '../../ast'
4
+ import { type ParsedAstDeploymentView, ast, toAutoLayout, toElementStyle, ViewOps } from '../../ast'
5
5
  import { logWarnError } from '../../logger'
6
6
  import { stringHash } from '../../utils'
7
7
  import { parseViewManualLayout } from '../../view-utils/manual-layout'
@@ -14,7 +14,7 @@ export type WithDeploymentView = ReturnType<typeof DeploymentViewParser>
14
14
  export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploymentModel>(B: TBase) {
15
15
  return class DeploymentViewParser extends B {
16
16
  parseDeploymentView(
17
- astNode: ast.DeploymentView
17
+ astNode: ast.DeploymentView,
18
18
  ): ParsedAstDeploymentView {
19
19
  const body = astNode.body
20
20
  invariant(body, 'DynamicElementView body is not defined')
@@ -26,7 +26,7 @@ export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploy
26
26
  if (!id) {
27
27
  id = 'deployment_' + stringHash(
28
28
  this.doc.uri.toString(),
29
- astPath
29
+ astPath,
30
30
  ) as c4.ViewId
31
31
  }
32
32
 
@@ -56,7 +56,7 @@ export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploy
56
56
  return []
57
57
  }
58
58
  }),
59
- ...(manualLayout && { manualLayout })
59
+ ...(manualLayout && { manualLayout }),
60
60
  }
61
61
  }
62
62
 
@@ -87,6 +87,9 @@ export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploy
87
87
  case ast.isRelationExpr(expr):
88
88
  exprs.unshift(this.parseRelationExpr(expr))
89
89
  break
90
+ case ast.isRelationPredicateWhereV2(expr):
91
+ exprs.unshift(this.parseRelationWhereExpr(expr))
92
+ break
90
93
  default:
91
94
  nonexhaustive(expr)
92
95
  }
@@ -108,8 +111,8 @@ export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploy
108
111
  targets,
109
112
  ...(notation && { notation }),
110
113
  style: {
111
- ...toElementStyle(styleProps, this.isValid)
112
- }
114
+ ...toElementStyle(styleProps, this.isValid),
115
+ },
113
116
  }
114
117
  }
115
118
  }
@@ -4,6 +4,7 @@ import { isNonNullish } from 'remeda'
4
4
  import { ast } from '../../ast'
5
5
  import { logWarnError } from '../../logger'
6
6
  import { instanceRef } from '../../utils/fqnRef'
7
+ import { parseWhereClause } from '../model-parser-where'
7
8
  import type { Base } from './Base'
8
9
 
9
10
  export type WithExpressionV2 = ReturnType<typeof ExpressionV2Parser>
@@ -89,7 +90,20 @@ export function ExpressionV2Parser<TBase extends Base>(B: TBase) {
89
90
  return exprs.reverse()
90
91
  }
91
92
 
93
+ parseRelationWhereExpr(astNode: ast.RelationPredicateWhereV2): c4.RelationExpr {
94
+ return {
95
+ where: {
96
+ expr: this.parseRelationExpr(astNode.subject as ast.RelationExpr),
97
+ condition: astNode.where ? parseWhereClause(astNode.where) : {
98
+ kind: { neq: '--always-true--' },
99
+ },
100
+ },
101
+ }
102
+ }
103
+
92
104
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr {
105
+ if (ast.isRelationPredicateWhere(astNode)) {
106
+ }
93
107
  if (ast.isDirectedRelationExpr(astNode)) {
94
108
  return {
95
109
  source: this.parseFqnExpr(astNode.source.from),