@likec4/language-server 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.mts +2 -2
  5. package/dist/browser.d.ts +2 -2
  6. package/dist/browser.mjs +2 -2
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.d.cts +2 -2
  9. package/dist/index.d.mts +2 -2
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.mjs +2 -2
  12. package/dist/protocol.d.cts +8 -5
  13. package/dist/protocol.d.mts +8 -5
  14. package/dist/protocol.d.ts +8 -5
  15. package/dist/shared/{language-server.DZRuJVSg.cjs → language-server.CO_nmHiL.cjs} +5605 -4215
  16. package/dist/shared/{language-server.DJo88TnT.d.cts → language-server.Da6ey08o.d.cts} +391 -110
  17. package/dist/shared/{language-server.PEjk7U9s.d.ts → language-server.De7S3e5Z.d.ts} +391 -110
  18. package/dist/shared/{language-server.BgDKnNok.d.mts → language-server.Dj4iDjtB.d.mts} +391 -110
  19. package/dist/shared/{language-server.B8qSDsWW.mjs → language-server.oO_9JoAG.mjs} +5594 -4215
  20. package/package.json +17 -31
  21. package/src/Rpc.ts +6 -3
  22. package/src/ast.ts +124 -71
  23. package/src/formatting/LikeC4Formatter.ts +9 -4
  24. package/src/generated/ast.ts +656 -40
  25. package/src/generated/grammar.ts +2 -2
  26. package/src/generated/module.ts +3 -2
  27. package/src/index.ts +1 -0
  28. package/src/like-c4.langium +170 -22
  29. package/src/logger.ts +7 -2
  30. package/src/lsp/CodeLensProvider.ts +0 -1
  31. package/src/lsp/CompletionProvider.ts +17 -2
  32. package/src/lsp/DocumentSymbolProvider.ts +5 -2
  33. package/src/lsp/HoverProvider.ts +34 -2
  34. package/src/lsp/SemanticTokenProvider.ts +58 -32
  35. package/src/model/deployments-index.ts +218 -0
  36. package/src/model/fqn-computation.ts +1 -1
  37. package/src/model/fqn-index.ts +0 -1
  38. package/src/model/index.ts +1 -0
  39. package/src/model/model-builder.ts +172 -37
  40. package/src/model/model-locator.ts +36 -7
  41. package/src/model/model-parser.ts +554 -92
  42. package/src/model-change/changeViewLayout.ts +2 -2
  43. package/src/module.ts +10 -4
  44. package/src/protocol.ts +10 -6
  45. package/src/references/index.ts +1 -0
  46. package/src/references/name-provider.ts +37 -0
  47. package/src/references/scope-computation.ts +130 -21
  48. package/src/references/scope-provider.ts +63 -36
  49. package/src/shared/NodeKindProvider.ts +15 -3
  50. package/src/utils/deploymentRef.ts +31 -0
  51. package/src/{elementRef.ts → utils/elementRef.ts} +1 -1
  52. package/src/utils/stringHash.ts +2 -2
  53. package/src/validation/_shared.ts +7 -5
  54. package/src/validation/deployment-checks.ts +144 -0
  55. package/src/validation/dynamic-view-step.ts +1 -1
  56. package/src/validation/index.ts +7 -0
  57. package/src/validation/relation.ts +1 -1
  58. package/src/validation/view-predicates/deployments.ts +56 -0
  59. package/src/validation/view-predicates/index.ts +1 -0
  60. package/src/view-utils/assignNavigateTo.ts +6 -5
  61. package/src/view-utils/index.ts +0 -1
  62. package/dist/model-graph/index.cjs +0 -10
  63. package/dist/model-graph/index.d.cts +0 -81
  64. package/dist/model-graph/index.d.mts +0 -81
  65. package/dist/model-graph/index.d.ts +0 -81
  66. package/dist/model-graph/index.mjs +0 -1
  67. package/dist/shared/language-server.BGGRJRnr.d.mts +0 -1338
  68. package/dist/shared/language-server.BXFhlTPo.mjs +0 -1953
  69. package/dist/shared/language-server.Bmpq16Gw.d.ts +0 -1338
  70. package/dist/shared/language-server.C1ZfM22X.d.cts +0 -1338
  71. package/dist/shared/language-server.N8HLDQqz.cjs +0 -1967
  72. package/src/model-graph/LikeC4ModelGraph.ts +0 -338
  73. package/src/model-graph/compute-view/__test__/fixture.ts +0 -630
  74. package/src/model-graph/compute-view/compute.ts +0 -788
  75. package/src/model-graph/compute-view/index.ts +0 -33
  76. package/src/model-graph/compute-view/predicates.ts +0 -509
  77. package/src/model-graph/dynamic-view/__test__/fixture.ts +0 -61
  78. package/src/model-graph/dynamic-view/compute.ts +0 -281
  79. package/src/model-graph/dynamic-view/index.ts +0 -29
  80. package/src/model-graph/index.ts +0 -3
  81. package/src/model-graph/utils/applyCustomElementProperties.ts +0 -65
  82. package/src/model-graph/utils/applyCustomRelationProperties.ts +0 -41
  83. package/src/model-graph/utils/applyViewRuleStyles.ts +0 -49
  84. package/src/model-graph/utils/buildComputeNodes.ts +0 -113
  85. package/src/model-graph/utils/buildElementNotations.ts +0 -63
  86. package/src/model-graph/utils/elementExpressionToPredicate.ts +0 -39
  87. package/src/model-graph/utils/relationExpressionToPredicates.ts +0 -43
  88. package/src/model-graph/utils/sortNodes.ts +0 -105
  89. package/src/model-graph/utils/uniqueTags.test.ts +0 -42
  90. package/src/model-graph/utils/uniqueTags.ts +0 -19
  91. package/src/utils/graphlib.ts +0 -9
  92. package/src/view-utils/resolve-extended-views.ts +0 -66
  93. package/src/view-utils/resolve-global-rules.ts +0 -88
  94. package/src/view-utils/view-hash.ts +0 -27
