@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
|
@@ -61,8 +61,19 @@ function elementExprToPredicate(target) {
|
|
|
61
61
|
core.nonexhaustive(target);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function flattenGroupRules(guard) {
|
|
65
|
+
return (rule) => {
|
|
66
|
+
if (core.isViewRuleGroup(rule)) {
|
|
67
|
+
return rule.groupRules.flatMap(flattenGroupRules(guard));
|
|
68
|
+
}
|
|
69
|
+
if (core.isViewRulePredicate(rule)) {
|
|
70
|
+
return "include" in rule ? rule.include.filter(guard) : [];
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
};
|
|
74
|
+
}
|
|
64
75
|
function applyCustomElementProperties(_rules, _nodes) {
|
|
65
|
-
const rules = _rules.flatMap((
|
|
76
|
+
const rules = _rules.flatMap(flattenGroupRules(core.Expr.isCustomElement));
|
|
66
77
|
if (rules.length === 0) {
|
|
67
78
|
return _nodes;
|
|
68
79
|
}
|
|
@@ -74,7 +85,7 @@ function applyCustomElementProperties(_rules, _nodes) {
|
|
|
74
85
|
const notEmpty = !remeda.isEmpty(rest);
|
|
75
86
|
const satisfies = elementExprToPredicate(expr);
|
|
76
87
|
nodes.forEach((node, i) => {
|
|
77
|
-
if (!satisfies(node)) {
|
|
88
|
+
if (core.ComputedNode.isNodesGroup(node) || !satisfies(node)) {
|
|
78
89
|
return;
|
|
79
90
|
}
|
|
80
91
|
if (notEmpty) {
|
|
@@ -110,7 +121,9 @@ function applyCustomElementProperties(_rules, _nodes) {
|
|
|
110
121
|
function relationExpressionToPredicates(expr) {
|
|
111
122
|
switch (true) {
|
|
112
123
|
case core.Expr.isRelationWhere(expr):
|
|
113
|
-
|
|
124
|
+
const predicate = relationExpressionToPredicates(expr.where.expr);
|
|
125
|
+
const where = core.whereOperatorAsPredicate(expr.where.condition);
|
|
126
|
+
return (e) => predicate(e) && where(e);
|
|
114
127
|
case core.Expr.isRelation(expr): {
|
|
115
128
|
const isSource = elementExprToPredicate(expr.source);
|
|
116
129
|
const isTarget = elementExprToPredicate(expr.target);
|
|
@@ -134,8 +147,9 @@ function relationExpressionToPredicates(expr) {
|
|
|
134
147
|
core.nonexhaustive(expr);
|
|
135
148
|
}
|
|
136
149
|
}
|
|
150
|
+
|
|
137
151
|
function applyCustomRelationProperties(_rules, nodes, _edges) {
|
|
138
|
-
const rules = _rules.flatMap((
|
|
152
|
+
const rules = _rules.flatMap(flattenGroupRules(core.Expr.isCustomRelationExpr));
|
|
139
153
|
const edges = Array.from(_edges);
|
|
140
154
|
if (rules.length === 0 || edges.length === 0) {
|
|
141
155
|
return edges;
|
|
@@ -151,12 +165,12 @@ function applyCustomRelationProperties(_rules, nodes, _edges) {
|
|
|
151
165
|
if (!source || !target) {
|
|
152
166
|
return;
|
|
153
167
|
}
|
|
154
|
-
if (satisfies({ source, target })) {
|
|
168
|
+
if (satisfies({ source, target, ...remeda.pick(edge, ["kind", "tags"]) })) {
|
|
155
169
|
edges[i] = {
|
|
156
170
|
...edge,
|
|
171
|
+
...props,
|
|
157
172
|
label: title ?? edge.label,
|
|
158
|
-
isCustomized: true
|
|
159
|
-
...props
|
|
173
|
+
isCustomized: true
|
|
160
174
|
};
|
|
161
175
|
}
|
|
162
176
|
});
|
|
@@ -178,29 +192,34 @@ function applyViewRuleStyles(_rules, nodes) {
|
|
|
178
192
|
}
|
|
179
193
|
predicates.push(elementExprToPredicate(target));
|
|
180
194
|
}
|
|
181
|
-
remeda.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
...
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
195
|
+
remeda.pipe(
|
|
196
|
+
nodes,
|
|
197
|
+
remeda.filter(remeda.isNot(core.ComputedNode.isNodesGroup)),
|
|
198
|
+
remeda.filter(remeda.anyPass(predicates)),
|
|
199
|
+
remeda.forEach((n) => {
|
|
200
|
+
n.shape = rule.style.shape ?? n.shape;
|
|
201
|
+
n.color = rule.style.color ?? n.color;
|
|
202
|
+
if (remeda.isDefined(rule.style.icon)) {
|
|
203
|
+
n.icon = rule.style.icon;
|
|
204
|
+
}
|
|
205
|
+
if (remeda.isDefined(rule.notation)) {
|
|
206
|
+
n.notation = rule.notation;
|
|
207
|
+
}
|
|
208
|
+
let styleOverride;
|
|
209
|
+
if (remeda.isDefined(rule.style.border)) {
|
|
210
|
+
styleOverride = { border: rule.style.border };
|
|
211
|
+
}
|
|
212
|
+
if (remeda.isDefined(rule.style.opacity)) {
|
|
213
|
+
styleOverride = { ...styleOverride, opacity: rule.style.opacity };
|
|
214
|
+
}
|
|
215
|
+
if (styleOverride) {
|
|
216
|
+
n.style = {
|
|
217
|
+
...n.style,
|
|
218
|
+
...styleOverride
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
);
|
|
204
223
|
}
|
|
205
224
|
return nodes;
|
|
206
225
|
}
|
|
@@ -216,40 +235,80 @@ function updateDepthOfAncestors(node, nodes) {
|
|
|
216
235
|
node = parentNd;
|
|
217
236
|
}
|
|
218
237
|
}
|
|
219
|
-
function buildComputeNodes(elements) {
|
|
220
|
-
|
|
238
|
+
function buildComputeNodes(elements, groups) {
|
|
239
|
+
const nodesMap = /* @__PURE__ */ new Map();
|
|
240
|
+
const elementToGroup = /* @__PURE__ */ new Map();
|
|
241
|
+
groups?.forEach(({ id, parent, viewRule, explicits }) => {
|
|
242
|
+
if (parent) {
|
|
243
|
+
core.nonNullable(nodesMap.get(parent), `Parent group node ${parent} not found`).children.push(id);
|
|
244
|
+
}
|
|
245
|
+
nodesMap.set(id, {
|
|
246
|
+
id,
|
|
247
|
+
parent,
|
|
248
|
+
kind: core.ElementKind.Group,
|
|
249
|
+
title: viewRule.title ?? "",
|
|
250
|
+
color: viewRule.color ?? "muted",
|
|
251
|
+
shape: "rectangle",
|
|
252
|
+
children: [],
|
|
253
|
+
inEdges: [],
|
|
254
|
+
outEdges: [],
|
|
255
|
+
level: 0,
|
|
256
|
+
depth: 0,
|
|
257
|
+
description: null,
|
|
258
|
+
technology: null,
|
|
259
|
+
tags: null,
|
|
260
|
+
links: null,
|
|
261
|
+
style: {
|
|
262
|
+
border: viewRule.border ?? "dashed",
|
|
263
|
+
opacity: viewRule.opacity ?? 0
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
for (const e of explicits) {
|
|
267
|
+
elementToGroup.set(e.id, id);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
return Array.from(elements).sort(core.compareByFqnHierarchically).reduce((map, { id, color, shape, style, kind, title, ...el }) => {
|
|
221
271
|
let parent = core.parentFqn(id);
|
|
222
272
|
let level = 0;
|
|
273
|
+
let parentNd;
|
|
223
274
|
while (parent) {
|
|
224
|
-
|
|
275
|
+
parentNd = map.get(parent);
|
|
225
276
|
if (parentNd) {
|
|
226
|
-
if (parentNd.children.length == 0) {
|
|
227
|
-
parentNd.depth = 1;
|
|
228
|
-
updateDepthOfAncestors(parentNd, map);
|
|
229
|
-
}
|
|
230
|
-
parentNd.children.push(id);
|
|
231
|
-
level = parentNd.level + 1;
|
|
232
277
|
break;
|
|
233
278
|
}
|
|
234
279
|
parent = core.parentFqn(parent);
|
|
235
280
|
}
|
|
281
|
+
if (!parentNd && elementToGroup.has(id)) {
|
|
282
|
+
parent = elementToGroup.get(id);
|
|
283
|
+
parentNd = map.get(parent);
|
|
284
|
+
}
|
|
285
|
+
if (parentNd) {
|
|
286
|
+
if (parentNd.children.length == 0) {
|
|
287
|
+
parentNd.depth = 1;
|
|
288
|
+
updateDepthOfAncestors(parentNd, map);
|
|
289
|
+
}
|
|
290
|
+
parentNd.children.push(id);
|
|
291
|
+
level = parentNd.level + 1;
|
|
292
|
+
}
|
|
236
293
|
const node = {
|
|
237
|
-
...el,
|
|
238
294
|
id,
|
|
239
295
|
parent,
|
|
240
|
-
|
|
296
|
+
kind,
|
|
297
|
+
title,
|
|
241
298
|
color: color ?? core.DefaultThemeColor,
|
|
242
299
|
shape: shape ?? core.DefaultElementShape,
|
|
243
300
|
children: [],
|
|
244
301
|
inEdges: [],
|
|
245
302
|
outEdges: [],
|
|
303
|
+
level,
|
|
304
|
+
...el,
|
|
246
305
|
style: {
|
|
247
306
|
...style
|
|
248
307
|
}
|
|
249
308
|
};
|
|
250
309
|
map.set(id, node);
|
|
251
310
|
return map;
|
|
252
|
-
},
|
|
311
|
+
}, nodesMap);
|
|
253
312
|
}
|
|
254
313
|
|
|
255
314
|
function buildElementNotations(nodes) {
|
|
@@ -321,9 +380,16 @@ function sortNodes({
|
|
|
321
380
|
nodes,
|
|
322
381
|
edges
|
|
323
382
|
}) {
|
|
324
|
-
if (
|
|
383
|
+
if (nodes.length < 2) {
|
|
325
384
|
return nodes;
|
|
326
385
|
}
|
|
386
|
+
if (edges.length === 0) {
|
|
387
|
+
return remeda.pipe(
|
|
388
|
+
nodes,
|
|
389
|
+
remeda.sort(core.compareByFqnHierarchically),
|
|
390
|
+
remeda.tap(sortChildren)
|
|
391
|
+
);
|
|
392
|
+
}
|
|
327
393
|
const g = new Graph({
|
|
328
394
|
compound: false,
|
|
329
395
|
directed: true,
|
|
@@ -358,15 +424,15 @@ function sortNodes({
|
|
|
358
424
|
if (sources.length === 0) {
|
|
359
425
|
sources = remeda.pipe(
|
|
360
426
|
nodes,
|
|
361
|
-
remeda.sort(core.compareByFqnHierarchically),
|
|
362
427
|
remeda.filter((n) => n.inEdges.length === 0 || n.parent === null),
|
|
428
|
+
remeda.sort(core.compareByFqnHierarchically),
|
|
363
429
|
remeda.map((n) => n.id)
|
|
364
430
|
);
|
|
365
431
|
}
|
|
366
432
|
const orderedIds = postorder(g, sources).reverse();
|
|
367
433
|
const sorted = orderedIds.map(getNode);
|
|
368
434
|
if (sorted.length < nodes.length) {
|
|
369
|
-
const unsorted = remeda.difference(nodes, sorted);
|
|
435
|
+
const unsorted = remeda.difference(nodes, sorted).sort(core.compareByFqnHierarchically);
|
|
370
436
|
sorted.push(...unsorted);
|
|
371
437
|
}
|
|
372
438
|
core.invariant(sorted.length === nodes.length, "Not all nodes were processed by graphlib");
|
|
@@ -453,7 +519,9 @@ function includeWildcardRef(_expr, where = NoFilter) {
|
|
|
453
519
|
])
|
|
454
520
|
];
|
|
455
521
|
for (const el of children) {
|
|
456
|
-
this.addEdges(this.graph.anyEdgesBetween(el, neighbours))
|
|
522
|
+
this.addEdges(this.graph.anyEdgesBetween(el, neighbours)).forEach((edge) => {
|
|
523
|
+
this.addImplicit(edge.source, edge.target);
|
|
524
|
+
});
|
|
457
525
|
}
|
|
458
526
|
if (!hasChildren && _elRoot) {
|
|
459
527
|
const edgesWithSiblings = this.graph.anyEdgesBetween(_elRoot, this.graph.siblings(root));
|
|
@@ -644,8 +712,9 @@ function includeIncomingExpr(expr, where) {
|
|
|
644
712
|
if (edges.length === 0) {
|
|
645
713
|
return;
|
|
646
714
|
}
|
|
647
|
-
this.addEdges(edges)
|
|
648
|
-
|
|
715
|
+
this.addEdges(edges).forEach((edge) => {
|
|
716
|
+
this.addImplicit(edge.target);
|
|
717
|
+
});
|
|
649
718
|
}
|
|
650
719
|
function excludeIncomingExpr(expr, where) {
|
|
651
720
|
let relations = filterRelations(edgesIncomingExpr.call(this, expr.incoming), where);
|
|
@@ -685,8 +754,9 @@ function includeOutgoingExpr(expr, where) {
|
|
|
685
754
|
if (edges.length === 0) {
|
|
686
755
|
return;
|
|
687
756
|
}
|
|
688
|
-
this.addEdges(edges)
|
|
689
|
-
|
|
757
|
+
this.addEdges(edges).forEach((edge) => {
|
|
758
|
+
this.addImplicit(edge.source);
|
|
759
|
+
});
|
|
690
760
|
}
|
|
691
761
|
function excludeOutgoingExpr(expr, where) {
|
|
692
762
|
const relations = filterRelations(edgesOutgoingExpr.call(this, expr.outgoing), where);
|
|
@@ -771,7 +841,9 @@ function includeRelationExpr(expr, where) {
|
|
|
771
841
|
if (expr.isBidirectional === true) {
|
|
772
842
|
edges.push(...this.graph.edgesBetween(targets, sources));
|
|
773
843
|
}
|
|
774
|
-
this.addEdges(filterEdges(edges, where))
|
|
844
|
+
this.addEdges(filterEdges(edges, where)).forEach((edge) => {
|
|
845
|
+
this.activeGroup.addImplicit(edge.source, edge.target);
|
|
846
|
+
});
|
|
775
847
|
}
|
|
776
848
|
function excludeRelationExpr(expr, where) {
|
|
777
849
|
const isSource = elementExprToPredicate(expr.source);
|
|
@@ -794,6 +866,50 @@ function compareEdges(a, b) {
|
|
|
794
866
|
{ source: b.source.id, target: b.target.id }
|
|
795
867
|
);
|
|
796
868
|
}
|
|
869
|
+
class NodesGroup {
|
|
870
|
+
constructor(id, viewRule, parent = null) {
|
|
871
|
+
this.id = id;
|
|
872
|
+
this.viewRule = viewRule;
|
|
873
|
+
this.parent = parent;
|
|
874
|
+
}
|
|
875
|
+
static kind = core.ElementKind.Group;
|
|
876
|
+
static root() {
|
|
877
|
+
return new NodesGroup("@root", { title: null, groupRules: [] });
|
|
878
|
+
}
|
|
879
|
+
static is(node) {
|
|
880
|
+
return node.kind === NodesGroup.kind;
|
|
881
|
+
}
|
|
882
|
+
explicits = /* @__PURE__ */ new Set();
|
|
883
|
+
implicits = /* @__PURE__ */ new Set();
|
|
884
|
+
/**
|
|
885
|
+
* Add element explicitly
|
|
886
|
+
* Included even without relationships
|
|
887
|
+
*/
|
|
888
|
+
addElement(...el) {
|
|
889
|
+
for (const r of el) {
|
|
890
|
+
this.explicits.add(r);
|
|
891
|
+
this.implicits.add(r);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Add element implicitly
|
|
896
|
+
* Included if only has relationships
|
|
897
|
+
*/
|
|
898
|
+
addImplicit(...el) {
|
|
899
|
+
for (const r of el) {
|
|
900
|
+
this.implicits.add(r);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
excludeElement(...excludes) {
|
|
904
|
+
for (const el of excludes) {
|
|
905
|
+
this.explicits.delete(el);
|
|
906
|
+
this.implicits.delete(el);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
isEmpty() {
|
|
910
|
+
return this.explicits.size === 0 && this.implicits.size === 0;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
797
913
|
class ComputeCtx {
|
|
798
914
|
constructor(view, graph) {
|
|
799
915
|
this.view = view;
|
|
@@ -803,6 +919,16 @@ class ComputeCtx {
|
|
|
803
919
|
explicits = /* @__PURE__ */ new Set();
|
|
804
920
|
implicits = /* @__PURE__ */ new Set();
|
|
805
921
|
ctxEdges = [];
|
|
922
|
+
/**
|
|
923
|
+
* Root group - not included in the groups
|
|
924
|
+
* But used to accumulate elements that are not in any group
|
|
925
|
+
*/
|
|
926
|
+
__rootGroup = NodesGroup.root();
|
|
927
|
+
groups = [];
|
|
928
|
+
activeGroupStack = [];
|
|
929
|
+
get activeGroup() {
|
|
930
|
+
return this.activeGroupStack[0] ?? this.__rootGroup;
|
|
931
|
+
}
|
|
806
932
|
static elementView(view, graph) {
|
|
807
933
|
return new ComputeCtx(view, graph).compute();
|
|
808
934
|
}
|
|
@@ -814,14 +940,30 @@ class ComputeCtx {
|
|
|
814
940
|
rules,
|
|
815
941
|
...view
|
|
816
942
|
} = this.view;
|
|
817
|
-
const viewPredicates = rules.filter(core.isViewRulePredicate);
|
|
943
|
+
const viewPredicates = rules.filter(remeda.anyPass([core.isViewRulePredicate, core.isViewRuleGroup]));
|
|
818
944
|
if (this.root && viewPredicates.length == 0) {
|
|
819
945
|
this.addElement(this.graph.element(this.root));
|
|
820
946
|
}
|
|
821
947
|
this.processPredicates(viewPredicates);
|
|
822
948
|
this.removeRedundantImplicitEdges();
|
|
949
|
+
if (this.groups.length > 0) {
|
|
950
|
+
this.cleanGroupElements();
|
|
951
|
+
}
|
|
823
952
|
const elements = [...this.includedElements];
|
|
824
|
-
const nodesMap = buildComputeNodes(elements);
|
|
953
|
+
const nodesMap = buildComputeNodes(elements, this.groups);
|
|
954
|
+
const ancestorsOf = (node) => {
|
|
955
|
+
const ancestors = [];
|
|
956
|
+
let parent = node.parent;
|
|
957
|
+
while (parent) {
|
|
958
|
+
const parentNode = nodesMap.get(parent);
|
|
959
|
+
if (!parentNode) {
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
ancestors.push(parentNode);
|
|
963
|
+
parent = parentNode.parent;
|
|
964
|
+
}
|
|
965
|
+
return ancestors;
|
|
966
|
+
};
|
|
825
967
|
const edgesMap = /* @__PURE__ */ new Map();
|
|
826
968
|
const edges = this.computeEdges();
|
|
827
969
|
for (const edge of edges) {
|
|
@@ -830,25 +972,37 @@ class ComputeCtx {
|
|
|
830
972
|
const target = nodesMap.get(edge.target);
|
|
831
973
|
core.invariant(source, `Source node ${edge.source} not found`);
|
|
832
974
|
core.invariant(target, `Target node ${edge.target} not found`);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
975
|
+
const sourceAncestors = ancestorsOf(source);
|
|
976
|
+
const targetAncestors = ancestorsOf(target);
|
|
977
|
+
const edgeParent = remeda.last(
|
|
978
|
+
core.commonHead(
|
|
979
|
+
remeda.reverse(sourceAncestors),
|
|
980
|
+
remeda.reverse(targetAncestors)
|
|
981
|
+
)
|
|
982
|
+
);
|
|
983
|
+
edge.parent = edgeParent?.id ?? null;
|
|
836
984
|
source.outEdges.push(edge.id);
|
|
837
985
|
target.inEdges.push(edge.id);
|
|
838
|
-
for (const sourceAncestor of
|
|
839
|
-
if (sourceAncestor ===
|
|
986
|
+
for (const sourceAncestor of sourceAncestors) {
|
|
987
|
+
if (sourceAncestor === edgeParent) {
|
|
840
988
|
break;
|
|
841
989
|
}
|
|
842
|
-
|
|
990
|
+
sourceAncestor.outEdges.push(edge.id);
|
|
843
991
|
}
|
|
844
|
-
for (const targetAncestor of
|
|
845
|
-
if (targetAncestor ===
|
|
992
|
+
for (const targetAncestor of targetAncestors) {
|
|
993
|
+
if (targetAncestor === edgeParent) {
|
|
846
994
|
break;
|
|
847
995
|
}
|
|
848
|
-
|
|
996
|
+
targetAncestor.inEdges.push(edge.id);
|
|
849
997
|
}
|
|
850
998
|
}
|
|
851
|
-
|
|
999
|
+
let initialSort = elements.map((e) => core.nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`));
|
|
1000
|
+
if (this.groups.length > 0) {
|
|
1001
|
+
initialSort = remeda.concat(
|
|
1002
|
+
this.groups.map((g) => core.nonNullable(nodesMap.get(g.id), `Node ${g.id} not found in nodesMap`)),
|
|
1003
|
+
initialSort
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
852
1006
|
const nodes = applyCustomElementProperties(
|
|
853
1007
|
rules,
|
|
854
1008
|
applyViewRuleStyles(
|
|
@@ -994,6 +1148,7 @@ class ComputeCtx {
|
|
|
994
1148
|
return this.ctxEdges;
|
|
995
1149
|
}
|
|
996
1150
|
addEdges(edges) {
|
|
1151
|
+
const added = [];
|
|
997
1152
|
for (const e of edges) {
|
|
998
1153
|
if (!remeda.hasAtLeast(e.relations, 1)) {
|
|
999
1154
|
continue;
|
|
@@ -1003,10 +1158,13 @@ class ComputeCtx {
|
|
|
1003
1158
|
);
|
|
1004
1159
|
if (existing) {
|
|
1005
1160
|
existing.relations = remeda.unique([...existing.relations, ...e.relations]);
|
|
1161
|
+
added.push(existing);
|
|
1006
1162
|
continue;
|
|
1007
1163
|
}
|
|
1164
|
+
added.push(e);
|
|
1008
1165
|
this.ctxEdges.push(e);
|
|
1009
1166
|
}
|
|
1167
|
+
return added;
|
|
1010
1168
|
}
|
|
1011
1169
|
/**
|
|
1012
1170
|
* Add element explicitly
|
|
@@ -1014,6 +1172,14 @@ class ComputeCtx {
|
|
|
1014
1172
|
*/
|
|
1015
1173
|
addElement(...el) {
|
|
1016
1174
|
for (const r of el) {
|
|
1175
|
+
if (!this.explicits.has(r)) {
|
|
1176
|
+
this.activeGroup.addElement(r);
|
|
1177
|
+
} else if (this.activeGroup !== this.__rootGroup) {
|
|
1178
|
+
this.groups.forEach((g) => {
|
|
1179
|
+
g.implicits.delete(r);
|
|
1180
|
+
});
|
|
1181
|
+
this.activeGroup.addImplicit(r);
|
|
1182
|
+
}
|
|
1017
1183
|
this.explicits.add(r);
|
|
1018
1184
|
this.implicits.add(r);
|
|
1019
1185
|
}
|
|
@@ -1025,7 +1191,13 @@ class ComputeCtx {
|
|
|
1025
1191
|
addImplicit(...el) {
|
|
1026
1192
|
for (const r of el) {
|
|
1027
1193
|
this.implicits.add(r);
|
|
1194
|
+
if (this.activeGroup !== this.__rootGroup) {
|
|
1195
|
+
this.groups.forEach((g) => {
|
|
1196
|
+
g.implicits.delete(r);
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1028
1199
|
}
|
|
1200
|
+
this.activeGroup.addImplicit(...el);
|
|
1029
1201
|
}
|
|
1030
1202
|
excludeElement(...excludes) {
|
|
1031
1203
|
for (const el of excludes) {
|
|
@@ -1033,12 +1205,11 @@ class ComputeCtx {
|
|
|
1033
1205
|
this.explicits.delete(el);
|
|
1034
1206
|
this.implicits.delete(el);
|
|
1035
1207
|
}
|
|
1208
|
+
this.__rootGroup.excludeElement(...excludes);
|
|
1209
|
+
this.groups.forEach((g) => {
|
|
1210
|
+
g.excludeElement(...excludes);
|
|
1211
|
+
});
|
|
1036
1212
|
}
|
|
1037
|
-
// protected excludeImplicit(...excludes: Element[]) {
|
|
1038
|
-
// for (const el of excludes) {
|
|
1039
|
-
// this.implicits.delete(el)
|
|
1040
|
-
// }
|
|
1041
|
-
// }
|
|
1042
1213
|
excludeRelation(...relations) {
|
|
1043
1214
|
if (relations.length === 0) {
|
|
1044
1215
|
return;
|
|
@@ -1070,11 +1241,15 @@ class ComputeCtx {
|
|
|
1070
1241
|
const remaining = this.includedElements;
|
|
1071
1242
|
if (remaining.size === 0) {
|
|
1072
1243
|
this.implicits.clear();
|
|
1244
|
+
this.__rootGroup.implicits.clear();
|
|
1245
|
+
this.groups.forEach((g) => g.implicits.clear());
|
|
1073
1246
|
return;
|
|
1074
1247
|
}
|
|
1075
1248
|
for (const el of excludedImplicits) {
|
|
1076
1249
|
if (!remaining.has(el)) {
|
|
1077
1250
|
this.implicits.delete(el);
|
|
1251
|
+
this.__rootGroup.implicits.delete(el);
|
|
1252
|
+
this.groups.forEach((g) => g.implicits.delete(el));
|
|
1078
1253
|
}
|
|
1079
1254
|
}
|
|
1080
1255
|
}
|
|
@@ -1082,6 +1257,9 @@ class ComputeCtx {
|
|
|
1082
1257
|
this.explicits.clear();
|
|
1083
1258
|
this.implicits.clear();
|
|
1084
1259
|
this.ctxEdges = [];
|
|
1260
|
+
this.__rootGroup = NodesGroup.root();
|
|
1261
|
+
this.groups = [];
|
|
1262
|
+
this.activeGroupStack = [];
|
|
1085
1263
|
}
|
|
1086
1264
|
// Filter out edges if there are edges between descendants
|
|
1087
1265
|
// i.e. remove implicit edges, derived from childs
|
|
@@ -1124,8 +1302,41 @@ class ComputeCtx {
|
|
|
1124
1302
|
return acc;
|
|
1125
1303
|
}, []);
|
|
1126
1304
|
}
|
|
1305
|
+
cleanGroupElements() {
|
|
1306
|
+
const unprocessed = new Set(remeda.difference(
|
|
1307
|
+
[...this.includedElements],
|
|
1308
|
+
[...this.__rootGroup.explicits]
|
|
1309
|
+
));
|
|
1310
|
+
for (const group of this.groups) {
|
|
1311
|
+
const explicits = [...group.explicits];
|
|
1312
|
+
group.explicits.clear();
|
|
1313
|
+
for (const el of explicits) {
|
|
1314
|
+
if (unprocessed.delete(el)) {
|
|
1315
|
+
group.explicits.add(el);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
for (const group of this.groups) {
|
|
1320
|
+
for (const el of group.implicits) {
|
|
1321
|
+
if (unprocessed.delete(el)) {
|
|
1322
|
+
group.explicits.add(el);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
group.implicits.clear();
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1127
1328
|
processPredicates(viewRules) {
|
|
1128
1329
|
for (const rule of viewRules) {
|
|
1330
|
+
if (core.isViewRuleGroup(rule)) {
|
|
1331
|
+
const parent = remeda.first(this.activeGroupStack);
|
|
1332
|
+
const groupId = `@gr${this.groups.length + 1}`;
|
|
1333
|
+
const group = new NodesGroup(groupId, rule, parent?.id ?? null);
|
|
1334
|
+
this.groups.push(group);
|
|
1335
|
+
this.activeGroupStack.unshift(group);
|
|
1336
|
+
this.processPredicates(rule.groupRules);
|
|
1337
|
+
this.activeGroupStack.shift();
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1129
1340
|
const isInclude = "include" in rule;
|
|
1130
1341
|
const exprs = rule.include ?? rule.exclude;
|
|
1131
1342
|
for (const expr of exprs) {
|