@likec4/language-server 1.15.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/dist/browser.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const lsp = require('langium/lsp');
4
4
  const browser = require('vscode-languageserver/browser');
5
- const module$1 = require('./shared/language-server.BUtiWTKg.cjs');
5
+ const module$1 = require('./shared/language-server.DfjkvknB.cjs');
6
6
 
7
7
  function startLanguageServer() {
8
8
  const messageReader = new browser.BrowserMessageReader(self);
package/dist/browser.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { startLanguageServer as startLanguageServer$1 } from 'langium/lsp';
2
2
  import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser';
3
- import { c as createLanguageServices } from './shared/language-server.zY53FGJE.mjs';
4
- export { L as LikeC4Module, a as createCustomLanguageServices, s as setLogLevel } from './shared/language-server.zY53FGJE.mjs';
3
+ import { c as createLanguageServices } from './shared/language-server.BuChFlda.mjs';
4
+ export { L as LikeC4Module, a as createCustomLanguageServices, s as setLogLevel } from './shared/language-server.BuChFlda.mjs';
5
5
 
6
6
  function startLanguageServer() {
7
7
  const messageReader = new BrowserMessageReader(self);
package/dist/index.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  const lsp = require('langium/lsp');
4
4
  const node$1 = require('langium/node');
5
5
  const node = require('vscode-languageserver/node');
6
- const module$1 = require('./shared/language-server.BUtiWTKg.cjs');
6
+ const module$1 = require('./shared/language-server.DfjkvknB.cjs');
7
7
 
8
8
  function startLanguageServer() {
9
9
  const connection = node.createConnection(node.ProposedFeatures.all);
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { startLanguageServer as startLanguageServer$1 } from 'langium/lsp';
2
2
  import { NodeFileSystem } from 'langium/node';
3
3
  import { createConnection, ProposedFeatures } from 'vscode-languageserver/node';
4
- import { c as createLanguageServices } from './shared/language-server.zY53FGJE.mjs';
5
- export { L as LikeC4Module, a as createCustomLanguageServices, l as lspLogger, s as setLogLevel } from './shared/language-server.zY53FGJE.mjs';
4
+ import { c as createLanguageServices } from './shared/language-server.BuChFlda.mjs';
5
+ export { L as LikeC4Module, a as createCustomLanguageServices, l as lspLogger, s as setLogLevel } from './shared/language-server.BuChFlda.mjs';
6
6
 
7
7
  function startLanguageServer() {
8
8
  const connection = createConnection(ProposedFeatures.all);
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const LikeC4ModelGraph = require('../shared/language-server.80ITEDo5.cjs');
3
+ const LikeC4ModelGraph = require('../shared/language-server.B8s2wfT_.cjs');
4
4
 
5
5
 
6
6
 
@@ -1 +1 @@
1
- export { L as LikeC4ModelGraph, b as computeDynamicView, c as computeElementView, a as computeView } from '../shared/language-server.DXC9g4_f.mjs';
1
+ export { L as LikeC4ModelGraph, b as computeDynamicView, c as computeElementView, a as computeView } from '../shared/language-server.BT4WTbFI.mjs';
@@ -121,7 +121,9 @@ function applyCustomElementProperties(_rules, _nodes) {
121
121
  function relationExpressionToPredicates(expr) {
122
122
  switch (true) {
123
123
  case core.Expr.isRelationWhere(expr):
124
- return relationExpressionToPredicates(expr.where.expr);
124
+ const predicate = relationExpressionToPredicates(expr.where.expr);
125
+ const where = core.whereOperatorAsPredicate(expr.where.condition);
126
+ return (e) => predicate(e) && where(e);
125
127
  case core.Expr.isRelation(expr): {
126
128
  const isSource = elementExprToPredicate(expr.source);
127
129
  const isTarget = elementExprToPredicate(expr.target);
@@ -145,6 +147,7 @@ function relationExpressionToPredicates(expr) {
145
147
  core.nonexhaustive(expr);
146
148
  }
147
149
  }
150
+
148
151
  function applyCustomRelationProperties(_rules, nodes, _edges) {
149
152
  const rules = _rules.flatMap(flattenGroupRules(core.Expr.isCustomRelationExpr));
150
153
  const edges = Array.from(_edges);
@@ -162,12 +165,12 @@ function applyCustomRelationProperties(_rules, nodes, _edges) {
162
165
  if (!source || !target) {
163
166
  return;
164
167
  }
165
- if (satisfies({ source, target })) {
168
+ if (satisfies({ source, target, ...remeda.pick(edge, ["kind", "tags"]) })) {
166
169
  edges[i] = {
167
170
  ...edge,
171
+ ...props,
168
172
  label: title ?? edge.label,
169
- isCustomized: true,
170
- ...props
173
+ isCustomized: true
171
174
  };
172
175
  }
173
176
  });
@@ -114,7 +114,9 @@ function applyCustomElementProperties(_rules, _nodes) {
114
114
  function relationExpressionToPredicates(expr) {
115
115
  switch (true) {
116
116
  case Expr.isRelationWhere(expr):
117
- return relationExpressionToPredicates(expr.where.expr);
117
+ const predicate = relationExpressionToPredicates(expr.where.expr);
118
+ const where = whereOperatorAsPredicate(expr.where.condition);
119
+ return (e) => predicate(e) && where(e);
118
120
  case Expr.isRelation(expr): {
119
121
  const isSource = elementExprToPredicate(expr.source);
120
122
  const isTarget = elementExprToPredicate(expr.target);
@@ -138,6 +140,7 @@ function relationExpressionToPredicates(expr) {
138
140
  nonexhaustive(expr);
139
141
  }
140
142
  }
143
+
141
144
  function applyCustomRelationProperties(_rules, nodes, _edges) {
142
145
  const rules = _rules.flatMap(flattenGroupRules(Expr.isCustomRelationExpr));
143
146
  const edges = Array.from(_edges);
@@ -155,12 +158,12 @@ function applyCustomRelationProperties(_rules, nodes, _edges) {
155
158
  if (!source || !target) {
156
159
  return;
157
160
  }
158
- if (satisfies({ source, target })) {
161
+ if (satisfies({ source, target, ...pick(edge, ["kind", "tags"]) })) {
159
162
  edges[i] = {
160
163
  ...edge,
164
+ ...props,
161
165
  label: title ?? edge.label,
162
- isCustomized: true,
163
- ...props
166
+ isCustomized: true
164
167
  };
165
168
  }
166
169
  });
@@ -13,7 +13,7 @@ import { URI as URI$1 } from 'vscode-uri';
13
13
  import stripIndent from 'strip-indent';
14
14
  import hash from 'string-hash';
15
15
  import { deepEqual } from 'fast-equals';
16
- import { G as Graph, i as isAcyclic, f as findCycles, p as postorder, L as LikeC4ModelGraph, a as computeView, b as computeDynamicView } from './language-server.DXC9g4_f.mjs';
16
+ import { G as Graph, i as isAcyclic, f as findCycles, p as postorder, L as LikeC4ModelGraph, a as computeView, b as computeDynamicView } from './language-server.BT4WTbFI.mjs';
17
17
  import { hasProtocol, hasLeadingSlash, isRelative, withoutLeadingSlash, withoutBase, parsePath } from 'ufo';
18
18
  import { DocumentHighlight, DocumentHighlightKind } from 'vscode-languageserver';
19
19
 
@@ -2410,7 +2410,7 @@ class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
2410
2410
  SemanticTokenModifiers.readonly
2411
2411
  ]
2412
2412
  });
2413
- return;
2413
+ return "prune";
2414
2414
  }
2415
2415
  if (isWhereRelationKind(node) && isTruthy(node.value)) {
2416
2416
  acceptor({
@@ -2439,6 +2439,7 @@ class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
2439
2439
  type: SemanticTokenTypes.type,
2440
2440
  modifier: [SemanticTokenModifiers.definition]
2441
2441
  });
2442
+ return "prune";
2442
2443
  }
2443
2444
  if (isElementTagExpression(node) && isTruthy(node.tag)) {
2444
2445
  acceptor({
@@ -2447,6 +2448,7 @@ class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
2447
2448
  type: SemanticTokenTypes.type,
2448
2449
  modifier: [SemanticTokenModifiers.definition]
2449
2450
  });
2451
+ return "prune";
2450
2452
  }
2451
2453
  if (isElementRef(node) || isFqnElementRef(node)) {
2452
2454
  acceptor({
@@ -3139,17 +3141,22 @@ function buildModel(services, docs) {
3139
3141
  const links = unresolvedLinks ? resolveLinks(doc, unresolvedLinks) : null;
3140
3142
  return {
3141
3143
  ...model,
3144
+ customColorDefinitions,
3142
3145
  tags,
3143
3146
  links,
3144
3147
  docUri,
3145
3148
  description,
3146
3149
  title,
3147
- id,
3148
- customColorDefinitions
3150
+ id
3149
3151
  };
3150
3152
  };
3151
3153
  }
3152
- const parsedViews = docs.flatMap((d) => map(d.c4Views, toC4View(d)));
3154
+ const parsedViews = pipe(
3155
+ docs,
3156
+ flatMap((d) => map(d.c4Views, toC4View(d))),
3157
+ // Resolve relative paths and sort by
3158
+ resolveRelativePaths
3159
+ );
3153
3160
  if (!parsedViews.some((v) => v.id === "index")) {
3154
3161
  parsedViews.unshift({
3155
3162
  __: "element",
@@ -3172,7 +3179,6 @@ function buildModel(services, docs) {
3172
3179
  }
3173
3180
  const views = pipe(
3174
3181
  parsedViews,
3175
- resolveRelativePaths,
3176
3182
  indexBy(prop("id")),
3177
3183
  resolveRulesExtendedViews
3178
3184
  );
@@ -3235,7 +3241,7 @@ class LikeC4ModelBuilder {
3235
3241
  logger.debug("[ModelBuilder] No documents to build model from");
3236
3242
  return null;
3237
3243
  }
3238
- logger.debug(`[ModelBuilder] onValidated (${docs.length} docs)`);
3244
+ logger.debug(`[ModelBuilder] buildModel (${docs.length} docs)`);
3239
3245
  return buildModel(this.services, docs);
3240
3246
  });
3241
3247
  }
@@ -3264,7 +3270,8 @@ class LikeC4ModelBuilder {
3264
3270
  const index = new LikeC4ModelGraph(model);
3265
3271
  const allViews = [];
3266
3272
  for (const view of values(model.views)) {
3267
- const result = isElementView$1(view) ? computeView(view, index) : computeDynamicView(view, index);
3273
+ const resolvedView = resolveGlobalRules(view, model.globals.styles);
3274
+ const result = isElementView$1(resolvedView) ? computeView(resolvedView, index) : computeDynamicView(resolvedView, index);
3268
3275
  if (!result.isSuccess) {
3269
3276
  logWarnError(result.error);
3270
3277
  continue;
@@ -5822,16 +5829,19 @@ class LikeC4Formatter extends AbstractFormatter {
5822
5829
  this.removeIndentFromTopLevelStatements(node);
5823
5830
  this.indentContentInBraces(node);
5824
5831
  this.formatSpecificationRule(node);
5832
+ this.formatGlobals(node);
5825
5833
  this.formatElementDeclaration(node);
5826
5834
  this.formatRelation(node);
5835
+ this.formatMetadataProperty(node);
5827
5836
  this.formatView(node);
5828
- this.formatViewRuleStyle(node);
5837
+ this.formatViewRuleGroup(node);
5838
+ this.formatViewRuleGlobalStyle(node);
5829
5839
  this.formatIncludeExcludeExpressions(node);
5830
5840
  this.formatWhereExpression(node);
5841
+ this.formatAutolayoutProperty(node);
5831
5842
  this.formatWithPredicate(node);
5843
+ this.formatViewRuleStyle(node);
5832
5844
  this.formatLeafProperty(node);
5833
- this.formatMetadataProperty(node);
5834
- this.formatAutolayoutProperty(node);
5835
5845
  this.formatLinkProperty(node);
5836
5846
  this.formatNavigateToProperty(node);
5837
5847
  this.formatTags(node);
@@ -5868,13 +5878,13 @@ class LikeC4Formatter extends AbstractFormatter {
5868
5878
  this.on(node, isInOutRelationExpression)?.keyword("->").prepend(FormattingOptions.oneSpace);
5869
5879
  }
5870
5880
  removeIndentFromTopLevelStatements(node) {
5871
- if (isModel(node) || isSpecificationRule(node) || isModelViews(node) || isLikeC4Lib(node)) {
5881
+ if (isModel(node) || isSpecificationRule(node) || isModelViews(node) || isLikeC4Lib(node) || isGlobals(node)) {
5872
5882
  const formatter = this.getNodeFormatter(node);
5873
- formatter.keywords("specification", "model", "views", "likec4lib").prepend(FormattingOptions.noIndent);
5883
+ formatter.keywords("specification", "model", "views", "likec4lib", "global").prepend(FormattingOptions.noIndent);
5874
5884
  }
5875
5885
  }
5876
5886
  indentContentInBraces(node) {
5877
- if (isLikeC4Lib(node) || isSpecificationRule(node) || isSpecificationElementKind(node) || isSpecificationRelationshipKind(node) || isModel(node) || isElementBody(node) || isExtendElementBody(node) || isRelationBody(node) || isRelationStyleProperty(node) || isMetadataBody(node) || isModelViews(node) || isElementViewBody(node) || isDynamicViewBody(node) || isViewRuleStyle(node) || isCustomElementProperties(node) || isCustomRelationProperties(node) || isElementStyleProperty(node) || isDynamicViewParallelSteps(node)) {
5887
+ if (isLikeC4Lib(node) || isSpecificationRule(node) || isSpecificationElementKind(node) || isSpecificationRelationshipKind(node) || isGlobals(node) || isGlobalStyle(node) || isGlobalStyleGroup(node) || isModel(node) || isElementBody(node) || isExtendElementBody(node) || isRelationBody(node) || isRelationStyleProperty(node) || isMetadataBody(node) || isModelViews(node) || isElementViewBody(node) || isDynamicViewBody(node) || isViewRuleStyle(node) || isViewRuleGroup(node) || isCustomElementProperties(node) || isCustomRelationProperties(node) || isElementStyleProperty(node) || isDynamicViewParallelSteps(node)) {
5878
5888
  const formatter = this.getNodeFormatter(node);
5879
5889
  const openBrace = formatter.keywords("{");
5880
5890
  const closeBrace = formatter.keywords("}");
@@ -5968,6 +5978,15 @@ class LikeC4Formatter extends AbstractFormatter {
5968
5978
  f.properties("props").prepend(FormattingOptions.oneSpace);
5969
5979
  });
5970
5980
  }
5981
+ formatGlobals(node) {
5982
+ this.on(node, isGlobalStyle, (n, f) => {
5983
+ f.keyword("style").append(FormattingOptions.oneSpace);
5984
+ f.property("id").append(FormattingOptions.oneSpace);
5985
+ });
5986
+ this.on(node, isGlobalStyleGroup, (n, f) => {
5987
+ f.keyword("styleGroup").append(FormattingOptions.oneSpace);
5988
+ });
5989
+ }
5971
5990
  formatSpecificationRule(node) {
5972
5991
  if (isSpecificationElementKind(node) || isSpecificationRelationshipKind(node) || isSpecificationTag(node)) {
5973
5992
  const formatter = this.getNodeFormatter(node);
@@ -5985,6 +6004,16 @@ class LikeC4Formatter extends AbstractFormatter {
5985
6004
  formatter.keyword("with").prepend(FormattingOptions.oneSpace);
5986
6005
  }
5987
6006
  }
6007
+ formatViewRuleGlobalStyle(node) {
6008
+ this.on(node, isViewRuleGlobalStyle, (n, f) => {
6009
+ f.keywords("global", "style").append(FormattingOptions.oneSpace);
6010
+ });
6011
+ }
6012
+ formatViewRuleGroup(node) {
6013
+ this.on(node, isViewRuleGroup, (n, f) => {
6014
+ f.keyword("group").append(FormattingOptions.oneSpace);
6015
+ });
6016
+ }
5988
6017
  formatViewRuleStyle(node) {
5989
6018
  this.on(node, isViewRuleStyle)?.keyword("style").append(FormattingOptions.oneSpace);
5990
6019
  this.on(node, isElementExpressionsIterator)?.keyword(",").prepend(FormattingOptions.noSpace).append(FormattingOptions.oneSpace);
@@ -15,7 +15,7 @@ const vscodeUri = require('vscode-uri');
15
15
  const stripIndent = require('strip-indent');
16
16
  const hash = require('string-hash');
17
17
  const fastEquals = require('fast-equals');
18
- const LikeC4ModelGraph = require('./language-server.80ITEDo5.cjs');
18
+ const LikeC4ModelGraph = require('./language-server.B8s2wfT_.cjs');
19
19
  const ufo = require('ufo');
20
20
  const vscodeLanguageserver = require('vscode-languageserver');
21
21
 
@@ -2418,7 +2418,7 @@ class LikeC4SemanticTokenProvider extends lsp.AbstractSemanticTokenProvider {
2418
2418
  vscodeLanguageserverTypes.SemanticTokenModifiers.readonly
2419
2419
  ]
2420
2420
  });
2421
- return;
2421
+ return "prune";
2422
2422
  }
2423
2423
  if (isWhereRelationKind(node) && remeda.isTruthy(node.value)) {
2424
2424
  acceptor({
@@ -2447,6 +2447,7 @@ class LikeC4SemanticTokenProvider extends lsp.AbstractSemanticTokenProvider {
2447
2447
  type: vscodeLanguageserverTypes.SemanticTokenTypes.type,
2448
2448
  modifier: [vscodeLanguageserverTypes.SemanticTokenModifiers.definition]
2449
2449
  });
2450
+ return "prune";
2450
2451
  }
2451
2452
  if (isElementTagExpression(node) && remeda.isTruthy(node.tag)) {
2452
2453
  acceptor({
@@ -2455,6 +2456,7 @@ class LikeC4SemanticTokenProvider extends lsp.AbstractSemanticTokenProvider {
2455
2456
  type: vscodeLanguageserverTypes.SemanticTokenTypes.type,
2456
2457
  modifier: [vscodeLanguageserverTypes.SemanticTokenModifiers.definition]
2457
2458
  });
2459
+ return "prune";
2458
2460
  }
2459
2461
  if (isElementRef(node) || isFqnElementRef(node)) {
2460
2462
  acceptor({
@@ -3147,17 +3149,22 @@ function buildModel(services, docs) {
3147
3149
  const links = unresolvedLinks ? resolveLinks(doc, unresolvedLinks) : null;
3148
3150
  return {
3149
3151
  ...model,
3152
+ customColorDefinitions,
3150
3153
  tags,
3151
3154
  links,
3152
3155
  docUri,
3153
3156
  description,
3154
3157
  title,
3155
- id,
3156
- customColorDefinitions
3158
+ id
3157
3159
  };
3158
3160
  };
3159
3161
  }
3160
- const parsedViews = docs.flatMap((d) => remeda.map(d.c4Views, toC4View(d)));
3162
+ const parsedViews = remeda.pipe(
3163
+ docs,
3164
+ remeda.flatMap((d) => remeda.map(d.c4Views, toC4View(d))),
3165
+ // Resolve relative paths and sort by
3166
+ resolveRelativePaths
3167
+ );
3161
3168
  if (!parsedViews.some((v) => v.id === "index")) {
3162
3169
  parsedViews.unshift({
3163
3170
  __: "element",
@@ -3180,7 +3187,6 @@ function buildModel(services, docs) {
3180
3187
  }
3181
3188
  const views = remeda.pipe(
3182
3189
  parsedViews,
3183
- resolveRelativePaths,
3184
3190
  remeda.indexBy(remeda.prop("id")),
3185
3191
  resolveRulesExtendedViews
3186
3192
  );
@@ -3243,7 +3249,7 @@ class LikeC4ModelBuilder {
3243
3249
  logger.debug("[ModelBuilder] No documents to build model from");
3244
3250
  return null;
3245
3251
  }
3246
- logger.debug(`[ModelBuilder] onValidated (${docs.length} docs)`);
3252
+ logger.debug(`[ModelBuilder] buildModel (${docs.length} docs)`);
3247
3253
  return buildModel(this.services, docs);
3248
3254
  });
3249
3255
  }
@@ -3272,7 +3278,8 @@ class LikeC4ModelBuilder {
3272
3278
  const index = new LikeC4ModelGraph.LikeC4ModelGraph(model);
3273
3279
  const allViews = [];
3274
3280
  for (const view of remeda.values(model.views)) {
3275
- const result = core.isElementView(view) ? LikeC4ModelGraph.computeView(view, index) : LikeC4ModelGraph.computeDynamicView(view, index);
3281
+ const resolvedView = resolveGlobalRules(view, model.globals.styles);
3282
+ const result = core.isElementView(resolvedView) ? LikeC4ModelGraph.computeView(resolvedView, index) : LikeC4ModelGraph.computeDynamicView(resolvedView, index);
3276
3283
  if (!result.isSuccess) {
3277
3284
  logWarnError(result.error);
3278
3285
  continue;
@@ -5830,16 +5837,19 @@ class LikeC4Formatter extends lsp.AbstractFormatter {
5830
5837
  this.removeIndentFromTopLevelStatements(node);
5831
5838
  this.indentContentInBraces(node);
5832
5839
  this.formatSpecificationRule(node);
5840
+ this.formatGlobals(node);
5833
5841
  this.formatElementDeclaration(node);
5834
5842
  this.formatRelation(node);
5843
+ this.formatMetadataProperty(node);
5835
5844
  this.formatView(node);
5836
- this.formatViewRuleStyle(node);
5845
+ this.formatViewRuleGroup(node);
5846
+ this.formatViewRuleGlobalStyle(node);
5837
5847
  this.formatIncludeExcludeExpressions(node);
5838
5848
  this.formatWhereExpression(node);
5849
+ this.formatAutolayoutProperty(node);
5839
5850
  this.formatWithPredicate(node);
5851
+ this.formatViewRuleStyle(node);
5840
5852
  this.formatLeafProperty(node);
5841
- this.formatMetadataProperty(node);
5842
- this.formatAutolayoutProperty(node);
5843
5853
  this.formatLinkProperty(node);
5844
5854
  this.formatNavigateToProperty(node);
5845
5855
  this.formatTags(node);
@@ -5876,13 +5886,13 @@ class LikeC4Formatter extends lsp.AbstractFormatter {
5876
5886
  this.on(node, isInOutRelationExpression)?.keyword("->").prepend(FormattingOptions.oneSpace);
5877
5887
  }
5878
5888
  removeIndentFromTopLevelStatements(node) {
5879
- if (isModel(node) || isSpecificationRule(node) || isModelViews(node) || isLikeC4Lib(node)) {
5889
+ if (isModel(node) || isSpecificationRule(node) || isModelViews(node) || isLikeC4Lib(node) || isGlobals(node)) {
5880
5890
  const formatter = this.getNodeFormatter(node);
5881
- formatter.keywords("specification", "model", "views", "likec4lib").prepend(FormattingOptions.noIndent);
5891
+ formatter.keywords("specification", "model", "views", "likec4lib", "global").prepend(FormattingOptions.noIndent);
5882
5892
  }
5883
5893
  }
5884
5894
  indentContentInBraces(node) {
5885
- if (isLikeC4Lib(node) || isSpecificationRule(node) || isSpecificationElementKind(node) || isSpecificationRelationshipKind(node) || isModel(node) || isElementBody(node) || isExtendElementBody(node) || isRelationBody(node) || isRelationStyleProperty(node) || isMetadataBody(node) || isModelViews(node) || isElementViewBody(node) || isDynamicViewBody(node) || isViewRuleStyle(node) || isCustomElementProperties(node) || isCustomRelationProperties(node) || isElementStyleProperty(node) || isDynamicViewParallelSteps(node)) {
5895
+ if (isLikeC4Lib(node) || isSpecificationRule(node) || isSpecificationElementKind(node) || isSpecificationRelationshipKind(node) || isGlobals(node) || isGlobalStyle(node) || isGlobalStyleGroup(node) || isModel(node) || isElementBody(node) || isExtendElementBody(node) || isRelationBody(node) || isRelationStyleProperty(node) || isMetadataBody(node) || isModelViews(node) || isElementViewBody(node) || isDynamicViewBody(node) || isViewRuleStyle(node) || isViewRuleGroup(node) || isCustomElementProperties(node) || isCustomRelationProperties(node) || isElementStyleProperty(node) || isDynamicViewParallelSteps(node)) {
5886
5896
  const formatter = this.getNodeFormatter(node);
5887
5897
  const openBrace = formatter.keywords("{");
5888
5898
  const closeBrace = formatter.keywords("}");
@@ -5976,6 +5986,15 @@ class LikeC4Formatter extends lsp.AbstractFormatter {
5976
5986
  f.properties("props").prepend(FormattingOptions.oneSpace);
5977
5987
  });
5978
5988
  }
5989
+ formatGlobals(node) {
5990
+ this.on(node, isGlobalStyle, (n, f) => {
5991
+ f.keyword("style").append(FormattingOptions.oneSpace);
5992
+ f.property("id").append(FormattingOptions.oneSpace);
5993
+ });
5994
+ this.on(node, isGlobalStyleGroup, (n, f) => {
5995
+ f.keyword("styleGroup").append(FormattingOptions.oneSpace);
5996
+ });
5997
+ }
5979
5998
  formatSpecificationRule(node) {
5980
5999
  if (isSpecificationElementKind(node) || isSpecificationRelationshipKind(node) || isSpecificationTag(node)) {
5981
6000
  const formatter = this.getNodeFormatter(node);
@@ -5993,6 +6012,16 @@ class LikeC4Formatter extends lsp.AbstractFormatter {
5993
6012
  formatter.keyword("with").prepend(FormattingOptions.oneSpace);
5994
6013
  }
5995
6014
  }
6015
+ formatViewRuleGlobalStyle(node) {
6016
+ this.on(node, isViewRuleGlobalStyle, (n, f) => {
6017
+ f.keywords("global", "style").append(FormattingOptions.oneSpace);
6018
+ });
6019
+ }
6020
+ formatViewRuleGroup(node) {
6021
+ this.on(node, isViewRuleGroup, (n, f) => {
6022
+ f.keyword("group").append(FormattingOptions.oneSpace);
6023
+ });
6024
+ }
5996
6025
  formatViewRuleStyle(node) {
5997
6026
  this.on(node, isViewRuleStyle)?.keyword("style").append(FormattingOptions.oneSpace);
5998
6027
  this.on(node, isElementExpressionsIterator)?.keyword(",").prepend(FormattingOptions.noSpace).append(FormattingOptions.oneSpace);
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.0",
4
+ "version": "1.15.1",
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.0",
116
- "@likec4/log": "1.15.0",
115
+ "@likec4/core": "1.15.1",
116
+ "@likec4/log": "1.15.1",
117
117
  "@msgpack/msgpack": "^3.0.0-beta2",
118
118
  "@smithy/util-base64": "^3.0.0",
119
119
  "fast-equals": "^5.0.1",
@@ -122,7 +122,7 @@
122
122
  "langium": "3.2.0",
123
123
  "object-hash": "^3.0.0",
124
124
  "p-debounce": "^4.0.0",
125
- "remeda": "^2.15.0",
125
+ "remeda": "^2.16.0",
126
126
  "string-hash": "^1.1.3",
127
127
  "strip-indent": "^4.0.0",
128
128
  "type-fest": "4.26.1",
@@ -133,8 +133,8 @@
133
133
  "vscode-uri": "3.0.8"
134
134
  },
135
135
  "devDependencies": {
136
- "@likec4/icons": "1.15.0",
137
- "@likec4/tsconfig": "1.15.0",
136
+ "@likec4/icons": "1.15.1",
137
+ "@likec4/tsconfig": "1.15.1",
138
138
  "@types/node": "^20.16.14",
139
139
  "@types/object-hash": "^3.0.6",
140
140
  "@types/string-hash": "^1.1.3",
@@ -17,17 +17,29 @@ export class LikeC4Formatter extends AbstractFormatter {
17
17
  this.removeIndentFromTopLevelStatements(node)
18
18
  this.indentContentInBraces(node)
19
19
 
20
+ // Specification
20
21
  this.formatSpecificationRule(node)
22
+
23
+ // Globals
24
+ this.formatGlobals(node)
25
+
26
+ // Models
21
27
  this.formatElementDeclaration(node)
22
28
  this.formatRelation(node)
29
+ this.formatMetadataProperty(node)
30
+
31
+ // Views
23
32
  this.formatView(node)
24
- this.formatViewRuleStyle(node)
33
+ this.formatViewRuleGroup(node)
34
+ this.formatViewRuleGlobalStyle(node)
25
35
  this.formatIncludeExcludeExpressions(node)
26
36
  this.formatWhereExpression(node)
37
+ this.formatAutolayoutProperty(node)
27
38
  this.formatWithPredicate(node)
39
+
40
+ // Common
41
+ this.formatViewRuleStyle(node)
28
42
  this.formatLeafProperty(node)
29
- this.formatMetadataProperty(node)
30
- this.formatAutolayoutProperty(node)
31
43
  this.formatLinkProperty(node)
32
44
  this.formatNavigateToProperty(node)
33
45
  this.formatTags(node)
@@ -92,9 +104,10 @@ export class LikeC4Formatter extends AbstractFormatter {
92
104
  || ast.isSpecificationRule(node)
93
105
  || ast.isModelViews(node)
94
106
  || ast.isLikeC4Lib(node)
107
+ || ast.isGlobals(node)
95
108
  ) {
96
109
  const formatter = this.getNodeFormatter(node)
97
- formatter.keywords('specification', 'model', 'views', 'likec4lib')
110
+ formatter.keywords('specification', 'model', 'views', 'likec4lib', 'global')
98
111
  .prepend(FormattingOptions.noIndent)
99
112
  }
100
113
  }
@@ -105,6 +118,9 @@ export class LikeC4Formatter extends AbstractFormatter {
105
118
  || ast.isSpecificationRule(node)
106
119
  || ast.isSpecificationElementKind(node)
107
120
  || ast.isSpecificationRelationshipKind(node)
121
+ || ast.isGlobals(node)
122
+ || ast.isGlobalStyle(node)
123
+ || ast.isGlobalStyleGroup(node)
108
124
  || ast.isModel(node)
109
125
  || ast.isElementBody(node)
110
126
  || ast.isExtendElementBody(node)
@@ -115,6 +131,7 @@ export class LikeC4Formatter extends AbstractFormatter {
115
131
  || ast.isElementViewBody(node)
116
132
  || ast.isDynamicViewBody(node)
117
133
  || ast.isViewRuleStyle(node)
134
+ || ast.isViewRuleGroup(node)
118
135
  || ast.isCustomElementProperties(node)
119
136
  || ast.isCustomRelationProperties(node)
120
137
  || ast.isElementStyleProperty(node)
@@ -270,6 +287,17 @@ export class LikeC4Formatter extends AbstractFormatter {
270
287
  })
271
288
  }
272
289
 
290
+ protected formatGlobals(node: AstNode) {
291
+ this.on(node, ast.isGlobalStyle, (n, f) => {
292
+ f.keyword('style').append(FormattingOptions.oneSpace)
293
+ f.property('id').append(FormattingOptions.oneSpace)
294
+ })
295
+
296
+ this.on(node, ast.isGlobalStyleGroup, (n, f) => {
297
+ f.keyword('styleGroup').append(FormattingOptions.oneSpace)
298
+ })
299
+ }
300
+
273
301
  protected formatSpecificationRule(node: AstNode) {
274
302
  if (
275
303
  ast.isSpecificationElementKind(node)
@@ -300,6 +328,18 @@ export class LikeC4Formatter extends AbstractFormatter {
300
328
  }
301
329
  }
302
330
 
331
+ protected formatViewRuleGlobalStyle(node: AstNode) {
332
+ this.on(node, ast.isViewRuleGlobalStyle, (n, f) => {
333
+ f.keywords('global', 'style').append(FormattingOptions.oneSpace)
334
+ })
335
+ }
336
+
337
+ protected formatViewRuleGroup(node: AstNode) {
338
+ this.on(node, ast.isViewRuleGroup, (n, f) => {
339
+ f.keyword('group').append(FormattingOptions.oneSpace)
340
+ })
341
+ }
342
+
303
343
  protected formatViewRuleStyle(node: AstNode) {
304
344
  this.on(node, ast.isViewRuleStyle)
305
345
  ?.keyword('style').append(FormattingOptions.oneSpace)
@@ -68,7 +68,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
68
68
  SemanticTokenModifiers.readonly
69
69
  ]
70
70
  })
71
- return
71
+ return 'prune'
72
72
  }
73
73
  if (ast.isWhereRelationKind(node) && isTruthy(node.value)) {
74
74
  acceptor({
@@ -97,6 +97,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
97
97
  type: SemanticTokenTypes.type,
98
98
  modifier: [SemanticTokenModifiers.definition]
99
99
  })
100
+ return 'prune'
100
101
  }
101
102
  if (ast.isElementTagExpression(node) && isTruthy(node.tag)) {
102
103
  acceptor({
@@ -105,6 +106,7 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
105
106
  type: SemanticTokenTypes.type,
106
107
  modifier: [SemanticTokenModifiers.definition]
107
108
  })
109
+ return 'prune'
108
110
  }
109
111
  if (ast.isElementRef(node) || ast.isFqnElementRef(node)) {
110
112
  acceptor({
@@ -255,18 +255,23 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
255
255
 
256
256
  return {
257
257
  ...model,
258
+ customColorDefinitions,
258
259
  tags,
259
260
  links,
260
261
  docUri,
261
262
  description,
262
263
  title,
263
- id,
264
- customColorDefinitions
264
+ id
265
265
  }
266
266
  }
267
267
  }
268
268
 
269
- const parsedViews = docs.flatMap(d => map(d.c4Views, toC4View(d)))
269
+ const parsedViews = pipe(
270
+ docs,
271
+ flatMap(d => map(d.c4Views, toC4View(d))),
272
+ // Resolve relative paths and sort by
273
+ resolveRelativePaths
274
+ )
270
275
  // Add index view if not present
271
276
  if (!parsedViews.some(v => v.id === 'index')) {
272
277
  parsedViews.unshift({
@@ -291,7 +296,6 @@ function buildModel(services: LikeC4Services, docs: ParsedLikeC4LangiumDocument[
291
296
 
292
297
  const views = pipe(
293
298
  parsedViews,
294
- resolveRelativePaths,
295
299
  indexBy(prop('id')),
296
300
  resolveRulesExtendedViews
297
301
  )
@@ -362,7 +366,7 @@ export class LikeC4ModelBuilder {
362
366
  logger.debug('[ModelBuilder] No documents to build model from')
363
367
  return null
364
368
  }
365
- logger.debug(`[ModelBuilder] onValidated (${docs.length} docs)`)
369
+ logger.debug(`[ModelBuilder] buildModel (${docs.length} docs)`)
366
370
  return buildModel(this.services, docs)
367
371
  })
368
372
  }
@@ -395,7 +399,10 @@ export class LikeC4ModelBuilder {
395
399
 
396
400
  const allViews = [] as c4.ComputedView[]
397
401
  for (const view of values(model.views)) {
398
- const result = isElementView(view) ? computeView(view, index) : computeDynamicView(view, index)
402
+ const resolvedView = resolveGlobalRules(view, model.globals.styles)
403
+ const result = isElementView(resolvedView)
404
+ ? computeView(resolvedView, index)
405
+ : computeDynamicView(resolvedView, index)
399
406
  if (!result.isSuccess) {
400
407
  logWarnError(result.error)
401
408
  continue
@@ -11,12 +11,16 @@ import {
11
11
  type ElementWhereExpr,
12
12
  type Expression as C4Expression,
13
13
  type Fqn,
14
+ type IncomingExpr as C4IncomingExpr,
15
+ type InOutExpr as C4InOutExpr,
14
16
  isElementRef,
15
17
  isElementWhere,
16
18
  isRelationExpression,
17
19
  isRelationWhere,
18
20
  type NonEmptyArray,
21
+ type OutgoingExpr as C4OutgoingExpr,
19
22
  type Relation,
23
+ type RelationExpr as C4RelationExpr,
20
24
  type RelationID,
21
25
  type RelationshipArrowType,
22
26
  type RelationshipLineType,
@@ -414,6 +418,49 @@ export function $where(
414
418
  }
415
419
  }
416
420
 
421
+ export function $inout(
422
+ expr: InOutExpr | C4ElementExpression
423
+ ): C4InOutExpr {
424
+ const innerExpression = !isString(expr)
425
+ ? expr as C4Expression
426
+ : $expr(expr.replace(/->/g, '').trim() as ElementRefExpr) as any
427
+
428
+ return { inout: innerExpression }
429
+ }
430
+
431
+ export function $incoming(
432
+ expr: IncomingExpr | C4ElementExpression
433
+ ): C4IncomingExpr {
434
+ const innerExpression = !isString(expr)
435
+ ? expr as C4Expression
436
+ : $expr(expr.replace('-> ', '') as ElementRefExpr) as any
437
+
438
+ return { incoming: innerExpression }
439
+ }
440
+
441
+ export function $outgoing(
442
+ expr: OutgoingExpr | C4ElementExpression
443
+ ): C4OutgoingExpr {
444
+ const innerExpression = !isString(expr)
445
+ ? expr as C4Expression
446
+ : $expr(expr.replace(' ->', '') as ElementRefExpr) as any
447
+
448
+ return { outgoing: innerExpression }
449
+ }
450
+
451
+ export function $relation(
452
+ expr: RelationExpr
453
+ ): C4RelationExpr {
454
+ const [source, target] = expr.split(/ -> | <-> /)
455
+ const isBidirectional = expr.includes(' <-> ')
456
+
457
+ return {
458
+ source: $expr(source as ElementRefExpr) as any,
459
+ target: $expr(target as ElementRefExpr) as any,
460
+ ...(isBidirectional && { isBidirectional })
461
+ }
462
+ }
463
+
417
464
  export function $expr(expr: Expression | C4Expression): C4Expression {
418
465
  if (!isString(expr)) {
419
466
  return expr as C4Expression
@@ -422,34 +469,13 @@ export function $expr(expr: Expression | C4Expression): C4Expression {
422
469
  return { wildcard: true }
423
470
  }
424
471
  if (expr.startsWith('->')) {
425
- if (expr.endsWith('->')) {
426
- return {
427
- inout: $expr(expr.replace(/->/g, '').trim() as ElementRefExpr) as any
428
- }
429
- }
430
- return {
431
- incoming: $expr(expr.replace('-> ', '') as ElementRefExpr) as any
432
- }
472
+ return expr.endsWith('->') ? $inout(expr as InOutExpr) : $incoming(expr as IncomingExpr)
433
473
  }
434
474
  if (expr.endsWith(' ->')) {
435
- return {
436
- outgoing: $expr(expr.replace(' ->', '') as ElementRefExpr) as any
437
- }
438
- }
439
- if (expr.includes(' <-> ')) {
440
- const [source, target] = expr.split(' <-> ')
441
- return {
442
- source: $expr(source as ElementRefExpr) as any,
443
- target: $expr(target as ElementRefExpr) as any,
444
- isBidirectional: true
445
- }
475
+ return $outgoing(expr as OutgoingExpr)
446
476
  }
447
- if (expr.includes(' -> ')) {
448
- const [source, target] = expr.split(' -> ')
449
- return {
450
- source: $expr(source as ElementRefExpr) as any,
451
- target: $expr(target as ElementRefExpr) as any
452
- }
477
+ if (expr.includes(' -> ') || expr.includes(' <-> ')) {
478
+ return $relation(expr as RelationExpr)
453
479
  }
454
480
  if (expr.endsWith('._')) {
455
481
  return {
@@ -1,39 +1,8 @@
1
- import type { ComputedEdge, ComputedNode, Element, ViewRule } from '@likec4/core'
2
- import { Expr, nonexhaustive } from '@likec4/core'
3
- import { isNullish, omitBy } from 'remeda'
1
+ import type { ComputedEdge, ComputedNode, ViewRule } from '@likec4/core'
2
+ import { Expr } from '@likec4/core'
3
+ import { isNullish, omitBy, pick } from 'remeda'
4
4
  import { flattenGroupRules } from './applyCustomElementProperties'
5
- import { elementExprToPredicate } from './elementExpressionToPredicate'
6
-
7
- function relationExpressionToPredicates(
8
- expr: Expr.RelationExpression | Expr.RelationWhereExpr
9
- ): (edge: { source: Element; target: Element }) => boolean {
10
- switch (true) {
11
- case Expr.isRelationWhere(expr):
12
- return relationExpressionToPredicates(expr.where.expr)
13
- case Expr.isRelation(expr): {
14
- const isSource = elementExprToPredicate(expr.source)
15
- const isTarget = elementExprToPredicate(expr.target)
16
- return edge => {
17
- return (isSource(edge.source) && isTarget(edge.target))
18
- || (!!expr.isBidirectional && isSource(edge.target) && isTarget(edge.source))
19
- }
20
- }
21
- case Expr.isInOut(expr): {
22
- const isInOut = elementExprToPredicate(expr.inout)
23
- return edge => isInOut(edge.source) || isInOut(edge.target)
24
- }
25
- case Expr.isIncoming(expr): {
26
- const isTarget = elementExprToPredicate(expr.incoming)
27
- return edge => isTarget(edge.target)
28
- }
29
- case Expr.isOutgoing(expr): {
30
- const isSource = elementExprToPredicate(expr.outgoing)
31
- return edge => isSource(edge.source)
32
- }
33
- default:
34
- nonexhaustive(expr)
35
- }
36
- }
5
+ import { relationExpressionToPredicates } from './relationExpressionToPredicates'
37
6
 
38
7
  export function applyCustomRelationProperties(
39
8
  _rules: ViewRule[],
@@ -58,12 +27,12 @@ export function applyCustomRelationProperties(
58
27
  if (!source || !target) {
59
28
  return
60
29
  }
61
- if (satisfies({ source, target })) {
30
+ if (satisfies({ source, target, ...pick(edge, ['kind', 'tags']) })) {
62
31
  edges[i] = {
63
32
  ...edge,
33
+ ...props,
64
34
  label: title ?? edge.label,
65
- isCustomized: true,
66
- ...props
35
+ isCustomized: true
67
36
  }
68
37
  }
69
38
  })
@@ -0,0 +1,43 @@
1
+ import type { Element, Relation } from '@likec4/core'
2
+ import { Expr, nonexhaustive, whereOperatorAsPredicate } from '@likec4/core'
3
+ import { elementExprToPredicate } from './elementExpressionToPredicate'
4
+
5
+ type Predicate<T> = (x: T) => boolean
6
+ export type FilterableEdge = Pick<Relation, 'kind' | 'tags'> & {
7
+ source: Element
8
+ target: Element
9
+ }
10
+
11
+ export function relationExpressionToPredicates<T extends FilterableEdge>(
12
+ expr: Expr.RelationExpression | Expr.RelationWhereExpr
13
+ ): Predicate<T> {
14
+ switch (true) {
15
+ case Expr.isRelationWhere(expr):
16
+ const predicate = relationExpressionToPredicates(expr.where.expr)
17
+ const where = whereOperatorAsPredicate(expr.where.condition)
18
+
19
+ return e => predicate(e) && where(e)
20
+ case Expr.isRelation(expr): {
21
+ const isSource = elementExprToPredicate(expr.source)
22
+ const isTarget = elementExprToPredicate(expr.target)
23
+ return edge => {
24
+ return (isSource(edge.source) && isTarget(edge.target))
25
+ || (!!expr.isBidirectional && isSource(edge.target) && isTarget(edge.source))
26
+ }
27
+ }
28
+ case Expr.isInOut(expr): {
29
+ const isInOut = elementExprToPredicate(expr.inout)
30
+ return edge => isInOut(edge.source) || isInOut(edge.target)
31
+ }
32
+ case Expr.isIncoming(expr): {
33
+ const isTarget = elementExprToPredicate(expr.incoming)
34
+ return edge => isTarget(edge.target)
35
+ }
36
+ case Expr.isOutgoing(expr): {
37
+ const isSource = elementExprToPredicate(expr.outgoing)
38
+ return edge => isSource(edge.source)
39
+ }
40
+ default:
41
+ nonexhaustive(expr)
42
+ }
43
+ }