@likec4/language-server 1.41.0 → 1.42.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.
Files changed (85) 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 +4125 -3660
  9. package/dist/documentation/documentation-provider.mjs +1 -1
  10. package/dist/filesystem/FileSystemWatcher.d.ts +2 -2
  11. package/dist/filesystem/LikeC4FileSystem.mjs +10 -4
  12. package/dist/filesystem/index.d.ts +1 -1
  13. package/dist/formatting/LikeC4Formatter.mjs +41 -10
  14. package/dist/formatting/utils.d.ts +3 -3
  15. package/dist/formatting/utils.mjs +1 -1
  16. package/dist/generated/ast.d.ts +35 -16
  17. package/dist/generated/ast.mjs +69 -26
  18. package/dist/generated/grammar.mjs +1 -1
  19. package/dist/lsp/CompletionProvider.mjs +1 -1
  20. package/dist/lsp/DocumentLinkProvider.d.ts +1 -1
  21. package/dist/lsp/DocumentLinkProvider.mjs +1 -1
  22. package/dist/lsp/DocumentSymbolProvider.mjs +1 -1
  23. package/dist/mcp/NoopLikeC4MCPServer.d.ts +1 -1
  24. package/dist/mcp/NoopLikeC4MCPServer.mjs +1 -1
  25. package/dist/mcp/server/StdioLikeC4MCPServer.mjs +4 -1
  26. package/dist/mcp/server/StreamableLikeC4MCPServer.mjs +3 -3
  27. package/dist/mcp/server/WithMCPServer.mjs +2 -2
  28. package/dist/mcp/tools/_common.mjs +2 -2
  29. package/dist/model/builder/MergedSpecification.d.ts +3 -3
  30. package/dist/model/builder/MergedSpecification.mjs +13 -39
  31. package/dist/model/builder/buildModel.mjs +14 -17
  32. package/dist/model/model-builder.d.ts +1 -1
  33. package/dist/model/model-builder.mjs +12 -9
  34. package/dist/model/model-locator.d.ts +5 -0
  35. package/dist/model/model-locator.mjs +40 -3
  36. package/dist/model/model-parser-where.mjs +1 -2
  37. package/dist/model/model-parser.d.ts +19 -2
  38. package/dist/model/parser/Base.mjs +8 -8
  39. package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
  40. package/dist/model/parser/DeploymentModelParser.mjs +7 -7
  41. package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
  42. package/dist/model/parser/FqnRefParser.d.ts +2 -0
  43. package/dist/model/parser/FqnRefParser.mjs +16 -11
  44. package/dist/model/parser/GlobalsParser.d.ts +8 -2
  45. package/dist/model/parser/ModelParser.d.ts +1 -0
  46. package/dist/model/parser/ModelParser.mjs +16 -11
  47. package/dist/model/parser/PredicatesParser.d.ts +1 -0
  48. package/dist/model/parser/SpecificationParser.mjs +4 -4
  49. package/dist/model/parser/ViewsParser.d.ts +12 -2
  50. package/dist/model/parser/ViewsParser.mjs +123 -31
  51. package/dist/model-change/ModelChanges.d.ts +1 -1
  52. package/dist/module.mjs +3 -2
  53. package/dist/protocol.d.ts +28 -4
  54. package/dist/references/scope-computation.mjs +2 -3
  55. package/dist/references/scope-provider.d.ts +2 -2
  56. package/dist/references/scope-provider.mjs +8 -15
  57. package/dist/test/testServices.d.ts +2 -0
  58. package/dist/test/testServices.mjs +10 -11
  59. package/dist/utils/disposable.mjs +2 -2
  60. package/dist/utils/index.mjs +1 -1
  61. package/dist/validation/_shared.d.ts +1 -1
  62. package/dist/validation/deployment-checks.d.ts +1 -1
  63. package/dist/validation/deployment-checks.mjs +4 -1
  64. package/dist/validation/dynamic-view.d.ts +3 -2
  65. package/dist/validation/dynamic-view.mjs +21 -2
  66. package/dist/validation/element-ref.d.ts +2 -2
  67. package/dist/validation/element-ref.mjs +1 -1
  68. package/dist/validation/imports.d.ts +0 -1
  69. package/dist/validation/imports.mjs +0 -5
  70. package/dist/validation/index.d.ts +1 -1
  71. package/dist/validation/index.mjs +19 -13
  72. package/dist/validation/view-predicates/relation-with.d.ts +1 -1
  73. package/dist/validation/view.d.ts +1 -1
  74. package/dist/view-utils/index.d.ts +0 -1
  75. package/dist/view-utils/index.mjs +0 -1
  76. package/dist/views/likec4-views.d.ts +6 -0
  77. package/dist/views/likec4-views.mjs +31 -18
  78. package/dist/workspace/ProjectsManager.d.ts +23 -31
  79. package/dist/workspace/ProjectsManager.mjs +78 -89
  80. package/dist/workspace/WorkspaceManager.mjs +1 -1
  81. package/likec4lib/package.json +4 -0
  82. package/package.json +24 -28
  83. package/protocol/package.json +4 -0
  84. package/dist/view-utils/resolve-relative-paths.d.ts +0 -2
  85. package/dist/view-utils/resolve-relative-paths.mjs +0 -78
