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