@likec4/language-server 0.6.1 → 0.6.2

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 (101) hide show
  1. package/package.json +5 -5
  2. package/contrib/likec4.monarch.ts +0 -31
  3. package/contrib/likec4.tmLanguage.json +0 -73
  4. package/dist/__test__/parser-smoke/01-Specification.d.ts +0 -3
  5. package/dist/__test__/parser-smoke/01-Specification.js +0 -42
  6. package/dist/__test__/parser-smoke/02-Model.d.ts +0 -9
  7. package/dist/__test__/parser-smoke/02-Model.js +0 -110
  8. package/dist/__test__/parser-smoke/03-ModelRelation.d.ts +0 -6
  9. package/dist/__test__/parser-smoke/03-ModelRelation.js +0 -81
  10. package/dist/__test__/parser-smoke/04-Scope.d.ts +0 -2
  11. package/dist/__test__/parser-smoke/04-Scope.js +0 -38
  12. package/dist/__test__/parser-smoke/05-StrictElementRef.d.ts +0 -3
  13. package/dist/__test__/parser-smoke/05-StrictElementRef.js +0 -46
  14. package/dist/__test__/parser-smoke/06-ElementRef.d.ts +0 -2
  15. package/dist/__test__/parser-smoke/06-ElementRef.js +0 -59
  16. package/dist/__test__/parser-smoke/07-Views.d.ts +0 -10
  17. package/dist/__test__/parser-smoke/07-Views.js +0 -146
  18. package/dist/__test__/parser-smoke/08-Structurizr.d.ts +0 -1
  19. package/dist/__test__/parser-smoke/08-Structurizr.js +0 -22
  20. package/dist/__test__/parser-smoke/index.d.ts +0 -8
  21. package/dist/__test__/parser-smoke/index.js +0 -8
  22. package/dist/__test__/parser-smoke-extendsElement.spec.d.ts +0 -1
  23. package/dist/__test__/parser-smoke-extendsElement.spec.js +0 -36
  24. package/dist/__test__/parser-smoke.spec.d.ts +0 -1
  25. package/dist/__test__/parser-smoke.spec.js +0 -28
  26. package/dist/ast.d.ts +0 -73
  27. package/dist/ast.js +0 -133
  28. package/dist/builtin.d.ts +0 -4
  29. package/dist/builtin.js +0 -8
  30. package/dist/elementRef.d.ts +0 -6
  31. package/dist/elementRef.js +0 -39
  32. package/dist/generated/ast.d.ts +0 -359
  33. package/dist/generated/ast.js +0 -376
  34. package/dist/generated/grammar.d.ts +0 -6
  35. package/dist/generated/grammar.js +0 -2542
  36. package/dist/generated/module.d.ts +0 -9
  37. package/dist/generated/module.js +0 -26
  38. package/dist/index.d.ts +0 -3
  39. package/dist/index.js +0 -14
  40. package/dist/logger.d.ts +0 -8
  41. package/dist/logger.js +0 -20
  42. package/dist/lsp/DocumentSymbolProvider.d.ts +0 -21
  43. package/dist/lsp/DocumentSymbolProvider.js +0 -149
  44. package/dist/lsp/HoverProvider.d.ts +0 -8
  45. package/dist/lsp/HoverProvider.js +0 -54
  46. package/dist/lsp/SemanticTokenProvider.d.ts +0 -6
  47. package/dist/lsp/SemanticTokenProvider.js +0 -221
  48. package/dist/lsp/index.d.ts +0 -3
  49. package/dist/lsp/index.js +0 -3
  50. package/dist/model/fqn-index.d.ts +0 -17
  51. package/dist/model/fqn-index.js +0 -138
  52. package/dist/model/index.d.ts +0 -3
  53. package/dist/model/index.js +0 -3
  54. package/dist/model/model-builder.d.ts +0 -26
  55. package/dist/model/model-builder.js +0 -332
  56. package/dist/model/model-builder.spec.d.ts +0 -1
  57. package/dist/model/model-builder.spec.js +0 -141
  58. package/dist/model/model-locator.d.ts +0 -16
  59. package/dist/model/model-locator.js +0 -108
  60. package/dist/module.d.ts +0 -18
  61. package/dist/module.js +0 -61
  62. package/dist/protocol.d.ts +0 -39
  63. package/dist/protocol.js +0 -19
  64. package/dist/references/index.d.ts +0 -2
  65. package/dist/references/index.js +0 -2
  66. package/dist/references/scope-computation.d.ts +0 -10
  67. package/dist/references/scope-computation.js +0 -76
  68. package/dist/references/scope-provider.d.ts +0 -15
  69. package/dist/references/scope-provider.js +0 -110
  70. package/dist/registerProtocolHandlers.d.ts +0 -2
  71. package/dist/registerProtocolHandlers.js +0 -64
  72. package/dist/shared/CodeLensProvider.d.ts +0 -8
  73. package/dist/shared/CodeLensProvider.js +0 -35
  74. package/dist/shared/WorkspaceManager.d.ts +0 -13
  75. package/dist/shared/WorkspaceManager.js +0 -19
  76. package/dist/shared/index.d.ts +0 -2
  77. package/dist/shared/index.js +0 -2
  78. package/dist/test/index.d.ts +0 -1
  79. package/dist/test/index.js +0 -1
  80. package/dist/test/testServices.d.ts +0 -15
  81. package/dist/test/testServices.js +0 -57
  82. package/dist/utils.d.ts +0 -2
  83. package/dist/utils.js +0 -7
  84. package/dist/validation/element.d.ts +0 -5
  85. package/dist/validation/element.js +0 -20
  86. package/dist/validation/element.spec.d.ts +0 -1
  87. package/dist/validation/element.spec.js +0 -65
  88. package/dist/validation/index.d.ts +0 -2
  89. package/dist/validation/index.js +0 -22
  90. package/dist/validation/relation.d.ts +0 -4
  91. package/dist/validation/relation.js +0 -53
  92. package/dist/validation/relation.spec.d.ts +0 -1
  93. package/dist/validation/relation.spec.js +0 -93
  94. package/dist/validation/specification.d.ts +0 -5
  95. package/dist/validation/specification.js +0 -33
  96. package/dist/validation/specification.spec.d.ts +0 -1
  97. package/dist/validation/specification.spec.js +0 -31
  98. package/dist/validation/view.d.ts +0 -4
  99. package/dist/validation/view.js +0 -20
  100. package/dist/validation/view.spec.d.ts +0 -1
  101. package/dist/validation/view.spec.js +0 -20