@@ -1,4 +1,4 @@
1
- import { ThemeColors } from "@likec4/core";
1
+ import { ThemeColors } from "@likec4/core/styles";
2
2
  import { AstUtils } from "langium";
3
3
  import {
4
4
  DefaultCompletionProvider
@@ -5,7 +5,7 @@ import type { LikeC4Services } from '../module';
5
5
  export declare class LikeC4DocumentLinkProvider implements DocumentLinkProvider {
6
6
  private services;
7
7
  constructor(services: LikeC4Services);
8
- getDocumentLinks(doc: LangiumDocument, _params: DocumentLinkParams, cancelToken?: CancellationToken): Promise<DocumentLink[]>;
8
+ getDocumentLinks(doc: LangiumDocument, _params: DocumentLinkParams, _cancelToken?: CancellationToken): Promise<DocumentLink[]>;
9
9
  resolveLink(doc: LangiumDocument, link: string): string;
10
10
  relativeLink(doc: LangiumDocument, link: string): string | null;
11
11
  }
@@ -6,7 +6,7 @@ export class LikeC4DocumentLinkProvider {
6
6
  constructor(services) {
7
7
  this.services = services;
8
8
  }
9
- async getDocumentLinks(doc, _params, cancelToken) {
9
+ async getDocumentLinks(doc, _params, _cancelToken) {
10
10
  if (!isLikeC4LangiumDocument(doc) || this.services.likec4.LanguageServices.isExcluded(doc)) {
11
11
  return [];
12
12
  }
@@ -269,7 +269,7 @@ export class LikeC4DocumentSymbolProvider {
269
269
  const doc = AstUtils.getDocument(astElement);
270
270
  const instance = this.parser.forDocument(doc).parseDeployedInstance(astElement);
271
271
  const name = this.nameProvider.getNameStrict(astElement);
272
- const detail = "instance of " + instance.element;
272
+ const detail = "instance of " + instance.element.model;
273
273
  return [
274
274
  {
275
275
  kind: this.symbolKind(astElement),
@@ -4,6 +4,6 @@ export declare class NoopLikeC4MCPServer implements LikeC4MCPServer {
4
4
  get mcp(): McpServer;
5
5
  get isStarted(): boolean;
6
6
  get port(): number;
7
- start(port: number): Promise<void>;
7
+ start(): Promise<void>;
8
8
  stop(): Promise<void>;
9
9
  }
@@ -8,7 +8,7 @@ export class NoopLikeC4MCPServer {
8
8
  get port() {
9
9
  return NaN;
10
10
  }
11
- start(port) {
11
+ start() {
12
12
  return Promise.resolve();
13
13
  }
14
14
  stop() {
@@ -37,7 +37,10 @@ export class StdioLikeC4MCPServer {
37
37
  }
38
38
  logger.info("Stopping MCP stdio server");
39
39
  await this.transport.close();
40
- this._mcp?.close();
40
+ if (this._mcp) {
41
+ await this._mcp.close();
42
+ }
43
+ this._mcp = void 0;
41
44
  this.transport = void 0;
42
45
  }
43
46
  }
@@ -47,10 +47,10 @@ export class StreamableLikeC4MCPServer {
47
47
  };
48
48
  await server.connect(transport);
49
49
  await transport.handleRequest(req, res, await c.req.json());
50
- res.on("close", () => {
50
+ res.on("close", async () => {
51
51
  logger.debug("Request closed");
52
- transport.close();
53
- server.close();
52
+ await transport.close();
53
+ await server.close();
54
54
  });
55
55
  return toFetchResponse(res);
56
56
  } catch (e) {
@@ -19,10 +19,10 @@ const streamableLikeC4MCPServer = (services, defaultPort = 33335) => {
19
19
  port = defaultPort
20
20
  } = update.configuration.mcp;
21
21
  if (!enabled) {
22
- server.stop();
22
+ void server.stop();
23
23
  return;
24
24
  }
25
- Promise.resolve().then(() => server.start(port)).then(() => {
25
+ void Promise.resolve().then(() => server.start(port)).then(() => {
26
26
  connection?.telemetry?.logEvent({
27
27
  eventName: "mcp-server-started",
28
28
  mcpPort: port
@@ -15,7 +15,7 @@ export const locationSchema = z.object({
15
15
  })
16
16
  }).describe("Range in the file")
17
17
  }).nullable();
18
- export const projectIdSchema = z.string().refine((v) => true).optional().default(ProjectsManager.DefaultProjectId).describe('Project id (optional, will use "default" if not specified)');
18
+ export const projectIdSchema = z.string().refine((_v) => true).optional().default(ProjectsManager.DefaultProjectId).describe('Project id (optional, will use "default" if not specified)');
19
19
  export const includedInViewsSchema = z.array(z.object({
20
20
  id: z.string().describe("View id"),
21
21
  title: z.string().describe("View title"),
@@ -36,7 +36,7 @@ export const mkLocate = (languageServices, projectId) => (params) => {
36
36
  range: loc.range
37
37
  } : null;
38
38
  } catch (e) {
39
- logger.debug(`Failed to locate ${params}`, { error: e });
39
+ logger.debug(`Failed to locate {params}`, { error: e, params });
40
40
  return null;
41
41
  }
42
42
  };
@@ -16,11 +16,11 @@ export declare class MergedSpecification {
16
16
  /**
17
17
  * Converts a parsed model into a C4 model element.
18
18
  */
19
- toModelElement: ({ tags, links, style: { color, shape, icon, opacity, border, size, multiple, padding, textSize, }, id, kind, title, description, technology, summary, metadata, }: ParsedAstElement) => c4.Element | null;
19
+ toModelElement: ({ tags, links, style, id, kind, title, description, technology, summary, metadata, }: ParsedAstElement) => c4.Element | null;
20
20
  /**
21
21
  * Converts a parsed model into a C4 model relation.
22
22
  */
23
- toModelRelation: ({ astPath, source, target, kind, links, id, ...model }: ParsedAstRelation) => c4.Relationship | null;
23
+ toModelRelation: ({ astPath: _astPath, source, target, kind, links, id, ...model }: ParsedAstRelation) => c4.Relationship | null;
24
24
  /**
25
25
  * Converts a parsed deployment model into a C4 deployment model
26
26
  */
@@ -28,5 +28,5 @@ export declare class MergedSpecification {
28
28
  /**
29
29
  * Converts a parsed deployment relation into a C4 deployment relation.
30
30
  */
31
- toDeploymentRelation: ({ astPath, source, target, kind, links, id, ...model }: ParsedAstDeploymentRelation) => c4.DeploymentRelationship | null;
31
+ toDeploymentRelation: ({ astPath: _astPath, source, target, kind, links, id, ...model }: ParsedAstDeploymentRelation) => c4.DeploymentRelationship | null;
32
32
  }
@@ -1,4 +1,4 @@
1
- import { FqnRef, omitUndefined } from "@likec4/core/types";
1
+ import { exact, FqnRef } from "@likec4/core/types";
2
2
  import { isNonEmptyArray, MultiMap, nameFromFqn } from "@likec4/core/utils";
3
3
  import {
4
4
  isEmpty,
@@ -49,17 +49,7 @@ export class MergedSpecification {
49
49
  toModelElement = ({
50
50
  tags,
51
51
  links,
52
- style: {
53
- color,
54
- shape,
55
- icon,
56
- opacity,
57
- border,
58
- size,
59
- multiple,
60
- padding,
61
- textSize
62
- },
52
+ style,
63
53
  id,
64
54
  kind,
65
55
  title,
@@ -74,16 +64,7 @@ export class MergedSpecification {
74
64
  logger.warn`No kind '${kind}' found for ${id}`;
75
65
  return null;
76
66
  }
77
- color ??= __kind.style.color;
78
- shape ??= __kind.style.shape;
79
- icon ??= __kind.style.icon;
80
- opacity ??= __kind.style.opacity;
81
- border ??= __kind.style.border;
82
67
  technology ??= __kind.technology;
83
- multiple ??= __kind.style.multiple;
84
- size ??= __kind.style.size;
85
- padding ??= __kind.style.padding;
86
- textSize ??= __kind.style.textSize;
87
68
  description ??= __kind.description;
88
69
  summary ??= __kind.summary;
89
70
  links ??= __kind.links;
@@ -94,19 +75,12 @@ export class MergedSpecification {
94
75
  ...tags
95
76
  ]) : __kind.tags;
96
77
  }
97
- return omitUndefined({
78
+ return exact({
98
79
  metadata: metadata && !isEmpty(metadata) ? metadata : void 0,
99
- color,
100
- notation: __kind?.notation,
101
- shape,
102
- icon,
103
- style: omitUndefined({
104
- border,
105
- size,
106
- padding,
107
- textSize,
108
- multiple,
109
- opacity
80
+ notation: __kind.notation,
81
+ style: exact({
82
+ ...__kind.style,
83
+ ...style
110
84
  }),
111
85
  links,
112
86
  tags,
@@ -126,7 +100,8 @@ export class MergedSpecification {
126
100
  * Converts a parsed model into a C4 model relation.
127
101
  */
128
102
  toModelRelation = ({
129
- astPath,
103
+ astPath: _astPath,
104
+ // omit
130
105
  source,
131
106
  target,
132
107
  kind,
@@ -180,13 +155,11 @@ export class MergedSpecification {
180
155
  ...rest
181
156
  } = parsed;
182
157
  title = title === nameFromFqn(parsed.id) && __kind.title ? __kind.title : title;
183
- return omitUndefined({
158
+ return exact({
184
159
  ...__kind,
185
160
  ...rest,
186
161
  title,
187
- style: omitUndefined({
188
- border: "dashed",
189
- opacity: 10,
162
+ style: exact({
190
163
  ...__kind.style,
191
164
  ...style
192
165
  }),
@@ -201,7 +174,8 @@ export class MergedSpecification {
201
174
  * Converts a parsed deployment relation into a C4 deployment relation.
202
175
  */
203
176
  toDeploymentRelation = ({
204
- astPath,
177
+ astPath: _astPath,
178
+ // omit
205
179
  source,
206
180
  target,
207
181
  kind,
@@ -1,15 +1,16 @@
1
1
  import {
2
- computeColorValues,
3
2
  isDeploymentNode,
4
3
  isGlobalFqn
5
4
  } from "@likec4/core";
6
5
  import { resolveRulesExtendedViews } from "@likec4/core/compute-view";
7
- import { _stage, _type, FqnRef } from "@likec4/core/types";
6
+ import { computeColorValues } from "@likec4/core/styles";
7
+ import { _stage, _type, exact, FqnRef, isExtendsElementView } from "@likec4/core/types";
8
8
  import {
9
9
  compareNatural,
10
10
  parentFqn,
11
11
  sortByFqnHierarchically
12
12
  } from "@likec4/core/utils";
13
+ import { UriUtils } from "langium";
13
14
  import {
14
15
  filter,
15
16
  flatMap,
@@ -27,7 +28,6 @@ import {
27
28
  reduce
28
29
  } from "remeda";
29
30
  import { logger } from "../../logger.mjs";
30
- import { resolveRelativePaths } from "../../view-utils/index.mjs";
31
31
  import { MergedExtends } from "./MergedExtends.mjs";
32
32
  import { MergedSpecification } from "./MergedSpecification.mjs";
33
33
  export function buildModelData(project, docs) {
@@ -156,6 +156,7 @@ export function buildModelData(project, docs) {
156
156
  return {
157
157
  ...omitBy(model, (v) => v === void 0),
158
158
  [_stage]: "parsed",
159
+ sourcePath: UriUtils.relative(project.folderUri, docUri),
159
160
  docUri,
160
161
  description,
161
162
  title,
@@ -163,12 +164,7 @@ export function buildModelData(project, docs) {
163
164
  };
164
165
  };
165
166
  }
166
- const parsedViews = pipe(
167
- docs,
168
- flatMap((d) => map(d.c4Views, toC4View(d))),
169
- // Resolve relative paths and sort by
170
- resolveRelativePaths
171
- );
167
+ const parsedViews = docs.flatMap((d) => map(d.c4Views, toC4View(d)));
172
168
  if (!parsedViews.some((v) => v.id === "index")) {
173
169
  parsedViews.unshift({
174
170
  [_stage]: "parsed",
@@ -176,8 +172,6 @@ export function buildModelData(project, docs) {
176
172
  id: "index",
177
173
  title: "Landscape view",
178
174
  description: null,
179
- tags: null,
180
- links: null,
181
175
  rules: [
182
176
  {
183
177
  include: [
@@ -189,19 +183,22 @@ export function buildModelData(project, docs) {
189
183
  ]
190
184
  });
191
185
  }
192
- const views = pipe(
186
+ let views = pipe(
193
187
  parsedViews,
194
- indexBy(prop("id")),
195
- resolveRulesExtendedViews
188
+ indexBy(prop("id"))
196
189
  );
190
+ if (parsedViews.some(isExtendsElementView)) {
191
+ views = resolveRulesExtendedViews(views);
192
+ }
197
193
  return {
198
194
  data: {
199
195
  [_stage]: "parsed",
200
196
  projectId: project.id,
201
- project: {
197
+ project: exact({
202
198
  id: project.id,
203
- title: project.config.title ?? project.config.name
204
- },
199
+ title: project.config.title ?? project.config.name,
200
+ styles: project.config.styles
201
+ }),
205
202
  specification: {
206
203
  tags: c4Specification.tags,
207
204
  elements: c4Specification.specs.elements,
@@ -1,6 +1,6 @@
1
- import * as c4 from '@likec4/core';
2
1
  import { type ViewId } from '@likec4/core';
3
2
  import { LikeC4Model } from '@likec4/core/model';
3
+ import type * as c4 from '@likec4/core/types';
4
4
  import { type URI, Disposable } from 'langium';
5
5
  import { CancellationToken } from 'vscode-jsonrpc';
6
6
  import type { LikeC4Services } from '../module';
@@ -1,5 +1,5 @@
1
- import * as c4 from "@likec4/core";
2
1
  import {
2
+ _stage,
3
3
  isScopedElementView
4
4
  } from "@likec4/core";
5
5
  import { computeView } from "@likec4/core/compute-view";
@@ -22,7 +22,6 @@ import {
22
22
  prop,
23
23
  values
24
24
  } from "remeda";
25
- import { CancellationToken } from "vscode-jsonrpc";
26
25
  import { isLikeC4Builtin } from "../likec4lib.mjs";
27
26
  import { logger as mainLogger, logWarnError } from "../logger.mjs";
28
27
  import { ADisposable, performanceMark } from "../utils/index.mjs";
@@ -132,7 +131,7 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
132
131
  return LikeC4Model.create(parsedData);
133
132
  });
134
133
  }
135
- async parseModel(projectId, cancelToken = CancellationToken.None) {
134
+ async parseModel(projectId, cancelToken) {
136
135
  const project = this.projects.ensureProjectId(projectId);
137
136
  const logger = builderLogger.getChild(project);
138
137
  const cache = this.cache;
@@ -143,7 +142,9 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
143
142
  }
144
143
  const t0 = performanceMark();
145
144
  return await this.mutex.read(async () => {
146
- await interruptAndCheck(cancelToken);
145
+ if (cancelToken) {
146
+ await interruptAndCheck(cancelToken);
147
+ }
147
148
  const result = this.unsafeSyncJoinedModelData(project);
148
149
  logger.debug`parseModel in ${t0.pretty}`;
149
150
  return result;
@@ -178,18 +179,18 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
178
179
  const key = computedViewKey(projectId, v.id);
179
180
  const previous = this.previousViews[key];
180
181
  const view = previous && eq(v, previous) ? previous : v;
182
+ this.previousViews[key] = view;
181
183
  viewsCache.set(key, view);
182
184
  return [v.id, view];
183
185
  });
184
- this.previousViews = { ...this.previousViews, ...views };
185
186
  return LikeC4Model.create({
186
187
  ...parsedModel.$data,
187
- [c4._stage]: "computed",
188
+ [_stage]: "computed",
188
189
  views
189
190
  });
190
191
  });
191
192
  }
192
- async buildLikeC4Model(projectId, cancelToken = CancellationToken.None) {
193
+ async buildLikeC4Model(projectId, cancelToken) {
193
194
  const project = this.projects.ensureProjectId(projectId);
194
195
  const logger = builderLogger.getChild(project);
195
196
  const cache = this.cache;
@@ -200,13 +201,15 @@ export class DefaultLikeC4ModelBuilder extends ADisposable {
200
201
  }
201
202
  const t0 = performanceMark();
202
203
  return await this.mutex.read(async () => {
203
- await interruptAndCheck(cancelToken);
204
+ if (cancelToken) {
205
+ await interruptAndCheck(cancelToken);
206
+ }
204
207
  const result = this.unsafeSyncBuildModel(project);
205
208
  logger.debug(`buildLikeC4Model in ${t0.pretty}`);
206
209
  return result;
207
210
  });
208
211
  }
209
- async computeView(viewId, projectId, cancelToken = CancellationToken.None) {
212
+ async computeView(viewId, projectId, cancelToken) {
210
213
  const project = this.projects.ensureProjectId(projectId);
211
214
  const logger = builderLogger.getChild(project);
212
215
  const cache = this.cache;
@@ -30,4 +30,9 @@ export declare class LikeC4ModelLocator {
30
30
  range: Range;
31
31
  isSpecification: boolean;
32
32
  }>>;
33
+ locateDynamicViewStep(params: {
34
+ view: c4.ViewId;
35
+ astPath: string;
36
+ projectId?: c4.ProjectId | undefined;
37
+ }): Location | null;
33
38
  }
@@ -95,9 +95,10 @@ export class LikeC4ModelLocator {
95
95
  if (!ast.isRelation(node) && !ast.isDeploymentRelation(node)) {
96
96
  continue;
97
97
  }
98
- let targetNode = node.title ? findNodeForProperty(node.$cstNode, "title") : void 0;
99
- targetNode ??= node.kind ? findNodeForProperty(node.$cstNode, "kind") : void 0;
98
+ let targetNode = node.kind ? findNodeForProperty(node.$cstNode, "kind") : void 0;
99
+ targetNode ??= node.dotKind ? findNodeForProperty(node.$cstNode, "dotKind") : void 0;
100
100
  targetNode ??= findNodeForKeyword(node.$cstNode, "->");
101
+ targetNode ??= findNodeForProperty(node.$cstNode, "title");
101
102
  targetNode ??= findNodeForProperty(node.$cstNode, "target");
102
103
  targetNode ??= node.$cstNode;
103
104
  if (!targetNode) {
@@ -105,7 +106,10 @@ export class LikeC4ModelLocator {
105
106
  }
106
107
  return {
107
108
  uri: doc.uri.toString(),
108
- range: targetNode.range
109
+ range: {
110
+ start: targetNode.range.start,
111
+ end: targetNode.range.start
112
+ }
109
113
  };
110
114
  }
111
115
  return null;
@@ -199,4 +203,37 @@ export class LikeC4ModelLocator {
199
203
  return [];
200
204
  }
201
205
  }
206
+ locateDynamicViewStep(params) {
207
+ const { doc, viewAst } = this.locateViewAst(params.view, params.projectId) ?? {};
208
+ if (!doc || !viewAst) {
209
+ return null;
210
+ }
211
+ if (!ast.isDynamicView(viewAst) || !viewAst.body) {
212
+ logger.warn(`View ${params.view} is not a dynamic view`);
213
+ return null;
214
+ }
215
+ const astPath = this.services.workspace.AstNodeLocator.getAstNodePath(viewAst.body) + params.astPath;
216
+ const node = this.services.workspace.AstNodeLocator.getAstNode(doc.parseResult.value, astPath);
217
+ if (!node || !ast.isDynamicViewStep(node)) {
218
+ logger.warn(`Failed to locate dynamic view step ${astPath} in view ${params.view}`);
219
+ return null;
220
+ }
221
+ let targetNode = node.kind ? findNodeForProperty(node.$cstNode, "kind") : void 0;
222
+ targetNode ??= node.dotKind ? findNodeForProperty(node.$cstNode, "dotKind") : void 0;
223
+ targetNode ??= findNodeForKeyword(node.$cstNode, "->");
224
+ targetNode ??= findNodeForKeyword(node.$cstNode, "<-");
225
+ targetNode ??= findNodeForProperty(node.$cstNode, "title");
226
+ targetNode ??= findNodeForProperty(node.$cstNode, "target");
227
+ targetNode ??= node.$cstNode;
228
+ if (!targetNode) {
229
+ return null;
230
+ }
231
+ return {
232
+ uri: doc.uri.toString(),
233
+ range: {
234
+ start: targetNode.range.start,
235
+ end: targetNode.range.start
236
+ }
237
+ };
238
+ }
202
239
  }
@@ -1,5 +1,4 @@
1
- import { invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
2
- import { isAndOperator, isOrOperator } from "@likec4/core";
1
+ import { invariant, isAndOperator, isNonEmptyArray, isOrOperator, nonexhaustive } from "@likec4/core";
3
2
  import { ast } from "../ast.mjs";
4
3
  const parseEquals = ({ operator, not }, value) => {
5
4
  if (operator.startsWith("!=")) {
@@ -24,7 +24,12 @@ declare const DocumentParserFromMixins: {
24
24
  parseDynamicViewRule(astRule: import("../generated/ast").DynamicViewRule): ProjectId;
25
25
  parseDynamicViewIncludePredicate(astRule: import("../generated/ast").DynamicViewIncludePredicate): ProjectId;
26
26
  parseDynamicParallelSteps(node: import("../generated/ast").DynamicViewParallelSteps): ProjectId;
27
- parseDynamicStep(node: import("../generated/ast").DynamicViewStep): ProjectId;
27
+ parseDynamicStep(node: import("../generated/ast").DynamicViewStep): ProjectId | ProjectId;
28
+ recursiveParseDynamicStepChain(node: import("../generated/ast").DynamicStepChain, callstack?: Array<[source: ProjectId, target: ProjectId]>): ProjectId[];
29
+ parseDynamicStepSingle(node: import("../generated/ast").DynamicStepSingle): ProjectId;
30
+ parseAbstractDynamicStep(astnode: import("../generated/ast").AbstractDynamicStep): import("type-fest").Writable<import("type-fest").Except<ProjectId, "source", {
31
+ requireExactProps: true;
32
+ }>>;
28
33
  parsePredicate(astNode: import("../generated/ast").ExpressionV2): ProjectId;
29
34
  parseElementPredicate(astNode: import("../generated/ast").FqnExprOrWith): ProjectId;
30
35
  parseElementPredicateOrWhere(astNode: import("../generated/ast").FqnExprOrWhere): ProjectId;
@@ -47,6 +52,7 @@ declare const DocumentParserFromMixins: {
47
52
  parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): ProjectId[];
48
53
  parseRelationExprOrWith(astNode: import("../generated/ast").RelationExprOrWith): ProjectId;
49
54
  parseRelationExprWith(astNode: import("../generated/ast").RelationExprWith): ProjectId;
55
+ parseCustomRelationProperties(custom: import("../generated/ast").CustomRelationProperties | undefined): import("type-fest").Except<ProjectId["customRelation"], "expr">;
50
56
  parseRelationExprOrWhere(astNode: import("../generated/ast").RelationExprOrWhere): ProjectId;
51
57
  parseRelationExprWhere(astNode: import("../generated/ast").RelationExprWhere): ProjectId;
52
58
  parseRelationExpr(astNode: import("../generated/ast").RelationExpr): ProjectId;
@@ -117,7 +123,12 @@ declare const DocumentParserFromMixins: {
117
123
  parseDynamicViewRule(astRule: import("../generated/ast").DynamicViewRule): ProjectId;
118
124
  parseDynamicViewIncludePredicate(astRule: import("../generated/ast").DynamicViewIncludePredicate): ProjectId;
119
125
  parseDynamicParallelSteps(node: import("../generated/ast").DynamicViewParallelSteps): ProjectId;
120
- parseDynamicStep(node: import("../generated/ast").DynamicViewStep): ProjectId;
126
+ parseDynamicStep(node: import("../generated/ast").DynamicViewStep): ProjectId | ProjectId;
127
+ recursiveParseDynamicStepChain(node: import("../generated/ast").DynamicStepChain, callstack?: Array<[source: ProjectId, target: ProjectId]>): ProjectId[];
128
+ parseDynamicStepSingle(node: import("../generated/ast").DynamicStepSingle): ProjectId;
129
+ parseAbstractDynamicStep(astnode: import("../generated/ast").AbstractDynamicStep): import("type-fest").Writable<import("type-fest").Except<ProjectId, "source", {
130
+ requireExactProps: true;
131
+ }>>;
121
132
  parsePredicate(astNode: import("../generated/ast").ExpressionV2): ProjectId;
122
133
  parseElementPredicate(astNode: import("../generated/ast").FqnExprOrWith): ProjectId;
123
134
  parseElementPredicateOrWhere(astNode: import("../generated/ast").FqnExprOrWhere): ProjectId;
@@ -140,6 +151,7 @@ declare const DocumentParserFromMixins: {
140
151
  parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): ProjectId[];
141
152
  parseRelationExprOrWith(astNode: import("../generated/ast").RelationExprOrWith): ProjectId;
142
153
  parseRelationExprWith(astNode: import("../generated/ast").RelationExprWith): ProjectId;
154
+ parseCustomRelationProperties(custom: import("../generated/ast").CustomRelationProperties | undefined): import("type-fest").Except<ProjectId["customRelation"], "expr">;
143
155
  parseRelationExprOrWhere(astNode: import("../generated/ast").RelationExprOrWhere): ProjectId;
144
156
  parseRelationExprWhere(astNode: import("../generated/ast").RelationExprWhere): ProjectId;
145
157
  parseRelationExpr(astNode: import("../generated/ast").RelationExpr): ProjectId;
@@ -264,6 +276,7 @@ declare const DocumentParserFromMixins: {
264
276
  parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): ProjectId[];
265
277
  parseRelationExprOrWith(astNode: import("../generated/ast").RelationExprOrWith): ProjectId;
266
278
  parseRelationExprWith(astNode: import("../generated/ast").RelationExprWith): ProjectId;
279
+ parseCustomRelationProperties(custom: import("../generated/ast").CustomRelationProperties | undefined): import("type-fest").Except<ProjectId["customRelation"], "expr">;
267
280
  parseRelationExprOrWhere(astNode: import("../generated/ast").RelationExprOrWhere): ProjectId;
268
281
  parseRelationExprWhere(astNode: import("../generated/ast").RelationExprWhere): ProjectId;
269
282
  parseRelationExpr(astNode: import("../generated/ast").RelationExpr): ProjectId;
@@ -326,6 +339,7 @@ declare const DocumentParserFromMixins: {
326
339
  parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): ProjectId[];
327
340
  parseRelationExprOrWith(astNode: import("../generated/ast").RelationExprOrWith): ProjectId;
328
341
  parseRelationExprWith(astNode: import("../generated/ast").RelationExprWith): ProjectId;
342
+ parseCustomRelationProperties(custom: import("../generated/ast").CustomRelationProperties | undefined): import("type-fest").Except<ProjectId["customRelation"], "expr">;
329
343
  parseRelationExprOrWhere(astNode: import("../generated/ast").RelationExprOrWhere): ProjectId;
330
344
  parseRelationExprWhere(astNode: import("../generated/ast").RelationExprWhere): ProjectId;
331
345
  parseRelationExpr(astNode: import("../generated/ast").RelationExpr): ProjectId;
@@ -396,6 +410,7 @@ declare const DocumentParserFromMixins: {
396
410
  parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): ProjectId[];
397
411
  parseRelationExprOrWith(astNode: import("../generated/ast").RelationExprOrWith): ProjectId;
398
412
  parseRelationExprWith(astNode: import("../generated/ast").RelationExprWith): ProjectId;
413
+ parseCustomRelationProperties(custom: import("../generated/ast").CustomRelationProperties | undefined): import("type-fest").Except<ProjectId["customRelation"], "expr">;
399
414
  parseRelationExprOrWhere(astNode: import("../generated/ast").RelationExprOrWhere): ProjectId;
400
415
  parseRelationExprWhere(astNode: import("../generated/ast").RelationExprWhere): ProjectId;
401
416
  parseRelationExpr(astNode: import("../generated/ast").RelationExpr): ProjectId;
@@ -459,6 +474,7 @@ declare const DocumentParserFromMixins: {
459
474
  parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): ProjectId[];
460
475
  parseRelationExprOrWith(astNode: import("../generated/ast").RelationExprOrWith): ProjectId;
461
476
  parseRelationExprWith(astNode: import("../generated/ast").RelationExprWith): ProjectId;
477
+ parseCustomRelationProperties(custom: import("../generated/ast").CustomRelationProperties | undefined): import("type-fest").Except<ProjectId["customRelation"], "expr">;
462
478
  parseRelationExprOrWhere(astNode: import("../generated/ast").RelationExprOrWhere): ProjectId;
463
479
  parseRelationExprWhere(astNode: import("../generated/ast").RelationExprWhere): ProjectId;
464
480
  parseRelationExpr(astNode: import("../generated/ast").RelationExpr): ProjectId;
@@ -560,6 +576,7 @@ declare const DocumentParserFromMixins: {
560
576
  parseFqnExpressions(astNode: import("../generated/ast").FqnExpressions): ProjectId[];
561
577
  parseRelationExprOrWith(astNode: import("../generated/ast").RelationExprOrWith): ProjectId;
562
578
  parseRelationExprWith(astNode: import("../generated/ast").RelationExprWith): ProjectId;
579
+ parseCustomRelationProperties(custom: import("../generated/ast").CustomRelationProperties | undefined): import("type-fest").Except<ProjectId["customRelation"], "expr">;
563
580
  parseRelationExprOrWhere(astNode: import("../generated/ast").RelationExprOrWhere): ProjectId;
564
581
  parseRelationExprWhere(astNode: import("../generated/ast").RelationExprWhere): ProjectId;
565
582
  parseRelationExpr(astNode: import("../generated/ast").RelationExpr): ProjectId;
@@ -1,9 +1,9 @@
1
1
  import {
2
+ exact,
2
3
  GlobalFqn,
3
4
  isNonEmptyArray,
4
5
  nonexhaustive,
5
- nonNullable,
6
- omitUndefined
6
+ nonNullable
7
7
  } from "@likec4/core";
8
8
  import {
9
9
  filter,
@@ -138,7 +138,7 @@ export class BaseParser {
138
138
  tags.push(...values);
139
139
  }
140
140
  }
141
- } catch (e) {
141
+ } catch {
142
142
  }
143
143
  iter = iter.prev;
144
144
  }
@@ -210,7 +210,7 @@ export class BaseParser {
210
210
  const slashIndex = value.indexOf("/");
211
211
  const aliasName = slashIndex > 0 ? value.substring(0, slashIndex) : value;
212
212
  const remainingPath = slashIndex > 0 ? value.substring(slashIndex + 1) : "";
213
- const imageAliases = { "@": "./images", ...this.project.config.imageAliases || {} };
213
+ const imageAliases = { "@": "./images", ...this.project.config.imageAliases };
214
214
  const aliasPath = imageAliases[aliasName];
215
215
  if (!aliasPath) {
216
216
  logger.warn(`Image alias "${aliasName}" not found in project configuration`);
@@ -243,8 +243,8 @@ export class BaseParser {
243
243
  return {};
244
244
  }
245
245
  if (isArray(elementProps)) {
246
- const style = this.parseStyleProps(elementProps?.find(ast.isElementStyleProperty)?.props);
247
- const iconProp = this.parseIconProperty(elementProps?.find(ast.isIconProperty));
246
+ const style = this.parseStyleProps(elementProps.find(ast.isElementStyleProperty)?.props);
247
+ const iconProp = this.parseIconProperty(elementProps.find(ast.isIconProperty));
248
248
  if (iconProp) {
249
249
  style.icon = iconProp;
250
250
  }
@@ -318,7 +318,7 @@ export class BaseParser {
318
318
  nonexhaustive(prop);
319
319
  }
320
320
  }
321
- return result;
321
+ return exact(result);
322
322
  }
323
323
  /**
324
324
  * Parse base properties: title, description and technology
@@ -332,7 +332,7 @@ export class BaseParser {
332
332
  const description = override?.description ? { txt: removeIndent(override.description) } : this.parseMarkdownOrString(props.description);
333
333
  const summary = override?.summary ? { txt: removeIndent(override.summary) } : this.parseMarkdownOrString(props.summary);
334
334
  const technology = toSingleLine(override?.technology) ?? removeIndent(parseMarkdownAsString(props.technology));
335
- return omitUndefined({
335
+ return exact({
336
336
  title,
337
337
  summary,
338
338
  description,
@@ -22,6 +22,7 @@ export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B:
22
22
  parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[];
23
23
  parseRelationExprOrWith(astNode: ast.RelationExprOrWith): c4.RelationExpr.Any;
24
24
  parseRelationExprWith(astNode: ast.RelationExprWith): c4.RelationExpr.Custom;
25
+ parseCustomRelationProperties(custom: ast.CustomRelationProperties | undefined): import("type-fest").Except<c4.RelationExpr.Custom["customRelation"], "expr">;
25
26
  parseRelationExprOrWhere(astNode: ast.RelationExprOrWhere): c4.RelationExpr.OrWhere;
26
27
  parseRelationExprWhere(astNode: ast.RelationExprWhere): c4.RelationExpr.Where;
27
28
  parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr.OrWhere;