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