@likec4/language-server 1.25.1 → 1.26.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/LikeC4FileSystem.js +6 -1
- package/dist/LikeC4LanguageServices.d.ts +77 -0
- package/dist/LikeC4LanguageServices.js +118 -0
- package/dist/Rpc.js +106 -28
- package/dist/ast.d.ts +10 -0
- package/dist/bundled.mjs +2352 -2278
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/lsp/CodeLensProvider.js +3 -1
- package/dist/model/builder/buildModel.d.ts +8 -1
- package/dist/model/deployments-index.js +1 -1
- package/dist/model/fqn-index.d.ts +9 -6
- package/dist/model/fqn-index.js +24 -14
- package/dist/model/model-builder.d.ts +16 -6
- package/dist/model/model-builder.js +64 -50
- package/dist/model/model-locator.d.ts +11 -10
- package/dist/model/model-locator.js +32 -14
- package/dist/model/model-parser.d.ts +148 -148
- package/dist/model/model-parser.js +2 -2
- package/dist/model/parser/ModelParser.js +14 -2
- package/dist/model-change/ModelChanges.d.ts +1 -1
- package/dist/model-change/ModelChanges.js +2 -2
- package/dist/module.d.ts +9 -3
- package/dist/module.js +19 -5
- package/dist/protocol.d.ts +98 -16
- package/dist/protocol.js +21 -6
- package/dist/references/scope-provider.d.ts +24 -11
- package/dist/references/scope-provider.js +97 -68
- package/dist/shared/index.d.ts +0 -1
- package/dist/shared/index.js +0 -1
- package/dist/test/testServices.d.ts +15 -1
- package/dist/test/testServices.js +65 -17
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/projectId.d.ts +3 -0
- package/dist/utils/projectId.js +6 -0
- package/dist/validation/deployment-checks.js +5 -2
- package/dist/validation/element.js +2 -1
- package/dist/validation/specification.js +14 -7
- package/dist/validation/view.js +10 -8
- package/dist/views/index.d.ts +1 -1
- package/dist/views/index.js +1 -1
- package/dist/views/likec4-views.d.ts +17 -8
- package/dist/views/likec4-views.js +12 -11
- package/dist/workspace/AstNodeDescriptionProvider.d.ts +7 -0
- package/dist/workspace/AstNodeDescriptionProvider.js +18 -0
- package/dist/workspace/IndexManager.d.ts +10 -0
- package/dist/workspace/IndexManager.js +17 -0
- package/dist/workspace/LangiumDocuments.d.ts +14 -0
- package/dist/workspace/LangiumDocuments.js +31 -0
- package/dist/workspace/ProjectsManager.d.ts +48 -0
- package/dist/workspace/ProjectsManager.js +150 -0
- package/dist/{shared → workspace}/WorkspaceManager.d.ts +8 -2
- package/dist/{shared → workspace}/WorkspaceManager.js +22 -0
- package/dist/workspace/index.d.ts +5 -0
- package/dist/workspace/index.js +5 -0
- package/package.json +17 -8
|
@@ -14,11 +14,25 @@ export declare function createTestServices(workspace?: string): {
|
|
|
14
14
|
errors: any;
|
|
15
15
|
warnings: any;
|
|
16
16
|
}>;
|
|
17
|
-
buildModel: () => Promise<
|
|
17
|
+
buildModel: () => Promise<ComputedLikeC4ModelData>;
|
|
18
18
|
buildLikeC4Model: () => Promise<any>;
|
|
19
19
|
resetState: () => Promise<void>;
|
|
20
20
|
format: (input: string | LikeC4LangiumDocument, uri?: string) => Promise<any>;
|
|
21
21
|
};
|
|
22
|
+
export declare function createMultiProjectTestServices<const Projects extends Record<string, Record<string, string>>>(data: Projects): Promise<{
|
|
23
|
+
projects: { readonly [K in keyof Projects]: { readonly [L in keyof Projects[K]]: LikeC4LangiumDocument; }; };
|
|
24
|
+
/**
|
|
25
|
+
* Add document outside of projects
|
|
26
|
+
*/
|
|
27
|
+
addDocumentOutside: (input: string) => Promise<LikeC4LangiumDocument>;
|
|
28
|
+
validateAll: () => Promise<{
|
|
29
|
+
diagnostics: any;
|
|
30
|
+
errors: any;
|
|
31
|
+
warnings: any;
|
|
32
|
+
}>;
|
|
33
|
+
buildModel: (projectId: keyof Projects) => Promise<ComputedLikeC4ModelData>;
|
|
34
|
+
buildLikeC4Model: (projectId: keyof Projects) => Promise<any>;
|
|
35
|
+
}>;
|
|
22
36
|
export type TestServices = ReturnType<typeof createTestServices>;
|
|
23
37
|
export type TestParseFn = TestServices['validate'];
|
|
24
38
|
export type TestValidateFn = TestServices['validate'];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { DocumentState, EmptyFileSystem, TextDocument } from "langium";
|
|
1
|
+
import { DocumentState, EmptyFileSystem, TextDocument, UriUtils } from "langium";
|
|
2
2
|
import * as assert from "node:assert";
|
|
3
|
+
import { entries } from "remeda";
|
|
3
4
|
import stripIndent from "strip-indent";
|
|
4
5
|
import { DiagnosticSeverity } from "vscode-languageserver-types";
|
|
5
6
|
import { URI, Utils } from "vscode-uri";
|
|
@@ -18,19 +19,21 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
18
19
|
};
|
|
19
20
|
let isInitialized = false;
|
|
20
21
|
let documentIndex = 1;
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
await services.shared.workspace.WorkspaceManager.initializeWorkspace([workspaceFolder]);
|
|
22
|
+
async function initialize() {
|
|
23
|
+
if (isInitialized) return;
|
|
24
|
+
isInitialized = true;
|
|
25
|
+
await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
|
|
26
|
+
services.shared.workspace.WorkspaceManager.initialize({
|
|
27
|
+
capabilities: {},
|
|
28
|
+
processId: null,
|
|
29
|
+
rootUri: workspaceFolder.uri,
|
|
30
|
+
workspaceFolders: [workspaceFolder]
|
|
32
31
|
});
|
|
33
|
-
|
|
32
|
+
await services.shared.workspace.WorkspaceManager.initializeWorkspace([workspaceFolder]);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const addDocument = async (input, uri) => {
|
|
36
|
+
await initialize();
|
|
34
37
|
const docUri = Utils.resolvePath(
|
|
35
38
|
workspaceUri,
|
|
36
39
|
"./src/",
|
|
@@ -82,9 +85,7 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
82
85
|
const validateAll = async () => {
|
|
83
86
|
const docs = langiumDocuments.all.toArray();
|
|
84
87
|
assert.ok(docs.length > 0, "no documents to validate");
|
|
85
|
-
await
|
|
86
|
-
await documentBuilder.build(docs, { validation: true }, cancelToken);
|
|
87
|
-
});
|
|
88
|
+
await documentBuilder.build(docs, { validation: true });
|
|
88
89
|
const diagnostics = docs.flatMap((doc) => doc.diagnostics ?? []);
|
|
89
90
|
const warnings = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Warning ? d.message : []);
|
|
90
91
|
const errors = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Error ? d.message : []);
|
|
@@ -112,7 +113,7 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
112
113
|
};
|
|
113
114
|
const resetState = async () => {
|
|
114
115
|
await services.shared.workspace.WorkspaceLock.write(async (cancelToken) => {
|
|
115
|
-
const docs = langiumDocuments.
|
|
116
|
+
const docs = langiumDocuments.allExcludingBuiltin.toArray().map((doc) => doc.uri);
|
|
116
117
|
await documentBuilder.update([], docs, cancelToken);
|
|
117
118
|
});
|
|
118
119
|
};
|
|
@@ -128,3 +129,50 @@ export function createTestServices(workspace = "file:///test/workspace") {
|
|
|
128
129
|
format
|
|
129
130
|
};
|
|
130
131
|
}
|
|
132
|
+
export async function createMultiProjectTestServices(data) {
|
|
133
|
+
const workspace = "file:///test/workspace";
|
|
134
|
+
const {
|
|
135
|
+
services,
|
|
136
|
+
addDocument,
|
|
137
|
+
validateAll
|
|
138
|
+
} = createTestServices(workspace);
|
|
139
|
+
const projects = {};
|
|
140
|
+
for (const [name, files] of entries(data)) {
|
|
141
|
+
const folderUri = UriUtils.joinPath(URI.parse(workspace), "src", name);
|
|
142
|
+
services.shared.workspace.ProjectsManager.registerProject({
|
|
143
|
+
config: {
|
|
144
|
+
name
|
|
145
|
+
},
|
|
146
|
+
folderUri
|
|
147
|
+
});
|
|
148
|
+
projects[name] = {};
|
|
149
|
+
for (let [docName, content] of entries(files)) {
|
|
150
|
+
const fileName = docName.endsWith(".c4") ? docName : `${docName}.c4`;
|
|
151
|
+
projects[name][docName] = await addDocument(content, `${name}/${fileName}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async function buildLikeC4Model(projectId) {
|
|
155
|
+
if (services.shared.workspace.LangiumDocuments.all.some((doc) => doc.state < DocumentState.Validated)) {
|
|
156
|
+
await validateAll();
|
|
157
|
+
}
|
|
158
|
+
const likec4model = await services.likec4.ModelBuilder.buildLikeC4Model(projectId);
|
|
159
|
+
if (!likec4model) throw new Error("No model found");
|
|
160
|
+
return likec4model;
|
|
161
|
+
}
|
|
162
|
+
async function buildModel(projectId) {
|
|
163
|
+
const model = await buildLikeC4Model(projectId);
|
|
164
|
+
return model.$model;
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
projects,
|
|
168
|
+
/**
|
|
169
|
+
* Add document outside of projects
|
|
170
|
+
*/
|
|
171
|
+
addDocumentOutside: async (input) => {
|
|
172
|
+
return await addDocument(input);
|
|
173
|
+
},
|
|
174
|
+
validateAll,
|
|
175
|
+
buildModel,
|
|
176
|
+
buildLikeC4Model
|
|
177
|
+
};
|
|
178
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { nonNullable } from "@likec4/core";
|
|
2
|
+
import { AstUtils, isAstNode } from "langium";
|
|
3
|
+
export function projectIdFrom(value) {
|
|
4
|
+
const doc = isAstNode(value) ? AstUtils.getDocument(value) : value;
|
|
5
|
+
return nonNullable(doc.likec4ProjectId, () => `Invalid state, document ${doc.uri} has no project ID assigned`);
|
|
6
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FqnRef, isSameHierarchy, nonNullable } from "@likec4/core";
|
|
2
2
|
import { AstUtils } from "langium";
|
|
3
3
|
import { ast } from "../ast.js";
|
|
4
|
+
import { projectIdFrom } from "../utils/index.js";
|
|
4
5
|
import { RESERVED_WORDS, tryOrLog } from "./_shared.js";
|
|
5
6
|
const { getDocument } = AstUtils;
|
|
6
7
|
export const deploymentNodeChecks = (services) => {
|
|
@@ -21,8 +22,9 @@ export const deploymentNodeChecks = (services) => {
|
|
|
21
22
|
range
|
|
22
23
|
});
|
|
23
24
|
}
|
|
25
|
+
const projectId = projectIdFrom(el);
|
|
24
26
|
const fqnName = DeploymentsIndex.getFqn(el);
|
|
25
|
-
const withSameName = DeploymentsIndex.byFqn(fqnName).limit(2).toArray();
|
|
27
|
+
const withSameName = DeploymentsIndex.byFqn(projectId, fqnName).limit(2).toArray();
|
|
26
28
|
if (withSameName.length > 1) {
|
|
27
29
|
accept(
|
|
28
30
|
"error",
|
|
@@ -53,8 +55,9 @@ export const deployedInstanceChecks = (services) => {
|
|
|
53
55
|
range
|
|
54
56
|
});
|
|
55
57
|
}
|
|
58
|
+
const projectId = projectIdFrom(el);
|
|
56
59
|
const fqnName = DeploymentsIndex.getFqn(el);
|
|
57
|
-
const withSameName = DeploymentsIndex.byFqn(fqnName).limit(2).toArray();
|
|
60
|
+
const withSameName = DeploymentsIndex.byFqn(projectId, fqnName).limit(2).toArray();
|
|
58
61
|
if (withSameName.length > 1) {
|
|
59
62
|
accept(
|
|
60
63
|
"error",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AstUtils } from "langium";
|
|
2
|
+
import { projectIdFrom } from "../utils/index.js";
|
|
2
3
|
import { RESERVED_WORDS, tryOrLog } from "./_shared.js";
|
|
3
4
|
const { getDocument } = AstUtils;
|
|
4
5
|
export const elementChecks = (services) => {
|
|
@@ -22,7 +23,7 @@ export const elementChecks = (services) => {
|
|
|
22
23
|
const doc = getDocument(el);
|
|
23
24
|
const docUri = doc.uri;
|
|
24
25
|
const elPath = locator.getAstNodePath(el);
|
|
25
|
-
const withSameFqn = fqnIndex.byFqn(fqn).filter((v) => v.documentUri !== docUri || v.path !== elPath).head();
|
|
26
|
+
const withSameFqn = fqnIndex.byFqn(projectIdFrom(doc), fqn).filter((v) => v.documentUri !== docUri || v.path !== elPath).head();
|
|
26
27
|
if (withSameFqn) {
|
|
27
28
|
const isAnotherDoc = withSameFqn.documentUri !== docUri;
|
|
28
29
|
accept(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AstUtils } from "langium";
|
|
2
2
|
import { ast } from "../ast.js";
|
|
3
|
+
import { projectIdFrom } from "../utils/index.js";
|
|
3
4
|
import { RESERVED_WORDS, tryOrLog } from "./_shared.js";
|
|
4
5
|
export const specificationRuleChecks = (_) => {
|
|
5
6
|
return tryOrLog((node, accept) => {
|
|
@@ -40,7 +41,8 @@ export const elementKindChecks = (services) => {
|
|
|
40
41
|
property: "name"
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
|
-
const
|
|
44
|
+
const projectId = projectIdFrom(node);
|
|
45
|
+
const sameKind = index.projectElements(projectId, ast.ElementKind).filter((n) => n.name === node.name && n.node !== node).head();
|
|
44
46
|
if (sameKind) {
|
|
45
47
|
const isAnotherDoc = sameKind.documentUri !== AstUtils.getDocument(node).uri;
|
|
46
48
|
accept("error", `Duplicate element kind '${node.name}'`, {
|
|
@@ -70,7 +72,8 @@ export const deploymentNodeKindChecks = (services) => {
|
|
|
70
72
|
property: "name"
|
|
71
73
|
});
|
|
72
74
|
}
|
|
73
|
-
const
|
|
75
|
+
const projectId = projectIdFrom(node);
|
|
76
|
+
const sameKind = index.projectElements(projectId, ast.DeploymentNodeKind).filter((n) => n.name === node.name && n.node !== node).head();
|
|
74
77
|
if (sameKind) {
|
|
75
78
|
const isAnotherDoc = sameKind.documentUri !== AstUtils.getDocument(node).uri;
|
|
76
79
|
accept("error", `Duplicate deploymentNode kind '${node.name}'`, {
|
|
@@ -95,7 +98,8 @@ export const tagChecks = (services) => {
|
|
|
95
98
|
const index = services.shared.workspace.IndexManager;
|
|
96
99
|
return tryOrLog((node, accept) => {
|
|
97
100
|
const tagname = "#" + node.name;
|
|
98
|
-
const
|
|
101
|
+
const projectId = projectIdFrom(node);
|
|
102
|
+
const sameTag = index.projectElements(projectId, ast.Tag).filter((n) => n.name === tagname && n.node !== node).head();
|
|
99
103
|
if (sameTag) {
|
|
100
104
|
const isAnotherDoc = sameTag.documentUri !== AstUtils.getDocument(node).uri;
|
|
101
105
|
accept(
|
|
@@ -129,7 +133,8 @@ export const relationshipChecks = (services) => {
|
|
|
129
133
|
property: "name"
|
|
130
134
|
});
|
|
131
135
|
}
|
|
132
|
-
const
|
|
136
|
+
const projectId = projectIdFrom(node);
|
|
137
|
+
const sameKinds = index.projectElements(projectId, ast.RelationshipKind).filter((n) => n.name === node.name).limit(2).count();
|
|
133
138
|
if (sameKinds > 1) {
|
|
134
139
|
accept("error", `Duplicate RelationshipKind '${node.name}'`, {
|
|
135
140
|
node,
|
|
@@ -141,8 +146,9 @@ export const relationshipChecks = (services) => {
|
|
|
141
146
|
export const globalPredicateChecks = (services) => {
|
|
142
147
|
const index = services.shared.workspace.IndexManager;
|
|
143
148
|
return tryOrLog((node, accept) => {
|
|
144
|
-
const
|
|
145
|
-
const
|
|
149
|
+
const projectId = projectIdFrom(node);
|
|
150
|
+
const predicateGroups = index.projectElements(projectId, ast.GlobalPredicateGroup);
|
|
151
|
+
const dynamicPredicateGroups = index.projectElements(projectId, ast.GlobalDynamicPredicateGroup);
|
|
146
152
|
const sameName = predicateGroups.concat(dynamicPredicateGroups).filter((s) => s.name === node.name).limit(2).count();
|
|
147
153
|
if (sameName > 1) {
|
|
148
154
|
accept("error", `Duplicate GlobalPredicateGroup or GlobalDynamicPredicateGroup name '${node.name}'`, {
|
|
@@ -155,7 +161,8 @@ export const globalPredicateChecks = (services) => {
|
|
|
155
161
|
export const globalStyleIdChecks = (services) => {
|
|
156
162
|
const index = services.shared.workspace.IndexManager;
|
|
157
163
|
return tryOrLog((node, accept) => {
|
|
158
|
-
const
|
|
164
|
+
const projectId = projectIdFrom(node);
|
|
165
|
+
const sameName = index.projectElements(projectId, ast.GlobalStyleId).filter((s) => s.name === node.name).limit(2).count();
|
|
159
166
|
if (sameName > 1) {
|
|
160
167
|
accept("error", `Duplicate GlobalStyleId name '${node.name}'`, {
|
|
161
168
|
node,
|
package/dist/validation/view.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { ast } from "../ast.js";
|
|
2
|
+
import { projectIdFrom } from "../utils/index.js";
|
|
2
3
|
import { RESERVED_WORDS, tryOrLog } from "./_shared.js";
|
|
3
4
|
export const viewChecks = (services) => {
|
|
4
5
|
const index = services.shared.workspace.IndexManager;
|
|
5
|
-
return tryOrLog((
|
|
6
|
-
if (!
|
|
6
|
+
return tryOrLog((node, accept) => {
|
|
7
|
+
if (!node.name) {
|
|
7
8
|
return;
|
|
8
9
|
}
|
|
9
|
-
if (RESERVED_WORDS.includes(
|
|
10
|
-
accept("error", `Reserved word: ${
|
|
11
|
-
node
|
|
10
|
+
if (RESERVED_WORDS.includes(node.name)) {
|
|
11
|
+
accept("error", `Reserved word: ${node.name}`, {
|
|
12
|
+
node,
|
|
12
13
|
property: "name"
|
|
13
14
|
});
|
|
14
15
|
}
|
|
15
|
-
const
|
|
16
|
+
const projectId = projectIdFrom(node);
|
|
17
|
+
const anotherViews = index.projectElements(projectId, ast.LikeC4View).filter((n) => n.name === node.name).limit(2).count();
|
|
16
18
|
if (anotherViews > 1) {
|
|
17
|
-
accept("error", `Duplicate view '${
|
|
18
|
-
node
|
|
19
|
+
accept("error", `Duplicate view '${node.name}'`, {
|
|
20
|
+
node,
|
|
19
21
|
property: "name"
|
|
20
22
|
});
|
|
21
23
|
}
|
package/dist/views/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { LikeC4Views } from './likec4-views';
|
|
1
|
+
export { DefaultLikeC4Views, type LikeC4Views } from './likec4-views';
|
package/dist/views/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { DefaultLikeC4Views } from "./likec4-views.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ComputedView, DiagramView, OverviewGraph, ViewId } from '@likec4/core';
|
|
1
|
+
import type { ComputedView, DiagramView, OverviewGraph, ProjectId, ViewId } from '@likec4/core';
|
|
2
2
|
import { GraphvizLayouter } from '@likec4/layouts';
|
|
3
|
-
import {
|
|
3
|
+
import { CancellationToken } from 'vscode-jsonrpc';
|
|
4
4
|
import type { LikeC4Services } from '../module';
|
|
5
5
|
export type GraphvizOut = {
|
|
6
6
|
dot: string;
|
|
@@ -11,18 +11,27 @@ type GraphvizSvgOut = {
|
|
|
11
11
|
dot: string;
|
|
12
12
|
svg: string;
|
|
13
13
|
};
|
|
14
|
-
export
|
|
14
|
+
export interface LikeC4Views {
|
|
15
|
+
readonly layouter: GraphvizLayouter;
|
|
16
|
+
computedViews(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<ComputedView[]>;
|
|
17
|
+
layoutAllViews(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<Readonly<GraphvizOut>>>;
|
|
18
|
+
layoutView(viewId: ViewId, projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<GraphvizOut | null>;
|
|
19
|
+
diagrams(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<DiagramView>>;
|
|
20
|
+
viewsAsGraphvizOut(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<GraphvizSvgOut>>;
|
|
21
|
+
overviewGraph(): Promise<OverviewGraph>;
|
|
22
|
+
}
|
|
23
|
+
export declare class DefaultLikeC4Views implements LikeC4Views {
|
|
15
24
|
private services;
|
|
16
25
|
private cache;
|
|
17
26
|
private viewsWithReportedErrors;
|
|
18
27
|
private ModelBuilder;
|
|
19
28
|
constructor(services: LikeC4Services);
|
|
20
29
|
get layouter(): GraphvizLayouter;
|
|
21
|
-
computedViews(cancelToken?:
|
|
22
|
-
layoutAllViews(cancelToken?:
|
|
23
|
-
layoutView(viewId: ViewId, cancelToken?:
|
|
24
|
-
diagrams(): Promise<Array<DiagramView>>;
|
|
25
|
-
viewsAsGraphvizOut(): Promise<Array<GraphvizSvgOut>>;
|
|
30
|
+
computedViews(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<ComputedView[]>;
|
|
31
|
+
layoutAllViews(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<Readonly<GraphvizOut>>>;
|
|
32
|
+
layoutView(viewId: ViewId, projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<GraphvizOut | null>;
|
|
33
|
+
diagrams(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<DiagramView>>;
|
|
34
|
+
viewsAsGraphvizOut(projectId?: ProjectId | undefined, cancelToken?: CancellationToken): Promise<Array<GraphvizSvgOut>>;
|
|
26
35
|
overviewGraph(): Promise<OverviewGraph>;
|
|
27
36
|
}
|
|
28
37
|
export {};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { loggable } from "@likec4/log";
|
|
2
2
|
import prettyMs from "pretty-ms";
|
|
3
3
|
import { values } from "remeda";
|
|
4
|
+
import { CancellationToken } from "vscode-jsonrpc";
|
|
4
5
|
import { logError, logger as rootLogger, logWarnError } from "../logger.js";
|
|
5
6
|
const logger = rootLogger.getChild("Views");
|
|
6
|
-
export class
|
|
7
|
+
export class DefaultLikeC4Views {
|
|
7
8
|
constructor(services) {
|
|
8
9
|
this.services = services;
|
|
9
10
|
this.ModelBuilder = services.likec4.ModelBuilder;
|
|
@@ -14,12 +15,12 @@ export class LikeC4Views {
|
|
|
14
15
|
get layouter() {
|
|
15
16
|
return this.services.likec4.Layouter;
|
|
16
17
|
}
|
|
17
|
-
async computedViews(cancelToken) {
|
|
18
|
-
const likeC4Model = await this.ModelBuilder.buildLikeC4Model(cancelToken);
|
|
18
|
+
async computedViews(projectId, cancelToken = CancellationToken.None) {
|
|
19
|
+
const likeC4Model = await this.ModelBuilder.buildLikeC4Model(projectId, cancelToken);
|
|
19
20
|
return values(likeC4Model.$model.views);
|
|
20
21
|
}
|
|
21
|
-
async layoutAllViews(cancelToken) {
|
|
22
|
-
const views = await this.computedViews(cancelToken);
|
|
22
|
+
async layoutAllViews(projectId, cancelToken = CancellationToken.None) {
|
|
23
|
+
const views = await this.computedViews(projectId, cancelToken);
|
|
23
24
|
if (views.length === 0) {
|
|
24
25
|
return [];
|
|
25
26
|
}
|
|
@@ -52,8 +53,8 @@ export class LikeC4Views {
|
|
|
52
53
|
}
|
|
53
54
|
return results;
|
|
54
55
|
}
|
|
55
|
-
async layoutView(viewId, cancelToken) {
|
|
56
|
-
const model = await this.ModelBuilder.buildLikeC4Model(cancelToken);
|
|
56
|
+
async layoutView(viewId, projectId, cancelToken = CancellationToken.None) {
|
|
57
|
+
const model = await this.ModelBuilder.buildLikeC4Model(projectId, cancelToken);
|
|
57
58
|
const view = model.findView(viewId)?.$view;
|
|
58
59
|
if (!view) {
|
|
59
60
|
logger.warn`layoutView ${viewId} not found`;
|
|
@@ -81,17 +82,17 @@ export class LikeC4Views {
|
|
|
81
82
|
return Promise.reject(e);
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
|
-
async diagrams() {
|
|
85
|
-
const layouted = await this.layoutAllViews();
|
|
85
|
+
async diagrams(projectId, cancelToken = CancellationToken.None) {
|
|
86
|
+
const layouted = await this.layoutAllViews(projectId, cancelToken);
|
|
86
87
|
return layouted.map((l) => l.diagram);
|
|
87
88
|
}
|
|
88
|
-
async viewsAsGraphvizOut() {
|
|
89
|
+
async viewsAsGraphvizOut(projectId, cancelToken = CancellationToken.None) {
|
|
89
90
|
const KEY = "All-LayoutedViews-DotWithSvg";
|
|
90
91
|
const cache = this.services.ValidatedWorkspaceCache;
|
|
91
92
|
if (cache.has(KEY)) {
|
|
92
93
|
return await Promise.resolve(cache.get(KEY));
|
|
93
94
|
}
|
|
94
|
-
const views = await this.computedViews();
|
|
95
|
+
const views = await this.computedViews(projectId, cancelToken);
|
|
95
96
|
const tasks = views.map(async (view) => {
|
|
96
97
|
const { dot, svg } = await this.layouter.svg(view);
|
|
97
98
|
return {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AstNode, type AstNodeDescription, type LangiumDocument, DefaultAstNodeDescriptionProvider } from 'langium';
|
|
2
|
+
import type { LikeC4Services } from '../module';
|
|
3
|
+
export declare class AstNodeDescriptionProvider extends DefaultAstNodeDescriptionProvider {
|
|
4
|
+
private projects;
|
|
5
|
+
constructor(services: LikeC4Services);
|
|
6
|
+
createDescription(node: AstNode, name: string | undefined, document?: LangiumDocument): AstNodeDescription;
|
|
7
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AstUtils,
|
|
3
|
+
DefaultAstNodeDescriptionProvider
|
|
4
|
+
} from "langium";
|
|
5
|
+
export class AstNodeDescriptionProvider extends DefaultAstNodeDescriptionProvider {
|
|
6
|
+
projects;
|
|
7
|
+
constructor(services) {
|
|
8
|
+
super(services);
|
|
9
|
+
this.projects = services.shared.workspace.ProjectsManager;
|
|
10
|
+
}
|
|
11
|
+
createDescription(node, name, document) {
|
|
12
|
+
const doc = document ?? AstUtils.getDocument(node);
|
|
13
|
+
const description = super.createDescription(node, name, document);
|
|
14
|
+
doc.likec4ProjectId ??= this.projects.belongsTo(doc.uri);
|
|
15
|
+
description.likec4ProjectId = doc.likec4ProjectId;
|
|
16
|
+
return description;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProjectId } from '@likec4/core';
|
|
2
|
+
import { type AstNodeDescription, type LangiumDocument, type Stream, DefaultIndexManager } from 'langium';
|
|
3
|
+
import { CancellationToken } from 'vscode-jsonrpc';
|
|
4
|
+
import type { LikeC4SharedServices } from '../module';
|
|
5
|
+
export declare class IndexManager extends DefaultIndexManager {
|
|
6
|
+
private projects;
|
|
7
|
+
constructor(services: LikeC4SharedServices);
|
|
8
|
+
updateContent(document: LangiumDocument, cancelToken?: CancellationToken): Promise<void>;
|
|
9
|
+
projectElements(projectId: ProjectId, nodeType?: string, uris?: Set<string>): Stream<AstNodeDescription>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DefaultIndexManager, stream } from "langium";
|
|
2
|
+
import { CancellationToken } from "vscode-jsonrpc";
|
|
3
|
+
export class IndexManager extends DefaultIndexManager {
|
|
4
|
+
projects;
|
|
5
|
+
constructor(services) {
|
|
6
|
+
super(services);
|
|
7
|
+
this.projects = services.workspace.ProjectsManager;
|
|
8
|
+
}
|
|
9
|
+
async updateContent(document, cancelToken = CancellationToken.None) {
|
|
10
|
+
document.likec4ProjectId = this.projects.belongsTo(document.uri);
|
|
11
|
+
await super.updateContent(document, cancelToken);
|
|
12
|
+
}
|
|
13
|
+
projectElements(projectId, nodeType, uris) {
|
|
14
|
+
let documentUris = stream(this.symbolIndex.keys());
|
|
15
|
+
return documentUris.filter((uri) => this.projects.belongsTo(uri) === projectId && (!uris || uris.has(uri))).flatMap((uri) => this.getFileDescriptions(uri, nodeType));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { NonEmptyArray, ProjectId } from '@likec4/core';
|
|
2
|
+
import { type Stream, DefaultLangiumDocuments } from 'langium';
|
|
3
|
+
import { type LikeC4LangiumDocument } from '../ast';
|
|
4
|
+
import type { LikeC4SharedServices } from '../module';
|
|
5
|
+
export declare class LangiumDocuments extends DefaultLangiumDocuments {
|
|
6
|
+
private projects;
|
|
7
|
+
constructor(services: LikeC4SharedServices);
|
|
8
|
+
/**
|
|
9
|
+
* Returns all user documents, excluding built-in documents.
|
|
10
|
+
*/
|
|
11
|
+
get allExcludingBuiltin(): Stream<LikeC4LangiumDocument>;
|
|
12
|
+
projectDocuments(projectId: ProjectId): Stream<LikeC4LangiumDocument>;
|
|
13
|
+
groupedByProject(): Record<ProjectId, NonEmptyArray<LikeC4LangiumDocument>>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DefaultLangiumDocuments } from "langium";
|
|
2
|
+
import { groupBy, prop } from "remeda";
|
|
3
|
+
import { isLikeC4LangiumDocument } from "../ast.js";
|
|
4
|
+
import { isLikeC4Builtin } from "../likec4lib.js";
|
|
5
|
+
export class LangiumDocuments extends DefaultLangiumDocuments {
|
|
6
|
+
projects;
|
|
7
|
+
constructor(services) {
|
|
8
|
+
super(services);
|
|
9
|
+
this.projects = services.workspace.ProjectsManager;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns all user documents, excluding built-in documents.
|
|
13
|
+
*/
|
|
14
|
+
get allExcludingBuiltin() {
|
|
15
|
+
return super.all.filter((doc) => {
|
|
16
|
+
if (!isLikeC4LangiumDocument(doc) || isLikeC4Builtin(doc.uri)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (!doc.likec4ProjectId) {
|
|
20
|
+
doc.likec4ProjectId = this.projects.belongsTo(doc.uri);
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
projectDocuments(projectId) {
|
|
26
|
+
return this.allExcludingBuiltin.filter((doc) => doc.likec4ProjectId === projectId);
|
|
27
|
+
}
|
|
28
|
+
groupedByProject() {
|
|
29
|
+
return groupBy(this.allExcludingBuiltin.toArray(), prop("likec4ProjectId"));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { NonEmptyReadonlyArray, ProjectId } from '@likec4/core';
|
|
2
|
+
import { type FileSystemNode, type LangiumDocument, URI, WorkspaceCache } from 'langium';
|
|
3
|
+
import { ProjectConfig } from '../config';
|
|
4
|
+
import type { LikeC4SharedServices } from '../module';
|
|
5
|
+
export declare class ProjectsManager {
|
|
6
|
+
protected services: LikeC4SharedServices;
|
|
7
|
+
/**
|
|
8
|
+
* The global project ID used for all documents
|
|
9
|
+
* that are not part of a specific project.
|
|
10
|
+
*/
|
|
11
|
+
static readonly DefaultProjectId: ProjectId;
|
|
12
|
+
static readonly ConfigFileNames: string[];
|
|
13
|
+
/**
|
|
14
|
+
* The mapping between project config files and project IDs.
|
|
15
|
+
*/
|
|
16
|
+
private projectIdToFolder;
|
|
17
|
+
private _mappingsToProject;
|
|
18
|
+
/**
|
|
19
|
+
* Registered projects.
|
|
20
|
+
* Sorted descending by the number of segments in the folder path.
|
|
21
|
+
* This ensures that the most specific project is used for a document.
|
|
22
|
+
*/
|
|
23
|
+
private _projects;
|
|
24
|
+
constructor(services: LikeC4SharedServices);
|
|
25
|
+
get defaultProjectId(): ProjectId | undefined;
|
|
26
|
+
get all(): NonEmptyReadonlyArray<ProjectId>;
|
|
27
|
+
getProject(projectId: ProjectId): {
|
|
28
|
+
folder: URI;
|
|
29
|
+
config: Readonly<ProjectConfig>;
|
|
30
|
+
};
|
|
31
|
+
ensureProjectId(projectId?: ProjectId | undefined): ProjectId;
|
|
32
|
+
hasMultipleProjects(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Checks if the provided file system entry is a valid project config file.
|
|
35
|
+
*
|
|
36
|
+
* @param entry The file system entry to check
|
|
37
|
+
* @returns {boolean} Returns true if the entry is a valid config file, false otherwise.
|
|
38
|
+
*/
|
|
39
|
+
loadConfigFile(entry: FileSystemNode): Promise<boolean>;
|
|
40
|
+
registerProject(configFile: URI): Promise<void>;
|
|
41
|
+
registerProject(opts: {
|
|
42
|
+
config: ProjectConfig;
|
|
43
|
+
folderUri: URI | string;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
belongsTo(document: LangiumDocument | URI | string): ProjectId;
|
|
46
|
+
private getProjectId;
|
|
47
|
+
protected get mappingsToProject(): WorkspaceCache<string, ProjectId>;
|
|
48
|
+
}
|