@likec4/language-server 0.33.1 → 0.35.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 (51) hide show
  1. package/contrib/likec4.monarch.ts +17 -3
  2. package/dist/ast.d.ts +24 -11
  3. package/dist/ast.js +12 -9
  4. package/dist/elementRef.d.ts +1 -1
  5. package/dist/elementRef.js +2 -3
  6. package/dist/generated/ast.d.ts +29 -10
  7. package/dist/generated/ast.js +20 -1
  8. package/dist/generated/grammar.d.ts +1 -1
  9. package/dist/generated/grammar.js +11 -11
  10. package/dist/generated/module.d.ts +7 -3
  11. package/dist/generated/module.js +3 -3
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +1 -0
  14. package/dist/logger.d.ts +6 -4
  15. package/dist/logger.js +34 -6
  16. package/dist/{shared → lsp}/CodeLensProvider.d.ts +3 -3
  17. package/dist/{shared → lsp}/DocumentLinkProvider.d.ts +3 -2
  18. package/dist/{shared → lsp}/DocumentLinkProvider.js +3 -3
  19. package/dist/lsp/DocumentSymbolProvider.js +3 -3
  20. package/dist/lsp/index.d.ts +2 -0
  21. package/dist/lsp/index.js +2 -0
  22. package/dist/model/fqn-computation.js +6 -3
  23. package/dist/model/fqn-index.d.ts +4 -7
  24. package/dist/model/fqn-index.js +51 -21
  25. package/dist/model/index.d.ts +1 -0
  26. package/dist/model/index.js +1 -0
  27. package/dist/model/model-builder.d.ts +1 -18
  28. package/dist/model/model-builder.js +111 -340
  29. package/dist/model/model-locator.js +19 -24
  30. package/dist/model/model-parser.d.ts +27 -0
  31. package/dist/model/model-parser.js +293 -0
  32. package/dist/module.d.ts +2 -1
  33. package/dist/module.js +20 -29
  34. package/dist/protocol.d.ts +8 -16
  35. package/dist/protocol.js +2 -6
  36. package/dist/references/scope-computation.js +11 -5
  37. package/dist/references/scope-provider.js +10 -4
  38. package/dist/registerProtocolHandlers.js +33 -17
  39. package/dist/shared/index.d.ts +0 -2
  40. package/dist/shared/index.js +0 -2
  41. package/dist/test/testServices.d.ts +2 -2
  42. package/dist/test/testServices.js +16 -10
  43. package/dist/utils.d.ts +2 -2
  44. package/dist/utils.js +2 -7
  45. package/dist/validation/element.d.ts +1 -1
  46. package/dist/validation/element.js +23 -3
  47. package/dist/validation/index.js +26 -0
  48. package/dist/validation/relation.d.ts +1 -1
  49. package/dist/validation/relation.js +32 -26
  50. package/package.json +10 -8
  51. /package/dist/{shared → lsp}/CodeLensProvider.js +0 -0
@@ -1,71 +1,20 @@
1
- import { ModelIndex, assignNavigateTo, compareByFqnHierarchically, computeView, invariant, isNonEmptyArray, nonexhaustive, parentFqn } from '@likec4/core';
2
- import { DocumentState, getDocument } from 'langium';
3
- import objectHash from 'object-hash';
1
+ import { ModelIndex, assignNavigateTo, compareByFqnHierarchically, computeView, parentFqn } from '@likec4/core';
4
2
  import { clone } from 'rambdax';
5
3
  import * as R from 'remeda';
6
- import stripIndent from 'strip-indent';
7
- import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, isValidLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle, toElementStyleExcludeDefaults } from '../ast';
8
- import { elementRef, strictElementRefFqn } from '../elementRef';
9
- import { logger } from '../logger';
4
+ import { isValidLikeC4LangiumDocument } from '../ast';
5
+ import { logError, logWarnError, logger } from '../logger';
10
6
  import { Rpc } from '../protocol';
