@likec4/language-server 1.15.1 → 1.16.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/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.mts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.mjs +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/model-graph/index.cjs +1 -1
- package/dist/model-graph/index.d.cts +4 -2
- package/dist/model-graph/index.d.mts +4 -2
- package/dist/model-graph/index.d.ts +4 -2
- package/dist/model-graph/index.mjs +1 -1
- package/dist/shared/language-server.7iILaJYc.d.ts +1338 -0
- package/dist/shared/{language-server.B8s2wfT_.cjs → language-server.Bd3NZ8uH.cjs} +123 -72
- package/dist/shared/{language-server.BT4WTbFI.mjs → language-server.C5gxpVUH.mjs} +125 -74
- package/dist/shared/language-server.CmBZHwSl.d.cts +1338 -0
- package/dist/shared/{language-server.U2piOAVt.d.cts → language-server.CnkCWVtf.d.cts} +44 -10
- package/dist/shared/{language-server.DfMwkd2l.d.mts → language-server.DIaiY0-C.d.mts} +44 -10
- package/dist/shared/language-server.DViE1Zxi.d.mts +1338 -0
- package/dist/shared/{language-server.BuChFlda.mjs → language-server.D_13fWJQ.mjs} +177 -84
- package/dist/shared/{language-server.j-ShR6as.d.ts → language-server.DwyQ1FtY.d.ts} +44 -10
- package/dist/shared/{language-server.DfjkvknB.cjs → language-server.Dym6GL4P.cjs} +175 -82
- package/package.json +7 -7
- package/src/ast.ts +11 -3
- package/src/generated/ast.ts +111 -10
- package/src/generated/grammar.ts +1 -1
- package/src/like-c4.langium +19 -0
- package/src/model/model-builder.ts +25 -30
- package/src/model/model-parser.ts +91 -18
- package/src/model-graph/LikeC4ModelGraph.ts +22 -11
- package/src/model-graph/compute-view/__test__/fixture.ts +63 -19
- package/src/model-graph/compute-view/compute.ts +77 -72
- package/src/model-graph/dynamic-view/compute.ts +4 -1
- package/src/model-graph/utils/applyViewRuleStyles.ts +0 -4
- package/src/references/scope-computation.ts +10 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/specification.ts +21 -0
- package/src/view-utils/index.ts +0 -1
- package/src/view-utils/resolve-global-rules.ts +66 -50
package/src/like-c4.langium
CHANGED
|
@@ -267,6 +267,7 @@ ViewLayoutDirection returns string:
|
|
|
267
267
|
|
|
268
268
|
ViewRule:
|
|
269
269
|
ViewRulePredicate |
|
|
270
|
+
ViewRuleGlobalPredicateRef |
|
|
270
271
|
ViewRuleGroup |
|
|
271
272
|
ViewRuleStyleOrGlobalRef |
|
|
272
273
|
ViewRuleAutoLayout
|
|
@@ -287,6 +288,7 @@ ViewRuleGroup:
|
|
|
287
288
|
|
|
288
289
|
DynamicViewRule:
|
|
289
290
|
DynamicViewIncludePredicate |
|
|
291
|
+
DynamicViewGlobalPredicateRef |
|
|
290
292
|
ViewRuleStyleOrGlobalRef |
|
|
291
293
|
ViewRuleAutoLayout
|
|
292
294
|
;
|
|
@@ -318,6 +320,9 @@ Predicate:
|
|
|
318
320
|
ElementPredicate
|
|
319
321
|
;
|
|
320
322
|
|
|
323
|
+
ViewRuleGlobalPredicateRef:
|
|
324
|
+
'global' 'predicate' predicate=[GlobalPredicateGroup];
|
|
325
|
+
|
|
321
326
|
ElementPredicate:
|
|
322
327
|
ElementPredicates;
|
|
323
328
|
|
|
@@ -443,6 +448,9 @@ DynamicViewIncludePredicate:
|
|
|
443
448
|
'include' predicates=DynamicViewPredicateIterator
|
|
444
449
|
;
|
|
445
450
|
|
|
451
|
+
DynamicViewGlobalPredicateRef:
|
|
452
|
+
'global' 'predicate' predicate=[GlobalDynamicPredicateGroup];
|
|
453
|
+
|
|
446
454
|
DynamicViewPredicateIterator:
|
|
447
455
|
value=ElementPredicate ({infer DynamicViewPredicateIterator.prev=current} ',' (value=ElementPredicate)?)*
|
|
448
456
|
;
|
|
@@ -506,10 +514,21 @@ RelationNavigateToProperty:
|
|
|
506
514
|
Globals:
|
|
507
515
|
name='global' '{'
|
|
508
516
|
(
|
|
517
|
+
predicates+=(GlobalPredicateGroup | GlobalDynamicPredicateGroup)*
|
|
509
518
|
styles+=(GlobalStyle | GlobalStyleGroup)*
|
|
510
519
|
)*
|
|
511
520
|
'}';
|
|
512
521
|
|
|
522
|
+
GlobalPredicateGroup:
|
|
523
|
+
'predicateGroup' name=IdTerminal '{'
|
|
524
|
+
predicates+=ViewRulePredicate*
|
|
525
|
+
'}';
|
|
526
|
+
|
|
527
|
+
GlobalDynamicPredicateGroup:
|
|
528
|
+
'dynamicPredicateGroup' name=IdTerminal '{'
|
|
529
|
+
predicates+=DynamicViewIncludePredicate*
|
|
530
|
+
'}';
|
|
531
|
+
|
|
513
532
|
GlobalStyleId:
|
|
514
533
|
name=IdTerminal;
|
|
515
534
|
|
|
@@ -13,10 +13,8 @@ import { deepEqual as eq } from 'fast-equals'
|
|
|
13
13
|
import type { Cancellation, LangiumDocument, LangiumDocuments, URI, WorkspaceCache } from 'langium'
|
|
14
14
|
import { Disposable, DocumentState, interruptAndCheck } from 'langium'
|
|
15
15
|
import {
|
|
16
|
-
entries,
|
|
17
16
|
filter,
|
|
18
17
|
flatMap,
|
|
19
|
-
forEach,
|
|
20
18
|
groupBy,
|
|
21
19
|
indexBy,
|
|
22
20
|
isEmpty,
|
|
@@ -46,21 +44,37 @@ import { isParsedLikeC4LangiumDocument } from '../ast'
|
|
|
46
44
|
import { logError, logger, logWarnError } from '../logger'
|
|
47
45
|
import { computeDynamicView, computeView, LikeC4ModelGraph } from '../model-graph'
|
|
48
46
|
import type { LikeC4Services } from '../module'
|
|
49
|
-
import { assignNavigateTo,
|
|
47
|
+
import { assignNavigateTo, resolveRelativePaths, resolveRulesExtendedViews } from '../view-utils'
|
|
50
48
|
|
|
51
49
|
function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[]): c4.ParsedLikeC4Model {
|
|
50
|
+
// Merge specifications and globals from all documents
|
|
52
51
|
const c4Specification: ParsedAstSpecification = {
|
|
53
52
|
tags: new Set(),
|
|
54
53
|
elements: {},
|
|
55
54
|
relationships: {},
|
|
56
55
|
colors: {}
|
|
57
56
|
}
|
|
58
|
-
|
|
57
|
+
const globals: c4.ModelGlobals = {
|
|
58
|
+
predicates: {},
|
|
59
|
+
dynamicPredicates: {},
|
|
60
|
+
styles: {}
|
|
61
|
+
}
|
|
62
|
+
for (const doc of docs) {
|
|
63
|
+
const {
|
|
64
|
+
c4Specification: spec,
|
|
65
|
+
c4Globals
|
|
66
|
+
} = doc
|
|
67
|
+
|
|
59
68
|
spec.tags.forEach(t => c4Specification.tags.add(t))
|
|
60
69
|
Object.assign(c4Specification.elements, spec.elements)
|
|
61
70
|
Object.assign(c4Specification.relationships, spec.relationships)
|
|
62
71
|
Object.assign(c4Specification.colors, spec.colors)
|
|
63
|
-
|
|
72
|
+
|
|
73
|
+
Object.assign(globals.predicates, c4Globals.predicates)
|
|
74
|
+
Object.assign(globals.dynamicPredicates, c4Globals.dynamicPredicates)
|
|
75
|
+
Object.assign(globals.styles, c4Globals.styles)
|
|
76
|
+
}
|
|
77
|
+
|
|
64
78
|
function resolveLinks(doc: LangiumDocument, links: c4.NonEmptyArray<ParsedLink>) {
|
|
65
79
|
return map(
|
|
66
80
|
links,
|
|
@@ -211,23 +225,6 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
|
|
|
211
225
|
indexBy(prop('id'))
|
|
212
226
|
)
|
|
213
227
|
|
|
214
|
-
const parsedGlobals: {
|
|
215
|
-
styles: Record<c4.GlobalStyleID, c4.ViewRuleStyle[]>
|
|
216
|
-
} = {
|
|
217
|
-
styles: {}
|
|
218
|
-
}
|
|
219
|
-
Object.assign(parsedGlobals.styles, ...docs.map(d => d.c4Globals.styles))
|
|
220
|
-
|
|
221
|
-
const globals: {
|
|
222
|
-
styles: Record<c4.GlobalStyleID, c4.GlobalStyle>
|
|
223
|
-
} = {
|
|
224
|
-
styles: pipe(
|
|
225
|
-
entries(parsedGlobals.styles),
|
|
226
|
-
map(([id, styles]) => ({ id, styles })),
|
|
227
|
-
indexBy(prop('id'))
|
|
228
|
-
)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
228
|
function toC4View(doc: LangiumDocument) {
|
|
232
229
|
const docUri = doc.uri.toString()
|
|
233
230
|
return (parsedAstView: ParsedAstView): c4.LikeC4View => {
|
|
@@ -399,10 +396,9 @@ export class LikeC4ModelBuilder {
|
|
|
399
396
|
|
|
400
397
|
const allViews = [] as c4.ComputedView[]
|
|
401
398
|
for (const view of values(model.views)) {
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
: computeDynamicView(resolvedView, index)
|
|
399
|
+
const result = isElementView(view)
|
|
400
|
+
? computeView(view, index)
|
|
401
|
+
: computeDynamicView(view, index)
|
|
406
402
|
if (!result.isSuccess) {
|
|
407
403
|
logWarnError(result.error)
|
|
408
404
|
continue
|
|
@@ -467,10 +463,9 @@ export class LikeC4ModelBuilder {
|
|
|
467
463
|
return null
|
|
468
464
|
}
|
|
469
465
|
const index = new LikeC4ModelGraph(model)
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
: computeDynamicView(resolvedView, index)
|
|
466
|
+
const result = isElementView(view)
|
|
467
|
+
? computeView(view, index)
|
|
468
|
+
: computeDynamicView(view, index)
|
|
474
469
|
if (!result.isSuccess) {
|
|
475
470
|
logError(result.error)
|
|
476
471
|
return null
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ParsedAstDynamicView,
|
|
12
12
|
ParsedAstElement,
|
|
13
13
|
ParsedAstElementView,
|
|
14
|
+
ParsedAstGlobals,
|
|
14
15
|
ParsedAstRelation,
|
|
15
16
|
ParsedLikeC4LangiumDocument,
|
|
16
17
|
ParsedLink
|
|
@@ -288,6 +289,25 @@ export class LikeC4ModelParser {
|
|
|
288
289
|
const { parseResult, c4Globals } = doc
|
|
289
290
|
|
|
290
291
|
const globals = parseResult.value.globals.filter(isValid)
|
|
292
|
+
|
|
293
|
+
const elRelPredicates = globals.flatMap(r => r.predicates.filter(isValid))
|
|
294
|
+
for (const predicate of elRelPredicates) {
|
|
295
|
+
try {
|
|
296
|
+
const globalPredicateId = predicate.name as c4.GlobalPredicateId
|
|
297
|
+
if (!isTruthy(globalPredicateId)) {
|
|
298
|
+
continue
|
|
299
|
+
}
|
|
300
|
+
if (globalPredicateId in c4Globals.predicates) {
|
|
301
|
+
logger.warn(`Global predicate named "${globalPredicateId}" is already defined`)
|
|
302
|
+
continue
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
this.parseAndStoreGlobalPredicateGroupOrDynamic(predicate, globalPredicateId, c4Globals, isValid)
|
|
306
|
+
} catch (e) {
|
|
307
|
+
logWarnError(e)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
291
311
|
const styles = globals.flatMap(r => r.styles.filter(isValid))
|
|
292
312
|
for (const style of styles) {
|
|
293
313
|
try {
|
|
@@ -310,12 +330,49 @@ export class LikeC4ModelParser {
|
|
|
310
330
|
}
|
|
311
331
|
}
|
|
312
332
|
|
|
333
|
+
private parseAndStoreGlobalPredicateGroupOrDynamic(
|
|
334
|
+
astRule: ast.GlobalPredicateGroup | ast.GlobalDynamicPredicateGroup,
|
|
335
|
+
id: c4.GlobalPredicateId,
|
|
336
|
+
c4Globals: ParsedAstGlobals,
|
|
337
|
+
isValid: IsValidFn
|
|
338
|
+
) {
|
|
339
|
+
if (ast.isGlobalPredicateGroup(astRule)) {
|
|
340
|
+
const predicates = this.parseGlobalPredicateGroup(astRule, isValid)
|
|
341
|
+
if (predicates.length > 0) {
|
|
342
|
+
c4Globals.predicates[id] = predicates as c4.NonEmptyArray<c4.ViewRulePredicate>
|
|
343
|
+
}
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
if (ast.isGlobalDynamicPredicateGroup(astRule)) {
|
|
347
|
+
const predicates = this.parseGlobalDynamicPredicateGroup(astRule, isValid)
|
|
348
|
+
if (predicates.length > 0) {
|
|
349
|
+
c4Globals.dynamicPredicates[id] = predicates as c4.NonEmptyArray<c4.DynamicViewIncludeRule>
|
|
350
|
+
}
|
|
351
|
+
return
|
|
352
|
+
}
|
|
353
|
+
nonexhaustive(astRule)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private parseGlobalPredicateGroup(
|
|
357
|
+
astRule: ast.GlobalPredicateGroup,
|
|
358
|
+
isValid: IsValidFn
|
|
359
|
+
): c4.ViewRulePredicate[] {
|
|
360
|
+
return astRule.predicates.map(p => this.parseViewRulePredicate(p, isValid))
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private parseGlobalDynamicPredicateGroup(
|
|
364
|
+
astRule: ast.GlobalDynamicPredicateGroup,
|
|
365
|
+
isValid: IsValidFn
|
|
366
|
+
): c4.DynamicViewIncludeRule[] {
|
|
367
|
+
return astRule.predicates.map(p => this.parseDynamicViewIncludePredicate(p, isValid))
|
|
368
|
+
}
|
|
369
|
+
|
|
313
370
|
private parseGlobalStyleOrGroup(
|
|
314
371
|
astRule: ast.GlobalStyle | ast.GlobalStyleGroup,
|
|
315
372
|
isValid: IsValidFn
|
|
316
373
|
): c4.ViewRuleStyle[] {
|
|
317
374
|
if (ast.isGlobalStyle(astRule)) {
|
|
318
|
-
return [this.
|
|
375
|
+
return [this.parseViewRuleStyle(astRule, isValid)]
|
|
319
376
|
}
|
|
320
377
|
if (ast.isGlobalStyleGroup(astRule)) {
|
|
321
378
|
return astRule.styles.map(s => this.parseViewRuleStyle(s, isValid))
|
|
@@ -323,19 +380,17 @@ export class LikeC4ModelParser {
|
|
|
323
380
|
nonexhaustive(astRule)
|
|
324
381
|
}
|
|
325
382
|
|
|
326
|
-
private parseGlobalStyle(astRule: ast.GlobalStyle, isValid: IsValidFn): c4.ViewRuleStyle {
|
|
327
|
-
const styleProps = astRule.props.filter(ast.isStyleProperty)
|
|
328
|
-
const targets = astRule.target
|
|
329
|
-
const notation = astRule.props.find(ast.isNotationProperty)
|
|
330
|
-
return this.parseRuleStyle(styleProps, targets, isValid, notation)
|
|
331
|
-
}
|
|
332
|
-
|
|
333
383
|
private parseViews(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
|
|
334
384
|
const viewBlocks = doc.parseResult.value.views.filter(v => isValid(v))
|
|
335
385
|
for (const viewBlock of viewBlocks) {
|
|
336
|
-
const localStyles = viewBlock.styles
|
|
337
|
-
|
|
338
|
-
|
|
386
|
+
const localStyles = viewBlock.styles.flatMap(s => {
|
|
387
|
+
try {
|
|
388
|
+
return this.parseViewRuleStyleOrGlobalRef(s, isValid)
|
|
389
|
+
} catch (e) {
|
|
390
|
+
logWarnError(e)
|
|
391
|
+
return []
|
|
392
|
+
}
|
|
393
|
+
})
|
|
339
394
|
|
|
340
395
|
for (const view of viewBlock.views) {
|
|
341
396
|
try {
|
|
@@ -344,8 +399,8 @@ export class LikeC4ModelParser {
|
|
|
344
399
|
}
|
|
345
400
|
doc.c4Views.push(
|
|
346
401
|
ast.isElementView(view)
|
|
347
|
-
? this.parseElementView(view,
|
|
348
|
-
: this.parseDynamicElementView(view,
|
|
402
|
+
? this.parseElementView(view, localStyles, isValid)
|
|
403
|
+
: this.parseDynamicElementView(view, localStyles, isValid)
|
|
349
404
|
)
|
|
350
405
|
} catch (e) {
|
|
351
406
|
logWarnError(e)
|
|
@@ -415,14 +470,14 @@ export class LikeC4ModelParser {
|
|
|
415
470
|
}
|
|
416
471
|
}
|
|
417
472
|
if (ast.isElementKindExpression(astNode)) {
|
|
418
|
-
invariant(astNode.kind, 'ElementKindExpr kind is not resolved: ' + astNode.$cstNode?.text)
|
|
473
|
+
invariant(astNode.kind?.ref, 'ElementKindExpr kind is not resolved: ' + astNode.$cstNode?.text)
|
|
419
474
|
return {
|
|
420
|
-
elementKind: astNode.kind
|
|
475
|
+
elementKind: astNode.kind.ref.name as c4.ElementKind,
|
|
421
476
|
isEqual: astNode.isEqual
|
|
422
477
|
}
|
|
423
478
|
}
|
|
424
479
|
if (ast.isElementTagExpression(astNode)) {
|
|
425
|
-
invariant(astNode.tag, 'ElementTagExpr tag is not resolved: ' + astNode.$cstNode?.text)
|
|
480
|
+
invariant(astNode.tag?.ref, 'ElementTagExpr tag is not resolved: ' + astNode.$cstNode?.text)
|
|
426
481
|
let elementTag = astNode.tag.$refText
|
|
427
482
|
if (elementTag.startsWith('#')) {
|
|
428
483
|
elementTag = elementTag.slice(1)
|
|
@@ -653,6 +708,9 @@ export class LikeC4ModelParser {
|
|
|
653
708
|
if (ast.isViewRulePredicate(astRule)) {
|
|
654
709
|
return this.parseViewRulePredicate(astRule, isValid)
|
|
655
710
|
}
|
|
711
|
+
if (ast.isViewRuleGlobalPredicateRef(astRule)) {
|
|
712
|
+
return this.parseViewRuleGlobalPredicateRef(astRule, isValid)
|
|
713
|
+
}
|
|
656
714
|
if (ast.isViewRuleStyleOrGlobalRef(astRule)) {
|
|
657
715
|
return this.parseViewRuleStyleOrGlobalRef(astRule, isValid)
|
|
658
716
|
}
|
|
@@ -665,7 +723,19 @@ export class LikeC4ModelParser {
|
|
|
665
723
|
nonexhaustive(astRule)
|
|
666
724
|
}
|
|
667
725
|
|
|
668
|
-
private
|
|
726
|
+
private parseViewRuleGlobalPredicateRef(
|
|
727
|
+
astRule: ast.ViewRuleGlobalPredicateRef | ast.DynamicViewGlobalPredicateRef,
|
|
728
|
+
_isValid: IsValidFn
|
|
729
|
+
): c4.ViewRuleGlobalPredicateRef {
|
|
730
|
+
return {
|
|
731
|
+
predicateId: astRule.predicate.$refText as c4.GlobalPredicateId
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
private parseViewRuleStyleOrGlobalRef(
|
|
736
|
+
astRule: ast.ViewRuleStyleOrGlobalRef,
|
|
737
|
+
isValid: IsValidFn
|
|
738
|
+
): c4.ViewRuleStyleOrGlobalRef {
|
|
669
739
|
if (ast.isViewRuleStyle(astRule)) {
|
|
670
740
|
return this.parseViewRuleStyle(astRule, isValid)
|
|
671
741
|
}
|
|
@@ -675,7 +745,7 @@ export class LikeC4ModelParser {
|
|
|
675
745
|
nonexhaustive(astRule)
|
|
676
746
|
}
|
|
677
747
|
|
|
678
|
-
private parseViewRuleStyle(astRule: ast.ViewRuleStyle, isValid: IsValidFn): c4.ViewRuleStyle {
|
|
748
|
+
private parseViewRuleStyle(astRule: ast.ViewRuleStyle | ast.GlobalStyle, isValid: IsValidFn): c4.ViewRuleStyle {
|
|
679
749
|
const styleProps = astRule.props.filter(ast.isStyleProperty)
|
|
680
750
|
const targets = astRule.target
|
|
681
751
|
const notation = astRule.props.find(ast.isNotationProperty)
|
|
@@ -971,6 +1041,9 @@ export class LikeC4ModelParser {
|
|
|
971
1041
|
if (ast.isDynamicViewIncludePredicate(astRule)) {
|
|
972
1042
|
return this.parseDynamicViewIncludePredicate(astRule, isValid)
|
|
973
1043
|
}
|
|
1044
|
+
if (ast.isDynamicViewGlobalPredicateRef(astRule)) {
|
|
1045
|
+
return this.parseViewRuleGlobalPredicateRef(astRule, isValid)
|
|
1046
|
+
}
|
|
974
1047
|
if (ast.isViewRuleStyleOrGlobalRef(astRule)) {
|
|
975
1048
|
return this.parseViewRuleStyleOrGlobalRef(astRule, isValid)
|
|
976
1049
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type Fqn,
|
|
6
6
|
invariant,
|
|
7
7
|
isSameHierarchy,
|
|
8
|
+
type ModelGlobals,
|
|
8
9
|
parentFqn,
|
|
9
10
|
type Relation,
|
|
10
11
|
type RelationID
|
|
@@ -14,7 +15,8 @@ import { isArray, isString } from 'remeda'
|
|
|
14
15
|
type Params = {
|
|
15
16
|
elements: Record<Fqn, Element>
|
|
16
17
|
relations: Record<RelationID, Relation>
|
|
17
|
-
//
|
|
18
|
+
// Optional for tests
|
|
19
|
+
globals?: ModelGlobals
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
type RelationEdge = {
|
|
@@ -41,24 +43,33 @@ function intersection<T>(a: Set<T>, b: Set<T>) {
|
|
|
41
43
|
* Subject to change.
|
|
42
44
|
*/
|
|
43
45
|
export class LikeC4ModelGraph {
|
|
44
|
-
#elements = new Map<Fqn, Element>()
|
|
46
|
+
readonly #elements = new Map<Fqn, Element>()
|
|
45
47
|
// Parent element for given FQN
|
|
46
|
-
#parents = new Map<Fqn, Element>()
|
|
48
|
+
readonly #parents = new Map<Fqn, Element>()
|
|
47
49
|
// Children elements for given FQN
|
|
48
|
-
#children = new Map<Fqn, Element[]>()
|
|
49
|
-
#rootElements = new Set<Element>()
|
|
50
|
+
readonly #children = new Map<Fqn, Element[]>()
|
|
51
|
+
readonly #rootElements = new Set<Element>()
|
|
50
52
|
|
|
51
|
-
#relations = new Map<RelationID, Relation>()
|
|
53
|
+
readonly #relations = new Map<RelationID, Relation>()
|
|
52
54
|
// Incoming to an element or its descendants
|
|
53
|
-
#incoming = new MapRelations()
|
|
55
|
+
readonly #incoming = new MapRelations()
|
|
54
56
|
// Outgoing from an element or its descendants
|
|
55
|
-
#outgoing = new MapRelations()
|
|
57
|
+
readonly #outgoing = new MapRelations()
|
|
56
58
|
// Relationships inside the element, among descendants
|
|
57
|
-
#internal = new MapRelations()
|
|
59
|
+
readonly #internal = new MapRelations()
|
|
58
60
|
|
|
59
|
-
#cacheAscendingSiblings = new Map<Fqn, Element[]>()
|
|
61
|
+
readonly #cacheAscendingSiblings = new Map<Fqn, Element[]>()
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
public readonly globals: ModelGlobals
|
|
64
|
+
|
|
65
|
+
constructor(
|
|
66
|
+
{ elements, relations, globals }: Params
|
|
67
|
+
) {
|
|
68
|
+
this.globals = globals ?? {
|
|
69
|
+
predicates: {},
|
|
70
|
+
dynamicPredicates: {},
|
|
71
|
+
styles: {}
|
|
72
|
+
}
|
|
62
73
|
for (const el of Object.values(elements)) {
|
|
63
74
|
this.addElement(el)
|
|
64
75
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type ElementWhereExpr,
|
|
12
12
|
type Expression as C4Expression,
|
|
13
13
|
type Fqn,
|
|
14
|
+
type GlobalStyleID,
|
|
14
15
|
type IncomingExpr as C4IncomingExpr,
|
|
15
16
|
type InOutExpr as C4InOutExpr,
|
|
16
17
|
isElementRef,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
type Tag,
|
|
29
30
|
type ViewID,
|
|
30
31
|
type ViewRule,
|
|
32
|
+
type ViewRuleGlobalStyle,
|
|
31
33
|
type ViewRuleGroup,
|
|
32
34
|
type ViewRulePredicate,
|
|
33
35
|
type ViewRuleStyle,
|
|
@@ -331,11 +333,37 @@ export const fakeRelations = [
|
|
|
331
333
|
})
|
|
332
334
|
]
|
|
333
335
|
|
|
336
|
+
export const globalStyles = {
|
|
337
|
+
'mute_old': [{
|
|
338
|
+
targets: [$expr({
|
|
339
|
+
elementTag: 'old' as Tag,
|
|
340
|
+
isEqual: true
|
|
341
|
+
})],
|
|
342
|
+
style: {
|
|
343
|
+
color: 'muted'
|
|
344
|
+
}
|
|
345
|
+
}],
|
|
346
|
+
'red_next': [{
|
|
347
|
+
targets: [$expr({
|
|
348
|
+
elementTag: 'next' as Tag,
|
|
349
|
+
isEqual: true
|
|
350
|
+
})],
|
|
351
|
+
style: {
|
|
352
|
+
color: 'red'
|
|
353
|
+
}
|
|
354
|
+
}]
|
|
355
|
+
} as const
|
|
356
|
+
|
|
334
357
|
export type FakeRelationIds = (typeof fakeRelations)[number]['id']
|
|
335
358
|
|
|
336
359
|
export const fakeModel = new LikeC4ModelGraph({
|
|
337
360
|
elements: fakeElements,
|
|
338
|
-
relations: indexBy(fakeRelations, r => r.id)
|
|
361
|
+
relations: indexBy(fakeRelations, r => r.id),
|
|
362
|
+
globals: {
|
|
363
|
+
predicates: {},
|
|
364
|
+
dynamicPredicates: {},
|
|
365
|
+
styles: globalStyles
|
|
366
|
+
}
|
|
339
367
|
})
|
|
340
368
|
|
|
341
369
|
const emptyView = {
|
|
@@ -510,30 +538,34 @@ type CustomProps = {
|
|
|
510
538
|
}
|
|
511
539
|
export function $include(expr: Expression | C4Expression, props?: CustomProps): ViewRulePredicate {
|
|
512
540
|
let _expr = props?.where ? $where(expr, props.where) : $expr(expr)
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
541
|
+
_expr = props?.with ? $with(_expr, props.with) : _expr
|
|
542
|
+
return {
|
|
543
|
+
include: [_expr]
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
export function $with(expr: C4Expression, props?: CustomProps['with']): C4CustomRelationExpr | C4CustomElementExpr {
|
|
547
|
+
if (isRelationExpression(expr) || isRelationWhere(expr)) {
|
|
548
|
+
return {
|
|
549
|
+
customRelation: {
|
|
550
|
+
relation: expr,
|
|
551
|
+
...props as any
|
|
520
552
|
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
553
|
+
}
|
|
554
|
+
} else if (isElementRef(expr) || isElementWhere(expr)) {
|
|
555
|
+
return {
|
|
556
|
+
custom: {
|
|
557
|
+
expr: expr,
|
|
558
|
+
...props as any
|
|
527
559
|
}
|
|
528
560
|
}
|
|
529
561
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}
|
|
562
|
+
|
|
563
|
+
throw 'Unsupported type of internal expression'
|
|
533
564
|
}
|
|
534
|
-
export function $exclude(expr: Expression | C4Expression): ViewRulePredicate {
|
|
565
|
+
export function $exclude(expr: Expression | C4Expression, where?: WhereOperator<TestTag, string>): ViewRulePredicate {
|
|
566
|
+
let _expr = where ? $where(expr, where) : $expr(expr)
|
|
535
567
|
return {
|
|
536
|
-
exclude: [
|
|
568
|
+
exclude: [_expr]
|
|
537
569
|
}
|
|
538
570
|
}
|
|
539
571
|
export function $group(groupRules: ViewRuleGroup['groupRules']): ViewRuleGroup {
|
|
@@ -550,6 +582,18 @@ export function $style(element: ElementRefExpr, style: ViewRuleStyle['style']):
|
|
|
550
582
|
}
|
|
551
583
|
}
|
|
552
584
|
|
|
585
|
+
type GlobalStyles = keyof typeof globalStyles
|
|
586
|
+
type GlobalExpr = `style ${GlobalStyles}`
|
|
587
|
+
export function $global(expr: GlobalExpr): ViewRuleGlobalStyle {
|
|
588
|
+
const [_t, id] = expr.split(' ') as [string, string]
|
|
589
|
+
if (_t === 'style') {
|
|
590
|
+
return {
|
|
591
|
+
styleId: id as GlobalStyleID
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
throw new Error('Invalid global expression')
|
|
595
|
+
}
|
|
596
|
+
|
|
553
597
|
export function computeView(
|
|
554
598
|
...args: [FakeElementIds, ViewRule | ViewRule[]] | [ViewRule | ViewRule[]]
|
|
555
599
|
) {
|