@likec4/language-server 1.23.1 → 1.24.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 (73) hide show
  1. package/dist/LikeC4FileSystem.d.ts +1 -0
  2. package/dist/LikeC4FileSystem.js +7 -0
  3. package/dist/Rpc.js +13 -11
  4. package/dist/ast.d.ts +13 -29
  5. package/dist/ast.js +3 -70
  6. package/dist/bundled.mjs +2441 -2610
  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 +10 -6
  15. package/dist/lsp/CompletionProvider.js +20 -2
  16. package/dist/lsp/DocumentLinkProvider.d.ts +3 -3
  17. package/dist/lsp/DocumentLinkProvider.js +5 -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 +5 -56
  29. package/dist/model/deployments-index.js +61 -137
  30. package/dist/model/fqn-index.d.ts +50 -19
  31. package/dist/model/fqn-index.js +176 -69
  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 +10 -9
  35. package/dist/model/model-builder.js +102 -547
  36. package/dist/model/model-locator.d.ts +2 -1
  37. package/dist/model/model-locator.js +7 -9
  38. package/dist/model/model-parser.d.ts +156 -150
  39. package/dist/model/model-parser.js +68 -38
  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 +23 -20
  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 +12 -13
  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
@@ -1,7 +1,10 @@
1
- import { invariant } from "@likec4/core";
2
- import { DocumentCache, DocumentState } from "langium";
1
+ import { DefaultWeakMap } from "@likec4/core/utils";
2
+ import { loggable } from "@likec4/log";
3
+ import { DocumentState } from "langium";
3
4
  import { pipe } from "remeda";
4
- import { isFqnIndexedDocument } from "../ast.js";
5
+ import { DiagnosticSeverity } from "vscode-languageserver-types";
6
+ import { isLikeC4Builtin } from "../likec4lib.js";
7
+ import { logger as rootLogger } from "../logger.js";
5
8
  import { BaseParser } from "./parser/Base.js";
6
9
  import { DeploymentModelParser } from "./parser/DeploymentModelParser.js";
7
10
  import { DeploymentViewParser } from "./parser/DeploymentViewParser.js";
@@ -24,53 +27,80 @@ const DocumentParserFromMixins = pipe(
24
27
  );
25
28
  export class DocumentParser extends DocumentParserFromMixins {
26
29
  }
30
+ const logger = rootLogger.getChild("ModelParser");
27
31
  export class LikeC4ModelParser {
28
32
  constructor(services) {
29
33
  this.services = services;
30
- this.cachedParsers = new DocumentCache(services.shared, DocumentState.Validated);
34
+ services.shared.workspace.DocumentBuilder.onDocumentPhase(
35
+ DocumentState.Linked,
36
+ (doc) => {
37
+ try {
38
+ if (!isLikeC4Builtin(doc.uri)) {
39
+ this.cachedParsers.set(doc, this.createParser(doc));
40
+ }
41
+ } catch (e) {
42
+ logger.warn(loggable(e));
43
+ }
44
+ }
45
+ );
46
+ services.shared.workspace.DocumentBuilder.onDocumentPhase(
47
+ DocumentState.Validated,
48
+ (doc) => {
49
+ if (doc.diagnostics?.some((d) => d.severity === DiagnosticSeverity.Error)) {
50
+ this.cachedParsers.delete(doc);
51
+ }
52
+ }
53
+ );
54
+ }
55
+ cachedParsers = new DefaultWeakMap((doc) => this.createParser(doc));
56
+ documents() {
57
+ return this.services.shared.workspace.LangiumDocuments.all.map(
58
+ (d) => this.parse(d)
59
+ );
31
60
  }
32
- cachedParsers;
33
61
  parse(doc) {
34
- invariant(isFqnIndexedDocument(doc), `Not a FqnIndexedDocument: ${doc.uri.toString(true)}`);
35
62
  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
63
  const parser = this.forDocument(doc);
58
- parser.parseSpecification();
59
- parser.parseModel();
60
- parser.parseGlobals();
61
- parser.parseDeployment();
62
- parser.parseViews();
63
64
  return parser.doc;
64
65
  } catch (cause) {
65
66
  throw new Error(`Error parsing document ${doc.uri.toString()}`, { cause });
66
67
  }
67
68
  }
