@likec4/language-server 1.41.0 → 1.42.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 (84) hide show
  1. package/browser/package.json +4 -0
  2. package/browser-worker/package.json +4 -0
  3. package/dist/LikeC4LanguageServices.d.ts +1 -1
  4. package/dist/LikeC4LanguageServices.mjs +3 -2
  5. package/dist/Rpc.mjs +30 -24
  6. package/dist/ast.d.ts +1 -7
  7. package/dist/ast.mjs +0 -10
  8. package/dist/bundled.mjs +4118 -3653
  9. package/dist/documentation/documentation-provider.mjs +1 -1
  10. package/dist/filesystem/FileSystemWatcher.d.ts +2 -2
  11. package/dist/filesystem/index.d.ts +1 -1
  12. package/dist/formatting/LikeC4Formatter.mjs +41 -10
  13. package/dist/formatting/utils.d.ts +3 -3
  14. package/dist/formatting/utils.mjs +1 -1
  15. package/dist/generated/ast.d.ts +35 -16
  16. package/dist/generated/ast.mjs +69 -26
  17. package/dist/generated/grammar.mjs +1 -1
  18. package/dist/lsp/CompletionProvider.mjs +1 -1
  19. package/dist/lsp/DocumentLinkProvider.d.ts +1 -1
  20. package/dist/lsp/DocumentLinkProvider.mjs +1 -1
  21. package/dist/lsp/DocumentSymbolProvider.mjs +1 -1
  22. package/dist/mcp/NoopLikeC4MCPServer.d.ts +1 -1
  23. package/dist/mcp/NoopLikeC4MCPServer.mjs +1 -1
  24. package/dist/mcp/server/StdioLikeC4MCPServer.mjs +4 -1
  25. package/dist/mcp/server/StreamableLikeC4MCPServer.mjs +3 -3
  26. package/dist/mcp/server/WithMCPServer.mjs +2 -2
  27. package/dist/mcp/tools/_common.mjs +2 -2
  28. package/dist/model/builder/MergedSpecification.d.ts +3 -3
  29. package/dist/model/builder/MergedSpecification.mjs +13 -39
  30. package/dist/model/builder/buildModel.mjs +14 -17
  31. package/dist/model/model-builder.d.ts +1 -1
  32. package/dist/model/model-builder.mjs +12 -9
  33. package/dist/model/model-locator.d.ts +5 -0
  34. package/dist/model/model-locator.mjs +40 -3
  35. package/dist/model/model-parser-where.mjs +1 -2
  36. package/dist/model/model-parser.d.ts +19 -2
  37. package/dist/model/parser/Base.mjs +8 -8
  38. package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
  39. package/dist/model/parser/DeploymentModelParser.mjs +7 -7
  40. package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
  41. package/dist/model/parser/FqnRefParser.d.ts +2 -0
  42. package/dist/model/parser/FqnRefParser.mjs +16 -11
  43. package/dist/model/parser/GlobalsParser.d.ts +8 -2
  44. package/dist/model/parser/ModelParser.d.ts +1 -0
  45. package/dist/model/parser/ModelParser.mjs +16 -11
  46. package/dist/model/parser/PredicatesParser.d.ts +1 -0
  47. package/dist/model/parser/SpecificationParser.mjs +4 -4
  48. package/dist/model/parser/ViewsParser.d.ts +12 -2
  49. package/dist/model/parser/ViewsParser.mjs +123 -31
  50. package/dist/model-change/ModelChanges.d.ts +1 -1
  51. package/dist/module.mjs +3 -2
  52. package/dist/protocol.d.ts +28 -4
  53. package/dist/references/scope-computation.mjs +2 -3
  54. package/dist/references/scope-provider.d.ts +2 -2
  55. package/dist/references/scope-provider.mjs +8 -15
  56. package/dist/test/testServices.d.ts +2 -0
  57. package/dist/test/testServices.mjs +10 -11
  58. package/dist/utils/disposable.mjs +2 -2
  59. package/dist/utils/index.mjs +1 -1
  60. package/dist/validation/_shared.d.ts +1 -1
  61. package/dist/validation/deployment-checks.d.ts +1 -1
  62. package/dist/validation/deployment-checks.mjs +4 -1
  63. package/dist/validation/dynamic-view.d.ts +3 -2
  64. package/dist/validation/dynamic-view.mjs +21 -2
  65. package/dist/validation/element-ref.d.ts +2 -2
  66. package/dist/validation/element-ref.mjs +1 -1
  67. package/dist/validation/imports.d.ts +0 -1
  68. package/dist/validation/imports.mjs +0 -5
  69. package/dist/validation/index.d.ts +1 -1
  70. package/dist/validation/index.mjs +19 -13
  71. package/dist/validation/view-predicates/relation-with.d.ts +1 -1
  72. package/dist/validation/view.d.ts +1 -1
  73. package/dist/view-utils/index.d.ts +0 -1
  74. package/dist/view-utils/index.mjs +0 -1
  75. package/dist/views/likec4-views.d.ts +6 -0
  76. package/dist/views/likec4-views.mjs +31 -18
  77. package/dist/workspace/ProjectsManager.d.ts +23 -31
  78. package/dist/workspace/ProjectsManager.mjs +78 -89
  79. package/dist/workspace/WorkspaceManager.mjs +1 -1
  80. package/likec4lib/package.json +4 -0
  81. package/package.json +25 -29
  82. package/protocol/package.json +4 -0
  83. package/dist/view-utils/resolve-relative-paths.d.ts +0 -2
  84. package/dist/view-utils/resolve-relative-paths.mjs +0 -78
