@likec4/language-server 1.8.1 → 1.10.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 +21 -0
- package/dist/browser.d.cts +22 -0
- package/dist/browser.d.mts +22 -0
- package/dist/browser.d.ts +22 -0
- package/dist/browser.mjs +19 -0
- package/dist/index.cjs +10 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.mjs +1 -0
- package/dist/likec4lib.cjs +961 -0
- package/dist/likec4lib.d.cts +6 -0
- package/dist/likec4lib.d.mts +6 -0
- package/dist/likec4lib.d.ts +6 -0
- package/dist/likec4lib.mjs +957 -0
- package/dist/model-graph/index.cjs +10 -0
- package/dist/model-graph/index.d.cts +79 -0
- package/dist/model-graph/index.d.mts +79 -0
- package/dist/model-graph/index.d.ts +79 -0
- package/dist/model-graph/index.mjs +1 -0
- package/dist/node.cjs +18 -0
- package/dist/node.d.cts +20 -0
- package/dist/node.d.mts +20 -0
- package/dist/node.d.ts +20 -0
- package/dist/node.mjs +16 -0
- package/dist/protocol.cjs +25 -0
- package/dist/protocol.d.cts +43 -0
- package/dist/protocol.d.mts +43 -0
- package/dist/protocol.d.ts +43 -0
- package/dist/protocol.mjs +17 -0
- package/dist/shared/language-server.CjFzaJwI.d.cts +1223 -0
- package/dist/shared/language-server.CtKHXJDD.d.ts +1223 -0
- package/dist/shared/language-server.D-84I33F.d.mts +1223 -0
- package/dist/shared/language-server.DBJJUUgF.mjs +5737 -0
- package/dist/shared/language-server.DtBRb9os.mjs +1656 -0
- package/dist/shared/language-server.DwyCJvXm.cjs +1669 -0
- package/dist/shared/language-server.JWkqVjGv.cjs +5748 -0
- package/package.json +36 -20
- package/src/ast.ts +48 -36
- package/src/browser.ts +0 -3
- package/src/elementRef.ts +1 -1
- package/src/formatting/LikeC4Formatter.ts +388 -0
- package/src/formatting/utils.ts +26 -0
- package/src/generated/ast.ts +170 -12
- package/src/generated/grammar.ts +1 -1
- package/src/generated-lib/icons.ts +1 -1
- package/src/like-c4.langium +49 -8
- package/src/likec4lib.ts +2 -3
- package/src/logger.ts +9 -1
- package/src/lsp/DocumentLinkProvider.ts +27 -15
- package/src/lsp/RenameProvider.ts +8 -0
- package/src/lsp/SemanticTokenProvider.ts +20 -2
- package/src/lsp/index.ts +1 -0
- package/src/model/fqn-computation.ts +33 -23
- package/src/model/fqn-index.ts +5 -21
- package/src/model/model-builder.ts +180 -112
- package/src/model/model-locator.ts +1 -1
- package/src/model/model-parser-where.ts +3 -2
- package/src/model/model-parser.ts +99 -39
- package/src/model-graph/LikeC4ModelGraph.ts +42 -21
- package/src/model-graph/compute-view/__test__/fixture.ts +16 -14
- package/src/model-graph/compute-view/compute.ts +110 -81
- package/src/model-graph/compute-view/predicates.ts +6 -8
- package/src/model-graph/dynamic-view/__test__/fixture.ts +1 -0
- package/src/model-graph/dynamic-view/compute.ts +98 -61
- package/src/model-graph/utils/buildElementNotations.ts +1 -1
- package/src/model-graph/utils/elementExpressionToPredicate.ts +1 -1
- package/src/model-graph/utils/sortNodes.ts +2 -6
- package/src/module.ts +21 -4
- package/src/protocol.ts +4 -5
- package/src/references/scope-computation.ts +10 -1
- package/src/references/scope-provider.ts +2 -1
- package/src/shared/NodeKindProvider.ts +73 -34
- package/src/test/setup.ts +3 -8
- package/src/test/testServices.ts +27 -7
- package/src/utils/graphlib.ts +11 -0
- package/src/validation/index.ts +2 -1
- package/src/validation/property-checks.ts +13 -1
- package/src/validation/specification.ts +3 -3
- package/src/view-utils/manual-layout.ts +1 -1
- package/src/view-utils/resolve-extended-views.ts +19 -10
- package/src/view-utils/resolve-relative-paths.ts +19 -24
- package/src/view-utils/view-hash.ts +1 -1
- package/src/reset.d.ts +0 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
Color,
|
|
2
3
|
ComputedEdge,
|
|
3
4
|
ComputedElementView,
|
|
4
5
|
EdgeId,
|
|
@@ -12,7 +13,7 @@ import type {
|
|
|
12
13
|
RelationshipKind,
|
|
13
14
|
RelationshipLineType,
|
|
14
15
|
Tag,
|
|
15
|
-
|
|
16
|
+
ViewID,
|
|
16
17
|
ViewRulePredicate
|
|
17
18
|
} from '@likec4/core'
|
|
18
19
|
import {
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
parentFqn,
|
|
30
31
|
whereOperatorAsPredicate
|
|
31
32
|
} from '@likec4/core'
|
|
32
|
-
import {
|
|
33
|
+
import { filter, flatMap, hasAtLeast, isNonNull, isTruthy, map, omit, only, pipe, reduce, sort, unique } from 'remeda'
|
|
33
34
|
import { calcViewLayoutHash } from '../../view-utils/view-hash'
|
|
34
35
|
import type { LikeC4ModelGraph } from '../LikeC4ModelGraph'
|
|
35
36
|
import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
|
|
@@ -191,7 +192,7 @@ export class ComputeCtx {
|
|
|
191
192
|
protected computeEdges(): ComputedEdge[] {
|
|
192
193
|
return this.ctxEdges.map((e): ComputedEdge => {
|
|
193
194
|
invariant(hasAtLeast(e.relations, 1), 'Edge must have at least one relation')
|
|
194
|
-
const relations = e.relations
|
|
195
|
+
const relations = sort(e.relations, compareRelations)
|
|
195
196
|
const source = e.source.id
|
|
196
197
|
const target = e.target.id
|
|
197
198
|
|
|
@@ -204,67 +205,78 @@ export class ComputeCtx {
|
|
|
204
205
|
relations: relations.map(r => r.id)
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
let relation:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
| undefined
|
|
222
|
-
if (relations.length === 1) {
|
|
223
|
-
relation = relations[0]
|
|
224
|
-
} else {
|
|
225
|
-
relation = relations.find(r => r.source === source && r.target === target)
|
|
226
|
-
// relation ??= relations.toReversed().find(r => r.source === source || r.target === target)
|
|
227
|
-
}
|
|
208
|
+
let relation: {
|
|
209
|
+
// TODO refactor with type-fest
|
|
210
|
+
title: string
|
|
211
|
+
description?: string | undefined
|
|
212
|
+
technology?: string | undefined
|
|
213
|
+
kind?: RelationshipKind | undefined
|
|
214
|
+
color?: Color | undefined
|
|
215
|
+
line?: RelationshipLineType | undefined
|
|
216
|
+
head?: RelationshipArrowType | undefined
|
|
217
|
+
tail?: RelationshipArrowType | undefined
|
|
218
|
+
tags?: NonEmptyArray<Tag>
|
|
219
|
+
navigateTo?: ViewID | undefined
|
|
220
|
+
} | undefined
|
|
221
|
+
relation = relations.length === 1 ? relations[0] : relations.find(r => r.source === source && r.target === target)
|
|
228
222
|
|
|
229
223
|
// This edge represents mutliple relations
|
|
230
224
|
// We use label if only it is the same for all relations
|
|
231
225
|
if (!relation) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
226
|
+
const allprops = pipe(
|
|
227
|
+
relations,
|
|
228
|
+
reduce((acc, r) => {
|
|
229
|
+
if (isTruthy(r.title) && !acc.title.includes(r.title)) {
|
|
230
|
+
acc.title.push(r.title)
|
|
231
|
+
}
|
|
232
|
+
if (isTruthy(r.description) && !acc.description.includes(r.description)) {
|
|
233
|
+
acc.description.push(r.description)
|
|
234
|
+
}
|
|
235
|
+
if (isTruthy(r.technology) && !acc.technology.includes(r.technology)) {
|
|
236
|
+
acc.technology.push(r.technology)
|
|
237
|
+
}
|
|
238
|
+
if (isTruthy(r.kind) && !acc.kind.includes(r.kind)) {
|
|
239
|
+
acc.kind.push(r.kind)
|
|
240
|
+
}
|
|
241
|
+
if (isTruthy(r.color) && !acc.color.includes(r.color)) {
|
|
242
|
+
acc.color.push(r.color)
|
|
243
|
+
}
|
|
244
|
+
if (isTruthy(r.line) && !acc.line.includes(r.line)) {
|
|
245
|
+
acc.line.push(r.line)
|
|
246
|
+
}
|
|
247
|
+
if (isTruthy(r.head) && !acc.head.includes(r.head)) {
|
|
248
|
+
acc.head.push(r.head)
|
|
249
|
+
}
|
|
250
|
+
if (isTruthy(r.tail) && !acc.tail.includes(r.tail)) {
|
|
251
|
+
acc.tail.push(r.tail)
|
|
252
|
+
}
|
|
253
|
+
if (isTruthy(r.navigateTo) && !acc.navigateTo.includes(r.navigateTo)) {
|
|
254
|
+
acc.navigateTo.push(r.navigateTo)
|
|
255
|
+
}
|
|
256
|
+
return acc
|
|
257
|
+
}, {
|
|
258
|
+
title: [] as string[],
|
|
259
|
+
description: [] as string[],
|
|
260
|
+
technology: [] as string[],
|
|
261
|
+
kind: [] as RelationshipKind[],
|
|
262
|
+
head: [] as RelationshipArrowType[],
|
|
263
|
+
tail: [] as RelationshipArrowType[],
|
|
264
|
+
color: [] as Color[],
|
|
265
|
+
line: [] as RelationshipLineType[],
|
|
266
|
+
navigateTo: [] as ViewID[]
|
|
267
|
+
})
|
|
268
|
+
)
|
|
269
|
+
relation = {
|
|
270
|
+
title: only(allprops.title) ?? '[...]',
|
|
271
|
+
description: only(allprops.description),
|
|
272
|
+
technology: only(allprops.technology),
|
|
273
|
+
kind: only(allprops.kind),
|
|
274
|
+
head: only(allprops.head),
|
|
275
|
+
tail: only(allprops.tail),
|
|
276
|
+
color: only(allprops.color),
|
|
277
|
+
line: only(allprops.line),
|
|
278
|
+
navigateTo: only(allprops.navigateTo)
|
|
279
|
+
}
|
|
268
280
|
}
|
|
269
281
|
|
|
270
282
|
const tags = unique(flatMap(relations, r => r.tags ?? []))
|
|
@@ -273,12 +285,13 @@ export class ComputeCtx {
|
|
|
273
285
|
edge,
|
|
274
286
|
this.getEdgeLabel(relation),
|
|
275
287
|
isTruthy(relation.description) && { description: relation.description },
|
|
276
|
-
isTruthy(relation.technology) && {
|
|
288
|
+
isTruthy(relation.technology) && { technology: relation.technology },
|
|
277
289
|
isTruthy(relation.kind) && { kind: relation.kind },
|
|
278
290
|
relation.color && { color: relation.color },
|
|
279
291
|
relation.line && { line: relation.line },
|
|
280
292
|
relation.head && { head: relation.head },
|
|
281
293
|
relation.tail && { tail: relation.tail },
|
|
294
|
+
relation.navigateTo && { navigateTo: relation.navigateTo },
|
|
282
295
|
hasAtLeast(tags, 1) && { tags }
|
|
283
296
|
)
|
|
284
297
|
})
|
|
@@ -348,29 +361,40 @@ export class ComputeCtx {
|
|
|
348
361
|
}
|
|
349
362
|
}
|
|
350
363
|
|
|
351
|
-
protected excludeImplicit(...excludes: Element[]) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
364
|
+
// protected excludeImplicit(...excludes: Element[]) {
|
|
365
|
+
// for (const el of excludes) {
|
|
366
|
+
// this.implicits.delete(el)
|
|
367
|
+
// }
|
|
368
|
+
// }
|
|
356
369
|
|
|
357
370
|
protected excludeRelation(...relations: Relation[]) {
|
|
371
|
+
if (relations.length === 0) {
|
|
372
|
+
return
|
|
373
|
+
}
|
|
358
374
|
const excludedImplicits = new Set<Element>()
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
375
|
+
const ctxEdges = pipe(
|
|
376
|
+
this.ctxEdges,
|
|
377
|
+
map(edge => {
|
|
378
|
+
const edgerelations = edge.relations.filter(r => !relations.includes(r))
|
|
379
|
+
if (edgerelations.length === 0) {
|
|
363
380
|
excludedImplicits.add(edge.source)
|
|
364
381
|
excludedImplicits.add(edge.target)
|
|
365
|
-
|
|
366
|
-
continue
|
|
382
|
+
return null
|
|
367
383
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
384
|
+
if (edgerelations.length !== edge.relations.length) {
|
|
385
|
+
return {
|
|
386
|
+
...edge,
|
|
387
|
+
relations: edgerelations
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return edge
|
|
391
|
+
}),
|
|
392
|
+
filter(isNonNull)
|
|
393
|
+
)
|
|
371
394
|
if (excludedImplicits.size === 0) {
|
|
372
395
|
return
|
|
373
396
|
}
|
|
397
|
+
this.ctxEdges = ctxEdges
|
|
374
398
|
const remaining = this.includedElements
|
|
375
399
|
if (remaining.size === 0) {
|
|
376
400
|
this.implicits.clear()
|
|
@@ -537,19 +561,24 @@ export class ComputeCtx {
|
|
|
537
561
|
return this
|
|
538
562
|
}
|
|
539
563
|
nonexhaustive(expr)
|
|
540
|
-
}
|
|
564
|
+
}
|
|
541
565
|
|
|
542
|
-
protected getEdgeLabel(
|
|
543
|
-
|
|
566
|
+
protected getEdgeLabel(
|
|
567
|
+
relation: {
|
|
568
|
+
title: string
|
|
569
|
+
technology?: string | undefined
|
|
570
|
+
}
|
|
571
|
+
) {
|
|
572
|
+
const labelParts: string[] = []
|
|
544
573
|
|
|
545
|
-
if(isTruthy(relation.title)) {
|
|
574
|
+
if (isTruthy(relation.title)) {
|
|
546
575
|
labelParts.push(relation.title)
|
|
547
576
|
}
|
|
548
577
|
|
|
549
|
-
if(isTruthy(relation.technology)) {
|
|
578
|
+
if (isTruthy(relation.technology)) {
|
|
550
579
|
labelParts.push(`[${relation.technology}]`)
|
|
551
580
|
}
|
|
552
581
|
|
|
553
|
-
return labelParts.length > 0
|
|
582
|
+
return labelParts.length > 0 ? { label: labelParts.join('\n') } : {}
|
|
554
583
|
}
|
|
555
584
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Element, Relation } from '@likec4/core'
|
|
2
2
|
import { Expr, isAncestor, nonexhaustive, parentFqn } from '@likec4/core'
|
|
3
|
-
import { allPass, filter as remedaFilter, flatMap, isNullish as isNil, map, pipe } from 'remeda'
|
|
3
|
+
import { allPass, filter as remedaFilter, flatMap, isNullish as isNil, map, pipe, unique } from 'remeda'
|
|
4
4
|
import { elementExprToPredicate } from '../utils/elementExpressionToPredicate'
|
|
5
5
|
import type { ComputeCtx } from './compute'
|
|
6
6
|
|
|
@@ -306,25 +306,23 @@ function edgesIncomingExpr(this: ComputeCtx, expr: Expr.ElementExpression) {
|
|
|
306
306
|
return this.graph.edgesBetween(sources, targets)
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
const filterEdges = (edges: ComputeCtx.Edge
|
|
309
|
+
const filterEdges = (edges: ReadonlyArray<ComputeCtx.Edge>, where?: RelationPredicateFn) => {
|
|
310
310
|
if (!where) {
|
|
311
|
-
return edges
|
|
311
|
+
return edges as ComputeCtx.Edge[]
|
|
312
312
|
}
|
|
313
313
|
return pipe(
|
|
314
314
|
edges,
|
|
315
315
|
map(e => ({ ...e, relations: e.relations.filter(where) })),
|
|
316
316
|
remedaFilter(e => e.relations.length > 0)
|
|
317
|
-
)
|
|
317
|
+
) as ComputeCtx.Edge[]
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
const filterRelations = (edges: ComputeCtx.Edge[], where?: RelationPredicateFn) => {
|
|
321
|
-
if (!where) {
|
|
322
|
-
return edges.flatMap(e => e.relations)
|
|
323
|
-
}
|
|
324
321
|
return pipe(
|
|
325
322
|
edges,
|
|
326
323
|
flatMap(e => e.relations),
|
|
327
|
-
remedaFilter(where)
|
|
324
|
+
where ? remedaFilter(where) : Identity,
|
|
325
|
+
unique()
|
|
328
326
|
)
|
|
329
327
|
}
|
|
330
328
|
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
Color,
|
|
2
3
|
ComputedDynamicView,
|
|
3
4
|
ComputedEdge,
|
|
4
5
|
DynamicView,
|
|
6
|
+
DynamicViewStep,
|
|
5
7
|
Element,
|
|
6
8
|
NonEmptyArray,
|
|
7
9
|
RelationID,
|
|
8
10
|
RelationshipArrowType,
|
|
9
11
|
RelationshipLineType,
|
|
10
12
|
Tag,
|
|
11
|
-
|
|
13
|
+
ViewID
|
|
12
14
|
} from '@likec4/core'
|
|
13
15
|
import {
|
|
14
16
|
ancestorsFqn,
|
|
@@ -17,12 +19,13 @@ import {
|
|
|
17
19
|
DefaultLineStyle,
|
|
18
20
|
DefaultRelationshipColor,
|
|
19
21
|
isDynamicViewIncludeRule,
|
|
22
|
+
isDynamicViewParallelSteps,
|
|
20
23
|
isViewRuleAutoLayout,
|
|
21
24
|
nonNullable,
|
|
22
25
|
parentFqn,
|
|
23
26
|
StepEdgeId
|
|
24
27
|
} from '@likec4/core'
|
|
25
|
-
import { hasAtLeast, isTruthy, map, omit, unique } from 'remeda'
|
|
28
|
+
import { filter, flatMap, hasAtLeast, isTruthy, map, omit, only, pipe, unique } from 'remeda'
|
|
26
29
|
import { calcViewLayoutHash } from '../../view-utils/view-hash'
|
|
27
30
|
import type { LikeC4ModelGraph } from '../LikeC4ModelGraph'
|
|
28
31
|
import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
|
|
@@ -33,17 +36,19 @@ import { elementExprToPredicate } from '../utils/elementExpressionToPredicate'
|
|
|
33
36
|
|
|
34
37
|
export namespace DynamicViewComputeCtx {
|
|
35
38
|
export interface Step {
|
|
39
|
+
id: StepEdgeId
|
|
36
40
|
source: Element
|
|
37
41
|
target: Element
|
|
38
42
|
title: string | null
|
|
39
43
|
description?: string
|
|
40
44
|
technology?: string
|
|
41
|
-
color?:
|
|
45
|
+
color?: Color
|
|
42
46
|
line?: RelationshipLineType
|
|
43
47
|
head?: RelationshipArrowType
|
|
44
48
|
tail?: RelationshipArrowType
|
|
45
49
|
relations: RelationID[]
|
|
46
50
|
isBackward: boolean
|
|
51
|
+
navigateTo?: ViewID
|
|
47
52
|
tags?: NonEmptyArray<Tag>
|
|
48
53
|
}
|
|
49
54
|
}
|
|
@@ -62,6 +67,47 @@ export class DynamicViewComputeCtx {
|
|
|
62
67
|
protected graph: LikeC4ModelGraph
|
|
63
68
|
) {}
|
|
64
69
|
|
|
70
|
+
private addStep(
|
|
71
|
+
{
|
|
72
|
+
source: stepSource,
|
|
73
|
+
target: stepTarget,
|
|
74
|
+
title: stepTitle,
|
|
75
|
+
isBackward,
|
|
76
|
+
navigateTo: stepNavigateTo,
|
|
77
|
+
...step
|
|
78
|
+
}: DynamicViewStep,
|
|
79
|
+
index: number,
|
|
80
|
+
parent?: number
|
|
81
|
+
) {
|
|
82
|
+
const id = parent ? StepEdgeId(parent, index) : StepEdgeId(index)
|
|
83
|
+
const source = this.graph.element(stepSource)
|
|
84
|
+
const target = this.graph.element(stepTarget)
|
|
85
|
+
|
|
86
|
+
this.explicits.add(source)
|
|
87
|
+
this.explicits.add(target)
|
|
88
|
+
|
|
89
|
+
const {
|
|
90
|
+
title,
|
|
91
|
+
relations,
|
|
92
|
+
tags,
|
|
93
|
+
navigateTo: derivedNavigateTo
|
|
94
|
+
} = this.findRelations(source, target)
|
|
95
|
+
|
|
96
|
+
const navigateTo = isTruthy(stepNavigateTo) && stepNavigateTo !== this.view.id ? stepNavigateTo : derivedNavigateTo
|
|
97
|
+
|
|
98
|
+
this.steps.push({
|
|
99
|
+
id,
|
|
100
|
+
...step,
|
|
101
|
+
source,
|
|
102
|
+
target,
|
|
103
|
+
title: stepTitle ?? title,
|
|
104
|
+
relations: relations ?? [],
|
|
105
|
+
isBackward: isBackward ?? false,
|
|
106
|
+
...(navigateTo ? { navigateTo } : {}),
|
|
107
|
+
...(tags ? { tags } : {})
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
65
111
|
protected compute(): ComputedDynamicView {
|
|
66
112
|
const {
|
|
67
113
|
docUri: _docUri, // exclude docUri
|
|
@@ -70,32 +116,21 @@ export class DynamicViewComputeCtx {
|
|
|
70
116
|
...view
|
|
71
117
|
} = this.view
|
|
72
118
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const { title, relations, tags } = this.findRelations(source, target)
|
|
89
|
-
|
|
90
|
-
this.steps.push({
|
|
91
|
-
...step,
|
|
92
|
-
source,
|
|
93
|
-
target,
|
|
94
|
-
title: isTruthy(stepTitle) ? stepTitle : title,
|
|
95
|
-
relations: relations ?? [],
|
|
96
|
-
isBackward: isBackward ?? false,
|
|
97
|
-
...(tags ? { tags } : {})
|
|
98
|
-
})
|
|
119
|
+
let stepNum = 1
|
|
120
|
+
for (const step of viewSteps) {
|
|
121
|
+
if (isDynamicViewParallelSteps(step)) {
|
|
122
|
+
if (step.__parallel.length === 0) {
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
if (step.__parallel.length === 1) {
|
|
126
|
+
this.addStep(step.__parallel[0]!, stepNum)
|
|
127
|
+
} else {
|
|
128
|
+
step.__parallel.forEach((s, i) => this.addStep(s, i + 1, stepNum))
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
this.addStep(step, stepNum)
|
|
132
|
+
}
|
|
133
|
+
stepNum++
|
|
99
134
|
}
|
|
100
135
|
|
|
101
136
|
for (const rule of rules) {
|
|
@@ -110,12 +145,10 @@ export class DynamicViewComputeCtx {
|
|
|
110
145
|
const elements = [...this.explicits]
|
|
111
146
|
const nodesMap = buildComputeNodes(elements)
|
|
112
147
|
|
|
113
|
-
const edges = this.steps.map(({ source, target, relations, title, isBackward, ...step }
|
|
148
|
+
const edges = this.steps.map(({ source, target, relations, title, isBackward, ...step }) => {
|
|
114
149
|
const sourceNode = nonNullable(nodesMap.get(source.id), `Source node ${source.id} not found`)
|
|
115
150
|
const targetNode = nonNullable(nodesMap.get(target.id), `Target node ${target.id} not found`)
|
|
116
|
-
const stepNum = index + 1
|
|
117
151
|
const edge: ComputedEdge = {
|
|
118
|
-
id: StepEdgeId(stepNum),
|
|
119
152
|
parent: commonAncestor(source.id, target.id),
|
|
120
153
|
source: source.id,
|
|
121
154
|
target: target.id,
|
|
@@ -182,49 +215,53 @@ export class DynamicViewComputeCtx {
|
|
|
182
215
|
title: string | null
|
|
183
216
|
tags: NonEmptyArray<Tag> | null
|
|
184
217
|
relations: NonEmptyArray<RelationID> | null
|
|
218
|
+
navigateTo: ViewID | null
|
|
185
219
|
} {
|
|
186
220
|
const relationships = unique(this.graph.edgesBetween(source, target).flatMap(e => e.relations))
|
|
187
|
-
const alltags = unique(relationships.flatMap(r => r.tags ?? []))
|
|
188
|
-
const tags = hasAtLeast(alltags, 1) ? alltags : null
|
|
189
|
-
|
|
190
|
-
const relations = hasAtLeast(relationships, 1) ? map(relationships, r => r.id) : null
|
|
191
221
|
if (relationships.length === 0) {
|
|
192
222
|
return {
|
|
193
223
|
title: null,
|
|
194
|
-
tags,
|
|
195
|
-
relations
|
|
224
|
+
tags: null,
|
|
225
|
+
relations: null,
|
|
226
|
+
navigateTo: null
|
|
196
227
|
}
|
|
197
228
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
229
|
+
const alltags = pipe(
|
|
230
|
+
relationships,
|
|
231
|
+
flatMap(r => r.tags),
|
|
232
|
+
filter(isTruthy),
|
|
233
|
+
unique()
|
|
234
|
+
)
|
|
235
|
+
const tags = hasAtLeast(alltags, 1) ? alltags : null
|
|
236
|
+
const relations = hasAtLeast(relationships, 1) ? map(relationships, r => r.id) : null
|
|
204
237
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
title: relation.title,
|
|
208
|
-
tags,
|
|
209
|
-
relations
|
|
210
|
-
}
|
|
211
|
-
}
|
|
238
|
+
// Most closest relation
|
|
239
|
+
const relation = only(relationships) || relationships.find(r => r.source === source.id && r.target === target.id)
|
|
212
240
|
|
|
213
241
|
// This edge represents mutliple relations
|
|
214
242
|
// We use label if only it is the same for all relations
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
243
|
+
const title = isTruthy(relation?.title) ? relation.title : pipe(
|
|
244
|
+
relationships,
|
|
245
|
+
map(r => r.title),
|
|
246
|
+
filter(isTruthy),
|
|
247
|
+
unique(),
|
|
248
|
+
only()
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
const navigateTo = !!relation?.navigateTo && relation.navigateTo !== this.view.id ? relation.navigateTo : pipe(
|
|
252
|
+
relationships,
|
|
253
|
+
map(r => r.navigateTo),
|
|
254
|
+
filter(isTruthy),
|
|
255
|
+
filter(v => v !== this.view.id),
|
|
256
|
+
unique(),
|
|
257
|
+
only()
|
|
258
|
+
)
|
|
223
259
|
|
|
224
260
|
return {
|
|
225
|
-
title: null,
|
|
261
|
+
title: title ?? null,
|
|
226
262
|
tags,
|
|
227
|
-
relations
|
|
263
|
+
relations,
|
|
264
|
+
navigateTo: navigateTo ?? null
|
|
228
265
|
}
|
|
229
266
|
}
|
|
230
267
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Expr, nonexhaustive, parentFqn } from '@likec4/core'
|
|
2
|
-
import { type Element, whereOperatorAsPredicate } from '@likec4/core
|
|
2
|
+
import { type Element, whereOperatorAsPredicate } from '@likec4/core'
|
|
3
3
|
import { isNullish } from 'remeda'
|
|
4
4
|
|
|
5
5
|
type Predicate<T> = (x: T) => boolean
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import pkg from '@dagrejs/graphlib'
|
|
2
1
|
import {
|
|
3
2
|
compareByFqnHierarchically,
|
|
4
3
|
compareRelations,
|
|
@@ -10,10 +9,7 @@ import {
|
|
|
10
9
|
nonNullable
|
|
11
10
|
} from '@likec4/core'
|
|
12
11
|
import { difference, filter, map, pipe, sort, take } from 'remeda'
|
|
13
|
-
|
|
14
|
-
// '@dagrejs/graphlib' is a CommonJS module
|
|
15
|
-
// Here is a workaround to import it
|
|
16
|
-
const { Graph, alg } = pkg
|
|
12
|
+
import { Graph, postorder } from '../../utils/graphlib'
|
|
17
13
|
|
|
18
14
|
// side effect
|
|
19
15
|
function sortChildren(nodes: readonly ComputedNode[]) {
|
|
@@ -92,7 +88,7 @@ export function sortNodes({
|
|
|
92
88
|
map(n => n.id)
|
|
93
89
|
)
|
|
94
90
|
}
|
|
95
|
-
const orderedIds =
|
|
91
|
+
const orderedIds = postorder(g, sources).reverse() as Fqn[]
|
|
96
92
|
const sorted = orderedIds.map(getNode)
|
|
97
93
|
if (sorted.length < nodes.length) {
|
|
98
94
|
const unsorted = difference(nodes, sorted)
|