68
69
  forDocument(doc) {
69
- invariant(isFqnIndexedDocument(doc), `Not a FqnIndexedDocument: ${doc.uri.toString(true)}`);
70
- return this.cachedParsers.get(
71
- doc.uri,
72
- "DocumentParser",
73
- () => new DocumentParser(this.services, doc)
74
- );
70
+ if (doc.state < DocumentState.Linked) {
71
+ logger.warn(`Document ${doc.uri.toString()} is not linked`);
72
+ }
73
+ return this.cachedParsers.get(doc);
74
+ }
75
+ createParser(doc) {
76
+ const props = {
77
+ c4Specification: {
78
+ tags: /* @__PURE__ */ new Set(),
79
+ elements: {},
80
+ relationships: {},
81
+ colors: {},
82
+ deployments: {}
83
+ },
84
+ c4Elements: [],
85
+ c4ExtendElements: [],
86
+ c4ExtendDeployments: [],
87
+ c4Relations: [],
88
+ c4Deployments: [],
89
+ c4DeploymentRelations: [],
90
+ c4Globals: {
91
+ predicates: {},
92
+ dynamicPredicates: {},
93
+ styles: {}
94
+ },
95
+ c4Views: []
96
+ };
97
+ doc = Object.assign(doc, props);
98
+ const parser = new DocumentParser(this.services, doc);
99
+ parser.parseSpecification();
100
+ parser.parseModel();
101
+ parser.parseGlobals();
102
+ parser.parseDeployment();
103
+ parser.parseViews();
104
+ return parser;
75
105
  }
76
106
  }
@@ -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;
@@ -45,8 +45,8 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
45
45
  parseTags<E extends {
46
46
  tags?: ast.Tags;
47
47
  }>(withTags?: E): c4.NonEmptyArray<c4.Tag> | null;
48
- convertLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
49
- parseLinks(source?: ast.LinkProperty["$container"]): import("../../ast").ParsedLink[] | undefined;
48
+ convertLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
49
+ parseLinks(source?: ast.LinkProperty["$container"]): c4.Link[] | undefined;
50
50
  parseDeploymentView(astNode: ast.DeploymentView): import("../../ast").ParsedAstDeploymentView;
51
51
  parseDeploymentViewRule(astRule: ast.DeploymentViewRule): c4.DeploymentViewRule;
52
52
  parseDeploymentViewRulePredicate(astRule: ast.DeploymentViewRulePredicate): c4.DeploymentViewRulePredicate;
@@ -61,6 +61,7 @@ export declare function ViewsParser<TBase extends WithPredicates & WithDeploymen
61
61
  parseDeployment(): void;
62
62
  parseDeploymentNode(astNode: ast.DeploymentNode): import("../../ast").ParsedAstDeployment.Node;
63
63
  parseDeployedInstance(astNode: ast.DeployedInstance): import("../../ast").ParsedAstDeployment.Instance;
64
+ parseExtendDeployment(astNode: ast.ExtendDeployment): import("../../ast").ParsedAstExtend | null;
64
65
  parseDeploymentRelation(astNode: ast.DeploymentRelation): import("../../ast").ParsedAstDeploymentRelation;
65
66
  };
66
67
  } & TBase;
package/dist/module.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { GraphvizLayouter } from '@likec4/layouts';
2
- import { type Module, DocumentCache, WorkspaceCache } from 'langium';
2
+ import { type Module, WorkspaceCache } from 'langium';
3
3
  import { type DefaultSharedModuleContext, type LangiumServices, type LangiumSharedServices, type PartialLangiumServices } from 'langium/lsp';
4
4
  import { LikeC4DocumentationProvider } from './documentation';
