@likec4/language-server 0.35.0 → 0.37.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 +1 -1
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/ast.d.ts +2 -1
- package/dist/elementRef.d.ts +14 -2
- package/dist/elementRef.js +14 -2
- package/dist/generated/ast.d.ts +28 -10
- package/dist/generated/ast.js +16 -4
- package/dist/generated/grammar.js +251 -154
- package/dist/logger.js +3 -2
- package/dist/lsp/DocumentSymbolProvider.js +2 -2
- package/dist/lsp/SemanticTokenProvider.js +14 -13
- package/dist/model/fqn-computation.js +2 -2
- package/dist/model/fqn-index.js +4 -6
- package/dist/model/model-builder.d.ts +3 -1
- package/dist/model/model-builder.js +56 -33
- package/dist/model/model-locator.js +1 -1
- package/dist/model/model-parser.js +32 -16
- package/dist/protocol.d.ts +10 -2
- package/dist/protocol.js +5 -1
- package/dist/references/scope-computation.d.ts +0 -3
- package/dist/references/scope-computation.js +10 -21
- package/dist/references/scope-provider.js +13 -7
- package/dist/registerProtocolHandlers.js +16 -0
- package/dist/validation/view.js +3 -0
- package/package.json +2 -2
package/dist/logger.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { normalizeError
|
|
2
|
+
import { normalizeError } from '@likec4/core';
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
|
4
4
|
let isSilent = false;
|
|
5
5
|
export const logger = {
|
|
@@ -44,6 +44,7 @@ export function logWarnError(err) {
|
|
|
44
44
|
logger.warn(err);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
const error = normalizeError(err);
|
|
48
|
+
logger.warn(`${error.name}: ${error.message}`);
|
|
48
49
|
}
|
|
49
50
|
//# sourceMappingURL=logger.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { findNodeForProperty } from 'langium';
|
|
2
|
-
import { compact, isEmpty, map, pipe } from 'remeda';
|
|
2
|
+
import { compact, concat, isEmpty, map, pipe } from 'remeda';
|
|
3
3
|
import { SymbolKind } from 'vscode-languageserver-protocol';
|
|
4
4
|
import { ast } from '../ast';
|
|
5
5
|
import { logError } from '../logger';
|
|
@@ -68,7 +68,7 @@ export class LikeC4DocumentSymbolProvider {
|
|
|
68
68
|
const specKeywordNode = findNodeForProperty(cstModel, 'name');
|
|
69
69
|
if (!specKeywordNode)
|
|
70
70
|
return [];
|
|
71
|
-
const specSymbols = pipe(astSpec.
|
|
71
|
+
const specSymbols = pipe(concat(astSpec.elements, astSpec.tags), map(nd => {
|
|
72
72
|
if (ast.isSpecificationElementKind(nd)) {
|
|
73
73
|
return getElementKindSymbol(nd);
|
|
74
74
|
}
|
|
@@ -10,6 +10,14 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
10
10
|
type: SemanticTokenTypes.keyword,
|
|
11
11
|
modifier: [SemanticTokenModifiers.defaultLibrary]
|
|
12
12
|
});
|
|
13
|
+
if ('arr' in node) {
|
|
14
|
+
acceptor({
|
|
15
|
+
node,
|
|
16
|
+
property: 'arr',
|
|
17
|
+
type: SemanticTokenTypes.keyword,
|
|
18
|
+
modifier: [SemanticTokenModifiers.defaultLibrary]
|
|
19
|
+
});
|
|
20
|
+
}
|
|
13
21
|
if (ast.isElementRef(node) || ast.isStrictElementRef(node)) {
|
|
14
22
|
acceptor({
|
|
15
23
|
node,
|
|
@@ -18,21 +26,21 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
18
26
|
});
|
|
19
27
|
return;
|
|
20
28
|
}
|
|
21
|
-
if (ast.
|
|
29
|
+
if (ast.isElementViewRef(node)) {
|
|
22
30
|
acceptor({
|
|
23
31
|
node,
|
|
24
|
-
property: '
|
|
32
|
+
property: 'view',
|
|
25
33
|
type: SemanticTokenTypes.variable
|
|
26
34
|
});
|
|
27
35
|
return;
|
|
28
36
|
}
|
|
29
|
-
if (
|
|
37
|
+
if (ast.isWildcardExpression(node)) {
|
|
30
38
|
acceptor({
|
|
31
39
|
node,
|
|
32
|
-
property: '
|
|
33
|
-
type: SemanticTokenTypes.
|
|
34
|
-
modifier: [SemanticTokenModifiers.defaultLibrary]
|
|
40
|
+
property: 'isWildcard',
|
|
41
|
+
type: SemanticTokenTypes.variable
|
|
35
42
|
});
|
|
43
|
+
return;
|
|
36
44
|
}
|
|
37
45
|
// if (
|
|
38
46
|
// ast.isRelation(node) ||
|
|
@@ -197,13 +205,6 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
|
|
|
197
205
|
modifier: [SemanticTokenModifiers.declaration]
|
|
198
206
|
});
|
|
199
207
|
}
|
|
200
|
-
if (node.viewOf) {
|
|
201
|
-
acceptor({
|
|
202
|
-
node,
|
|
203
|
-
keyword: 'of',
|
|
204
|
-
type: SemanticTokenTypes.keyword
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
210
|
//# sourceMappingURL=SemanticTokenProvider.js.map
|
|
@@ -2,7 +2,7 @@ import { AsFqn, nonexhaustive } from '@likec4/core';
|
|
|
2
2
|
import { MultiMap } from 'langium';
|
|
3
3
|
import { isEmpty, isNil } from 'remeda';
|
|
4
4
|
import { ElementOps, ast } from '../ast';
|
|
5
|
-
import {
|
|
5
|
+
import { fqnElementRef } from '../elementRef';
|
|
6
6
|
export function computeDocumentFqn(document, services) {
|
|
7
7
|
const c4fqns = (document.c4fqns = new MultiMap());
|
|
8
8
|
const { model } = document.parseResult.value;
|
|
@@ -19,7 +19,7 @@ export function computeDocumentFqn(document, services) {
|
|
|
19
19
|
}
|
|
20
20
|
if (ast.isExtendElement(el)) {
|
|
21
21
|
if (!isNil(el.body) && !isEmpty(el.body.elements)) {
|
|
22
|
-
const fqn =
|
|
22
|
+
const fqn = fqnElementRef(el.element);
|
|
23
23
|
el.body.elements.forEach(child => traverseStack.push([child, fqn]));
|
|
24
24
|
}
|
|
25
25
|
continue;
|
package/dist/model/fqn-index.js
CHANGED
|
@@ -51,9 +51,10 @@ export class FqnIndex {
|
|
|
51
51
|
documents() {
|
|
52
52
|
return this.langiumDocuments.all.filter(isFqnIndexedDocument);
|
|
53
53
|
}
|
|
54
|
-
entries() {
|
|
54
|
+
entries(filterByFqn = () => true) {
|
|
55
55
|
return this.documents().flatMap(doc => doc.c4fqns
|
|
56
56
|
.entries()
|
|
57
|
+
.filter(([fqn]) => filterByFqn(fqn))
|
|
57
58
|
.map(([fqn, entry]) => {
|
|
58
59
|
const el = entry.el.deref();
|
|
59
60
|
if (el) {
|
|
@@ -90,8 +91,7 @@ export class FqnIndex {
|
|
|
90
91
|
}
|
|
91
92
|
directChildrenOf(parent) {
|
|
92
93
|
return stream([parent]).flatMap(_parent => {
|
|
93
|
-
const children = this.entries()
|
|
94
|
-
.filter(e => parentFqn(e.fqn) === _parent)
|
|
94
|
+
const children = this.entries(fqn => parentFqn(fqn) === _parent)
|
|
95
95
|
.map((entry) => [entry.name, entry])
|
|
96
96
|
.toArray();
|
|
97
97
|
if (children.length === 0) {
|
|
@@ -112,9 +112,7 @@ export class FqnIndex {
|
|
|
112
112
|
const childrenNames = new Set();
|
|
113
113
|
const descedants = [];
|
|
114
114
|
const nested = new MultiMap();
|
|
115
|
-
this.entries()
|
|
116
|
-
.filter(e => e.fqn.startsWith(prefix))
|
|
117
|
-
.forEach(e => {
|
|
115
|
+
this.entries(f => f.startsWith(prefix)).forEach(e => {
|
|
118
116
|
const name = nameFromFqn(e.fqn);
|
|
119
117
|
const entry = { ...e, name };
|
|
120
118
|
// To keep direct children always
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type c4 } from '@likec4/core';
|
|
1
|
+
import { type ViewID, type c4 } from '@likec4/core';
|
|
2
2
|
import type { LikeC4Services } from '../module';
|
|
3
3
|
export declare class LikeC4ModelBuilder {
|
|
4
4
|
private services;
|
|
@@ -7,7 +7,9 @@ export declare class LikeC4ModelBuilder {
|
|
|
7
7
|
constructor(services: LikeC4Services);
|
|
8
8
|
private cleanCache;
|
|
9
9
|
private documents;
|
|
10
|
+
buildRawModel(): c4.LikeC4RawModel | null;
|
|
10
11
|
buildModel(): c4.LikeC4Model | null;
|
|
12
|
+
computeView(viewId: ViewID): c4.ComputedView | null;
|
|
11
13
|
private scheduledCb;
|
|
12
14
|
private notifyClient;
|
|
13
15
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { ModelIndex,
|
|
2
|
-
import { clone } from 'rambdax';
|
|
1
|
+
import { ModelIndex, compareByFqnHierarchically, parentFqn, resolveRulesExtendedViews, computeView, assignNavigateTo, isStrictElementView } from '@likec4/core';
|
|
3
2
|
import * as R from 'remeda';
|
|
4
3
|
import { isValidLikeC4LangiumDocument } from '../ast';
|
|
5
4
|
import { logError, logWarnError, logger } from '../logger';
|
|
@@ -58,42 +57,29 @@ function buildModel(docs) {
|
|
|
58
57
|
return null;
|
|
59
58
|
};
|
|
60
59
|
const relations = R.pipe(R.flatMap(docs, d => d.c4Relations), R.map(toModelRelation), R.compact, R.mapToObj(r => [r.id, r]));
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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;
|
|
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;
|
|
85
65
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return null;
|
|
66
|
+
if (!title && view.id === 'index') {
|
|
67
|
+
title = 'Landscape view';
|
|
89
68
|
}
|
|
69
|
+
return {
|
|
70
|
+
...model,
|
|
71
|
+
title: title ?? null,
|
|
72
|
+
description: description ?? null,
|
|
73
|
+
tags: tags ?? null,
|
|
74
|
+
links: links ?? null,
|
|
75
|
+
rules
|
|
76
|
+
};
|
|
90
77
|
};
|
|
91
|
-
const views = R.pipe(R.flatMap(docs, d => d.c4Views), R.map(
|
|
92
|
-
assignNavigateTo(views);
|
|
78
|
+
const views = R.pipe(R.flatMap(docs, d => d.c4Views), R.map(toElementView), R.compact, R.mapToObj(v => [v.id, v]));
|
|
93
79
|
return {
|
|
94
80
|
elements,
|
|
95
81
|
relations,
|
|
96
|
-
views:
|
|
82
|
+
views: resolveRulesExtendedViews(views)
|
|
97
83
|
};
|
|
98
84
|
}
|
|
99
85
|
export class LikeC4ModelBuilder {
|
|
@@ -114,7 +100,7 @@ export class LikeC4ModelBuilder {
|
|
|
114
100
|
documents() {
|
|
115
101
|
return this.langiumDocuments.all.filter(isValidLikeC4LangiumDocument).toArray();
|
|
116
102
|
}
|
|
117
|
-
|
|
103
|
+
buildRawModel() {
|
|
118
104
|
if ('last' in this.cachedModel) {
|
|
119
105
|
logger.debug('[ModelBuilder] returning cached model');
|
|
120
106
|
return this.cachedModel.last;
|
|
@@ -133,6 +119,43 @@ export class LikeC4ModelBuilder {
|
|
|
133
119
|
return null;
|
|
134
120
|
}
|
|
135
121
|
}
|
|
122
|
+
buildModel() {
|
|
123
|
+
const model = this.buildRawModel();
|
|
124
|
+
if (!model) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const index = ModelIndex.from(model);
|
|
128
|
+
const views = R.pipe(R.values(model.views), R.map(view => computeView(view, index).view), R.compact);
|
|
129
|
+
assignNavigateTo(views);
|
|
130
|
+
return {
|
|
131
|
+
elements: model.elements,
|
|
132
|
+
relations: model.relations,
|
|
133
|
+
views: R.mapToObj(views, v => [v.id, v])
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
computeView(viewId) {
|
|
137
|
+
const model = this.buildRawModel();
|
|
138
|
+
const view = model?.views[viewId];
|
|
139
|
+
if (!view) {
|
|
140
|
+
logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const result = computeView(view, ModelIndex.from(model));
|
|
144
|
+
if (!result.isSuccess) {
|
|
145
|
+
logError(result.error);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const allElementViews = R.values(model.views).filter((v) => isStrictElementView(v) && v.id !== viewId);
|
|
149
|
+
const computedView = result.view;
|
|
150
|
+
computedView.nodes.forEach(node => {
|
|
151
|
+
// find first element view that is not the current one
|
|
152
|
+
const navigateTo = R.find(allElementViews, v => v.viewOf === node.id);
|
|
153
|
+
if (navigateTo) {
|
|
154
|
+
node.navigateTo = navigateTo.id;
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
return computedView;
|
|
158
|
+
}
|
|
136
159
|
scheduledCb = null;
|
|
137
160
|
notifyClient() {
|
|
138
161
|
const connection = this.services.shared.lsp.Connection;
|
|
@@ -91,7 +91,7 @@ export class LikeC4ModelLocator {
|
|
|
91
91
|
if (node.name) {
|
|
92
92
|
targetNode = findNodeForProperty(node.$cstNode, 'name') ?? targetNode;
|
|
93
93
|
}
|
|
94
|
-
else if (node
|
|
94
|
+
else if ('viewOf' in node) {
|
|
95
95
|
targetNode = findNodeForProperty(node.$cstNode, 'viewOf') ?? targetNode;
|
|
96
96
|
}
|
|
97
97
|
if (!targetNode) {
|
|
@@ -4,7 +4,7 @@ import objectHash from 'object-hash';
|
|
|
4
4
|
import stripIndent from 'strip-indent';
|
|
5
5
|
import { Disposable } from 'vscode-languageserver-protocol';
|
|
6
6
|
import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle, toElementStyleExcludeDefaults } from '../ast';
|
|
7
|
-
import { elementRef,
|
|
7
|
+
import { elementRef, fqnElementRef } from '../elementRef';
|
|
8
8
|
import { logError, logWarnError, logger } from '../logger';
|
|
9
9
|
import { printDocs } from '../utils';
|
|
10
10
|
export class LikeC4ModelParser {
|
|
@@ -46,7 +46,7 @@ export class LikeC4ModelParser {
|
|
|
46
46
|
}
|
|
47
47
|
async parseDocument(doc, cancelToken) {
|
|
48
48
|
const { elements, relations, views, specification } = cleanParsedModel(doc);
|
|
49
|
-
const specs = doc.parseResult.value.specification?.
|
|
49
|
+
const specs = doc.parseResult.value.specification?.elements;
|
|
50
50
|
if (specs) {
|
|
51
51
|
for (const { kind, style } of specs) {
|
|
52
52
|
if (kind.name in specification.kinds) {
|
|
@@ -226,31 +226,29 @@ export class LikeC4ModelParser {
|
|
|
226
226
|
nonexhaustive(astRule);
|
|
227
227
|
}
|
|
228
228
|
parseElementView(astNode) {
|
|
229
|
-
const
|
|
230
|
-
|
|
229
|
+
const body = astNode.body;
|
|
230
|
+
invariant(body, 'ElementView body is not defined');
|
|
231
231
|
const astPath = this.getAstNodePath(astNode);
|
|
232
232
|
let id = astNode.name;
|
|
233
233
|
if (!id) {
|
|
234
234
|
const doc = getDocument(astNode).uri.toString();
|
|
235
235
|
id = objectHash({
|
|
236
236
|
doc,
|
|
237
|
-
astPath
|
|
238
|
-
viewOf: viewOf ?? null
|
|
237
|
+
astPath
|
|
239
238
|
});
|
|
240
239
|
}
|
|
241
|
-
const title =
|
|
242
|
-
const description =
|
|
243
|
-
const tags = this.convertTags(
|
|
244
|
-
const links =
|
|
245
|
-
|
|
246
|
-
id,
|
|
240
|
+
const title = body.props.find(p => p.key === 'title')?.value;
|
|
241
|
+
const description = body.props.find(p => p.key === 'description')?.value;
|
|
242
|
+
const tags = this.convertTags(body);
|
|
243
|
+
const links = body.props.filter(ast.isLinkProperty).map(p => p.value);
|
|
244
|
+
const basic = {
|
|
245
|
+
id: id,
|
|
247
246
|
astPath,
|
|
248
|
-
...(viewOf && { viewOf }),
|
|
249
247
|
...(title && { title }),
|
|
250
248
|
...(description && { description }),
|
|
251
249
|
...(tags && { tags }),
|
|
252
|
-
...(
|
|
253
|
-
rules:
|
|
250
|
+
...(isNonEmptyArray(links) && { links }),
|
|
251
|
+
rules: body.rules.flatMap(n => {
|
|
254
252
|
try {
|
|
255
253
|
return this.parseViewRule(n);
|
|
256
254
|
}
|
|
@@ -260,10 +258,28 @@ export class LikeC4ModelParser {
|
|
|
260
258
|
}
|
|
261
259
|
})
|
|
262
260
|
};
|
|
261
|
+
if ('viewOf' in astNode) {
|
|
262
|
+
const viewOfEl = elementRef(astNode.viewOf);
|
|
263
|
+
const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
|
|
264
|
+
invariant(viewOf, ' viewOf is not resolved: ' + astNode.$cstNode?.text);
|
|
265
|
+
return {
|
|
266
|
+
...basic,
|
|
267
|
+
viewOf
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if ('extends' in astNode) {
|
|
271
|
+
const extendsView = astNode.extends.view.ref;
|
|
272
|
+
invariant(extendsView?.name, 'view extends is not resolved: ' + astNode.$cstNode?.text);
|
|
273
|
+
return {
|
|
274
|
+
...basic,
|
|
275
|
+
extends: extendsView.name
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return basic;
|
|
263
279
|
}
|
|
264
280
|
resolveFqn(node) {
|
|
265
281
|
if (ast.isExtendElement(node)) {
|
|
266
|
-
return
|
|
282
|
+
return fqnElementRef(node.element);
|
|
267
283
|
}
|
|
268
284
|
const fqn = this.fqnIndex.getFqn(node);
|
|
269
285
|
invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Fqn, LikeC4Model, RelationID, ViewID } from '@likec4/core';
|
|
1
|
+
import type { ComputedView, Fqn, LikeC4Model, LikeC4RawModel, RelationID, ViewID } from '@likec4/core';
|
|
2
2
|
import type { DocumentUri, Location } from 'vscode-languageserver-protocol';
|
|
3
|
-
import { NotificationType,
|
|
3
|
+
import { NotificationType, RequestType, RequestType0 } from 'vscode-languageserver-protocol';
|
|
4
4
|
interface BuildDocumentsParams {
|
|
5
5
|
docs: DocumentUri[];
|
|
6
6
|
}
|
|
@@ -18,6 +18,14 @@ export declare const Rpc: {
|
|
|
18
18
|
readonly fetchModel: RequestType0<{
|
|
19
19
|
model: LikeC4Model | null;
|
|
20
20
|
}, void>;
|
|
21
|
+
readonly fetchRawModel: RequestType0<{
|
|
22
|
+
rawmodel: LikeC4RawModel | null;
|
|
23
|
+
}, void>;
|
|
24
|
+
readonly computeView: RequestType<{
|
|
25
|
+
viewId: ViewID;
|
|
26
|
+
}, {
|
|
27
|
+
view: ComputedView | null;
|
|
28
|
+
}, void>;
|
|
21
29
|
readonly rebuild: RequestType0<{
|
|
22
30
|
docs: DocumentUri[];
|
|
23
31
|
}, void>;
|
package/dist/protocol.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { NotificationType,
|
|
1
|
+
import { NotificationType, RequestType, RequestType0 } from 'vscode-languageserver-protocol';
|
|
2
2
|
//#region From server
|
|
3
3
|
const onDidChangeModel = new NotificationType('likec4/onDidChangeModel');
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region To server
|
|
6
|
+
const fetchRawModel = new RequestType0('likec4/fetchRaw');
|
|
6
7
|
const fetchModel = new RequestType0('likec4/fetchModel');
|
|
8
|
+
const computeView = new RequestType('likec4/computeView');
|
|
7
9
|
const rebuild = new RequestType0('likec4/rebuildModel');
|
|
8
10
|
const buildDocuments = new RequestType('likec4/buildDocuments');
|
|
9
11
|
export const locate = new RequestType('likec4/locate');
|
|
@@ -11,6 +13,8 @@ export const locate = new RequestType('likec4/locate');
|
|
|
11
13
|
export const Rpc = {
|
|
12
14
|
onDidChangeModel,
|
|
13
15
|
fetchModel,
|
|
16
|
+
fetchRawModel,
|
|
17
|
+
computeView,
|
|
14
18
|
rebuild,
|
|
15
19
|
buildDocuments,
|
|
16
20
|
locate
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { DefaultScopeComputation, MultiMap, type AstNodeDescription, type PrecomputedScopes } from 'langium';
|
|
2
2
|
import type { CancellationToken } from 'vscode-languageserver';
|
|
3
3
|
import { ast, type LikeC4LangiumDocument } from '../ast';
|
|
4
|
-
import type { LikeC4Services } from '../module';
|
|
5
4
|
type ElementsContainer = ast.Model | ast.ElementBody | ast.ExtendElementBody;
|
|
6
5
|
export declare class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
7
|
-
private services;
|
|
8
|
-
constructor(services: LikeC4Services);
|
|
9
6
|
computeExports(document: LikeC4LangiumDocument, _cancelToken: CancellationToken): Promise<AstNodeDescription[]>;
|
|
10
7
|
computeLocalScopes(document: LikeC4LangiumDocument, _cancelToken: CancellationToken): Promise<PrecomputedScopes>;
|
|
11
8
|
protected processContainer(container: ElementsContainer, scopes: PrecomputedScopes, document: LikeC4LangiumDocument): MultiMap<string, AstNodeDescription>;
|
|
@@ -1,32 +1,21 @@
|
|
|
1
1
|
import { DefaultScopeComputation, MultiMap } from 'langium';
|
|
2
|
-
import { ast } from '../ast';
|
|
3
2
|
import { isEmpty } from 'remeda';
|
|
4
|
-
import {
|
|
3
|
+
import { ast } from '../ast';
|
|
5
4
|
export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
6
|
-
services;
|
|
7
|
-
constructor(services) {
|
|
8
|
-
super(services);
|
|
9
|
-
this.services = services;
|
|
10
|
-
}
|
|
11
5
|
computeExports(document, _cancelToken) {
|
|
12
6
|
const { specification, model, views } = document.parseResult.value;
|
|
13
7
|
const docExports = [];
|
|
14
|
-
if (specification
|
|
15
|
-
for (const spec of specification.
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
|
|
19
|
-
}
|
|
20
|
-
continue;
|
|
8
|
+
if (specification) {
|
|
9
|
+
for (const spec of specification.elements) {
|
|
10
|
+
if (spec.kind && !isEmpty(spec.kind.name)) {
|
|
11
|
+
docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
|
|
21
12
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
continue;
|
|
13
|
+
}
|
|
14
|
+
for (const spec of specification.tags) {
|
|
15
|
+
if (spec.tag && !isEmpty(spec.tag.name)) {
|
|
16
|
+
docExports.push(this.descriptions.createDescription(spec.tag, spec.tag.name, document));
|
|
17
|
+
docExports.push(this.descriptions.createDescription(spec.tag, '#' + spec.tag.name, document));
|
|
28
18
|
}
|
|
29
|
-
nonexhaustive(spec);
|
|
30
19
|
}
|
|
31
20
|
}
|
|
32
21
|
// Only root model elements are exported
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DONE_RESULT, DefaultScopeProvider, EMPTY_STREAM, StreamImpl, StreamScope, getDocument, stream, findNodeForProperty, toDocumentSegment } from 'langium';
|
|
2
2
|
import { ast } from '../ast';
|
|
3
|
-
import { elementRef, isElementRefHead,
|
|
3
|
+
import { elementRef, isElementRefHead, parentFqnElementRef } from '../elementRef';
|
|
4
4
|
import { logError } from '../logger';
|
|
5
5
|
function toAstNodeDescription(entry) {
|
|
6
6
|
const $cstNode = findNodeForProperty(entry.el.$cstNode, 'name');
|
|
@@ -52,11 +52,17 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
52
52
|
scopeExtendElement(extend) {
|
|
53
53
|
return this.uniqueDescedants(() => elementRef(extend.element));
|
|
54
54
|
}
|
|
55
|
-
scopeElementView({ viewOf }) {
|
|
56
|
-
if (
|
|
57
|
-
return
|
|
55
|
+
scopeElementView({ viewOf, extends: ext }) {
|
|
56
|
+
if (ext) {
|
|
57
|
+
return stream([ext]).flatMap(v => {
|
|
58
|
+
const view = v.view.ref;
|
|
59
|
+
return view ? this.scopeElementView(view) : EMPTY_STREAM;
|
|
60
|
+
});
|
|
58
61
|
}
|
|
59
|
-
|
|
62
|
+
if (viewOf) {
|
|
63
|
+
return this.uniqueDescedants(() => elementRef(viewOf));
|
|
64
|
+
}
|
|
65
|
+
return EMPTY_STREAM;
|
|
60
66
|
}
|
|
61
67
|
getScope(context) {
|
|
62
68
|
const referenceType = this.reflection.getReferenceType(context);
|
|
@@ -68,7 +74,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
68
74
|
if (isElementRefHead(container)) {
|
|
69
75
|
return this.getGlobalScope(referenceType);
|
|
70
76
|
}
|
|
71
|
-
const parent =
|
|
77
|
+
const parent = parentFqnElementRef(container);
|
|
72
78
|
return new StreamScope(this.directChildrenOf(parent));
|
|
73
79
|
}
|
|
74
80
|
if (ast.isElementRef(container) && !isElementRefHead(container)) {
|
|
@@ -102,7 +108,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
102
108
|
if (ast.isExtendElementBody(container)) {
|
|
103
109
|
scopes.push(this.scopeExtendElement(container.$container));
|
|
104
110
|
}
|
|
105
|
-
if (ast.
|
|
111
|
+
if (ast.isElementViewBody(container)) {
|
|
106
112
|
scopes.push(this.scopeElementView(container.$container));
|
|
107
113
|
}
|
|
108
114
|
}
|
|
@@ -22,6 +22,22 @@ export function registerProtocolHandlers(services) {
|
|
|
22
22
|
}
|
|
23
23
|
return Promise.resolve({ model });
|
|
24
24
|
});
|
|
25
|
+
connection.onRequest(Rpc.fetchRawModel, async (_cancelToken) => {
|
|
26
|
+
let rawmodel;
|
|
27
|
+
try {
|
|
28
|
+
rawmodel = modelBuilder.buildRawModel() ?? null;
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
rawmodel = null;
|
|
32
|
+
logError(e);
|
|
33
|
+
}
|
|
34
|
+
return Promise.resolve({ rawmodel });
|
|
35
|
+
});
|
|
36
|
+
connection.onRequest(Rpc.computeView, ({ viewId }) => {
|
|
37
|
+
return {
|
|
38
|
+
view: modelBuilder.computeView(viewId)
|
|
39
|
+
};
|
|
40
|
+
});
|
|
25
41
|
connection.onRequest(Rpc.rebuild, async (cancelToken) => {
|
|
26
42
|
const changed = LangiumDocuments.all
|
|
27
43
|
.map(d => {
|
package/dist/validation/view.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/language-server",
|
|
3
3
|
"description": "LikeC4 Language Server",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.37.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"test:watch": "vitest"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@likec4/core": "0.
|
|
45
|
+
"@likec4/core": "0.37.0",
|
|
46
46
|
"langium": "^2.0.2",
|
|
47
47
|
"nanoid": "^4.0.2",
|
|
48
48
|
"object-hash": "^3.0.0",
|