@@ -1,18 +1,18 @@
1
1
  import {
2
+ exact,
2
3
  FqnRef,
3
4
  invariant,
4
5
  isNonEmptyArray,
5
6
  LinkedList,
6
7
  nameFromFqn,
7
8
  nonexhaustive,
8
- nonNullable,
9
- omitUndefined
9
+ nonNullable
10
10
  } from "@likec4/core";
11
11
  import { loggable } from "@likec4/log";
12
12
  import { filter, first, isDefined, isEmpty, isTruthy, mapToObj, pipe } from "remeda";
13
13
  import {
14
14
  ast,
15
- toRelationshipStyleExcludeDefaults
15
+ toRelationshipStyle
16
16
  } from "../../ast.mjs";
17
17
  import { serverLogger } from "../../logger.mjs";
18
18
  import { stringHash } from "../../utils/stringHash.mjs";
@@ -91,7 +91,7 @@ export function DeploymentModelParser(B) {
91
91
  summary: astNode.summary
92
92
  });
93
93
  const links = this.convertLinks(astNode.body);
94
- return omitUndefined({
94
+ return exact({
95
95
  id,
96
96
  kind,
97
97
  title: title ?? nameFromFqn(id),
@@ -121,7 +121,7 @@ export function DeploymentModelParser(B) {
121
121
  summary: astNode.summary
122
122
  });
123
123
  const links = this.convertLinks(astNode.body);
124
- return omitUndefined({
124
+ return exact({
125
125
  id,
126
126
  element: target,
127
127
  tags: tags ?? void 0,
@@ -194,7 +194,7 @@ export function DeploymentModelParser(B) {
194
194
  source.deployment,
195
195
  target.deployment
196
196
  );
197
- return omitUndefined({
197
+ return exact({
198
198
  id,
199
199
  source,
200
200
  target,
@@ -203,7 +203,7 @@ export function DeploymentModelParser(B) {
203
203
  kind,
204
204
  tags: tags ?? void 0,
205
205
  ...isNonEmptyArray(links) && { links },
206
- ...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid),
206
+ ...toRelationshipStyle(styleProp?.props, isValid),
207
207
  navigateTo,
208
208
  astPath
209
209
  });
@@ -20,6 +20,7 @@ export declare function DeploymentViewParser<TBase extends WithExpressionV2 & Wi
20
20
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
21
21
  parseRelationExprOrWith(astNode: ast.RelationExprOrWith): c4.RelationExpr.Any;
22
22
  parseRelationExprWith(astNode: ast.RelationExprWith): c4.RelationExpr.Custom;
23
+ parseCustomRelationProperties(custom: ast.CustomRelationProperties | undefined): import("type-fest").Except<c4.RelationExpr.Custom["customRelation"], "expr">;
23
24
  parseRelationExprOrWhere(astNode: ast.RelationExprOrWhere): c4.RelationExpr.OrWhere;
24
25
  parseRelationExprWhere(astNode: ast.RelationExprWhere): c4.RelationExpr.Where;
25
26
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr.OrWhere;
@@ -1,5 +1,6 @@
1
1
  import type * as c4 from '@likec4/core';
2
2
  import { type AstNode } from 'langium';
3
+ import type { Except } from 'type-fest';
3
4
  import { ast } from '../../ast';
4
5
  import { type Base } from './Base';
5
6
  export type WithExpressionV2 = ReturnType<typeof ExpressionV2Parser>;
@@ -16,6 +17,7 @@ export declare function ExpressionV2Parser<TBase extends Base>(B: TBase): {
16
17
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
17
18
  parseRelationExprOrWith(astNode: ast.RelationExprOrWith): c4.RelationExpr.Any;
18
19
  parseRelationExprWith(astNode: ast.RelationExprWith): c4.RelationExpr.Custom;
20
+ parseCustomRelationProperties(custom: ast.CustomRelationProperties | undefined): Except<c4.RelationExpr.Custom["customRelation"], "expr">;
19
21
  parseRelationExprOrWhere(astNode: ast.RelationExprOrWhere): c4.RelationExpr.OrWhere;
20
22
  parseRelationExprWhere(astNode: ast.RelationExprWhere): c4.RelationExpr.Where;
21
23
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr.OrWhere;
@@ -273,49 +273,54 @@ export function ExpressionV2Parser(B) {
273
273
  }
274
274
  parseRelationExprWith(astNode) {
275
275
  const expr = this.parseRelationExprOrWhere(astNode.subject);
276
- const props = astNode.custom?.props ?? [];
276
+ const customProps = this.parseCustomRelationProperties(astNode.custom);
277
+ return {
278
+ customRelation: {
279
+ ...customProps,
280
+ expr
281
+ }
282
+ };
283
+ }
284
+ parseCustomRelationProperties(custom) {
285
+ const props = custom?.props ?? [];
277
286
  return props.reduce(
278
287
  (acc, prop) => {
279
288
  if (ast.isRelationStringProperty(prop) || ast.isNotationProperty(prop) || ast.isNotesProperty(prop)) {
280
289
  const value = isTruthy(prop.value) ? removeIndent(parseMarkdownAsString(prop.value)) : void 0;
281
290
  if (value) {
282
- acc.customRelation[prop.key] = value;
291
+ acc[prop.key] = value;
283
292
  }
284
293
  return acc;
285
294
  }
286
295
  if (ast.isArrowProperty(prop)) {
287
296
  if (isTruthy(prop.value)) {
288
- acc.customRelation[prop.key] = prop.value;
297
+ acc[prop.key] = prop.value;
289
298
  }
290
299
  return acc;
291
300
  }
292
301
  if (ast.isColorProperty(prop)) {
293
302
  const value = toColor(prop);
294
303
  if (isTruthy(value)) {
295
- acc.customRelation[prop.key] = value;
304
+ acc[prop.key] = value;
296
305
  }
297
306
  return acc;
298
307
  }
299
308
  if (ast.isLineProperty(prop)) {
300
309
  if (isTruthy(prop.value)) {
301
- acc.customRelation[prop.key] = prop.value;
310
+ acc[prop.key] = prop.value;
302
311
  }
303
312
  return acc;
304
313
  }
305
314
  if (ast.isRelationNavigateToProperty(prop)) {
306
315
  const viewId = prop.value.view.ref?.name;
307
316
  if (isTruthy(viewId)) {
308
- acc.customRelation.navigateTo = viewId;
317
+ acc[prop.key] = viewId;
309
318
  }
310
319
  return acc;
311
320
  }
312
321
  nonexhaustive(prop);
313
322
  },
314
- {
315
- customRelation: {
316
- expr
317
- }
318
- }
323
+ {}
319
324
  );
320
325
  }
321
326
  parseRelationExprOrWhere(astNode) {
@@ -20,8 +20,13 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
20
20
  parseDynamicElementView(astNode: ast.DynamicView, additionalStyles: any[]): import("../../ast").ParsedAstDynamicView;
21
21
  parseDynamicViewRule(astRule: ast.DynamicViewRule): c4.DynamicViewRule;
22
22
  parseDynamicViewIncludePredicate(astRule: ast.DynamicViewIncludePredicate): c4.DynamicViewIncludeRule;
23
- parseDynamicParallelSteps(node: ast.DynamicViewParallelSteps): c4.DynamicViewParallelSteps;
24
- parseDynamicStep(node: ast.DynamicViewStep): c4.DynamicViewStep;
23
+ parseDynamicParallelSteps(node: ast.DynamicViewParallelSteps): c4.DynamicStepsParallel;
24
+ parseDynamicStep(node: ast.DynamicViewStep): c4.DynamicStep | c4.DynamicStepsSeries;
25
+ recursiveParseDynamicStepChain(node: ast.DynamicStepChain, callstack?: Array<[source: c4.Fqn, target: c4.Fqn]>): c4.DynamicStep[];
26
+ parseDynamicStepSingle(node: ast.DynamicStepSingle): c4.DynamicStep;
27
+ parseAbstractDynamicStep(astnode: ast.AbstractDynamicStep): import("type-fest").Writable<import("type-fest").Except<c4.DynamicStep, "source", {
28
+ requireExactProps: true;
29
+ }>>;
25
30
  parsePredicate(astNode: ast.ExpressionV2): c4.ModelExpression;
26
31
  parseElementPredicate(astNode: ast.FqnExprOrWith): c4.ModelFqnExpr.Any;
27
32
  parseElementPredicateOrWhere(astNode: ast.FqnExprOrWhere): c4.ModelFqnExpr.OrWhere;
@@ -44,6 +49,7 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
44
49
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
45
50
  parseRelationExprOrWith(astNode: ast.RelationExprOrWith): c4.RelationExpr.Any;
46
51
  parseRelationExprWith(astNode: ast.RelationExprWith): c4.RelationExpr.Custom;
52
+ parseCustomRelationProperties(custom: ast.CustomRelationProperties | undefined): import("type-fest").Except<c4.RelationExpr.Custom["customRelation"], "expr">;
47
53
  parseRelationExprOrWhere(astNode: ast.RelationExprOrWhere): c4.RelationExpr.OrWhere;
48
54
  parseRelationExprWhere(astNode: ast.RelationExprWhere): c4.RelationExpr.Where;
49
55
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr.OrWhere;
@@ -21,6 +21,7 @@ export declare function ModelParser<TBase extends WithExpressionV2>(B: TBase): {
21
21
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
22
22
  parseRelationExprOrWith(astNode: ast.RelationExprOrWith): c4.RelationExpr.Any;
23
23
  parseRelationExprWith(astNode: ast.RelationExprWith): c4.RelationExpr.Custom;
24
+ parseCustomRelationProperties(custom: ast.CustomRelationProperties | undefined): import("type-fest").Except<c4.RelationExpr.Custom["customRelation"], "expr">;
24
25
  parseRelationExprOrWhere(astNode: ast.RelationExprOrWhere): c4.RelationExpr.OrWhere;
25
26
  parseRelationExprWhere(astNode: ast.RelationExprWhere): c4.RelationExpr.Where;
26
27
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr.OrWhere;
@@ -1,10 +1,10 @@
1
1
  import { invariant, isNonEmptyArray, LinkedList, nonexhaustive, nonNullable } from "@likec4/core";
2
- import { FqnRef, omitUndefined } from "@likec4/core/types";
2
+ import { exact, FqnRef } from "@likec4/core/types";
3
3
  import { loggable } from "@likec4/log";
4
4
  import { filter, first, isDefined, isEmpty, isTruthy, map, mapToObj, pipe } from "remeda";
5
5
  import {
6
6
  ast,
7
- toRelationshipStyleExcludeDefaults
7
+ toRelationshipStyle
8
8
  } from "../../ast.mjs";
9
9
  import { logger as mainLogger } from "../../logger.mjs";
10
10
  import { stringHash } from "../../utils/stringHash.mjs";
@@ -88,7 +88,7 @@ ${error}`, {
88
88
  technology: _technology
89
89
  });
90
90
  const links = this.parseLinks(astNode.body);
91
- return omitUndefined({
91
+ return exact({
92
92
  id,
93
93
  kind,
94
94
  astPath,
@@ -109,7 +109,7 @@ ${error}`, {
109
109
  if (!tags && isEmpty(metadata ?? {}) && isEmpty(links)) {
110
110
  return null;
111
111
  }
112
- return omitUndefined({
112
+ return exact({
113
113
  id,
114
114
  astPath,
115
115
  metadata,
@@ -123,12 +123,17 @@ ${error}`, {
123
123
  invariant(FqnRef.isModelRef(source) || FqnRef.isImportRef(source), "Relation source must be a model reference");
124
124
  return source;
125
125
  }
126
- if (!ast.isElementBody(node.$container)) {
127
- throw new Error("RelationRefError: Invalid container for sourceless relation");
126
+ if (ast.isElementBody(node.$container)) {
127
+ return {
128
+ model: this.resolveFqn(node.$container.$container)
129
+ };
128
130
  }
129
- return {
130
- model: this.resolveFqn(node.$container.$container)
131
- };
131
+ if (ast.isExtendElementBody(node.$container)) {
132
+ return {
133
+ model: this.resolveFqn(node.$container.$container)
134
+ };
135
+ }
136
+ throw new Error("RelationRefError: Invalid container for sourceless relation");
132
137
  }
133
138
  parseRelation(astNode) {
134
139
  const isValid = this.isValid;
@@ -165,7 +170,7 @@ ${error}`, {
165
170
  source.model,
166
171
  target.model
167
172
  );
168
- return omitUndefined({
173
+ return exact({
169
174
  id,
170
175
  astPath,
171
176
  source,
@@ -178,7 +183,7 @@ ${error}`, {
178
183
  navigateTo: navigateTo ? navigateTo : void 0,
179
184
  description,
180
185
  technology,
181
- ...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid)
186
+ ...toRelationshipStyle(styleProp?.props, isValid)
182
187
  });
183
188
  }
184
189
  };
@@ -26,6 +26,7 @@ export declare function PredicatesParser<TBase extends WithExpressionV2>(B: TBas
26
26
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
27
27
  parseRelationExprOrWith(astNode: ast.RelationExprOrWith): c4.RelationExpr.Any;
28
28
  parseRelationExprWith(astNode: ast.RelationExprWith): c4.RelationExpr.Custom;
29
+ parseCustomRelationProperties(custom: ast.CustomRelationProperties | undefined): import("type-fest").Except<c4.RelationExpr.Custom["customRelation"], "expr">;
29
30
  parseRelationExprOrWhere(astNode: ast.RelationExprOrWhere): c4.RelationExpr.OrWhere;
30
31
  parseRelationExprWhere(astNode: ast.RelationExprWhere): c4.RelationExpr.Where;
31
32
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr.OrWhere;
@@ -1,9 +1,9 @@
1
1
  import * as c4 from "@likec4/core";
2
- import { omitUndefined } from "@likec4/core";
2
+ import { exact } from "@likec4/core";
3
3
  import { nonNullable } from "@likec4/core/utils";
4
4
  import { loggable } from "@likec4/log";
5
5
  import { filter, isNonNullish, isNullish, isTruthy, mapToObj, omitBy, pipe } from "remeda";
6
- import { ast, parseMarkdownAsString, toRelationshipStyleExcludeDefaults } from "../../ast.mjs";
6
+ import { ast, parseMarkdownAsString, toRelationshipStyle } from "../../ast.mjs";
7
7
  import { serverLogger } from "../../logger.mjs";
8
8
  import { removeIndent } from "./Base.mjs";
9
9
  const logger = serverLogger.getChild("SpecificationParser");
@@ -52,7 +52,7 @@ export function SpecificationParser(B) {
52
52
  );
53
53
  c4Specification.relationships[kindName] = {
54
54
  ...bodyProps,
55
- ...toRelationshipStyleExcludeDefaults(props, this.isValid)
55
+ ...toRelationshipStyle(props.filter(ast.isRelationshipStyleProperty), this.isValid)
56
56
  };
57
57
  } catch (e) {
58
58
  logger.warn(loggable(e));
@@ -107,7 +107,7 @@ export function SpecificationParser(B) {
107
107
  const baseProps = this.parseBaseProps(bodyProps);
108
108
  const notation = removeIndent(parseMarkdownAsString(bodyProps.notation));
109
109
  return {
110
- [kindName]: omitUndefined({
110
+ [kindName]: exact({
111
111
  ...baseProps,
112
112
  notation,
113
113
  tags: tags ?? void 0,
@@ -1,4 +1,5 @@
1
1
  import * as c4 from '@likec4/core';
2
+ import type { Except, Writable } from 'type-fest';
2
3
  import { type ParsedAstDynamicView, type ParsedAstElementView, ast } from '../../ast';
3
4
  import type { WithDeploymentView } from './DeploymentViewParser';
4
5
  import type { WithPredicates } from './PredicatesParser';
@@ -18,8 +19,16 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
18
19
  parseDynamicElementView(astNode: ast.DynamicView, additionalStyles: ViewRuleStyleOrGlobalRef[]): ParsedAstDynamicView;
19
20
  parseDynamicViewRule(astRule: ast.DynamicViewRule): c4.DynamicViewRule;
20
21
  parseDynamicViewIncludePredicate(astRule: ast.DynamicViewIncludePredicate): c4.DynamicViewIncludeRule;
21
- parseDynamicParallelSteps(node: ast.DynamicViewParallelSteps): c4.DynamicViewParallelSteps;
22
- parseDynamicStep(node: ast.DynamicViewStep): c4.DynamicViewStep;
22
+ parseDynamicParallelSteps(node: ast.DynamicViewParallelSteps): c4.DynamicStepsParallel;
23
+ /**
24
+ * @returns non-empty array in case of step chain A -> B -> C
25
+ */
26
+ parseDynamicStep(node: ast.DynamicViewStep): c4.DynamicStep | c4.DynamicStepsSeries;
27
+ recursiveParseDynamicStepChain(node: ast.DynamicStepChain, callstack?: Array<[source: c4.Fqn, target: c4.Fqn]>): c4.DynamicStep[];
28
+ parseDynamicStepSingle(node: ast.DynamicStepSingle): c4.DynamicStep;
29
+ parseAbstractDynamicStep(astnode: ast.AbstractDynamicStep): Writable<Except<c4.DynamicStep, "source", {
30
+ requireExactProps: true;
31
+ }>>;
23
32
  parsePredicate(astNode: ast.ExpressionV2): c4.ModelExpression;
24
33
  parseElementPredicate(astNode: ast.FqnExprOrWith): c4.ModelFqnExpr.Any;
25
34
  parseElementPredicateOrWhere(astNode: ast.FqnExprOrWhere): c4.ModelFqnExpr.OrWhere;
@@ -42,6 +51,7 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
42
51
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
43
52
  parseRelationExprOrWith(astNode: ast.RelationExprOrWith): c4.RelationExpr.Any;
44
53
  parseRelationExprWith(astNode: ast.RelationExprWith): c4.RelationExpr.Custom;
54
+ parseCustomRelationProperties(custom: ast.CustomRelationProperties | undefined): Except<c4.RelationExpr.Custom["customRelation"], "expr">;
45
55
  parseRelationExprOrWhere(astNode: ast.RelationExprOrWhere): c4.RelationExpr.OrWhere;
46
56
  parseRelationExprWhere(astNode: ast.RelationExprWhere): c4.RelationExpr.Where;
47
57
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr.OrWhere;
@@ -1,6 +1,7 @@
1
1
  import * as c4 from "@likec4/core";
2
2
  import { invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
3
- import { filter, find, isArray, isDefined, isEmpty, isNonNullish, isTruthy, mapToObj, pipe } from "remeda";
3
+ import { loggable } from "@likec4/log";
4
+ import { filter, find, isDefined, isEmpty, isNonNullish, isNumber, isTruthy, last, mapToObj, pipe } from "remeda";
4
5
  import {
5
6
  ast,
6
7
  parseMarkdownAsString,
@@ -8,11 +9,12 @@ import {
8
9
  toColor,
9
10
  ViewOps
10
11
  } from "../../ast.mjs";
11
- import { logger, logWarnError } from "../../logger.mjs";
12
+ import { logger as mainLogger } from "../../logger.mjs";
12
13
  import { stringHash } from "../../utils/index.mjs";
13
14
  import { elementRef } from "../../utils/elementRef.mjs";
14
15
  import { parseViewManualLayout } from "../../view-utils/manual-layout.mjs";
15
16
  import { removeIndent, toSingleLine } from "./Base.mjs";
17
+ const logger = mainLogger.getChild("ViewsParser");
16
18
  export function ViewsParser(B) {
17
19
  return class ViewsParser extends B {
18
20
  parseViews() {
@@ -22,7 +24,7 @@ export function ViewsParser(B) {
22
24
  try {
23
25
  return isValid(s) ? this.parseViewRuleStyleOrGlobalRef(s) : [];
24
26
  } catch (e) {
25
- logWarnError(e);
27
+ logger.warn(loggable(e));
26
28
  return [];
27
29
  }
28
30
  });
@@ -50,7 +52,7 @@ export function ViewsParser(B) {
50
52
  view2.title = folder + " / " + (view2.title || view2.id);
51
53
  }
52
54
  } catch (e) {
53
- logWarnError(e);
55
+ logger.warn(loggable(e));
54
56
  }
55
57
  }
56
58
  }
@@ -102,7 +104,7 @@ export function ViewsParser(B) {
102
104
  try {
103
105
  return this.isValid(n) ? this.parseElementViewRule(n) : [];
104
106
  } catch (e) {
105
- logWarnError(e);
107
+ logger.warn(loggable(e));
106
108
  return [];
107
109
  }
108
110
  })
@@ -149,7 +151,7 @@ export function ViewsParser(B) {
149
151
  exprs.unshift(expr);
150
152
  }
151
153
  } catch (e) {
152
- logWarnError(e);
154
+ logger.warn(loggable(e));
153
155
  }
154
156
  if (!prev) {
155
157
  break;
@@ -189,7 +191,7 @@ export function ViewsParser(B) {
189
191
  }
190
192
  nonexhaustive(rule);
191
193
  } catch (e) {
192
- logWarnError(e);
194
+ logger.warn(loggable(e));
193
195
  }
194
196
  }
195
197
  return {
@@ -255,7 +257,7 @@ export function ViewsParser(B) {
255
257
  try {
256
258
  return isValid(n) ? this.parseDynamicViewRule(n) : [];
257
259
  } catch (e) {
258
- logWarnError(e);
260
+ logger.warn(loggable(e));
259
261
  return [];
260
262
  }
261
263
  }, [])
@@ -270,7 +272,7 @@ export function ViewsParser(B) {
270
272
  }
271
273
  }
272
274
  } catch (e) {
273
- logWarnError(e);
275
+ logger.warn(loggable(e));
274
276
  }
275
277
  return acc;
276
278
  }, []),
@@ -304,46 +306,113 @@ export function ViewsParser(B) {
304
306
  }
305
307
  }
306
308
  } catch (e) {
307
- logWarnError(e);
309
+ logger.warn(loggable(e));
308
310
  }
309
311
  iter = iter.prev;
310
312
  }
311
313
  return { include };
312
314
  }
313
315
  parseDynamicParallelSteps(node) {
316
+ const parallelId = pathInsideDynamicView(node);
317
+ const __parallel = node.steps.map((step) => this.parseDynamicStep(step));
318
+ invariant(isNonEmptyArray(__parallel), "Dynamic parallel steps must have at least one step");
314
319
  return {
315
- __parallel: node.steps.map((step) => this.parseDynamicStep(step))
320
+ parallelId,
321
+ __parallel
316
322
  };
317
323
  }
324
+ /**
325
+ * @returns non-empty array in case of step chain A -> B -> C
326
+ */
318
327
  parseDynamicStep(node) {
328
+ if (ast.isDynamicStepSingle(node)) {
329
+ invariant(this.isValid(node));
330
+ return this.parseDynamicStepSingle(node);
331
+ }
332
+ const __series = this.recursiveParseDynamicStepChain(node);
333
+ invariant(isNonEmptyArray(__series), "Dynamic step chain must have at least one step");
334
+ return {
335
+ seriesId: pathInsideDynamicView(node),
336
+ __series
337
+ };
338
+ }
339
+ recursiveParseDynamicStepChain(node, callstack) {
340
+ if (ast.isDynamicStepSingle(node.source)) {
341
+ if (!this.isValid(node.source)) {
342
+ return [];
343
+ }
344
+ const previous2 = this.parseDynamicStepSingle(node.source);
345
+ if (previous2.isBackward) {
346
+ return [];
347
+ }
348
+ const thisStep2 = {
349
+ ...this.parseAbstractDynamicStep(node),
350
+ source: previous2.target
351
+ };
352
+ if (thisStep2.target === previous2.source) {
353
+ thisStep2.isBackward = true;
354
+ } else if (callstack) {
355
+ callstack.push([previous2.source, previous2.target]);
356
+ callstack.push([thisStep2.source, thisStep2.target]);
357
+ }
358
+ return [previous2, thisStep2];
359
+ }
360
+ callstack ??= [];
361
+ const allprevious = this.recursiveParseDynamicStepChain(node.source, callstack);
362
+ if (!isNonEmptyArray(allprevious) || !this.isValid(node)) {
363
+ return [];
364
+ }
365
+ const previous = last(allprevious);
366
+ const thisStep = {
367
+ ...this.parseAbstractDynamicStep(node),
368
+ source: previous.target
369
+ };
370
+ const index = callstack.findIndex(([source, target]) => source === thisStep.target && target === thisStep.source);
371
+ if (index !== -1) {
372
+ thisStep.isBackward = true;
373
+ callstack.splice(index, callstack.length - index);
374
+ } else {
375
+ callstack.push([thisStep.source, thisStep.target]);
376
+ }
377
+ return [...allprevious, thisStep];
378
+ }
379
+ parseDynamicStepSingle(node) {
319
380
  const sourceEl = elementRef(node.source);
320
381
  if (!sourceEl) {
321
382
  throw new Error("Invalid reference to source");
322
383
  }
323
- const targetEl = elementRef(node.target);
324
- if (!targetEl) {
325
- throw new Error("Invalid reference to target");
326
- }
327
- let source = this.resolveFqn(sourceEl);
328
- let target = this.resolveFqn(targetEl);
329
- const title = removeIndent(node.title) ?? null;
330
- let step = {
331
- source,
332
- target,
333
- title
384
+ let baseStep = {
385
+ ...this.parseAbstractDynamicStep(node),
386
+ source: this.resolveFqn(sourceEl)
334
387
  };
335
388
  if (node.isBackward) {
336
- step = {
337
- source: target,
338
- target: source,
339
- title,
389
+ baseStep = {
390
+ ...baseStep,
391
+ source: baseStep.target,
392
+ target: baseStep.source,
340
393
  isBackward: true
341
394
  };
342
395
  }
343
- if (!isArray(node.custom?.props)) {
344
- return step;
396
+ return baseStep;
397
+ }
398
+ parseAbstractDynamicStep(astnode) {
399
+ const targetEl = elementRef(astnode.target);
400
+ if (!targetEl) {
401
+ throw new Error("Invalid reference to target");
402
+ }
403
+ const step = {
404
+ target: this.resolveFqn(targetEl),
405
+ astPath: pathInsideDynamicView(astnode)
406
+ };
407
+ const title = removeIndent(astnode.title);
408
+ if (title) {
409
+ step.title = title;
410
+ }
411
+ const kind = astnode.kind?.ref?.name ?? astnode.dotKind?.kind.ref?.name;
412
+ if (kind) {
413
+ step.kind = kind;
345
414
  }
346
- for (const prop of node.custom.props) {
415
+ for (const prop of astnode.custom?.props ?? []) {
347
416
  try {
348
417
  switch (true) {
349
418
  case ast.isRelationNavigateToProperty(prop): {
@@ -356,7 +425,14 @@ export function ViewsParser(B) {
356
425
  case ast.isRelationStringProperty(prop):
357
426
  case ast.isNotationProperty(prop): {
358
427
  if (isDefined(prop.value)) {
359
- step[prop.key] = removeIndent(parseMarkdownAsString(prop.value)) ?? "";
428
+ if (prop.key === "description") {
429
+ const value = removeIndent(prop.value);
430
+ if (value) {
431
+ step.description = value;
432
+ }
433
+ } else {
434
+ step[prop.key] = removeIndent(parseMarkdownAsString(prop.value)) ?? "";
435
+ }
360
436
  }
361
437
  break;
362
438
  }
@@ -389,10 +465,26 @@ export function ViewsParser(B) {
389
465
  nonexhaustive(prop);
390
466
  }
391
467
  } catch (e) {
392
- logWarnError(e);
468
+ logger.warn(loggable(e));
393
469
  }
394
470
  }
395
471
  return step;
396
472
  }
397
473
  };
398
474
  }
475
+ function pathInsideDynamicView(_node) {
476
+ let node = _node;
477
+ let path = [];
478
+ while (!ast.isDynamicViewBody(node)) {
479
+ if (isNumber(node.$containerIndex)) {
480
+ path.unshift(
481
+ `@${node.$containerIndex}`
482
+ );
483
+ }
484
+ path.unshift(
485
+ `/${node.$containerProperty ?? "__invalid__"}`
486
+ );
487
+ node = node.$container;
488
+ }
489
+ return path.join("");
490
+ }
@@ -1,5 +1,5 @@
1
1
  import { Location, Range, TextEdit } from 'vscode-languageserver-types';
2
- import { type ParsedLikeC4LangiumDocument } from '../ast';
2
+ import type { ParsedLikeC4LangiumDocument } from '../ast';
3
3
  import type { LikeC4Services } from '../module';
4
4
  import type { ChangeView } from '../protocol';
5
5
  export declare class LikeC4ModelChanges {
package/dist/module.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { onNextTick } from "@likec4/core/utils";
1
2
  import { GraphvizWasmAdapter, QueueGraphvizLayoter } from "@likec4/layouts";
2
3
  import {
3
4
  DocumentState,
@@ -144,9 +145,9 @@ export function createLanguageServices(context, module, module2, module3) {
144
145
  shared.ServiceRegistry.register(likec4);
145
146
  registerValidationChecks(likec4);
146
147
  if (!context.connection) {
147
- shared.workspace.ConfigurationProvider.initialized({});
148
+ void shared.workspace.ConfigurationProvider.initialized({});
148
149
  } else {
149
- likec4.Rpc.init();
150
+ onNextTick(() => likec4.Rpc.init());
150
151
  }
151
152
  return { shared, likec4 };
152
153
  }
@@ -176,18 +176,42 @@ export declare namespace BuildDocuments {
176
176
  * If LSP has multiple projects, the projectId is required.
177
177
  */
178
178
  export declare namespace Locate {
179
- type Params = {
179
+ type Params =
180
+ /**
181
+ * Locate an element by its fqn
182
+ */
183
+ {
180
184
  element: Fqn;
181
185
  projectId?: string | undefined;
182
186
  property?: string;
183
- } | {
187
+ }
188
+ /**
189
+ * Locate a relation by its id
190
+ */
191
+ | {
184
192
  projectId?: string | undefined;
185
193
  relation: RelationId;
186
- } | {
194
+ }
195
+ /**
196
+ * Locate a deployment by its fqn
197
+ */
198
+ | {
187
199
  deployment: DeploymentFqn;
188
200
  projectId?: string | undefined;
189
201
  property?: string;
190
- } | {
202
+ }
203
+ /**
204
+ * Locate a step in a dynamic view by its astPath
205
+ */
206
+ | {
207
+ view: ViewId;
208
+ astPath: string;
209
+ projectId?: string | undefined;
210
+ }
211
+ /**
212
+ * Locate a view by its id
213
+ */
214
+ | {
191
215
  view: ViewId;
192
216
  projectId?: string | undefined;
193
217
  };