@likec4/language-server 0.37.1 → 0.41.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 (60) hide show
  1. package/contrib/likec4.monarch.ts +5 -5
  2. package/contrib/likec4.tmLanguage.json +1 -1
  3. package/dist/Rpc.d.ts +7 -0
  4. package/dist/Rpc.js +130 -0
  5. package/dist/ast.d.ts +20 -0
  6. package/dist/ast.js +169 -141
  7. package/dist/elementRef.js +31 -44
  8. package/dist/generated/ast.d.ts +48 -7
  9. package/dist/generated/ast.js +344 -315
  10. package/dist/generated/grammar.js +2 -3177
  11. package/dist/generated/module.js +13 -18
  12. package/dist/index.js +2 -3
  13. package/dist/logger.js +39 -42
  14. package/dist/lsp/CodeLensProvider.js +28 -32
  15. package/dist/lsp/DocumentLinkProvider.js +26 -33
  16. package/dist/lsp/DocumentSymbolProvider.js +165 -167
  17. package/dist/lsp/HoverProvider.js +35 -48
  18. package/dist/lsp/SemanticTokenProvider.js +160 -201
  19. package/dist/lsp/index.js +5 -6
  20. package/dist/model/fqn-computation.js +39 -40
  21. package/dist/model/fqn-index.js +117 -141
  22. package/dist/model/index.js +5 -6
  23. package/dist/model/model-builder.d.ts +10 -5
  24. package/dist/model/model-builder.js +247 -176
  25. package/dist/model/model-locator.d.ts +1 -1
  26. package/dist/model/model-locator.js +102 -100
  27. package/dist/model/model-parser.d.ts +2 -6
  28. package/dist/model/model-parser.js +284 -286
  29. package/dist/module.d.ts +4 -1
  30. package/dist/module.js +69 -60
  31. package/dist/protocol.d.ts +16 -19
  32. package/dist/protocol.js +14 -22
  33. package/dist/references/index.js +2 -3
  34. package/dist/references/scope-computation.js +72 -69
  35. package/dist/references/scope-provider.js +126 -116
  36. package/dist/shared/WorkspaceManager.d.ts +2 -3
  37. package/dist/shared/WorkspaceManager.js +13 -16
  38. package/dist/shared/index.js +1 -2
  39. package/dist/test/index.js +1 -2
  40. package/dist/test/testServices.js +73 -61
  41. package/dist/utils.d.ts +1 -0
  42. package/dist/utils.js +4 -3
  43. package/dist/validation/element.d.ts +1 -0
  44. package/dist/validation/element.js +31 -38
  45. package/dist/validation/index.js +37 -46
  46. package/dist/validation/relation.js +46 -46
  47. package/dist/validation/specification.d.ts +1 -0
  48. package/dist/validation/specification.js +33 -30
  49. package/dist/validation/view.js +16 -22
  50. package/dist/view-utils/assignNavigateTo.d.ts +3 -0
  51. package/dist/view-utils/assignNavigateTo.js +20 -0
  52. package/dist/view-utils/index.d.ts +4 -0
  53. package/dist/view-utils/index.js +3 -0
  54. package/dist/view-utils/resolve-extended-views.d.ts +7 -0
  55. package/dist/view-utils/resolve-extended-views.js +41 -0
  56. package/dist/view-utils/resolve-relative-paths.d.ts +3 -0
  57. package/dist/view-utils/resolve-relative-paths.js +76 -0
  58. package/package.json +33 -18
  59. package/dist/registerProtocolHandlers.d.ts +0 -3
  60. package/dist/registerProtocolHandlers.js +0 -112
@@ -1,6 +1,5 @@
1
1
  import { type c4 } from '@likec4/core';
2
2
  import type { LangiumDocument } from 'langium';
3
- import { Disposable, type CancellationToken } from 'vscode-languageserver-protocol';
4
3
  import type { LikeC4LangiumDocument } from '../ast';
5
4
  import { ast } from '../ast';
6
5
  import type { LikeC4Services } from '../module';
