@likec4/language-server 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.mts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.mjs +1 -1
- 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 +1 -1
- package/dist/model-graph/index.cjs +1 -1
- package/dist/model-graph/index.mjs +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.mjs +1 -1
- package/dist/shared/{language-server.86lmJ8ZN.d.cts → language-server.CjFzaJwI.d.cts} +42 -13
- package/dist/shared/{language-server.RjhrBZS0.d.ts → language-server.CtKHXJDD.d.ts} +42 -13
- package/dist/shared/{language-server.CFTY6j4e.d.mts → language-server.D-84I33F.d.mts} +42 -13
- package/dist/shared/{language-server.Q-wtPShM.mjs → language-server.DBJJUUgF.mjs} +485 -108
- package/dist/shared/{language-server.CCB4ESN5.mjs → language-server.DtBRb9os.mjs} +166 -116
- package/dist/shared/{language-server.D0bOlrCi.cjs → language-server.DwyCJvXm.cjs} +164 -114
- package/dist/shared/{language-server.B1TZgyoH.cjs → language-server.JWkqVjGv.cjs} +481 -104
- package/package.json +6 -5
- package/src/ast.ts +8 -6
- package/src/formatting/LikeC4Formatter.ts +388 -0
- package/src/formatting/utils.ts +26 -0
- package/src/generated/ast.ts +104 -10
- package/src/generated/grammar.ts +1 -1
- package/src/like-c4.langium +34 -7
- package/src/lsp/DocumentLinkProvider.ts +27 -15
- package/src/lsp/SemanticTokenProvider.ts +1 -1
- package/src/lsp/index.ts +1 -1
- package/src/model/fqn-index.ts +0 -1
- package/src/model/model-builder.ts +43 -32
- package/src/model/model-parser.ts +43 -21
- package/src/model-graph/compute-view/compute.ts +104 -78
- package/src/model-graph/compute-view/predicates.ts +3 -5
- package/src/model-graph/dynamic-view/compute.ts +96 -60
- package/src/model-graph/utils/buildElementNotations.ts +1 -1
- package/src/module.ts +6 -9
- package/src/test/testServices.ts +27 -7
- package/src/validation/index.ts +2 -1
- package/src/validation/property-checks.ts +13 -1
- package/src/validation/specification.ts +3 -3
- package/src/view-utils/resolve-relative-paths.ts +14 -17
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, isViewRuleStyle, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant, nonNullable, isAncestor, isViewRulePredicate, ancestorsFqn, isViewRuleAutoLayout, isScopedElementView, commonAncestor,
|
|
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,
|
|
1
|
+
import { Expr, whereOperatorAsPredicate, parentFqn, nonexhaustive, isViewRuleStyle, compareByFqnHierarchically, DefaultThemeColor, DefaultElementShape, compareRelations, invariant, nonNullable, isAncestor, isViewRulePredicate, ancestorsFqn, isViewRuleAutoLayout, isScopedElementView, commonAncestor, StepEdgeId, isDynamicViewParallelSteps, isDynamicViewIncludeRule, DefaultRelationshipColor, DefaultLineStyle, DefaultArrowType, isSameHierarchy } from '@likec4/core';
|
|
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, reduce, only, isNonNull, isString, isArray } from 'remeda';
|
|
3
3
|
import graphlib, { Graph } from '@dagrejs/graphlib';
|
|
4
4
|
import objectHash from 'object-hash';
|
|
5
5
|
|
|
@@ -288,8 +288,8 @@ function buildElementNotations(nodes) {
|
|
|
288
288
|
}))
|
|
289
289
|
),
|
|
290
290
|
sortBy(
|
|
291
|
-
prop("title"),
|
|
292
291
|
prop("shape"),
|
|
292
|
+
prop("title"),
|
|
293
293
|
[
|
|
294
294
|
(n) => n.kinds.length,
|
|
295
295
|
"desc"
|
|
@@ -614,13 +614,11 @@ const filterEdges = (edges, where) => {
|
|
|
614
614
|
);
|
|
615
615
|
};
|
|
616
616
|
const filterRelations = (edges, where) => {
|
|
617
|
-
if (!where) {
|
|
618
|
-
return edges.flatMap((e) => e.relations);
|
|
619
|
-
}
|
|
620
617
|
return pipe(
|
|
621
618
|
edges,
|
|
622
619
|
flatMap((e) => e.relations),
|
|
623
|
-
filter(where)
|
|
620
|
+
where ? filter(where) : Identity,
|
|
621
|
+
unique()
|
|
624
622
|
);
|
|
625
623
|
};
|
|
626
624
|
function includeIncomingExpr(expr, where) {
|
|
@@ -868,7 +866,7 @@ class ComputeCtx {
|
|
|
868
866
|
computeEdges() {
|
|
869
867
|
return this.ctxEdges.map((e) => {
|
|
870
868
|
invariant(hasAtLeast(e.relations, 1), "Edge must have at least one relation");
|
|
871
|
-
const relations = e.relations
|
|
869
|
+
const relations = sort(e.relations, compareRelations);
|
|
872
870
|
const source = e.source.id;
|
|
873
871
|
const target = e.target.id;
|
|
874
872
|
const edge = {
|
|
@@ -880,48 +878,62 @@ class ComputeCtx {
|
|
|
880
878
|
relations: relations.map((r) => r.id)
|
|
881
879
|
};
|
|
882
880
|
let relation;
|
|
883
|
-
|
|
884
|
-
relation = relations[0];
|
|
885
|
-
} else {
|
|
886
|
-
relation = relations.find((r) => r.source === source && r.target === target);
|
|
887
|
-
}
|
|
881
|
+
relation = relations.length === 1 ? relations[0] : relations.find((r) => r.source === source && r.target === target);
|
|
888
882
|
if (!relation) {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
883
|
+
const allprops = pipe(
|
|
884
|
+
relations,
|
|
885
|
+
reduce((acc, r) => {
|
|
886
|
+
if (isTruthy(r.title) && !acc.title.includes(r.title)) {
|
|
887
|
+
acc.title.push(r.title);
|
|
888
|
+
}
|
|
889
|
+
if (isTruthy(r.description) && !acc.description.includes(r.description)) {
|
|
890
|
+
acc.description.push(r.description);
|
|
891
|
+
}
|
|
892
|
+
if (isTruthy(r.technology) && !acc.technology.includes(r.technology)) {
|
|
893
|
+
acc.technology.push(r.technology);
|
|
894
|
+
}
|
|
895
|
+
if (isTruthy(r.kind) && !acc.kind.includes(r.kind)) {
|
|
896
|
+
acc.kind.push(r.kind);
|
|
897
|
+
}
|
|
898
|
+
if (isTruthy(r.color) && !acc.color.includes(r.color)) {
|
|
899
|
+
acc.color.push(r.color);
|
|
900
|
+
}
|
|
901
|
+
if (isTruthy(r.line) && !acc.line.includes(r.line)) {
|
|
902
|
+
acc.line.push(r.line);
|
|
903
|
+
}
|
|
904
|
+
if (isTruthy(r.head) && !acc.head.includes(r.head)) {
|
|
905
|
+
acc.head.push(r.head);
|
|
906
|
+
}
|
|
907
|
+
if (isTruthy(r.tail) && !acc.tail.includes(r.tail)) {
|
|
908
|
+
acc.tail.push(r.tail);
|
|
909
|
+
}
|
|
910
|
+
if (isTruthy(r.navigateTo) && !acc.navigateTo.includes(r.navigateTo)) {
|
|
911
|
+
acc.navigateTo.push(r.navigateTo);
|
|
912
|
+
}
|
|
913
|
+
return acc;
|
|
914
|
+
}, {
|
|
915
|
+
title: [],
|
|
916
|
+
description: [],
|
|
917
|
+
technology: [],
|
|
918
|
+
kind: [],
|
|
919
|
+
head: [],
|
|
920
|
+
tail: [],
|
|
921
|
+
color: [],
|
|
922
|
+
line: [],
|
|
923
|
+
navigateTo: []
|
|
924
|
+
})
|
|
925
|
+
);
|
|
926
|
+
relation = {
|
|
927
|
+
title: only(allprops.title) ?? "[...]",
|
|
928
|
+
description: only(allprops.description),
|
|
929
|
+
technology: only(allprops.technology),
|
|
930
|
+
kind: only(allprops.kind),
|
|
931
|
+
head: only(allprops.head),
|
|
932
|
+
tail: only(allprops.tail),
|
|
933
|
+
color: only(allprops.color),
|
|
934
|
+
line: only(allprops.line),
|
|
935
|
+
navigateTo: only(allprops.navigateTo)
|
|
936
|
+
};
|
|
925
937
|
}
|
|
926
938
|
const tags = unique(flatMap(relations, (r) => r.tags ?? []));
|
|
927
939
|
return Object.assign(
|
|
@@ -934,6 +946,7 @@ class ComputeCtx {
|
|
|
934
946
|
relation.line && { line: relation.line },
|
|
935
947
|
relation.head && { head: relation.head },
|
|
936
948
|
relation.tail && { tail: relation.tail },
|
|
949
|
+
relation.navigateTo && { navigateTo: relation.navigateTo },
|
|
937
950
|
hasAtLeast(tags, 1) && { tags }
|
|
938
951
|
);
|
|
939
952
|
});
|
|
@@ -995,28 +1008,39 @@ class ComputeCtx {
|
|
|
995
1008
|
this.implicits.delete(el);
|
|
996
1009
|
}
|
|
997
1010
|
}
|
|
998
|
-
excludeImplicit(...excludes) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
}
|
|
1011
|
+
// protected excludeImplicit(...excludes: Element[]) {
|
|
1012
|
+
// for (const el of excludes) {
|
|
1013
|
+
// this.implicits.delete(el)
|
|
1014
|
+
// }
|
|
1015
|
+
// }
|
|
1003
1016
|
excludeRelation(...relations) {
|
|
1017
|
+
if (relations.length === 0) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1004
1020
|
const excludedImplicits = /* @__PURE__ */ new Set();
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1021
|
+
const ctxEdges = pipe(
|
|
1022
|
+
this.ctxEdges,
|
|
1023
|
+
map((edge) => {
|
|
1024
|
+
const edgerelations = edge.relations.filter((r) => !relations.includes(r));
|
|
1025
|
+
if (edgerelations.length === 0) {
|
|
1009
1026
|
excludedImplicits.add(edge.source);
|
|
1010
1027
|
excludedImplicits.add(edge.target);
|
|
1011
|
-
|
|
1012
|
-
continue;
|
|
1028
|
+
return null;
|
|
1013
1029
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1030
|
+
if (edgerelations.length !== edge.relations.length) {
|
|
1031
|
+
return {
|
|
1032
|
+
...edge,
|
|
1033
|
+
relations: edgerelations
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
return edge;
|
|
1037
|
+
}),
|
|
1038
|
+
filter(isNonNull)
|
|
1039
|
+
);
|
|
1017
1040
|
if (excludedImplicits.size === 0) {
|
|
1018
1041
|
return;
|
|
1019
1042
|
}
|
|
1043
|
+
this.ctxEdges = ctxEdges;
|
|
1020
1044
|
const remaining = this.includedElements;
|
|
1021
1045
|
if (remaining.size === 0) {
|
|
1022
1046
|
this.implicits.clear();
|
|
@@ -1160,7 +1184,7 @@ class ComputeCtx {
|
|
|
1160
1184
|
if (isTruthy(relation.technology)) {
|
|
1161
1185
|
labelParts.push(`[${relation.technology}]`);
|
|
1162
1186
|
}
|
|
1163
|
-
return labelParts.length > 0
|
|
1187
|
+
return labelParts.length > 0 ? { label: labelParts.join("\n") } : {};
|
|
1164
1188
|
}
|
|
1165
1189
|
}
|
|
1166
1190
|
|
|
@@ -1193,6 +1217,38 @@ class DynamicViewComputeCtx {
|
|
|
1193
1217
|
static compute(view, graph) {
|
|
1194
1218
|
return new DynamicViewComputeCtx(view, graph).compute();
|
|
1195
1219
|
}
|
|
1220
|
+
addStep({
|
|
1221
|
+
source: stepSource,
|
|
1222
|
+
target: stepTarget,
|
|
1223
|
+
title: stepTitle,
|
|
1224
|
+
isBackward,
|
|
1225
|
+
navigateTo: stepNavigateTo,
|
|
1226
|
+
...step
|
|
1227
|
+
}, index, parent) {
|
|
1228
|
+
const id = parent ? StepEdgeId(parent, index) : StepEdgeId(index);
|
|
1229
|
+
const source = this.graph.element(stepSource);
|
|
1230
|
+
const target = this.graph.element(stepTarget);
|
|
1231
|
+
this.explicits.add(source);
|
|
1232
|
+
this.explicits.add(target);
|
|
1233
|
+
const {
|
|
1234
|
+
title,
|
|
1235
|
+
relations,
|
|
1236
|
+
tags,
|
|
1237
|
+
navigateTo: derivedNavigateTo
|
|
1238
|
+
} = this.findRelations(source, target);
|
|
1239
|
+
const navigateTo = isTruthy(stepNavigateTo) && stepNavigateTo !== this.view.id ? stepNavigateTo : derivedNavigateTo;
|
|
1240
|
+
this.steps.push({
|
|
1241
|
+
id,
|
|
1242
|
+
...step,
|
|
1243
|
+
source,
|
|
1244
|
+
target,
|
|
1245
|
+
title: stepTitle ?? title,
|
|
1246
|
+
relations: relations ?? [],
|
|
1247
|
+
isBackward: isBackward ?? false,
|
|
1248
|
+
...navigateTo ? { navigateTo } : {},
|
|
1249
|
+
...tags ? { tags } : {}
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1196
1252
|
compute() {
|
|
1197
1253
|
const {
|
|
1198
1254
|
docUri: _docUri,
|
|
@@ -1201,27 +1257,21 @@ class DynamicViewComputeCtx {
|
|
|
1201
1257
|
steps: viewSteps,
|
|
1202
1258
|
...view
|
|
1203
1259
|
} = this.view;
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
target,
|
|
1220
|
-
title: isTruthy(stepTitle) ? stepTitle : title,
|
|
1221
|
-
relations: relations ?? [],
|
|
1222
|
-
isBackward: isBackward ?? false,
|
|
1223
|
-
...tags ? { tags } : {}
|
|
1224
|
-
});
|
|
1260
|
+
let stepNum = 1;
|
|
1261
|
+
for (const step of viewSteps) {
|
|
1262
|
+
if (isDynamicViewParallelSteps(step)) {
|
|
1263
|
+
if (step.__parallel.length === 0) {
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
if (step.__parallel.length === 1) {
|
|
1267
|
+
this.addStep(step.__parallel[0], stepNum);
|
|
1268
|
+
} else {
|
|
1269
|
+
step.__parallel.forEach((s, i) => this.addStep(s, i + 1, stepNum));
|
|
1270
|
+
}
|
|
1271
|
+
} else {
|
|
1272
|
+
this.addStep(step, stepNum);
|
|
1273
|
+
}
|
|
1274
|
+
stepNum++;
|
|
1225
1275
|
}
|
|
1226
1276
|
for (const rule of rules) {
|
|
1227
1277
|
if (isDynamicViewIncludeRule(rule)) {
|
|
@@ -1233,12 +1283,10 @@ class DynamicViewComputeCtx {
|
|
|
1233
1283
|
}
|
|
1234
1284
|
const elements = [...this.explicits];
|
|
1235
1285
|
const nodesMap = buildComputeNodes(elements);
|
|
1236
|
-
const edges = this.steps.map(({ source, target, relations, title, isBackward, ...step }
|
|
1286
|
+
const edges = this.steps.map(({ source, target, relations, title, isBackward, ...step }) => {
|
|
1237
1287
|
const sourceNode = nonNullable(nodesMap.get(source.id), `Source node ${source.id} not found`);
|
|
1238
1288
|
const targetNode = nonNullable(nodesMap.get(target.id), `Target node ${target.id} not found`);
|
|
1239
|
-
const stepNum = index + 1;
|
|
1240
1289
|
const edge = {
|
|
1241
|
-
id: StepEdgeId(stepNum),
|
|
1242
1290
|
parent: commonAncestor(source.id, target.id),
|
|
1243
1291
|
source: source.id,
|
|
1244
1292
|
target: target.id,
|
|
@@ -1295,41 +1343,43 @@ class DynamicViewComputeCtx {
|
|
|
1295
1343
|
}
|
|
1296
1344
|
findRelations(source, target) {
|
|
1297
1345
|
const relationships = unique(this.graph.edgesBetween(source, target).flatMap((e) => e.relations));
|
|
1298
|
-
const alltags = unique(relationships.flatMap((r) => r.tags ?? []));
|
|
1299
|
-
const tags = hasAtLeast(alltags, 1) ? alltags : null;
|
|
1300
|
-
const relations = hasAtLeast(relationships, 1) ? map(relationships, (r) => r.id) : null;
|
|
1301
1346
|
if (relationships.length === 0) {
|
|
1302
1347
|
return {
|
|
1303
1348
|
title: null,
|
|
1304
|
-
tags,
|
|
1305
|
-
relations
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
let relation;
|
|
1309
|
-
if (relationships.length === 1) {
|
|
1310
|
-
relation = relationships[0];
|
|
1311
|
-
} else {
|
|
1312
|
-
relation = relationships.find((r) => r.source === source.id && r.target === target.id);
|
|
1313
|
-
}
|
|
1314
|
-
if (relation && isTruthy(relation.title)) {
|
|
1315
|
-
return {
|
|
1316
|
-
title: relation.title,
|
|
1317
|
-
tags,
|
|
1318
|
-
relations
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
const labels = unique(relationships.flatMap((r) => isTruthy(r.title) ? r.title : []));
|
|
1322
|
-
if (labels.length === 1) {
|
|
1323
|
-
return {
|
|
1324
|
-
title: labels[0],
|
|
1325
|
-
tags,
|
|
1326
|
-
relations
|
|
1349
|
+
tags: null,
|
|
1350
|
+
relations: null,
|
|
1351
|
+
navigateTo: null
|
|
1327
1352
|
};
|
|
1328
1353
|
}
|
|
1354
|
+
const alltags = pipe(
|
|
1355
|
+
relationships,
|
|
1356
|
+
flatMap((r) => r.tags),
|
|
1357
|
+
filter(isTruthy),
|
|
1358
|
+
unique()
|
|
1359
|
+
);
|
|
1360
|
+
const tags = hasAtLeast(alltags, 1) ? alltags : null;
|
|
1361
|
+
const relations = hasAtLeast(relationships, 1) ? map(relationships, (r) => r.id) : null;
|
|
1362
|
+
const relation = only(relationships) || relationships.find((r) => r.source === source.id && r.target === target.id);
|
|
1363
|
+
const title = isTruthy(relation?.title) ? relation.title : pipe(
|
|
1364
|
+
relationships,
|
|
1365
|
+
map((r) => r.title),
|
|
1366
|
+
filter(isTruthy),
|
|
1367
|
+
unique(),
|
|
1368
|
+
only()
|
|
1369
|
+
);
|
|
1370
|
+
const navigateTo = !!relation?.navigateTo && relation.navigateTo !== this.view.id ? relation.navigateTo : pipe(
|
|
1371
|
+
relationships,
|
|
1372
|
+
map((r) => r.navigateTo),
|
|
1373
|
+
filter(isTruthy),
|
|
1374
|
+
filter((v) => v !== this.view.id),
|
|
1375
|
+
unique(),
|
|
1376
|
+
only()
|
|
1377
|
+
);
|
|
1329
1378
|
return {
|
|
1330
|
-
title: null,
|
|
1379
|
+
title: title ?? null,
|
|
1331
1380
|
tags,
|
|
1332
|
-
relations
|
|
1381
|
+
relations,
|
|
1382
|
+
navigateTo: navigateTo ?? null
|
|
1333
1383
|
};
|
|
1334
1384
|
}
|
|
1335
1385
|
}
|