@@ -1,138 +0,0 @@
1
- import { DocumentState, DONE_RESULT, MultiMap, StreamImpl } from 'langium';
2
- import { ast, ElementOps } from '../ast';
3
- import { logger } from '../logger';
4
- import { parentFqn } from '@likec4/core/utils';
5
- import { Fqn } from '@likec4/core/types';
6
- import { strictElementRefFqn } from '../elementRef';
7
- export class FqnIndex {
8
- services;
9
- // #fqnMap = new WeakMap<ast.Element, Fqn>()
10
- #index = new MultiMap();
11
- descriptions;
12
- constructor(services) {
13
- this.services = services;
14
- this.descriptions = services.workspace.AstNodeDescriptionProvider;
15
- services.shared.workspace.DocumentBuilder.onUpdate((_changed, removed) => {
16
- for (const uri of [...removed]) {
17
- this.cleanIndexedElements(uri);
18
- }
19
- });
20
- services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.ComputedScopes, (docs, _cancelToken) => {
21
- for (const doc of docs) {
22
- this.cleanIndexedElements(doc.uri);
23
- this.doIndexElements(doc);
24
- }
25
- });
26
- }
27
- get(el) {
28
- let fqn = ElementOps.readId(el) ?? null;
29
- // if ()
30
- // let fqn = this.#fqnMap.get(el) ?? null
31
- if (fqn && !this.#index.has(fqn)) {
32
- const path = this.services.workspace.AstNodeLocator.getAstNodePath(el);
33
- logger.error(`Clean cached FQN ${fqn} at ${path}`);
34
- // this.#fqnMap.delete(el)
35
- fqn = null;
36
- }
37
- return fqn;
38
- }
39
- byFqn(fqn) {
40
- return this.#index.get(fqn);
41
- }
42
- directChildrenOf(parent) {
43
- return this.#index
44
- .entriesGroupedByKey()
45
- .flatMap(([fqn, descrs]) => (descrs.length === 1 && parentFqn(fqn) === parent ? descrs : []));
46
- }
47
- uniqueDescedants(parent) {
48
- return new StreamImpl(() => {
49
- const prefix = `${parent}.`;
50
- const children = [];
51
- const childrenNames = new Set();
52
- const descedants = [];
53
- this.#index.entries().forEach(([fqn, desc]) => {
54
- if (fqn.startsWith(prefix)) {
55
- if (parentFqn(fqn) === parent) {
56
- childrenNames.add(desc.name);
57
- children.push([fqn, desc]);
58
- }
59
- else {
60
- descedants.push([fqn, desc]);
61
- }
62
- }
63
- });
64
- if (children.length + descedants.length === 0) {
65
- return null;
66
- }
67
- const nested = new MultiMap(children.map(([_fqn, desc]) => [desc.name, desc]));
68
- for (const [_, indexed] of descedants) {
69
- if (!childrenNames.has(indexed.name)) {
70
- nested.add(indexed.name, indexed);
71
- }
72
- }
73
- return nested
74
- .entriesGroupedByKey()
75
- .flatMap(([_name, descrs]) => (descrs.length === 1 ? descrs : []))
76
- .iterator();
77
- }, iterator => {
78
- if (iterator) {
79
- return iterator.next();
80
- }
81
- return DONE_RESULT;
82
- });
83
- }
84
- doIndexElements(doc) {
85
- const visitElement = (element, parent = null) => {
86
- try {
87
- const name = element.name;
88
- const fqn = Fqn(name, parent);
89
- this.#index.add(fqn, this.descriptions.createDescription(element, name, doc));
90
- ElementOps.writeId(element, fqn);
91
- // this.#fqnMap.set(element, fqn)
92
- if (element.body) {
93
- for (const nested of element.body.elements) {
94
- if (ast.isElement(nested)) {
95
- visitElement(nested, fqn);
96
- }
97
- }
98
- }
99
- }
100
- catch (e) {
101
- logger.warn(e);
102
- }
103
- };
104
- const visitExtendElement = (extendElement) => {
105
- try {
106
- const fqn = strictElementRefFqn(extendElement.element);
107
- for (const nested of extendElement.body.elements) {
108
- if (ast.isElement(nested)) {
109
- visitElement(nested, fqn);
110
- }
111
- }
112
- }
113
- catch (e) {
114
- logger.warn(e);
115
- }
116
- };
117
- const elements = doc.parseResult.value.model?.elements ?? [];
118
- for (const modelElement of elements) {
119
- if (ast.isExtendElement(modelElement)) {
120
- visitExtendElement(modelElement);
121
- continue;
122
- }
123
- if (ast.isElement(modelElement)) {
124
- visitElement(modelElement);
125
- continue;
126
- }
127
- }
128
- }
129
- cleanIndexedElements(docUri) {
130
- const docUriAsString = docUri.toString();
131
- const toDelete = this.#index
132
- .entries()
133
- .filter(([, indexed]) => indexed.documentUri.toString() === docUriAsString);
134
- for (const [fqn, indexed] of toDelete.toArray()) {
135
- this.#index.delete(fqn, indexed);
136
- }
137
- }
138
- }
@@ -1,3 +0,0 @@
1
- export * from './fqn-index';
2
- export * from './model-builder';
3
- export * from './model-locator';
@@ -1,3 +0,0 @@
1
- export * from './fqn-index';
2
- export * from './model-builder';
3
- export * from './model-locator';
@@ -1,26 +0,0 @@
1
- import type * as c4 from '@likec4/core/types';
2
- import { ast, type LikeC4LangiumDocument } from '../ast';
3
- import type { LikeC4Services } from '../module';
4
- export declare class LikeC4ModelBuilder {
5
- private services;
6
- private fqnIndex;
7
- private langiumDocuments;
8
- constructor(services: LikeC4Services);
9
- private get connection();
10
- private documents;
11
- buildModel(): c4.LikeC4Model | undefined;
12
- /**
13
- * @returns if the document was changed
14
- */
15
- protected parseDocument(doc: LikeC4LangiumDocument): boolean;
16
- private parseElement;
17
- private parseRelation;
18
- private parseElementExpression;
19
- private parseExpression;
20
- private parseViewRule;
21
- private parseElementView;
22
- protected resolveFqn(node: ast.Element | ast.ExtendElement): c4.Fqn;
23
- private getAstNodePath;
24
- private convertTags;
25
- private notifyClient;
26
- }
@@ -1,332 +0,0 @@
1
- import { computeViews } from '@likec4/core/compute-view';
2
- import { DefaultElementShape, DefaultThemeColor } from '@likec4/core/types';
3
- import { compareByFqnHierarchically, parentFqn } from '@likec4/core/utils';
4
- import { A, O, flow, pipe } from '@mobily/ts-belt';
5
- import { DocumentState, getDocument, interruptAndCheck } from 'langium';
6
- import objectHash from 'object-hash';
7
- import { clone, isNil, mergeDeepRight, omit, reduce } from 'rambdax';
8
- import invariant from 'tiny-invariant';
9
- import { toAutoLayout } from '../ast';
10
- import { ElementViewOps, ast, c4hash, cleanParsedModel, isLikeC4LangiumDocument, isParsedLikeC4LangiumDocument, resolveRelationPoints, streamModel, toElementStyle } from '../ast';
11
- import { elementRef, strictElementRefFqn } from '../elementRef';
12
- import { logger } from '../logger';
13
- import { Rpc } from '@likec4/language-protocol';
14
- import { failExpectedNever } from '../utils';
15
- export class LikeC4ModelBuilder {
16
- services;
17
- fqnIndex;
18
- langiumDocuments;
19
- constructor(services) {
20
- this.services = services;
21
- this.fqnIndex = services.likec4.FqnIndex;
22
- this.langiumDocuments = services.shared.workspace.LangiumDocuments;
23
- services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, async (docs, cancelToken) => {
24
- let countOfChangedDocs = 0;
25
- try {
26
- for (const doc of docs) {
27
- await interruptAndCheck(cancelToken);
28
- try {
29
- if (isLikeC4LangiumDocument(doc) && this.parseDocument(doc)) {
30
- countOfChangedDocs++;
31
- }
32
- }
33
- catch (e) {
34
- logger.warn(`Error parsing document ${doc.uri.toString()}`);
35
- }
36
- }
37
- }
38
- finally {
39
- if (countOfChangedDocs > 0 && !cancelToken.isCancellationRequested) {
40
- await this.notifyClient();
41
- }
42
- }
43
- });
44
- }
45
- get connection() {
46
- return this.services.shared.lsp.Connection;
47
- }
48
- documents() {
49
- return this.langiumDocuments.all.toArray().filter(isParsedLikeC4LangiumDocument);
50
- }
51
- buildModel() {
52
- const docs = this.documents();
53
- if (docs.length === 0) {
54
- return;
55
- }
56
- try {
57
- const c4Specification = reduce((acc, doc) => mergeDeepRight(acc, doc.c4Specification), {
58
- kinds: {}
59
- }, docs);
60
- // const c4Specification = docs.reduce<ParsedAstSpecification>((acc, doc) => {
61
- // return mergeDeepRight(acc, doc.c4Specification)
62
- // }, {
63
- // kinds: {}
64
- // })
65
- const toModelElement = (el) => {
66
- const kind = c4Specification.kinds[el.kind];
67
- if (kind) {
68
- return {
69
- ...(kind.shape !== DefaultElementShape ? { shape: kind.shape } : {}),
70
- ...(kind.color !== DefaultThemeColor ? { color: kind.color } : {}),
71
- ...omit(['astPath'], el)
72
- };
73
- }
74
- return null;
75
- };
76
- const toModelRelation = (rel) => {
77
- return omit(['astPath'], rel);
78
- };
79
- const elements = pipe(docs.flatMap(d => d.c4Elements), A.filterMap(flow(toModelElement, O.fromNullable)), A.sort(compareByFqnHierarchically), A.reduce({}, (acc, el) => {
80
- const parent = parentFqn(el.id);
81
- if (!parent || parent in acc) {
82
- invariant(!(el.id in acc), 'Duplicate element id: ' + el.id);
83
- acc[el.id] = el;
84
- }
85
- return acc;
86
- }));
87
- const relations = pipe(docs.flatMap(d => d.c4Relations), A.filterMap(flow(toModelRelation, O.fromPredicate(({ source, target }) => source in elements && target in elements))), A.reduce({}, (acc, el) => {
88
- invariant(!(el.id in acc), 'Duplicate relation id: ' + el.id);
89
- acc[el.id] = el;
90
- return acc;
91
- }));
92
- const toModelView = (view) => {
93
- let title = view.title;
94
- if (!title && view.viewOf) {
95
- title = elements[view.viewOf]?.title;
96
- }
97
- return {
98
- ...omit(['astPath', 'rules', 'tite'], view),
99
- ...(!!title ? { title } : {}),
100
- rules: clone(view.rules)
101
- };
102
- };
103
- const views = pipe(docs.flatMap(d => d.c4Views), A.filterMap(flow(toModelView, O.fromPredicate(v => isNil(v.viewOf) || v.viewOf in elements))), A.reduce({}, (acc, v) => {
104
- invariant(!(v.id in acc), 'Duplicate view id: ' + v.id);
105
- acc[v.id] = v;
106
- return acc;
107
- }));
108
- return computeViews({
109
- elements,
110
- relations,
111
- views
112
- });
113
- }
114
- catch (e) {
115
- logger.error(e);
116
- return;
117
- }
118
- }
119
- /**
120
- * @returns if the document was changed
121
- */
122
- parseDocument(doc) {
123
- const { elements, relations, views, specification } = cleanParsedModel(doc);
124
- const spec = doc.parseResult.value.specification;
125
- if (spec) {
126
- for (const { kind, style } of spec.elementKinds) {
127
- try {
128
- const styleProps = toElementStyle(style?.props);
129
- specification.kinds[kind.name] = {
130
- color: styleProps.color ?? DefaultThemeColor,
131
- shape: styleProps.shape ?? DefaultElementShape
132
- };
133
- }
134
- catch (e) {
135
- logger.warn(e);
136
- }
137
- }
138
- }
139
- for (const el of streamModel(doc)) {
140
- if (ast.isElement(el)) {
141
- try {
142
- elements.push(this.parseElement(el));
143
- }
144
- catch (e) {
145
- logger.warn(e);
146
- }
147
- continue;
148
- }
149
- if (ast.isRelation(el)) {
150
- try {
151
- relations.push(this.parseRelation(el));
152
- }
153
- catch (e) {
154
- logger.warn(e);
155
- }
156
- continue;
157
- }
158
- failExpectedNever(el);
159
- }
160
- const docviews = doc.parseResult.value.views?.views;
161
- if (docviews) {
162
- for (const view of docviews) {
163
- try {
164
- const v = this.parseElementView(view);
165
- ElementViewOps.writeId(view, v.id);
166
- views.push(v);
167
- }
168
- catch (e) {
169
- logger.warn(e);
170
- }
171
- }
172
- }
173
- const prevHash = doc.c4hash ?? '';
174
- doc.c4hash = c4hash(doc);
175
- return prevHash !== doc.c4hash;
176
- }
177
- parseElement(astNode) {
178
- const id = this.resolveFqn(astNode);
179
- invariant(astNode.kind.ref, 'Element kind is not resolved: ' + astNode.name);
180
- const kind = astNode.kind.ref.name;
181
- const tags = (astNode.body && this.convertTags(astNode.body)) ?? [];
182
- const styleProps = astNode.body?.props.find(ast.isElementStyleProperty)?.props;
183
- const { color, shape } = toElementStyle(styleProps);
184
- const astPath = this.getAstNodePath(astNode);
185
- let [title, description, technology] = astNode.props;
186
- const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
187
- title = title ?? bodyProps.find(p => p.key === 'title')?.value;
188
- description = description ?? bodyProps.find(p => p.key === 'description')?.value;
189
- technology = technology ?? bodyProps.find(p => p.key === 'technology')?.value;
190
- return {
191
- id,
192
- kind,
193
- astPath,
194
- title: title ?? astNode.name,
195
- ...(technology && { technology }),
196
- ...(description && { description }),
197
- ...(tags.length > 0 ? { tags } : {}),
198
- ...(shape && shape !== DefaultElementShape ? { shape } : {}),
199
- ...(color && color !== DefaultThemeColor ? { color } : {})
200
- };
201
- }
202
- parseRelation(astNode) {
203
- const coupling = resolveRelationPoints(astNode);
204
- const target = this.resolveFqn(coupling.target);
205
- const source = this.resolveFqn(coupling.source);
206
- const hashdata = {
207
- astPath: this.getAstNodePath(astNode),
208
- source,
209
- target
210
- };
211
- const id = objectHash(hashdata);
212
- const title = astNode.title ?? astNode.definition?.props.find(p => p.key === 'title')?.value ?? '';
213
- return {
214
- id,
215
- ...hashdata,
216
- title
217
- };
218
- }
219
- parseElementExpression(astNode) {
220
- if (ast.isWildcardExpression(astNode)) {
221
- return {
222
- wildcard: true
223
- };
224
- }
225
- if (ast.isElementRefExpression(astNode)) {
226
- const element = elementRef(astNode.id);
227
- invariant(element, 'Element not found ' + astNode.id.$cstNode?.text);
228
- return {
229
- element: this.resolveFqn(element),
230
- isDescedants: astNode.isDescedants
231
- };
232
- }
233
- failExpectedNever(astNode);
234
- }
235
- parseExpression(astNode) {
236
- if (ast.isElementExpression(astNode)) {
237
- return this.parseElementExpression(astNode);
238
- }
239
- if (ast.isIncomingExpression(astNode)) {
240
- return {
241
- incoming: this.parseElementExpression(astNode.target)
242
- };
243
- }
244
- if (ast.isOutgoingExpression(astNode)) {
245
- return {
246
- outgoing: this.parseElementExpression(astNode.source)
247
- };
248
- }
249
- if (ast.isInOutExpression(astNode)) {
250
- return {
251
- inout: this.parseElementExpression(astNode.inout.target)
252
- };
253
- }
254
- if (ast.isRelationExpression(astNode)) {
255
- return {
256
- source: this.parseElementExpression(astNode.source),
257
- target: this.parseElementExpression(astNode.target)
258
- };
259
- }
260
- failExpectedNever(astNode);
261
- }
262
- parseViewRule(astRule) {
263
- if (ast.isViewRuleExpression(astRule)) {
264
- const exprs = astRule.expressions.map(n => this.parseExpression(n));
265
- return {
266
- isInclude: astRule.isInclude,
267
- exprs
268
- };
269
- }
270
- if (ast.isViewRuleStyle(astRule)) {
271
- const styleProps = toElementStyle(astRule.props);
272
- return {
273
- targets: astRule.targets.map(n => this.parseElementExpression(n)),
274
- style: {
275
- ...styleProps
276
- }
277
- };
278
- }
279
- if (ast.isViewRuleAutoLayout(astRule)) {
280
- return {
281
- autoLayout: toAutoLayout(astRule.direction)
282
- };
283
- }
284
- failExpectedNever(astRule);
285
- }
286
- parseElementView(astNode) {
287
- const viewOfEl = astNode.viewOf && elementRef(astNode.viewOf);
288
- const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
289
- const astPath = this.getAstNodePath(astNode);
290
- let id = astNode.name;
291
- if (!id) {
292
- const doc = getDocument(astNode).uri.toString();
293
- id = objectHash({
294
- doc,
295
- astPath,
296
- viewOf: viewOf ?? null
297
- });
298
- }
299
- const title = astNode.properties.find(p => p.key === 'title')?.value;
300
- const description = astNode.properties.find(p => p.key === 'description')?.value;
301
- return {
302
- id,
303
- astPath,
304
- ...(viewOf && { viewOf }),
305
- ...(title && { title }),
306
- ...(description && { description }),
307
- rules: astNode.rules.map(n => this.parseViewRule(n))
308
- };
309
- }
310
- resolveFqn(node) {
311
- if (ast.isExtendElement(node)) {
312
- return strictElementRefFqn(node.element);
313
- }
314
- const fqn = this.fqnIndex.get(node);
315
- invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
316
- return fqn;
317
- }
318
- getAstNodePath(node) {
319
- return this.services.workspace.AstNodeLocator.getAstNodePath(node);
320
- }
321
- convertTags(el) {
322
- return el.tags?.value.map(tagRef => tagRef.ref?.name) ?? [];
323
- }
324
- async notifyClient() {
325
- const connection = this.connection;
326
- if (!connection) {
327
- return;
328
- }
329
- logger.debug('Send onDidChangeModel');
330
- await connection.sendNotification(Rpc.onDidChangeModel);
331
- }
332
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,141 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createTestServices } from '../test';
3
- import { keys } from 'rambdax';
4
- describe('LikeC4ModelBuilder', () => {
5
- it('builds model', async () => {
6
- const { validate, buildModel } = createTestServices();
7
- const { diagnostics } = await validate(`
8
- specification {
9
- element component
10
- element user {
11
- style {
12
- shape: person
13
- }
14
- }
15
- tag deprecated
16
- }
17
- model {
18
- user client {
19
- -> frontend
20
- }
21
- component system {
22
- backend = component 'Backend' {
23
- technology 'NodeJS'
24
-
25
- style {
26
- color secondary
27
- }
28
- }
29
- component frontend {
30
- #deprecated
31
- description 'Frontend description'
32
-
33
- style {
34
- color: muted
35
- shape: browser
36
- }
37
-
38
- -> backend 'requests'
39
- }
40
- }
41
- }
42
- `);
43
- expect(diagnostics).toHaveLength(0);
44
- const model = await buildModel();
45
- expect(model).toBeDefined();
46
- expect(model.elements).toMatchObject({
47
- client: {
48
- kind: 'user',
49
- shape: 'person'
50
- },
51
- 'system.backend': {
52
- color: 'secondary',
53
- title: 'Backend',
54
- technology: 'NodeJS'
55
- },
56
- 'system.frontend': {
57
- color: 'muted',
58
- shape: 'browser',
59
- description: 'Frontend description'
60
- }
61
- });
62
- expect(model.elements['client']).not.toHaveProperty('color');
63
- expect(model.elements['system']).not.toHaveProperty('color');
64
- expect(model.elements['system']).not.toHaveProperty('shape');
65
- expect(model.elements['system.backend']).toHaveProperty('color', 'secondary');
66
- expect(model.elements['system.backend']).not.toHaveProperty('description');
67
- expect(model).toMatchSnapshot();
68
- });
69
- it('builds model with extend', async () => {
70
- const { parse, validateAll, buildModel } = createTestServices();
71
- await parse(`
72
- specification {
73
- element component
74
- element user
75
- tag deprecated
76
- }
77
- model {
78
- user client
79
- component system {
80
- backend = component
81
- component frontend
82
- }
83
- }
84
- `);
85
- await parse(`
86
- model {
87
- extend system.backend {
88
- component api
89
- }
90
- system.frontend -> api 'requests'
91
- client -> system.frontend {
92
- title 'opens'
93
- }
94
- }
95
- views {
96
- view index {
97
- title 'Index'
98
- include *
99
- }
100
-
101
- view v1 of api {
102
- include *
103
- autoLayout LeftRight
104
- }
105
-
106
- view of system.frontend {
107
- include *
108
- }
109
- }
110
- `);
111
- const { errors } = await validateAll();
112
- expect(errors).toEqual([]);
113
- const model = await buildModel();
114
- expect(model).toBeDefined();
115
- expect(model.elements).toMatchObject({
116
- client: {
117
- kind: 'user'
118
- },
119
- 'system.backend.api': {
120
- kind: 'component'
121
- }
122
- });
123
- expect(keys(model.relations)).toHaveLength(2);
124
- expect(keys(model.views)).toHaveLength(3);
125
- expect(model.views).toMatchObject({
126
- index: {
127
- id: 'index',
128
- title: 'Index',
129
- autoLayout: 'TB'
130
- },
131
- v1: {
132
- id: 'v1',
133
- viewOf: 'system.backend.api',
134
- title: 'api',
135
- autoLayout: 'LR'
136
- }
137
- });
138
- expect(model.views['index']).not.toHaveProperty('viewOf');
139
- expect(model).toMatchSnapshot();
140
- });
141
- });
@@ -1,16 +0,0 @@
1
- import type * as c4 from '@likec4/core/types';
2
- import type { Location } from 'vscode-languageserver-protocol';
3
- import type { ParsedAstElement } from '../ast';
4
- import { ast } from '../ast';
5
- import type { LikeC4Services } from '../module';
6
- export declare class LikeC4ModelLocator {
7
- private services;
8
- private fqnIndex;
9
- private langiumDocuments;
10
- constructor(services: LikeC4Services);
11
- private documents;
12
- getParsedElement(astNode: ast.Element): ParsedAstElement | null;
13
- locateElement(fqn: c4.Fqn, property?: string): Location | null;
14
- locateRelation(relationId: c4.RelationID): Location | null;
15
- locateView(viewId: c4.ViewID): Location | null;
16
- }