@likec4/language-server 1.15.1 → 1.17.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.
Files changed (49) hide show
  1. package/contrib/likec4.tmLanguage.json +1 -1
  2. package/dist/browser.cjs +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.mts +2 -2
  5. package/dist/browser.d.ts +2 -2
  6. package/dist/browser.mjs +2 -2
  7. package/dist/index.cjs +35 -5
  8. package/dist/index.d.cts +17 -4
  9. package/dist/index.d.mts +17 -4
  10. package/dist/index.d.ts +17 -4
  11. package/dist/index.mjs +34 -5
  12. package/dist/model-graph/index.cjs +1 -1
  13. package/dist/model-graph/index.d.cts +4 -2
  14. package/dist/model-graph/index.d.mts +4 -2
  15. package/dist/model-graph/index.d.ts +4 -2
  16. package/dist/model-graph/index.mjs +1 -1
  17. package/dist/shared/{language-server.BuChFlda.mjs → language-server.B8qSDsWW.mjs} +211 -96
  18. package/dist/shared/language-server.BGGRJRnr.d.mts +1338 -0
  19. package/dist/shared/{language-server.BT4WTbFI.mjs → language-server.BXFhlTPo.mjs} +139 -76
  20. package/dist/shared/{language-server.DfMwkd2l.d.mts → language-server.BgDKnNok.d.mts} +45 -11
  21. package/dist/shared/language-server.Bmpq16Gw.d.ts +1338 -0
  22. package/dist/shared/language-server.C1ZfM22X.d.cts +1338 -0
  23. package/dist/shared/{language-server.U2piOAVt.d.cts → language-server.DJo88TnT.d.cts} +45 -11
  24. package/dist/shared/{language-server.DfjkvknB.cjs → language-server.DZRuJVSg.cjs} +209 -94
  25. package/dist/shared/{language-server.B8s2wfT_.cjs → language-server.N8HLDQqz.cjs} +137 -74
  26. package/dist/shared/{language-server.j-ShR6as.d.ts → language-server.PEjk7U9s.d.ts} +45 -11
  27. package/package.json +7 -7
  28. package/src/LikeC4FileSystem.ts +36 -0
  29. package/src/Rpc.ts +2 -2
  30. package/src/ast.ts +11 -3
  31. package/src/generated/ast.ts +112 -11
  32. package/src/generated/grammar.ts +1 -1
  33. package/src/index.ts +3 -3
  34. package/src/like-c4.langium +20 -1
  35. package/src/lsp/SemanticTokenProvider.ts +26 -8
  36. package/src/model/fqn-computation.ts +6 -2
  37. package/src/model/model-builder.ts +25 -30
  38. package/src/model/model-parser.ts +91 -18
  39. package/src/model-graph/LikeC4ModelGraph.ts +22 -11
  40. package/src/model-graph/compute-view/__test__/fixture.ts +69 -19
  41. package/src/model-graph/compute-view/compute.ts +85 -73
  42. package/src/model-graph/dynamic-view/compute.ts +12 -2
  43. package/src/model-graph/utils/applyCustomElementProperties.ts +1 -3
  44. package/src/model-graph/utils/applyViewRuleStyles.ts +0 -4
  45. package/src/references/scope-computation.ts +10 -0
  46. package/src/validation/index.ts +3 -0
  47. package/src/validation/specification.ts +21 -0
  48. package/src/view-utils/index.ts +0 -1
  49. package/src/view-utils/resolve-global-rules.ts +66 -50
@@ -3,6 +3,7 @@
3
3
  const core = require('@likec4/core');
4
4
  const remeda = require('remeda');
5
5
  const dagre = require('@dagrejs/dagre');
6
+ const log = require('@likec4/log');
6
7
  const objectHash = require('object-hash');
7
8
 
8
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -10,6 +11,51 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
10
11
  const dagre__default = /*#__PURE__*/_interopDefaultCompat(dagre);
11
12
  const objectHash__default = /*#__PURE__*/_interopDefaultCompat(objectHash);
12
13
 
