@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.
- package/contrib/likec4.monarch.ts +5 -5
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/Rpc.d.ts +7 -0
- package/dist/Rpc.js +130 -0
- package/dist/ast.d.ts +20 -0
- package/dist/ast.js +169 -141
- package/dist/elementRef.js +31 -44
- package/dist/generated/ast.d.ts +48 -7
- package/dist/generated/ast.js +344 -315
- package/dist/generated/grammar.js +2 -3177
- package/dist/generated/module.js +13 -18
- package/dist/index.js +2 -3
- package/dist/logger.js +39 -42
- package/dist/lsp/CodeLensProvider.js +28 -32
- package/dist/lsp/DocumentLinkProvider.js +26 -33
- package/dist/lsp/DocumentSymbolProvider.js +165 -167
- package/dist/lsp/HoverProvider.js +35 -48
- package/dist/lsp/SemanticTokenProvider.js +160 -201
- package/dist/lsp/index.js +5 -6
- package/dist/model/fqn-computation.js +39 -40
- package/dist/model/fqn-index.js +117 -141
- package/dist/model/index.js +5 -6
- package/dist/model/model-builder.d.ts +10 -5
- package/dist/model/model-builder.js +247 -176
- package/dist/model/model-locator.d.ts +1 -1
- package/dist/model/model-locator.js +102 -100
- package/dist/model/model-parser.d.ts +2 -6
- package/dist/model/model-parser.js +284 -286
- package/dist/module.d.ts +4 -1
- package/dist/module.js +69 -60
- package/dist/protocol.d.ts +16 -19
- package/dist/protocol.js +14 -22
- package/dist/references/index.js +2 -3
- package/dist/references/scope-computation.js +72 -69
- package/dist/references/scope-provider.js +126 -116
- package/dist/shared/WorkspaceManager.d.ts +2 -3
- package/dist/shared/WorkspaceManager.js +13 -16
- package/dist/shared/index.js +1 -2
- package/dist/test/index.js +1 -2
- package/dist/test/testServices.js +73 -61
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +4 -3
- package/dist/validation/element.d.ts +1 -0
- package/dist/validation/element.js +31 -38
- package/dist/validation/index.js +37 -46
- package/dist/validation/relation.js +46 -46
- package/dist/validation/specification.d.ts +1 -0
- package/dist/validation/specification.js +33 -30
- package/dist/validation/view.js +16 -22
- package/dist/view-utils/assignNavigateTo.d.ts +3 -0
- package/dist/view-utils/assignNavigateTo.js +20 -0
- package/dist/view-utils/index.d.ts +4 -0
- package/dist/view-utils/index.js +3 -0
- package/dist/view-utils/resolve-extended-views.d.ts +7 -0
- package/dist/view-utils/resolve-extended-views.js +41 -0
- package/dist/view-utils/resolve-relative-paths.d.ts +3 -0
- package/dist/view-utils/resolve-relative-paths.js +76 -0
- package/package.json +33 -18
- package/dist/registerProtocolHandlers.d.ts +0 -3
- package/dist/registerProtocolHandlers.js +0 -112
|
@@ -1,196 +1,267 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
64
|
+
logWarnError(`No parent found for ${el.id}`);
|
|
65
|
+
return acc;
|
|
40
66
|
}
|
|
41
67
|
if (el.id in acc) {
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
135
|
-
return (this.cachedModel.last = buildModel(docs));
|
|
136
|
-
}
|
|
137
|
-
catch (e) {
|
|
138
|
-
logError(e);
|
|
139
|
-
return null;
|
|
147
|
+
]
|
|
140
148
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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,111 +1,113 @@
|
|
|
1
|
-
import { InvalidModelError } from
|
|
2
|
-
import { findNodeForProperty, getDocument } from
|
|
3
|
-
import { ast, isParsedLikeC4LangiumDocument } from
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|