5
5
  import { LikeC4CodeLensProvider, LikeC4CompletionProvider, LikeC4DocumentHighlightProvider, LikeC4DocumentLinkProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
@@ -26,8 +26,7 @@ export interface LikeC4AddedServices {
26
26
  documentation: {
27
27
  DocumentationProvider: LikeC4DocumentationProvider;
28
28
  };
29
- WorkspaceCache: WorkspaceCache<string, any>;
30
- DocumentCache: DocumentCache<string, any>;
29
+ ValidatedWorkspaceCache: WorkspaceCache<string, any>;
31
30
  Rpc: Rpc;
32
31
  likec4: {
33
32
  Views: LikeC4Views;
package/dist/module.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { GraphvizLayouter, GraphvizWasmAdapter } from "@likec4/layouts";
2
2
  import {
3
- DocumentCache,
3
+ DocumentState,
4
4
  EmptyFileSystem,
5
5
  inject,
6
6
  WorkspaceCache
@@ -62,8 +62,7 @@ export const LikeC4Module = {
62
62
  documentation: {
63
63
  DocumentationProvider: bind(LikeC4DocumentationProvider)
64
64
  },
65
- WorkspaceCache: (services) => new WorkspaceCache(services.shared),
66
- DocumentCache: (services) => new DocumentCache(services.shared),
65
+ ValidatedWorkspaceCache: (services) => new WorkspaceCache(services.shared, DocumentState.Validated),
67
66
  Rpc: bind(Rpc),
68
67
  likec4: {
69
68
  Layouter: (_services) => {
@@ -3,7 +3,7 @@ import type { CancellationToken } from 'vscode-languageserver';
3
3
  import { type LikeC4LangiumDocument, ast } from '../ast';
4
4
  import type { LikeC4Services } from '../module';
5
5
  type ElementsContainer = ast.Model | ast.ElementBody | ast.ExtendElementBody;
6
- type DeploymentsContainer = ast.ModelDeployments | ast.DeploymentNodeBody;
6
+ type DeploymentsContainer = ast.ModelDeployments | ast.DeploymentNodeBody | ast.ExtendDeploymentBody;
7
7
  export declare class LikeC4ScopeComputation extends DefaultScopeComputation {
8
8
  constructor(services: LikeC4Services);
9
9
  computeExports(document: LikeC4LangiumDocument, _cancelToken?: CancellationToken): Promise<AstNodeDescription[]>;
@@ -242,19 +242,20 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
242
242
  return local;
243
243
  }
244
244
  processDeployments(container, scopes, document) {
245
- const localnames = /* @__PURE__ */ new Set();
245
+ const localScope = new MultiMap();
246
246
  const descedants = [];
247
247
  for (const el of container.elements) {
248
248
  if (ast.isDeploymentRelation(el)) {
249
249
  continue;
250
250
  }
251
- let name = this.nameProvider.getName(el);
252
- if (isTruthy(name)) {
253
- const desc = this.descriptions.createDescription(el, name, document);
254
- scopes.add(container, desc);
255
- localnames.add(desc.name);
251
+ if (!ast.isExtendDeployment(el)) {
252
+ let name = this.nameProvider.getName(el);
253
+ if (isTruthy(name)) {
254
+ const desc = this.descriptions.createDescription(el, name, document);
255
+ localScope.add(name, desc);
256
+ }
256
257
  }
257
- if (ast.isDeploymentNode(el) && el.body) {
258
+ if (!ast.isDeployedInstance(el) && el.body) {
258
259
  try {
259
260
  descedants.push(
260
261
  ...this.processDeployments(el.body, scopes, document)
@@ -264,18 +265,20 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
264
265
  }
265
266
  }
266
267
  }
267
- if (descedants.length > 0) {
268
+ if (descedants.length) {
268
269
  pipe(
269
270
  descedants,
270
- filter((desc) => !localnames.has(desc.name)),
271
+ filter((desc) => !localScope.has(desc.name)),
271
272
  groupBy((desc) => desc.name),
272
273
  forEachObj((descs, name) => {
273
274
  if (descs.length === 1) {
274
- scopes.add(container, descs[0]);
275
+ localScope.add(name, descs[0]);
275
276
  }
276
277
  })
277
278
  );
278
279
  }
279
- return [...scopes.get(container).values()];
280
+ const local = [...localScope.values()];
281
+ scopes.addAll(container, local);
282
+ return local;
280
283
  }
281
284
  }
@@ -5,12 +5,24 @@ export declare class LikeC4ScopeProvider extends DefaultScopeProvider {
5
5
  private deploymentsIndex;
6
6
  private fqnIndex;
7
7
  constructor(services: LikeC4Services);
8
- private directChildrenOf;
9
8
  private uniqueDescedants;
10
- private scopeElementRef;
11
- private scopeExtendElement;
12
- private scopeElementView;
13
9
  getScope(context: ReferenceInfo): Scope;
10
+ private getScopeElementRef;
11
+ private getScopeExtendElement;
12
+ private getScopeElementView;
13
+ private getScopeForStrictFqnRef;
14
+ private getScopeExtendDeployment;
14
15
  protected getScopeForFqnRef(container: ast.FqnRef, context: ReferenceInfo): any;
16
+ /**
17
+ * Computes the scope for a given reference context.
18
+ *
19
+ * @param context - The reference information containing the context for which the scope is being computed.
20
+ * @param referenceType - The type of reference being resolved. Defaults to the reference type derived from the context.
21
+ * @returns A scope containing the relevant AST node descriptions for the given reference context.
22
+ *
23
+ * This method first checks if there are precomputed scopes available in the document. If not, it falls back to the global scope.
24
+ * It then iterates through the container hierarchy, collecting relevant scopes based on the reference type and container type.
25
+ * Finally, it combines the collected scopes with the global scope to produce the final scope.
26
+ */
15
27
  protected computeScope(context: ReferenceInfo, referenceType?: any): any;
16
28
  }