@likec4/language-server 1.23.1 → 1.24.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 (73) hide show
  1. package/dist/LikeC4FileSystem.d.ts +1 -0
  2. package/dist/LikeC4FileSystem.js +7 -0
  3. package/dist/Rpc.js +10 -7
  4. package/dist/ast.d.ts +13 -29
  5. package/dist/ast.js +3 -70
  6. package/dist/bundled.mjs +2466 -2641
  7. package/dist/generated/ast.d.ts +36 -8
  8. package/dist/generated/ast.js +44 -2
  9. package/dist/generated/grammar.js +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +1 -0
  12. package/dist/likec4lib.d.ts +2 -0
  13. package/dist/likec4lib.js +3 -0
  14. package/dist/lsp/CodeLensProvider.js +7 -4
  15. package/dist/lsp/CompletionProvider.js +20 -2
  16. package/dist/lsp/DocumentLinkProvider.d.ts +3 -3
  17. package/dist/lsp/DocumentLinkProvider.js +14 -5
  18. package/dist/lsp/DocumentSymbolProvider.d.ts +1 -1
  19. package/dist/lsp/DocumentSymbolProvider.js +5 -2
  20. package/dist/lsp/HoverProvider.js +20 -7
  21. package/dist/lsp/SemanticTokenProvider.js +18 -1
  22. package/dist/model/builder/MergedExtends.d.ts +12 -0
  23. package/dist/model/builder/MergedExtends.js +67 -0
  24. package/dist/model/builder/MergedSpecification.d.ts +29 -0
  25. package/dist/model/builder/MergedSpecification.js +203 -0
  26. package/dist/model/builder/buildModel.d.ts +3 -0
  27. package/dist/model/builder/buildModel.js +194 -0
  28. package/dist/model/deployments-index.d.ts +6 -56
  29. package/dist/model/deployments-index.js +59 -137
  30. package/dist/model/fqn-index.d.ts +47 -17
  31. package/dist/model/fqn-index.js +155 -68
  32. package/dist/model/index.d.ts +0 -1
  33. package/dist/model/index.js +0 -1
  34. package/dist/model/model-builder.d.ts +13 -9
  35. package/dist/model/model-builder.js +101 -547
  36. package/dist/model/model-locator.d.ts +1 -0
  37. package/dist/model/model-locator.js +7 -9
  38. package/dist/model/model-parser.d.ts +24 -18
  39. package/dist/model/model-parser.js +51 -31
  40. package/dist/model/parser/Base.d.ts +3 -3
  41. package/dist/model/parser/Base.js +15 -9
  42. package/dist/model/parser/DeploymentModelParser.d.ts +4 -3
  43. package/dist/model/parser/DeploymentModelParser.js +54 -3
  44. package/dist/model/parser/DeploymentViewParser.d.ts +3 -2
  45. package/dist/model/parser/FqnRefParser.d.ts +2 -2
  46. package/dist/model/parser/GlobalsParser.d.ts +3 -2
  47. package/dist/model/parser/ModelParser.d.ts +4 -4
  48. package/dist/model/parser/ModelParser.js +45 -4
  49. package/dist/model/parser/PredicatesParser.d.ts +2 -2
  50. package/dist/model/parser/SpecificationParser.d.ts +2 -2
  51. package/dist/model/parser/ViewsParser.d.ts +3 -2
  52. package/dist/module.d.ts +2 -3
  53. package/dist/module.js +2 -3
  54. package/dist/references/scope-computation.d.ts +1 -1
  55. package/dist/references/scope-computation.js +14 -11
  56. package/dist/references/scope-provider.d.ts +16 -4
  57. package/dist/references/scope-provider.js +64 -30
  58. package/dist/test/testServices.d.ts +2 -1
  59. package/dist/test/testServices.js +17 -14
  60. package/dist/utils/elementRef.d.ts +1 -1
  61. package/dist/utils/elementRef.js +3 -3
  62. package/dist/validation/deployment-checks.d.ts +1 -0
  63. package/dist/validation/deployment-checks.js +12 -0
  64. package/dist/validation/index.d.ts +1 -1
  65. package/dist/validation/index.js +8 -1
  66. package/dist/views/configurable-layouter.js +3 -3
  67. package/dist/views/likec4-views.d.ts +1 -0
  68. package/dist/views/likec4-views.js +11 -11
  69. package/package.json +6 -7
  70. package/dist/bundled.d.ts +0 -8
  71. package/dist/bundled.js +0 -25
  72. package/dist/model/fqn-computation.d.ts +0 -3
  73. package/dist/model/fqn-computation.js +0 -72
