@likec4/language-server 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/contrib/likec4.tmLanguage.json +1 -1
- package/package.json +26 -14
- package/src/Rpc.ts +25 -2
- package/src/ast.ts +19 -15
- package/src/generated/ast.ts +390 -203
- package/src/generated/grammar.ts +1 -1
- package/src/generated-lib/icons.ts +952 -0
- package/src/like-c4.langium +120 -64
- package/src/likec4lib.ts +7 -0
- package/src/lsp/DocumentSymbolProvider.ts +28 -1
- package/src/lsp/SemanticTokenProvider.ts +41 -22
- package/src/model/fqn-computation.ts +29 -7
- package/src/model/fqn-index.ts +18 -31
- package/src/model/model-builder.ts +13 -9
- package/src/model/model-locator.ts +7 -7
- package/src/model/model-parser.ts +166 -69
- package/src/model-change/changeElementStyle.ts +6 -6
- package/src/model-graph/compute-view/__test__/fixture.ts +52 -24
- package/src/model-graph/compute-view/compute.ts +51 -20
- package/src/model-graph/compute-view/predicates.ts +6 -1
- package/src/model-graph/dynamic-view/__test__/fixture.ts +2 -2
- package/src/model-graph/dynamic-view/compute.ts +2 -2
- package/src/model-graph/utils/{applyElementCustomProperties.ts → applyCustomElementProperties.ts} +5 -3
- package/src/model-graph/utils/applyCustomRelationProperties.ts +50 -0
- package/src/model-graph/utils/applyViewRuleStyles.ts +11 -34
- package/src/model-graph/utils/elementExpressionToPredicate.ts +32 -0
- package/src/references/scope-computation.ts +113 -60
- package/src/references/scope-provider.ts +3 -23
- package/src/shared/NodeKindProvider.ts +1 -0
- package/src/shared/WorkspaceManager.ts +15 -6
- package/src/validation/dynamic-view-rule.ts +19 -26
- package/src/validation/element.ts +8 -4
- package/src/validation/index.ts +9 -6
- package/src/validation/property-checks.ts +23 -1
- package/src/validation/view-predicates/custom-element-expr.ts +21 -8
- package/src/validation/view-predicates/custom-relation-expr.ts +16 -0
- package/src/validation/view-predicates/expanded-element.ts +13 -24
- package/src/validation/view-predicates/incoming.ts +5 -5
- package/src/validation/view-predicates/index.ts +1 -0
- package/src/validation/view-predicates/outgoing.ts +5 -5
- package/src/view-utils/assignNavigateTo.ts +2 -2
- package/src/view-utils/manual-layout.ts +4 -2
- package/src/view-utils/resolve-extended-views.ts +2 -2
- package/src/view-utils/resolve-relative-paths.ts +3 -3
package/src/like-c4.langium
CHANGED
|
@@ -4,10 +4,22 @@ entry LikeC4Grammar:
|
|
|
4
4
|
(
|
|
5
5
|
specifications+=SpecificationRule |
|
|
6
6
|
models+=Model |
|
|
7
|
-
views+=ModelViews
|
|
7
|
+
views+=ModelViews |
|
|
8
|
+
likec4lib+=LikeC4Lib
|
|
8
9
|
)*
|
|
9
10
|
;
|
|
10
11
|
|
|
12
|
+
// Lib -------------------------------------
|
|
13
|
+
LikeC4Lib:
|
|
14
|
+
'likec4lib' '{'
|
|
15
|
+
'icons' '{'
|
|
16
|
+
(icons+=LibIcon)+
|
|
17
|
+
'}'
|
|
18
|
+
'}';
|
|
19
|
+
|
|
20
|
+
LibIcon:
|
|
21
|
+
name=IconId;
|
|
22
|
+
|
|
11
23
|
// Specification -------------------------------------
|
|
12
24
|
|
|
13
25
|
ElementKind:
|
|
@@ -80,10 +92,10 @@ ElementBody: '{'
|
|
|
80
92
|
;
|
|
81
93
|
|
|
82
94
|
ElementProperty:
|
|
83
|
-
ElementStringProperty | StyleProperties | LinkProperty;
|
|
95
|
+
ElementStringProperty | StyleProperties | LinkProperty | IconProperty;
|
|
84
96
|
|
|
85
97
|
ElementStringProperty:
|
|
86
|
-
key=('title' | 'technology' | 'description')
|
|
98
|
+
key=('title' | 'technology' | 'description') ':'? value=String ';'?;
|
|
87
99
|
|
|
88
100
|
ExtendElement:
|
|
89
101
|
'extend' element=FqnElementRef body=ExtendElementBody
|
|
@@ -105,7 +117,7 @@ ElementRef:
|
|
|
105
117
|
el=[Element] ({infer ElementRef.parent=current} dot=StickyDot el=[Element])*;
|
|
106
118
|
|
|
107
119
|
Tags:
|
|
108
|
-
|
|
120
|
+
value+=[Tag:TagId] (','? value+=[Tag:TagId])* (';')?
|
|
109
121
|
;
|
|
110
122
|
|
|
111
123
|
Relation:
|
|
@@ -135,7 +147,7 @@ RelationProperty:
|
|
|
135
147
|
RelationStringProperty | RelationStyleProperty | LinkProperty;
|
|
136
148
|
|
|
137
149
|
RelationStringProperty:
|
|
138
|
-
key='title'
|
|
150
|
+
key='title' ':'? value=String ';'?;
|
|
139
151
|
|
|
140
152
|
RelationStyleProperty:
|
|
141
153
|
key='style' '{'
|
|
@@ -168,13 +180,6 @@ DynamicView:
|
|
|
168
180
|
'dynamic' 'view' name=Id body=DynamicViewBody
|
|
169
181
|
;
|
|
170
182
|
|
|
171
|
-
DynamicViewBody: '{'
|
|
172
|
-
tags=Tags?
|
|
173
|
-
props+=ViewProperty*
|
|
174
|
-
(steps+=DynamicViewStep | rules+=DynamicViewRule)*
|
|
175
|
-
'}'
|
|
176
|
-
;
|
|
177
|
-
|
|
178
183
|
ViewRef:
|
|
179
184
|
view=[LikeC4View];
|
|
180
185
|
|
|
@@ -188,14 +193,22 @@ ElementViewBody: '{'
|
|
|
188
193
|
'}'
|
|
189
194
|
;
|
|
190
195
|
|
|
191
|
-
|
|
196
|
+
DynamicViewBody: '{'
|
|
197
|
+
tags=Tags?
|
|
198
|
+
props+=ViewProperty*
|
|
199
|
+
(steps+=DynamicViewStep | rules+=DynamicViewRule)*
|
|
200
|
+
'}'
|
|
201
|
+
;
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
type StringProperty = ElementStringProperty| ViewStringProperty | RelationStringProperty;
|
|
192
205
|
|
|
193
206
|
ViewProperty:
|
|
194
207
|
ViewStringProperty | LinkProperty
|
|
195
208
|
;
|
|
196
209
|
|
|
197
210
|
ViewStringProperty:
|
|
198
|
-
key=('title' | 'description')
|
|
211
|
+
key=('title' | 'description') ':'? value=String ';'?;
|
|
199
212
|
|
|
200
213
|
ViewLayoutDirection returns string:
|
|
201
214
|
'TopBottom' | 'LeftRight' | 'BottomTop' | 'RightLeft';
|
|
@@ -213,67 +226,95 @@ DynamicViewRule:
|
|
|
213
226
|
;
|
|
214
227
|
|
|
215
228
|
DynamicViewStep:
|
|
216
|
-
source=ElementRef (isBackward?='<-' | '->' | '-[' kind=[RelationshipKind] ']->') target=ElementRef title=String?
|
|
229
|
+
source=ElementRef (isBackward?='<-' | '->' | '-[' kind=[RelationshipKind] ']->' | kind=[RelationshipKind:DotId] ) target=ElementRef title=String?
|
|
217
230
|
;
|
|
218
231
|
|
|
219
232
|
ViewRulePredicate:
|
|
220
|
-
|
|
221
|
-
|
|
233
|
+
{infer IncludePredicate} 'include' exprs=Expressions |
|
|
234
|
+
{infer ExcludePredicate} 'exclude' exprs=Expressions
|
|
222
235
|
;
|
|
223
236
|
|
|
224
|
-
|
|
225
|
-
|
|
237
|
+
// IncludePredicate:
|
|
238
|
+
// 'include' expr=WithExpressions ({infer IncludePredicate.prev=current} ',' (expr=WithExpressions)?)*
|
|
239
|
+
// ;
|
|
240
|
+
// ExcludePredicate:
|
|
241
|
+
// 'exclude' expr=WithExpressions ({infer ExcludePredicate.prev=current} ',' (expr=WithExpressions)?)*
|
|
242
|
+
// ;
|
|
243
|
+
|
|
244
|
+
Expressions:
|
|
245
|
+
value=WithExpressions ({infer Expressions.prev=current} ',' (value=WithExpressions)?)*
|
|
226
246
|
;
|
|
227
247
|
|
|
228
|
-
|
|
229
|
-
'
|
|
230
|
-
|
|
231
|
-
|
|
248
|
+
WithExpressions infers Expression:
|
|
249
|
+
RelationExpression ({infer CustomRelationExpression.relation=current} 'with' custom=CustomRelationProperties)? |
|
|
250
|
+
ElementExpression ({infer CustomElementExpression.target=current} 'with' custom=CustomElementProperties)?
|
|
251
|
+
;
|
|
232
252
|
|
|
233
|
-
|
|
234
|
-
|
|
253
|
+
ElementExpression:
|
|
254
|
+
ElementSelectorExpression |
|
|
255
|
+
ElementDescedantsExpression
|
|
256
|
+
;
|
|
257
|
+
|
|
258
|
+
ElementSelectorExpression infers ElementExpression:
|
|
259
|
+
{infer WildcardExpression} isWildcard?='*' |
|
|
260
|
+
{infer ElementTagExpression} 'element.tag' IsEqual tag=[Tag:TagId]? |
|
|
261
|
+
{infer ElementKindExpression} 'element.kind' IsEqual kind=[ElementKind]?
|
|
262
|
+
;
|
|
235
263
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
{infer
|
|
240
|
-
{infer RelationExpr.source=current} (isBidirectional?='<->' | '->') target=ElementExpr |
|
|
241
|
-
{infer OutgoingExpr.from=current} '->'
|
|
264
|
+
ElementDescedantsExpression infers ElementExpression:
|
|
265
|
+
ElementRef (
|
|
266
|
+
{infer ExpandElementExpression.expand=current} DotUnderscore |
|
|
267
|
+
{infer ElementDescedantsExpression.parent=current} DotWildcard
|
|
242
268
|
)?
|
|
243
269
|
;
|
|
244
270
|
|
|
245
|
-
|
|
246
|
-
|
|
271
|
+
RelationExpression:
|
|
272
|
+
InOutRelationExpressions |
|
|
273
|
+
DirectedRelationExpressions
|
|
274
|
+
;
|
|
247
275
|
|
|
248
|
-
|
|
249
|
-
'->'
|
|
276
|
+
InOutRelationExpressions infers RelationExpression:
|
|
277
|
+
IncomingRelationExpression ({infer InOutRelationExpression.inout=current} '->')?
|
|
278
|
+
;
|
|
250
279
|
|
|
280
|
+
IncomingRelationExpression:
|
|
281
|
+
'->' to=ElementExpression;
|
|
251
282
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
ElementSelectorExpr |
|
|
255
|
-
DescedantsExpr
|
|
283
|
+
DirectedRelationExpressions infers RelationExpression:
|
|
284
|
+
OutgoingRelationExpression ({infer DirectedRelationExpression.source=current} target=ElementExpression)?
|
|
256
285
|
;
|
|
257
286
|
|
|
258
|
-
|
|
259
|
-
|
|
287
|
+
OutgoingRelationExpression:
|
|
288
|
+
from=ElementExpression
|
|
289
|
+
(isBidirectional?='<->' | '->' | '-[' kind=[RelationshipKind] ']->' | kind=[RelationshipKind:DotId])
|
|
260
290
|
;
|
|
261
291
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
{infer ElementKindExpr} 'kind' IsEqual kind=[ElementKind]
|
|
266
|
-
)
|
|
292
|
+
// Comma-separated list of ElementExpressions
|
|
293
|
+
ElementExpressionsIterator:
|
|
294
|
+
value=ElementExpression ({infer ElementExpressionsIterator.prev=current} ',' (value=ElementExpression)?)*
|
|
267
295
|
;
|
|
268
296
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
297
|
+
DynamicViewRulePredicate:
|
|
298
|
+
'include' exprs=DynamicViewRulePredicateIterator
|
|
299
|
+
;
|
|
300
|
+
|
|
301
|
+
DynamicViewRulePredicateIterator:
|
|
302
|
+
value=DynamicViewElementExpressions ({infer DynamicViewRulePredicateIterator.prev=current} ',' (value=DynamicViewElementExpressions)?)*
|
|
274
303
|
;
|
|
275
304
|
|
|
276
|
-
|
|
305
|
+
DynamicViewElementExpressions infers DynamicViewElementExpression:
|
|
306
|
+
ElementExpression ({infer CustomElementExpression.target=current} 'with' custom=CustomElementProperties)?
|
|
307
|
+
;
|
|
308
|
+
|
|
309
|
+
ViewRuleStyle:
|
|
310
|
+
'style' target=ElementExpressionsIterator '{'
|
|
311
|
+
props+=StyleProperty*
|
|
312
|
+
'}';
|
|
313
|
+
|
|
314
|
+
ViewRuleAutoLayout:
|
|
315
|
+
'autoLayout' direction=ViewLayoutDirection;
|
|
316
|
+
|
|
317
|
+
CustomElementProperties: '{'
|
|
277
318
|
props+=(
|
|
278
319
|
NavigateToProperty |
|
|
279
320
|
ElementStringProperty |
|
|
@@ -282,32 +323,40 @@ CustomElementExprBody: '{'
|
|
|
282
323
|
'}'
|
|
283
324
|
;
|
|
284
325
|
|
|
326
|
+
CustomRelationProperties: '{'
|
|
327
|
+
props+=(
|
|
328
|
+
RelationStringProperty |
|
|
329
|
+
RelationshipStyleProperty
|
|
330
|
+
)*
|
|
331
|
+
'}'
|
|
332
|
+
;
|
|
333
|
+
|
|
285
334
|
NavigateToProperty:
|
|
286
335
|
key='navigateTo' value=ViewRef;
|
|
287
336
|
|
|
288
337
|
// Common properties -------------------------------------
|
|
289
338
|
|
|
290
339
|
LinkProperty:
|
|
291
|
-
key='link'
|
|
340
|
+
key='link' ':'? value=Uri ';'?;
|
|
292
341
|
ColorProperty:
|
|
293
|
-
key='color'
|
|
342
|
+
key='color' ':'? value=ThemeColor ';'?;
|
|
294
343
|
|
|
295
344
|
OpacityProperty:
|
|
296
|
-
key='opacity'
|
|
345
|
+
key='opacity' ':'? value=Percent ';'?;
|
|
297
346
|
|
|
298
347
|
// Element properties -------------------------------------
|
|
299
348
|
IconProperty:
|
|
300
|
-
key='icon'
|
|
349
|
+
key='icon' ':'? (libicon=[LibIcon:IconId] | value=Uri) ';'?;
|
|
301
350
|
|
|
302
351
|
ShapeProperty:
|
|
303
|
-
key='shape'
|
|
352
|
+
key='shape' ':'? value=ElementShape ';'?;
|
|
304
353
|
|
|
305
354
|
|
|
306
355
|
BorderStyleValue returns string:
|
|
307
356
|
LineOptions | 'none';
|
|
308
357
|
|
|
309
358
|
BorderProperty:
|
|
310
|
-
key='border'
|
|
359
|
+
key='border' ':'? value=BorderStyleValue ';'?;
|
|
311
360
|
|
|
312
361
|
StyleProperty:
|
|
313
362
|
ColorProperty |
|
|
@@ -325,9 +374,9 @@ StyleProperties:
|
|
|
325
374
|
// -------------------------
|
|
326
375
|
// Relationship Style Properties
|
|
327
376
|
LineProperty:
|
|
328
|
-
key='line'
|
|
377
|
+
key='line' ':'? value=LineOptions ';'?;
|
|
329
378
|
ArrowProperty:
|
|
330
|
-
key=('head' | 'tail')
|
|
379
|
+
key=('head' | 'tail') ':'? value=ArrowType ';'?;
|
|
331
380
|
|
|
332
381
|
RelationshipStyleProperty:
|
|
333
382
|
ColorProperty | LineProperty | ArrowProperty;
|
|
@@ -342,6 +391,8 @@ ArrowType returns string:
|
|
|
342
391
|
'none' |
|
|
343
392
|
'normal' |
|
|
344
393
|
'onormal' |
|
|
394
|
+
'dot' |
|
|
395
|
+
'odot' |
|
|
345
396
|
'diamond' |
|
|
346
397
|
'odiamond' |
|
|
347
398
|
'crow' |
|
|
@@ -353,6 +404,11 @@ ThemeColor returns string:
|
|
|
353
404
|
'primary' | 'secondary' | 'muted' | 'slate' | 'blue' | 'indigo' | 'sky' | 'red' | 'gray' | 'green' | 'amber';
|
|
354
405
|
ElementShape returns string:
|
|
355
406
|
'rectangle' | 'person' | 'browser' | 'mobile' | 'cylinder' | 'storage' | 'queue';
|
|
407
|
+
|
|
408
|
+
IconId returns string:
|
|
409
|
+
LIB_ICON;
|
|
410
|
+
// ('aws:' | 'gcp:' | 'tech:') IdTerminal;
|
|
411
|
+
|
|
356
412
|
Uri returns string:
|
|
357
413
|
URI_WITH_SCHEMA | URI_RELATIVE;
|
|
358
414
|
|
|
@@ -390,6 +446,9 @@ hidden terminal WS: /\s+/;
|
|
|
390
446
|
// Terminals
|
|
391
447
|
//terminal LineStartWithDash: /(?<=([\r?\n][^\S\r\n]*))-/;
|
|
392
448
|
|
|
449
|
+
// LibIcons
|
|
450
|
+
terminal LIB_ICON: /\b(aws|gcp|tech):[-\w]*/;
|
|
451
|
+
|
|
393
452
|
terminal URI_WITH_SCHEMA: /\w+:\/\/\S+/;
|
|
394
453
|
terminal URI_RELATIVE: /\.{0,2}\/[^\/]\S+/;
|
|
395
454
|
|
|
@@ -402,13 +461,10 @@ terminal StickyDot: /\b\./;
|
|
|
402
461
|
terminal Dot: '.';
|
|
403
462
|
terminal NotEqual: /\!\={1,2}/;
|
|
404
463
|
terminal Eq: /\={1,2}/;
|
|
405
|
-
terminal Colon: ':';
|
|
406
|
-
terminal SemiColon: ';';
|
|
407
|
-
terminal Comma: ',';
|
|
408
464
|
terminal Percent: /\b\d+%/;
|
|
409
465
|
|
|
410
466
|
terminal String: /"[^"]*"|'[^']*'/;
|
|
411
467
|
|
|
412
468
|
// terminal TagId: HASH LETTER (LETTER | DIGIT | UNDERSCORE | DASH)*;
|
|
413
469
|
// terminal IdTerminal: (LETTER | UNDERSCORE+ (LETTER | DIGIT)) (LETTER | DIGIT | UNDERSCORE | DASH)*;
|
|
414
|
-
terminal IdTerminal:
|
|
470
|
+
terminal IdTerminal: /[_]*[a-zA-Z][-\w]*/;
|
package/src/likec4lib.ts
ADDED
|
@@ -17,10 +17,11 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
|
|
|
17
17
|
|
|
18
18
|
getSymbols({
|
|
19
19
|
parseResult: {
|
|
20
|
-
value: { specifications, models, views }
|
|
20
|
+
value: { specifications, models, views, likec4lib }
|
|
21
21
|
}
|
|
22
22
|
}: LikeC4LangiumDocument): MaybePromise<DocumentSymbol[]> {
|
|
23
23
|
return [
|
|
24
|
+
...likec4lib.map(l => () => this.getLikec4LibSymbol(l)),
|
|
24
25
|
...specifications.map(s => () => this.getSpecSymbol(s)),
|
|
25
26
|
...models.map(s => () => this.getModelSymbol(s)),
|
|
26
27
|
...views.map(s => () => this.getModelViewsSymbol(s))
|
|
@@ -34,6 +35,22 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
|
|
|
34
35
|
})
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
protected getLikec4LibSymbol(astLib: ast.LikeC4Lib): DocumentSymbol[] {
|
|
39
|
+
const cstModel = astLib?.$cstNode
|
|
40
|
+
if (!cstModel) return []
|
|
41
|
+
const children = astLib.icons.map(i => this.getLibIconSymbol(i)).filter(isTruthy)
|
|
42
|
+
if (children.length === 0) return []
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
kind: SymbolKind.Namespace,
|
|
46
|
+
name: 'icons',
|
|
47
|
+
range: cstModel.range,
|
|
48
|
+
selectionRange: GrammarUtils.findNodeForKeyword(cstModel, 'icons')?.range ?? cstModel.range,
|
|
49
|
+
children
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
|
|
37
54
|
protected getSpecSymbol(astSpec: ast.SpecificationRule): DocumentSymbol[] {
|
|
38
55
|
const cstModel = astSpec?.$cstNode
|
|
39
56
|
if (!cstModel) return []
|
|
@@ -179,6 +196,16 @@ export class LikeC4DocumentSymbolProvider implements DocumentSymbolProvider {
|
|
|
179
196
|
}
|
|
180
197
|
}
|
|
181
198
|
|
|
199
|
+
protected getLibIconSymbol(astTag: ast.LibIcon): DocumentSymbol | null {
|
|
200
|
+
if (!astTag.$cstNode || isEmpty(astTag.name)) return null
|
|
201
|
+
return {
|
|
202
|
+
kind: this.symbolKind(astTag),
|
|
203
|
+
name: astTag.name,
|
|
204
|
+
range: astTag.$cstNode.range,
|
|
205
|
+
selectionRange: astTag.$cstNode.range
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
182
209
|
protected getViewSymbol(astView: ast.LikeC4View): DocumentSymbol[] {
|
|
183
210
|
const cst = astView?.$cstNode
|
|
184
211
|
if (!cst) return []
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AstNode } from 'langium'
|
|
2
2
|
import { AbstractSemanticTokenProvider, type SemanticTokenAcceptor } from 'langium/lsp'
|
|
3
|
+
import { isTruthy } from 'remeda'
|
|
3
4
|
import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver-protocol'
|
|
4
5
|
import { ast } from '../ast'
|
|
5
6
|
|
|
@@ -16,23 +17,32 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
16
17
|
type: SemanticTokenTypes.function
|
|
17
18
|
})
|
|
18
19
|
}
|
|
20
|
+
if (ast.isLibIcon(node)) {
|
|
21
|
+
acceptor({
|
|
22
|
+
node,
|
|
23
|
+
property: 'name',
|
|
24
|
+
type: SemanticTokenTypes.type,
|
|
25
|
+
modifier: [SemanticTokenModifiers.definition]
|
|
26
|
+
})
|
|
27
|
+
return 'prune'
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
if (ast.isRelation(node) && 'kind' in node) {
|
|
20
|
-
|
|
31
|
+
acceptor({
|
|
21
32
|
node,
|
|
22
33
|
property: 'kind',
|
|
23
34
|
type: SemanticTokenTypes.function
|
|
24
35
|
})
|
|
25
36
|
}
|
|
26
|
-
if (ast.
|
|
27
|
-
|
|
37
|
+
if (ast.isNavigateToProperty(node)) {
|
|
38
|
+
acceptor({
|
|
28
39
|
node,
|
|
29
|
-
property: '
|
|
30
|
-
type: SemanticTokenTypes.
|
|
40
|
+
property: 'key',
|
|
41
|
+
type: SemanticTokenTypes.property
|
|
31
42
|
})
|
|
32
|
-
}
|
|
33
|
-
if (ast.isDescedantsExpr(node) && node.$cstNode) {
|
|
34
43
|
acceptor({
|
|
35
|
-
|
|
44
|
+
node,
|
|
45
|
+
property: 'value',
|
|
36
46
|
type: SemanticTokenTypes.variable,
|
|
37
47
|
modifier: [
|
|
38
48
|
SemanticTokenModifiers.definition,
|
|
@@ -41,7 +51,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
41
51
|
})
|
|
42
52
|
return 'prune'
|
|
43
53
|
}
|
|
44
|
-
if (ast.
|
|
54
|
+
if ((ast.isElementDescedantsExpression(node) || ast.isWildcardExpression(node)) && node.$cstNode) {
|
|
45
55
|
acceptor({
|
|
46
56
|
cst: node.$cstNode,
|
|
47
57
|
type: SemanticTokenTypes.variable,
|
|
@@ -52,8 +62,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
52
62
|
})
|
|
53
63
|
return 'prune'
|
|
54
64
|
}
|
|
55
|
-
|
|
56
|
-
if (ast.isElementKindExpr(node)) {
|
|
65
|
+
if (ast.isElementKindExpression(node) && isTruthy(node.kind)) {
|
|
57
66
|
acceptor({
|
|
58
67
|
node,
|
|
59
68
|
property: 'kind',
|
|
@@ -61,7 +70,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
61
70
|
modifier: [SemanticTokenModifiers.definition]
|
|
62
71
|
})
|
|
63
72
|
}
|
|
64
|
-
if (ast.
|
|
73
|
+
if (ast.isElementTagExpression(node) && isTruthy(node.tag)) {
|
|
65
74
|
acceptor({
|
|
66
75
|
node,
|
|
67
76
|
property: 'tag',
|
|
@@ -98,6 +107,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
98
107
|
property: 'value',
|
|
99
108
|
type: SemanticTokenTypes.interface
|
|
100
109
|
})
|
|
110
|
+
return
|
|
101
111
|
}
|
|
102
112
|
if (ast.isTag(node)) {
|
|
103
113
|
return acceptor({
|
|
@@ -134,6 +144,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
134
144
|
property: 'value',
|
|
135
145
|
type: SemanticTokenTypes.enum
|
|
136
146
|
})
|
|
147
|
+
return 'prune'
|
|
137
148
|
}
|
|
138
149
|
if (ast.isOpacityProperty(node)) {
|
|
139
150
|
acceptor({
|
|
@@ -146,26 +157,34 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
146
157
|
property: 'value',
|
|
147
158
|
type: SemanticTokenTypes.number
|
|
148
159
|
})
|
|
149
|
-
return
|
|
160
|
+
return 'prune'
|
|
150
161
|
}
|
|
151
162
|
if (
|
|
152
163
|
ast.isLinkProperty(node)
|
|
153
164
|
|| ast.isIconProperty(node)
|
|
154
|
-
|| ast.
|
|
155
|
-
|| ast.isRelationStringProperty(node)
|
|
156
|
-
|| ast.isViewStringProperty(node)
|
|
165
|
+
|| ast.isStringProperty(node)
|
|
157
166
|
) {
|
|
158
167
|
acceptor({
|
|
159
168
|
node,
|
|
160
169
|
property: 'key',
|
|
161
170
|
type: SemanticTokenTypes.property
|
|
162
171
|
})
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
if (ast.isIconProperty(node) && node.libicon) {
|
|
173
|
+
acceptor({
|
|
174
|
+
node,
|
|
175
|
+
property: 'libicon',
|
|
176
|
+
type: SemanticTokenTypes.enum
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
if ('value' in node) {
|
|
180
|
+
acceptor({
|
|
181
|
+
node,
|
|
182
|
+
property: 'value',
|
|
183
|
+
type: SemanticTokenTypes.string
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return 'prune'
|
|
169
188
|
}
|
|
170
189
|
if (ast.isElement(node)) {
|
|
171
190
|
return this.highlightAstElement(node, acceptor)
|
|
@@ -1,14 +1,38 @@
|
|
|
1
1
|
import { AsFqn, type c4, nonexhaustive } from '@likec4/core'
|
|
2
|
-
import { MultiMap } from 'langium'
|
|
2
|
+
import { type AstNodeDescription, type AstNodeLocator, AstUtils, CstUtils, GrammarUtils, MultiMap } from 'langium'
|
|
3
3
|
import { isEmpty, isNullish as isNil } from 'remeda'
|
|
4
4
|
import { ast, ElementOps, type LikeC4LangiumDocument } from '../ast'
|
|
5
5
|
import { getFqnElementRef } from '../elementRef'
|
|
6
6
|
import type { LikeC4Services } from '../module'
|
|
7
7
|
|
|
8
|
+
const { findNodeForProperty } = GrammarUtils
|
|
9
|
+
const { toDocumentSegment } = CstUtils
|
|
10
|
+
const { getDocument } = AstUtils
|
|
11
|
+
|
|
8
12
|
type TraversePair = [el: ast.Element | ast.ExtendElement | ast.Relation, parent: c4.Fqn | null]
|
|
9
13
|
|
|
14
|
+
function toAstNodeDescription(
|
|
15
|
+
locator: AstNodeLocator,
|
|
16
|
+
entry: ast.Element,
|
|
17
|
+
doc: LikeC4LangiumDocument
|
|
18
|
+
): AstNodeDescription {
|
|
19
|
+
const $cstNode = findNodeForProperty(entry.$cstNode, 'name')
|
|
20
|
+
return {
|
|
21
|
+
documentUri: doc.uri,
|
|
22
|
+
name: entry.name,
|
|
23
|
+
...(entry.$cstNode && {
|
|
24
|
+
selectionSegment: toDocumentSegment(entry.$cstNode)
|
|
25
|
+
}),
|
|
26
|
+
...($cstNode && {
|
|
27
|
+
nameSegment: toDocumentSegment($cstNode)
|
|
28
|
+
}),
|
|
29
|
+
path: locator.getAstNodePath(entry),
|
|
30
|
+
type: ast.Element
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
10
34
|
export function computeDocumentFqn(document: LikeC4LangiumDocument, services: LikeC4Services) {
|
|
11
|
-
const
|
|
35
|
+
const c4fqnIndex = (document.c4fqnIndex = new MultiMap())
|
|
12
36
|
const elements = document.parseResult.value.models.flatMap(m => m.elements)
|
|
13
37
|
if (elements.length === 0) {
|
|
14
38
|
return
|
|
@@ -30,11 +54,9 @@ export function computeDocumentFqn(document: LikeC4LangiumDocument, services: Li
|
|
|
30
54
|
}
|
|
31
55
|
if (ast.isElement(el)) {
|
|
32
56
|
const fqn = AsFqn(el.name, parent)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
path,
|
|
37
|
-
name: el.name
|
|
57
|
+
c4fqnIndex.add(fqn, {
|
|
58
|
+
...toAstNodeDescription(locator, el, document),
|
|
59
|
+
fqn
|
|
38
60
|
})
|
|
39
61
|
ElementOps.writeId(el, fqn)
|
|
40
62
|
if (!isNil(el.body) && !isEmpty(el.body.elements)) {
|
package/src/model/fqn-index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Fqn } from '@likec4/core'
|
|
2
2
|
import { nameFromFqn, parentFqn } from '@likec4/core'
|
|
3
|
-
import type { LangiumDocuments, Stream } from 'langium'
|
|
3
|
+
import type { AstNodeDescription, LangiumDocuments, Stream } from 'langium'
|
|
4
4
|
import { DocumentState, DONE_RESULT, MultiMap, stream, StreamImpl } from 'langium'
|
|
5
|
-
import type { ast, FqnIndexedDocument } from '../ast'
|
|
5
|
+
import type { ast, DocFqnIndexAstNodeDescription, FqnIndexedDocument } from '../ast'
|
|
6
6
|
import { ElementOps, isFqnIndexedDocument, isLikeC4LangiumDocument } from '../ast'
|
|
7
7
|
import { logError, logger } from '../logger'
|
|
8
8
|
import type { LikeC4Services } from '../module'
|
|
@@ -17,8 +17,6 @@ export interface FqnIndexEntry {
|
|
|
17
17
|
path: string
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const True = () => true
|
|
21
|
-
|
|
22
20
|
export class FqnIndex {
|
|
23
21
|
protected langiumDocuments: LangiumDocuments
|
|
24
22
|
|
|
@@ -31,7 +29,7 @@ export class FqnIndex {
|
|
|
31
29
|
logger.debug(`[FqnIndex] onIndexedContent ${docs.length}:\n` + printDocs(docs))
|
|
32
30
|
for (const doc of docs) {
|
|
33
31
|
if (isLikeC4LangiumDocument(doc)) {
|
|
34
|
-
delete doc.
|
|
32
|
+
delete doc.c4fqnIndex
|
|
35
33
|
delete doc.c4Elements
|
|
36
34
|
delete doc.c4Specification
|
|
37
35
|
delete doc.c4Relations
|
|
@@ -53,18 +51,13 @@ export class FqnIndex {
|
|
|
53
51
|
return this.langiumDocuments.all.filter(isFqnIndexedDocument)
|
|
54
52
|
}
|
|
55
53
|
|
|
56
|
-
private entries(filterByFqn
|
|
57
|
-
return this.documents.flatMap(doc =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return []
|
|
66
|
-
})
|
|
67
|
-
)
|
|
54
|
+
private entries(filterByFqn?: (fqn: Fqn) => boolean): Stream<DocFqnIndexAstNodeDescription> {
|
|
55
|
+
return this.documents.flatMap(doc => {
|
|
56
|
+
if (filterByFqn) {
|
|
57
|
+
return doc.c4fqnIndex.keys().filter(filterByFqn).flatMap(fqn => doc.c4fqnIndex.get(fqn))
|
|
58
|
+
}
|
|
59
|
+
return doc.c4fqnIndex.values()
|
|
60
|
+
})
|
|
68
61
|
}
|
|
69
62
|
|
|
70
63
|
public getFqn(el: ast.Element): Fqn | null {
|
|
@@ -83,22 +76,16 @@ export class FqnIndex {
|
|
|
83
76
|
// return fqn
|
|
84
77
|
}
|
|
85
78
|
|
|
86
|
-
public byFqn(fqn: Fqn): Stream<
|
|
79
|
+
public byFqn(fqn: Fqn): Stream<AstNodeDescription> {
|
|
87
80
|
return this.documents.flatMap(doc => {
|
|
88
|
-
return doc.
|
|
89
|
-
const el = entry.el.deref()
|
|
90
|
-
if (el) {
|
|
91
|
-
return { fqn, el, doc, path: entry.path, name: entry.name }
|
|
92
|
-
}
|
|
93
|
-
return []
|
|
94
|
-
})
|
|
81
|
+
return doc.c4fqnIndex.get(fqn)
|
|
95
82
|
})
|
|
96
83
|
}
|
|
97
84
|
|
|
98
|
-
public directChildrenOf(parent: Fqn): Stream<
|
|
85
|
+
public directChildrenOf(parent: Fqn): Stream<AstNodeDescription> {
|
|
99
86
|
return stream([parent]).flatMap(_parent => {
|
|
100
87
|
const children = this.entries(fqn => parentFqn(fqn) === _parent)
|
|
101
|
-
.map((entry)
|
|
88
|
+
.map((entry) => [entry.name, entry] as [string, AstNodeDescription])
|
|
102
89
|
.toArray()
|
|
103
90
|
if (children.length === 0) {
|
|
104
91
|
return []
|
|
@@ -113,15 +100,15 @@ export class FqnIndex {
|
|
|
113
100
|
/**
|
|
114
101
|
* Returns descedant elements with unique names in the scope
|
|
115
102
|
*/
|
|
116
|
-
public uniqueDescedants(parent: Fqn): Stream<
|
|
103
|
+
public uniqueDescedants(parent: Fqn): Stream<AstNodeDescription> {
|
|
117
104
|
return new StreamImpl(
|
|
118
105
|
() => {
|
|
119
106
|
const prefix = `${parent}.`
|
|
120
107
|
|
|
121
108
|
const childrenNames = new Set<string>()
|
|
122
|
-
const descedants = [] as
|
|
109
|
+
const descedants = [] as AstNodeDescription[]
|
|
123
110
|
|
|
124
|
-
const nested = new MultiMap<string,
|
|
111
|
+
const nested = new MultiMap<string, AstNodeDescription>()
|
|
125
112
|
|
|
126
113
|
this.entries(f => f.startsWith(prefix)).forEach(e => {
|
|
127
114
|
const name = nameFromFqn(e.fqn)
|
|
@@ -154,7 +141,7 @@ export class FqnIndex {
|
|
|
154
141
|
if (iterator) {
|
|
155
142
|
return iterator.next()
|
|
156
143
|
}
|
|
157
|
-
return DONE_RESULT as IteratorResult<
|
|
144
|
+
return DONE_RESULT as IteratorResult<AstNodeDescription>
|
|
158
145
|
}
|
|
159
146
|
)
|
|
160
147
|
}
|