@likec4/language-server 1.13.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.mts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.mjs +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/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.80ITEDo5.cjs} +272 -64
- package/dist/shared/{language-server.C2ebP2pZ.cjs → language-server.BUtiWTKg.cjs} +383 -32
- package/dist/shared/{language-server.DFLaUdYu.mjs → language-server.DXC9g4_f.mjs} +274 -66
- package/dist/shared/{language-server.ryB8CivX.d.mts → language-server.DfMwkd2l.d.mts} +81 -15
- package/dist/shared/{language-server.Cnq_hgfm.d.cts → language-server.U2piOAVt.d.cts} +81 -15
- package/dist/shared/{language-server.eY70DuKx.d.ts → language-server.j-ShR6as.d.ts} +81 -15
- package/dist/shared/{language-server.CrE0nFSB.mjs → language-server.zY53FGJE.mjs} +385 -34
- package/package.json +12 -12
- package/src/ast.ts +11 -0
- package/src/generated/ast.ts +177 -17
- package/src/generated/grammar.ts +1 -1
- package/src/generated-lib/icons.ts +0 -1
- package/src/like-c4.langium +50 -4
- package/src/lsp/CompletionProvider.ts +70 -2
- package/src/model/model-builder.ts +25 -3
- package/src/model/model-parser.ts +150 -33
- package/src/model-graph/compute-view/__test__/fixture.ts +45 -4
- 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 +2 -1
- 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/sortNodes.ts +11 -7
- package/src/references/scope-computation.ts +24 -1
- package/src/validation/index.ts +4 -0
- package/src/validation/specification.ts +30 -0
- package/src/view-utils/index.ts +1 -0
- package/src/view-utils/resolve-global-rules.ts +72 -0
- package/dist/shared/language-server.B-9_mDoo.d.mts +0 -1238
- package/dist/shared/language-server.C3oS5yhF.d.cts +0 -1238
- package/dist/shared/language-server.r5AXAWzc.d.ts +0 -1238
|
@@ -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) {
|
|
@@ -128,7 +139,7 @@ function relationExpressionToPredicates(expr) {
|
|
|
128
139
|
}
|
|
129
140
|
}
|
|
130
141
|
function applyCustomRelationProperties(_rules, nodes, _edges) {
|
|
131
|
-
const rules = _rules.flatMap((
|
|
142
|
+
const rules = _rules.flatMap(flattenGroupRules(Expr.isCustomRelationExpr));
|
|
132
143
|
const edges = Array.from(_edges);
|
|
133
144
|
if (rules.length === 0 || edges.length === 0) {
|
|
134
145
|
return edges;
|
|
@@ -171,29 +182,34 @@ function applyViewRuleStyles(_rules, nodes) {
|
|
|
171
182
|
}
|
|
172
183
|
predicates.push(elementExprToPredicate(target));
|
|
173
184
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
...
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
185
|
+
pipe(
|
|
186
|
+
nodes,
|
|
187
|
+
filter(isNot(ComputedNode.isNodesGroup)),
|
|
188
|
+
filter(anyPass(predicates)),
|
|
189
|
+
forEach((n) => {
|
|
190
|
+
n.shape = rule.style.shape ?? n.shape;
|
|
191
|
+
n.color = rule.style.color ?? n.color;
|
|
192
|
+
if (isDefined(rule.style.icon)) {
|
|
193
|
+
n.icon = rule.style.icon;
|
|
194
|
+
}
|
|
195
|
+
if (isDefined(rule.notation)) {
|
|
196
|
+
n.notation = rule.notation;
|
|
197
|
+
}
|
|
198
|
+
let styleOverride;
|
|
199
|
+
if (isDefined(rule.style.border)) {
|
|
200
|
+
styleOverride = { border: rule.style.border };
|
|
201
|
+
}
|
|
202
|
+
if (isDefined(rule.style.opacity)) {
|
|
203
|
+
styleOverride = { ...styleOverride, opacity: rule.style.opacity };
|
|
204
|
+
}
|
|
205
|
+
if (styleOverride) {
|
|
206
|
+
n.style = {
|
|
207
|
+
...n.style,
|
|
208
|
+
...styleOverride
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
);
|
|
197
213
|
}
|
|
198
214
|
return nodes;
|
|
199
215
|
}
|
|
@@ -209,40 +225,80 @@ function updateDepthOfAncestors(node, nodes) {
|
|
|
209
225
|
node = parentNd;
|
|
210
226
|
}
|
|
211
227
|
}
|
|
212
|
-
function buildComputeNodes(elements) {
|
|
213
|
-
|
|
228
|
+
function buildComputeNodes(elements, groups) {
|
|
229
|
+
const nodesMap = /* @__PURE__ */ new Map();
|
|
230
|
+
const elementToGroup = /* @__PURE__ */ new Map();
|
|
231
|
+
groups?.forEach(({ id, parent, viewRule, explicits }) => {
|
|
232
|
+
if (parent) {
|
|
233
|
+
nonNullable(nodesMap.get(parent), `Parent group node ${parent} not found`).children.push(id);
|
|
234
|
+
}
|
|
235
|
+
nodesMap.set(id, {
|
|
236
|
+
id,
|
|
237
|
+
parent,
|
|
238
|
+
kind: ElementKind.Group,
|
|
239
|
+
title: viewRule.title ?? "",
|
|
240
|
+
color: viewRule.color ?? "muted",
|
|
241
|
+
shape: "rectangle",
|
|
242
|
+
children: [],
|
|
243
|
+
inEdges: [],
|
|
244
|
+
outEdges: [],
|
|
245
|
+
level: 0,
|
|
246
|
+
depth: 0,
|
|
247
|
+
description: null,
|
|
248
|
+
technology: null,
|
|
249
|
+
tags: null,
|
|
250
|
+
links: null,
|
|
251
|
+
style: {
|
|
252
|
+
border: viewRule.border ?? "dashed",
|
|
253
|
+
opacity: viewRule.opacity ?? 0
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
for (const e of explicits) {
|
|
257
|
+
elementToGroup.set(e.id, id);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
return Array.from(elements).sort(compareByFqnHierarchically).reduce((map, { id, color, shape, style, kind, title, ...el }) => {
|
|
214
261
|
let parent = parentFqn(id);
|
|
215
262
|
let level = 0;
|
|
263
|
+
let parentNd;
|
|
216
264
|
while (parent) {
|
|
217
|
-
|
|
265
|
+
parentNd = map.get(parent);
|
|
218
266
|
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
267
|
break;
|
|
226
268
|
}
|
|
227
269
|
parent = parentFqn(parent);
|
|
228
270
|
}
|
|
271
|
+
if (!parentNd && elementToGroup.has(id)) {
|
|
272
|
+
parent = elementToGroup.get(id);
|
|
273
|
+
parentNd = map.get(parent);
|
|
274
|
+
}
|
|
275
|
+
if (parentNd) {
|
|
276
|
+
if (parentNd.children.length == 0) {
|
|
277
|
+
parentNd.depth = 1;
|
|
278
|
+
updateDepthOfAncestors(parentNd, map);
|
|
279
|
+
}
|
|
280
|
+
parentNd.children.push(id);
|
|
281
|
+
level = parentNd.level + 1;
|
|
282
|
+
}
|
|
229
283
|
const node = {
|
|
230
|
-
...el,
|
|
231
284
|
id,
|
|
232
285
|
parent,
|
|
233
|
-
|
|
286
|
+
kind,
|
|
287
|
+
title,
|
|
234
288
|
color: color ?? DefaultThemeColor,
|
|
235
289
|
shape: shape ?? DefaultElementShape,
|
|
236
290
|
children: [],
|
|
237
291
|
inEdges: [],
|
|
238
292
|
outEdges: [],
|
|
293
|
+
level,
|
|
294
|
+
...el,
|
|
239
295
|
style: {
|
|
240
296
|
...style
|
|
241
297
|
}
|
|
242
298
|
};
|
|
243
299
|
map.set(id, node);
|
|
244
300
|
return map;
|
|
245
|
-
},
|
|
301
|
+
}, nodesMap);
|
|
246
302
|
}
|
|
247
303
|
|
|
248
304
|
function buildElementNotations(nodes) {
|
|
@@ -314,9 +370,16 @@ function sortNodes({
|
|
|
314
370
|
nodes,
|
|
315
371
|
edges
|
|
316
372
|
}) {
|
|
317
|
-
if (
|
|
373
|
+
if (nodes.length < 2) {
|
|
318
374
|
return nodes;
|
|
319
375
|
}
|
|
376
|
+
if (edges.length === 0) {
|
|
377
|
+
return pipe(
|
|
378
|
+
nodes,
|
|
379
|
+
sort(compareByFqnHierarchically),
|
|
380
|
+
tap(sortChildren)
|
|
381
|
+
);
|
|
382
|
+
}
|
|
320
383
|
const g = new Graph({
|
|
321
384
|
compound: false,
|
|
322
385
|
directed: true,
|
|
@@ -351,15 +414,15 @@ function sortNodes({
|
|
|
351
414
|
if (sources.length === 0) {
|
|
352
415
|
sources = pipe(
|
|
353
416
|
nodes,
|
|
354
|
-
sort(compareByFqnHierarchically),
|
|
355
417
|
filter((n) => n.inEdges.length === 0 || n.parent === null),
|
|
418
|
+
sort(compareByFqnHierarchically),
|
|
356
419
|
map((n) => n.id)
|
|
357
420
|
);
|
|
358
421
|
}
|
|
359
422
|
const orderedIds = postorder(g, sources).reverse();
|
|
360
423
|
const sorted = orderedIds.map(getNode);
|
|
361
424
|
if (sorted.length < nodes.length) {
|
|
362
|
-
const unsorted = difference(nodes, sorted);
|
|
425
|
+
const unsorted = difference(nodes, sorted).sort(compareByFqnHierarchically);
|
|
363
426
|
sorted.push(...unsorted);
|
|
364
427
|
}
|
|
365
428
|
invariant(sorted.length === nodes.length, "Not all nodes were processed by graphlib");
|
|
@@ -446,7 +509,9 @@ function includeWildcardRef(_expr, where = NoFilter) {
|
|
|
446
509
|
])
|
|
447
510
|
];
|
|
448
511
|
for (const el of children) {
|
|
449
|
-
this.addEdges(this.graph.anyEdgesBetween(el, neighbours))
|
|
512
|
+
this.addEdges(this.graph.anyEdgesBetween(el, neighbours)).forEach((edge) => {
|
|
513
|
+
this.addImplicit(edge.source, edge.target);
|
|
514
|
+
});
|
|
450
515
|
}
|
|
451
516
|
if (!hasChildren && _elRoot) {
|
|
452
517
|
const edgesWithSiblings = this.graph.anyEdgesBetween(_elRoot, this.graph.siblings(root));
|
|
@@ -637,8 +702,9 @@ function includeIncomingExpr(expr, where) {
|
|
|
637
702
|
if (edges.length === 0) {
|
|
638
703
|
return;
|
|
639
704
|
}
|
|
640
|
-
this.addEdges(edges)
|
|
641
|
-
|
|
705
|
+
this.addEdges(edges).forEach((edge) => {
|
|
706
|
+
this.addImplicit(edge.target);
|
|
707
|
+
});
|
|
642
708
|
}
|
|
643
709
|
function excludeIncomingExpr(expr, where) {
|
|
644
710
|
let relations = filterRelations(edgesIncomingExpr.call(this, expr.incoming), where);
|
|
@@ -678,8 +744,9 @@ function includeOutgoingExpr(expr, where) {
|
|
|
678
744
|
if (edges.length === 0) {
|
|
679
745
|
return;
|
|
680
746
|
}
|
|
681
|
-
this.addEdges(edges)
|
|
682
|
-
|
|
747
|
+
this.addEdges(edges).forEach((edge) => {
|
|
748
|
+
this.addImplicit(edge.source);
|
|
749
|
+
});
|
|
683
750
|
}
|
|
684
751
|
function excludeOutgoingExpr(expr, where) {
|
|
685
752
|
const relations = filterRelations(edgesOutgoingExpr.call(this, expr.outgoing), where);
|
|
@@ -764,7 +831,9 @@ function includeRelationExpr(expr, where) {
|
|
|
764
831
|
if (expr.isBidirectional === true) {
|
|
765
832
|
edges.push(...this.graph.edgesBetween(targets, sources));
|
|
766
833
|
}
|
|
767
|
-
this.addEdges(filterEdges(edges, where))
|
|
834
|
+
this.addEdges(filterEdges(edges, where)).forEach((edge) => {
|
|
835
|
+
this.activeGroup.addImplicit(edge.source, edge.target);
|
|
836
|
+
});
|
|
768
837
|
}
|
|
769
838
|
function excludeRelationExpr(expr, where) {
|
|
770
839
|
const isSource = elementExprToPredicate(expr.source);
|
|
@@ -787,6 +856,50 @@ function compareEdges(a, b) {
|
|
|
787
856
|
{ source: b.source.id, target: b.target.id }
|
|
788
857
|
);
|
|
789
858
|
}
|
|
859
|
+
class NodesGroup {
|
|
860
|
+
constructor(id, viewRule, parent = null) {
|
|
861
|
+
this.id = id;
|
|
862
|
+
this.viewRule = viewRule;
|
|
863
|
+
this.parent = parent;
|
|
864
|
+
}
|
|
865
|
+
static kind = ElementKind.Group;
|
|
866
|
+
static root() {
|
|
867
|
+
return new NodesGroup("@root", { title: null, groupRules: [] });
|
|
868
|
+
}
|
|
869
|
+
static is(node) {
|
|
870
|
+
return node.kind === NodesGroup.kind;
|
|
871
|
+
}
|
|
872
|
+
explicits = /* @__PURE__ */ new Set();
|
|
873
|
+
implicits = /* @__PURE__ */ new Set();
|
|
874
|
+
/**
|
|
875
|
+
* Add element explicitly
|
|
876
|
+
* Included even without relationships
|
|
877
|
+
*/
|
|
878
|
+
addElement(...el) {
|
|
879
|
+
for (const r of el) {
|
|
880
|
+
this.explicits.add(r);
|
|
881
|
+
this.implicits.add(r);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Add element implicitly
|
|
886
|
+
* Included if only has relationships
|
|
887
|
+
*/
|
|
888
|
+
addImplicit(...el) {
|
|
889
|
+
for (const r of el) {
|
|
890
|
+
this.implicits.add(r);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
excludeElement(...excludes) {
|
|
894
|
+
for (const el of excludes) {
|
|
895
|
+
this.explicits.delete(el);
|
|
896
|
+
this.implicits.delete(el);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
isEmpty() {
|
|
900
|
+
return this.explicits.size === 0 && this.implicits.size === 0;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
790
903
|
class ComputeCtx {
|
|
791
904
|
constructor(view, graph) {
|
|
792
905
|
this.view = view;
|
|
@@ -796,6 +909,16 @@ class ComputeCtx {
|
|
|
796
909
|
explicits = /* @__PURE__ */ new Set();
|
|
797
910
|
implicits = /* @__PURE__ */ new Set();
|
|
798
911
|
ctxEdges = [];
|
|
912
|
+
/**
|
|
913
|
+
* Root group - not included in the groups
|
|
914
|
+
* But used to accumulate elements that are not in any group
|
|
915
|
+
*/
|
|
916
|
+
__rootGroup = NodesGroup.root();
|
|
917
|
+
groups = [];
|
|
918
|
+
activeGroupStack = [];
|
|
919
|
+
get activeGroup() {
|
|
920
|
+
return this.activeGroupStack[0] ?? this.__rootGroup;
|
|
921
|
+
}
|
|
799
922
|
static elementView(view, graph) {
|
|
800
923
|
return new ComputeCtx(view, graph).compute();
|
|
801
924
|
}
|
|
@@ -807,14 +930,30 @@ class ComputeCtx {
|
|
|
807
930
|
rules,
|
|
808
931
|
...view
|
|
809
932
|
} = this.view;
|
|
810
|
-
const viewPredicates = rules.filter(isViewRulePredicate);
|
|
933
|
+
const viewPredicates = rules.filter(anyPass([isViewRulePredicate, isViewRuleGroup]));
|
|
811
934
|
if (this.root && viewPredicates.length == 0) {
|
|
812
935
|
this.addElement(this.graph.element(this.root));
|
|
813
936
|
}
|
|
814
937
|
this.processPredicates(viewPredicates);
|
|
815
938
|
this.removeRedundantImplicitEdges();
|
|
939
|
+
if (this.groups.length > 0) {
|
|
940
|
+
this.cleanGroupElements();
|
|
941
|
+
}
|
|
816
942
|
const elements = [...this.includedElements];
|
|
817
|
-
const nodesMap = buildComputeNodes(elements);
|
|
943
|
+
const nodesMap = buildComputeNodes(elements, this.groups);
|
|
944
|
+
const ancestorsOf = (node) => {
|
|
945
|
+
const ancestors = [];
|
|
946
|
+
let parent = node.parent;
|
|
947
|
+
while (parent) {
|
|
948
|
+
const parentNode = nodesMap.get(parent);
|
|
949
|
+
if (!parentNode) {
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
ancestors.push(parentNode);
|
|
953
|
+
parent = parentNode.parent;
|
|
954
|
+
}
|
|
955
|
+
return ancestors;
|
|
956
|
+
};
|
|
818
957
|
const edgesMap = /* @__PURE__ */ new Map();
|
|
819
958
|
const edges = this.computeEdges();
|
|
820
959
|
for (const edge of edges) {
|
|
@@ -823,25 +962,37 @@ class ComputeCtx {
|
|
|
823
962
|
const target = nodesMap.get(edge.target);
|
|
824
963
|
invariant(source, `Source node ${edge.source} not found`);
|
|
825
964
|
invariant(target, `Target node ${edge.target} not found`);
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
965
|
+
const sourceAncestors = ancestorsOf(source);
|
|
966
|
+
const targetAncestors = ancestorsOf(target);
|
|
967
|
+
const edgeParent = last(
|
|
968
|
+
commonHead(
|
|
969
|
+
reverse(sourceAncestors),
|
|
970
|
+
reverse(targetAncestors)
|
|
971
|
+
)
|
|
972
|
+
);
|
|
973
|
+
edge.parent = edgeParent?.id ?? null;
|
|
829
974
|
source.outEdges.push(edge.id);
|
|
830
975
|
target.inEdges.push(edge.id);
|
|
831
|
-
for (const sourceAncestor of
|
|
832
|
-
if (sourceAncestor ===
|
|
976
|
+
for (const sourceAncestor of sourceAncestors) {
|
|
977
|
+
if (sourceAncestor === edgeParent) {
|
|
833
978
|
break;
|
|
834
979
|
}
|
|
835
|
-
|
|
980
|
+
sourceAncestor.outEdges.push(edge.id);
|
|
836
981
|
}
|
|
837
|
-
for (const targetAncestor of
|
|
838
|
-
if (targetAncestor ===
|
|
982
|
+
for (const targetAncestor of targetAncestors) {
|
|
983
|
+
if (targetAncestor === edgeParent) {
|
|
839
984
|
break;
|
|
840
985
|
}
|
|
841
|
-
|
|
986
|
+
targetAncestor.inEdges.push(edge.id);
|
|
842
987
|
}
|
|
843
988
|
}
|
|
844
|
-
|
|
989
|
+
let initialSort = elements.map((e) => nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`));
|
|
990
|
+
if (this.groups.length > 0) {
|
|
991
|
+
initialSort = concat(
|
|
992
|
+
this.groups.map((g) => nonNullable(nodesMap.get(g.id), `Node ${g.id} not found in nodesMap`)),
|
|
993
|
+
initialSort
|
|
994
|
+
);
|
|
995
|
+
}
|
|
845
996
|
const nodes = applyCustomElementProperties(
|
|
846
997
|
rules,
|
|
847
998
|
applyViewRuleStyles(
|
|
@@ -987,6 +1138,7 @@ class ComputeCtx {
|
|
|
987
1138
|
return this.ctxEdges;
|
|
988
1139
|
}
|
|
989
1140
|
addEdges(edges) {
|
|
1141
|
+
const added = [];
|
|
990
1142
|
for (const e of edges) {
|
|
991
1143
|
if (!hasAtLeast$1(e.relations, 1)) {
|
|
992
1144
|
continue;
|
|
@@ -996,10 +1148,13 @@ class ComputeCtx {
|
|
|
996
1148
|
);
|
|
997
1149
|
if (existing) {
|
|
998
1150
|
existing.relations = unique([...existing.relations, ...e.relations]);
|
|
1151
|
+
added.push(existing);
|
|
999
1152
|
continue;
|
|
1000
1153
|
}
|
|
1154
|
+
added.push(e);
|
|
1001
1155
|
this.ctxEdges.push(e);
|
|
1002
1156
|
}
|
|
1157
|
+
return added;
|
|
1003
1158
|
}
|
|
1004
1159
|
/**
|
|
1005
1160
|
* Add element explicitly
|
|
@@ -1007,6 +1162,14 @@ class ComputeCtx {
|
|
|
1007
1162
|
*/
|
|
1008
1163
|
addElement(...el) {
|
|
1009
1164
|
for (const r of el) {
|
|
1165
|
+
if (!this.explicits.has(r)) {
|
|
1166
|
+
this.activeGroup.addElement(r);
|
|
1167
|
+
} else if (this.activeGroup !== this.__rootGroup) {
|
|
1168
|
+
this.groups.forEach((g) => {
|
|
1169
|
+
g.implicits.delete(r);
|
|
1170
|
+
});
|
|
1171
|
+
this.activeGroup.addImplicit(r);
|
|
1172
|
+
}
|
|
1010
1173
|
this.explicits.add(r);
|
|
1011
1174
|
this.implicits.add(r);
|
|
1012
1175
|
}
|
|
@@ -1018,7 +1181,13 @@ class ComputeCtx {
|
|
|
1018
1181
|
addImplicit(...el) {
|
|
1019
1182
|
for (const r of el) {
|
|
1020
1183
|
this.implicits.add(r);
|
|
1184
|
+
if (this.activeGroup !== this.__rootGroup) {
|
|
1185
|
+
this.groups.forEach((g) => {
|
|
1186
|
+
g.implicits.delete(r);
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1021
1189
|
}
|
|
1190
|
+
this.activeGroup.addImplicit(...el);
|
|
1022
1191
|
}
|
|
1023
1192
|
excludeElement(...excludes) {
|
|
1024
1193
|
for (const el of excludes) {
|
|
@@ -1026,12 +1195,11 @@ class ComputeCtx {
|
|
|
1026
1195
|
this.explicits.delete(el);
|
|
1027
1196
|
this.implicits.delete(el);
|
|
1028
1197
|
}
|
|
1198
|
+
this.__rootGroup.excludeElement(...excludes);
|
|
1199
|
+
this.groups.forEach((g) => {
|
|
1200
|
+
g.excludeElement(...excludes);
|
|
1201
|
+
});
|
|
1029
1202
|
}
|
|
1030
|
-
// protected excludeImplicit(...excludes: Element[]) {
|
|
1031
|
-
// for (const el of excludes) {
|
|
1032
|
-
// this.implicits.delete(el)
|
|
1033
|
-
// }
|
|
1034
|
-
// }
|
|
1035
1203
|
excludeRelation(...relations) {
|
|
1036
1204
|
if (relations.length === 0) {
|
|
1037
1205
|
return;
|
|
@@ -1063,11 +1231,15 @@ class ComputeCtx {
|
|
|
1063
1231
|
const remaining = this.includedElements;
|
|
1064
1232
|
if (remaining.size === 0) {
|
|
1065
1233
|
this.implicits.clear();
|
|
1234
|
+
this.__rootGroup.implicits.clear();
|
|
1235
|
+
this.groups.forEach((g) => g.implicits.clear());
|
|
1066
1236
|
return;
|
|
1067
1237
|
}
|
|
1068
1238
|
for (const el of excludedImplicits) {
|
|
1069
1239
|
if (!remaining.has(el)) {
|
|
1070
1240
|
this.implicits.delete(el);
|
|
1241
|
+
this.__rootGroup.implicits.delete(el);
|
|
1242
|
+
this.groups.forEach((g) => g.implicits.delete(el));
|
|
1071
1243
|
}
|
|
1072
1244
|
}
|
|
1073
1245
|
}
|
|
@@ -1075,6 +1247,9 @@ class ComputeCtx {
|
|
|
1075
1247
|
this.explicits.clear();
|
|
1076
1248
|
this.implicits.clear();
|
|
1077
1249
|
this.ctxEdges = [];
|
|
1250
|
+
this.__rootGroup = NodesGroup.root();
|
|
1251
|
+
this.groups = [];
|
|
1252
|
+
this.activeGroupStack = [];
|
|
1078
1253
|
}
|
|
1079
1254
|
// Filter out edges if there are edges between descendants
|
|
1080
1255
|
// i.e. remove implicit edges, derived from childs
|
|
@@ -1117,8 +1292,41 @@ class ComputeCtx {
|
|
|
1117
1292
|
return acc;
|
|
1118
1293
|
}, []);
|
|
1119
1294
|
}
|
|
1295
|
+
cleanGroupElements() {
|
|
1296
|
+
const unprocessed = new Set(difference(
|
|
1297
|
+
[...this.includedElements],
|
|
1298
|
+
[...this.__rootGroup.explicits]
|
|
1299
|
+
));
|
|
1300
|
+
for (const group of this.groups) {
|
|
1301
|
+
const explicits = [...group.explicits];
|
|
1302
|
+
group.explicits.clear();
|
|
1303
|
+
for (const el of explicits) {
|
|
1304
|
+
if (unprocessed.delete(el)) {
|
|
1305
|
+
group.explicits.add(el);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
for (const group of this.groups) {
|
|
1310
|
+
for (const el of group.implicits) {
|
|
1311
|
+
if (unprocessed.delete(el)) {
|
|
1312
|
+
group.explicits.add(el);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
group.implicits.clear();
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1120
1318
|
processPredicates(viewRules) {
|
|
1121
1319
|
for (const rule of viewRules) {
|
|
1320
|
+
if (isViewRuleGroup(rule)) {
|
|
1321
|
+
const parent = first(this.activeGroupStack);
|
|
1322
|
+
const groupId = `@gr${this.groups.length + 1}`;
|
|
1323
|
+
const group = new NodesGroup(groupId, rule, parent?.id ?? null);
|
|
1324
|
+
this.groups.push(group);
|
|
1325
|
+
this.activeGroupStack.unshift(group);
|
|
1326
|
+
this.processPredicates(rule.groupRules);
|
|
1327
|
+
this.activeGroupStack.shift();
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1122
1330
|
const isInclude = "include" in rule;
|
|
1123
1331
|
const exprs = rule.include ?? rule.exclude;
|
|
1124
1332
|
for (const expr of exprs) {
|