@likec4/language-server 0.57.1 → 0.60.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/dist/Rpc.js +9 -22
- package/dist/ast.d.ts +1 -0
- package/dist/ast.js +2 -2
- package/dist/browser/index.js +2 -2
- package/dist/generated/ast.d.ts +3 -3
- package/dist/generated/ast.js +322 -49
- package/dist/generated/grammar.d.ts +1 -1
- package/dist/generated/grammar.js +1 -1
- package/dist/generated/module.d.ts +4 -4
- package/dist/index.d.ts +2 -1
- package/dist/logger.js +4 -0
- package/dist/lsp/CodeLensProvider.d.ts +3 -2
- package/dist/lsp/CodeLensProvider.js +5 -1
- package/dist/lsp/DocumentHighlightProvider.d.ts +2 -1
- package/dist/lsp/DocumentHighlightProvider.js +1 -1
- package/dist/lsp/DocumentLinkProvider.d.ts +2 -1
- package/dist/lsp/DocumentLinkProvider.js +3 -3
- package/dist/lsp/DocumentSymbolProvider.d.ts +3 -2
- package/dist/lsp/DocumentSymbolProvider.js +6 -8
- package/dist/lsp/HoverProvider.d.ts +2 -1
- package/dist/lsp/HoverProvider.js +1 -1
- package/dist/lsp/SemanticTokenProvider.d.ts +2 -1
- package/dist/lsp/SemanticTokenProvider.js +1 -1
- package/dist/model/model-builder.d.ts +4 -4
- package/dist/model/model-builder.js +53 -43
- package/dist/model/model-locator.js +3 -1
- package/dist/model/model-parser.js +6 -3
- package/dist/module.d.ts +2 -1
- package/dist/module.js +3 -5
- package/dist/node/index.js +1 -1
- package/dist/references/scope-provider.js +9 -6
- package/dist/shared/NodeKindProvider.d.ts +3 -2
- package/dist/shared/WorkspaceSymbolProvider.d.ts +1 -1
- package/dist/shared/WorkspaceSymbolProvider.js +1 -1
- package/dist/test/testServices.d.ts +1 -0
- package/dist/test/testServices.js +39 -30
- package/dist/validation/element.js +14 -10
- package/dist/validation/index.js +16 -22
- package/dist/validation/specification.d.ts +1 -1
- package/dist/validation/specification.js +38 -9
- package/package.json +9 -9
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
parentFqn
|
|
5
5
|
} from "@likec4/core";
|
|
6
6
|
import { computeView, LikeC4ModelGraph } from "@likec4/graph";
|
|
7
|
-
import { DocumentState } from "langium";
|
|
7
|
+
import { DocumentState, interruptAndCheck } from "langium";
|
|
8
8
|
import * as R from "remeda";
|
|
9
9
|
import { Disposable } from "vscode-languageserver";
|
|
10
10
|
import { isParsedLikeC4LangiumDocument } from "../ast.js";
|
|
@@ -36,6 +36,7 @@ function buildModel(services, docs) {
|
|
|
36
36
|
...parsed
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
+
logger.warn(`No kind '${parsed.kind}' found for ${parsed.id}`);
|
|
39
40
|
} catch (e) {
|
|
40
41
|
logWarnError(e);
|
|
41
42
|
}
|
|
@@ -63,34 +64,38 @@ function buildModel(services, docs) {
|
|
|
63
64
|
{}
|
|
64
65
|
)
|
|
65
66
|
);
|
|
66
|
-
const toModelRelation = ({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
const toModelRelation = (doc) => {
|
|
68
|
+
return ({
|
|
69
|
+
astPath,
|
|
70
|
+
source,
|
|
71
|
+
target,
|
|
72
|
+
kind,
|
|
73
|
+
links,
|
|
74
|
+
...model
|
|
75
|
+
}) => {
|
|
76
|
+
if (source in elements && target in elements) {
|
|
77
|
+
if (!!kind && kind in c4Specification.relationships) {
|
|
78
|
+
return {
|
|
79
|
+
source,
|
|
80
|
+
target,
|
|
81
|
+
kind,
|
|
82
|
+
...links && { links: resolveLinks(doc, links) },
|
|
83
|
+
...c4Specification.relationships[kind],
|
|
84
|
+
...model
|
|
85
|
+
};
|
|
86
|
+
}
|
|
75
87
|
return {
|
|
76
88
|
source,
|
|
77
89
|
target,
|
|
78
|
-
|
|
79
|
-
...c4Specification.relationships[kind],
|
|
90
|
+
...links && { links: resolveLinks(doc, links) },
|
|
80
91
|
...model
|
|
81
92
|
};
|
|
82
93
|
}
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
target,
|
|
86
|
-
...model
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
return null;
|
|
94
|
+
return null;
|
|
95
|
+
};
|
|
90
96
|
};
|
|
91
97
|
const relations = R.pipe(
|
|
92
|
-
R.flatMap(docs, (d) => d.c4Relations),
|
|
93
|
-
R.map(toModelRelation),
|
|
98
|
+
R.flatMap(docs, (d) => R.map(d.c4Relations, toModelRelation(d))),
|
|
94
99
|
R.compact,
|
|
95
100
|
R.mapToObj((r) => [r.id, r])
|
|
96
101
|
);
|
|
@@ -160,9 +165,14 @@ export class LikeC4ModelBuilder {
|
|
|
160
165
|
services.shared.workspace.DocumentBuilder.onBuildPhase(
|
|
161
166
|
DocumentState.Validated,
|
|
162
167
|
async (docs, _cancelToken) => {
|
|
163
|
-
|
|
168
|
+
let parsed = [];
|
|
169
|
+
try {
|
|
170
|
+
logger.debug(`[ModelBuilder] onValidated (${docs.length} docs)
|
|
164
171
|
${printDocs(docs)}`);
|
|
165
|
-
|
|
172
|
+
parsed.push(...parser.parse(docs).map((d) => d.uri));
|
|
173
|
+
} catch (e) {
|
|
174
|
+
logger.error(e);
|
|
175
|
+
}
|
|
166
176
|
if (parsed.length > 0) {
|
|
167
177
|
this.notifyListeners(parsed);
|
|
168
178
|
}
|
|
@@ -173,10 +183,13 @@ ${printDocs(docs)}`);
|
|
|
173
183
|
}
|
|
174
184
|
langiumDocuments;
|
|
175
185
|
listeners = [];
|
|
176
|
-
buildRawModel() {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
186
|
+
async buildRawModel(cancelToken) {
|
|
187
|
+
return await this.services.shared.workspace.WorkspaceLock.read(async () => {
|
|
188
|
+
if (cancelToken) {
|
|
189
|
+
await interruptAndCheck(cancelToken);
|
|
190
|
+
}
|
|
191
|
+
const cache = this.services.WorkspaceCache;
|
|
192
|
+
return cache.get(RAW_MODEL_CACHE, () => {
|
|
180
193
|
const docs = this.documents();
|
|
181
194
|
if (docs.length === 0) {
|
|
182
195
|
logger.debug("[ModelBuilder] No documents to build model from");
|
|
@@ -185,21 +198,18 @@ ${printDocs(docs)}`);
|
|
|
185
198
|
logger.debug(`[ModelBuilder] buildModel from ${docs.length} docs:
|
|
186
199
|
${printDocs(docs)}`);
|
|
187
200
|
return buildModel(this.services, docs);
|
|
188
|
-
}
|
|
189
|
-
logError(e);
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
201
|
+
});
|
|
192
202
|
});
|
|
193
203
|
}
|
|
194
204
|
previousViews = {};
|
|
195
|
-
buildModel() {
|
|
205
|
+
async buildModel(cancelToken) {
|
|
206
|
+
const model = await this.buildRawModel(cancelToken);
|
|
207
|
+
if (!model) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
196
210
|
const cache = this.services.WorkspaceCache;
|
|
197
211
|
const viewsCache = this.services.WorkspaceCache;
|
|
198
212
|
return cache.get(MODEL_CACHE, () => {
|
|
199
|
-
const model = this.buildRawModel();
|
|
200
|
-
if (!model) {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
213
|
const index = new LikeC4ModelGraph(model);
|
|
204
214
|
const allViews = [];
|
|
205
215
|
for (const view of R.values(model.views)) {
|
|
@@ -225,15 +235,15 @@ ${printDocs(docs)}`);
|
|
|
225
235
|
};
|
|
226
236
|
});
|
|
227
237
|
}
|
|
228
|
-
computeView(viewId) {
|
|
238
|
+
async computeView(viewId, cancelToken) {
|
|
239
|
+
const model = await this.buildRawModel(cancelToken);
|
|
240
|
+
const view = model?.views[viewId];
|
|
241
|
+
if (!view) {
|
|
242
|
+
logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
229
245
|
const cache = this.services.WorkspaceCache;
|
|
230
246
|
return cache.get(computedViewKey(viewId), () => {
|
|
231
|
-
const model = this.buildRawModel();
|
|
232
|
-
const view = model?.views[viewId];
|
|
233
|
-
if (!view) {
|
|
234
|
-
logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
247
|
const index = new LikeC4ModelGraph(model);
|
|
238
248
|
const result = computeView(view, index);
|
|
239
249
|
if (!result.isSuccess) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AstUtils, GrammarUtils } from "langium";
|
|
2
2
|
import { ast, isParsedLikeC4LangiumDocument } from "../ast.js";
|
|
3
|
+
const { findNodeForKeyword, findNodeForProperty } = GrammarUtils;
|
|
4
|
+
const { getDocument } = AstUtils;
|
|
3
5
|
export class LikeC4ModelLocator {
|
|
4
6
|
constructor(services) {
|
|
5
7
|
this.services = services;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
|
|
2
|
-
import {
|
|
2
|
+
import { AstUtils } from "langium";
|
|
3
3
|
import objectHash from "object-hash";
|
|
4
4
|
import { isTruthy } from "remeda";
|
|
5
5
|
import stripIndent from "strip-indent";
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "../ast.js";
|
|
19
19
|
import { elementRef, getFqnElementRef } from "../elementRef.js";
|
|
20
20
|
import { logError, logger, logWarnError } from "../logger.js";
|
|
21
|
+
const { getDocument } = AstUtils;
|
|
21
22
|
function toSingleLine(str) {
|
|
22
23
|
return str ? removeIndent(str).split("\n").join(" ") : void 0;
|
|
23
24
|
}
|
|
@@ -111,7 +112,7 @@ export class LikeC4ModelParser {
|
|
|
111
112
|
const stylePropsAst = astNode.body?.props.find(ast.isStyleProperties)?.props;
|
|
112
113
|
const styleProps = toElementStyleExcludeDefaults(stylePropsAst);
|
|
113
114
|
const astPath = this.getAstNodePath(astNode);
|
|
114
|
-
let [title, description, technology] = astNode.props;
|
|
115
|
+
let [title, description, technology] = astNode.props ?? [];
|
|
115
116
|
const bodyProps = astNode.body?.props.filter(ast.isElementStringProperty) ?? [];
|
|
116
117
|
title = toSingleLine(title ?? bodyProps.find((p) => p.key === "title")?.value);
|
|
117
118
|
description = removeIndent(description ?? bodyProps.find((p) => p.key === "description")?.value);
|
|
@@ -134,6 +135,7 @@ export class LikeC4ModelParser {
|
|
|
134
135
|
const target = this.resolveFqn(coupling.target);
|
|
135
136
|
const source = this.resolveFqn(coupling.source);
|
|
136
137
|
const tags = this.convertTags(astNode) ?? this.convertTags(astNode.body);
|
|
138
|
+
const links = astNode.body?.props.filter(ast.isLinkProperty).map((p) => p.value);
|
|
137
139
|
const kind = astNode.kind?.ref?.name;
|
|
138
140
|
const astPath = this.getAstNodePath(astNode);
|
|
139
141
|
const title = toSingleLine(
|
|
@@ -153,6 +155,7 @@ export class LikeC4ModelParser {
|
|
|
153
155
|
title,
|
|
154
156
|
...kind && { kind },
|
|
155
157
|
...tags && { tags },
|
|
158
|
+
...isNonEmptyArray(links) && { links },
|
|
156
159
|
...toRelationshipStyleExcludeDefaults(styleProp?.props)
|
|
157
160
|
};
|
|
158
161
|
}
|
|
@@ -372,6 +375,6 @@ export class LikeC4ModelParser {
|
|
|
372
375
|
return null;
|
|
373
376
|
}
|
|
374
377
|
const tags = withTags.tags?.value.flatMap(({ ref }) => ref ? ref.name : []);
|
|
375
|
-
return
|
|
378
|
+
return isNonEmptyArray(tags) ? tags : null;
|
|
376
379
|
}
|
|
377
380
|
}
|
package/dist/module.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Module, WorkspaceCache } from 'langium';
|
|
2
|
+
import { type DefaultSharedModuleContext, type LangiumServices, type LangiumSharedServices, type PartialLangiumServices } from 'langium/lsp';
|
|
2
3
|
import { LikeC4CodeLensProvider, LikeC4DocumentHighlightProvider, LikeC4DocumentLinkProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
|
|
3
4
|
import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
|
|
4
5
|
import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references';
|
package/dist/module.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { normalizeError } from "@likec4/core";
|
|
2
|
+
import { EmptyFileSystem, inject, WorkspaceCache } from "langium";
|
|
2
3
|
import {
|
|
3
4
|
createDefaultModule,
|
|
4
|
-
createDefaultSharedModule
|
|
5
|
-
|
|
6
|
-
inject,
|
|
7
|
-
WorkspaceCache
|
|
8
|
-
} from "langium";
|
|
5
|
+
createDefaultSharedModule
|
|
6
|
+
} from "langium/lsp";
|
|
9
7
|
import { LikeC4GeneratedModule, LikeC4GeneratedSharedModule } from "./generated/module.js";
|
|
10
8
|
import { logger } from "./logger.js";
|
|
11
9
|
import {
|
package/dist/node/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { startLanguageServer as startLanguim } from "langium";
|
|
1
|
+
import { startLanguageServer as startLanguim } from "langium/lsp";
|
|
2
2
|
import { NodeFileSystem } from "langium/node";
|
|
3
3
|
import { createConnection, ProposedFeatures } from "vscode-languageserver/node";
|
|
4
4
|
import { createLanguageServices } from "../module.js";
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
AstUtils,
|
|
3
|
+
CstUtils,
|
|
3
4
|
DefaultScopeProvider,
|
|
5
|
+
DONE_RESULT,
|
|
4
6
|
EMPTY_STREAM,
|
|
5
|
-
|
|
6
|
-
StreamScope,
|
|
7
|
-
findNodeForProperty,
|
|
8
|
-
getDocument,
|
|
7
|
+
GrammarUtils,
|
|
9
8
|
stream,
|
|
10
|
-
|
|
9
|
+
StreamImpl,
|
|
10
|
+
StreamScope
|
|
11
11
|
} from "langium";
|
|
12
12
|
import { ast } from "../ast.js";
|
|
13
13
|
import { elementRef, getFqnElementRef } from "../elementRef.js";
|
|
14
14
|
import { logError } from "../logger.js";
|
|
15
|
+
const { findNodeForProperty } = GrammarUtils;
|
|
16
|
+
const { toDocumentSegment } = CstUtils;
|
|
17
|
+
const { getDocument } = AstUtils;
|
|
15
18
|
function toAstNodeDescription(entry) {
|
|
16
19
|
const $cstNode = findNodeForProperty(entry.el.$cstNode, "name");
|
|
17
20
|
return {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type AstNode, type AstNodeDescription
|
|
1
|
+
import { type AstNode, type AstNodeDescription } from 'langium';
|
|
2
|
+
import type { LangiumSharedServices, NodeKindProvider as LspNodeKindProvider } from 'langium/lsp';
|
|
2
3
|
import { CompletionItemKind, SymbolKind } from 'vscode-languageserver-protocol';
|
|
3
|
-
export declare class NodeKindProvider implements
|
|
4
|
+
export declare class NodeKindProvider implements LspNodeKindProvider {
|
|
4
5
|
private services;
|
|
5
6
|
constructor(services: LangiumSharedServices);
|
|
6
7
|
/**
|
|
@@ -15,6 +15,7 @@ export declare function createTestServices(workspace?: string): {
|
|
|
15
15
|
warnings: string[];
|
|
16
16
|
}>;
|
|
17
17
|
buildModel: () => Promise<import("@likec4/core").LikeC4Model>;
|
|
18
|
+
resetState: () => Promise<void>;
|
|
18
19
|
};
|
|
19
20
|
export type TestServices = ReturnType<typeof createTestServices>;
|
|
20
21
|
export type TestParseFn = TestServices['validate'];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmptyFileSystem } from "langium";
|
|
1
|
+
import { DocumentState, EmptyFileSystem } from "langium";
|
|
2
2
|
import * as assert from "node:assert";
|
|
3
3
|
import stripIndent from "strip-indent";
|
|
4
4
|
import { DiagnosticSeverity } from "vscode-languageserver-protocol";
|
|
@@ -19,10 +19,15 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
19
19
|
let documentIndex = 1;
|
|
20
20
|
const parse = async (input, uri) => {
|
|
21
21
|
if (!isInitialized) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
23
|
+
if (isInitialized) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
isInitialized = true;
|
|
27
|
+
await services.shared.workspace.WorkspaceManager.initializeWorkspace([workspaceFolder]);
|
|
28
|
+
Object.assign(services.shared.workspace.WorkspaceManager, {
|
|
29
|
+
folders: [workspaceFolder]
|
|
30
|
+
});
|
|
26
31
|
});
|
|
27
32
|
}
|
|
28
33
|
const docUri = Utils.resolvePath(
|
|
@@ -35,12 +40,16 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
35
40
|
docUri
|
|
36
41
|
);
|
|
37
42
|
langiumDocuments.addDocument(document);
|
|
38
|
-
await
|
|
43
|
+
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
44
|
+
await documentBuilder.build([document], { validation: false });
|
|
45
|
+
});
|
|
39
46
|
return document;
|
|
40
47
|
};
|
|
41
48
|
const validate = async (input, uri) => {
|
|
42
49
|
const document = typeof input === "string" ? await parse(input, uri) : input;
|
|
43
|
-
await
|
|
50
|
+
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
51
|
+
await documentBuilder.build([document], { validation: true });
|
|
52
|
+
});
|
|
44
53
|
const diagnostics = document.diagnostics ?? [];
|
|
45
54
|
const warnings = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Warning ? d.message : []);
|
|
46
55
|
const errors = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Error ? d.message : []);
|
|
@@ -53,41 +62,41 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
53
62
|
};
|
|
54
63
|
let previousPromise = Promise.resolve();
|
|
55
64
|
const validateAll = async () => {
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
await documentBuilder.build(docs, { validation: true });
|
|
60
|
-
const diagnostics = docs.flatMap((doc) => doc.diagnostics ?? []);
|
|
61
|
-
const warnings = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Warning ? d.message : []);
|
|
62
|
-
const errors = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Error ? d.message : []);
|
|
63
|
-
return {
|
|
64
|
-
diagnostics,
|
|
65
|
-
errors,
|
|
66
|
-
warnings
|
|
67
|
-
};
|
|
68
|
-
}).catch((e) => {
|
|
69
|
-
console.error(e);
|
|
70
|
-
return Promise.resolve({
|
|
71
|
-
diagnostics: [],
|
|
72
|
-
errors: [],
|
|
73
|
-
warnings: []
|
|
74
|
-
});
|
|
65
|
+
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
66
|
+
const docs2 = langiumDocuments.all.toArray();
|
|
67
|
+
await documentBuilder.build(docs2, { validation: true });
|
|
75
68
|
});
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
await documentBuilder.waitUntil(DocumentState.Validated);
|
|
70
|
+
const docs = langiumDocuments.all.toArray();
|
|
71
|
+
assert.ok(docs.length > 0, "no documents to validate");
|
|
72
|
+
const diagnostics = docs.flatMap((doc) => doc.diagnostics ?? []);
|
|
73
|
+
const warnings = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Warning ? d.message : []);
|
|
74
|
+
const errors = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Error ? d.message : []);
|
|
75
|
+
return {
|
|
76
|
+
diagnostics,
|
|
77
|
+
errors,
|
|
78
|
+
warnings
|
|
79
|
+
};
|
|
78
80
|
};
|
|
79
81
|
const buildModel = async () => {
|
|
80
82
|
await validateAll();
|
|
81
|
-
const model = modelBuilder.buildModel();
|
|
83
|
+
const model = await modelBuilder.buildModel();
|
|
82
84
|
if (!model)
|
|
83
85
|
throw new Error("No model found");
|
|
84
86
|
return model;
|
|
85
87
|
};
|
|
88
|
+
const resetState = async () => {
|
|
89
|
+
await services.shared.workspace.WorkspaceLock.write(async (cancelToken) => {
|
|
90
|
+
const docs = langiumDocuments.all.toArray().map((doc) => doc.uri);
|
|
91
|
+
await documentBuilder.update([], docs, cancelToken);
|
|
92
|
+
});
|
|
93
|
+
};
|
|
86
94
|
return {
|
|
87
95
|
services,
|
|
88
96
|
parse,
|
|
89
97
|
validate,
|
|
90
98
|
validateAll,
|
|
91
|
-
buildModel
|
|
99
|
+
buildModel,
|
|
100
|
+
resetState
|
|
92
101
|
};
|
|
93
102
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AstUtils } from "langium";
|
|
2
|
+
const { getDocument } = AstUtils;
|
|
2
3
|
export const elementChecks = (services) => {
|
|
3
4
|
const fqnIndex = services.likec4.FqnIndex;
|
|
4
5
|
return (el, accept) => {
|
|
@@ -12,21 +13,24 @@ export const elementChecks = (services) => {
|
|
|
12
13
|
}
|
|
13
14
|
const withSameFqn = fqnIndex.byFqn(fqn).filter((v) => v.el !== el).head();
|
|
14
15
|
if (withSameFqn) {
|
|
16
|
+
const isAnotherDoc = withSameFqn.doc.uri !== getDocument(el).uri;
|
|
15
17
|
accept(
|
|
16
18
|
"error",
|
|
17
19
|
`Duplicate element name ${el.name !== fqn ? el.name + " (" + fqn + ")" : el.name}`,
|
|
18
20
|
{
|
|
19
21
|
node: el,
|
|
20
22
|
property: "name",
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
...isAnotherDoc && {
|
|
24
|
+
relatedInformation: [
|
|
25
|
+
{
|
|
26
|
+
location: {
|
|
27
|
+
range: withSameFqn.el.$cstNode.range,
|
|
28
|
+
uri: withSameFqn.doc.uri.toString()
|
|
29
|
+
},
|
|
30
|
+
message: `conflicting element`
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
);
|
|
32
36
|
}
|
package/dist/validation/index.js
CHANGED
|
@@ -10,35 +10,29 @@ import {
|
|
|
10
10
|
tagChecks
|
|
11
11
|
} from "./specification.js";
|
|
12
12
|
import { viewChecks } from "./view.js";
|
|
13
|
-
import {
|
|
14
|
-
customElementExprChecks,
|
|
15
|
-
incomingExpressionChecks,
|
|
16
|
-
outgoingExpressionChecks
|
|
17
|
-
} from "./view-predicates/index.js";
|
|
13
|
+
import { customElementExprChecks, incomingExpressionChecks, outgoingExpressionChecks } from "./view-predicates/index.js";
|
|
18
14
|
export function registerValidationChecks(services) {
|
|
19
15
|
logger.info("registerValidationChecks");
|
|
20
16
|
const registry = services.validation.ValidationRegistry;
|
|
21
|
-
registry.register(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
"slow"
|
|
37
|
-
);
|
|
17
|
+
registry.register({
|
|
18
|
+
SpecificationRule: specificationRuleChecks(services),
|
|
19
|
+
Model: modelRuleChecks(services),
|
|
20
|
+
ModelViews: modelViewsChecks(services),
|
|
21
|
+
ElementView: viewChecks(services),
|
|
22
|
+
Element: elementChecks(services),
|
|
23
|
+
ElementKind: elementKindChecks(services),
|
|
24
|
+
Relation: relationChecks(services),
|
|
25
|
+
Tag: tagChecks(services),
|
|
26
|
+
CustomElementExpr: customElementExprChecks(services),
|
|
27
|
+
RelationshipKind: relationshipChecks(services),
|
|
28
|
+
IncomingExpr: incomingExpressionChecks(services),
|
|
29
|
+
OutgoingExpr: outgoingExpressionChecks(services)
|
|
30
|
+
});
|
|
38
31
|
const connection = services.shared.lsp.Connection;
|
|
39
32
|
if (connection) {
|
|
40
33
|
services.shared.workspace.DocumentBuilder.onUpdate((_, deleted) => {
|
|
41
34
|
for (const uri of deleted) {
|
|
35
|
+
logger.debug(`clear diagnostics for deleted ${uri.path}`);
|
|
42
36
|
void connection.sendDiagnostics({
|
|
43
37
|
uri: uri.toString(),
|
|
44
38
|
diagnostics: []
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ValidationCheck } from 'langium';
|
|
2
2
|
import { ast } from '../ast';
|
|
3
3
|
import type { LikeC4Services } from '../module';
|
|
4
4
|
export declare const specificationRuleChecks: (_: LikeC4Services) => ValidationCheck<ast.SpecificationRule>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AstUtils } from "langium";
|
|
1
2
|
import { ast } from "../ast.js";
|
|
2
3
|
export const specificationRuleChecks = (_) => {
|
|
3
4
|
return (node, accept) => {
|
|
@@ -32,11 +33,23 @@ export const modelViewsChecks = (_) => {
|
|
|
32
33
|
export const elementKindChecks = (services) => {
|
|
33
34
|
const index = services.shared.workspace.IndexManager;
|
|
34
35
|
return (node, accept) => {
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
36
|
+
const sameKind = index.allElements(ast.ElementKind).filter((n) => n.name === node.name && n.node !== node).head();
|
|
37
|
+
if (sameKind) {
|
|
38
|
+
const isAnotherDoc = sameKind.documentUri !== AstUtils.getDocument(node).uri;
|
|
37
39
|
accept("error", `Duplicate element kind '${node.name}'`, {
|
|
38
40
|
node,
|
|
39
|
-
property: "name"
|
|
41
|
+
property: "name",
|
|
42
|
+
...isAnotherDoc && {
|
|
43
|
+
relatedInformation: [
|
|
44
|
+
{
|
|
45
|
+
location: {
|
|
46
|
+
range: sameKind.nameSegment.range,
|
|
47
|
+
uri: sameKind.documentUri.toString()
|
|
48
|
+
},
|
|
49
|
+
message: `conflicting definition`
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
40
53
|
});
|
|
41
54
|
}
|
|
42
55
|
};
|
|
@@ -45,12 +58,28 @@ export const tagChecks = (services) => {
|
|
|
45
58
|
const index = services.shared.workspace.IndexManager;
|
|
46
59
|
return (node, accept) => {
|
|
47
60
|
const tagname = "#" + node.name;
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
const sameTag = index.allElements(ast.Tag).filter((n) => n.name === tagname && n.node !== node).head();
|
|
62
|
+
if (sameTag) {
|
|
63
|
+
const isAnotherDoc = sameTag.documentUri !== AstUtils.getDocument(node).uri;
|
|
64
|
+
accept(
|
|
65
|
+
"error",
|
|
66
|
+
`Duplicate tag '${node.name}'`,
|
|
67
|
+
{
|
|
68
|
+
node,
|
|
69
|
+
property: "name",
|
|
70
|
+
...isAnotherDoc && {
|
|
71
|
+
relatedInformation: [
|
|
72
|
+
{
|
|
73
|
+
location: {
|
|
74
|
+
range: sameTag.nameSegment.range,
|
|
75
|
+
uri: sameTag.documentUri.toString()
|
|
76
|
+
},
|
|
77
|
+
message: `conflicting definition`
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
);
|
|
54
83
|
}
|
|
55
84
|
};
|
|
56
85
|
};
|
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.60.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -66,32 +66,32 @@
|
|
|
66
66
|
"test": "vitest run"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@likec4/core": "0.
|
|
70
|
-
"@likec4/graph": "0.
|
|
69
|
+
"@likec4/core": "0.60.0",
|
|
70
|
+
"@likec4/graph": "0.60.0",
|
|
71
71
|
"debounce-fn": "^6.0.0",
|
|
72
|
-
"langium": "^
|
|
72
|
+
"langium": "^3.0.0",
|
|
73
73
|
"object-hash": "^3.0.0",
|
|
74
74
|
"p-debounce": "^4.0.0",
|
|
75
75
|
"rambdax": "^9.1.1",
|
|
76
76
|
"remeda": "^1.40.1",
|
|
77
77
|
"strip-indent": "^4.0.0",
|
|
78
|
-
"type-fest": "^4.10.
|
|
78
|
+
"type-fest": "^4.10.3",
|
|
79
79
|
"ufo": "^1.3.2",
|
|
80
80
|
"vscode-languageserver": "9.0.1",
|
|
81
81
|
"vscode-languageserver-protocol": "3.17.5",
|
|
82
82
|
"vscode-uri": "3.0.8"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@types/node": "^20.11.
|
|
85
|
+
"@types/node": "^20.11.19",
|
|
86
86
|
"@types/object-hash": "^3.0.6",
|
|
87
87
|
"execa": "^8.0.1",
|
|
88
|
-
"langium-cli": "^
|
|
88
|
+
"langium-cli": "^3.0.1",
|
|
89
89
|
"npm-run-all2": "^5.0.2",
|
|
90
90
|
"typescript": "^5.3.3",
|
|
91
91
|
"unbuild": "^2.0.0",
|
|
92
|
-
"vitest": "^1.
|
|
92
|
+
"vitest": "^1.3.1"
|
|
93
93
|
},
|
|
94
|
-
"packageManager": "yarn@4.1.
|
|
94
|
+
"packageManager": "yarn@4.1.1",
|
|
95
95
|
"volta": {
|
|
96
96
|
"extends": "../../package.json"
|
|
97
97
|
}
|