@@ -8,6 +8,7 @@ export declare class LikeC4ModelLocator {
8
8
  private fqnIndex;
9
9
  private deploymentsIndex;
10
10
  private langiumDocuments;
11
+ private parser;
11
12
  constructor(services: LikeC4Services);
12
13
  private documents;
13
14
  getParsedElement(astNodeOrFqn: ast.Element | c4.Fqn): ParsedAstElement | null;
@@ -1,6 +1,6 @@
1
1
  import { AstUtils, GrammarUtils } from "langium";
2
2
  import { isString } from "remeda";
3
- import { ast, isParsedLikeC4LangiumDocument } from "../ast.js";
3
+ import { ast } from "../ast.js";
4
4
  const { findNodeForKeyword, findNodeForProperty } = GrammarUtils;
5
5
  const { getDocument } = AstUtils;
6
6
  export class LikeC4ModelLocator {
@@ -9,12 +9,14 @@ export class LikeC4ModelLocator {
9
9
  this.fqnIndex = services.likec4.FqnIndex;
10
10
  this.deploymentsIndex = services.likec4.DeploymentsIndex;
11
11
  this.langiumDocuments = services.shared.workspace.LangiumDocuments;
12
+ this.parser = services.likec4.ModelParser;
12
13
  }
13
14
  fqnIndex;
14
15
  deploymentsIndex;
15
16
  langiumDocuments;
17
+ parser;
16
18
  documents() {
17
- return this.langiumDocuments.all.filter(isParsedLikeC4LangiumDocument);
19
+ return this.parser.documents().toArray();
18
20
  }
19
21
  getParsedElement(astNodeOrFqn) {
20
22
  if (isString(astNodeOrFqn)) {
@@ -24,17 +26,13 @@ export class LikeC4ModelLocator {
24
26
  return null;
25
27
  }
26
28
  const doc2 = this.langiumDocuments.getDocument(entry.documentUri);
27
- if (!doc2 || !isParsedLikeC4LangiumDocument(doc2)) {
29
+ if (!doc2) {
28
30
  return null;
29
31
  }
30
- return doc2.c4Elements.find((e) => e.id === fqn2) ?? null;
32
+ return this.parser.parse(doc2).c4Elements.find((e) => e.id === fqn2) ?? null;
31
33
  }
32
34
  const fqn = this.fqnIndex.getFqn(astNodeOrFqn);
33
- if (!fqn) return null;
34
- const doc = getDocument(astNodeOrFqn);
35
- if (!isParsedLikeC4LangiumDocument(doc)) {
36
- return null;
37
- }
35
+ const doc = this.parser.parse(getDocument(astNodeOrFqn));
38
36
  return doc.c4Elements.find((e) => e.id === fqn) ?? null;
39
37
  }
40
38
  locateElement(fqn, _prop) {
@@ -1,5 +1,5 @@
1
1
  import { invariant } from '@likec4/core';
2
- import { type LangiumDocument } from 'langium';
2
+ import { type LangiumDocument, type Stream } from 'langium';
3
3
  import type { ParsedLikeC4LangiumDocument } from '../ast';
4
4
  import type { LikeC4Services } from '../module';
5
5
  import { BaseParser } from './parser/Base';
@@ -50,8 +50,8 @@ declare const DocumentParserFromMixins: {
50
50
  parseTags<E extends {
51
51
  tags?: import("../generated/ast").Tags;
52
52
  }>(withTags?: E): invariant<invariant> | null;
53
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
54
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
53
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
54
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
55
55
  parseDeploymentView(astNode: import("../generated/ast").DeploymentView): import("../ast").ParsedAstDeploymentView;
56
56
  parseDeploymentViewRule(astRule: import("../generated/ast").DeploymentViewRule): invariant;
57
57
  parseDeploymentViewRulePredicate(astRule: import("../generated/ast").DeploymentViewRulePredicate): invariant;
@@ -66,6 +66,7 @@ declare const DocumentParserFromMixins: {
66
66
  parseDeployment(): void;
67
67
  parseDeploymentNode(astNode: import("../generated/ast").DeploymentNode): import("../ast").ParsedAstDeployment.Node;
68
68
  parseDeployedInstance(astNode: import("../generated/ast").DeployedInstance): import("../ast").ParsedAstDeployment.Instance;
69
+ parseExtendDeployment(astNode: import("../generated/ast").ExtendDeployment): import("../ast").ParsedAstExtend | null;
69
70
  parseDeploymentRelation(astNode: import("../generated/ast").DeploymentRelation): import("../ast").ParsedAstDeploymentRelation;
70
71
  };
71
72
  } & {
@@ -109,8 +110,8 @@ declare const DocumentParserFromMixins: {
109
110
  parseTags<E extends {
110
111
  tags?: import("../generated/ast").Tags;
111
112
  }>(withTags?: E): invariant<invariant> | null;
112
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
113
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
113
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
114
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
114
115
  parseDeploymentView(astNode: import("../generated/ast").DeploymentView): import("../ast").ParsedAstDeploymentView;
115
116
  parseDeploymentViewRule(astRule: import("../generated/ast").DeploymentViewRule): invariant;
116
117
  parseDeploymentViewRulePredicate(astRule: import("../generated/ast").DeploymentViewRulePredicate): invariant;
@@ -125,6 +126,7 @@ declare const DocumentParserFromMixins: {
125
126
  parseDeployment(): void;
126
127
  parseDeploymentNode(astNode: import("../generated/ast").DeploymentNode): import("../ast").ParsedAstDeployment.Node;
127
128
  parseDeployedInstance(astNode: import("../generated/ast").DeployedInstance): import("../ast").ParsedAstDeployment.Instance;
129
+ parseExtendDeployment(astNode: import("../generated/ast").ExtendDeployment): import("../ast").ParsedAstExtend | null;
128
130
  parseDeploymentRelation(astNode: import("../generated/ast").DeploymentRelation): import("../ast").ParsedAstDeploymentRelation;
129
131
  };
130
132
  } & {
@@ -145,8 +147,8 @@ declare const DocumentParserFromMixins: {
145
147
  parseTags<E extends {
146
148
  tags?: import("../generated/ast").Tags;
147
149
  }>(withTags?: E): invariant<invariant> | null;
148
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
149
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
150
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
151
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
150
152
  };
151
153
  } & {
152
154
  new (...args: any[]): {
@@ -174,8 +176,8 @@ declare const DocumentParserFromMixins: {
174
176
  parseTags<E extends {
175
177
  tags?: import("../generated/ast").Tags;
176
178
  }>(withTags?: E): invariant<invariant> | null;
177
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
178
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
179
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
180
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
179
181
  };
180
182
  } & {
181
183
  new (...args: any[]): {
@@ -204,11 +206,12 @@ declare const DocumentParserFromMixins: {
204
206
  parseTags<E extends {
205
207
  tags?: import("../generated/ast").Tags;
206
208
  }>(withTags?: E): invariant<invariant> | null;
207
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
208
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
209
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
210
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
209
211
  parseDeployment(): void;
210
212
  parseDeploymentNode(astNode: import("../generated/ast").DeploymentNode): import("../ast").ParsedAstDeployment.Node;
211
213
  parseDeployedInstance(astNode: import("../generated/ast").DeployedInstance): import("../ast").ParsedAstDeployment.Instance;
214
+ parseExtendDeployment(astNode: import("../generated/ast").ExtendDeployment): import("../ast").ParsedAstExtend | null;
212
215
  parseDeploymentRelation(astNode: import("../generated/ast").DeploymentRelation): import("../ast").ParsedAstDeploymentRelation;
213
216
  };
214
217
  } & {
@@ -216,6 +219,7 @@ declare const DocumentParserFromMixins: {
216
219
  parseDeployment(): void;
217
220
  parseDeploymentNode(astNode: import("../generated/ast").DeploymentNode): import("../ast").ParsedAstDeployment.Node;
218
221
  parseDeployedInstance(astNode: import("../generated/ast").DeployedInstance): import("../ast").ParsedAstDeployment.Instance;
222
+ parseExtendDeployment(astNode: import("../generated/ast").ExtendDeployment): import("../ast").ParsedAstExtend | null;
219
223
  parseDeploymentRelation(astNode: import("../generated/ast").DeploymentRelation): import("../ast").ParsedAstDeploymentRelation;
220
224
  parseFqnRef(astNode: import("../generated/ast").FqnRef): invariant;
221
225
  parseFqnExpr(astNode: import("../generated/ast").FqnExpr): invariant;
@@ -238,14 +242,14 @@ declare const DocumentParserFromMixins: {
238
242
  parseTags<E extends {
239
243
  tags?: import("../generated/ast").Tags;
240
244
  }>(withTags?: E): invariant<invariant> | null;
241
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
242
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
245
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
246
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
243
247
  };
244
248
  } & {
245
249
  new (...args: any[]): {
246
250
  parseModel(): void;
247
251
  parseElement(astNode: import("../generated/ast").Element): import("../ast").ParsedAstElement;
248
- parseExtendElement(astNode: import("../generated/ast").ExtendElement): import("../ast").ParsedAstExtendElement | null;
252
+ parseExtendElement(astNode: import("../generated/ast").ExtendElement): import("../ast").ParsedAstExtend | null;
249
253
  parseRelation(astNode: import("../generated/ast").Relation): import("../ast").ParsedAstRelation;
250
254
  isValid: import("../validation").IsValidFn;
251
255
  readonly services: LikeC4Services;
@@ -261,8 +265,8 @@ declare const DocumentParserFromMixins: {
261
265
  parseTags<E extends {
262
266
  tags?: import("../generated/ast").Tags;
263
267
  }>(withTags?: E): invariant<invariant> | null;
264
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
265
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
268
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
269
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
266
270
  };
267
271
  } & {
268
272
  new (...args: any[]): {
@@ -287,8 +291,8 @@ declare const DocumentParserFromMixins: {
287
291
  parseTags<E extends {
288
292
  tags?: import("../generated/ast").Tags;
289
293
  }>(withTags?: E): invariant<invariant> | null;
290
- convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
291
- parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): import("../ast").ParsedLink[] | undefined;
294
+ convertLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
295
+ parseLinks(source?: import("../generated/ast").LinkProperty["$container"]): invariant[] | undefined;
292
296
  };
293
297
  } & typeof BaseParser;
294
298
  export declare class DocumentParser extends DocumentParserFromMixins {
@@ -297,7 +301,9 @@ export declare class LikeC4ModelParser {
297
301
  private services;
298
302
  private cachedParsers;
299
303
  constructor(services: LikeC4Services);
304
+ documents(): Stream<ParsedLikeC4LangiumDocument>;
300
305
  parse(doc: LangiumDocument): ParsedLikeC4LangiumDocument;
301
306
  forDocument(doc: LangiumDocument): DocumentParser;
307
+ private createParser;
302
308
  }
303
309
  export {};
@@ -1,7 +1,7 @@
1
1
  import { invariant } from "@likec4/core";
2
2
  import { DocumentCache, DocumentState } from "langium";
3
3
  import { pipe } from "remeda";
4
- import { isFqnIndexedDocument } from "../ast.js";
4
+ import { logger } from "../logger.js";
5
5
  import { BaseParser } from "./parser/Base.js";
6
6
  import { DeploymentModelParser } from "./parser/DeploymentModelParser.js";
7
7
  import { DeploymentViewParser } from "./parser/DeploymentViewParser.js";
@@ -27,50 +27,70 @@ export class DocumentParser extends DocumentParserFromMixins {
27
27
  export class LikeC4ModelParser {
28
28
  constructor(services) {
29
29
  this.services = services;
30
- this.cachedParsers = new DocumentCache(services.shared, DocumentState.Validated);
30
+ this.cachedParsers = new DocumentCache(services.shared, DocumentState.IndexedReferences);
31
+ services.shared.workspace.DocumentBuilder.onDocumentPhase(
32
+ DocumentState.Validated,
33
+ (doc) => {
34
+ try {
35
+ this.cachedParsers.set(doc.uri, "DocumentParser", this.createParser(doc));
36
+ } catch (error) {
37
+ logger.error(`Error caching parser for document ${doc.uri.toString()}`, { error });
38
+ }
39
+ return Promise.resolve();
40
+ }
41
+ );
31
42
  }
32
43
  cachedParsers;
44
+ documents() {
45
+ return this.services.shared.workspace.LangiumDocuments.all.map(
46
+ (d) => this.parse(d)
47
+ );
48
+ }
33
49
  parse(doc) {
34
- invariant(isFqnIndexedDocument(doc), `Not a FqnIndexedDocument: ${doc.uri.toString(true)}`);
35
50
  try {
36
- const props = {
37
- c4Specification: {
38
- tags: /* @__PURE__ */ new Set(),
39
- elements: {},
40
- relationships: {},
41
- colors: {},
42
- deployments: {}
43
- },
44
- c4Elements: [],
45
- c4ExtendElements: [],
46
- c4Relations: [],
47
- c4Deployments: [],
48
- c4DeploymentRelations: [],
49
- c4Globals: {
50
- predicates: {},
51
- dynamicPredicates: {},
52
- styles: {}
53
- },
54
- c4Views: []
55
- };
56
- doc = Object.assign(doc, props);
57
51
  const parser = this.forDocument(doc);
58
- parser.parseSpecification();
59
- parser.parseModel();
60
- parser.parseGlobals();
61
- parser.parseDeployment();
62
- parser.parseViews();
63
52
  return parser.doc;
64
53
  } catch (cause) {
65
54
  throw new Error(`Error parsing document ${doc.uri.toString()}`, { cause });
66
55
  }
67
56
  }
68
57
  forDocument(doc) {
69
- invariant(isFqnIndexedDocument(doc), `Not a FqnIndexedDocument: ${doc.uri.toString(true)}`);
58
+ invariant(doc.state >= DocumentState.IndexedReferences, `Not a IndexedReferences: ${doc.uri.toString(true)}`);
70
59
  return this.cachedParsers.get(
71
60
  doc.uri,
72
61
  "DocumentParser",
73
- () => new DocumentParser(this.services, doc)
62
+ () => this.createParser(doc)
74
63
  );
75
64
  }
65
+ createParser(doc) {
66
+ const props = {
67
+ c4Specification: {
68
+ tags: /* @__PURE__ */ new Set(),
69
+ elements: {},
70
+ relationships: {},
71
+ colors: {},
72
+ deployments: {}
73
+ },
74
+ c4Elements: [],
75
+ c4ExtendElements: [],
76
+ c4ExtendDeployments: [],
77
+ c4Relations: [],
78
+ c4Deployments: [],
79
+ c4DeploymentRelations: [],
80
+ c4Globals: {
81
+ predicates: {},
82
+ dynamicPredicates: {},
83
+ styles: {}
84
+ },
85
+ c4Views: []
86
+ };
87
+ doc = Object.assign(doc, props);
88
+ const parser = new DocumentParser(this.services, doc);
89
+ parser.parseSpecification();
90
+ parser.parseModel();
91
+ parser.parseGlobals();
92
+ parser.parseDeployment();
93
+ parser.parseViews();
94
+ return parser;
95
+ }
76
96
  }
@@ -1,6 +1,6 @@
1
1
  import type * as c4 from '@likec4/core';
2
2
  import type { AstNode } from 'langium';
3
- import { type ParsedLikeC4LangiumDocument, type ParsedLink, ast } from '../../ast';
3
+ import { type ParsedLikeC4LangiumDocument, ast } from '../../ast';
4
4
  import type { LikeC4Services } from '../../module';
5
5
  import { type IsValidFn } from '../../validation';
6
6
  export type GConstructor<T = {}> = new (...args: any[]) => T;
@@ -23,6 +23,6 @@ export declare class BaseParser {
23
23
  parseTags<E extends {
24
24
  tags?: ast.Tags;
25
25
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
26
- convertLinks(source?: ast.LinkProperty['$container']): ParsedLink[] | undefined;
27
- parseLinks(source?: ast.LinkProperty['$container']): ParsedLink[] | undefined;
26
+ convertLinks(source?: ast.LinkProperty['$container']): c4.Link[] | undefined;
27
+ parseLinks(source?: ast.LinkProperty['$container']): c4.Link[] | undefined;
28
28
  }
@@ -1,8 +1,8 @@
1
- import { invariant, isNonEmptyArray } from "@likec4/core";
1
+ import { isNonEmptyArray } from "@likec4/core";
2
2
  import { filter, flatMap, fromEntries, isEmpty, isNonNullish, isTruthy, map, pipe, unique } from "remeda";
3
3
  import stripIndent from "strip-indent";
4
4
  import { ast } from "../../ast.js";
5
- import { getFqnElementRef } from "../../utils/elementRef.js";
5
+ import { readStrictFqn } from "../../utils/elementRef.js";
6
6
  import { checksFromDiagnostics } from "../../validation/index.js";
7
7
  export function toSingleLine(str) {
8
8
  return isNonNullish(str) ? removeIndent(str).split("\n").join(" ") : void 0;
@@ -18,15 +18,16 @@ export class BaseParser {
18
18
  }
19
19
  isValid;
20
20
  resolveFqn(node) {
21
+ if (ast.isExtendElement(node)) {
22
+ return readStrictFqn(node.element);
23
+ }
24
+ if (ast.isExtendDeployment(node)) {
25
+ return readStrictFqn(node.deploymentNode);
26
+ }
21
27
  if (ast.isDeploymentElement(node)) {
22
28
  return this.services.likec4.DeploymentsIndex.getFqn(node);
23
29
  }
24
- if (ast.isExtendElement(node)) {
25
- return getFqnElementRef(node.element);
26
- }
27
- const fqn = this.services.likec4.FqnIndex.getFqn(node);
28
- invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
29
- return fqn;
30
+ return this.services.likec4.FqnIndex.getFqn(node);
30
31
  }
31
32
  getAstNodePath(node) {
32
33
  return this.services.workspace.AstNodeLocator.getAstNodePath(node);
@@ -84,7 +85,12 @@ export class BaseParser {
84
85
  const url = p.value;
85
86
  if (isTruthy(url)) {
86
87
  const title = isTruthy(p.title) ? toSingleLine(p.title) : void 0;
87
- return title ? { url, title } : { url };
88
+ const relative = this.services.lsp.DocumentLinkProvider.relativeLink(this.doc, url);
89
+ return {
90
+ url,
91
+ ...title && { title },
92
+ ...relative && relative !== url && { relative }
93
+ };
88
94
  }
89
95
  return [];
90
96
  })
@@ -1,5 +1,5 @@
1
1
  import type * as c4 from '@likec4/core';
2
- import { type ParsedAstDeployment, type ParsedAstDeploymentRelation, ast } from '../../ast';
2
+ import { type ParsedAstDeployment, type ParsedAstDeploymentRelation, type ParsedAstExtend, ast } from '../../ast';
3
3
  import type { WithExpressionV2 } from './FqnRefParser';
4
4
  export type WithDeploymentModel = ReturnType<typeof DeploymentModelParser>;
5
5
  export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B: TBase): {
@@ -7,6 +7,7 @@ export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B:
7
7
  parseDeployment(): void;
8
8
  parseDeploymentNode(astNode: ast.DeploymentNode): ParsedAstDeployment.Node;
9
9
  parseDeployedInstance(astNode: ast.DeployedInstance): ParsedAstDeployment.Instance;
10
+ parseExtendDeployment(astNode: ast.ExtendDeployment): ParsedAstExtend | null;
10
11
  parseDeploymentRelation(astNode: ast.DeploymentRelation): ParsedAstDeploymentRelation;
11
12
  parseFqnRef(astNode: ast.FqnRef): c4.FqnRef;
12
13
  parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr;
@@ -29,7 +30,7 @@ export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B:
29
30
  parseTags<E extends {
30
31
  tags?: ast.Tags;
31
32
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
32
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
33
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
33
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
34
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
34
35
  };
35
36
  } & TBase;
@@ -1,8 +1,7 @@
1
- import { FqnRef, isNonEmptyArray, nameFromFqn, nonexhaustive, nonNullable } from "@likec4/core";
2
- import { filter, first, isTruthy, map, mapToObj, pipe } from "remeda";
1
+ import { FqnRef, isNonEmptyArray, LinkedList, nameFromFqn, nonexhaustive, nonNullable } from "@likec4/core";
2
+ import { filter, first, isEmpty, isTruthy, map, mapToObj, pipe } from "remeda";
3
3
  import {
4
4
  ast,
5
- streamDeploymentModel,
6
5
  toElementStyle,
7
6
  toRelationshipStyleExcludeDefaults
8
7
  } from "../../ast.js";
@@ -10,6 +9,31 @@ import { logWarnError } from "../../logger.js";
10
9
  import { elementRef } from "../../utils/elementRef.js";
11
10
  import { stringHash } from "../../utils/stringHash.js";
12
11
  import { removeIndent, toSingleLine } from "./Base.js";
12
+ function* streamDeploymentModel(doc) {
13
+ const traverseStack = LinkedList.from(
14
+ doc.parseResult.value.deployments.flatMap((m) => m.elements)
15
+ );
16
+ const relations = [];
17
+ let el;
18
+ while (el = traverseStack.shift()) {
19
+ if (ast.isDeploymentRelation(el)) {
20
+ relations.push(el);
21
+ continue;
22
+ }
23
+ if (ast.isDeployedInstance(el)) {
24
+ yield el;
25
+ continue;
26
+ }
27
+ if (el.body && el.body.elements.length > 0) {
28
+ for (const child of el.body.elements) {
29
+ traverseStack.push(child);
30
+ }
31
+ }
32
+ yield el;
33
+ }
34
+ yield* relations;
35
+ return;
36
+ }
13
37
  export function DeploymentModelParser(B) {
14
38
  return class DeploymentModelParser extends B {
15
39
  parseDeployment() {
@@ -30,6 +54,13 @@ export function DeploymentModelParser(B) {
30
54
  doc.c4Deployments.push(this.parseDeploymentNode(el));
31
55
  break;
32
56
  }
57
+ case ast.isExtendDeployment(el): {
58
+ const parsed = this.parseExtendDeployment(el);
59
+ if (parsed) {
60
+ doc.c4ExtendDeployments.push(parsed);
61
+ }
62
+ break;
63
+ }
33
64
  default:
34
65
  nonexhaustive(el);
35
66
  }
@@ -112,6 +143,26 @@ export function DeploymentModelParser(B) {
112
143
  style
113
144
  };
114
145
  }
146
+ parseExtendDeployment(astNode) {
147
+ if (!this.isValid(astNode)) {
148
+ return null;
149
+ }
150
+ const id = this.resolveFqn(astNode);
151
+ const tags = this.parseTags(astNode.body);
152
+ const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
153
+ const astPath = this.getAstNodePath(astNode);
154
+ const links = this.parseLinks(astNode.body) ?? [];
155
+ if (!tags && isEmpty(metadata ?? {}) && isEmpty(links)) {
156
+ return null;
157
+ }
158
+ return {
159
+ id,
160
+ astPath,
161
+ ...metadata && { metadata },
162
+ ...tags && { tags },
163
+ ...links && isNonEmptyArray(links) && { links }
164
+ };
165
+ }
115
166
  parseDeploymentRelation(astNode) {
116
167
  const isValid = this.isValid;
117
168
  const astPath = this.getAstNodePath(astNode);
@@ -30,11 +30,12 @@ export declare function DeploymentViewParser<TBase extends WithExpressionV2 & Wi
30
30
  parseTags<E extends {
31
31
  tags?: ast.Tags;
32
32
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
33
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
34
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
33
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
34
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
35
35
  parseDeployment(): void;
36
36
  parseDeploymentNode(astNode: ast.DeploymentNode): import("../../ast").ParsedAstDeployment.Node;
37
37
  parseDeployedInstance(astNode: ast.DeployedInstance): import("../../ast").ParsedAstDeployment.Instance;
38
+ parseExtendDeployment(astNode: ast.ExtendDeployment): import("../../ast").ParsedAstExtend | null;
38
39
  parseDeploymentRelation(astNode: ast.DeploymentRelation): import("../../ast").ParsedAstDeploymentRelation;
39
40
  };
40
41
  } & TBase;
@@ -25,7 +25,7 @@ export declare function ExpressionV2Parser<TBase extends Base>(B: TBase): {
25
25
  parseTags<E extends {
26
26
  tags?: ast.Tags;
27
27
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
28
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
29
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
28
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
29
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
30
30
  };
31
31
  } & TBase;
@@ -47,8 +47,8 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
47
47
  parseTags<E extends {
48
48
  tags?: ast.Tags;
49
49
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
50
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
51
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
50
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
51
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
52
52
  parseDeploymentView(astNode: ast.DeploymentView): import("../../ast").ParsedAstDeploymentView;
53
53
  parseDeploymentViewRule(astRule: ast.DeploymentViewRule): c4.DeploymentViewRule;
54
54
  parseDeploymentViewRulePredicate(astRule: ast.DeploymentViewRulePredicate): c4.DeploymentViewRulePredicate;
@@ -63,6 +63,7 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
63
63
  parseDeployment(): void;
64
64
  parseDeploymentNode(astNode: ast.DeploymentNode): import("../../ast").ParsedAstDeployment.Node;
65
65
  parseDeployedInstance(astNode: ast.DeployedInstance): import("../../ast").ParsedAstDeployment.Instance;
66
+ parseExtendDeployment(astNode: ast.ExtendDeployment): import("../../ast").ParsedAstExtend | null;
66
67
  parseDeploymentRelation(astNode: ast.DeploymentRelation): import("../../ast").ParsedAstDeploymentRelation;
67
68
  };
68
69
  } & TBase;
@@ -1,12 +1,12 @@
1
1
  import type * as c4 from '@likec4/core';
2
- import { type ParsedAstElement, type ParsedAstExtendElement, type ParsedAstRelation, ast } from '../../ast';
2
+ import { type ParsedAstElement, type ParsedAstExtend, type ParsedAstRelation, ast } from '../../ast';
3
3
  import { type Base } from './Base';
4
4
  export type WithModel = ReturnType<typeof ModelParser>;
5
5
  export declare function ModelParser<TBase extends Base>(B: TBase): {
6
6
  new (...args: any[]): {
7
7
  parseModel(): void;
8
8
  parseElement(astNode: ast.Element): ParsedAstElement;
9
- parseExtendElement(astNode: ast.ExtendElement): ParsedAstExtendElement | null;
9
+ parseExtendElement(astNode: ast.ExtendElement): ParsedAstExtend | null;
10
10
  parseRelation(astNode: ast.Relation): ParsedAstRelation;
11
11
  isValid: import("../../validation").IsValidFn;
12
12
  readonly services: import("../..").LikeC4Services;
@@ -22,7 +22,7 @@ export declare function ModelParser<TBase extends Base>(B: TBase): {
22
22
  parseTags<E extends {
23
23
  tags?: ast.Tags;
24
24
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
25
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
26
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
25
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
26
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
27
27
  };
28
28
  } & TBase;
@@ -1,15 +1,56 @@
1
- import { isNonEmptyArray, nonexhaustive, nonNullable } from "@likec4/core";
2
- import { filter, first, isEmpty, isNonNullish, isTruthy, map, mapToObj, pipe } from "remeda";
1
+ import { isNonEmptyArray, LinkedList, nonexhaustive, nonNullable } from "@likec4/core";
2
+ import { filter, first, isDefined, isEmpty, isNonNullish, isTruthy, map, mapToObj, pipe } from "remeda";
3
3
  import {
4
4
  ast,
5
- resolveRelationPoints,
6
- streamModel,
7
5
  toElementStyle,
8
6
  toRelationshipStyleExcludeDefaults
9
7
  } from "../../ast.js";
10
8
  import { logWarnError } from "../../logger.js";
9
+ import { elementRef } from "../../utils/elementRef.js";
11
10
  import { stringHash } from "../../utils/stringHash.js";
12
11
  import { removeIndent, toSingleLine } from "./Base.js";
12
+ function resolveRelationPoints(node) {
13
+ const target = elementRef(node.target);
14
+ if (!target) {
15
+ throw new Error("RelationRefError: Invalid reference to target");
16
+ }
17
+ if (isDefined(node.source)) {
18
+ const source = elementRef(node.source);
19
+ if (!source) {
20
+ throw new Error("RelationRefError: Invalid reference to source");
21
+ }
22
+ return {
23
+ source,
24
+ target
25
+ };
26
+ }
27
+ if (!ast.isElementBody(node.$container)) {
28
+ throw new Error("RelationRefError: Invalid container for sourceless relation");
29
+ }
30
+ return {
31
+ source: node.$container.$container,
32
+ target
33
+ };
34
+ }
35
+ function* streamModel(doc) {
36
+ const traverseStack = LinkedList.from(doc.parseResult.value.models.flatMap((m) => m.elements));
37
+ const relations = [];
38
+ let el;
39
+ while (el = traverseStack.shift()) {
40
+ if (ast.isRelation(el)) {
41
+ relations.push(el);
42
+ continue;
43
+ }
44
+ if (el.body && el.body.elements && el.body.elements.length > 0) {
45
+ for (const child of el.body.elements) {
46
+ traverseStack.push(child);
47
+ }
48
+ }
49
+ yield el;
50
+ }
51
+ yield* relations;
52
+ return;
53
+ }
13
54
  export function ModelParser(B) {
14
55
  return class ModelParser extends B {
15
56
  parseModel() {
@@ -28,7 +28,7 @@ export declare function PredicatesParser<TBase extends Base>(B: TBase): {
28
28
  parseTags<E extends {
29
29
  tags?: ast.Tags;
30
30
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
31
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
32
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
31
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
32
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
33
33
  };
34
34
  } & TBase;
@@ -21,7 +21,7 @@ export declare function SpecificationParser<TBase extends Base>(B: TBase): {
21
21
  parseTags<E extends {
22
22
  tags?: ast.Tags;
23
23
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
24
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
25
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
24
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
25
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
26
26
  };
27
27
  } & TBase;