@likec4/language-server 1.14.0 → 1.15.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/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/likec4lib.cjs +0 -1
- package/dist/likec4lib.mjs +0 -1
- package/dist/model-graph/index.cjs +1 -1
- package/dist/model-graph/index.mjs +1 -1
- package/dist/shared/{language-server.CbDa016p.cjs → language-server.B8s2wfT_.cjs} +279 -68
- package/dist/shared/{language-server.DFLaUdYu.mjs → language-server.BT4WTbFI.mjs} +281 -70
- package/dist/shared/{language-server.DZYziEuF.mjs → language-server.BuChFlda.mjs} +231 -44
- package/dist/shared/{language-server.CO6aEDQm.d.mts → language-server.DfMwkd2l.d.mts} +46 -15
- package/dist/shared/{language-server.CITj0XN3.cjs → language-server.DfjkvknB.cjs} +230 -43
- package/dist/shared/{language-server.Cqyh6-9S.d.cts → language-server.U2piOAVt.d.cts} +46 -15
- package/dist/shared/{language-server.BI99piRy.d.ts → language-server.j-ShR6as.d.ts} +46 -15
- package/package.json +10 -10
- package/src/ast.ts +1 -0
- package/src/formatting/LikeC4Formatter.ts +44 -4
- package/src/generated/ast.ts +103 -19
- package/src/generated/grammar.ts +1 -1
- package/src/generated-lib/icons.ts +0 -1
- package/src/like-c4.langium +32 -9
- package/src/lsp/CompletionProvider.ts +70 -2
- package/src/lsp/SemanticTokenProvider.ts +3 -1
- package/src/model/model-builder.ts +13 -7
- package/src/model/model-parser.ts +71 -20
- package/src/model-graph/compute-view/__test__/fixture.ts +96 -29
- package/src/model-graph/compute-view/compute.ts +223 -40
- package/src/model-graph/compute-view/predicates.ts +12 -6
- package/src/model-graph/utils/applyCustomElementProperties.ts +18 -4
- package/src/model-graph/utils/applyCustomRelationProperties.ts +9 -39
- package/src/model-graph/utils/applyViewRuleStyles.ts +30 -25
- package/src/model-graph/utils/buildComputeNodes.ts +69 -17
- package/src/model-graph/utils/elementExpressionToPredicate.ts +3 -1
- package/src/model-graph/utils/relationExpressionToPredicates.ts +43 -0
- package/src/model-graph/utils/sortNodes.ts +11 -7
- package/src/references/scope-computation.ts +3 -2
- package/src/validation/index.ts +2 -2
- package/src/validation/specification.ts +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, isViewRuleStyle, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant,
|
|
2
|
-
import { pipe, map, pick, mapToObj, isTruthy, isNullish, omitBy, isEmpty, filter, anyPass, isDefined, groupBy, prop, mapValues, piped, unique, entries, flatMap, sortBy, sort, difference, allPass, omit, hasAtLeast as hasAtLeast$1, only, reduce, isNonNull, isString, isArray } from 'remeda';
|
|
1
|
+
import { Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, ComputedNode, isViewRuleGroup, isViewRulePredicate, isViewRuleStyle, nonNullable, ElementKind, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant, compareNatural, hasAtLeast, isAncestor, commonHead, isViewRuleAutoLayout, isScopedElementView, commonAncestor, StepEdgeId, isDynamicViewParallelSteps, isDynamicViewIncludeRule, DefaultRelationshipColor, DefaultLineStyle, DefaultArrowType, ancestorsFqn, isSameHierarchy } from '@likec4/core';
|
|
2
|
+
import { pipe, map, pick, mapToObj, isTruthy, isNullish, omitBy, isEmpty, filter, isNot, anyPass, forEach, isDefined, groupBy, prop, mapValues, piped, unique, entries, flatMap, sortBy, sort, tap, difference, allPass, last, reverse, concat, omit, hasAtLeast as hasAtLeast$1, only, reduce, isNonNull, first, isString, isArray } from 'remeda';
|
|
3
3
|
import dagre from '@dagrejs/dagre';
|
|
4
4
|
import objectHash from 'object-hash';
|
|
5
5
|
|
|
@@ -54,8 +54,19 @@ function elementExprToPredicate(target) {
|
|
|
54
54
|
nonexhaustive(target);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
function flattenGroupRules(guard) {
|
|
58
|
+
return (rule) => {
|
|
59
|
+
if (isViewRuleGroup(rule)) {
|
|
60
|
+
return rule.groupRules.flatMap(flattenGroupRules(guard));
|
|
61
|
+
}
|
|
62
|
+
if (isViewRulePredicate(rule)) {
|
|
63
|
+
return "include" in rule ? rule.include.filter(guard) : [];
|
|
64
|
+
}
|
|
65
|
+
return [];
|
|
66
|
+
};
|
|
67
|
+
}
|
|
57
68
|
function applyCustomElementProperties(_rules, _nodes) {
|
|
58
|
-
const rules = _rules.flatMap((
|
|
69
|
+
const rules = _rules.flatMap(flattenGroupRules(Expr.isCustomElement));
|
|
59
70
|
if (rules.length === 0) {
|
|
60
71
|
return _nodes;
|
|
61
72
|
}
|
|
@@ -67,7 +78,7 @@ function applyCustomElementProperties(_rules, _nodes) {
|
|
|
67
78
|
const notEmpty = !isEmpty(rest);
|
|
68
79
|
const satisfies = elementExprToPredicate(expr);
|
|
69
80
|
nodes.forEach((node, i) => {
|
|
70
|
-
if (!satisfies(node)) {
|
|
81
|
+
if (ComputedNode.isNodesGroup(node) || !satisfies(node)) {
|
|
71
82
|
return;
|
|
72
83
|
}
|
|
73
84
|
if (notEmpty) {
|
|
@@ -103,7 +114,9 @@ function applyCustomElementProperties(_rules, _nodes) {
|
|
|
103
114
|
function relationExpressionToPredicates(expr) {
|
|
104
115
|
switch (true) {
|
|
105
116
|
case Expr.isRelationWhere(expr):
|
|
106
|
-
|
|
117
|
+
const predicate = relationExpressionToPredicates(expr.where.expr);
|
|
118
|
+
const where = whereOperatorAsPredicate(expr.where.condition);
|
|
119
|
+
return (e) => predicate(e) && where(e);
|
|
107
120
|
case Expr.isRelation(expr): {
|
|
108
121
|
const isSource = elementExprToPredicate(expr.source);
|
|
109
122
|
const isTarget = elementExprToPredicate(expr.target);
|
|
@@ -127,8 +140,9 @@ function relationExpressionToPredicates(expr) {
|
|
|
127
140
|
nonexhaustive(expr);
|
|
128
141
|
}
|
|
129
142
|
}
|
|
143
|
+
|
|
130
144
|
function applyCustomRelationProperties(_rules, nodes, _edges) {
|
|
131
|
-
const rules = _rules.flatMap((
|
|
145
|
+
const rules = _rules.flatMap(flattenGroupRules(Expr.isCustomRelationExpr));
|
|
132
146
|
const edges = Array.from(_edges);
|
|
133
147
|
if (rules.length === 0 || edges.length === 0) {
|
|
134
148
|
return edges;
|
|
@@ -144,12 +158,12 @@ function applyCustomRelationProperties(_rules, nodes, _edges) {
|
|
|
144
158
|
if (!source || !target) {
|
|
145
159
|
return;
|
|
146
160
|
}
|
|
147
|
-
if (satisfies({ source, target })) {
|
|
161
|
+
if (satisfies({ source, target, ...pick(edge, ["kind", "tags"]) })) {
|
|
148
162
|
edges[i] = {
|
|
149
163
|
...edge,
|
|
164
|
+
...props,
|
|
150
165
|
label: title ?? edge.label,
|
|
151
|
-
isCustomized: true
|
|
152
|
-
...props
|
|
166
|
+
isCustomized: true
|
|
153
167
|
};
|
|
154
168
|
}
|
|
155
169
|
});
|
|
@@ -171,29 +185,34 @@ function applyViewRuleStyles(_rules, nodes) {
|
|
|
171
185
|
}
|
|
172
186
|
predicates.push(elementExprToPredicate(target));
|
|
173
187
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
...
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
188
|
+
pipe(
|
|
189
|
+
nodes,
|
|
190
|
+
filter(isNot(ComputedNode.isNodesGroup)),
|
|
191
|
+
filter(anyPass(predicates)),
|
|
192
|
+
forEach((n) => {
|
|
193
|
+
n.shape = rule.style.shape ?? n.shape;
|
|
194
|
+
n.color = rule.style.color ?? n.color;
|
|
195
|
+
if (isDefined(rule.style.icon)) {
|
|
196
|
+
n.icon = rule.style.icon;
|
|
197
|
+
}
|
|
198
|
+
if (isDefined(rule.notation)) {
|
|
199
|
+
n.notation = rule.notation;
|
|
200
|
+
}
|
|
201
|
+
let styleOverride;
|
|
202
|
+
if (isDefined(rule.style.border)) {
|
|
203
|
+
styleOverride = { border: rule.style.border };
|
|
204
|
+
}
|
|
205
|
+
if (isDefined(rule.style.opacity)) {
|
|
206
|
+
styleOverride = { ...styleOverride, opacity: rule.style.opacity };
|
|
207
|
+
}
|
|
208
|
+
if (styleOverride) {
|
|
209
|
+
n.style = {
|
|
210
|
+
...n.style,
|
|
211
|
+
...styleOverride
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
);
|
|
197
216
|
}
|
|
198
217
|
return nodes;
|
|
199
218
|
}
|
|
@@ -209,40 +228,80 @@ function updateDepthOfAncestors(node, nodes) {
|
|
|
209
228
|
node = parentNd;
|
|
210
229
|
}
|
|
211
230
|
}
|
|
212
|
-
function buildComputeNodes(elements) {
|
|
213
|
-
|
|
231
|
+
function buildComputeNodes(elements, groups) {
|
|
232
|
+
const nodesMap = /* @__PURE__ */ new Map();
|
|
233
|
+
const elementToGroup = /* @__PURE__ */ new Map();
|
|
234
|
+
groups?.forEach(({ id, parent, viewRule, explicits }) => {
|
|
235
|
+
if (parent) {
|
|
236
|
+
nonNullable(nodesMap.get(parent), `Parent group node ${parent} not found`).children.push(id);
|
|
237
|
+
}
|
|
238
|
+
nodesMap.set(id, {
|
|
239
|
+
id,
|
|
240
|
+
parent,
|
|
241
|
+
kind: ElementKind.Group,
|
|
242
|
+
title: viewRule.title ?? "",
|
|
243
|
+
color: viewRule.color ?? "muted",
|
|
244
|
+
shape: "rectangle",
|
|
245
|
+
children: [],
|
|
246
|
+
inEdges: [],
|
|
247
|
+
outEdges: [],
|
|
248
|
+
level: 0,
|
|
249
|
+
depth: 0,
|
|
250
|
+
description: null,
|
|
251
|
+
technology: null,
|
|
252
|
+
tags: null,
|
|
253
|
+
links: null,
|
|
254
|
+
style: {
|
|
255
|
+
border: viewRule.border ?? "dashed",
|
|
256
|
+
opacity: viewRule.opacity ?? 0
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
for (const e of explicits) {
|
|
260
|
+
elementToGroup.set(e.id, id);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
return Array.from(elements).sort(compareByFqnHierarchically).reduce((map, { id, color, shape, style, kind, title, ...el }) => {
|
|
214
264
|
let parent = parentFqn(id);
|
|
215
265
|
let level = 0;
|
|
266
|
+
let parentNd;
|
|
216
267
|
while (parent) {
|
|
217
|
-
|
|
268
|
+
parentNd = map.get(parent);
|
|
218
269
|
if (parentNd) {
|
|
219
|
-
if (parentNd.children.length == 0) {
|
|
220
|
-
parentNd.depth = 1;
|
|
221
|
-
updateDepthOfAncestors(parentNd, map);
|
|
222
|
-
}
|
|
223
|
-
parentNd.children.push(id);
|
|
224
|
-
level = parentNd.level + 1;
|
|
225
270
|
break;
|
|
226
271
|
}
|
|
227
272
|
parent = parentFqn(parent);
|
|
228
273
|
}
|
|
274
|
+
if (!parentNd && elementToGroup.has(id)) {
|
|
275
|
+
parent = elementToGroup.get(id);
|
|
276
|
+
parentNd = map.get(parent);
|
|
277
|
+
}
|
|
278
|
+
if (parentNd) {
|
|
279
|
+
if (parentNd.children.length == 0) {
|
|
280
|
+
parentNd.depth = 1;
|
|
281
|
+
updateDepthOfAncestors(parentNd, map);
|
|
282
|
+
}
|
|
283
|
+
parentNd.children.push(id);
|
|
284
|
+
level = parentNd.level + 1;
|
|
285
|
+
}
|
|
229
286
|
const node = {
|
|
230
|
-
...el,
|
|
231
287
|
id,
|
|
232
288
|
parent,
|
|
233
|
-
|
|
289
|
+
kind,
|
|
290
|
+
title,
|
|
234
291
|
color: color ?? DefaultThemeColor,
|
|
235
292
|
shape: shape ?? DefaultElementShape,
|
|
236
293
|
children: [],
|
|
237
294
|
inEdges: [],
|
|
238
295
|
outEdges: [],
|
|
296
|
+
level,
|
|
297
|
+
...el,
|
|
239
298
|
style: {
|
|
240
299
|
...style
|
|
241
300
|
}
|
|
242
301
|
};
|
|
243
302
|
map.set(id, node);
|
|
244
303
|
return map;
|
|
245
|
-
},
|
|
304
|
+
}, nodesMap);
|
|
246
305
|
}
|
|
247
306
|
|
|
248
307
|
function buildElementNotations(nodes) {
|
|
@@ -314,9 +373,16 @@ function sortNodes({
|
|
|
314
373
|
nodes,
|
|
315
374
|
edges
|
|
316
375
|
}) {
|
|
317
|
-
if (
|
|
376
|
+
if (nodes.length < 2) {
|
|
318
377
|
return nodes;
|
|
319
378
|
}
|
|
379
|
+
if (edges.length === 0) {
|
|
380
|
+
return pipe(
|
|
381
|
+
nodes,
|
|
382
|
+
sort(compareByFqnHierarchically),
|
|
383
|
+
tap(sortChildren)
|
|
384
|
+
);
|
|
385
|
+
}
|
|
320
386
|
const g = new Graph({
|
|
321
387
|
compound: false,
|
|
322
388
|
directed: true,
|
|
@@ -351,15 +417,15 @@ function sortNodes({
|
|
|
351
417
|
if (sources.length === 0) {
|
|
352
418
|
sources = pipe(
|
|
353
419
|
nodes,
|
|
354
|
-
sort(compareByFqnHierarchically),
|
|
355
420
|
filter((n) => n.inEdges.length === 0 || n.parent === null),
|
|
421
|
+
sort(compareByFqnHierarchically),
|
|
356
422
|
map((n) => n.id)
|
|
357
423
|
);
|
|
358
424
|
}
|
|
359
425
|
const orderedIds = postorder(g, sources).reverse();
|
|
360
426
|
const sorted = orderedIds.map(getNode);
|
|
361
427
|
if (sorted.length < nodes.length) {
|
|
362
|
-
const unsorted = difference(nodes, sorted);
|
|
428
|
+
const unsorted = difference(nodes, sorted).sort(compareByFqnHierarchically);
|
|
363
429
|
sorted.push(...unsorted);
|
|
364
430
|
}
|
|
365
431
|
invariant(sorted.length === nodes.length, "Not all nodes were processed by graphlib");
|
|
@@ -446,7 +512,9 @@ function includeWildcardRef(_expr, where = NoFilter) {
|
|
|
446
512
|
])
|
|
447
513
|
];
|
|
448
514
|
for (const el of children) {
|
|
449
|
-
this.addEdges(this.graph.anyEdgesBetween(el, neighbours))
|
|
515
|
+
this.addEdges(this.graph.anyEdgesBetween(el, neighbours)).forEach((edge) => {
|
|
516
|
+
this.addImplicit(edge.source, edge.target);
|
|
517
|
+
});
|
|
450
518
|
}
|
|
451
519
|
if (!hasChildren && _elRoot) {
|
|
452
520
|
const edgesWithSiblings = this.graph.anyEdgesBetween(_elRoot, this.graph.siblings(root));
|
|
@@ -637,8 +705,9 @@ function includeIncomingExpr(expr, where) {
|
|
|
637
705
|
if (edges.length === 0) {
|
|
638
706
|
return;
|
|
639
707
|
}
|
|
640
|
-
this.addEdges(edges)
|
|
641
|
-
|
|
708
|
+
this.addEdges(edges).forEach((edge) => {
|
|
709
|
+
this.addImplicit(edge.target);
|
|
710
|
+
});
|
|
642
711
|
}
|
|
643
712
|
function excludeIncomingExpr(expr, where) {
|
|
644
713
|
let relations = filterRelations(edgesIncomingExpr.call(this, expr.incoming), where);
|
|
@@ -678,8 +747,9 @@ function includeOutgoingExpr(expr, where) {
|
|
|
678
747
|
if (edges.length === 0) {
|
|
679
748
|
return;
|
|
680
749
|
}
|
|
681
|
-
this.addEdges(edges)
|
|
682
|
-
|
|
750
|
+
this.addEdges(edges).forEach((edge) => {
|
|
751
|
+
this.addImplicit(edge.source);
|
|
752
|
+
});
|
|
683
753
|
}
|
|
684
754
|
function excludeOutgoingExpr(expr, where) {
|
|
685
755
|
const relations = filterRelations(edgesOutgoingExpr.call(this, expr.outgoing), where);
|
|
@@ -764,7 +834,9 @@ function includeRelationExpr(expr, where) {
|
|
|
764
834
|
if (expr.isBidirectional === true) {
|
|
765
835
|
edges.push(...this.graph.edgesBetween(targets, sources));
|
|
766
836
|
}
|
|
767
|
-
this.addEdges(filterEdges(edges, where))
|
|
837
|
+
this.addEdges(filterEdges(edges, where)).forEach((edge) => {
|
|
838
|
+
this.activeGroup.addImplicit(edge.source, edge.target);
|
|
839
|
+
});
|
|
768
840
|
}
|
|
769
841
|
function excludeRelationExpr(expr, where) {
|
|
770
842
|
const isSource = elementExprToPredicate(expr.source);
|
|
@@ -787,6 +859,50 @@ function compareEdges(a, b) {
|
|
|
787
859
|
{ source: b.source.id, target: b.target.id }
|
|
788
860
|
);
|
|
789
861
|
}
|
|
862
|
+
class NodesGroup {
|
|
863
|
+
constructor(id, viewRule, parent = null) {
|
|
864
|
+
this.id = id;
|
|
865
|
+
this.viewRule = viewRule;
|
|
866
|
+
this.parent = parent;
|
|
867
|
+
}
|
|
868
|
+
static kind = ElementKind.Group;
|
|
869
|
+
static root() {
|
|
870
|
+
return new NodesGroup("@root", { title: null, groupRules: [] });
|
|
871
|
+
}
|
|
872
|
+
static is(node) {
|
|
873
|
+
return node.kind === NodesGroup.kind;
|
|
874
|
+
}
|
|
875
|
+
explicits = /* @__PURE__ */ new Set();
|
|
876
|
+
implicits = /* @__PURE__ */ new Set();
|
|
877
|
+
/**
|
|
878
|
+
* Add element explicitly
|
|
879
|
+
* Included even without relationships
|
|
880
|
+
*/
|
|
881
|
+
addElement(...el) {
|
|
882
|
+
for (const r of el) {
|
|
883
|
+
this.explicits.add(r);
|
|
884
|
+
this.implicits.add(r);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Add element implicitly
|
|
889
|
+
* Included if only has relationships
|
|
890
|
+
*/
|
|
891
|
+
addImplicit(...el) {
|
|
892
|
+
for (const r of el) {
|
|
893
|
+
this.implicits.add(r);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
excludeElement(...excludes) {
|
|
897
|
+
for (const el of excludes) {
|
|
898
|
+
this.explicits.delete(el);
|
|
899
|
+
this.implicits.delete(el);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
isEmpty() {
|
|
903
|
+
return this.explicits.size === 0 && this.implicits.size === 0;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
790
906
|
class ComputeCtx {
|
|
791
907
|
constructor(view, graph) {
|
|
792
908
|
this.view = view;
|
|
@@ -796,6 +912,16 @@ class ComputeCtx {
|
|
|
796
912
|
explicits = /* @__PURE__ */ new Set();
|
|
797
913
|
implicits = /* @__PURE__ */ new Set();
|
|
798
914
|
ctxEdges = [];
|
|
915
|
+
/**
|
|
916
|
+
* Root group - not included in the groups
|
|
917
|
+
* But used to accumulate elements that are not in any group
|
|
918
|
+
*/
|
|
919
|
+
__rootGroup = NodesGroup.root();
|
|
920
|
+
groups = [];
|
|
921
|
+
activeGroupStack = [];
|
|
922
|
+
get activeGroup() {
|
|
923
|
+
return this.activeGroupStack[0] ?? this.__rootGroup;
|
|
924
|
+
}
|
|
799
925
|
static elementView(view, graph) {
|
|
800
926
|
return new ComputeCtx(view, graph).compute();
|
|
801
927
|
}
|
|
@@ -807,14 +933,30 @@ class ComputeCtx {
|
|
|
807
933
|
rules,
|
|
808
934
|
...view
|
|
809
935
|
} = this.view;
|
|
810
|
-
const viewPredicates = rules.filter(isViewRulePredicate);
|
|
936
|
+
const viewPredicates = rules.filter(anyPass([isViewRulePredicate, isViewRuleGroup]));
|
|
811
937
|
if (this.root && viewPredicates.length == 0) {
|
|
812
938
|
this.addElement(this.graph.element(this.root));
|
|
813
939
|
}
|
|
814
940
|
this.processPredicates(viewPredicates);
|
|
815
941
|
this.removeRedundantImplicitEdges();
|
|
942
|
+
if (this.groups.length > 0) {
|
|
943
|
+
this.cleanGroupElements();
|
|
944
|
+
}
|
|
816
945
|
const elements = [...this.includedElements];
|
|
817
|
-
const nodesMap = buildComputeNodes(elements);
|
|
946
|
+
const nodesMap = buildComputeNodes(elements, this.groups);
|
|
947
|
+
const ancestorsOf = (node) => {
|
|
948
|
+
const ancestors = [];
|
|
949
|
+
let parent = node.parent;
|
|
950
|
+
while (parent) {
|
|
951
|
+
const parentNode = nodesMap.get(parent);
|
|
952
|
+
if (!parentNode) {
|
|
953
|
+
break;
|
|
954
|
+
}
|
|
955
|
+
ancestors.push(parentNode);
|
|
956
|
+
parent = parentNode.parent;
|
|
957
|
+
}
|
|
958
|
+
return ancestors;
|
|
959
|
+
};
|
|
818
960
|
const edgesMap = /* @__PURE__ */ new Map();
|
|
819
961
|
const edges = this.computeEdges();
|
|
820
962
|
for (const edge of edges) {
|
|
@@ -823,25 +965,37 @@ class ComputeCtx {
|
|
|
823
965
|
const target = nodesMap.get(edge.target);
|
|
824
966
|
invariant(source, `Source node ${edge.source} not found`);
|
|
825
967
|
invariant(target, `Target node ${edge.target} not found`);
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
968
|
+
const sourceAncestors = ancestorsOf(source);
|
|
969
|
+
const targetAncestors = ancestorsOf(target);
|
|
970
|
+
const edgeParent = last(
|
|
971
|
+
commonHead(
|
|
972
|
+
reverse(sourceAncestors),
|
|
973
|
+
reverse(targetAncestors)
|
|
974
|
+
)
|
|
975
|
+
);
|
|
976
|
+
edge.parent = edgeParent?.id ?? null;
|
|
829
977
|
source.outEdges.push(edge.id);
|
|
830
978
|
target.inEdges.push(edge.id);
|
|
831
|
-
for (const sourceAncestor of
|
|
832
|
-
if (sourceAncestor ===
|
|
979
|
+
for (const sourceAncestor of sourceAncestors) {
|
|
980
|
+
if (sourceAncestor === edgeParent) {
|
|
833
981
|
break;
|
|
834
982
|
}
|
|
835
|
-
|
|
983
|
+
sourceAncestor.outEdges.push(edge.id);
|
|
836
984
|
}
|
|
837
|
-
for (const targetAncestor of
|
|
838
|
-
if (targetAncestor ===
|
|
985
|
+
for (const targetAncestor of targetAncestors) {
|
|
986
|
+
if (targetAncestor === edgeParent) {
|
|
839
987
|
break;
|
|
840
988
|
}
|
|
841
|
-
|
|
989
|
+
targetAncestor.inEdges.push(edge.id);
|
|
842
990
|
}
|
|
843
991
|
}
|
|
844
|
-
|
|
992
|
+
let initialSort = elements.map((e) => nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`));
|
|
993
|
+
if (this.groups.length > 0) {
|
|
994
|
+
initialSort = concat(
|
|
995
|
+
this.groups.map((g) => nonNullable(nodesMap.get(g.id), `Node ${g.id} not found in nodesMap`)),
|
|
996
|
+
initialSort
|
|
997
|
+
);
|
|
998
|
+
}
|
|
845
999
|
const nodes = applyCustomElementProperties(
|
|
846
1000
|
rules,
|
|
847
1001
|
applyViewRuleStyles(
|
|
@@ -987,6 +1141,7 @@ class ComputeCtx {
|
|
|
987
1141
|
return this.ctxEdges;
|
|
988
1142
|
}
|
|
989
1143
|
addEdges(edges) {
|
|
1144
|
+
const added = [];
|
|
990
1145
|
for (const e of edges) {
|
|
991
1146
|
if (!hasAtLeast$1(e.relations, 1)) {
|
|
992
1147
|
continue;
|
|
@@ -996,10 +1151,13 @@ class ComputeCtx {
|
|
|
996
1151
|
);
|
|
997
1152
|
if (existing) {
|
|
998
1153
|
existing.relations = unique([...existing.relations, ...e.relations]);
|
|
1154
|
+
added.push(existing);
|
|
999
1155
|
continue;
|
|
1000
1156
|
}
|
|
1157
|
+
added.push(e);
|
|
1001
1158
|
this.ctxEdges.push(e);
|
|
1002
1159
|
}
|
|
1160
|
+
return added;
|
|
1003
1161
|
}
|
|
1004
1162
|
/**
|
|
1005
1163
|
* Add element explicitly
|
|
@@ -1007,6 +1165,14 @@ class ComputeCtx {
|
|
|
1007
1165
|
*/
|
|
1008
1166
|
addElement(...el) {
|
|
1009
1167
|
for (const r of el) {
|
|
1168
|
+
if (!this.explicits.has(r)) {
|
|
1169
|
+
this.activeGroup.addElement(r);
|
|
1170
|
+
} else if (this.activeGroup !== this.__rootGroup) {
|
|
1171
|
+
this.groups.forEach((g) => {
|
|
1172
|
+
g.implicits.delete(r);
|
|
1173
|
+
});
|
|
1174
|
+
this.activeGroup.addImplicit(r);
|
|
1175
|
+
}
|
|
1010
1176
|
this.explicits.add(r);
|
|
1011
1177
|
this.implicits.add(r);
|
|
1012
1178
|
}
|
|
@@ -1018,7 +1184,13 @@ class ComputeCtx {
|
|
|
1018
1184
|
addImplicit(...el) {
|
|
1019
1185
|
for (const r of el) {
|
|
1020
1186
|
this.implicits.add(r);
|
|
1187
|
+
if (this.activeGroup !== this.__rootGroup) {
|
|
1188
|
+
this.groups.forEach((g) => {
|
|
1189
|
+
g.implicits.delete(r);
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1021
1192
|
}
|
|
1193
|
+
this.activeGroup.addImplicit(...el);
|
|
1022
1194
|
}
|
|
1023
1195
|
excludeElement(...excludes) {
|
|
1024
1196
|
for (const el of excludes) {
|
|
@@ -1026,12 +1198,11 @@ class ComputeCtx {
|
|
|
1026
1198
|
this.explicits.delete(el);
|
|
1027
1199
|
this.implicits.delete(el);
|
|
1028
1200
|
}
|
|
1201
|
+
this.__rootGroup.excludeElement(...excludes);
|
|
1202
|
+
this.groups.forEach((g) => {
|
|
1203
|
+
g.excludeElement(...excludes);
|
|
1204
|
+
});
|
|
1029
1205
|
}
|
|
1030
|
-
// protected excludeImplicit(...excludes: Element[]) {
|
|
1031
|
-
// for (const el of excludes) {
|
|
1032
|
-
// this.implicits.delete(el)
|
|
1033
|
-
// }
|
|
1034
|
-
// }
|
|
1035
1206
|
excludeRelation(...relations) {
|
|
1036
1207
|
if (relations.length === 0) {
|
|
1037
1208
|
return;
|
|
@@ -1063,11 +1234,15 @@ class ComputeCtx {
|
|
|
1063
1234
|
const remaining = this.includedElements;
|
|
1064
1235
|
if (remaining.size === 0) {
|
|
1065
1236
|
this.implicits.clear();
|
|
1237
|
+
this.__rootGroup.implicits.clear();
|
|
1238
|
+
this.groups.forEach((g) => g.implicits.clear());
|
|
1066
1239
|
return;
|
|
1067
1240
|
}
|
|
1068
1241
|
for (const el of excludedImplicits) {
|
|
1069
1242
|
if (!remaining.has(el)) {
|
|
1070
1243
|
this.implicits.delete(el);
|
|
1244
|
+
this.__rootGroup.implicits.delete(el);
|
|
1245
|
+
this.groups.forEach((g) => g.implicits.delete(el));
|
|
1071
1246
|
}
|
|
1072
1247
|
}
|
|
1073
1248
|
}
|
|
@@ -1075,6 +1250,9 @@ class ComputeCtx {
|
|
|
1075
1250
|
this.explicits.clear();
|
|
1076
1251
|
this.implicits.clear();
|
|
1077
1252
|
this.ctxEdges = [];
|
|
1253
|
+
this.__rootGroup = NodesGroup.root();
|
|
1254
|
+
this.groups = [];
|
|
1255
|
+
this.activeGroupStack = [];
|
|
1078
1256
|
}
|
|
1079
1257
|
// Filter out edges if there are edges between descendants
|
|
1080
1258
|
// i.e. remove implicit edges, derived from childs
|
|
@@ -1117,8 +1295,41 @@ class ComputeCtx {
|
|
|
1117
1295
|
return acc;
|
|
1118
1296
|
}, []);
|
|
1119
1297
|
}
|
|
1298
|
+
cleanGroupElements() {
|
|
1299
|
+
const unprocessed = new Set(difference(
|
|
1300
|
+
[...this.includedElements],
|
|
1301
|
+
[...this.__rootGroup.explicits]
|
|
1302
|
+
));
|
|
1303
|
+
for (const group of this.groups) {
|
|
1304
|
+
const explicits = [...group.explicits];
|
|
1305
|
+
group.explicits.clear();
|
|
1306
|
+
for (const el of explicits) {
|
|
1307
|
+
if (unprocessed.delete(el)) {
|
|
1308
|
+
group.explicits.add(el);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
for (const group of this.groups) {
|
|
1313
|
+
for (const el of group.implicits) {
|
|
1314
|
+
if (unprocessed.delete(el)) {
|
|
1315
|
+
group.explicits.add(el);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
group.implicits.clear();
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1120
1321
|
processPredicates(viewRules) {
|
|
1121
1322
|
for (const rule of viewRules) {
|
|
1323
|
+
if (isViewRuleGroup(rule)) {
|
|
1324
|
+
const parent = first(this.activeGroupStack);
|
|
1325
|
+
const groupId = `@gr${this.groups.length + 1}`;
|
|
1326
|
+
const group = new NodesGroup(groupId, rule, parent?.id ?? null);
|
|
1327
|
+
this.groups.push(group);
|
|
1328
|
+
this.activeGroupStack.unshift(group);
|
|
1329
|
+
this.processPredicates(rule.groupRules);
|
|
1330
|
+
this.activeGroupStack.shift();
|
|
1331
|
+
continue;
|
|
1332
|
+
}
|
|
1122
1333
|
const isInclude = "include" in rule;
|
|
1123
1334
|
const exprs = rule.include ?? rule.exclude;
|
|
1124
1335
|
for (const expr of exprs) {
|