@likec4/language-server 0.33.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contrib/likec4.monarch.ts +17 -3
- package/dist/ast.d.ts +24 -11
- package/dist/ast.js +12 -9
- package/dist/elementRef.d.ts +1 -1
- package/dist/elementRef.js +2 -3
- package/dist/generated/ast.d.ts +29 -10
- package/dist/generated/ast.js +20 -1
- package/dist/generated/grammar.d.ts +1 -1
- package/dist/generated/grammar.js +11 -11
- package/dist/generated/module.d.ts +7 -3
- package/dist/generated/module.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/logger.d.ts +6 -4
- package/dist/logger.js +34 -6
- package/dist/{shared → lsp}/CodeLensProvider.d.ts +3 -3
- package/dist/{shared → lsp}/DocumentLinkProvider.d.ts +3 -2
- package/dist/{shared → lsp}/DocumentLinkProvider.js +3 -3
- package/dist/lsp/DocumentSymbolProvider.js +3 -3
- package/dist/lsp/index.d.ts +2 -0
- package/dist/lsp/index.js +2 -0
- package/dist/model/fqn-computation.js +6 -3
- package/dist/model/fqn-index.d.ts +4 -7
- package/dist/model/fqn-index.js +51 -21
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.js +1 -0
- package/dist/model/model-builder.d.ts +1 -18
- package/dist/model/model-builder.js +111 -340
- package/dist/model/model-locator.js +19 -24
- package/dist/model/model-parser.d.ts +27 -0
- package/dist/model/model-parser.js +293 -0
- package/dist/module.d.ts +2 -1
- package/dist/module.js +20 -29
- package/dist/protocol.d.ts +8 -16
- package/dist/protocol.js +2 -6
- package/dist/references/scope-computation.js +11 -5
- package/dist/references/scope-provider.js +10 -4
- package/dist/registerProtocolHandlers.js +33 -17
- package/dist/shared/index.d.ts +0 -2
- package/dist/shared/index.js +0 -2
- package/dist/test/testServices.d.ts +2 -2
- package/dist/test/testServices.js +16 -10
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +2 -7
- package/dist/validation/element.d.ts +1 -1
- package/dist/validation/element.js +23 -3
- package/dist/validation/index.js +26 -0
- package/dist/validation/relation.d.ts +1 -1
- package/dist/validation/relation.js +32 -26
- package/package.json +10 -8
- /package/dist/{shared → lsp}/CodeLensProvider.js +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core';
|
|
2
|
+
import { DocumentState, getDocument, interruptAndCheck } from 'langium';
|
|
3
|
+
import objectHash from 'object-hash';
|
|
4
|
+
import stripIndent from 'strip-indent';
|
|
5
|
+
import { Disposable } from 'vscode-languageserver-protocol';
|
|
6
|
+
import { ElementViewOps, ast, cleanParsedModel, isLikeC4LangiumDocument, resolveRelationPoints, streamModel, toAutoLayout, toElementStyle, toElementStyleExcludeDefaults } from '../ast';
|
|
7
|
+
import { elementRef, strictElementRefFqn } from '../elementRef';
|
|
8
|
+
import { logError, logWarnError, logger } from '../logger';
|
|
9
|
+
import { printDocs } from '../utils';
|
|
10
|
+
export class LikeC4ModelParser {
|
|
11
|
+
services;
|
|
12
|
+
fqnIndex;
|
|
13
|
+
listeners = [];
|
|
14
|
+
constructor(services) {
|
|
15
|
+
this.services = services;
|
|
16
|
+
this.fqnIndex = services.likec4.FqnIndex;
|
|
17
|
+
services.shared.workspace.DocumentBuilder.onBuildPhase(DocumentState.Validated, async (docs, cancelToken) => await this.onValidated(docs, cancelToken));
|
|
18
|
+
}
|
|
19
|
+
onParsed(callback) {
|
|
20
|
+
this.listeners.push(callback);
|
|
21
|
+
return Disposable.create(() => {
|
|
22
|
+
const index = this.listeners.indexOf(callback);
|
|
23
|
+
if (index >= 0) {
|
|
24
|
+
this.listeners.splice(index, 1);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async onValidated(docs, cancelToken) {
|
|
29
|
+
let countOfChangedDocs = 0;
|
|
30
|
+
logger.debug(`[ModelParser] onValidated (${docs.length} docs)\n${printDocs(docs)}`);
|
|
31
|
+
for (const doc of docs) {
|
|
32
|
+
if (!isLikeC4LangiumDocument(doc)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
countOfChangedDocs++;
|
|
36
|
+
try {
|
|
37
|
+
await this.parseDocument(doc, cancelToken);
|
|
38
|
+
}
|
|
39
|
+
catch (cause) {
|
|
40
|
+
logError(new InvalidModelError(`Error parsing document ${doc.uri.toString()}`, { cause }));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (countOfChangedDocs > 0) {
|
|
44
|
+
this.notifyListeners();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async parseDocument(doc, cancelToken) {
|
|
48
|
+
const { elements, relations, views, specification } = cleanParsedModel(doc);
|
|
49
|
+
const specs = doc.parseResult.value.specification?.specs.filter(ast.isSpecificationElementKind);
|
|
50
|
+
if (specs) {
|
|
51
|
+
for (const { kind, style } of specs) {
|
|
52
|
+
if (kind.name in specification.kinds) {
|
|
53
|
+
logger.warn(`Duplicate specification for kind ${kind.name}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
specification.kinds[kind.name] = toElementStyleExcludeDefaults(style?.props);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
logWarnError(e);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
await interruptAndCheck(cancelToken);
|
|
65
|
+
for (const el of streamModel(doc)) {
|
|
66
|
+
if (ast.isElement(el)) {
|
|
67
|
+
try {
|
|
68
|
+
elements.push(this.parseElement(el));
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
logWarnError(e);
|
|
72
|
+
}
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (ast.isRelation(el)) {
|
|
76
|
+
try {
|
|
77
|
+
relations.push(this.parseRelation(el));
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
logWarnError(e);
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
nonexhaustive(el);
|
|
85
|
+
}
|
|
86
|
+
await interruptAndCheck(cancelToken);
|
|
87
|
+
const docviews = doc.parseResult.value.views?.views;
|
|
88
|
+
if (docviews) {
|
|
89
|
+
for (const view of docviews) {
|
|
90
|
+
try {
|
|
91
|
+
const v = this.parseElementView(view);
|
|
92
|
+
ElementViewOps.writeId(view, v.id);
|
|
93
|
+
views.push(v);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
logWarnError(e);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// const prevHash = doc.c4hash ?? ''
|
|
101
|
+
// doc.c4hash = c4hash(doc)
|
|
102
|
+
// return prevHash !== doc.c4hash
|
|
103
|
+
}
|
|
104
|
+
parseElement(astNode) {
|
|
105
|
+
const id = this.resolveFqn(astNode);
|
|
106
|
+
invariant(astNode.kind.ref, 'Element kind is not resolved: ' + astNode.name);
|
|
107
|
+
const kind = astNode.kind.ref.name;
|
|
108
|
+
const tags = this.convertTags(astNode.body);
|
|
109
|
+
const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
|
|
110
|
+
const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
|
|
111
|
+
const astPath = this.getAstNodePath(astNode);
|
|
112
|
+
let [title, description, technology] = astNode.props;
|
|
113
|
+
const bodyProps = astNode.body?.props.filter((p) => ast.isElementStringProperty(p)) ?? [];
|
|
114
|
+
title = title ?? bodyProps.find(p => p.key === 'title')?.value;
|
|
115
|
+
description = description ?? bodyProps.find(p => p.key === 'description')?.value;
|
|
116
|
+
technology = technology ?? bodyProps.find(p => p.key === 'technology')?.value;
|
|
117
|
+
const links = astNode.body?.props.filter(ast.isLinkProperty).map(p => p.value);
|
|
118
|
+
return {
|
|
119
|
+
id,
|
|
120
|
+
kind,
|
|
121
|
+
astPath,
|
|
122
|
+
title: title ? stripIndent(title).trim() : astNode.name,
|
|
123
|
+
...(tags && { tags }),
|
|
124
|
+
...(links && isNonEmptyArray(links) && { links }),
|
|
125
|
+
...(technology && { technology }),
|
|
126
|
+
...(description && { description: stripIndent(description).trim() }),
|
|
127
|
+
...styleProps
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
parseRelation(astNode) {
|
|
131
|
+
const coupling = resolveRelationPoints(astNode);
|
|
132
|
+
const target = this.resolveFqn(coupling.target);
|
|
133
|
+
const source = this.resolveFqn(coupling.source);
|
|
134
|
+
const hashdata = {
|
|
135
|
+
astPath: this.getAstNodePath(astNode),
|
|
136
|
+
source,
|
|
137
|
+
target
|
|
138
|
+
};
|
|
139
|
+
const id = objectHash(hashdata);
|
|
140
|
+
const title = astNode.title ?? astNode.body?.props.find(p => p.key === 'title')?.value ?? '';
|
|
141
|
+
return {
|
|
142
|
+
id,
|
|
143
|
+
...hashdata,
|
|
144
|
+
title
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
parseElementExpression(astNode) {
|
|
148
|
+
if (ast.isWildcardExpression(astNode)) {
|
|
149
|
+
return {
|
|
150
|
+
wildcard: true
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (ast.isElementKindExpression(astNode)) {
|
|
154
|
+
invariant(astNode.kind.ref, 'ElementKindExpression kind is not resolved: ' + astNode.$cstNode?.text);
|
|
155
|
+
return {
|
|
156
|
+
elementKind: astNode.kind.ref.name,
|
|
157
|
+
isEqual: astNode.isEqual
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (ast.isElementTagExpression(astNode)) {
|
|
161
|
+
invariant(astNode.tag.ref, 'ElementTagExpression tag is not resolved: ' + astNode.$cstNode?.text);
|
|
162
|
+
return {
|
|
163
|
+
elementTag: astNode.tag.ref.name,
|
|
164
|
+
isEqual: astNode.isEqual
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (ast.isElementRefExpression(astNode)) {
|
|
168
|
+
const element = elementRef(astNode.id);
|
|
169
|
+
invariant(element, 'Element not found ' + astNode.id.$cstNode?.text);
|
|
170
|
+
return {
|
|
171
|
+
element: this.resolveFqn(element),
|
|
172
|
+
isDescedants: astNode.isDescedants
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
nonexhaustive(astNode);
|
|
176
|
+
}
|
|
177
|
+
parseExpression(astNode) {
|
|
178
|
+
if (ast.isElementExpression(astNode)) {
|
|
179
|
+
return this.parseElementExpression(astNode);
|
|
180
|
+
}
|
|
181
|
+
if (ast.isIncomingExpression(astNode)) {
|
|
182
|
+
return {
|
|
183
|
+
incoming: this.parseElementExpression(astNode.target)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
if (ast.isOutgoingExpression(astNode)) {
|
|
187
|
+
return {
|
|
188
|
+
outgoing: this.parseElementExpression(astNode.source)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (ast.isInOutExpression(astNode)) {
|
|
192
|
+
return {
|
|
193
|
+
inout: this.parseElementExpression(astNode.inout.target)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (ast.isRelationExpression(astNode)) {
|
|
197
|
+
return {
|
|
198
|
+
source: this.parseElementExpression(astNode.source),
|
|
199
|
+
target: this.parseElementExpression(astNode.target)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
nonexhaustive(astNode);
|
|
203
|
+
}
|
|
204
|
+
parseViewRule(astRule) {
|
|
205
|
+
if (ast.isViewRuleExpression(astRule)) {
|
|
206
|
+
const exprs = astRule.expressions.map(n => this.parseExpression(n));
|
|
207
|
+
return {
|
|
208
|
+
isInclude: astRule.isInclude,
|
|
209
|
+
exprs
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (ast.isViewRuleStyle(astRule)) {
|
|
213
|
+
const styleProps = toElementStyle(astRule.props);
|
|
214
|
+
return {
|
|
215
|
+
targets: astRule.targets.map(n => this.parseElementExpression(n)),
|
|
216
|
+
style: {
|
|
217
|
+
...styleProps
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (ast.isViewRuleAutoLayout(astRule)) {
|
|
222
|
+
return {
|
|
223
|
+
autoLayout: toAutoLayout(astRule.direction)
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
nonexhaustive(astRule);
|
|
227
|
+
}
|
|
228
|
+
parseElementView(astNode) {
|
|
229
|
+
const viewOfEl = astNode.viewOf && elementRef(astNode.viewOf);
|
|
230
|
+
const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
|
|
231
|
+
const astPath = this.getAstNodePath(astNode);
|
|
232
|
+
let id = astNode.name;
|
|
233
|
+
if (!id) {
|
|
234
|
+
const doc = getDocument(astNode).uri.toString();
|
|
235
|
+
id = objectHash({
|
|
236
|
+
doc,
|
|
237
|
+
astPath,
|
|
238
|
+
viewOf: viewOf ?? null
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const title = astNode.props.find(p => p.key === 'title')?.value;
|
|
242
|
+
const description = astNode.props.find(p => p.key === 'description')?.value;
|
|
243
|
+
const tags = this.convertTags(astNode);
|
|
244
|
+
const links = astNode.props.filter(ast.isLinkProperty).map(p => p.value);
|
|
245
|
+
return {
|
|
246
|
+
id,
|
|
247
|
+
astPath,
|
|
248
|
+
...(viewOf && { viewOf }),
|
|
249
|
+
...(title && { title }),
|
|
250
|
+
...(description && { description }),
|
|
251
|
+
...(tags && { tags }),
|
|
252
|
+
...(links && isNonEmptyArray(links) && { links }),
|
|
253
|
+
rules: astNode.rules.flatMap(n => {
|
|
254
|
+
try {
|
|
255
|
+
return this.parseViewRule(n);
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
logWarnError(e);
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
resolveFqn(node) {
|
|
265
|
+
if (ast.isExtendElement(node)) {
|
|
266
|
+
return strictElementRefFqn(node.element);
|
|
267
|
+
}
|
|
268
|
+
const fqn = this.fqnIndex.getFqn(node);
|
|
269
|
+
invariant(fqn, `Not indexed element: ${this.getAstNodePath(node)}`);
|
|
270
|
+
return fqn;
|
|
271
|
+
}
|
|
272
|
+
getAstNodePath(node) {
|
|
273
|
+
return this.services.workspace.AstNodeLocator.getAstNodePath(node);
|
|
274
|
+
}
|
|
275
|
+
convertTags(withTags) {
|
|
276
|
+
if (!withTags) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const tags = withTags.tags?.value.flatMap(({ ref }) => (ref ? ref.name : []));
|
|
280
|
+
return tags && isNonEmptyArray(tags) ? tags : null;
|
|
281
|
+
}
|
|
282
|
+
notifyListeners() {
|
|
283
|
+
for (const listener of this.listeners) {
|
|
284
|
+
try {
|
|
285
|
+
listener();
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
logError(e);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=model-parser.js.map
|
package/dist/module.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { DefaultSharedModuleContext, LangiumServices, LangiumSharedServices, Module, PartialLangiumServices } from 'langium';
|
|
2
2
|
import { LikeC4DocumentSymbolProvider } from './lsp';
|
|
3
|
-
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator } from './model';
|
|
3
|
+
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
|
|
4
4
|
/**
|
|
5
5
|
* Declaration of custom services - add your own service classes here.
|
|
6
6
|
*/
|
|
7
7
|
export interface LikeC4AddedServices {
|
|
8
8
|
likec4: {
|
|
9
9
|
FqnIndex: FqnIndex;
|
|
10
|
+
ModelParser: LikeC4ModelParser;
|
|
10
11
|
ModelBuilder: LikeC4ModelBuilder;
|
|
11
12
|
ModelLocator: LikeC4ModelLocator;
|
|
12
13
|
};
|
package/dist/module.js
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { EmptyFileSystem, createDefaultModule, createDefaultSharedModule, inject } from 'langium';
|
|
2
2
|
import { LikeC4GeneratedModule, LikeC4GeneratedSharedModule } from './generated/module';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator } from './model';
|
|
3
|
+
import { LikeC4CodeLensProvider, LikeC4DocumentLinkProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
|
|
4
|
+
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
|
|
6
5
|
import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references';
|
|
7
6
|
import { registerProtocolHandlers } from './registerProtocolHandlers';
|
|
8
|
-
import {
|
|
7
|
+
import { LikeC4WorkspaceManager } from './shared';
|
|
9
8
|
import { registerValidationChecks } from './validation';
|
|
9
|
+
import { logger } from './logger';
|
|
10
|
+
import { serializeError } from '@likec4/core';
|
|
10
11
|
function bind(Type) {
|
|
11
12
|
return (services) => new Type(services);
|
|
12
13
|
}
|
|
13
14
|
export const LikeC4Module = {
|
|
14
15
|
likec4: {
|
|
15
16
|
FqnIndex: bind(FqnIndex),
|
|
17
|
+
ModelParser: bind(LikeC4ModelParser),
|
|
16
18
|
ModelBuilder: bind(LikeC4ModelBuilder),
|
|
17
19
|
ModelLocator: bind(LikeC4ModelLocator)
|
|
18
20
|
},
|
|
19
21
|
lsp: {
|
|
20
22
|
DocumentSymbolProvider: bind(LikeC4DocumentSymbolProvider),
|
|
21
23
|
SemanticTokenProvider: bind(LikeC4SemanticTokenProvider),
|
|
22
|
-
HoverProvider: bind(LikeC4HoverProvider)
|
|
24
|
+
HoverProvider: bind(LikeC4HoverProvider),
|
|
25
|
+
CodeLensProvider: bind(LikeC4CodeLensProvider),
|
|
26
|
+
DocumentLinkProvider: bind(LikeC4DocumentLinkProvider)
|
|
23
27
|
},
|
|
24
28
|
//
|
|
25
29
|
// // Formatter: bind(LikeC4Formatter),
|
|
@@ -31,44 +35,31 @@ export const LikeC4Module = {
|
|
|
31
35
|
}
|
|
32
36
|
};
|
|
33
37
|
const LikeC4SharedModule = {
|
|
34
|
-
...LikeC4GeneratedSharedModule,
|
|
35
38
|
workspace: {
|
|
36
39
|
WorkspaceManager: services => new LikeC4WorkspaceManager(services)
|
|
37
|
-
},
|
|
38
|
-
lsp: {
|
|
39
|
-
CodeLensProvider: services => new LikeC4CodeLensProvider(services),
|
|
40
|
-
DocumentLinkProvider: services => new LikeC4DocumentLinkProvider(services)
|
|
41
40
|
}
|
|
42
41
|
};
|
|
43
42
|
export function createLanguageServices(context) {
|
|
44
43
|
const connection = context?.connection;
|
|
45
44
|
if (connection) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
console.error(error);
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
46
|
+
const original = logger.error.bind(logger);
|
|
47
|
+
logger.error = (arg) => {
|
|
48
|
+
if (typeof arg === 'string') {
|
|
49
|
+
original(arg);
|
|
50
|
+
connection.telemetry.logEvent({ eventName: 'error', error: arg });
|
|
51
|
+
return;
|
|
56
52
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
logger.warn = log('warn');
|
|
61
|
-
logger.error = log('error');
|
|
62
|
-
logger.trace = logger.debug = (message) => {
|
|
63
|
-
console.debug(message);
|
|
64
|
-
connection.tracer.log(message);
|
|
53
|
+
const { message, error } = serializeError(arg);
|
|
54
|
+
original(error);
|
|
55
|
+
connection.telemetry.logEvent({ eventName: 'error', error: message });
|
|
65
56
|
};
|
|
66
57
|
}
|
|
67
58
|
const moduleContext = {
|
|
68
59
|
...EmptyFileSystem,
|
|
69
60
|
...context
|
|
70
61
|
};
|
|
71
|
-
const shared = inject(createDefaultSharedModule(moduleContext), LikeC4SharedModule);
|
|
62
|
+
const shared = inject(createDefaultSharedModule(moduleContext), LikeC4GeneratedSharedModule, LikeC4SharedModule);
|
|
72
63
|
const likec4 = inject(createDefaultModule({ shared }), LikeC4GeneratedModule, LikeC4Module);
|
|
73
64
|
shared.ServiceRegistry.register(likec4);
|
|
74
65
|
registerValidationChecks(likec4);
|
package/dist/protocol.d.ts
CHANGED
|
@@ -4,17 +4,15 @@ import { NotificationType, RequestType0, RequestType } from 'vscode-languageserv
|
|
|
4
4
|
interface BuildDocumentsParams {
|
|
5
5
|
docs: DocumentUri[];
|
|
6
6
|
}
|
|
7
|
-
export
|
|
7
|
+
export type LocateParams = {
|
|
8
8
|
element: Fqn;
|
|
9
9
|
property?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
export declare const
|
|
16
|
-
id: ViewID;
|
|
17
|
-
}, Location | null, void>;
|
|
10
|
+
} | {
|
|
11
|
+
relation: RelationID;
|
|
12
|
+
} | {
|
|
13
|
+
view: ViewID;
|
|
14
|
+
};
|
|
15
|
+
export declare const locate: RequestType<LocateParams, Location | null, void>;
|
|
18
16
|
export declare const Rpc: {
|
|
19
17
|
readonly onDidChangeModel: NotificationType<string>;
|
|
20
18
|
readonly fetchModel: RequestType0<{
|
|
@@ -24,13 +22,7 @@ export declare const Rpc: {
|
|
|
24
22
|
docs: DocumentUri[];
|
|
25
23
|
}, void>;
|
|
26
24
|
readonly buildDocuments: RequestType<BuildDocumentsParams, void, void>;
|
|
27
|
-
readonly
|
|
28
|
-
readonly locateRelation: RequestType<{
|
|
29
|
-
id: RelationID;
|
|
30
|
-
}, Location | null, void>;
|
|
31
|
-
readonly locateView: RequestType<{
|
|
32
|
-
id: ViewID;
|
|
33
|
-
}, Location | null, void>;
|
|
25
|
+
readonly locate: RequestType<LocateParams, Location | null, void>;
|
|
34
26
|
};
|
|
35
27
|
export {};
|
|
36
28
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.js
CHANGED
|
@@ -6,17 +6,13 @@ const onDidChangeModel = new NotificationType('likec4/onDidChangeModel');
|
|
|
6
6
|
const fetchModel = new RequestType0('likec4/fetchModel');
|
|
7
7
|
const rebuild = new RequestType0('likec4/rebuildModel');
|
|
8
8
|
const buildDocuments = new RequestType('likec4/buildDocuments');
|
|
9
|
-
export const
|
|
10
|
-
export const locateRelation = new RequestType('likec4/locateRelation');
|
|
11
|
-
export const locateView = new RequestType('likec4/locateView');
|
|
9
|
+
export const locate = new RequestType('likec4/locate');
|
|
12
10
|
//#endregion
|
|
13
11
|
export const Rpc = {
|
|
14
12
|
onDidChangeModel,
|
|
15
13
|
fetchModel,
|
|
16
14
|
rebuild,
|
|
17
15
|
buildDocuments,
|
|
18
|
-
|
|
19
|
-
locateRelation,
|
|
20
|
-
locateView
|
|
16
|
+
locate
|
|
21
17
|
};
|
|
22
18
|
//# sourceMappingURL=protocol.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DefaultScopeComputation, MultiMap } from 'langium';
|
|
2
2
|
import { ast } from '../ast';
|
|
3
3
|
import { isEmpty } from 'remeda';
|
|
4
|
+
import { nonexhaustive } from '@likec4/core';
|
|
4
5
|
export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
5
6
|
services;
|
|
6
7
|
constructor(services) {
|
|
@@ -12,15 +13,20 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
|
|
|
12
13
|
const docExports = [];
|
|
13
14
|
if (specification && specification.specs.length > 0) {
|
|
14
15
|
for (const spec of specification.specs) {
|
|
15
|
-
if (ast.isSpecificationElementKind(spec)
|
|
16
|
-
|
|
16
|
+
if (ast.isSpecificationElementKind(spec)) {
|
|
17
|
+
if (spec.kind && !isEmpty(spec.kind.name)) {
|
|
18
|
+
docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
|
|
19
|
+
}
|
|
17
20
|
continue;
|
|
18
21
|
}
|
|
19
|
-
if (ast.isSpecificationTag(spec)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
if (ast.isSpecificationTag(spec)) {
|
|
23
|
+
if (spec.tag && !isEmpty(spec.tag.name)) {
|
|
24
|
+
docExports.push(this.descriptions.createDescription(spec.tag, spec.tag.name, document));
|
|
25
|
+
docExports.push(this.descriptions.createDescription(spec.tag, '#' + spec.tag.name, document));
|
|
26
|
+
}
|
|
22
27
|
continue;
|
|
23
28
|
}
|
|
29
|
+
nonexhaustive(spec);
|
|
24
30
|
}
|
|
25
31
|
}
|
|
26
32
|
// Only root model elements are exported
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { DONE_RESULT, DefaultScopeProvider, EMPTY_STREAM, StreamImpl, StreamScope, getDocument, stream } from 'langium';
|
|
1
|
+
import { DONE_RESULT, DefaultScopeProvider, EMPTY_STREAM, StreamImpl, StreamScope, getDocument, stream, findNodeForProperty, toDocumentSegment } from 'langium';
|
|
2
2
|
import { ast } from '../ast';
|
|
3
3
|
import { elementRef, isElementRefHead, parentStrictElementRef } from '../elementRef';
|
|
4
|
-
import {
|
|
4
|
+
import { logError } from '../logger';
|
|
5
5
|
function toAstNodeDescription(entry) {
|
|
6
|
+
const $cstNode = findNodeForProperty(entry.el.$cstNode, 'name');
|
|
6
7
|
return {
|
|
7
8
|
documentUri: entry.doc.uri,
|
|
8
9
|
name: entry.name,
|
|
10
|
+
...(entry.el.$cstNode && {
|
|
11
|
+
selectionSegment: toDocumentSegment(entry.el.$cstNode)
|
|
12
|
+
}),
|
|
13
|
+
...($cstNode && {
|
|
14
|
+
nameSegment: toDocumentSegment($cstNode)
|
|
15
|
+
}),
|
|
9
16
|
path: entry.path,
|
|
10
17
|
type: ast.Element
|
|
11
18
|
};
|
|
@@ -71,8 +78,7 @@ export class LikeC4ScopeProvider extends DefaultScopeProvider {
|
|
|
71
78
|
return this.computeScope(container, referenceType);
|
|
72
79
|
}
|
|
73
80
|
catch (e) {
|
|
74
|
-
|
|
75
|
-
logger.error(e);
|
|
81
|
+
logError(e);
|
|
76
82
|
return this.getGlobalScope(referenceType);
|
|
77
83
|
}
|
|
78
84
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { URI } from 'vscode-uri';
|
|
2
|
-
import { logger } from './logger';
|
|
2
|
+
import { logger, logError } from './logger';
|
|
3
3
|
import { Rpc } from './protocol';
|
|
4
|
+
import { nonexhaustive } from '@likec4/core';
|
|
5
|
+
import { isLikeC4LangiumDocument } from './ast';
|
|
4
6
|
export function registerProtocolHandlers(services) {
|
|
5
7
|
const connection = services.shared.lsp.Connection;
|
|
6
8
|
if (!connection) {
|
|
@@ -16,13 +18,25 @@ export function registerProtocolHandlers(services) {
|
|
|
16
18
|
}
|
|
17
19
|
catch (e) {
|
|
18
20
|
model = null;
|
|
19
|
-
|
|
21
|
+
logError(e);
|
|
20
22
|
}
|
|
21
23
|
return Promise.resolve({ model });
|
|
22
24
|
});
|
|
23
25
|
connection.onRequest(Rpc.rebuild, async (cancelToken) => {
|
|
24
|
-
const changed = LangiumDocuments.all
|
|
25
|
-
|
|
26
|
+
const changed = LangiumDocuments.all
|
|
27
|
+
.map(d => {
|
|
28
|
+
// clean up any computed properties
|
|
29
|
+
if (isLikeC4LangiumDocument(d)) {
|
|
30
|
+
delete d.c4Specification;
|
|
31
|
+
delete d.c4Elements;
|
|
32
|
+
delete d.c4Relations;
|
|
33
|
+
delete d.c4Views;
|
|
34
|
+
delete d.c4fqns;
|
|
35
|
+
}
|
|
36
|
+
return d.uri;
|
|
37
|
+
})
|
|
38
|
+
.toArray();
|
|
39
|
+
logger.debug(`[ProtocolHandlers] rebuild all documents: [
|
|
26
40
|
${changed.map(d => d.toString()).join('\n ')}
|
|
27
41
|
]`);
|
|
28
42
|
await services.shared.workspace.DocumentBuilder.update(changed, [], cancelToken);
|
|
@@ -32,12 +46,11 @@ export function registerProtocolHandlers(services) {
|
|
|
32
46
|
});
|
|
33
47
|
connection.onRequest(Rpc.buildDocuments, async ({ docs }, cancelToken) => {
|
|
34
48
|
if (docs.length === 0) {
|
|
35
|
-
logger.debug(`
|
|
49
|
+
logger.debug(`[ProtocolHandlers] received empty request to rebuild`);
|
|
36
50
|
return;
|
|
37
51
|
}
|
|
38
|
-
logger.debug(`
|
|
39
|
-
|
|
40
|
-
]`);
|
|
52
|
+
logger.debug(`[ProtocolHandlers] received request to buildDocuments:
|
|
53
|
+
- ${docs.join('\n - ')}`);
|
|
41
54
|
const changed = [];
|
|
42
55
|
for (const d of docs) {
|
|
43
56
|
try {
|
|
@@ -51,7 +64,7 @@ export function registerProtocolHandlers(services) {
|
|
|
51
64
|
}
|
|
52
65
|
}
|
|
53
66
|
catch (e) {
|
|
54
|
-
|
|
67
|
+
logError(e);
|
|
55
68
|
}
|
|
56
69
|
}
|
|
57
70
|
if (changed.length !== docs.length) {
|
|
@@ -67,14 +80,17 @@ We rebuild: [
|
|
|
67
80
|
}
|
|
68
81
|
await services.shared.workspace.DocumentBuilder.update(changed, [], cancelToken);
|
|
69
82
|
});
|
|
70
|
-
connection.onRequest(Rpc.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
connection.onRequest(Rpc.locate, params => {
|
|
84
|
+
if ('element' in params) {
|
|
85
|
+
return modelLocator.locateElement(params.element, params.property ?? 'name');
|
|
86
|
+
}
|
|
87
|
+
if ('relation' in params) {
|
|
88
|
+
return modelLocator.locateRelation(params.relation);
|
|
89
|
+
}
|
|
90
|
+
if ('view' in params) {
|
|
91
|
+
return modelLocator.locateView(params.view);
|
|
92
|
+
}
|
|
93
|
+
nonexhaustive(params);
|
|
78
94
|
});
|
|
79
95
|
}
|
|
80
96
|
//# sourceMappingURL=registerProtocolHandlers.js.map
|
package/dist/shared/index.d.ts
CHANGED
package/dist/shared/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { LikeC4LangiumDocument } from '../ast';
|
|
2
|
-
export declare function createTestServices(): {
|
|
2
|
+
export declare function createTestServices(workspace?: string): {
|
|
3
3
|
services: import("../module").LikeC4Services;
|
|
4
4
|
parse: (input: string, uri?: string) => Promise<LikeC4LangiumDocument>;
|
|
5
|
-
validate: (input: string | LikeC4LangiumDocument) => Promise<{
|
|
5
|
+
validate: (input: string | LikeC4LangiumDocument, uri?: string) => Promise<{
|
|
6
6
|
document: LikeC4LangiumDocument;
|
|
7
7
|
diagnostics: import("vscode-languageserver-types").Diagnostic[];
|
|
8
8
|
errors: string[];
|