@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,196 +1,267 @@
1
- import { ModelIndex, compareByFqnHierarchically, parentFqn, resolveRulesExtendedViews, computeView, assignNavigateTo, isStrictElementView } from '@likec4/core';
2
- import * as R from 'remeda';
3
- import { isValidLikeC4LangiumDocument } from '../ast';
4
- import { logError, logWarnError, logger } from '../logger';
5
- import { Rpc } from '../protocol';
6
- import { printDocs } from '../utils';
1
+ import {
2
+ compareByFqnHierarchically,
3
+ invariant,
4
+ isStrictElementView,
5
+ parentFqn
6
+ } from "@likec4/core";
7
+ import {
8
+ DocumentState,
9
+ interruptAndCheck
10
+ } from "langium";
11
+ import * as R from "remeda";
12
+ import { Disposable } from "vscode-languageserver";
13
+ import { isValidLikeC4LangiumDocument } from "../ast.js";
14
+ import { logError, logWarnError, logger } from "../logger.js";
15
+ import { LikeC4WorkspaceManager } from "../shared/index.js";
16
+ import { printDocs, queueMicrotask } from "../utils.js";
17
+ import { assignNavigateTo, resolveRelativePaths, resolveRulesExtendedViews } from "../view-utils/index.js";
18
+ import { LikeC4ModelGraph, computeView } from "@likec4/graph";
19
+ function isRelativeLink(link) {
20
+ return link.startsWith(".") || link.startsWith("/");
21
+ }
7
22
  function buildModel(docs) {
8
- const c4Specification = {
9
- kinds: {}
10
- };
11
- R.forEach(R.map(docs, R.prop('c4Specification')), spec => Object.assign(c4Specification.kinds, spec.kinds));
12
- const toModelElement = (doc) => {
13
- const base = new URL(doc.uri.toString());
14
- const resolveLinks = (links) => links.map((l) => new URL(l, base).toString());
15
- return ({ astPath, tags, links, ...parsed }) => {
16
- try {
17
- const kind = c4Specification.kinds[parsed.kind];
18
- if (kind) {
19
- return {
20
- ...kind,
21
- description: null,
22
- technology: null,
23
- tags: tags ?? null,
24
- links: links ? resolveLinks(links) : null,
25
- ...parsed
26
- };
27
- }
28
- }
29
- catch (e) {
30
- logWarnError(e);
31
- }
32
- return null;
33
- };
23
+ const c4Specification = {
24
+ kinds: {},
25
+ relationships: {}
26
+ };
27
+ R.forEach(R.map(docs, R.prop("c4Specification")), (spec) => {
28
+ Object.assign(c4Specification.kinds, spec.kinds), Object.assign(c4Specification.relationships, spec.relationships);
29
+ });
30
+ const resolveLinks = (doc, links) => {
31
+ const base = new URL(doc.uri.toString());
32
+ return links.map(
33
+ (l) => isRelativeLink(l) ? new URL(l, base).toString() : l
34
+ );
35
+ };
36
+ const toModelElement = (doc) => {
37
+ return ({ astPath, tags, links, ...parsed }) => {
38
+ try {
39
+ const kind = c4Specification.kinds[parsed.kind];
40
+ if (kind) {
41
+ return {
42
+ ...kind,
43
+ description: null,
44
+ technology: null,
45
+ tags: tags ?? null,
46
+ links: links ? resolveLinks(doc, links) : null,
47
+ ...parsed
48
+ };
49
+ }
50
+ } catch (e) {
51
+ logWarnError(e);
52
+ }
53
+ return null;
34
54
  };
35
- const elements = R.pipe(R.flatMap(docs, d => d.c4Elements.map(toModelElement(d))), R.compact, R.sort(compareByFqnHierarchically), R.reduce((acc, el) => {
55
+ };
56
+ const elements = R.pipe(
57
+ R.flatMap(docs, (d) => d.c4Elements.map(toModelElement(d))),
58
+ R.compact,
59
+ R.sort(compareByFqnHierarchically),
60
+ R.reduce(
61
+ (acc, el) => {
36
62
  const parent = parentFqn(el.id);
37
63
  if (parent && R.isNil(acc[parent])) {
38
- logWarnError(`No parent found for ${el.id}`);
39
- return acc;
64
+ logWarnError(`No parent found for ${el.id}`);
65
+ return acc;
40
66
  }
41
67
  if (el.id in acc) {
42
- // should not happen, as validated
43
- logWarnError(`Duplicate element id: ${el.id}`);
44
- return acc;
68
+ logWarnError(`Duplicate element id: ${el.id}`);
69
+ return acc;
45
70
  }
46
71
  acc[el.id] = el;
47
72
  return acc;
48
- }, {}));
49
- const toModelRelation = ({ astPath, source, target, ...model }) => {
50
- if (source in elements && target in elements) {
51
- return {
52
- source,
53
- target,
54
- ...model
55
- };
56
- }
57
- return null;
58
- };
59
- const relations = R.pipe(R.flatMap(docs, d => d.c4Relations), R.map(toModelRelation), R.compact, R.mapToObj(r => [r.id, r]));
60
- const toElementView = (view) => {
61
- // eslint-disable-next-line prefer-const
62
- let { astPath, rules, title, description, tags, links, ...model } = view;
63
- if (!title && 'viewOf' in view) {
64
- title = elements[view.viewOf]?.title;
65
- }
66
- if (!title && view.id === 'index') {
67
- title = 'Landscape view';
68
- }
73
+ },
74
+ {}
75
+ )
76
+ );
77
+ const toModelRelation = ({
78
+ astPath,
79
+ source,
80
+ target,
81
+ ...model
82
+ }) => {
83
+ if (source in elements && target in elements) {
84
+ if (model.kind) {
85
+ const kind = c4Specification.relationships[model.kind];
69
86
  return {
70
- ...model,
71
- title: title ?? null,
72
- description: description ?? null,
73
- tags: tags ?? null,
74
- links: links ?? null,
75
- rules
76
- };
77
- };
78
- const views = R.pipe(R.flatMap(docs, d => d.c4Views), R.map(toElementView), R.compact, R.mapToObj(v => [v.id, v]));
79
- // add index view if not present
80
- if (!('index' in views)) {
81
- views['index'] = {
82
- id: 'index',
83
- title: 'Landscape',
84
- description: null,
85
- tags: null,
86
- links: null,
87
- rules: [
88
- {
89
- isInclude: true,
90
- exprs: [
91
- {
92
- wildcard: true
93
- }
94
- ]
95
- }
96
- ]
87
+ source,
88
+ target,
89
+ ...kind,
90
+ ...model
97
91
  };
92
+ }
93
+ return {
94
+ source,
95
+ target,
96
+ ...model
97
+ };
98
98
  }
99
- return {
100
- elements,
101
- relations,
102
- views: resolveRulesExtendedViews(views)
99
+ return null;
100
+ };
101
+ const relations = R.pipe(
102
+ R.flatMap(docs, (d) => d.c4Relations),
103
+ R.map(toModelRelation),
104
+ R.compact,
105
+ R.mapToObj((r) => [r.id, r])
106
+ );
107
+ const toElementView = (doc) => {
108
+ const docUri = doc.uri.toString();
109
+ return (view) => {
110
+ let { astPath, rules, title, description, tags, links, ...model } = view;
111
+ if (!title && "viewOf" in view) {
112
+ title = elements[view.viewOf]?.title;
113
+ }
114
+ if (!title && view.id === "index") {
115
+ title = "Landscape view";
116
+ }
117
+ return {
118
+ ...model,
119
+ title: title ?? null,
120
+ description: description ?? null,
121
+ tags: tags ?? null,
122
+ links: links ? resolveLinks(doc, links) : null,
123
+ docUri,
124
+ rules
125
+ };
103
126
  };
104
- }
105
- export class LikeC4ModelBuilder {
106
- services;
107
- langiumDocuments;
108
- cachedModel = {};
109
- constructor(services) {
110
- this.services = services;
111
- this.langiumDocuments = services.shared.workspace.LangiumDocuments;
112
- services.likec4.ModelParser.onParsed(() => {
113
- this.cleanCache();
114
- this.notifyClient();
115
- });
116
- }
117
- cleanCache() {
118
- delete this.cachedModel.last;
119
- }
120
- documents() {
121
- return this.langiumDocuments.all.filter(isValidLikeC4LangiumDocument).toArray();
122
- }
123
- buildRawModel() {
124
- if ('last' in this.cachedModel) {
125
- logger.debug('[ModelBuilder] returning cached model');
126
- return this.cachedModel.last;
127
- }
128
- try {
129
- const docs = this.documents();
130
- if (docs.length === 0) {
131
- logger.debug('[ModelBuilder] No documents to build model from');
132
- return null;
127
+ };
128
+ const views = R.pipe(
129
+ R.flatMap(docs, (d) => R.map(d.c4Views, toElementView(d))),
130
+ resolveRelativePaths,
131
+ R.mapToObj((v) => [v.id, v]),
132
+ resolveRulesExtendedViews
133
+ );
134
+ if (!("index" in views)) {
135
+ views["index"] = {
136
+ id: "index",
137
+ title: "Landscape",
138
+ description: null,
139
+ tags: null,
140
+ links: null,
141
+ rules: [
142
+ {
143
+ include: [
144
+ {
145
+ wildcard: true
133
146
  }
134
- logger.debug(`[ModelBuilder] buildModel from ${docs.length} docs:\n${printDocs(docs)}`);
135
- return (this.cachedModel.last = buildModel(docs));
136
- }
137
- catch (e) {
138
- logError(e);
139
- return null;
147
+ ]
140
148
  }
141
- }
142
- buildModel() {
143
- const model = this.buildRawModel();
144
- if (!model) {
145
- return null;
149
+ ]
150
+ };
151
+ }
152
+ return {
153
+ elements,
154
+ relations,
155
+ views
156
+ };
157
+ }
158
+ const RAW_MODEL_CACHE = "LikeC4RawModel";
159
+ const MODEL_CACHE = "LikeC4Model";
160
+ export class LikeC4ModelBuilder {
161
+ constructor(services) {
162
+ this.services = services;
163
+ this.langiumDocuments = services.shared.workspace.LangiumDocuments;
164
+ invariant(services.shared.workspace.WorkspaceManager instanceof LikeC4WorkspaceManager);
165
+ this.workspaceManager = services.shared.workspace.WorkspaceManager;
166
+ const parser = services.likec4.ModelParser;
167
+ services.shared.workspace.DocumentBuilder.onBuildPhase(
168
+ DocumentState.Validated,
169
+ async (docs, cancelToken) => {
170
+ await queueMicrotask(() => parser.parse(docs));
171
+ await interruptAndCheck(cancelToken);
172
+ this.notifyListeners(docs.map((d) => d.uri));
173
+ }
174
+ );
175
+ }
176
+ langiumDocuments;
177
+ workspaceManager;
178
+ listeners = [];
179
+ get workspaceUri() {
180
+ return this.workspaceManager.workspace()?.uri ?? null;
181
+ }
182
+ buildRawModel() {
183
+ const cache = this.services.WorkspaceCache;
184
+ return cache.get(RAW_MODEL_CACHE, () => {
185
+ try {
186
+ const docs = this.documents();
187
+ if (docs.length === 0) {
188
+ logger.debug("[ModelBuilder] No documents to build model from");
189
+ return null;
146
190
  }
147
- const index = ModelIndex.from(model);
148
- const views = R.pipe(R.values(model.views), R.map(view => computeView(view, index).view), R.compact);
149
- assignNavigateTo(views);
150
- return {
151
- elements: model.elements,
152
- relations: model.relations,
153
- views: R.mapToObj(views, v => [v.id, v])
154
- };
191
+ logger.debug(`[ModelBuilder] buildModel from ${docs.length} docs:
192
+ ${printDocs(docs)}`);
193
+ return buildModel(docs);
194
+ } catch (e) {
195
+ logError(e);
196
+ return null;
197
+ }
198
+ });
199
+ }
200
+ buildModel() {
201
+ const cache = this.services.WorkspaceCache;
202
+ return cache.get(MODEL_CACHE, () => {
203
+ const model = this.buildRawModel();
204
+ if (!model) {
205
+ return null;
206
+ }
207
+ const index = new LikeC4ModelGraph(model);
208
+ const views = R.pipe(
209
+ R.values(model.views),
210
+ R.map((view) => computeView(view, index).view),
211
+ R.compact
212
+ );
213
+ assignNavigateTo(views);
214
+ return {
215
+ elements: model.elements,
216
+ relations: model.relations,
217
+ views: R.mapToObj(views, (v) => [v.id, v])
218
+ };
219
+ });
220
+ }
221
+ computeView(viewId) {
222
+ const model = this.buildRawModel();
223
+ const view = model?.views[viewId];
224
+ if (!view) {
225
+ logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
226
+ return null;
155
227
  }
156
- computeView(viewId) {
157
- const model = this.buildRawModel();
158
- const view = model?.views[viewId];
159
- if (!view) {
160
- logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
161
- return null;
162
- }
163
- const result = computeView(view, ModelIndex.from(model));
164
- if (!result.isSuccess) {
165
- logError(result.error);
166
- return null;
167
- }
168
- const allElementViews = R.values(model.views).filter((v) => isStrictElementView(v) && v.id !== viewId);
169
- const computedView = result.view;
170
- computedView.nodes.forEach(node => {
171
- // find first element view that is not the current one
172
- const navigateTo = R.find(allElementViews, v => v.viewOf === node.id);
173
- if (navigateTo) {
174
- node.navigateTo = navigateTo.id;
175
- }
176
- });
177
- return computedView;
228
+ const index = new LikeC4ModelGraph(model);
229
+ const result = computeView(view, index);
230
+ if (!result.isSuccess) {
231
+ logError(result.error);
232
+ return null;
178
233
  }
179
- scheduledCb = null;
180
- notifyClient() {
181
- const connection = this.services.shared.lsp.Connection;
182
- if (!connection) {
183
- return;
184
- }
185
- if (this.scheduledCb) {
186
- logger.debug('[ModelBuilder] debounce onDidChangeModel');
187
- clearTimeout(this.scheduledCb);
188
- }
189
- this.scheduledCb = setTimeout(() => {
190
- this.scheduledCb = null;
191
- logger.debug('[ModelBuilder] send onDidChangeModel');
192
- void connection.sendNotification(Rpc.onDidChangeModel, '');
193
- }, 200);
234
+ const allElementViews = R.values(model.views).filter(
235
+ (v) => isStrictElementView(v) && v.id !== viewId
236
+ );
237
+ const computedView = result.view;
238
+ computedView.nodes.forEach((node) => {
239
+ const navigateTo = R.find(allElementViews, (v) => v.viewOf === node.id);
240
+ if (navigateTo) {
241
+ node.navigateTo = navigateTo.id;
242
+ }
243
+ });
244
+ return computedView;
245
+ }
246
+ onModelParsed(callback) {
247
+ this.listeners.push(callback);
248
+ return Disposable.create(() => {
249
+ const index = this.listeners.indexOf(callback);
250
+ if (index >= 0) {
251
+ this.listeners.splice(index, 1);
252
+ }
253
+ });
254
+ }
255
+ documents() {
256
+ return this.langiumDocuments.all.filter(isValidLikeC4LangiumDocument).toArray();
257
+ }
258
+ notifyListeners(docs) {
259
+ for (const listener of this.listeners) {
260
+ try {
261
+ listener(docs);
262
+ } catch (e) {
263
+ logError(e);
264
+ }
194
265
  }
266
+ }
195
267
  }
196
- //# sourceMappingURL=model-builder.js.map
@@ -1,4 +1,4 @@
1
- import type * as c4 from '@likec4/core/types';
1
+ import type { likec4 as c4 } from '@likec4/core';
2
2
  import type { Location } from 'vscode-languageserver-protocol';
3
3
  import type { ParsedAstElement } from '../ast';
4
4
  import { ast } from '../ast';
@@ -1,111 +1,113 @@
1
- import { InvalidModelError } from '@likec4/core';
2
- import { findNodeForProperty, getDocument } from 'langium';
3
- import { ast, isParsedLikeC4LangiumDocument } from '../ast';
4
- import {} from './fqn-index';
1
+ import { InvalidModelError } from "@likec4/core";
2
+ import { findNodeForProperty, getDocument } from "langium";
3
+ import { ast, isParsedLikeC4LangiumDocument } from "../ast.js";
5
4
  export class LikeC4ModelLocator {
6
- services;
7
- fqnIndex;
8
- langiumDocuments;
9
- constructor(services) {
10
- this.services = services;
11
- this.fqnIndex = services.likec4.FqnIndex;
12
- this.langiumDocuments = services.shared.workspace.LangiumDocuments;
5
+ constructor(services) {
6
+ this.services = services;
7
+ this.fqnIndex = services.likec4.FqnIndex;
8
+ this.langiumDocuments = services.shared.workspace.LangiumDocuments;
9
+ }
10
+ fqnIndex;
11
+ langiumDocuments;
12
+ documents() {
13
+ return this.langiumDocuments.all.filter(isParsedLikeC4LangiumDocument);
14
+ }
15
+ getParsedElement(astNode) {
16
+ const fqn = this.fqnIndex.getFqn(astNode);
17
+ if (!fqn)
18
+ return null;
19
+ const doc = getDocument(astNode);
20
+ if (!isParsedLikeC4LangiumDocument(doc)) {
21
+ return null;
13
22
  }
14
- documents() {
15
- return this.langiumDocuments.all.filter(isParsedLikeC4LangiumDocument);
23
+ return doc.c4Elements.find((e) => e.id === fqn) ?? null;
24
+ }
25
+ locateElement(fqn, property = "name") {
26
+ const entry = this.fqnIndex.byFqn(fqn).head();
27
+ if (!entry) {
28
+ return null;
16
29
  }
17
- getParsedElement(astNode) {
18
- const fqn = this.fqnIndex.getFqn(astNode);
19
- if (!fqn)
20
- return null;
21
- const doc = getDocument(astNode);
22
- if (!isParsedLikeC4LangiumDocument(doc)) {
23
- return null;
24
- }
25
- return doc.c4Elements.find(e => e.id === fqn) ?? null;
26
- }
27
- locateElement(fqn, property = 'name') {
28
- const entry = this.fqnIndex.byFqn(fqn).head();
29
- if (!entry) {
30
- return null;
31
- }
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
- };
30
+ const propertyNode = findNodeForProperty(entry.el.$cstNode, property) ?? entry.el.$cstNode;
31
+ if (!propertyNode) {
32
+ return null;
40
33
  }
41
- locateRelation(relationId) {
42
- for (const doc of this.documents()) {
43
- const relation = doc.c4Relations.find(r => r.id === relationId);
44
- if (!relation) {
45
- continue;
46
- }
47
- const node = this.services.workspace.AstNodeLocator.getAstNode(doc.parseResult.value, relation.astPath);
48
- if (!ast.isRelation(node)) {
49
- continue;
50
- }
51
- if (node.title) {
52
- const targetNode = findNodeForProperty(node.$cstNode, 'title');
53
- if (targetNode) {
54
- return {
55
- uri: doc.uri.toString(),
56
- range: {
57
- start: targetNode.range.start,
58
- end: targetNode.range.start
59
- }
60
- };
61
- }
62
- }
63
- if (node.arr == null) {
64
- throw new InvalidModelError('Relation.arr is not defined, but should be');
65
- }
66
- const targetNode = findNodeForProperty(node.$cstNode, 'arr');
67
- if (!targetNode) {
68
- return null;
34
+ return {
35
+ uri: entry.doc.uri.toString(),
36
+ range: propertyNode.range
37
+ };
38
+ }
39
+ locateRelation(relationId) {
40
+ for (const doc of this.documents()) {
41
+ const relation = doc.c4Relations.find((r) => r.id === relationId);
42
+ if (!relation) {
43
+ continue;
44
+ }
45
+ const node = this.services.workspace.AstNodeLocator.getAstNode(
46
+ doc.parseResult.value,
47
+ relation.astPath
48
+ );
49
+ if (!ast.isRelation(node)) {
50
+ continue;
51
+ }
52
+ if (node.title) {
53
+ const targetNode2 = findNodeForProperty(node.$cstNode, "title");
54
+ if (targetNode2) {
55
+ return {
56
+ uri: doc.uri.toString(),
57
+ range: {
58
+ start: targetNode2.range.start,
59
+ end: targetNode2.range.start
69
60
  }
70
- return {
71
- uri: doc.uri.toString(),
72
- range: {
73
- start: targetNode.range.start,
74
- end: targetNode.range.end
75
- }
76
- };
61
+ };
77
62
  }
63
+ }
64
+ if (node.arr == null) {
65
+ throw new InvalidModelError("Relation.arr is not defined, but should be");
66
+ }
67
+ const targetNode = findNodeForProperty(node.$cstNode, "arr");
68
+ if (!targetNode) {
78
69
  return null;
79
- }
80
- locateView(viewId) {
81
- for (const doc of this.documents()) {
82
- const view = doc.c4Views.find(r => r.id === viewId);
83
- if (!view) {
84
- continue;
85
- }
86
- const node = this.services.workspace.AstNodeLocator.getAstNode(doc.parseResult.value, view.astPath);
87
- if (!ast.isElementView(node)) {
88
- continue;
89
- }
90
- let targetNode = node.$cstNode;
91
- if (node.name) {
92
- targetNode = findNodeForProperty(node.$cstNode, 'name') ?? targetNode;
93
- }
94
- else if ('viewOf' in node) {
95
- targetNode = findNodeForProperty(node.$cstNode, 'viewOf') ?? targetNode;
96
- }
97
- if (!targetNode) {
98
- return null;
99
- }
100
- return {
101
- uri: doc.uri.toString(),
102
- range: {
103
- start: targetNode.range.start,
104
- end: targetNode.range.start
105
- }
106
- };
70
+ }
71
+ return {
72
+ uri: doc.uri.toString(),
73
+ range: {
74
+ start: targetNode.range.start,
75
+ end: targetNode.range.end
107
76
  }
77
+ };
78
+ }
79
+ return null;
80
+ }
81
+ locateView(viewId) {
82
+ for (const doc of this.documents()) {
83
+ const view = doc.c4Views.find((r) => r.id === viewId);
84
+ if (!view) {
85
+ continue;
86
+ }
87
+ const node = this.services.workspace.AstNodeLocator.getAstNode(
88
+ doc.parseResult.value,
89
+ view.astPath
90
+ );
91
+ if (!ast.isElementView(node)) {
92
+ continue;
93
+ }
94
+ let targetNode = node.$cstNode;
95
+ if (node.name) {
96
+ targetNode = findNodeForProperty(node.$cstNode, "name") ?? targetNode;
97
+ } else if ("viewOf" in node) {
98
+ targetNode = findNodeForProperty(node.$cstNode, "viewOf") ?? targetNode;
99
+ }
100
+ if (!targetNode) {
108
101
  return null;
102
+ }
103
+ return {
104
+ uri: doc.uri.toString(),
105
+ range: {
106
+ start: targetNode.range.start,
107
+ end: targetNode.range.start
108
+ }
109
+ };
109
110
  }
111
+ return null;
112
+ }
110
113
  }
111
- //# sourceMappingURL=model-locator.js.map