11
- import { failExpectedNever } from '../utils';
12
- export class LikeC4ModelBuilder {
13
- services;
14
- fqnIndex;
15
- langiumDocuments;
16
- cachedModel = {};
17
- constructor(services) {
18
- this.services = services;
19
- this.fqnIndex = services.likec4.FqnIndex;
20
- this.langiumDocuments = services.shared.workspace.LangiumDocuments;
21
- services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, (docs, cancelToken) => {
22
- let countOfChangedDocs = 0;
23
- for (const doc of docs) {
24
- if (!isLikeC4LangiumDocument(doc)) {
25
- continue;
26
- }
27
- countOfChangedDocs++;
28
- try {
29
- this.parseDocument(doc);
30
- }
31
- catch (e) {
32
- logger.error(`Error parsing document ${doc.uri.toString()}`);
33
- logger.error(e);
34
- }
35
- }
36
- if (countOfChangedDocs > 0) {
37
- this.cleanCache();
38
- this.notifyClient(cancelToken);
39
- }
40
- });
41
- }
42
- cleanCache() {
43
- delete this.cachedModel.last;
44
- }
45
- documents() {
46
- return this.langiumDocuments.all.filter(isValidLikeC4LangiumDocument).toArray();
47
- }
48
- buildModel() {
49
- if ('last' in this.cachedModel) {
50
- logger.debug('returning cached model');
51
- return this.cachedModel.last;
52
- }
53
- return (this.cachedModel.last = this._buildModel());
54
- }
55
- _buildModel() {
56
- logger.debug('_buildModel');
57
- const docs = this.documents();
58
- if (docs.length === 0) {
59
- logger.debug('No documents to build model from');
60
- return null;
61
- }
62
- // TODO:
63
- try {
64
- const c4Specification = {
65
- kinds: {}
66
- };
67
- R.forEach(R.map(docs, R.prop('c4Specification')), spec => Object.assign(c4Specification.kinds, spec.kinds));
68
- const toModelElement = ({ astPath, tags, links, ...parsed }) => {
7
+ import { printDocs } from '../utils';
8
+ function buildModel(docs) {
9
+ const c4Specification = {
10
+ kinds: {}
11
+ };
12
+ R.forEach(R.map(docs, R.prop('c4Specification')), spec => Object.assign(c4Specification.kinds, spec.kinds));
13
+ const toModelElement = (doc) => {
14
+ const base = new URL(doc.uri.toString());
15
+ const resolveLinks = (links) => links.map((l) => new URL(l, base).toString());
16
+ return ({ astPath, tags, links, ...parsed }) => {
17
+ try {
69
18
  const kind = c4Specification.kinds[parsed.kind];
70
19
  if (kind) {
71
20
  return {
@@ -73,310 +22,132 @@ export class LikeC4ModelBuilder {
73
22
  description: null,
74
23
  technology: null,
75
24
  tags: tags ?? null,
76
- links: links ?? null,
25
+ links: links ? resolveLinks(links) : null,
77
26
  ...parsed
78
27
  };
79
28
  }
80
- return null;
81
- };
82
- const elements = R.pipe(R.flatMap(docs, d => d.c4Elements), R.map(toModelElement), R.compact, R.sort(compareByFqnHierarchically), R.reduce((acc, el) => {
83
- const parent = parentFqn(el.id);
84
- if (parent && R.isNil(acc[parent])) {
85
- logger.warn(`No parent found for ${el.id}`);
86
- return acc;
87
- }
88
- if (el.id in acc) {
89
- // should not happen, as validated
90
- logger.warn(`Duplicate element id: ${el.id}`);
91
- return acc;
92
- }
93
- acc[el.id] = el;
94
- return acc;
95
- }, {}));
96
- const toModelRelation = ({ astPath, source, target, ...model }) => {
97
- if (source in elements && target in elements) {
98
- return {
99
- source,
100
- target,
101
- ...model
102
- };
103
- }
104
- return null;
105
- };
106
- const relations = R.pipe(R.flatMap(docs, d => d.c4Relations), R.map(toModelRelation), R.compact, R.mapToObj(r => [r.id, r]));
107
- const modelIndex = ModelIndex.from({ elements, relations });
108
- const toModelView = (view) => {
109
- // eslint-disable-next-line prefer-const
110
- let { astPath, rules, title, description, tags, links, ...model } = view;
111
- if (!title && view.viewOf) {
112
- title = elements[view.viewOf]?.title;
113
- }
114
- if (!title && view.id === 'index') {
115
- title = 'Landscape view';
116
- }
117
- return computeView({
118
- ...model,
119
- title: title ?? null,
120
- description: description ?? null,
121
- tags: tags ?? null,
122
- links: links ?? null,
123
- rules: clone(rules)
124
- }, modelIndex);
125
- };
126
- const views = R.pipe(R.flatMap(docs, d => d.c4Views), R.map(toModelView), R.compact);
127
- assignNavigateTo(views);
128
- return {
129
- elements,
130
- relations,
131
- views: R.mapToObj(views, v => [v.id, v])
132
- };
133
- }
134
- catch (e) {
135
- logger.error(e);
136
- return null;
137
- }
138
- }
139
- /**
140
- * @returns if the document was changed
141
- */
142
- parseDocument(doc) {
143
- const { elements, relations, views, specification } = cleanParsedModel(doc);
144
- const specs = doc.parseResult.value.specification?.specs.filter(ast.isSpecificationElementKind);
145
- if (specs) {
146
- for (const { kind, style } of specs) {
147
- try {
148
- specification.kinds[kind.name] = toElementStyleExcludeDefaults(style?.props);
149
- }
150
- catch (e) {
151
- logger.warn(e);
152
- }
153
- }
154
- }
155
- for (const el of streamModel(doc)) {
156
- if (ast.isElement(el)) {
157
- try {
158
- elements.push(this.parseElement(el));
159
- }
160
- catch (e) {
161
- logger.warn(e);
162
- }
163
- continue;
164
- }
165
- if (ast.isRelation(el)) {
166
- try {
167
- relations.push(this.parseRelation(el));
168
- }
169
- catch (e) {
170
- logger.warn(e);
171
- }
172
- continue;
173
29
  }
174
- nonexhaustive(el);
175
- }
176
- const docviews = doc.parseResult.value.views?.views;
177
- if (docviews) {
178
- for (const view of docviews) {
179
- try {
180
- const v = this.parseElementView(view);
181
- ElementViewOps.writeId(view, v.id);
182
- views.push(v);
183
- }
184
- catch (e) {
185
- logger.warn(e);
186
- }
30
+ catch (e) {
31
+ logWarnError(e);
187
32
  }
188
- }
189
- // const prevHash = doc.c4hash ?? ''
190
- // doc.c4hash = c4hash(doc)
191
- // return prevHash !== doc.c4hash
192
- }
193
- parseElement(astNode) {
194
- const id = this.resolveFqn(astNode);
195
- invariant(astNode.kind.ref, 'Element kind is not resolved: ' + astNode.name);
196
- const kind = astNode.kind.ref.name;
197
- const tags = this.convertTags(astNode.body);
198
- const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
199
- const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
200
- const astPath = this.getAstNodePath(astNode);
201
- let [title, description, technology] = astNode.props;
202
- const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
203
- title = title ?? bodyProps.find(p => p.key === 'title')?.value;
204
- description = description ?? bodyProps.find(p => p.key === 'description')?.value;
205
- technology = technology ?? bodyProps.find(p => p.key === 'technology')?.value;
206
- const links = astNode.body?.props.filter(ast.isLinkProperty).map(p => p.value);
207
- return {
208
- id,
209
- kind,
210
- astPath,
211
- title: title ? stripIndent(title).trim() : astNode.name,
212
- ...(tags && { tags }),
213
- ...(links && isNonEmptyArray(links) && { links }),
214
- ...(technology && { technology }),
215
- ...(description && { description: stripIndent(description).trim() }),
216
- ...styleProps
217
- };
218
- }
219
- parseRelation(astNode) {
220
- const coupling = resolveRelationPoints(astNode);
221
- const target = this.resolveFqn(coupling.target);
222
- const source = this.resolveFqn(coupling.source);
223
- const hashdata = {
224
- astPath: this.getAstNodePath(astNode),
225
- source,
226
- target
227
- };
228
- const id = objectHash(hashdata);
229
- const title = astNode.title ?? astNode.body?.props.find(p => p.key === 'title')?.value ?? '';
230
- return {
231
- id,
232
- ...hashdata,
233
- title
33
+ return null;
234
34
  };
235
- }
236
- parseElementExpression(astNode) {
237
- if (ast.isWildcardExpression(astNode)) {
238
- return {
239
- wildcard: true
240
- };
241
- }
242
- if (ast.isElementKindExpression(astNode)) {
243
- invariant(astNode.kind.ref, 'ElementKindExpression kind is not resolved: ' + astNode.$cstNode?.text);
35
+ };
36
+ const elements = R.pipe(R.flatMap(docs, d => d.c4Elements.map(toModelElement(d))), R.compact, R.sort(compareByFqnHierarchically), R.reduce((acc, el) => {
37
+ const parent = parentFqn(el.id);
38
+ if (parent && R.isNil(acc[parent])) {
39
+ logWarnError(`No parent found for ${el.id}`);
40
+ return acc;
41
+ }
42
+ if (el.id in acc) {
43
+ // should not happen, as validated
44
+ logWarnError(`Duplicate element id: ${el.id}`);
45
+ return acc;
46
+ }
47
+ acc[el.id] = el;
48
+ return acc;
49
+ }, {}));
50
+ const toModelRelation = ({ astPath, source, target, ...model }) => {
51
+ if (source in elements && target in elements) {
244
52
  return {
245
- elementKind: astNode.kind.ref.name,
246
- isEqual: astNode.isEqual
53
+ source,
54
+ target,
55
+ ...model
247
56
  };
248
57
  }
249
- if (ast.isElementTagExpression(astNode)) {
250
- invariant(astNode.tag.ref, 'ElementTagExpression tag is not resolved: ' + astNode.$cstNode?.text);
251
- return {
252
- elementTag: astNode.tag.ref.name,
253
- isEqual: astNode.isEqual
254
- };
58
+ return null;
59
+ };
60
+ const relations = R.pipe(R.flatMap(docs, d => d.c4Relations), R.map(toModelRelation), R.compact, R.mapToObj(r => [r.id, r]));
61
+ const modelIndex = ModelIndex.from({ elements, relations });
62
+ const toModelView = (view) => {
63
+ try {
64
+ // eslint-disable-next-line prefer-const
65
+ let { astPath, rules, title, description, tags, links, ...model } = view;
66
+ if (!title && view.viewOf) {
67
+ title = elements[view.viewOf]?.title;
68
+ }
69
+ if (!title && view.id === 'index') {
70
+ title = 'Landscape view';
71
+ }
72
+ const computeResult = computeView({
73
+ ...model,
74
+ title: title ?? null,
75
+ description: description ?? null,
76
+ tags: tags ?? null,
77
+ links: links ?? null,
78
+ rules: clone(rules)
79
+ }, modelIndex);
80
+ if (!computeResult.isSuccess) {
81
+ logWarnError(computeResult.error);
82
+ return null;
83
+ }
84
+ return computeResult.view;
255
85
  }
256
- if (ast.isElementRefExpression(astNode)) {
257
- const element = elementRef(astNode.id);
258
- invariant(element, 'Element not found ' + astNode.id.$cstNode?.text);
259
- return {
260
- element: this.resolveFqn(element),
261
- isDescedants: astNode.isDescedants
262
- };
86
+ catch (e) {
87
+ logError(e);
88
+ return null;
263
89
  }
264
- failExpectedNever(astNode);
90
+ };
91
+ const views = R.pipe(R.flatMap(docs, d => d.c4Views), R.map(toModelView), R.compact);
92
+ assignNavigateTo(views);
93
+ return {
94
+ elements,
95
+ relations,
96
+ views: R.mapToObj(views, v => [v.id, v])
97
+ };
98
+ }
99
+ export class LikeC4ModelBuilder {
100
+ services;
101
+ langiumDocuments;
102
+ cachedModel = {};
103
+ constructor(services) {
104
+ this.services = services;
105
+ this.langiumDocuments = services.shared.workspace.LangiumDocuments;
106
+ services.likec4.ModelParser.onParsed(() => {
107
+ this.cleanCache();
108
+ this.notifyClient();
109
+ });
265
110
  }
266
- parseExpression(astNode) {
267
- if (ast.isElementExpression(astNode)) {
268
- return this.parseElementExpression(astNode);
269
- }
270
- if (ast.isIncomingExpression(astNode)) {
271
- return {
272
- incoming: this.parseElementExpression(astNode.target)
273
- };
274
- }
275
- if (ast.isOutgoingExpression(astNode)) {
276
- return {
277
- outgoing: this.parseElementExpression(astNode.source)
278
- };
279
- }
280
- if (ast.isInOutExpression(astNode)) {
281
- return {
282
- inout: this.parseElementExpression(astNode.inout.target)
283
- };
284
- }
285
- if (ast.isRelationExpression(astNode)) {
286
- return {
287
- source: this.parseElementExpression(astNode.source),
288
- target: this.parseElementExpression(astNode.target)
289
- };
290
- }
291
- failExpectedNever(astNode);
111
+ cleanCache() {
112
+ delete this.cachedModel.last;
292
113
  }
293
- parseViewRule(astRule) {
294
- if (ast.isViewRuleExpression(astRule)) {
295
- const exprs = astRule.expressions.map(n => this.parseExpression(n));
296
- return {
297
- isInclude: astRule.isInclude,
298
- exprs
299
- };
300
- }
301
- if (ast.isViewRuleStyle(astRule)) {
302
- const styleProps = toElementStyle(astRule.props);
303
- return {
304
- targets: astRule.targets.map(n => this.parseElementExpression(n)),
305
- style: {
306
- ...styleProps
307
- }
308
- };
309
- }
310
- if (ast.isViewRuleAutoLayout(astRule)) {
311
- return {
312
- autoLayout: toAutoLayout(astRule.direction)
313
- };
314
- }
315
- failExpectedNever(astRule);
114
+ documents() {
115
+ return this.langiumDocuments.all.filter(isValidLikeC4LangiumDocument).toArray();
316
116
  }
317
- parseElementView(astNode) {
318
- const viewOfEl = astNode.viewOf && elementRef(astNode.viewOf);
319
- const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
320
- const astPath = this.getAstNodePath(astNode);
321
- let id = astNode.name;
322
- if (!id) {
323
- const doc = getDocument(astNode).uri.toString();
324
- id = objectHash({
325
- doc,
326
- astPath,
327
- viewOf: viewOf ?? null
328
- });
117
+ buildModel() {
118
+ if ('last' in this.cachedModel) {
119
+ logger.debug('[ModelBuilder] returning cached model');
120
+ return this.cachedModel.last;
329
121
  }
330
- const title = astNode.props.find(p => p.key === 'title')?.value;
331
- const description = astNode.props.find(p => p.key === 'description')?.value;
332
- const tags = this.convertTags(astNode);
333
- const links = astNode.props.filter(ast.isLinkProperty).map(p => p.value);
334
- return {
335
- id,
336
- astPath,
337
- ...(viewOf && { viewOf }),
338
- ...(title && { title }),
339
- ...(description && { description }),
340
- ...(tags && { tags }),
341
- ...(links && isNonEmptyArray(links) && { links }),
342
- rules: astNode.rules.map(n => this.parseViewRule(n))
343
- };
344
- }
345
- resolveFqn(node) {
346
- if (ast.isExtendElement(node)) {
347
- return strictElementRefFqn(node.element);
122
+ try {
123
+ const docs = this.documents();
124
+ if (docs.length === 0) {
125
+ logger.debug('[ModelBuilder] No documents to build model from');
126
+ return null;
127
+ }
128
+ logger.debug(`[ModelBuilder] buildModel from ${docs.length} docs:\n${printDocs(docs)}`);
129
+ return (this.cachedModel.last = buildModel(docs));
348
130
  }
349
- const fqn = this.fqnIndex.getFqn(node);
350
- invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
351
- return fqn;
352
- }
353
- getAstNodePath(node) {
354
- return this.services.workspace.AstNodeLocator.getAstNodePath(node);
355
- }
356
- convertTags(withTags) {
357
- if (!withTags) {
131
+ catch (e) {
132
+ logError(e);
358
133
  return null;
359
134
  }
360
- const tags = withTags.tags?.value.flatMap(({ ref }) => (ref ? ref.name : []));
361
- return tags && isNonEmptyArray(tags) ? tags : null;
362
135
  }
363
136
  scheduledCb = null;
364
- notifyClient(cancelToken) {
137
+ notifyClient() {
365
138
  const connection = this.services.shared.lsp.Connection;
366
139
  if (!connection) {
367
140
  return;
368
141
  }
369
142
  if (this.scheduledCb) {
370
- logger.debug('debounce scheduled onDidChangeModel');
143
+ logger.debug('[ModelBuilder] debounce onDidChangeModel');
371
144
  clearTimeout(this.scheduledCb);
372
145
  }
373
146
  this.scheduledCb = setTimeout(() => {
374
- logger.debug('send onDidChangeModel');
375
147
  this.scheduledCb = null;
376
- if (!cancelToken.isCancellationRequested) {
377
- void connection.sendNotification(Rpc.onDidChangeModel, '');
378
- }
379
- }, 300);
148
+ logger.debug('[ModelBuilder] send onDidChangeModel');
149
+ void connection.sendNotification(Rpc.onDidChangeModel, '');
150
+ }, 200);
380
151
  }
381
152
  }
382
153
  //# sourceMappingURL=model-builder.js.map
@@ -1,5 +1,7 @@
1
+ import { InvalidModelError } from '@likec4/core';
1
2
  import { findNodeForProperty, getDocument } from 'langium';
2
- import { ElementOps, ast, isParsedLikeC4LangiumDocument } from '../ast';
3
+ import { ast, isParsedLikeC4LangiumDocument } from '../ast';
4
+ import {} from './fqn-index';
3
5
  export class LikeC4ModelLocator {
4
6
  services;
5
7
  fqnIndex;
@@ -13,7 +15,7 @@ export class LikeC4ModelLocator {
13
15
  return this.langiumDocuments.all.filter(isParsedLikeC4LangiumDocument);
14
16
  }
15
17
  getParsedElement(astNode) {
16
- const fqn = ElementOps.readId(astNode) ?? null;
18
+ const fqn = this.fqnIndex.getFqn(astNode);
17
19
  if (!fqn)
18
20
  return null;
19
21
  const doc = getDocument(astNode);
@@ -23,28 +25,18 @@ export class LikeC4ModelLocator {
23
25
  return doc.c4Elements.find(e => e.id === fqn) ?? null;
24
26
  }
25
27
  locateElement(fqn, property = 'name') {
26
- for (const doc of this.documents()) {
27
- if (!doc.c4fqns?.has(fqn)) {
28
- continue;
29
- }
30
- const element = doc.c4Elements.find(e => e.id === fqn);
31
- if (!element) {
32
- continue;
33
- }
34
- const node = this.services.workspace.AstNodeLocator.getAstNode(doc.parseResult.value, element.astPath);
35
- if (!ast.isElement(node)) {
36
- continue;
37
- }
38
- const propertyNode = findNodeForProperty(node.$cstNode, property) ?? node.$cstNode;
39
- if (!propertyNode) {
40
- return null;
41
- }
42
- return {
43
- uri: doc.uri.toString(),
44
- range: propertyNode.range
45
- };
28
+ const entry = this.fqnIndex.byFqn(fqn).head();
29
+ if (!entry) {
30
+ return null;
46
31
  }
47
- return null;
32
+ const propertyNode = findNodeForProperty(entry.el.$cstNode, property) ?? entry.el.$cstNode;
33
+ if (!propertyNode) {
34
+ return null;
35
+ }
36
+ return {
37
+ uri: entry.doc.uri.toString(),
38
+ range: propertyNode.range
39
+ };
48
40
  }
49
41
  locateRelation(relationId) {
50
42
  for (const doc of this.documents()) {
@@ -68,6 +60,9 @@ export class LikeC4ModelLocator {
68
60
  };
69
61
  }
70
62
  }
63
+ if (node.arr == null) {
64
+ throw new InvalidModelError('Relation.arr is not defined, but should be');
65
+ }
71
66
  const targetNode = findNodeForProperty(node.$cstNode, 'arr');
72
67
  if (!targetNode) {
73
68
  return null;
@@ -75,7 +70,7 @@ export class LikeC4ModelLocator {
75
70
  return {
76
71
  uri: doc.uri.toString(),
77
72
  range: {
78
- start: targetNode.range.end,
73
+ start: targetNode.range.start,
79
74
  end: targetNode.range.end
80
75
  }
81
76
  };
@@ -0,0 +1,27 @@
1
+ import { type c4 } from '@likec4/core';
2
+ import type { LangiumDocument } from 'langium';
3
+ import { Disposable, type CancellationToken } from 'vscode-languageserver-protocol';
4
+ import type { LikeC4LangiumDocument } from '../ast';
5
+ import { ast } from '../ast';
6
+ import type { LikeC4Services } from '../module';
7
+ export type ModelParsedListener = () => void;
8
+ export declare class LikeC4ModelParser {
9
+ private services;
10
+ private fqnIndex;
11
+ protected readonly listeners: ModelParsedListener[];
12
+ 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>;
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 notifyListeners;
26
+ }
27
+ //# sourceMappingURL=model-parser.d.ts.map