@likec4/language-server 1.6.0 → 1.7.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/contrib/likec4.tmLanguage.json +1 -1
- package/package.json +23 -19
- package/src/Rpc.ts +1 -1
- package/src/ast.ts +34 -9
- package/src/{browser/index.ts → browser.ts} +4 -1
- package/src/generated/ast.ts +498 -152
- package/src/generated/grammar.ts +2 -2
- package/src/generated/module.ts +1 -1
- package/src/index.ts +1 -1
- package/src/like-c4.langium +116 -44
- package/src/logger.ts +76 -55
- package/src/lsp/DocumentLinkProvider.ts +1 -1
- package/src/lsp/DocumentSymbolProvider.ts +1 -1
- package/src/lsp/HoverProvider.ts +1 -1
- package/src/lsp/SemanticTokenProvider.ts +54 -26
- package/src/model/model-builder.ts +11 -8
- package/src/model/model-locator.ts +12 -25
- package/src/model/model-parser-where.ts +75 -0
- package/src/model/model-parser.ts +168 -68
- package/src/model-change/ModelChanges.ts +2 -3
- package/src/model-change/changeElementStyle.ts +4 -1
- package/src/model-change/changeViewLayout.ts +8 -8
- package/src/model-change/saveManualLayout.ts +4 -6
- package/src/model-graph/LikeC4ModelGraph.ts +50 -48
- package/src/model-graph/compute-view/__test__/fixture.ts +41 -16
- package/src/model-graph/compute-view/compute.ts +135 -69
- package/src/model-graph/compute-view/predicates.ts +232 -136
- package/src/model-graph/dynamic-view/__test__/fixture.ts +5 -1
- package/src/model-graph/dynamic-view/compute.ts +50 -41
- package/src/model-graph/utils/applyCustomElementProperties.ts +31 -29
- package/src/model-graph/utils/applyCustomRelationProperties.ts +52 -15
- package/src/model-graph/utils/elementExpressionToPredicate.ts +8 -3
- package/src/module.ts +4 -18
- package/src/{node/index.ts → node.ts} +1 -1
- package/src/protocol.ts +2 -2
- package/src/shared/NodeKindProvider.ts +4 -2
- package/src/test/setup.ts +13 -0
- package/src/test/testServices.ts +1 -1
- package/src/validation/dynamic-view-rule.ts +12 -12
- package/src/validation/index.ts +6 -6
- package/src/validation/relation.ts +1 -1
- package/src/validation/view-predicates/{custom-element-expr.ts → element-with.ts} +11 -10
- package/src/validation/view-predicates/expanded-element.ts +2 -10
- package/src/validation/view-predicates/incoming.ts +1 -1
- package/src/validation/view-predicates/index.ts +2 -2
- package/src/validation/view-predicates/outgoing.ts +1 -1
- package/src/validation/view-predicates/{custom-relation-expr.ts → relation-with.ts} +2 -2
- package/src/validation/view.ts +8 -9
- package/src/view-utils/manual-layout.ts +65 -72
- package/src/view-utils/resolve-relative-paths.ts +28 -17
- package/src/view-utils/view-hash.ts +33 -0
package/src/like-c4.langium
CHANGED
|
@@ -111,18 +111,24 @@ ExtendElementBody: '{'
|
|
|
111
111
|
|
|
112
112
|
//
|
|
113
113
|
FqnElementRef:
|
|
114
|
-
el=[Element] ({infer FqnElementRef.parent=current}
|
|
114
|
+
el=[Element:Id] ({infer FqnElementRef.parent=current} StickyDot el=[Element:Id])*;
|
|
115
115
|
|
|
116
116
|
ElementRef:
|
|
117
|
-
el=[Element] ({infer ElementRef.parent=current}
|
|
117
|
+
el=[Element:Id] ({infer ElementRef.parent=current} StickyDot el=[Element:Id])*;
|
|
118
118
|
|
|
119
119
|
Tags:
|
|
120
|
-
|
|
120
|
+
(values+=[Tag:TagId])+ ({infer Tags.prev=current} ',' (values+=[Tag:TagId])*)* ';'?
|
|
121
121
|
;
|
|
122
122
|
|
|
123
123
|
Relation:
|
|
124
124
|
ExplicitRelation | ImplicitRelation;
|
|
125
125
|
|
|
126
|
+
ExplicitRelation:
|
|
127
|
+
source=ElementRef RelationFragment;
|
|
128
|
+
|
|
129
|
+
ImplicitRelation:
|
|
130
|
+
('this' | 'it')? RelationFragment;
|
|
131
|
+
|
|
126
132
|
fragment RelationFragment:
|
|
127
133
|
('->' | '-[' kind=[RelationshipKind] ']->' | kind=[RelationshipKind:DotId] )
|
|
128
134
|
target=ElementRef
|
|
@@ -131,12 +137,6 @@ fragment RelationFragment:
|
|
|
131
137
|
body=RelationBody?
|
|
132
138
|
;
|
|
133
139
|
|
|
134
|
-
ExplicitRelation:
|
|
135
|
-
source=ElementRef RelationFragment;
|
|
136
|
-
|
|
137
|
-
ImplicitRelation:
|
|
138
|
-
('this' | 'it')? RelationFragment;
|
|
139
|
-
|
|
140
140
|
RelationBody: '{'
|
|
141
141
|
tags=Tags?
|
|
142
142
|
props+=RelationProperty*
|
|
@@ -147,7 +147,7 @@ RelationProperty:
|
|
|
147
147
|
RelationStringProperty | RelationStyleProperty | LinkProperty;
|
|
148
148
|
|
|
149
149
|
RelationStringProperty:
|
|
150
|
-
key='title' ':'? value=String ';'?;
|
|
150
|
+
key=('title' | 'technology' | 'description') ':'? value=String ';'?;
|
|
151
151
|
|
|
152
152
|
RelationStyleProperty:
|
|
153
153
|
key='style' '{'
|
|
@@ -160,24 +160,23 @@ RelationStyleProperty:
|
|
|
160
160
|
|
|
161
161
|
ModelViews:
|
|
162
162
|
name='views' '{'
|
|
163
|
-
views+=
|
|
164
|
-
DynamicView |
|
|
165
|
-
ElementView
|
|
166
|
-
)*
|
|
163
|
+
views+=LikeC4ViewRule*
|
|
167
164
|
'}';
|
|
168
165
|
|
|
169
166
|
type LikeC4View = ElementView | DynamicView;
|
|
167
|
+
LikeC4ViewRule returns LikeC4View:
|
|
168
|
+
ElementView | DynamicView;
|
|
170
169
|
|
|
171
170
|
ElementView:
|
|
172
171
|
'view' name=Id? (
|
|
173
172
|
'extends' extends=ElementViewRef |
|
|
174
173
|
'of' viewOf=ElementRef
|
|
175
174
|
)?
|
|
176
|
-
body=ElementViewBody
|
|
175
|
+
body=ElementViewBody?
|
|
177
176
|
;
|
|
178
177
|
|
|
179
178
|
DynamicView:
|
|
180
|
-
'dynamic' 'view' name=Id body=DynamicViewBody
|
|
179
|
+
'dynamic' 'view' name=Id body=DynamicViewBody?
|
|
181
180
|
;
|
|
182
181
|
|
|
183
182
|
ViewRef:
|
|
@@ -201,7 +200,7 @@ DynamicViewBody: '{'
|
|
|
201
200
|
;
|
|
202
201
|
|
|
203
202
|
|
|
204
|
-
type StringProperty = ElementStringProperty| ViewStringProperty | RelationStringProperty;
|
|
203
|
+
type StringProperty = ElementStringProperty | ViewStringProperty | RelationStringProperty;
|
|
205
204
|
|
|
206
205
|
ViewProperty:
|
|
207
206
|
ViewStringProperty | LinkProperty
|
|
@@ -220,34 +219,65 @@ ViewRule:
|
|
|
220
219
|
;
|
|
221
220
|
|
|
222
221
|
DynamicViewRule:
|
|
223
|
-
|
|
222
|
+
DynamicViewIncludePredicate |
|
|
224
223
|
ViewRuleStyle |
|
|
225
224
|
ViewRuleAutoLayout
|
|
226
225
|
;
|
|
227
226
|
|
|
228
227
|
DynamicViewStep:
|
|
229
|
-
source=ElementRef
|
|
228
|
+
source=ElementRef
|
|
229
|
+
(isBackward?='<-' | '->' | '-[' kind=[RelationshipKind] ']->' | kind=[RelationshipKind:DotId] )
|
|
230
|
+
target=ElementRef
|
|
231
|
+
title=String?
|
|
232
|
+
custom=CustomRelationProperties?
|
|
230
233
|
;
|
|
231
234
|
|
|
232
235
|
ViewRulePredicate:
|
|
233
|
-
{infer IncludePredicate} 'include'
|
|
234
|
-
{infer ExcludePredicate} 'exclude'
|
|
236
|
+
{infer IncludePredicate} 'include' predicates=Predicates |
|
|
237
|
+
{infer ExcludePredicate} 'exclude' predicates=Predicates
|
|
238
|
+
;
|
|
239
|
+
Predicates:
|
|
240
|
+
value=Predicate ({infer Predicates.prev=current} ',' value=Predicate?)*
|
|
235
241
|
;
|
|
236
242
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
// 'exclude' expr=WithExpressions ({infer ExcludePredicate.prev=current} ',' (expr=WithExpressions)?)*
|
|
242
|
-
// ;
|
|
243
|
+
Predicate:
|
|
244
|
+
RelationPredicate |
|
|
245
|
+
ElementPredicate
|
|
246
|
+
;
|
|
243
247
|
|
|
244
|
-
|
|
245
|
-
|
|
248
|
+
ElementPredicate:
|
|
249
|
+
ElementPredicates;
|
|
250
|
+
|
|
251
|
+
ElementPredicates infers ElementPredicate:
|
|
252
|
+
ElementPredicateOrWhere ({infer ElementPredicateWith.subject=current} 'with' custom=CustomElementProperties?)?
|
|
246
253
|
;
|
|
247
254
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
255
|
+
ElementPredicateOrWhere:
|
|
256
|
+
ElementExpression ({infer ElementPredicateWhere.subject=current} 'where' where=WhereElementExpression?)?
|
|
257
|
+
;
|
|
258
|
+
|
|
259
|
+
WhereElementExpression:
|
|
260
|
+
WhereElementOr
|
|
261
|
+
;
|
|
262
|
+
WhereElementOr infers WhereElementExpression:
|
|
263
|
+
WhereElementAnd ({infer WhereBinaryExpression.left=current} operator='or' right=WhereElementAnd)*
|
|
264
|
+
;
|
|
265
|
+
WhereElementAnd infers WhereElementExpression:
|
|
266
|
+
WhereElementPrimary ({infer WhereBinaryExpression.left=current} operator='and' right=WhereElementPrimary)*
|
|
267
|
+
;
|
|
268
|
+
|
|
269
|
+
WhereElementPrimary infers WhereElementExpression:
|
|
270
|
+
'(' WhereElementExpression ')' |
|
|
271
|
+
WhereElementNegation |
|
|
272
|
+
WhereElement
|
|
273
|
+
;
|
|
274
|
+
|
|
275
|
+
WhereElementNegation:
|
|
276
|
+
'not' value=WhereElementExpression;
|
|
277
|
+
|
|
278
|
+
WhereElement:
|
|
279
|
+
{infer WhereElementTag} 'tag' EqOperator value=[Tag:TagId]? |
|
|
280
|
+
{infer WhereElementKind} 'kind' EqOperator value=[ElementKind]?
|
|
251
281
|
;
|
|
252
282
|
|
|
253
283
|
ElementExpression:
|
|
@@ -268,6 +298,48 @@ ElementDescedantsExpression infers ElementExpression:
|
|
|
268
298
|
)?
|
|
269
299
|
;
|
|
270
300
|
|
|
301
|
+
RelationPredicate:
|
|
302
|
+
RelationPredicates
|
|
303
|
+
;
|
|
304
|
+
|
|
305
|
+
RelationPredicates infers RelationPredicate:
|
|
306
|
+
RelationPredicateOrWhere ({infer RelationPredicateWith.subject=current} 'with' custom=CustomRelationProperties?)?
|
|
307
|
+
;
|
|
308
|
+
|
|
309
|
+
RelationPredicateOrWhere:
|
|
310
|
+
RelationExpression ({infer RelationPredicateWhere.subject=current} 'where' where=WhereRelationExpression?)?
|
|
311
|
+
;
|
|
312
|
+
|
|
313
|
+
WhereRelationExpression:
|
|
314
|
+
WhereRelationOr
|
|
315
|
+
;
|
|
316
|
+
|
|
317
|
+
WhereRelationOr infers WhereRelationExpression:
|
|
318
|
+
WhereRelationAnd ({infer WhereBinaryExpression.left=current} operator='or' right=WhereRelationAnd)*
|
|
319
|
+
;
|
|
320
|
+
WhereRelationAnd infers WhereRelationExpression:
|
|
321
|
+
WhereRelationPrimary ({infer WhereBinaryExpression.left=current} operator='and' right=WhereRelationPrimary)*
|
|
322
|
+
;
|
|
323
|
+
|
|
324
|
+
WhereRelationPrimary infers WhereRelationExpression:
|
|
325
|
+
'(' WhereRelationExpression ')' |
|
|
326
|
+
WhereRelationNegation |
|
|
327
|
+
WhereRelation
|
|
328
|
+
;
|
|
329
|
+
|
|
330
|
+
WhereRelationNegation:
|
|
331
|
+
'not' value=WhereRelationExpression;
|
|
332
|
+
|
|
333
|
+
WhereRelation:
|
|
334
|
+
{infer WhereRelationTag} 'tag' EqOperator value=[Tag:TagId]? |
|
|
335
|
+
{infer WhereRelationKind} 'kind' EqOperator value=[RelationshipKind]?
|
|
336
|
+
;
|
|
337
|
+
|
|
338
|
+
type WhereTagEqual = WhereElementTag | WhereRelationTag;
|
|
339
|
+
type WhereKindEqual = WhereElementKind | WhereRelationKind;
|
|
340
|
+
|
|
341
|
+
type WhereExpression = WhereElementExpression | WhereRelationExpression;
|
|
342
|
+
|
|
271
343
|
RelationExpression:
|
|
272
344
|
InOutRelationExpressions |
|
|
273
345
|
DirectedRelationExpressions
|
|
@@ -294,16 +366,12 @@ ElementExpressionsIterator:
|
|
|
294
366
|
value=ElementExpression ({infer ElementExpressionsIterator.prev=current} ',' (value=ElementExpression)?)*
|
|
295
367
|
;
|
|
296
368
|
|
|
297
|
-
|
|
298
|
-
'include'
|
|
369
|
+
DynamicViewIncludePredicate:
|
|
370
|
+
'include' predicates=DynamicViewPredicateIterator
|
|
299
371
|
;
|
|
300
372
|
|
|
301
|
-
|
|
302
|
-
value=
|
|
303
|
-
;
|
|
304
|
-
|
|
305
|
-
DynamicViewElementExpressions infers DynamicViewElementExpression:
|
|
306
|
-
ElementExpression ({infer CustomElementExpression.target=current} 'with' custom=CustomElementProperties)?
|
|
373
|
+
DynamicViewPredicateIterator:
|
|
374
|
+
value=ElementPredicate ({infer DynamicViewPredicateIterator.prev=current} ',' (value=ElementPredicate)?)*
|
|
307
375
|
;
|
|
308
376
|
|
|
309
377
|
ViewRuleStyle:
|
|
@@ -407,13 +475,10 @@ ElementShape returns string:
|
|
|
407
475
|
|
|
408
476
|
IconId returns string:
|
|
409
477
|
LIB_ICON;
|
|
410
|
-
// ('aws:' | 'gcp:' | 'tech:') IdTerminal;
|
|
411
478
|
|
|
412
479
|
Uri returns string:
|
|
413
480
|
URI_WITH_SCHEMA | URI_RELATIVE;
|
|
414
481
|
|
|
415
|
-
// Percent returns string: PERCENT;
|
|
416
|
-
|
|
417
482
|
TagId returns string:
|
|
418
483
|
Hash Id;
|
|
419
484
|
|
|
@@ -423,6 +488,13 @@ DotId returns string:
|
|
|
423
488
|
Id returns string:
|
|
424
489
|
IdTerminal | ElementShape | ThemeColor | ArrowType | LineOptions | 'element' | 'model';
|
|
425
490
|
|
|
491
|
+
fragment EqOperator:
|
|
492
|
+
(
|
|
493
|
+
operator=(Eq | NotEqual) |
|
|
494
|
+
operator="is" not?='not'?
|
|
495
|
+
)
|
|
496
|
+
;
|
|
497
|
+
|
|
426
498
|
fragment IsEqual:
|
|
427
499
|
isEqual?=Eq | NotEqual;
|
|
428
500
|
|
|
@@ -447,7 +519,7 @@ hidden terminal WS: /\s+/;
|
|
|
447
519
|
//terminal LineStartWithDash: /(?<=([\r?\n][^\S\r\n]*))-/;
|
|
448
520
|
|
|
449
521
|
// LibIcons
|
|
450
|
-
terminal LIB_ICON:
|
|
522
|
+
terminal LIB_ICON: /(aws|gcp|tech):[-\w]*/;
|
|
451
523
|
|
|
452
524
|
terminal URI_WITH_SCHEMA: /\w+:\/\/\S+/;
|
|
453
525
|
terminal URI_RELATIVE: /\.{0,2}\/[^\/]\S+/;
|
package/src/logger.ts
CHANGED
|
@@ -1,68 +1,89 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import {
|
|
2
|
+
import { type ConsolaReporter, LogLevels, rootLogger as root } from '@likec4/log'
|
|
3
|
+
import type { Connection } from 'vscode-languageserver'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
let isSilent = false
|
|
5
|
+
export const logger = root.withTag('lsp')
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
if (isSilent) return
|
|
10
|
-
console.trace(message)
|
|
11
|
-
},
|
|
12
|
-
debug(message: string) {
|
|
13
|
-
if (isSilent) return
|
|
14
|
-
console.debug(message)
|
|
15
|
-
},
|
|
16
|
-
info(message: string) {
|
|
17
|
-
if (isSilent) return
|
|
18
|
-
console.info(message)
|
|
19
|
-
},
|
|
20
|
-
warn(message: unknown) {
|
|
21
|
-
if (isSilent) return
|
|
22
|
-
if (typeof message === 'string' || message instanceof Error) {
|
|
23
|
-
console.warn(message)
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
const error = normalizeError(message)
|
|
27
|
-
console.warn(`${error.name}: ${error.message}`)
|
|
28
|
-
},
|
|
29
|
-
error(message: unknown) {
|
|
30
|
-
if (isSilent) return
|
|
31
|
-
if (typeof message === 'string' || message instanceof Error) {
|
|
32
|
-
console.error(message)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
const error = normalizeError(message)
|
|
36
|
-
console.error(`${error.name}: ${error.message}`, error)
|
|
37
|
-
},
|
|
38
|
-
silent(silent = true) {
|
|
39
|
-
isSilent = silent
|
|
40
|
-
}
|
|
7
|
+
export function logError(err: unknown): void {
|
|
8
|
+
logger.error(err)
|
|
41
9
|
}
|
|
42
10
|
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
export function logError(err: unknown): void {
|
|
46
|
-
if (typeof err === 'string') {
|
|
47
|
-
logger.error(err)
|
|
48
|
-
return
|
|
49
|
-
}
|
|
11
|
+
export function logWarnError(err: unknown): void {
|
|
50
12
|
if (err instanceof Error) {
|
|
51
|
-
logger.
|
|
13
|
+
logger.warn(err.stack ?? err.message)
|
|
52
14
|
return
|
|
53
15
|
}
|
|
54
|
-
logger.
|
|
16
|
+
logger.warn(err)
|
|
55
17
|
}
|
|
56
18
|
|
|
57
|
-
export function
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
19
|
+
export function setLogLevel(level: keyof typeof LogLevels): void {
|
|
20
|
+
logger.level = LogLevels[level]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function logErrorToTelemetry(connection: Connection): void {
|
|
24
|
+
const reporter: ConsolaReporter = {
|
|
25
|
+
log: ({ level, ...logObj }, ctx) => {
|
|
26
|
+
if (level !== LogLevels.error && level !== LogLevels.fatal) {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
const tag = logObj.tag || ''
|
|
30
|
+
const parts = logObj.args.map((arg) => {
|
|
31
|
+
if (arg && typeof arg.stack === 'string') {
|
|
32
|
+
return arg.message + '\n' + arg.stack
|
|
33
|
+
}
|
|
34
|
+
if (typeof arg === 'string') {
|
|
35
|
+
return arg
|
|
36
|
+
}
|
|
37
|
+
return String(arg)
|
|
38
|
+
})
|
|
39
|
+
if (tag) {
|
|
40
|
+
parts.unshift(`[${tag}]`)
|
|
41
|
+
}
|
|
42
|
+
const message = parts.join(' ')
|
|
43
|
+
connection.telemetry.logEvent({ eventName: 'error', error: message })
|
|
44
|
+
}
|
|
61
45
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
root.addReporter(reporter)
|
|
47
|
+
logger.setReporters(root.options.reporters)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function logToLspConnection(connection: Connection): void {
|
|
51
|
+
const reporter: ConsolaReporter = {
|
|
52
|
+
log: ({ level, ...logObj }, ctx) => {
|
|
53
|
+
const tag = logObj.tag || ''
|
|
54
|
+
const parts = logObj.args.map((arg) => {
|
|
55
|
+
if (arg && typeof arg.stack === 'string') {
|
|
56
|
+
return arg.message + '\n' + arg.stack
|
|
57
|
+
}
|
|
58
|
+
if (typeof arg === 'string') {
|
|
59
|
+
return arg
|
|
60
|
+
}
|
|
61
|
+
return String(arg)
|
|
62
|
+
})
|
|
63
|
+
if (tag) {
|
|
64
|
+
parts.unshift(`[${tag}]`)
|
|
65
|
+
}
|
|
66
|
+
const message = parts.join(' ')
|
|
67
|
+
switch (true) {
|
|
68
|
+
case level >= LogLevels.debug: {
|
|
69
|
+
connection.console.debug(message)
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
case level >= LogLevels.log: {
|
|
73
|
+
connection.console.info(message)
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
case level >= LogLevels.warn: {
|
|
77
|
+
connection.console.warn(message)
|
|
78
|
+
break
|
|
79
|
+
}
|
|
80
|
+
case level >= LogLevels.fatal: {
|
|
81
|
+
connection.console.error(message)
|
|
82
|
+
break
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
65
86
|
}
|
|
66
|
-
|
|
67
|
-
logger.
|
|
87
|
+
root.addReporter(reporter)
|
|
88
|
+
logger.setReporters(root.options.reporters)
|
|
68
89
|
}
|
|
@@ -2,7 +2,7 @@ import type { LangiumDocument, MaybePromise } from 'langium'
|
|
|
2
2
|
import { AstUtils, GrammarUtils } from 'langium'
|
|
3
3
|
import type { DocumentLinkProvider } from 'langium/lsp'
|
|
4
4
|
import { hasProtocol, isRelative, withBase } from 'ufo'
|
|
5
|
-
import type { DocumentLink, DocumentLinkParams } from 'vscode-languageserver
|
|
5
|
+
import type { DocumentLink, DocumentLinkParams } from 'vscode-languageserver'
|
|
6
6
|
import { ast, isParsedLikeC4LangiumDocument } from '../ast'
|
|
7
7
|
import { logError } from '../logger'
|
|
8
8
|
import type { LikeC4Services } from '../module'
|
|
@@ -2,7 +2,7 @@ import { nonexhaustive } from '@likec4/core'
|
|
|
2
2
|
import { type AstNode, GrammarUtils, type MaybePromise } from 'langium'
|
|
3
3
|
import type { DocumentSymbolProvider, NodeKindProvider } from 'langium/lsp'
|
|
4
4
|
import { filter, isEmpty, isTruthy, map, pipe } from 'remeda'
|
|
5
|
-
import { type DocumentSymbol, SymbolKind } from 'vscode-languageserver-
|
|
5
|
+
import { type DocumentSymbol, SymbolKind } from 'vscode-languageserver-types'
|
|
6
6
|
import { ast, type LikeC4LangiumDocument } from '../ast'
|
|
7
7
|
import { getFqnElementRef } from '../elementRef'
|
|
8
8
|
import { logError } from '../logger'
|
package/src/lsp/HoverProvider.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type AstNode, type MaybePromise } from 'langium'
|
|
|
2
2
|
import { AstNodeHoverProvider } from 'langium/lsp'
|
|
3
3
|
import { isTruthy } from 'remeda'
|
|
4
4
|
import stripIndent from 'strip-indent'
|
|
5
|
-
import type { Hover } from 'vscode-languageserver-
|
|
5
|
+
import type { Hover } from 'vscode-languageserver-types'
|
|
6
6
|
import { ast } from '../ast'
|
|
7
7
|
import type { LikeC4ModelLocator } from '../model'
|
|
8
8
|
import type { LikeC4Services } from '../module'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AstNode } from 'langium'
|
|
2
2
|
import { AbstractSemanticTokenProvider, type SemanticTokenAcceptor } from 'langium/lsp'
|
|
3
3
|
import { isTruthy } from 'remeda'
|
|
4
|
-
import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver-
|
|
4
|
+
import { SemanticTokenModifiers, SemanticTokenTypes } from 'vscode-languageserver-types'
|
|
5
5
|
import { ast } from '../ast'
|
|
6
6
|
|
|
7
7
|
export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
@@ -27,6 +27,14 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
27
27
|
return 'prune'
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
if (ast.isOutgoingRelationExpression(node) && 'kind' in node) {
|
|
31
|
+
acceptor({
|
|
32
|
+
node,
|
|
33
|
+
property: 'kind',
|
|
34
|
+
type: SemanticTokenTypes.function
|
|
35
|
+
})
|
|
36
|
+
return
|
|
37
|
+
}
|
|
30
38
|
if (ast.isRelation(node) && 'kind' in node) {
|
|
31
39
|
acceptor({
|
|
32
40
|
node,
|
|
@@ -60,7 +68,27 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
60
68
|
SemanticTokenModifiers.readonly
|
|
61
69
|
]
|
|
62
70
|
})
|
|
63
|
-
return
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
if (ast.isWhereRelationKind(node) && isTruthy(node.value)) {
|
|
74
|
+
acceptor({
|
|
75
|
+
node,
|
|
76
|
+
property: 'value',
|
|
77
|
+
type: SemanticTokenTypes.function
|
|
78
|
+
})
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
if ((ast.isWhereElement(node) || ast.isWhereRelation(node)) && isTruthy(node.value)) {
|
|
82
|
+
acceptor({
|
|
83
|
+
node,
|
|
84
|
+
property: 'value',
|
|
85
|
+
type: SemanticTokenTypes.type,
|
|
86
|
+
modifier: [
|
|
87
|
+
SemanticTokenModifiers.definition,
|
|
88
|
+
SemanticTokenModifiers.readonly
|
|
89
|
+
]
|
|
90
|
+
})
|
|
91
|
+
return
|
|
64
92
|
}
|
|
65
93
|
if (ast.isElementKindExpression(node) && isTruthy(node.kind)) {
|
|
66
94
|
acceptor({
|
|
@@ -102,12 +130,11 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
102
130
|
})
|
|
103
131
|
}
|
|
104
132
|
if (ast.isTags(node)) {
|
|
105
|
-
acceptor({
|
|
133
|
+
return acceptor({
|
|
106
134
|
node,
|
|
107
|
-
property: '
|
|
135
|
+
property: 'values',
|
|
108
136
|
type: SemanticTokenTypes.interface
|
|
109
137
|
})
|
|
110
|
-
return
|
|
111
138
|
}
|
|
112
139
|
if (ast.isTag(node)) {
|
|
113
140
|
return acceptor({
|
|
@@ -127,25 +154,6 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
127
154
|
type: SemanticTokenTypes.property
|
|
128
155
|
})
|
|
129
156
|
}
|
|
130
|
-
if (
|
|
131
|
-
ast.isColorProperty(node)
|
|
132
|
-
|| ast.isShapeProperty(node)
|
|
133
|
-
|| ast.isArrowProperty(node)
|
|
134
|
-
|| ast.isLineProperty(node)
|
|
135
|
-
|| ast.isBorderProperty(node)
|
|
136
|
-
) {
|
|
137
|
-
acceptor({
|
|
138
|
-
node,
|
|
139
|
-
property: 'key',
|
|
140
|
-
type: SemanticTokenTypes.property
|
|
141
|
-
})
|
|
142
|
-
acceptor({
|
|
143
|
-
node,
|
|
144
|
-
property: 'value',
|
|
145
|
-
type: SemanticTokenTypes.enum
|
|
146
|
-
})
|
|
147
|
-
return 'prune'
|
|
148
|
-
}
|
|
149
157
|
if (ast.isOpacityProperty(node)) {
|
|
150
158
|
acceptor({
|
|
151
159
|
node,
|
|
@@ -169,6 +177,13 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
169
177
|
property: 'key',
|
|
170
178
|
type: SemanticTokenTypes.property
|
|
171
179
|
})
|
|
180
|
+
if ('value' in node) {
|
|
181
|
+
acceptor({
|
|
182
|
+
node,
|
|
183
|
+
property: 'value',
|
|
184
|
+
type: SemanticTokenTypes.string
|
|
185
|
+
})
|
|
186
|
+
}
|
|
172
187
|
if (ast.isIconProperty(node) && node.libicon) {
|
|
173
188
|
acceptor({
|
|
174
189
|
node,
|
|
@@ -176,14 +191,27 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
176
191
|
type: SemanticTokenTypes.enum
|
|
177
192
|
})
|
|
178
193
|
}
|
|
194
|
+
return 'prune'
|
|
195
|
+
}
|
|
196
|
+
if (
|
|
197
|
+
ast.isColorProperty(node)
|
|
198
|
+
|| ast.isShapeProperty(node)
|
|
199
|
+
|| ast.isArrowProperty(node)
|
|
200
|
+
|| ast.isLineProperty(node)
|
|
201
|
+
|| ast.isBorderProperty(node)
|
|
202
|
+
) {
|
|
203
|
+
acceptor({
|
|
204
|
+
node,
|
|
205
|
+
property: 'key',
|
|
206
|
+
type: SemanticTokenTypes.property
|
|
207
|
+
})
|
|
179
208
|
if ('value' in node) {
|
|
180
209
|
acceptor({
|
|
181
210
|
node,
|
|
182
211
|
property: 'value',
|
|
183
|
-
type: SemanticTokenTypes.
|
|
212
|
+
type: SemanticTokenTypes.enum
|
|
184
213
|
})
|
|
185
214
|
}
|
|
186
|
-
|
|
187
215
|
return 'prune'
|
|
188
216
|
}
|
|
189
217
|
if (ast.isElement(node)) {
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
type ViewID
|
|
9
9
|
} from '@likec4/core'
|
|
10
10
|
import { deepEqual as eq } from 'fast-equals'
|
|
11
|
-
import type { URI, WorkspaceCache } from 'langium'
|
|
12
|
-
import { DocumentState, interruptAndCheck
|
|
11
|
+
import type { Cancellation, LangiumDocument, LangiumDocuments, URI, WorkspaceCache } from 'langium'
|
|
12
|
+
import { Disposable, DocumentState, interruptAndCheck } from 'langium'
|
|
13
13
|
import {
|
|
14
14
|
filter,
|
|
15
15
|
find,
|
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
sort,
|
|
27
27
|
values
|
|
28
28
|
} from 'remeda'
|
|
29
|
-
import { type CancellationToken, Disposable } from 'vscode-languageserver-protocol'
|
|
30
29
|
import type {
|
|
31
30
|
ParsedAstElement,
|
|
32
31
|
ParsedAstRelation,
|
|
@@ -245,7 +244,6 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
|
|
|
245
244
|
views
|
|
246
245
|
}
|
|
247
246
|
}
|
|
248
|
-
|
|
249
247
|
const RAW_MODEL_CACHE = 'LikeC4RawModel'
|
|
250
248
|
const MODEL_CACHE = 'LikeC4Model'
|
|
251
249
|
|
|
@@ -282,7 +280,7 @@ export class LikeC4ModelBuilder {
|
|
|
282
280
|
logger.debug(`[ModelBuilder] Created`)
|
|
283
281
|
}
|
|
284
282
|
|
|
285
|
-
public async buildModel(cancelToken?: CancellationToken): Promise<c4.LikeC4Model | null> {
|
|
283
|
+
public async buildModel(cancelToken?: Cancellation.CancellationToken): Promise<c4.LikeC4Model | null> {
|
|
286
284
|
return await this.services.shared.workspace.WorkspaceLock.read(async () => {
|
|
287
285
|
if (cancelToken) {
|
|
288
286
|
await interruptAndCheck(cancelToken)
|
|
@@ -302,7 +300,9 @@ export class LikeC4ModelBuilder {
|
|
|
302
300
|
|
|
303
301
|
private previousViews: Record<ViewID, c4.ComputedView> = {}
|
|
304
302
|
|
|
305
|
-
public async buildComputedModel(
|
|
303
|
+
public async buildComputedModel(
|
|
304
|
+
cancelToken?: Cancellation.CancellationToken
|
|
305
|
+
): Promise<c4.LikeC4ComputedModel | null> {
|
|
306
306
|
const model = await this.buildModel(cancelToken)
|
|
307
307
|
if (!model) {
|
|
308
308
|
return null
|
|
@@ -342,7 +342,10 @@ export class LikeC4ModelBuilder {
|
|
|
342
342
|
})
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
public async computeView(
|
|
345
|
+
public async computeView(
|
|
346
|
+
viewId: ViewID,
|
|
347
|
+
cancelToken?: Cancellation.CancellationToken
|
|
348
|
+
): Promise<c4.ComputedView | null> {
|
|
346
349
|
const model = await this.buildModel(cancelToken)
|
|
347
350
|
const view = model?.views[viewId]
|
|
348
351
|
if (!view) {
|
|
@@ -361,12 +364,12 @@ export class LikeC4ModelBuilder {
|
|
|
361
364
|
logError(result.error)
|
|
362
365
|
return null
|
|
363
366
|
}
|
|
367
|
+
let computedView = result.view
|
|
364
368
|
|
|
365
369
|
const allElementViews = values(model.views).filter(
|
|
366
370
|
(v): v is ScopedElementView => isScopedElementView(v) && v.id !== viewId
|
|
367
371
|
)
|
|
368
372
|
|
|
369
|
-
let computedView = result.view
|
|
370
373
|
computedView.nodes.forEach(node => {
|
|
371
374
|
if (!node.navigateTo) {
|
|
372
375
|
// find first element view that is not the current one
|