14
+ function resolveGlobalRulesInElementView(view, globals) {
15
+ return view.rules.reduce((acc, rule) => {
16
+ if (core.isViewRuleGlobalPredicateRef(rule)) {
17
+ const globalPredicates = globals.predicates[rule.predicateId];
18
+ if (remeda.isNullish(globalPredicates)) {
19
+ log.logger.warn(`Global predicate not found: ${rule.predicateId}`);
20
+ return acc;
21
+ }
22
+ return acc.concat(globalPredicates);
23
+ }
24
+ if (core.isViewRuleGlobalStyle(rule)) {
25
+ const globalStyles = globals.styles[rule.styleId];
26
+ if (remeda.isNullish(globalStyles)) {
27
+ log.logger.warn(`Global style not found: ${rule.styleId}`);
28
+ return acc;
29
+ }
30
+ return acc.concat(globalStyles);
31
+ }
32
+ acc.push(rule);
33
+ return acc;
34
+ }, []);
35
+ }
36
+ function resolveGlobalRulesInDynamicView(view, globals) {
37
+ return view.rules.reduce((acc, rule) => {
38
+ if (core.isViewRuleGlobalPredicateRef(rule)) {
39
+ const globalPredicates = globals.dynamicPredicates[rule.predicateId];
40
+ if (remeda.isNullish(globalPredicates)) {
41
+ log.logger.warn(`Global predicate not found: ${rule.predicateId}`);
42
+ return acc;
43
+ }
44
+ return acc.concat(globalPredicates);
45
+ }
46
+ if (core.isViewRuleGlobalStyle(rule)) {
47
+ const globalStyles = globals.styles[rule.styleId];
48
+ if (remeda.isNullish(globalStyles)) {
49
+ log.logger.warn(`Global style not found: ${rule.styleId}`);
50
+ return acc;
51
+ }
52
+ return acc.concat(globalStyles);
53
+ }
54
+ acc.push(rule);
55
+ return acc;
56
+ }, []);
57
+ }
58
+
13
59
  function calcViewLayoutHash(view) {
14
60
  const tohash = {
15
61
  id: view.id,
@@ -186,10 +232,6 @@ function applyViewRuleStyles(_rules, nodes) {
186
232
  for (const rule of rules) {
187
233
  const predicates = [];
188
234
  for (const target of rule.targets) {
189
- if (core.Expr.isWildcard(target)) {
190
- predicates.push(() => true);
191
- break;
192
- }
193
235
  predicates.push(elementExprToPredicate(target));
194
236
  }
195
237
  remeda.pipe(
@@ -929,6 +971,25 @@ class ComputeCtx {
929
971
  get activeGroup() {
930
972
  return this.activeGroupStack[0] ?? this.__rootGroup;
931
973
  }
974
+ get includedElements() {
975
+ return /* @__PURE__ */ new Set([
976
+ ...this.explicits,
977
+ ...this.ctxEdges.flatMap((e) => [e.source, e.target])
978
+ ]);
979
+ }
980
+ get resolvedElements() {
981
+ return /* @__PURE__ */ new Set([
982
+ ...this.explicits,
983
+ ...this.implicits,
984
+ ...this.ctxEdges.flatMap((e) => [e.source, e.target])
985
+ ]);
986
+ }
987
+ get edges() {
988
+ return this.ctxEdges;
989
+ }
990
+ get root() {
991
+ return core.isScopedElementView(this.view) ? this.view.viewOf : null;
992
+ }
932
993
  static elementView(view, graph) {
933
994
  return new ComputeCtx(view, graph).compute();
934
995
  }
@@ -937,9 +998,11 @@ class ComputeCtx {
937
998
  const {
938
999
  docUri: _docUri,
939
1000
  // exclude docUri
940
- rules,
1001
+ rules: _rules,
1002
+ // exclude rules
941
1003
  ...view
942
1004
  } = this.view;
1005
+ const rules = resolveGlobalRulesInElementView(this.view, this.graph.globals);
943
1006
  const viewPredicates = rules.filter(remeda.anyPass([core.isViewRulePredicate, core.isViewRuleGroup]));
944
1007
  if (this.root && viewPredicates.length == 0) {
945
1008
  this.addElement(this.graph.element(this.root));
@@ -951,51 +1014,9 @@ class ComputeCtx {
951
1014
  }
952
1015
  const elements = [...this.includedElements];
953
1016
  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
- };
967
- const edgesMap = /* @__PURE__ */ new Map();
968
1017
  const edges = this.computeEdges();
969
- for (const edge of edges) {
970
- edgesMap.set(edge.id, edge);
971
- const source = nodesMap.get(edge.source);
972
- const target = nodesMap.get(edge.target);
973
- core.invariant(source, `Source node ${edge.source} not found`);
974
- core.invariant(target, `Target node ${edge.target} not found`);
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;
984
- source.outEdges.push(edge.id);
985
- target.inEdges.push(edge.id);
986
- for (const sourceAncestor of sourceAncestors) {
987
- if (sourceAncestor === edgeParent) {
988
- break;
989
- }
990
- sourceAncestor.outEdges.push(edge.id);
991
- }
992
- for (const targetAncestor of targetAncestors) {
993
- if (targetAncestor === edgeParent) {
994
- break;
995
- }
996
- targetAncestor.inEdges.push(edge.id);
997
- }
998
- }
1018
+ const edgesMap = new Map(edges.map((edge) => [edge.id, edge]));
1019
+ this.linkNodesAndEdges(nodesMap, edges);
999
1020
  let initialSort = elements.map((e) => core.nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`));
1000
1021
  if (this.groups.length > 0) {
1001
1022
  initialSort = remeda.concat(
@@ -1027,7 +1048,13 @@ class ComputeCtx {
1027
1048
  ...autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep },
1028
1049
  ...autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }
1029
1050
  },
1030
- nodes: remeda.map(nodes, remeda.omit(["notation"])),
1051
+ nodes: remeda.map(nodes, (n) => {
1052
+ delete n.notation;
1053
+ if (n.icon === "none") {
1054
+ delete n.icon;
1055
+ }
1056
+ return n;
1057
+ }),
1031
1058
  edges: applyCustomRelationProperties(rules, nodes, sortedEdges),
1032
1059
  ...elementNotations.length > 0 && {
1033
1060
  notation: {
@@ -1036,9 +1063,6 @@ class ComputeCtx {
1036
1063
  }
1037
1064
  });
1038
1065
  }
1039
- get root() {
1040
- return core.isScopedElementView(this.view) ? this.view.viewOf : null;
1041
- }
1042
1066
  computeEdges() {
1043
1067
  return this.ctxEdges.map((e) => {
1044
1068
  core.invariant(remeda.hasAtLeast(e.relations, 1), "Edge must have at least one relation");
@@ -1131,21 +1155,36 @@ class ComputeCtx {
1131
1155
  );
1132
1156
  });
1133
1157
  }
1134
- get includedElements() {
1135
- return /* @__PURE__ */ new Set([
1136
- ...this.explicits,
1137
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
1138
- ]);
1139
- }
1140
- get resolvedElements() {
1141
- return /* @__PURE__ */ new Set([
1142
- ...this.explicits,
1143
- ...this.implicits,
1144
- ...this.ctxEdges.flatMap((e) => [e.source, e.target])
1145
- ]);
1146
- }
1147
- get edges() {
1148
- return this.ctxEdges;
1158
+ linkNodesAndEdges(nodesMap, edges) {
1159
+ for (const edge of edges) {
1160
+ const source = nodesMap.get(edge.source);
1161
+ const target = nodesMap.get(edge.target);
1162
+ core.invariant(source, `Source node ${edge.source} not found`);
1163
+ core.invariant(target, `Target node ${edge.target} not found`);
1164
+ const sourceAncestors = this.ancestorsOf(source, nodesMap);
1165
+ const targetAncestors = this.ancestorsOf(target, nodesMap);
1166
+ const edgeParent = remeda.last(
1167
+ core.commonHead(
1168
+ remeda.reverse(sourceAncestors),
1169
+ remeda.reverse(targetAncestors)
1170
+ )
1171
+ );
1172
+ edge.parent = edgeParent?.id ?? null;
1173
+ source.outEdges.push(edge.id);
1174
+ target.inEdges.push(edge.id);
1175
+ for (const sourceAncestor of sourceAncestors) {
1176
+ if (sourceAncestor === edgeParent) {
1177
+ break;
1178
+ }
1179
+ sourceAncestor.outEdges.push(edge.id);
1180
+ }
1181
+ for (const targetAncestor of targetAncestors) {
1182
+ if (targetAncestor === edgeParent) {
1183
+ break;
1184
+ }
1185
+ targetAncestor.inEdges.push(edge.id);
1186
+ }
1187
+ }
1149
1188
  }
1150
1189
  addEdges(edges) {
1151
1190
  const added = [];
@@ -1234,9 +1273,6 @@ class ComputeCtx {
1234
1273
  }),
1235
1274
  remeda.filter(remeda.isNonNull)
1236
1275
  );
1237
- if (excludedImplicits.size === 0) {
1238
- return;
1239
- }
1240
1276
  this.ctxEdges = ctxEdges;
1241
1277
  const remaining = this.includedElements;
1242
1278
  if (remaining.size === 0) {
@@ -1413,6 +1449,19 @@ class ComputeCtx {
1413
1449
  }
1414
1450
  core.nonexhaustive(expr);
1415
1451
  }
1452
+ ancestorsOf(node, nodesMap) {
1453
+ const ancestors = [];
1454
+ let parent = node.parent;
1455
+ while (parent) {
1456
+ const parentNode = nodesMap.get(parent);
1457
+ if (!parentNode) {
1458
+ break;
1459
+ }
1460
+ ancestors.push(parentNode);
1461
+ parent = parentNode.parent;
1462
+ }
1463
+ return ancestors;
1464
+ }
1416
1465
  getEdgeLabel(relation) {
1417
1466
  const labelParts = [];
1418
1467
  if (remeda.isTruthy(relation.title)) {
@@ -1490,7 +1539,8 @@ class DynamicViewComputeCtx {
1490
1539
  const {
1491
1540
  docUri: _docUri,
1492
1541
  // exclude docUri
1493
- rules,
1542
+ rules: _rules,
1543
+ // exclude rules
1494
1544
  steps: viewSteps,
1495
1545
  ...view
1496
1546
  } = this.view;
@@ -1510,6 +1560,7 @@ class DynamicViewComputeCtx {
1510
1560
  }
1511
1561
  stepNum++;
1512
1562
  }
1563
+ const rules = resolveGlobalRulesInDynamicView(this.view, this.graph.globals);
1513
1564
  for (const rule of rules) {
1514
1565
  if (core.isDynamicViewIncludeRule(rule)) {
1515
1566
  for (const expr of rule.include) {
@@ -1573,7 +1624,13 @@ class DynamicViewComputeCtx {
1573
1624
  ...autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep },
1574
1625
  ...autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }
1575
1626
  },
1576
- nodes: remeda.map(nodes, remeda.omit(["notation"])),
1627
+ nodes: remeda.map(nodes, (n) => {
1628
+ delete n.notation;
1629
+ if (n.icon === "none") {
1630
+ delete n.icon;
1631
+ }
1632
+ return n;
1633
+ }),
1577
1634
  edges,
1578
1635
  ...elementNotations.length > 0 && {
1579
1636
  notation: {
@@ -1663,7 +1720,13 @@ class LikeC4ModelGraph {
1663
1720
  // Relationships inside the element, among descendants
1664
1721
  #internal = new MapRelations();
1665
1722
  #cacheAscendingSiblings = /* @__PURE__ */ new Map();
1666
- constructor({ elements, relations }) {
1723
+ globals;
1724
+ constructor({ elements, relations, globals }) {
1725
+ this.globals = globals ?? {
1726
+ predicates: {},
1727
+ dynamicPredicates: {},
1728
+ styles: {}
1729
+ };
1667
1730
  for (const el of Object.values(elements)) {
1668
1731
  this.addElement(el);
1669
1732
  }
@@ -47,7 +47,7 @@ type ArrowType = 'crow' | 'diamond' | 'dot' | 'none' | 'normal' | 'odiamond' | '
47
47
  type BorderStyleValue = 'none' | LineOptions;
48
48
  type CustomColorId = 'element' | 'model' | ArrowType | ElementShape | LineOptions | string;
49
49
  type CustomColorValue = string;
50
- type DynamicViewRule = DynamicViewIncludePredicate | ViewRuleAutoLayout | ViewRuleStyleOrGlobalRef;
50
+ type DynamicViewRule = DynamicViewGlobalPredicateRef | DynamicViewIncludePredicate | ViewRuleAutoLayout | ViewRuleStyleOrGlobalRef;
51
51
  declare const DynamicViewRule = "DynamicViewRule";
52
52
  declare function isDynamicViewRule(item: unknown): item is DynamicViewRule;
53
53
  type ElementExpression = ElementDescedantsExpression | ElementKindExpression | ElementRef | ElementTagExpression | ExpandElementExpression | WildcardExpression;
@@ -94,7 +94,7 @@ type ViewLayoutDirection = 'BottomTop' | 'LeftRight' | 'RightLeft' | 'TopBottom'
94
94
  type ViewProperty = LinkProperty | ViewStringProperty;
95
95
  declare const ViewProperty = "ViewProperty";
96
96
  declare function isViewProperty(item: unknown): item is ViewProperty;
97
- type ViewRule = ViewRuleAutoLayout | ViewRuleGroup | ViewRulePredicate | ViewRuleStyleOrGlobalRef;
97
+ type ViewRule = ViewRuleAutoLayout | ViewRuleGlobalPredicateRef | ViewRuleGroup | ViewRulePredicate | ViewRuleStyleOrGlobalRef;
98
98
  declare const ViewRule = "ViewRule";
99
99
  declare function isViewRule(item: unknown): item is ViewRule;
100
100
  type ViewRulePredicate = ExcludePredicate | IncludePredicate;
@@ -179,8 +179,14 @@ interface DynamicViewBody extends AstNode {
179
179
  }
180
180
  declare const DynamicViewBody = "DynamicViewBody";
181
181
  declare function isDynamicViewBody(item: unknown): item is DynamicViewBody;
182
- interface DynamicViewIncludePredicate extends AstNode {
182
+ interface DynamicViewGlobalPredicateRef extends AstNode {
183
183
  readonly $container: DynamicViewBody;
184
+ readonly $type: 'DynamicViewGlobalPredicateRef';
185
+ predicate: Reference<GlobalDynamicPredicateGroup>;
186
+ }
187
+ declare const DynamicViewGlobalPredicateRef = "DynamicViewGlobalPredicateRef";
188
+ interface DynamicViewIncludePredicate extends AstNode {
189
+ readonly $container: DynamicViewBody | GlobalDynamicPredicateGroup;
184
190
  readonly $type: 'DynamicViewIncludePredicate';
185
191
  predicates: DynamicViewPredicateIterator;
186
192
  }
@@ -331,7 +337,7 @@ interface ElementViewRef extends AstNode {
331
337
  }
332
338
  declare const ElementViewRef = "ElementViewRef";
333
339
  interface ExcludePredicate extends AstNode {
334
- readonly $container: ElementViewBody | ViewRuleGroup;
340
+ readonly $container: ElementViewBody | GlobalPredicateGroup | ViewRuleGroup;
335
341
  readonly $type: 'ExcludePredicate';
336
342
  predicates: Predicates;
337
343
  }
@@ -364,10 +370,27 @@ interface FqnElementRef extends AstNode {
364
370
  parent?: FqnElementRef;
365
371
  }
366
372
  declare const FqnElementRef = "FqnElementRef";
373
+ interface GlobalDynamicPredicateGroup extends AstNode {
374
+ readonly $container: Globals;
375
+ readonly $type: 'GlobalDynamicPredicateGroup';
376
+ name: string;
377
+ predicates: Array<DynamicViewIncludePredicate>;
378
+ }
379
+ declare const GlobalDynamicPredicateGroup = "GlobalDynamicPredicateGroup";
380
+ declare function isGlobalDynamicPredicateGroup(item: unknown): item is GlobalDynamicPredicateGroup;
381
+ interface GlobalPredicateGroup extends AstNode {
382
+ readonly $container: Globals;
383
+ readonly $type: 'GlobalPredicateGroup';
384
+ name: string;
385
+ predicates: Array<ViewRulePredicate>;
386
+ }
387
+ declare const GlobalPredicateGroup = "GlobalPredicateGroup";
388
+ declare function isGlobalPredicateGroup(item: unknown): item is GlobalPredicateGroup;
367
389
  interface Globals extends AstNode {
368
390
  readonly $container: LikeC4Grammar;
369
391
  readonly $type: 'Globals';
370
392
  name: 'global';
393
+ predicates: Array<GlobalDynamicPredicateGroup | GlobalPredicateGroup>;
371
394
  styles: Array<GlobalStyle | GlobalStyleGroup>;
372
395
  }
373
396
  declare const Globals = "Globals";
@@ -400,11 +423,11 @@ interface IconProperty extends AstNode {
400
423
  readonly $type: 'IconProperty';
401
424
  key: 'icon';
402
425
  libicon?: Reference<LibIcon>;
403
- value?: Uri;
426
+ value?: 'none' | Uri;
404
427
  }
405
428
  declare const IconProperty = "IconProperty";
406
429
  interface IncludePredicate extends AstNode {
407
- readonly $container: ElementViewBody | ViewRuleGroup;
430
+ readonly $container: ElementViewBody | GlobalPredicateGroup | ViewRuleGroup;
408
431
  readonly $type: 'IncludePredicate';
409
432
  predicates: Predicates;
410
433
  }
@@ -684,6 +707,12 @@ interface ViewRuleAutoLayout extends AstNode {
684
707
  rankSep?: number;
685
708
  }
686
709
  declare const ViewRuleAutoLayout = "ViewRuleAutoLayout";
710
+ interface ViewRuleGlobalPredicateRef extends AstNode {
711
+ readonly $container: ElementViewBody;
712
+ readonly $type: 'ViewRuleGlobalPredicateRef';
713
+ predicate: Reference<GlobalPredicateGroup>;
714
+ }
715
+ declare const ViewRuleGlobalPredicateRef = "ViewRuleGlobalPredicateRef";
687
716
  interface ViewRuleGlobalStyle extends AstNode {
688
717
  readonly $container: DynamicViewBody | ElementViewBody | ModelViews;
689
718
  readonly $type: 'ViewRuleGlobalStyle';
@@ -780,6 +809,7 @@ type LikeC4AstType = {
780
809
  DirectedRelationExpression: DirectedRelationExpression;
781
810
  DynamicView: DynamicView;
782
811
  DynamicViewBody: DynamicViewBody;
812
+ DynamicViewGlobalPredicateRef: DynamicViewGlobalPredicateRef;
783
813
  DynamicViewIncludePredicate: DynamicViewIncludePredicate;
784
814
  DynamicViewParallelSteps: DynamicViewParallelSteps;
785
815
  DynamicViewPredicateIterator: DynamicViewPredicateIterator;
@@ -810,6 +840,8 @@ type LikeC4AstType = {
810
840
  ExtendElement: ExtendElement;
811
841
  ExtendElementBody: ExtendElementBody;
812
842
  FqnElementRef: FqnElementRef;
843
+ GlobalDynamicPredicateGroup: GlobalDynamicPredicateGroup;
844
+ GlobalPredicateGroup: GlobalPredicateGroup;
813
845
  GlobalStyle: GlobalStyle;
814
846
  GlobalStyleGroup: GlobalStyleGroup;
815
847
  GlobalStyleId: GlobalStyleId;
@@ -865,6 +897,7 @@ type LikeC4AstType = {
865
897
  ViewRef: ViewRef;
866
898
  ViewRule: ViewRule;
867
899
  ViewRuleAutoLayout: ViewRuleAutoLayout;
900
+ ViewRuleGlobalPredicateRef: ViewRuleGlobalPredicateRef;
868
901
  ViewRuleGlobalStyle: ViewRuleGlobalStyle;
869
902
  ViewRuleGroup: ViewRuleGroup;
870
903
  ViewRulePredicate: ViewRulePredicate;
@@ -960,9 +993,7 @@ interface ParsedAstRelation {
960
993
  [key: string]: string;
961
994
  };
962
995
  }
963
- interface ParsedAstGlobals {
964
- styles: Record<c4.GlobalStyleID, c4.NonEmptyArray<c4.ViewRuleStyle>>;
965
- }
996
+ type ParsedAstGlobals = c4.ModelGlobals;
966
997
  interface ParsedAstElementView {
967
998
  __: 'element';
968
999
  id: c4.ViewID;
@@ -1015,7 +1046,7 @@ interface ParsedLikeC4LangiumDocument extends Omit<LangiumDocument<LikeC4Grammar
1015
1046
  }
1016
1047
  type Guard<N extends AstNode> = (n: AstNode) => n is N;
1017
1048
  type Guarded<G> = G extends Guard<infer N> ? N : never;
1018
- declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isLikeC4View | typeof isRelation | typeof isExtendElement | typeof isElement | typeof isGlobals | typeof isGlobalStyle | typeof isGlobalStyleGroup | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
1049
+ declare const isValidatableAstNode: (n: AstNode) => n is Guarded<typeof isLikeC4View | typeof isRelation | typeof isExtendElement | typeof isElement | typeof isGlobals | typeof isGlobalPredicateGroup | typeof isGlobalDynamicPredicateGroup | typeof isGlobalStyle | typeof isGlobalStyleGroup | typeof isDynamicViewPredicateIterator | typeof isElementPredicateWith | typeof isRelationPredicateWith | typeof isElementExpression | typeof isRelationExpression | typeof isDynamicViewParallelSteps | typeof isDynamicViewStep | typeof isViewProperty | typeof isStyleProperty | typeof isTags | typeof isViewRule | typeof isDynamicViewRule | typeof isElementViewBody | typeof isDynamicViewBody | typeof isRelationProperty | typeof isRelationBody | typeof isElementProperty | typeof isElementBody | typeof isExtendElementBody | typeof isSpecificationElementKind | typeof isSpecificationRelationshipKind | typeof isSpecificationTag | typeof isSpecificationColor | typeof isSpecificationRule | typeof isModelViews | typeof isModel>;
1019
1050
  type ValidatableAstNode = Guarded<typeof isValidatableAstNode>;
1020
1051
  declare function checksFromDiagnostics(doc: LikeC4LangiumDocument): {
1021
1052
  isValid: (n: ValidatableAstNode) => boolean;
@@ -1135,8 +1166,10 @@ declare class LikeC4ModelParser {
1135
1166
  private parseElement;
1136
1167
  private parseRelation;
1137
1168
  private parseGlobal;
1169
+ private parseAndStoreGlobalPredicateGroupOrDynamic;
1170
+ private parseGlobalPredicateGroup;
1171
+ private parseGlobalDynamicPredicateGroup;
1138
1172
  private parseGlobalStyleOrGroup;
1139
- private parseGlobalStyle;
1140
1173
  private parseViews;
1141
1174
  private parseViewRulePredicate;
1142
1175
  private parsePredicate;
@@ -1150,6 +1183,7 @@ declare class LikeC4ModelParser {
1150
1183
  private parseRelationPredicateWith;
1151
1184
  private parseRelationExpr;
1152
1185
  private parseViewRule;
1186
+ private parseViewRuleGlobalPredicateRef;
1153
1187
  private parseViewRuleStyleOrGlobalRef;
1154
1188
  private parseViewRuleStyle;
1155
1189
  private parseViewRuleGroup;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "1.15.1",
4
+ "version": "1.17.0",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -112,8 +112,8 @@
112
112
  },
113
113
  "dependencies": {
114
114
  "@dagrejs/dagre": "^1.1.4",
115
- "@likec4/core": "1.15.1",
116
- "@likec4/log": "1.15.1",
115
+ "@likec4/core": "1.17.0",
116
+ "@likec4/log": "1.17.0",
117
117
  "@msgpack/msgpack": "^3.0.0-beta2",
118
118
  "@smithy/util-base64": "^3.0.0",
119
119
  "fast-equals": "^5.0.1",
@@ -133,12 +133,12 @@
133
133
  "vscode-uri": "3.0.8"
134
134
  },
135
135
  "devDependencies": {
136
- "@likec4/icons": "1.15.1",
137
- "@likec4/tsconfig": "1.15.1",
136
+ "@likec4/icons": "1.17.0",
137
+ "@likec4/tsconfig": "1.17.0",
138
138
  "@types/node": "^20.16.14",
139
139
  "@types/object-hash": "^3.0.6",
140
140
  "@types/string-hash": "^1.1.3",
141
- "@vitest/coverage-v8": "^2.1.3",
141
+ "@vitest/coverage-v8": "^2.1.5",
142
142
  "execa": "^9.3.1",
143
143
  "langium-cli": "3.2.0",
144
144
  "npm-run-all2": "^6.2.6",
@@ -146,7 +146,7 @@
146
146
  "turbo": "^2.2.3",
147
147
  "typescript": "^5.6.3",
148
148
  "unbuild": "^3.0.0-rc.11",
149
- "vitest": "^2.1.3"
149
+ "vitest": "^2.1.5"
150
150
  },
151
151
  "packageManager": "yarn@4.5.1"
152
152
  }
@@ -0,0 +1,36 @@
1
+ import { type FileSystemNode, URI, UriUtils } from 'langium'
2
+ import { NodeFileSystemProvider } from 'langium/node'
3
+ import { lstatSync, readlinkSync } from 'node:fs'
4
+ import { readdir } from 'node:fs/promises'
5
+ import { dirname, resolve } from 'node:path'
6
+
7
+ export const LikeC4FileSystem = {
8
+ fileSystemProvider: () => new SymLinkTraversingFileSystemProvider()
9
+ }
10
+
11
+ /**
12
+ * A file system provider that follows symbolic links.
13
+ * @see https://github.com/likec4/likec4/pull/1213
14
+ */
15
+ class SymLinkTraversingFileSystemProvider extends NodeFileSystemProvider {
16
+ override async readDirectory(folderPath: URI): Promise<FileSystemNode[]> {
17
+ const dirents = await readdir(folderPath.fsPath, { withFileTypes: true })
18
+ return dirents.map(dirent => (this.followUri(UriUtils.joinPath(folderPath, dirent.name))))
19
+ }
20
+
21
+ followUri(uri: URI): FileSystemNode {
22
+ const directoryPath = dirname(uri.fsPath)
23
+ const stat = lstatSync(uri.fsPath)
24
+ if (stat.isSymbolicLink()) {
25
+ const resolved_link = readlinkSync(uri.fsPath)
26
+ const linked_path = resolve(directoryPath, resolved_link)
27
+ return this.followUri(URI.file(linked_path))
28
+ } else {
29
+ return {
30
+ isFile: stat.isFile(),
31
+ isDirectory: stat.isDirectory(),
32
+ uri
33
+ }
34
+ }
35
+ }
36
+ }
package/src/Rpc.ts CHANGED
@@ -43,8 +43,8 @@ export class Rpc implements Disposable {
43
43
  },
44
44
  {
45
45
  timing: 'both',
46
- waitMs: 300,
47
- maxWaitMs: 600
46
+ waitMs: 250,
47
+ maxWaitMs: 500
48
48
  }
49
49
  )
50
50
 
package/src/ast.ts CHANGED
@@ -93,9 +93,13 @@ export interface ParsedAstRelation {
93
93
  metadata?: { [key: string]: string }
94
94
  }
95
95
 
96
- export interface ParsedAstGlobals {
97
- styles: Record<c4.GlobalStyleID, c4.NonEmptyArray<c4.ViewRuleStyle>>
98
- }
96
+ // export interface ParsedAstGlobals {
97
+ // predicates: Record<c4.GlobalElRelID, c4.NonEmptyArray<c4.ViewRulePredicate>>
98
+ // dynamicPredicates: Record<c4.GlobalElRelID, c4.NonEmptyArray<c4.DynamicViewIncludeRule>>
99
+ // styles: Record<c4.GlobalStyleID, c4.NonEmptyArray<c4.ViewRuleStyle>>
100
+ // }
101
+ // Alias for refactoring
102
+ export type ParsedAstGlobals = c4.ModelGlobals
99
103
 
100
104
  export interface ParsedAstElementView {
101
105
  __: 'element'
@@ -196,6 +200,8 @@ export function cleanParsedModel(doc: LikeC4LangiumDocument) {
196
200
  c4Elements: [],
197
201
  c4Relations: [],
198
202
  c4Globals: {
203
+ predicates: {},
204
+ dynamicPredicates: {},
199
205
  styles: {}
200
206
  },
201
207
  c4Views: []
@@ -235,6 +241,8 @@ function validatableAstNodeGuards<const Predicates extends Guard<AstNode>[]>(
235
241
  }
236
242
  const isValidatableAstNode = validatableAstNodeGuards([
237
243
  ast.isGlobals,
244
+ ast.isGlobalPredicateGroup,
245
+ ast.isGlobalDynamicPredicateGroup,
238
246
  ast.isGlobalStyle,
239
247
  ast.isGlobalStyleGroup,
240
248
  ast.isDynamicViewPredicateIterator,