@@ -8,11 +7,9 @@ export type ModelParsedListener = () => void;
8
7
  export declare class LikeC4ModelParser {
9
8
  private services;
10
9
  private fqnIndex;
11
- protected readonly listeners: ModelParsedListener[];
12
10
  constructor(services: LikeC4Services);
13
- onParsed(callback: ModelParsedListener): Disposable;
14
- protected onValidated(docs: LangiumDocument[], cancelToken: CancellationToken): Promise<void>;
15
- protected parseDocument(doc: LikeC4LangiumDocument, cancelToken: CancellationToken): Promise<void>;
11
+ parse(doc: LangiumDocument | LangiumDocument[]): void;
12
+ protected parseLikeC4Document(doc: LikeC4LangiumDocument): void;
16
13
  private parseElement;
17
14
  private parseRelation;
18
15
  private parseElementExpression;
@@ -22,6 +19,5 @@ export declare class LikeC4ModelParser {
22
19
  protected resolveFqn(node: ast.Element | ast.ExtendElement): c4.Fqn;
23
20
  private getAstNodePath;
24
21
  private convertTags;
25
- private notifyListeners;
26
22
  }
27
23
  //# sourceMappingURL=model-parser.d.ts.map
@@ -1,309 +1,307 @@
1
- import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core';
2
- import { DocumentState, getDocument, interruptAndCheck } from 'langium';
3
- import objectHash from 'object-hash';
4
- import stripIndent from 'strip-indent';
5
- import { Disposable } from 'vscode-languageserver-protocol';
6
- import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle, toElementStyleExcludeDefaults } from '../ast';
7
- import { elementRef, fqnElementRef } from '../elementRef';
8
- import { logError, logWarnError, logger } from '../logger';
9
- import { printDocs } from '../utils';
1
+ import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
2
+ import { getDocument } from "langium";
3
+ import objectHash from "object-hash";
4
+ import stripIndent from "strip-indent";
5
+ import {
6
+ ElementViewOps,
7
+ ast,
8
+ cleanParsedModel,
9
+ isLikeC4LangiumDocument,
10
+ resolveRelationPoints,
11
+ streamModel,
12
+ toAutoLayout,
13
+ toElementStyle,
14
+ toElementStyleExcludeDefaults,
15
+ toRelationshipStyleExcludeDefaults
16
+ } from "../ast.js";
17
+ import { elementRef, fqnElementRef } from "../elementRef.js";
18
+ import { logError, logWarnError, logger } from "../logger.js";
19
+ import { printDocs } from "../utils.js";
10
20
  export class LikeC4ModelParser {
11
- services;
12
- fqnIndex;
13
- listeners = [];
14
- constructor(services) {
15
- this.services = services;
16
- this.fqnIndex = services.likec4.FqnIndex;
17
- services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, async (docs, cancelToken) => await this.onValidated(docs, cancelToken));
21
+ constructor(services) {
22
+ this.services = services;
23
+ this.fqnIndex = services.likec4.FqnIndex;
24
+ }
25
+ fqnIndex;
26
+ parse(doc) {
27
+ const docs = Array.isArray(doc) ? doc : [doc];
28
+ logger.debug(`[ModelParser] onValidated (${docs.length} docs)
29
+ ${printDocs(docs)}`);
30
+ for (const doc2 of docs) {
31
+ if (!isLikeC4LangiumDocument(doc2)) {
32
+ continue;
33
+ }
34
+ try {
35
+ this.parseLikeC4Document(doc2);
36
+ } catch (cause) {
37
+ logError(new InvalidModelError(`Error parsing document ${doc2.uri.toString()}`, { cause }));
38
+ }
18
39
  }
19
- onParsed(callback) {
20
- this.listeners.push(callback);
21
- return Disposable.create(() => {
22
- const index = this.listeners.indexOf(callback);
23
- if (index >= 0) {
24
- this.listeners.splice(index, 1);
25
- }
26
- });
40
+ }
41
+ parseLikeC4Document(doc) {
42
+ const { elements, relations, views, specification } = cleanParsedModel(doc);
43
+ const specs = doc.parseResult.value.specification?.elements;
44
+ if (specs) {
45
+ for (const { kind, style } of specs) {
46
+ if (kind.name in specification.kinds) {
47
+ logger.warn(`Duplicate specification for kind ${kind.name}`);
48
+ continue;
49
+ }
50
+ try {
51
+ specification.kinds[kind.name] = toElementStyleExcludeDefaults(
52
+ style?.props
53
+ );
54
+ } catch (e) {
55
+ logWarnError(e);
56
+ }
57
+ }
27
58
  }
28
- async onValidated(docs, cancelToken) {
29
- let countOfChangedDocs = 0;
30
- logger.debug(`[ModelParser] onValidated (${docs.length} docs)\n${printDocs(docs)}`);
31
- for (const doc of docs) {
32
- if (!isLikeC4LangiumDocument(doc)) {
33
- continue;
34
- }
35
- countOfChangedDocs++;
36
- try {
37
- await this.parseDocument(doc, cancelToken);
38
- }
39
- catch (cause) {
40
- logError(new InvalidModelError(`Error parsing document ${doc.uri.toString()}`, { cause }));
41
- }
59
+ const relations_specs = doc.parseResult.value.specification?.relationships;
60
+ if (relations_specs) {
61
+ for (const { kind, props } of relations_specs) {
62
+ if (kind.name in specification.relationships) {
63
+ logger.warn(`Duplicate specification for kind ${kind.name}`);
64
+ continue;
42
65
  }
43
- if (countOfChangedDocs > 0) {
44
- this.notifyListeners();
66
+ try {
67
+ specification.relationships[kind.name] = toRelationshipStyleExcludeDefaults(
68
+ props
69
+ );
70
+ } catch (e) {
71
+ logWarnError(e);
45
72
  }
73
+ }
46
74
  }
47
- async parseDocument(doc, cancelToken) {
48
- const { elements, relations, views, specification } = cleanParsedModel(doc);
49
- const specs = doc.parseResult.value.specification?.elements;
50
- if (specs) {
51
- for (const { kind, style } of specs) {
52
- if (kind.name in specification.kinds) {
53
- logger.warn(`Duplicate specification for kind ${kind.name}`);
54
- continue;
55
- }
56
- try {
57
- specification.kinds[kind.name] = toElementStyleExcludeDefaults(style?.props);
58
- }
59
- catch (e) {
60
- logWarnError(e);
61
- }
62
- }
75
+ for (const el of streamModel(doc)) {
76
+ if (ast.isElement(el)) {
77
+ try {
78
+ elements.push(this.parseElement(el));
79
+ } catch (e) {
80
+ logWarnError(e);
63
81
  }
64
- await interruptAndCheck(cancelToken);
65
- for (const el of streamModel(doc)) {
66
- if (ast.isElement(el)) {
67
- try {
68
- elements.push(this.parseElement(el));
69
- }
70
- catch (e) {
71
- logWarnError(e);
72
- }
73
- continue;
74
- }
75
- if (ast.isRelation(el)) {
76
- try {
77
- relations.push(this.parseRelation(el));
78
- }
79
- catch (e) {
80
- logWarnError(e);
81
- }
82
- continue;
83
- }
84
- nonexhaustive(el);
82
+ continue;
83
+ }
84
+ if (ast.isRelation(el)) {
85
+ try {
86
+ relations.push(this.parseRelation(el));
87
+ } catch (e) {
88
+ logWarnError(e);
85
89
  }
86
- await interruptAndCheck(cancelToken);
87
- const docviews = doc.parseResult.value.views?.views;
88
- if (docviews) {
89
- for (const view of docviews) {
90
- try {
91
- const v = this.parseElementView(view);
92
- ElementViewOps.writeId(view, v.id);
93
- views.push(v);
94
- }
95
- catch (e) {
96
- logWarnError(e);
97
- }
98
- }
90
+ continue;
91
+ }
92
+ nonexhaustive(el);
93
+ }
94
+ const docviews = doc.parseResult.value.views?.views;
95
+ if (docviews) {
96
+ for (const view of docviews) {
97
+ try {
98
+ const v = this.parseElementView(view);
99
+ ElementViewOps.writeId(view, v.id);
100
+ views.push(v);
101
+ } catch (e) {
102
+ logWarnError(e);
99
103
  }
100
- // const prevHash = doc.c4hash ?? ''
101
- // doc.c4hash = c4hash(doc)
102
- // return prevHash !== doc.c4hash
104
+ }
103
105
  }
104
- parseElement(astNode) {
105
- const id = this.resolveFqn(astNode);
106
- invariant(astNode.kind.ref, 'Element kind is not resolved: ' + astNode.name);
107
- const kind = astNode.kind.ref.name;
108
- const tags = this.convertTags(astNode.body);
109
- const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
110
- const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
111
- const astPath = this.getAstNodePath(astNode);
112
- let [title, description, technology] = astNode.props;
113
- const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
114
- title = title ?? bodyProps.find(p => p.key === 'title')?.value;
115
- description = description ?? bodyProps.find(p => p.key === 'description')?.value;
116
- technology = technology ?? bodyProps.find(p => p.key === 'technology')?.value;
117
- const links = astNode.body?.props.filter(ast.isLinkProperty).map(p => p.value);
118
- return {
119
- id,
120
- kind,
121
- astPath,
122
- title: title ? stripIndent(title).trim() : astNode.name,
123
- ...(tags && { tags }),
124
- ...(links && isNonEmptyArray(links) && { links }),
125
- ...(technology && { technology }),
126
- ...(description && { description: stripIndent(description).trim() }),
127
- ...styleProps
128
- };
106
+ }
107
+ parseElement(astNode) {
108
+ const id = this.resolveFqn(astNode);
109
+ invariant(astNode.kind.ref, "Element kind is not resolved: " + astNode.name);
110
+ const kind = astNode.kind.ref.name;
111
+ const tags = this.convertTags(astNode.body);
112
+ const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
113
+ const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
114
+ const astPath = this.getAstNodePath(astNode);
115
+ let [title, description, technology] = astNode.props;
116
+ const bodyProps = astNode.body?.props.filter(
117
+ (p) => ast.isElementStringProperty(p)
118
+ ) ?? [];
119
+ title = title ?? bodyProps.find((p) => p.key === "title")?.value;
120
+ description = description ?? bodyProps.find((p) => p.key === "description")?.value;
121
+ technology = technology ?? bodyProps.find((p) => p.key === "technology")?.value;
122
+ const links = astNode.body?.props.filter(ast.isLinkProperty).map((p) => p.value);
123
+ return {
124
+ id,
125
+ kind,
126
+ astPath,
127
+ title: title ? stripIndent(title).trim() : astNode.name,
128
+ ...tags && { tags },
129
+ ...links && isNonEmptyArray(links) && { links },
130
+ ...technology && { technology },
131
+ ...description && { description: stripIndent(description).trim() },
132
+ ...styleProps
133
+ };
134
+ }
135
+ parseRelation(astNode) {
136
+ const coupling = resolveRelationPoints(astNode);
137
+ const target = this.resolveFqn(coupling.target);
138
+ const source = this.resolveFqn(coupling.source);
139
+ const kind = astNode.kind?.ref?.name;
140
+ const hashdata = {
141
+ astPath: this.getAstNodePath(astNode),
142
+ source,
143
+ target
144
+ };
145
+ const id = objectHash(hashdata);
146
+ const title = astNode.title ?? astNode.body?.props.find((p) => p.key === "title")?.value ?? "";
147
+ return {
148
+ id,
149
+ ...hashdata,
150
+ ...kind && { kind },
151
+ title
152
+ };
153
+ }
154
+ parseElementExpression(astNode) {
155
+ if (ast.isWildcardExpression(astNode)) {
156
+ return {
157
+ wildcard: true
158
+ };
129
159
  }
130
- parseRelation(astNode) {
131
- const coupling = resolveRelationPoints(astNode);
132
- const target = this.resolveFqn(coupling.target);
133
- const source = this.resolveFqn(coupling.source);
134
- const hashdata = {
135
- astPath: this.getAstNodePath(astNode),
136
- source,
137
- target
138
- };
139
- const id = objectHash(hashdata);
140
- const title = astNode.title ?? astNode.body?.props.find(p => p.key === 'title')?.value ?? '';
141
- return {
142
- id,
143
- ...hashdata,
144
- title
145
- };
160
+ if (ast.isElementKindExpression(astNode)) {
161
+ invariant(
162
+ astNode.kind.ref,
163
+ "ElementKindExpression kind is not resolved: " + astNode.$cstNode?.text
164
+ );
165
+ return {
166
+ elementKind: astNode.kind.ref.name,
167
+ isEqual: astNode.isEqual
168
+ };
146
169
  }
147
- parseElementExpression(astNode) {
148
- if (ast.isWildcardExpression(astNode)) {
149
- return {
150
- wildcard: true
151
- };
152
- }
153
- if (ast.isElementKindExpression(astNode)) {
154
- invariant(astNode.kind.ref, 'ElementKindExpression kind is not resolved: ' + astNode.$cstNode?.text);
155
- return {
156
- elementKind: astNode.kind.ref.name,
157
- isEqual: astNode.isEqual
158
- };
159
- }
160
- if (ast.isElementTagExpression(astNode)) {
161
- invariant(astNode.tag.ref, 'ElementTagExpression tag is not resolved: ' + astNode.$cstNode?.text);
162
- return {
163
- elementTag: astNode.tag.ref.name,
164
- isEqual: astNode.isEqual
165
- };
166
- }
167
- if (ast.isElementRefExpression(astNode)) {
168
- const element = elementRef(astNode.id);
169
- invariant(element, 'Element not found ' + astNode.id.$cstNode?.text);
170
- return {
171
- element: this.resolveFqn(element),
172
- isDescedants: astNode.isDescedants
173
- };
174
- }
175
- nonexhaustive(astNode);
170
+ if (ast.isElementTagExpression(astNode)) {
171
+ invariant(
172
+ astNode.tag.ref,
173
+ "ElementTagExpression tag is not resolved: " + astNode.$cstNode?.text
174
+ );
175
+ return {
176
+ elementTag: astNode.tag.ref.name,
177
+ isEqual: astNode.isEqual
178
+ };
176
179
  }
177
- parseExpression(astNode) {
178
- if (ast.isElementExpression(astNode)) {
179
- return this.parseElementExpression(astNode);
180
- }
181
- if (ast.isIncomingExpression(astNode)) {
182
- return {
183
- incoming: this.parseElementExpression(astNode.target)
184
- };
185
- }
186
- if (ast.isOutgoingExpression(astNode)) {
187
- return {
188
- outgoing: this.parseElementExpression(astNode.source)
189
- };
190
- }
191
- if (ast.isInOutExpression(astNode)) {
192
- return {
193
- inout: this.parseElementExpression(astNode.inout.target)
194
- };
195
- }
196
- if (ast.isRelationExpression(astNode)) {
197
- return {
198
- source: this.parseElementExpression(astNode.source),
199
- target: this.parseElementExpression(astNode.target)
200
- };
201
- }
202
- nonexhaustive(astNode);
180
+ if (ast.isElementRefExpression(astNode)) {
181
+ const element = elementRef(astNode.id);
182
+ invariant(element, "Element not found " + astNode.id.$cstNode?.text);
183
+ return {
184
+ element: this.resolveFqn(element),
185
+ isDescedants: astNode.isDescedants
186
+ };
203
187
  }
204
- parseViewRule(astRule) {
205
- if (ast.isViewRuleExpression(astRule)) {
206
- const exprs = astRule.expressions.map(n => this.parseExpression(n));
207
- return {
208
- isInclude: astRule.isInclude,
209
- exprs
210
- };
211
- }
212
- if (ast.isViewRuleStyle(astRule)) {
213
- const styleProps = toElementStyle(astRule.props);
214
- return {
215
- targets: astRule.targets.map(n => this.parseElementExpression(n)),
216
- style: {
217
- ...styleProps
218
- }
219
- };
220
- }
221
- if (ast.isViewRuleAutoLayout(astRule)) {
222
- return {
223
- autoLayout: toAutoLayout(astRule.direction)
224
- };
225
- }
226
- nonexhaustive(astRule);
188
+ nonexhaustive(astNode);
189
+ }
190
+ parseExpression(astNode) {
191
+ if (ast.isElementExpression(astNode)) {
192
+ return this.parseElementExpression(astNode);
227
193
  }
228
- parseElementView(astNode) {
229
- const body = astNode.body;
230
- invariant(body, 'ElementView body is not defined');
231
- const astPath = this.getAstNodePath(astNode);
232
- let id = astNode.name;
233
- if (!id) {
234
- const doc = getDocument(astNode).uri.toString();
235
- id = objectHash({
236
- doc,
237
- astPath
238
- });
239
- }
240
- const title = body.props.find(p => p.key === 'title')?.value;
241
- const description = body.props.find(p => p.key === 'description')?.value;
242
- const tags = this.convertTags(body);
243
- const links = body.props.filter(ast.isLinkProperty).map(p => p.value);
244
- const basic = {
245
- id: id,
246
- astPath,
247
- ...(title && { title }),
248
- ...(description && { description }),
249
- ...(tags && { tags }),
250
- ...(isNonEmptyArray(links) && { links }),
251
- rules: body.rules.flatMap(n => {
252
- try {
253
- return this.parseViewRule(n);
254
- }
255
- catch (e) {
256
- logWarnError(e);
257
- return [];
258
- }
259
- })
260
- };
261
- if ('viewOf' in astNode) {
262
- const viewOfEl = elementRef(astNode.viewOf);
263
- const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
264
- invariant(viewOf, ' viewOf is not resolved: ' + astNode.$cstNode?.text);
265
- return {
266
- ...basic,
267
- viewOf
268
- };
269
- }
270
- if ('extends' in astNode) {
271
- const extendsView = astNode.extends.view.ref;
272
- invariant(extendsView?.name, 'view extends is not resolved: ' + astNode.$cstNode?.text);
273
- return {
274
- ...basic,
275
- extends: extendsView.name
276
- };
277
- }
278
- return basic;
194
+ if (ast.isIncomingExpression(astNode)) {
195
+ return {
196
+ incoming: this.parseElementExpression(astNode.target)
197
+ };
279
198
  }
280
- resolveFqn(node) {
281
- if (ast.isExtendElement(node)) {
282
- return fqnElementRef(node.element);
283
- }
284
- const fqn = this.fqnIndex.getFqn(node);
285
- invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
286
- return fqn;
199
+ if (ast.isOutgoingExpression(astNode)) {
200
+ return {
201
+ outgoing: this.parseElementExpression(astNode.source)
202
+ };
203
+ }
204
+ if (ast.isInOutExpression(astNode)) {
205
+ return {
206
+ inout: this.parseElementExpression(astNode.inout.target)
207
+ };
287
208
  }
288
- getAstNodePath(node) {
289
- return this.services.workspace.AstNodeLocator.getAstNodePath(node);
209
+ if (ast.isRelationExpression(astNode)) {
210
+ return {
211
+ source: this.parseElementExpression(astNode.source),
212
+ target: this.parseElementExpression(astNode.target)
213
+ };
290
214
  }
291
- convertTags(withTags) {
292
- if (!withTags) {
293
- return null;
215
+ nonexhaustive(astNode);
216
+ }
217
+ parseViewRule(astRule) {
218
+ if (ast.isViewRuleExpression(astRule)) {
219
+ const exprs = astRule.expressions.map((n) => this.parseExpression(n));
220
+ return astRule.isInclude ? { include: exprs } : { exclude: exprs };
221
+ }
222
+ if (ast.isViewRuleStyle(astRule)) {
223
+ const styleProps = toElementStyle(astRule.props);
224
+ return {
225
+ targets: astRule.targets.map((n) => this.parseElementExpression(n)),
226
+ style: {
227
+ ...styleProps
294
228
  }
295
- const tags = withTags.tags?.value.flatMap(({ ref }) => (ref ? ref.name : []));
296
- return tags && isNonEmptyArray(tags) ? tags : null;
229
+ };
230
+ }
231
+ if (ast.isViewRuleAutoLayout(astRule)) {
232
+ return {
233
+ autoLayout: toAutoLayout(astRule.direction)
234
+ };
235
+ }
236
+ nonexhaustive(astRule);
237
+ }
238
+ parseElementView(astNode) {
239
+ const body = astNode.body;
240
+ invariant(body, "ElementView body is not defined");
241
+ const astPath = this.getAstNodePath(astNode);
242
+ let id = astNode.name;
243
+ if (!id) {
244
+ const doc = getDocument(astNode).uri.toString();
245
+ id = objectHash({
246
+ doc,
247
+ astPath
248
+ });
297
249
  }
298
- notifyListeners() {
299
- for (const listener of this.listeners) {
300
- try {
301
- listener();
302
- }
303
- catch (e) {
304
- logError(e);
305
- }
250
+ const title = body.props.find((p) => p.key === "title")?.value;
251
+ const description = body.props.find((p) => p.key === "description")?.value;
252
+ const tags = this.convertTags(body);
253
+ const links = body.props.filter(ast.isLinkProperty).map((p) => p.value);
254
+ const basic = {
255
+ id,
256
+ astPath,
257
+ ...title && { title },
258
+ ...description && { description },
259
+ ...tags && { tags },
260
+ ...isNonEmptyArray(links) && { links },
261
+ rules: body.rules.flatMap((n) => {
262
+ try {
263
+ return this.parseViewRule(n);
264
+ } catch (e) {
265
+ logWarnError(e);
266
+ return [];
306
267
  }
268
+ })
269
+ };
270
+ if ("viewOf" in astNode) {
271
+ const viewOfEl = elementRef(astNode.viewOf);
272
+ const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
273
+ invariant(viewOf, " viewOf is not resolved: " + astNode.$cstNode?.text);
274
+ return {
275
+ ...basic,
276
+ viewOf
277
+ };
278
+ }
279
+ if ("extends" in astNode) {
280
+ const extendsView = astNode.extends.view.ref;
281
+ invariant(extendsView?.name, "view extends is not resolved: " + astNode.$cstNode?.text);
282
+ return {
283
+ ...basic,
284
+ extends: extendsView.name
285
+ };
286
+ }
287
+ return basic;
288
+ }
289
+ resolveFqn(node) {
290
+ if (ast.isExtendElement(node)) {
291
+ return fqnElementRef(node.element);
292
+ }
293
+ const fqn = this.fqnIndex.getFqn(node);
294
+ invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
295
+ return fqn;
296
+ }
297
+ getAstNodePath(node) {
298
+ return this.services.workspace.AstNodeLocator.getAstNodePath(node);
299
+ }
300
+ convertTags(withTags) {
301
+ if (!withTags) {
302
+ return null;
307
303
  }
304
+ const tags = withTags.tags?.value.flatMap(({ ref }) => ref ? ref.name : []);
305
+ return tags && isNonEmptyArray(tags) ? tags : null;
306
+ }
308
307
  }
309
- //# sourceMappingURL=model-parser.js.map
package/dist/module.d.ts CHANGED
@@ -1,10 +1,13 @@
1
- import type { DefaultSharedModuleContext, LangiumServices, LangiumSharedServices, Module, PartialLangiumServices } from 'langium';
1
+ import { WorkspaceCache, type DefaultSharedModuleContext, type LangiumServices, type LangiumSharedServices, type Module, type PartialLangiumServices } from 'langium';
2
2
  import { LikeC4DocumentSymbolProvider } from './lsp';
3
3
  import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
4
+ import { Rpc } from './Rpc';
4
5
  /**
5
6
  * Declaration of custom services - add your own service classes here.
6
7
  */
7
8
  export interface LikeC4AddedServices {
9
+ WorkspaceCache: WorkspaceCache<string, any>;
10
+ Rpc: Rpc;
8
11
  likec4: {
9
12
  FqnIndex: FqnIndex;
10
13
  ModelParser: LikeC4ModelParser;