@@ -1,788 +0,0 @@
1
- import {
2
- type Color,
3
- type ComputedEdge,
4
- type ComputedElementView,
5
- type ComputedNode,
6
- type EdgeId,
7
- type Element,
8
- ElementKind,
9
- type ElementPredicateExpression,
10
- type ElementView,
11
- type Fqn,
12
- type NodeId,
13
- type NonEmptyArray,
14
- type Relation,
15
- type RelationPredicateExpression,
16
- type RelationshipArrowType,
17
- type RelationshipKind,
18
- type RelationshipLineType,
19
- type Tag,
20
- type ViewID,
21
- type ViewRuleGroup,
22
- type ViewRulePredicate
23
- } from '@likec4/core'
24
- import {
25
- commonAncestor,
26
- commonHead,
27
- compareRelations,
28
- Expr,
29
- invariant,
30
- isAncestor,
31
- isScopedElementView,
32
- isViewRuleAutoLayout,
33
- isViewRuleGroup,
34
- isViewRulePredicate,
35
- nonexhaustive,
36
- nonNullable,
37
- whereOperatorAsPredicate
38
- } from '@likec4/core'
39
- import {
40
- anyPass,
41
- concat,
42
- difference,
43
- filter,
44
- first,
45
- hasAtLeast,
46
- isNonNull,
47
- isTruthy,
48
- last,
49
- map,
50
- omit,
51
- only,
52
- pipe,
53
- reduce,
54
- reverse,
55
- sort,
56
- unique
57
- } from 'remeda'
58
- import { resolveGlobalRulesInElementView } from '../../view-utils/resolve-global-rules'
59
- import { calcViewLayoutHash } from '../../view-utils/view-hash'
60
- import type { LikeC4ModelGraph } from '../LikeC4ModelGraph'
61
- import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
62
- import { applyCustomRelationProperties } from '../utils/applyCustomRelationProperties'
63
- import { applyViewRuleStyles } from '../utils/applyViewRuleStyles'
64
- import { buildComputeNodes } from '../utils/buildComputeNodes'
65
- import { buildElementNotations } from '../utils/buildElementNotations'
66
- import { sortNodes } from '../utils/sortNodes'
67
- import { uniqueTags } from '../utils/uniqueTags'
68
- import {
69
- type ElementPredicateFn,
70
- excludeElementKindOrTag,
71
- excludeElementRef,
72
- excludeExpandedElementExpr,
73
- excludeIncomingExpr,
74
- excludeInOutExpr,
75
- excludeOutgoingExpr,
76
- excludeRelationExpr,
77
- excludeWildcardRef,
78
- includeElementKindOrTag,
79
- includeElementRef,
80
- includeExpandedElementExpr,
81
- includeIncomingExpr,
82
- includeInOutExpr,
83
- includeOutgoingExpr,
84
- includeRelationExpr,
85
- includeWildcardRef,
86
- type RelationPredicateFn
87
- } from './predicates'
88
-
89
- // eslint-disable-next-line @typescript-eslint/no-namespace
90
- export namespace ComputeCtx {
91
- // Intermediate ComputedEdge
92
- export interface Edge {
93
- source: Element
94
- target: Element
95
- relations: Relation[]
96
- }
97
- }
98
-
99
- function compareEdges(a: ComputeCtx.Edge, b: ComputeCtx.Edge) {
100
- return compareRelations(
101
- { source: a.source.id, target: a.target.id },
102
- { source: b.source.id, target: b.target.id }
103
- )
104
- }
105
-
106
- // If there is only one relation and it has same source and target as edge
107
- function isDirectEdge({ relations: [rel, ...tail], source, target }: ComputeCtx.Edge) {
108
- if (rel && tail.length === 0) {
109
- return rel.source === source.id && rel.target === target.id
110
- }
111
- return false
112
- }
113
-
114
- export class NodesGroup {
115
- static readonly kind = ElementKind.Group
116
-
117
- static root() {
118
- return new NodesGroup('@root' as NodeId, { title: null, groupRules: [] })
119
- }
120
-
121
- static is(node: ComputedNode) {
122
- return node.kind === NodesGroup.kind
123
- }
124
-
125
- readonly explicits = new Set<Element>()
126
- readonly implicits = new Set<Element>()
127
-
128
- constructor(
129
- public id: NodeId,
130
- public viewRule: ViewRuleGroup,
131
- public parent: NodeId | null = null
132
- ) {
133
- }
134
-
135
- /**
136
- * Add element explicitly
137
- * Included even without relationships
138
- */
139
- addElement(...el: Element[]) {
140
- for (const r of el) {
141
- this.explicits.add(r)
142
- this.implicits.add(r)
143
- }
144
- }
145
-
146
- /**
147
- * Add element implicitly
148
- * Included if only has relationships
149
- */
150
- addImplicit(...el: Element[]) {
151
- for (const r of el) {
152
- this.implicits.add(r)
153
- }
154
- }
155
-
156
- excludeElement(...excludes: Element[]) {
157
- for (const el of excludes) {
158
- this.explicits.delete(el)
159
- this.implicits.delete(el)
160
- }
161
- }
162
-
163
- isEmpty() {
164
- return this.explicits.size === 0 && this.implicits.size === 0
165
- }
166
- }
167
-
168
- export class ComputeCtx {
169
- // Intermediate state
170
- private explicits = new Set<Element>()
171
- private implicits = new Set<Element>()
172
- private ctxEdges = [] as ComputeCtx.Edge[]
173
-
174
- /**
175
- * Root group - not included in the groups
176
- * But used to accumulate elements that are not in any group
177
- */
178
- private __rootGroup = NodesGroup.root()
179
- private groups = [] as NodesGroup[]
180
- private activeGroupStack = [] as NodesGroup[]
181
-
182
- protected get activeGroup() {
183
- return this.activeGroupStack[0] ?? this.__rootGroup
184
- }
185
-
186
- protected get includedElements() {
187
- return new Set([
188
- ...this.explicits,
189
- ...this.ctxEdges.flatMap(e => [e.source, e.target])
190
- ]) as ReadonlySet<Element>
191
- }
192
-
193
- protected get resolvedElements() {
194
- return new Set([
195
- ...this.explicits,
196
- ...this.implicits,
197
- ...this.ctxEdges.flatMap(e => [e.source, e.target])
198
- ]) as ReadonlySet<Element>
199
- }
200
-
201
- protected get edges() {
202
- return this.ctxEdges
203
- }
204
-
205
- protected get root() {
206
- return isScopedElementView(this.view) ? this.view.viewOf : null
207
- }
208
-
209
- public static elementView(view: ElementView, graph: LikeC4ModelGraph) {
210
- return new ComputeCtx(view, graph).compute()
211
- }
212
-
213
- private constructor(
214
- protected view: ElementView,
215
- protected graph: LikeC4ModelGraph
216
- ) {}
217
-
218
- protected compute(): ComputedElementView {
219
- this.reset()
220
- const {
221
- docUri: _docUri, // exclude docUri
222
- rules: _rules, // exclude rules
223
- ...view
224
- } = this.view
225
-
226
- const rules = resolveGlobalRulesInElementView(this.view, this.graph.globals)
227
-
228
- const viewPredicates = rules.filter(anyPass([isViewRulePredicate, isViewRuleGroup])) as Array<
229
- ViewRulePredicate | ViewRuleGroup
230
- >
231
- if (this.root && viewPredicates.length == 0) {
232
- this.addElement(this.graph.element(this.root))
233
- }
234
- this.processPredicates(viewPredicates)
235
- this.removeRedundantImplicitEdges()
236
- if (this.groups.length > 0) {
237
- this.cleanGroupElements()
238
- }
239
-
240
- const elements = [...this.includedElements]
241
- const nodesMap = buildComputeNodes(elements, this.groups)
242
-
243
- const edges = this.computeEdges()
244
- const edgesMap = new Map<EdgeId, ComputedEdge>(edges.map(edge => [edge.id, edge]))
245
-
246
- this.linkNodesAndEdges(nodesMap, edges)
247
-
248
- // nodesMap sorted hierarchically,
249
- // but we need to keep the initial sort
250
- let initialSort = elements.map(e => nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`))
251
-
252
- if (this.groups.length > 0) {
253
- initialSort = concat(
254
- this.groups.map(g => nonNullable(nodesMap.get(g.id), `Node ${g.id} not found in nodesMap`)),
255
- initialSort
256
- )
257
- }
258
-
259
- const nodes = applyCustomElementProperties(
260
- rules,
261
- applyViewRuleStyles(
262
- rules,
263
- // Build graph and apply postorder sort
264
- sortNodes({
265
- nodes: initialSort,
266
- edges
267
- })
268
- )
269
- )
270
- const sortedEdges = new Set([
271
- ...nodes.flatMap(n => n.children.length === 0 ? n.outEdges.flatMap(id => edgesMap.get(id) ?? []) : []),
272
- ...edges
273
- ])
274
-
275
- const autoLayoutRule = this.view.rules.findLast(isViewRuleAutoLayout)
276
-
277
- const elementNotations = buildElementNotations(nodes)
278
-
279
- return calcViewLayoutHash({
280
- ...view,
281
- autoLayout: {
282
- direction: autoLayoutRule?.direction ?? 'TB',
283
- ...(autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep }),
284
- ...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep })
285
- },
286
- nodes: map(nodes, n => {
287
- // omit notation
288
- delete n.notation
289
- if (n.icon === 'none') {
290
- delete n.icon
291
- }
292
- return n
293
- }),
294
- edges: applyCustomRelationProperties(rules, nodes, sortedEdges),
295
- ...(elementNotations.length > 0 && {
296
- notation: {
297
- elements: elementNotations
298
- }
299
- })
300
- })
301
- }
302
-
303
- protected computeEdges(): ComputedEdge[] {
304
- return this.ctxEdges.map((e): ComputedEdge => {
305
- invariant(hasAtLeast(e.relations, 1), 'Edge must have at least one relation')
306
- const relations = sort(e.relations, compareRelations)
307
- const source = e.source.id
308
- const target = e.target.id
309
-
310
- const edge: ComputedEdge = {
311
- id: `${source}:${target}` as EdgeId,
312
- parent: commonAncestor(source, target),
313
- source,
314
- target,
315
- label: null,
316
- relations: relations.map(r => r.id)
317
- }
318
-
319
- let relation: {
320
- // TODO refactor with type-fest
321
- title: string
322
- description?: string | undefined
323
- technology?: string | undefined
324
- kind?: RelationshipKind | undefined
325
- color?: Color | undefined
326
- line?: RelationshipLineType | undefined
327
- head?: RelationshipArrowType | undefined
328
- tail?: RelationshipArrowType | undefined
329
- tags?: NonEmptyArray<Tag>
330
- navigateTo?: ViewID | undefined
331
- } | undefined
332
- relation = only(relations) ?? pipe(
333
- relations,
334
- filter(r => r.source === source && r.target === target),
335
- only()
336
- )
337
-
338
- // This edge represents mutliple relations
339
- // We use label if only it is the same for all relations
340
- if (!relation) {
341
- const allprops = pipe(
342
- relations,
343
- reduce((acc, r) => {
344
- if (isTruthy(r.title) && !acc.title.includes(r.title)) {
345
- acc.title.push(r.title)
346
- }
347
- if (isTruthy(r.description) && !acc.description.includes(r.description)) {
348
- acc.description.push(r.description)
349
- }
350
- if (isTruthy(r.technology) && !acc.technology.includes(r.technology)) {
351
- acc.technology.push(r.technology)
352
- }
353
- if (isTruthy(r.kind) && !acc.kind.includes(r.kind)) {
354
- acc.kind.push(r.kind)
355
- }
356
- if (isTruthy(r.color) && !acc.color.includes(r.color)) {
357
- acc.color.push(r.color)
358
- }
359
- if (isTruthy(r.line) && !acc.line.includes(r.line)) {
360
- acc.line.push(r.line)
361
- }
362
- if (isTruthy(r.head) && !acc.head.includes(r.head)) {
363
- acc.head.push(r.head)
364
- }
365
- if (isTruthy(r.tail) && !acc.tail.includes(r.tail)) {
366
- acc.tail.push(r.tail)
367
- }
368
- if (isTruthy(r.navigateTo) && !acc.navigateTo.includes(r.navigateTo)) {
369
- acc.navigateTo.push(r.navigateTo)
370
- }
371
- return acc
372
- }, {
373
- title: [] as string[],
374
- description: [] as string[],
375
- technology: [] as string[],
376
- kind: [] as RelationshipKind[],
377
- head: [] as RelationshipArrowType[],
378
- tail: [] as RelationshipArrowType[],
379
- color: [] as Color[],
380
- line: [] as RelationshipLineType[],
381
- navigateTo: [] as ViewID[]
382
- })
383
- )
384
- relation = {
385
- title: only(allprops.title) ?? '[...]',
386
- description: only(allprops.description),
387
- technology: only(allprops.technology),
388
- kind: only(allprops.kind),
389
- head: only(allprops.head),
390
- tail: only(allprops.tail),
391
- color: only(allprops.color),
392
- line: only(allprops.line),
393
- navigateTo: only(allprops.navigateTo)
394
- }
395
- }
396
-
397
- const tags = uniqueTags(relations)
398
-
399
- return Object.assign(
400
- edge,
401
- this.getEdgeLabel(relation),
402
- isTruthy(relation.description) && { description: relation.description },
403
- isTruthy(relation.technology) && { technology: relation.technology },
404
- isTruthy(relation.kind) && { kind: relation.kind },
405
- relation.color && { color: relation.color },
406
- relation.line && { line: relation.line },
407
- relation.head && { head: relation.head },
408
- relation.tail && { tail: relation.tail },
409
- relation.navigateTo && { navigateTo: relation.navigateTo },
410
- tags && { tags }
411
- )
412
- })
413
- }
414
-
415
- protected linkNodesAndEdges(nodesMap: ReadonlyMap<Fqn, ComputedNode>, edges: ComputedEdge[]) {
416
- for (const edge of edges) {
417
- const source = nodesMap.get(edge.source)
418
- const target = nodesMap.get(edge.target)
419
- invariant(source, `Source node ${edge.source} not found`)
420
- invariant(target, `Target node ${edge.target} not found`)
421
- // These ancestors are reversed: from bottom to top
422
- const sourceAncestors = this.ancestorsOf(source, nodesMap)
423
- const targetAncestors = this.ancestorsOf(target, nodesMap)
424
-
425
- const edgeParent = last(
426
- commonHead(
427
- reverse(sourceAncestors),
428
- reverse(targetAncestors)
429
- )
430
- )
431
- edge.parent = edgeParent?.id ?? null
432
- source.outEdges.push(edge.id)
433
- target.inEdges.push(edge.id)
434
- // Process edge source ancestors
435
- for (const sourceAncestor of sourceAncestors) {
436
- if (sourceAncestor === edgeParent) {
437
- break
438
- }
439
- sourceAncestor.outEdges.push(edge.id)
440
- }
441
- // Process target hierarchy
442
- for (const targetAncestor of targetAncestors) {
443
- if (targetAncestor === edgeParent) {
444
- break
445
- }
446
- targetAncestor.inEdges.push(edge.id)
447
- }
448
- }
449
- }
450
-
451
- protected addEdges(edges: ComputeCtx.Edge[]) {
452
- const added = [] as ComputeCtx.Edge[]
453
- for (const e of edges) {
454
- if (!hasAtLeast(e.relations, 1)) {
455
- continue
456
- }
457
- const existing = this.ctxEdges.find(
458
- _e => _e.source.id === e.source.id && _e.target.id === e.target.id
459
- )
460
- if (existing) {
461
- existing.relations = unique([...existing.relations, ...e.relations])
462
- added.push(existing)
463
- continue
464
- }
465
- added.push(e)
466
- this.ctxEdges.push(e)
467
- }
468
- return added
469
- }
470
-
471
- /**
472
- * Add element explicitly
473
- * Included even without relationships
474
- */
475
- protected addElement(...el: Element[]) {
476
- for (const r of el) {
477
- if (!this.explicits.has(r)) {
478
- this.activeGroup.addElement(r)
479
- } else if (this.activeGroup !== this.__rootGroup) {
480
- this.groups.forEach(g => {
481
- g.implicits.delete(r)
482
- })
483
- this.activeGroup.addImplicit(r)
484
- }
485
- this.explicits.add(r)
486
- this.implicits.add(r)
487
- }
488
- }
489
-
490
- /**
491
- * Add element implicitly
492
- * Included if only has relationships
493
- */
494
- protected addImplicit(...el: Element[]) {
495
- for (const r of el) {
496
- this.implicits.add(r)
497
- // Remove implicits from other groups
498
- if (this.activeGroup !== this.__rootGroup) {
499
- this.groups.forEach(g => {
500
- g.implicits.delete(r)
501
- })
502
- }
503
- }
504
- this.activeGroup.addImplicit(...el)
505
- }
506
-
507
- protected excludeElement(...excludes: Element[]) {
508
- for (const el of excludes) {
509
- this.ctxEdges = this.ctxEdges.filter(e => e.source.id !== el.id && e.target.id !== el.id)
510
- this.explicits.delete(el)
511
- this.implicits.delete(el)
512
- }
513
- this.__rootGroup.excludeElement(...excludes)
514
- this.groups.forEach(g => {
515
- g.excludeElement(...excludes)
516
- })
517
- }
518
-
519
- protected excludeRelation(...relations: Relation[]) {
520
- if (relations.length === 0) {
521
- return
522
- }
523
- const excludedImplicits = new Set<Element>()
524
- const ctxEdges = pipe(
525
- this.ctxEdges,
526
- map(edge => {
527
- const edgerelations = edge.relations.filter(r => !relations.includes(r))
528
- if (edgerelations.length === 0) {
529
- excludedImplicits.add(edge.source)
530
- excludedImplicits.add(edge.target)
531
- return null
532
- }
533
- if (edgerelations.length !== edge.relations.length) {
534
- return {
535
- ...edge,
536
- relations: edgerelations
537
- }
538
- }
539
- return edge
540
- }),
541
- filter(isNonNull)
542
- )
543
- this.ctxEdges = ctxEdges
544
- const remaining = this.includedElements
545
- if (remaining.size === 0) {
546
- this.implicits.clear()
547
- this.__rootGroup.implicits.clear()
548
- this.groups.forEach(g => g.implicits.clear())
549
- return
550
- }
551
- for (const el of excludedImplicits) {
552
- if (!remaining.has(el)) {
553
- this.implicits.delete(el)
554
- this.__rootGroup.implicits.delete(el)
555
- this.groups.forEach(g => g.implicits.delete(el))
556
- }
557
- }
558
- }
559
-
560
- protected reset() {
561
- this.explicits.clear()
562
- this.implicits.clear()
563
- this.ctxEdges = []
564
- // Reset groups
565
- this.__rootGroup = NodesGroup.root()
566
- this.groups = []
567
- this.activeGroupStack = []
568
- }
569
-
570
- // Filter out edges if there are edges between descendants
571
- // i.e. remove implicit edges, derived from childs
572
- protected removeRedundantImplicitEdges() {
573
- const processedRelations = new Set<Relation>()
574
-
575
- // Returns relations, that are not processed/included
576
- const excludeProcessed = (relations: Relation[]) =>
577
- relations.reduce((acc, rel) => {
578
- if (!processedRelations.has(rel)) {
579
- acc.push(rel)
580
- processedRelations.add(rel)
581
- }
582
- return acc
583
- }, [] as Relation[])
584
-
585
- // Returns predicate
586
- const isNestedEdgeOf = (parent: ComputeCtx.Edge) => {
587
- const { source, target } = parent
588
- // Checks if edge is between descendants of source and target of the parent edge
589
- return (edge: ComputeCtx.Edge) => {
590
- const isSameSource = source.id === edge.source.id
591
- const isSameTarget = target.id === edge.target.id
592
- if (isSameSource && isSameTarget) {
593
- return true
594
- }
595
- const isSourceNested = isAncestor(source.id, edge.source.id)
596
- const isTargetNested = isAncestor(target.id, edge.target.id)
597
- return (
598
- (isSourceNested && isTargetNested)
599
- || (isSameSource && isTargetNested)
600
- || (isSameTarget && isSourceNested)
601
- )
602
- }
603
- }
604
-
605
- // Sort edges from bottom to top (i.e. from more specific edges to implicit or between ancestors)
606
- const edges = [...this.ctxEdges].sort(compareEdges).reverse()
607
- this.ctxEdges = edges.reduce((acc, e) => {
608
- const relations = excludeProcessed(e.relations)
609
- if (relations.length === 0) {
610
- return acc
611
- }
612
- // If there is an edge between descendants of current edge,
613
- // then we don't add this edge
614
- if (acc.length > 0 && acc.some(isNestedEdgeOf(e))) {
615
- return acc
616
- }
617
- acc.push({
618
- source: e.source,
619
- target: e.target,
620
- relations
621
- })
622
- return acc
623
- }, [] as ComputeCtx.Edge[])
624
- }
625
-
626
- protected cleanGroupElements() {
627
- const unprocessed = new Set<Element>(difference(
628
- [...this.includedElements],
629
- [...this.__rootGroup.explicits]
630
- ))
631
- // Step 1 - Process explicits
632
- for (const group of this.groups) {
633
- const explicits = [...group.explicits]
634
- group.explicits.clear()
635
- for (const el of explicits) {
636
- if (unprocessed.delete(el)) {
637
- group.explicits.add(el)
638
- }
639
- }
640
- }
641
- // Step 2 - Process implicits
642
- for (const group of this.groups) {
643
- for (const el of group.implicits) {
644
- if (unprocessed.delete(el)) {
645
- group.explicits.add(el)
646
- }
647
- }
648
- group.implicits.clear()
649
- }
650
- }
651
-
652
- protected processPredicates(viewRules: Array<ViewRulePredicate | ViewRuleGroup>): this {
653
- // eslint-disable-next-line @typescript-eslint/no-this-alias
654
- for (const rule of viewRules) {
655
- if (isViewRuleGroup(rule)) {
656
- const parent = first(this.activeGroupStack)
657
- const groupId = `@gr${this.groups.length + 1}` as NodeId
658
- const group = new NodesGroup(groupId, rule, parent?.id ?? null)
659
- this.groups.push(group)
660
- this.activeGroupStack.unshift(group)
661
- this.processPredicates(rule.groupRules)
662
- this.activeGroupStack.shift()
663
- continue
664
- }
665
- const isInclude = 'include' in rule
666
- const exprs = rule.include ?? rule.exclude
667
- for (const expr of exprs) {
668
- if (Expr.isElementPredicateExpr(expr)) {
669
- this.processElementPredicate(expr, isInclude)
670
- continue
671
- }
672
- if (Expr.isRelationPredicateExpr(expr)) {
673
- this.processRelationPredicate(expr, isInclude)
674
- continue
675
- }
676
- nonexhaustive(expr)
677
- }
678
- }
679
- return this
680
- }
681
-
682
- protected processElementPredicate(
683
- expr: ElementPredicateExpression,
684
- isInclude: boolean,
685
- where?: ElementPredicateFn
686
- ): this {
687
- if (Expr.isCustomElement(expr)) {
688
- if (isInclude) {
689
- this.processElementPredicate(expr.custom.expr, isInclude)
690
- }
691
- return this
692
- }
693
- if (Expr.isElementWhere(expr)) {
694
- const where = whereOperatorAsPredicate(expr.where.condition)
695
- this.processElementPredicate(expr.where.expr, isInclude, where)
696
- return this
697
- }
698
- if (Expr.isExpandedElementExpr(expr)) {
699
- isInclude
700
- ? includeExpandedElementExpr.call(this, expr, where)
701
- : excludeExpandedElementExpr.call(this, expr, where)
702
- return this
703
- }
704
- if (Expr.isElementKindExpr(expr) || Expr.isElementTagExpr(expr)) {
705
- isInclude
706
- ? includeElementKindOrTag.call(this, expr, where)
707
- : excludeElementKindOrTag.call(this, expr, where)
708
- return this
709
- }
710
- if (Expr.isElementRef(expr)) {
711
- isInclude ? includeElementRef.call(this, expr, where) : excludeElementRef.call(this, expr, where)
712
- return this
713
- }
714
- if (Expr.isWildcard(expr)) {
715
- isInclude ? includeWildcardRef.call(this, expr, where) : excludeWildcardRef.call(this, expr, where)
716
- return this
717
- }
718
- nonexhaustive(expr)
719
- }
720
-
721
- protected processRelationPredicate(
722
- expr: RelationPredicateExpression,
723
- isInclude: boolean,
724
- where?: RelationPredicateFn
725
- ): this {
726
- if (Expr.isCustomRelationExpr(expr)) {
727
- if (isInclude) {
728
- this.processRelationPredicate(expr.customRelation.relation, isInclude)
729
- }
730
- return this
731
- }
732
- if (Expr.isRelationWhere(expr)) {
733
- const where = whereOperatorAsPredicate(expr.where.condition)
734
- this.processRelationPredicate(expr.where.expr, isInclude, where)
735
- return this
736
- }
737
- if (Expr.isIncoming(expr)) {
738
- isInclude ? includeIncomingExpr.call(this, expr, where) : excludeIncomingExpr.call(this, expr, where)
739
- return this
740
- }
741
- if (Expr.isOutgoing(expr)) {
742
- isInclude ? includeOutgoingExpr.call(this, expr, where) : excludeOutgoingExpr.call(this, expr, where)
743
- return this
744
- }
745
- if (Expr.isInOut(expr)) {
746
- isInclude ? includeInOutExpr.call(this, expr, where) : excludeInOutExpr.call(this, expr, where)
747
- return this
748
- }
749
- if (Expr.isRelation(expr)) {
750
- isInclude ? includeRelationExpr.call(this, expr, where) : excludeRelationExpr.call(this, expr, where)
751
- return this
752
- }
753
- nonexhaustive(expr)
754
- }
755
-
756
- private ancestorsOf(node: ComputedNode, nodesMap: ReadonlyMap<Fqn, ComputedNode>): ComputedNode[] {
757
- const ancestors = [] as ComputedNode[]
758
- let parent = node.parent
759
- while (parent) {
760
- const parentNode = nodesMap.get(parent)
761
- if (!parentNode) {
762
- break
763
- }
764
- ancestors.push(parentNode)
765
- parent = parentNode.parent
766
- }
767
- return ancestors
768
- }
769
-
770
- protected getEdgeLabel(
771
- relation: {
772
- title: string
773
- technology?: string | undefined
774
- }
775
- ) {
776
- const labelParts: string[] = []
777
-
778
- if (isTruthy(relation.title)) {
779
- labelParts.push(relation.title)
780
- }
781
-
782
- if (isTruthy(relation.technology)) {
783
- labelParts.push(`[${relation.technology}]`)
784
- }
785
-
786
- return labelParts.length > 0 ? { label: labelParts.join('\n') } : {}
787
- }
788
- }