@likec4/language-server 1.6.1 → 1.7.1
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
|
@@ -1,18 +1,35 @@
|
|
|
1
|
-
import type { Element } from '@likec4/core'
|
|
2
|
-
import { Expr,
|
|
3
|
-
import { isNullish as isNil } from 'remeda'
|
|
1
|
+
import type { Element, Relation } from '@likec4/core'
|
|
2
|
+
import { Expr, isAncestor, nonexhaustive, parentFqn } from '@likec4/core'
|
|
3
|
+
import { allPass, filter as remedaFilter, flatMap, isNullish as isNil, map, pipe } from 'remeda'
|
|
4
|
+
import { elementExprToPredicate } from '../utils/elementExpressionToPredicate'
|
|
4
5
|
import type { ComputeCtx } from './compute'
|
|
5
6
|
|
|
6
7
|
type Predicate<T> = (x: T) => boolean
|
|
7
|
-
|
|
8
|
-
export
|
|
8
|
+
export type ElementPredicateFn = Predicate<Element>
|
|
9
|
+
export type RelationPredicateFn = Predicate<Relation>
|
|
10
|
+
// // type ElementPredicate = Predicate<Element>
|
|
11
|
+
// type ElementPredicates<T> = T extends Element[] ? (x: T) => T : (T extends Element ? (x: T) => (Element | null) : never)
|
|
12
|
+
// type ElementPredicate = ElementPredicates<Element | Element[]>
|
|
13
|
+
|
|
14
|
+
const NoFilter: ElementPredicateFn = () => true
|
|
15
|
+
const Identity = <T>(x: T) => x
|
|
16
|
+
const filterBy = <T>(pred: Predicate<T>) => pred === NoFilter ? Identity : remedaFilter(pred)
|
|
17
|
+
const filterOne = <T>(pred: Predicate<T>) => pred === NoFilter ? Identity : (x: T) => pred(x) ? x : null
|
|
18
|
+
|
|
19
|
+
export function includeElementRef(this: ComputeCtx, expr: Expr.ElementRefExpr, where = NoFilter) {
|
|
9
20
|
// Get the elements that are already in the Ctx before any mutations
|
|
10
21
|
// Because we need to add edges between them and the new elements
|
|
11
22
|
const currentElements = [...this.resolvedElements]
|
|
23
|
+
const filter = filterBy(where)
|
|
12
24
|
|
|
13
|
-
const elements =
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
const elements = filter(
|
|
26
|
+
expr.isDescedants === true
|
|
27
|
+
? this.graph.childrenOrElement(expr.element)
|
|
28
|
+
: [this.graph.element(expr.element)]
|
|
29
|
+
)
|
|
30
|
+
if (elements.length === 0) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
16
33
|
|
|
17
34
|
this.addElement(...elements)
|
|
18
35
|
|
|
@@ -27,89 +44,84 @@ export function includeElementRef(this: ComputeCtx, expr: Expr.ElementRefExpr) {
|
|
|
27
44
|
}
|
|
28
45
|
}
|
|
29
46
|
|
|
30
|
-
export function excludeElementRef(this: ComputeCtx, expr: Expr.ElementRefExpr) {
|
|
31
|
-
const elements =
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
export function excludeElementRef(this: ComputeCtx, expr: Expr.ElementRefExpr, where = NoFilter) {
|
|
48
|
+
const elements = [...this.resolvedElements].filter(allPass([
|
|
49
|
+
elementExprToPredicate(expr),
|
|
50
|
+
where
|
|
51
|
+
]))
|
|
34
52
|
this.excludeElement(...elements)
|
|
35
53
|
}
|
|
36
54
|
|
|
37
|
-
export function
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (anyEdgesBetween.length > 0) {
|
|
52
|
-
const edges = this.graph.anyEdgesBetween(el, currentElements)
|
|
53
|
-
if (edges.length > 0) {
|
|
54
|
-
this.addEdges(edges)
|
|
55
|
-
expanded.push(el)
|
|
55
|
+
export function includeWildcardRef(this: ComputeCtx, _expr: Expr.WildcardExpr, where = NoFilter) {
|
|
56
|
+
const root = this.root
|
|
57
|
+
if (!root) {
|
|
58
|
+
// Take root elements
|
|
59
|
+
const elements = this.graph.rootElements.filter(where)
|
|
60
|
+
if (elements.length <= 0) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
const currentElements = [...this.resolvedElements]
|
|
64
|
+
this.addElement(...elements)
|
|
65
|
+
this.addEdges(this.graph.edgesWithin(elements))
|
|
66
|
+
if (currentElements.length > 0) {
|
|
67
|
+
for (const el of elements) {
|
|
68
|
+
this.addEdges(this.graph.anyEdgesBetween(el, currentElements))
|
|
56
69
|
}
|
|
57
70
|
}
|
|
71
|
+
return
|
|
58
72
|
}
|
|
59
|
-
if (expanded.length > 1) {
|
|
60
|
-
this.addEdges(this.graph.edgesWithin(expanded))
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
67
|
-
const currentElements = [...this.resolvedElements]
|
|
68
|
-
const _elRoot = this.graph.element(root)
|
|
74
|
+
const currentElements = [...this.resolvedElements]
|
|
75
|
+
const _elRoot = filterOne(where)(this.graph.element(root))
|
|
76
|
+
if (_elRoot) {
|
|
69
77
|
this.addElement(_elRoot)
|
|
78
|
+
}
|
|
79
|
+
const filter = filterBy(where)
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
const children = filter(this.graph.children(root))
|
|
82
|
+
const hasChildren = children.length > 0
|
|
83
|
+
if (hasChildren) {
|
|
84
|
+
this.addElement(...children)
|
|
85
|
+
this.addEdges(this.graph.edgesWithin(children))
|
|
86
|
+
} else if (_elRoot) {
|
|
87
|
+
children.push(_elRoot)
|
|
88
|
+
}
|
|
79
89
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
// All neighbours that may have relations with root or its children
|
|
91
|
+
const neighbours = [
|
|
92
|
+
...currentElements,
|
|
93
|
+
...filter([
|
|
83
94
|
...this.graph.siblings(root),
|
|
84
95
|
...this.graph.ancestors(root).flatMap(a => this.graph.siblings(a.id))
|
|
85
|
-
]
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
])
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
for (const el of children) {
|
|
100
|
+
this.addEdges(this.graph.anyEdgesBetween(el, neighbours))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// If root has no children
|
|
104
|
+
if (!hasChildren && _elRoot) {
|
|
105
|
+
// Any edges with siblings?
|
|
106
|
+
const edgesWithSiblings = this.graph.anyEdgesBetween(_elRoot, this.graph.siblings(root))
|
|
107
|
+
if (edgesWithSiblings.length === 0) {
|
|
108
|
+
// If no edges with siblings, i.e. root is orphan
|
|
109
|
+
// Lets add parent for better view
|
|
110
|
+
const _parentId = parentFqn(root)
|
|
111
|
+
const parent = _parentId && this.graph.element(_parentId)
|
|
112
|
+
if (parent && where(parent)) {
|
|
113
|
+
this.addElement(parent)
|
|
103
114
|
}
|
|
104
115
|
}
|
|
105
|
-
} else {
|
|
106
|
-
// Take root elements
|
|
107
|
-
this.addElement(...this.graph.rootElements)
|
|
108
|
-
this.addEdges(this.graph.edgesWithin(this.graph.rootElements))
|
|
109
116
|
}
|
|
110
117
|
}
|
|
111
118
|
|
|
112
|
-
export function excludeWildcardRef(this: ComputeCtx, _expr: Expr.WildcardExpr) {
|
|
119
|
+
export function excludeWildcardRef(this: ComputeCtx, _expr: Expr.WildcardExpr, where = NoFilter) {
|
|
120
|
+
if (where !== NoFilter) {
|
|
121
|
+
const elements = [...this.resolvedElements].filter(where)
|
|
122
|
+
this.excludeElement(...elements)
|
|
123
|
+
return
|
|
124
|
+
}
|
|
113
125
|
const root = this.root
|
|
114
126
|
if (root) {
|
|
115
127
|
this.excludeElement(
|
|
@@ -117,15 +129,56 @@ export function excludeWildcardRef(this: ComputeCtx, _expr: Expr.WildcardExpr) {
|
|
|
117
129
|
...this.graph.children(root)
|
|
118
130
|
)
|
|
119
131
|
this.excludeRelation(
|
|
120
|
-
...this.graph.
|
|
121
|
-
...this.graph.incoming(root),
|
|
122
|
-
...this.graph.outgoing(root)
|
|
132
|
+
...this.graph.connectedRelations(root)
|
|
123
133
|
)
|
|
124
134
|
} else {
|
|
125
135
|
this.reset()
|
|
126
136
|
}
|
|
127
137
|
}
|
|
128
138
|
|
|
139
|
+
export function includeExpandedElementExpr(
|
|
140
|
+
this: ComputeCtx,
|
|
141
|
+
expr: Expr.ExpandedElementExpr,
|
|
142
|
+
where = NoFilter
|
|
143
|
+
) {
|
|
144
|
+
const filter = filterBy(where)
|
|
145
|
+
const currentElements = [...this.resolvedElements]
|
|
146
|
+
|
|
147
|
+
// Always add parent
|
|
148
|
+
const parent = this.graph.element(expr.expanded)
|
|
149
|
+
if (where(parent)) {
|
|
150
|
+
this.addElement(parent)
|
|
151
|
+
const anyEdgesBetween = this.graph.anyEdgesBetween(parent, currentElements)
|
|
152
|
+
this.addEdges(anyEdgesBetween)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const expanded = [] as Element[]
|
|
156
|
+
|
|
157
|
+
for (const el of filter(this.graph.children(expr.expanded))) {
|
|
158
|
+
this.addImplicit(el)
|
|
159
|
+
const edges = this.graph.anyEdgesBetween(el, currentElements)
|
|
160
|
+
if (edges.length > 0) {
|
|
161
|
+
this.addEdges(edges)
|
|
162
|
+
expanded.push(el)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (expanded.length > 1) {
|
|
166
|
+
this.addEdges(this.graph.edgesWithin(expanded))
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function excludeExpandedElementExpr(
|
|
171
|
+
this: ComputeCtx,
|
|
172
|
+
expr: Expr.ExpandedElementExpr,
|
|
173
|
+
where = NoFilter
|
|
174
|
+
) {
|
|
175
|
+
const elements = [...this.resolvedElements].filter(allPass([
|
|
176
|
+
elementExprToPredicate(expr),
|
|
177
|
+
where
|
|
178
|
+
]))
|
|
179
|
+
this.excludeElement(...elements)
|
|
180
|
+
}
|
|
181
|
+
|
|
129
182
|
const asElementPredicate = (
|
|
130
183
|
expr: Expr.ElementKindExpr | Expr.ElementTagExpr
|
|
131
184
|
): Predicate<Element> => {
|
|
@@ -145,9 +198,10 @@ const asElementPredicate = (
|
|
|
145
198
|
}
|
|
146
199
|
export function includeElementKindOrTag(
|
|
147
200
|
this: ComputeCtx,
|
|
148
|
-
expr: Expr.ElementKindExpr | Expr.ElementTagExpr
|
|
201
|
+
expr: Expr.ElementKindExpr | Expr.ElementTagExpr,
|
|
202
|
+
where = NoFilter
|
|
149
203
|
) {
|
|
150
|
-
const elements = this.graph.elements.filter(asElementPredicate(expr))
|
|
204
|
+
const elements = this.graph.elements.filter(asElementPredicate(expr)).filter(where)
|
|
151
205
|
if (elements.length > 0) {
|
|
152
206
|
const currentElements = [...this.resolvedElements]
|
|
153
207
|
this.addElement(...elements)
|
|
@@ -160,9 +214,10 @@ export function includeElementKindOrTag(
|
|
|
160
214
|
|
|
161
215
|
export function excludeElementKindOrTag(
|
|
162
216
|
this: ComputeCtx,
|
|
163
|
-
expr: Expr.ElementKindExpr | Expr.ElementTagExpr
|
|
217
|
+
expr: Expr.ElementKindExpr | Expr.ElementTagExpr,
|
|
218
|
+
where = NoFilter
|
|
164
219
|
) {
|
|
165
|
-
const elements = this.
|
|
220
|
+
const elements = [...this.resolvedElements].filter(asElementPredicate(expr)).filter(where)
|
|
166
221
|
if (elements.length > 0) {
|
|
167
222
|
this.excludeElement(...elements)
|
|
168
223
|
}
|
|
@@ -236,21 +291,54 @@ function edgesIncomingExpr(this: ComputeCtx, expr: Expr.ElementExpression) {
|
|
|
236
291
|
if (targets.length === 0) {
|
|
237
292
|
return []
|
|
238
293
|
}
|
|
239
|
-
|
|
240
|
-
if (
|
|
241
|
-
|
|
294
|
+
let sources = [...this.resolvedElements]
|
|
295
|
+
if (Expr.isElementRef(expr) || Expr.isExpandedElementExpr(expr)) {
|
|
296
|
+
const exprElement = expr.element ?? expr.expanded
|
|
297
|
+
const isDescedants = expr.isDescedants ?? false
|
|
298
|
+
sources = sources.filter(el =>
|
|
299
|
+
// allow elements, that are not nested or are direct children
|
|
300
|
+
!isAncestor(exprElement, el.id) || (isDescedants && parentFqn(el.id) === exprElement)
|
|
301
|
+
)
|
|
242
302
|
}
|
|
243
|
-
|
|
303
|
+
if (sources.length === 0) {
|
|
304
|
+
sources = resolveNeighbours.call(this, expr)
|
|
305
|
+
}
|
|
306
|
+
return this.graph.edgesBetween(sources, targets)
|
|
244
307
|
}
|
|
245
308
|
|
|
246
|
-
|
|
247
|
-
|
|
309
|
+
const filterEdges = (edges: ComputeCtx.Edge[], where?: RelationPredicateFn) => {
|
|
310
|
+
if (!where) {
|
|
311
|
+
return edges
|
|
312
|
+
}
|
|
313
|
+
return pipe(
|
|
314
|
+
edges,
|
|
315
|
+
map(e => ({ ...e, relations: e.relations.filter(where) })),
|
|
316
|
+
remedaFilter(e => e.relations.length > 0)
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const filterRelations = (edges: ComputeCtx.Edge[], where?: RelationPredicateFn) => {
|
|
321
|
+
if (!where) {
|
|
322
|
+
return edges.flatMap(e => e.relations)
|
|
323
|
+
}
|
|
324
|
+
return pipe(
|
|
325
|
+
edges,
|
|
326
|
+
flatMap(e => e.relations),
|
|
327
|
+
remedaFilter(where)
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function includeIncomingExpr(this: ComputeCtx, expr: Expr.IncomingExpr, where?: RelationPredicateFn) {
|
|
332
|
+
const edges = filterEdges(edgesIncomingExpr.call(this, expr.incoming), where)
|
|
333
|
+
if (edges.length === 0) {
|
|
334
|
+
return
|
|
335
|
+
}
|
|
248
336
|
this.addEdges(edges)
|
|
249
337
|
this.addImplicit(...edges.map(e => e.target))
|
|
250
338
|
}
|
|
251
|
-
export function excludeIncomingExpr(this: ComputeCtx, expr: Expr.IncomingExpr) {
|
|
252
|
-
|
|
253
|
-
this.excludeRelation(...
|
|
339
|
+
export function excludeIncomingExpr(this: ComputeCtx, expr: Expr.IncomingExpr, where?: RelationPredicateFn) {
|
|
340
|
+
let relations = filterRelations(edgesIncomingExpr.call(this, expr.incoming), where)
|
|
341
|
+
this.excludeRelation(...relations)
|
|
254
342
|
}
|
|
255
343
|
|
|
256
344
|
// --------------------------------
|
|
@@ -269,21 +357,32 @@ function edgesOutgoingExpr(this: ComputeCtx, expr: Expr.ElementExpression) {
|
|
|
269
357
|
if (sources.length === 0) {
|
|
270
358
|
return []
|
|
271
359
|
}
|
|
272
|
-
|
|
360
|
+
let targets = [...this.resolvedElements]
|
|
361
|
+
if (Expr.isElementRef(expr) || Expr.isExpandedElementExpr(expr)) {
|
|
362
|
+
const sourceElement = expr.element ?? expr.expanded
|
|
363
|
+
const isDescedants = expr.isDescedants ?? false
|
|
364
|
+
targets = targets.filter(el =>
|
|
365
|
+
// allow elements, that are not nested or are direct children
|
|
366
|
+
!isAncestor(sourceElement, el.id) || (isDescedants && parentFqn(el.id) === sourceElement)
|
|
367
|
+
)
|
|
368
|
+
}
|
|
273
369
|
if (targets.length === 0) {
|
|
274
|
-
targets
|
|
370
|
+
targets = resolveNeighbours.call(this, expr)
|
|
275
371
|
}
|
|
276
372
|
return this.graph.edgesBetween(sources, targets)
|
|
277
373
|
}
|
|
278
374
|
|
|
279
|
-
export function includeOutgoingExpr(this: ComputeCtx, expr: Expr.OutgoingExpr) {
|
|
280
|
-
const edges = edgesOutgoingExpr.call(this, expr.outgoing)
|
|
375
|
+
export function includeOutgoingExpr(this: ComputeCtx, expr: Expr.OutgoingExpr, where?: RelationPredicateFn) {
|
|
376
|
+
const edges = filterEdges(edgesOutgoingExpr.call(this, expr.outgoing), where)
|
|
377
|
+
if (edges.length === 0) {
|
|
378
|
+
return
|
|
379
|
+
}
|
|
281
380
|
this.addEdges(edges)
|
|
282
381
|
this.addImplicit(...edges.map(e => e.source))
|
|
283
382
|
}
|
|
284
|
-
export function excludeOutgoingExpr(this: ComputeCtx, expr: Expr.OutgoingExpr) {
|
|
285
|
-
const
|
|
286
|
-
this.excludeRelation(...
|
|
383
|
+
export function excludeOutgoingExpr(this: ComputeCtx, expr: Expr.OutgoingExpr, where?: RelationPredicateFn) {
|
|
384
|
+
const relations = filterRelations(edgesOutgoingExpr.call(this, expr.outgoing), where)
|
|
385
|
+
this.excludeRelation(...relations)
|
|
287
386
|
}
|
|
288
387
|
|
|
289
388
|
// --------------------------------
|
|
@@ -299,27 +398,36 @@ namespace EdgePredicateResult {
|
|
|
299
398
|
edges: []
|
|
300
399
|
}
|
|
301
400
|
}
|
|
302
|
-
function edgesInOutExpr(this: ComputeCtx,
|
|
303
|
-
if (Expr.isWildcard(
|
|
401
|
+
function edgesInOutExpr(this: ComputeCtx, { inout }: Expr.InOutExpr, where?: RelationPredicateFn): EdgePredicateResult {
|
|
402
|
+
if (Expr.isWildcard(inout)) {
|
|
304
403
|
if (!this.root) {
|
|
305
404
|
return EdgePredicateResult.Empty
|
|
306
405
|
}
|
|
307
406
|
const neighbours = this.graph.ascendingSiblings(this.root)
|
|
308
407
|
return {
|
|
309
|
-
edges: this.graph.anyEdgesBetween(this.graph.element(this.root), neighbours),
|
|
408
|
+
edges: filterEdges(this.graph.anyEdgesBetween(this.graph.element(this.root), neighbours), where),
|
|
310
409
|
implicits: []
|
|
311
410
|
}
|
|
312
411
|
}
|
|
313
|
-
const elements = resolveElements.call(this,
|
|
412
|
+
const elements = resolveElements.call(this, inout)
|
|
314
413
|
if (elements.length === 0) {
|
|
315
414
|
return EdgePredicateResult.Empty
|
|
316
415
|
}
|
|
317
|
-
|
|
416
|
+
let currentElements = [...this.resolvedElements]
|
|
417
|
+
|
|
418
|
+
if (Expr.isElementRef(inout) || Expr.isExpandedElementExpr(inout)) {
|
|
419
|
+
const exprElement = inout.element ?? inout.expanded
|
|
420
|
+
const isDescedants = inout.isDescedants ?? false
|
|
421
|
+
currentElements = currentElements.filter(el =>
|
|
422
|
+
// allow elements, that are not nested or are direct children
|
|
423
|
+
!isAncestor(exprElement, el.id) || (isDescedants && parentFqn(el.id) === exprElement)
|
|
424
|
+
)
|
|
425
|
+
}
|
|
318
426
|
if (currentElements.length === 0) {
|
|
319
|
-
currentElements
|
|
427
|
+
currentElements = resolveNeighbours.call(this, inout)
|
|
320
428
|
}
|
|
321
429
|
return elements.reduce<EdgePredicateResult>((acc, el) => {
|
|
322
|
-
const edges = this.graph.anyEdgesBetween(el, currentElements)
|
|
430
|
+
const edges = filterEdges(this.graph.anyEdgesBetween(el, currentElements), where)
|
|
323
431
|
if (edges.length > 0) {
|
|
324
432
|
acc.implicits.push(el)
|
|
325
433
|
acc.edges.push(...edges)
|
|
@@ -328,13 +436,13 @@ function edgesInOutExpr(this: ComputeCtx, expr: Expr.InOutExpr): EdgePredicateRe
|
|
|
328
436
|
}, { implicits: [], edges: [] })
|
|
329
437
|
}
|
|
330
438
|
|
|
331
|
-
export function includeInOutExpr(this: ComputeCtx, expr: Expr.InOutExpr) {
|
|
332
|
-
const { implicits, edges } = edgesInOutExpr.call(this, expr)
|
|
439
|
+
export function includeInOutExpr(this: ComputeCtx, expr: Expr.InOutExpr, where?: RelationPredicateFn) {
|
|
440
|
+
const { implicits, edges } = edgesInOutExpr.call(this, expr, where)
|
|
333
441
|
this.addEdges(edges)
|
|
334
442
|
this.addImplicit(...implicits)
|
|
335
443
|
}
|
|
336
|
-
export function excludeInOutExpr(this: ComputeCtx, expr: Expr.InOutExpr) {
|
|
337
|
-
const { edges } = edgesInOutExpr.call(this, expr)
|
|
444
|
+
export function excludeInOutExpr(this: ComputeCtx, expr: Expr.InOutExpr, where?: RelationPredicateFn) {
|
|
445
|
+
const { edges } = edgesInOutExpr.call(this, expr, where)
|
|
338
446
|
this.excludeRelation(...edges.flatMap(e => e.relations))
|
|
339
447
|
}
|
|
340
448
|
|
|
@@ -356,10 +464,13 @@ function resolveRelationExprElements(this: ComputeCtx, expr: Expr.ElementExpress
|
|
|
356
464
|
if (Expr.isElementRef(expr) && this.root === expr.element && expr.isDescedants !== true) {
|
|
357
465
|
return [...this.graph.children(expr.element), this.graph.element(expr.element)]
|
|
358
466
|
}
|
|
467
|
+
if (Expr.isExpandedElementExpr(expr) && this.root === expr.expanded) {
|
|
468
|
+
return [...this.graph.children(expr.expanded), this.graph.element(expr.expanded)]
|
|
469
|
+
}
|
|
359
470
|
return resolveElements.call(this, expr)
|
|
360
471
|
}
|
|
361
472
|
|
|
362
|
-
export function includeRelationExpr(this: ComputeCtx, expr: Expr.RelationExpr) {
|
|
473
|
+
export function includeRelationExpr(this: ComputeCtx, expr: Expr.RelationExpr, where?: RelationPredicateFn) {
|
|
363
474
|
let sources, targets
|
|
364
475
|
if (Expr.isWildcard(expr.source) && !Expr.isWildcard(expr.target)) {
|
|
365
476
|
sources = resolveNeighbours.call(this, expr.target)
|
|
@@ -375,35 +486,20 @@ export function includeRelationExpr(this: ComputeCtx, expr: Expr.RelationExpr) {
|
|
|
375
486
|
if (expr.isBidirectional === true) {
|
|
376
487
|
edges.push(...this.graph.edgesBetween(targets, sources))
|
|
377
488
|
}
|
|
378
|
-
this.addEdges(edges)
|
|
489
|
+
this.addEdges(filterEdges(edges, where))
|
|
379
490
|
}
|
|
380
491
|
|
|
381
|
-
export function excludeRelationExpr(this: ComputeCtx, expr: Expr.RelationExpr) {
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
492
|
+
export function excludeRelationExpr(this: ComputeCtx, expr: Expr.RelationExpr, where?: RelationPredicateFn) {
|
|
493
|
+
const isSource = elementExprToPredicate(expr.source)
|
|
494
|
+
const isTarget = elementExprToPredicate(expr.target)
|
|
495
|
+
const satisfies = (edge: ComputeCtx.Edge) => {
|
|
496
|
+
let result = isSource(edge.source) && isTarget(edge.target)
|
|
497
|
+
if (!result && expr.isBidirectional) {
|
|
498
|
+
result = isSource(edge.target) && isTarget(edge.source)
|
|
499
|
+
}
|
|
500
|
+
return result
|
|
387
501
|
}
|
|
388
|
-
|
|
389
|
-
const relations =
|
|
502
|
+
const edges = this.edges.filter(satisfies)
|
|
503
|
+
const relations = filterRelations(edges, where)
|
|
390
504
|
this.excludeRelation(...relations)
|
|
391
505
|
}
|
|
392
|
-
|
|
393
|
-
export function includeCustomElement(this: ComputeCtx, expr: Expr.CustomElementExpr) {
|
|
394
|
-
// Get the elements that are already in the Ctx before any mutations
|
|
395
|
-
// Because we need to add edges between them and the new elements
|
|
396
|
-
const currentElements = [...this.resolvedElements]
|
|
397
|
-
|
|
398
|
-
const el = this.graph.element(expr.custom.element)
|
|
399
|
-
|
|
400
|
-
this.addElement(el)
|
|
401
|
-
|
|
402
|
-
if (currentElements.length > 0) {
|
|
403
|
-
this.addEdges(this.graph.anyEdgesBetween(el, currentElements))
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
export function includeCustomRelation(this: ComputeCtx, expr: Expr.CustomRelationExpr) {
|
|
408
|
-
includeRelationExpr.call(this, expr.customRelation.relation)
|
|
409
|
-
}
|
|
@@ -14,13 +14,16 @@ const emptyView = {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
type StepExpr = `${FakeElementIds} ${'->' | '<-'} ${FakeElementIds}`
|
|
17
|
+
type StepProps = Omit<DynamicViewStep, 'source' | 'target' | 'isBackward'>
|
|
17
18
|
|
|
18
|
-
export function $step(expr: StepExpr,
|
|
19
|
+
export function $step(expr: StepExpr, props?: string | Partial<StepProps>): DynamicViewStep {
|
|
20
|
+
const title = typeof props === 'string' ? props : props?.title
|
|
19
21
|
if (expr.includes(' -> ')) {
|
|
20
22
|
const [source, target] = expr.split(' -> ')
|
|
21
23
|
return {
|
|
22
24
|
source: source as Fqn,
|
|
23
25
|
target: target as Fqn,
|
|
26
|
+
...(typeof props === 'object' ? props : {}),
|
|
24
27
|
title: title ?? null
|
|
25
28
|
}
|
|
26
29
|
}
|
|
@@ -29,6 +32,7 @@ export function $step(expr: StepExpr, title?: string): DynamicViewStep {
|
|
|
29
32
|
return {
|
|
30
33
|
source: source as Fqn,
|
|
31
34
|
target: target as Fqn,
|
|
35
|
+
...(typeof props === 'object' ? props : {}),
|
|
32
36
|
title: title ?? null,
|
|
33
37
|
isBackward: true
|
|
34
38
|
}
|