@likec4/language-server 1.27.2 → 1.28.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/LikeC4LanguageServices.js +6 -7
- package/dist/ast.d.ts +16 -9
- package/dist/ast.js +58 -79
- package/dist/bundled.mjs +2130 -2127
- package/dist/config/schema.d.ts +3 -3
- package/dist/config/schema.js +12 -5
- package/dist/documentation/documentation-provider.js +3 -1
- package/dist/formatting/LikeC4Formatter.d.ts +0 -2
- package/dist/formatting/LikeC4Formatter.js +24 -53
- package/dist/generated/ast.d.ts +128 -233
- package/dist/generated/ast.js +134 -306
- package/dist/generated/grammar.js +1 -1
- package/dist/lsp/CompletionProvider.d.ts +3 -0
- package/dist/lsp/CompletionProvider.js +128 -113
- package/dist/lsp/DocumentLinkProvider.js +6 -3
- package/dist/lsp/HoverProvider.js +3 -1
- package/dist/lsp/SemanticTokenProvider.js +33 -43
- package/dist/model/builder/MergedSpecification.d.ts +5 -3
- package/dist/model/builder/MergedSpecification.js +21 -7
- package/dist/model/builder/buildModel.d.ts +6 -1
- package/dist/model/builder/buildModel.js +20 -15
- package/dist/model/deployments-index.js +4 -2
- package/dist/model/fqn-index.d.ts +4 -2
- package/dist/model/fqn-index.js +28 -5
- package/dist/model/model-builder.d.ts +2 -2
- package/dist/model/model-builder.js +54 -16
- package/dist/model/model-locator.js +7 -4
- package/dist/model/model-parser.d.ts +215 -52
- package/dist/model/model-parser.js +6 -2
- package/dist/model/parser/Base.d.ts +11 -2
- package/dist/model/parser/Base.js +138 -3
- package/dist/model/parser/DeploymentModelParser.d.ts +19 -2
- package/dist/model/parser/DeploymentModelParser.js +19 -29
- package/dist/model/parser/DeploymentViewParser.d.ts +18 -2
- package/dist/model/parser/DeploymentViewParser.js +6 -24
- package/dist/model/parser/FqnRefParser.d.ts +18 -3
- package/dist/model/parser/FqnRefParser.js +264 -40
- package/dist/model/parser/GlobalsParser.d.ts +35 -18
- package/dist/model/parser/ImportsParser.d.ts +32 -0
- package/dist/model/parser/ImportsParser.js +26 -0
- package/dist/model/parser/ModelParser.d.ts +26 -2
- package/dist/model/parser/ModelParser.js +21 -41
- package/dist/model/parser/PredicatesParser.d.ts +35 -12
- package/dist/model/parser/PredicatesParser.js +20 -271
- package/dist/model/parser/SpecificationParser.d.ts +8 -0
- package/dist/model/parser/SpecificationParser.js +5 -9
- package/dist/model/parser/ViewsParser.d.ts +36 -19
- package/dist/model/parser/ViewsParser.js +15 -11
- package/dist/model-change/changeElementStyle.d.ts +2 -2
- package/dist/model-change/changeElementStyle.js +2 -1
- package/dist/references/name-provider.js +8 -2
- package/dist/references/scope-computation.d.ts +1 -1
- package/dist/references/scope-computation.js +33 -3
- package/dist/references/scope-provider.d.ts +7 -8
- package/dist/references/scope-provider.js +59 -41
- package/dist/shared/NodeKindProvider.js +4 -2
- package/dist/test/testServices.d.ts +2 -0
- package/dist/test/testServices.js +4 -1
- package/dist/utils/elementRef.d.ts +1 -1
- package/dist/utils/elementRef.js +6 -1
- package/dist/utils/fqnRef.d.ts +3 -0
- package/dist/utils/fqnRef.js +15 -4
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/projectId.d.ts +2 -1
- package/dist/utils/projectId.js +11 -1
- package/dist/validation/_shared.js +2 -2
- package/dist/validation/deployment-checks.js +24 -10
- package/dist/validation/element-ref.d.ts +4 -0
- package/dist/validation/element-ref.js +12 -0
- package/dist/validation/element.d.ts +1 -1
- package/dist/validation/element.js +1 -1
- package/dist/validation/imports.d.ts +5 -0
- package/dist/validation/imports.js +30 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +47 -45
- package/dist/validation/relation.d.ts +2 -2
- package/dist/validation/relation.js +24 -27
- package/dist/validation/specification.d.ts +9 -9
- package/dist/validation/specification.js +9 -9
- package/dist/validation/view-predicates/{element-with.d.ts → fqn-expr-with.d.ts} +1 -1
- package/dist/validation/view-predicates/fqn-expr-with.js +42 -0
- package/dist/validation/view-predicates/fqn-ref-expr.d.ts +4 -0
- package/dist/validation/view-predicates/fqn-ref-expr.js +53 -0
- package/dist/validation/view-predicates/incoming.d.ts +1 -1
- package/dist/validation/view-predicates/incoming.js +2 -2
- package/dist/validation/view-predicates/index.d.ts +6 -6
- package/dist/validation/view-predicates/index.js +6 -6
- package/dist/validation/view-predicates/outgoing.d.ts +1 -1
- package/dist/validation/view-predicates/outgoing.js +8 -4
- package/dist/validation/view-predicates/{expanded-element.d.ts → relation-expr.d.ts} +1 -1
- package/dist/validation/view-predicates/relation-expr.js +39 -0
- package/dist/validation/view-predicates/relation-with.d.ts +1 -1
- package/dist/validation/view-predicates/relation-with.js +8 -5
- package/dist/workspace/AstNodeDescriptionProvider.d.ts +1 -1
- package/dist/workspace/AstNodeDescriptionProvider.js +2 -3
- package/dist/workspace/IndexManager.d.ts +1 -1
- package/dist/workspace/IndexManager.js +5 -4
- package/dist/workspace/LangiumDocuments.d.ts +1 -1
- package/dist/workspace/LangiumDocuments.js +3 -5
- package/dist/workspace/ProjectsManager.d.ts +25 -7
- package/dist/workspace/ProjectsManager.js +76 -32
- package/dist/workspace/WorkspaceManager.d.ts +4 -5
- package/dist/workspace/WorkspaceManager.js +53 -20
- package/package.json +12 -10
- package/dist/validation/dynamic-view-rule.d.ts +0 -4
- package/dist/validation/dynamic-view-rule.js +0 -17
- package/dist/validation/view-predicates/element-with.js +0 -31
- package/dist/validation/view-predicates/expanded-element.js +0 -12
- package/dist/validation/view-predicates/expression-v2.d.ts +0 -5
- package/dist/validation/view-predicates/expression-v2.js +0 -83
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { DefaultIndexManager, stream } from "langium";
|
|
2
2
|
import { CancellationToken } from "vscode-jsonrpc";
|
|
3
3
|
export class IndexManager extends DefaultIndexManager {
|
|
4
|
-
projects;
|
|
5
4
|
constructor(services) {
|
|
6
5
|
super(services);
|
|
7
|
-
this.
|
|
6
|
+
this.services = services;
|
|
8
7
|
}
|
|
9
8
|
async updateContent(document, cancelToken = CancellationToken.None) {
|
|
10
|
-
|
|
9
|
+
const projects = this.services.workspace.ProjectsManager;
|
|
10
|
+
document.likec4ProjectId = projects.belongsTo(document.uri);
|
|
11
11
|
await super.updateContent(document, cancelToken);
|
|
12
12
|
}
|
|
13
13
|
projectElements(projectId, nodeType, uris) {
|
|
14
|
+
const projects = this.services.workspace.ProjectsManager;
|
|
14
15
|
let documentUris = stream(this.symbolIndex.keys());
|
|
15
|
-
return documentUris.filter((uri) =>
|
|
16
|
+
return documentUris.filter((uri) => projects.belongsTo(uri) === projectId && (!uris || uris.has(uri))).flatMap((uri) => this.getFileDescriptions(uri, nodeType));
|
|
16
17
|
}
|
|
17
18
|
}
|
|
@@ -3,7 +3,7 @@ import { type Stream, DefaultLangiumDocuments } from 'langium';
|
|
|
3
3
|
import { type LikeC4LangiumDocument } from '../ast';
|
|
4
4
|
import type { LikeC4SharedServices } from '../module';
|
|
5
5
|
export declare class LangiumDocuments extends DefaultLangiumDocuments {
|
|
6
|
-
|
|
6
|
+
protected services: LikeC4SharedServices;
|
|
7
7
|
constructor(services: LikeC4SharedServices);
|
|
8
8
|
/**
|
|
9
9
|
* Returns all user documents, excluding built-in documents.
|
|
@@ -3,22 +3,20 @@ import { groupBy, prop } from "remeda";
|
|
|
3
3
|
import { isLikeC4LangiumDocument } from "../ast.js";
|
|
4
4
|
import { isLikeC4Builtin } from "../likec4lib.js";
|
|
5
5
|
export class LangiumDocuments extends DefaultLangiumDocuments {
|
|
6
|
-
projects;
|
|
7
6
|
constructor(services) {
|
|
8
7
|
super(services);
|
|
9
|
-
this.
|
|
8
|
+
this.services = services;
|
|
10
9
|
}
|
|
11
10
|
/**
|
|
12
11
|
* Returns all user documents, excluding built-in documents.
|
|
13
12
|
*/
|
|
14
13
|
get allExcludingBuiltin() {
|
|
14
|
+
const projects = this.services.workspace.ProjectsManager;
|
|
15
15
|
return super.all.filter((doc) => {
|
|
16
16
|
if (!isLikeC4LangiumDocument(doc) || isLikeC4Builtin(doc.uri)) {
|
|
17
17
|
return false;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
doc.likec4ProjectId = this.projects.belongsTo(doc.uri);
|
|
21
|
-
}
|
|
19
|
+
doc.likec4ProjectId ??= projects.belongsTo(doc.uri);
|
|
22
20
|
return true;
|
|
23
21
|
});
|
|
24
22
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { NonEmptyReadonlyArray, ProjectId } from '@likec4/core';
|
|
2
2
|
import { type FileSystemNode, type LangiumDocument, URI, WorkspaceCache } from 'langium';
|
|
3
|
+
import picomatch from 'picomatch/posix';
|
|
3
4
|
import { ProjectConfig } from '../config';
|
|
4
5
|
import type { LikeC4SharedServices } from '../module';
|
|
6
|
+
interface Project {
|
|
7
|
+
id: ProjectId;
|
|
8
|
+
config: ProjectConfig;
|
|
9
|
+
folder: string;
|
|
10
|
+
exclude?: picomatch.Matcher;
|
|
11
|
+
}
|
|
5
12
|
export declare class ProjectsManager {
|
|
6
13
|
protected services: LikeC4SharedServices;
|
|
7
14
|
/**
|
|
@@ -14,35 +21,46 @@ export declare class ProjectsManager {
|
|
|
14
21
|
* The mapping between project config files and project IDs.
|
|
15
22
|
*/
|
|
16
23
|
private projectIdToFolder;
|
|
17
|
-
private _mappingsToProject;
|
|
18
24
|
/**
|
|
19
25
|
* Registered projects.
|
|
20
26
|
* Sorted descending by the number of segments in the folder path.
|
|
21
27
|
* This ensures that the most specific project is used for a document.
|
|
22
28
|
*/
|
|
23
29
|
private _projects;
|
|
30
|
+
private defaultGlobalProject;
|
|
24
31
|
constructor(services: LikeC4SharedServices);
|
|
32
|
+
/**
|
|
33
|
+
* Returns:
|
|
34
|
+
* - the default project ID if there are no projects.
|
|
35
|
+
* - the ID of the only project
|
|
36
|
+
* - undefined if there are multiple projects.
|
|
37
|
+
*/
|
|
25
38
|
get defaultProjectId(): ProjectId | undefined;
|
|
26
39
|
get all(): NonEmptyReadonlyArray<ProjectId>;
|
|
27
|
-
getProject(
|
|
40
|
+
getProject(arg: ProjectId | LangiumDocument): {
|
|
41
|
+
id: ProjectId;
|
|
28
42
|
folder: URI;
|
|
29
43
|
config: Readonly<ProjectConfig>;
|
|
30
44
|
};
|
|
31
45
|
ensureProjectId(projectId?: ProjectId | undefined): ProjectId;
|
|
32
46
|
hasMultipleProjects(): boolean;
|
|
47
|
+
checkIfExcluded(documentUri: URI): boolean;
|
|
48
|
+
isConfigFile(entry: FileSystemNode): boolean;
|
|
33
49
|
/**
|
|
34
50
|
* Checks if the provided file system entry is a valid project config file.
|
|
35
51
|
*
|
|
36
52
|
* @param entry The file system entry to check
|
|
37
53
|
* @returns {boolean} Returns true if the entry is a valid config file, false otherwise.
|
|
38
54
|
*/
|
|
39
|
-
loadConfigFile(entry: FileSystemNode): Promise<
|
|
40
|
-
registerProject(configFile: URI): Promise<
|
|
55
|
+
loadConfigFile(entry: FileSystemNode): Promise<Project | undefined>;
|
|
56
|
+
registerProject(configFile: URI): Promise<Project>;
|
|
41
57
|
registerProject(opts: {
|
|
42
58
|
config: ProjectConfig;
|
|
43
59
|
folderUri: URI | string;
|
|
44
|
-
}): Promise<
|
|
60
|
+
}): Promise<Project>;
|
|
45
61
|
belongsTo(document: LangiumDocument | URI | string): ProjectId;
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
protected findProjectForDocument(documentUri: string): Omit<Project, 'folder'>;
|
|
63
|
+
private _mappingsToProject;
|
|
64
|
+
protected get mappingsToProject(): WorkspaceCache<string, Omit<Project, 'folder'>>;
|
|
48
65
|
}
|
|
66
|
+
export {};
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { BiMap, invariant, nonNullable } from "@likec4/core";
|
|
2
2
|
import { URI, WorkspaceCache } from "langium";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import picomatch from "picomatch/posix";
|
|
4
|
+
import { hasAtLeast, isNullish, map, pipe, prop, sortBy } from "remeda";
|
|
5
|
+
import {
|
|
6
|
+
hasProtocol,
|
|
7
|
+
joinRelativeURL,
|
|
8
|
+
parseFilename,
|
|
9
|
+
withoutProtocol,
|
|
10
|
+
withProtocol
|
|
11
|
+
} from "ufo";
|
|
5
12
|
import { parseConfigJson } from "../config/index.js";
|
|
6
13
|
import { logger as mainLogger } from "../logger.js";
|
|
7
14
|
const logger = mainLogger.getChild("ProjectsManager");
|
|
@@ -24,14 +31,26 @@ export class ProjectsManager {
|
|
|
24
31
|
* The mapping between project config files and project IDs.
|
|
25
32
|
*/
|
|
26
33
|
projectIdToFolder = new BiMap();
|
|
27
|
-
// The mapping between document URIs and their corresponding project IDs.
|
|
28
|
-
_mappingsToProject;
|
|
29
34
|
/**
|
|
30
35
|
* Registered projects.
|
|
31
36
|
* Sorted descending by the number of segments in the folder path.
|
|
32
37
|
* This ensures that the most specific project is used for a document.
|
|
33
38
|
*/
|
|
34
39
|
_projects = [];
|
|
40
|
+
defaultGlobalProject = {
|
|
41
|
+
id: ProjectsManager.DefaultProjectId,
|
|
42
|
+
config: {
|
|
43
|
+
name: ProjectsManager.DefaultProjectId,
|
|
44
|
+
exclude: ["**/node_modules/**/*"]
|
|
45
|
+
},
|
|
46
|
+
exclude: picomatch("**/node_modules/**/*")
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Returns:
|
|
50
|
+
* - the default project ID if there are no projects.
|
|
51
|
+
* - the ID of the only project
|
|
52
|
+
* - undefined if there are multiple projects.
|
|
53
|
+
*/
|
|
35
54
|
get defaultProjectId() {
|
|
36
55
|
if (this._projects.length > 1) {
|
|
37
56
|
return void 0;
|
|
@@ -47,20 +66,24 @@ export class ProjectsManager {
|
|
|
47
66
|
}
|
|
48
67
|
return [ProjectsManager.DefaultProjectId];
|
|
49
68
|
}
|
|
50
|
-
getProject(
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
getProject(arg) {
|
|
70
|
+
const id = typeof arg === "string" ? arg : arg.likec4ProjectId || this.belongsTo(arg);
|
|
71
|
+
if (id === ProjectsManager.DefaultProjectId) {
|
|
72
|
+
const folder2 = this.services.workspace.WorkspaceManager.workspaceUri;
|
|
53
73
|
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
74
|
+
id,
|
|
75
|
+
folder: folder2,
|
|
76
|
+
config: this.defaultGlobalProject.config
|
|
58
77
|
};
|
|
59
78
|
}
|
|
60
|
-
const
|
|
79
|
+
const {
|
|
80
|
+
config,
|
|
81
|
+
folder
|
|
82
|
+
} = nonNullable(this._projects.find((p) => p.id === id), `Project "${id}" not found`);
|
|
61
83
|
return {
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
id,
|
|
85
|
+
folder: URI.parse(folder),
|
|
86
|
+
config
|
|
64
87
|
};
|
|
65
88
|
}
|
|
66
89
|
ensureProjectId(projectId) {
|
|
@@ -79,6 +102,15 @@ export class ProjectsManager {
|
|
|
79
102
|
hasMultipleProjects() {
|
|
80
103
|
return this._projects.length > 1;
|
|
81
104
|
}
|
|
105
|
+
checkIfExcluded(documentUri) {
|
|
106
|
+
let docUriAsString = documentUri.toString();
|
|
107
|
+
const project = this.findProjectForDocument(docUriAsString);
|
|
108
|
+
return project.exclude ? project.exclude(withoutProtocol(docUriAsString)) : false;
|
|
109
|
+
}
|
|
110
|
+
isConfigFile(entry) {
|
|
111
|
+
const filename = parseFilename(entry.uri.toString(), { strict: false })?.toLowerCase();
|
|
112
|
+
return !!filename && ProjectsManager.ConfigFileNames.includes(filename);
|
|
113
|
+
}
|
|
82
114
|
/**
|
|
83
115
|
* Checks if the provided file system entry is a valid project config file.
|
|
84
116
|
*
|
|
@@ -87,17 +119,12 @@ export class ProjectsManager {
|
|
|
87
119
|
*/
|
|
88
120
|
async loadConfigFile(entry) {
|
|
89
121
|
if (entry.isDirectory) {
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
const filename = parseFilename(entry.uri.fsPath, { strict: false });
|
|
93
|
-
if (!filename) {
|
|
94
|
-
return false;
|
|
122
|
+
return void 0;
|
|
95
123
|
}
|
|
96
|
-
if (
|
|
97
|
-
await this.registerProject(entry.uri);
|
|
98
|
-
return true;
|
|
124
|
+
if (this.isConfigFile(entry)) {
|
|
125
|
+
return await this.registerProject(entry.uri);
|
|
99
126
|
}
|
|
100
|
-
return
|
|
127
|
+
return void 0;
|
|
101
128
|
}
|
|
102
129
|
async registerProject(opts) {
|
|
103
130
|
if (URI.isUri(opts)) {
|
|
@@ -109,9 +136,10 @@ export class ProjectsManager {
|
|
|
109
136
|
return this.registerProject({ config: config2, folderUri: folderUri2 });
|
|
110
137
|
}
|
|
111
138
|
const { config, folderUri } = opts;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
139
|
+
let id = config.name;
|
|
140
|
+
let i = 1;
|
|
141
|
+
while (this.projectIdToFolder.has(id)) {
|
|
142
|
+
id = `${config.name}-${i++}`;
|
|
115
143
|
}
|
|
116
144
|
let folder;
|
|
117
145
|
if (URI.isUri(folderUri)) {
|
|
@@ -119,14 +147,25 @@ export class ProjectsManager {
|
|
|
119
147
|
} else {
|
|
120
148
|
folder = hasProtocol(folderUri) ? folderUri : withProtocol(folderUri, "file://");
|
|
121
149
|
}
|
|
150
|
+
const project = {
|
|
151
|
+
id,
|
|
152
|
+
config,
|
|
153
|
+
folder
|
|
154
|
+
};
|
|
155
|
+
if (isNullish(config.exclude)) {
|
|
156
|
+
project.exclude = this.defaultGlobalProject.exclude;
|
|
157
|
+
} else if (hasAtLeast(config.exclude, 1)) {
|
|
158
|
+
project.exclude = picomatch(config.exclude);
|
|
159
|
+
}
|
|
122
160
|
this._projects = pipe(
|
|
123
|
-
[...this._projects,
|
|
161
|
+
[...this._projects, project],
|
|
124
162
|
sortBy(
|
|
125
163
|
[({ folder: folder2 }) => withoutProtocol(folder2).split("/").length, "desc"]
|
|
126
164
|
)
|
|
127
165
|
);
|
|
128
166
|
this.projectIdToFolder.set(id, folder);
|
|
129
|
-
logger.
|
|
167
|
+
logger.info`register project ${id} folder: ${folder})`;
|
|
168
|
+
return project;
|
|
130
169
|
}
|
|
131
170
|
belongsTo(document) {
|
|
132
171
|
let documentUri;
|
|
@@ -137,12 +176,17 @@ export class ProjectsManager {
|
|
|
137
176
|
} else {
|
|
138
177
|
documentUri = document.uri.toString();
|
|
139
178
|
}
|
|
140
|
-
return this.
|
|
179
|
+
return this.findProjectForDocument(documentUri).id;
|
|
141
180
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
181
|
+
findProjectForDocument(documentUri) {
|
|
182
|
+
return this.mappingsToProject.get(documentUri, () => {
|
|
183
|
+
const project = this._projects.find(({ folder }) => documentUri.startsWith(folder));
|
|
184
|
+
return project ?? this.defaultGlobalProject;
|
|
185
|
+
});
|
|
145
186
|
}
|
|
187
|
+
// The mapping between document URIs and their corresponding project ID
|
|
188
|
+
// Lazy-created due to initialization order of the LanguageServer
|
|
189
|
+
_mappingsToProject;
|
|
146
190
|
get mappingsToProject() {
|
|
147
191
|
this._mappingsToProject ??= new WorkspaceCache(this.services);
|
|
148
192
|
return this._mappingsToProject;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { LangiumDocument } from 'langium';
|
|
1
|
+
import type { FileSystemNode, LangiumDocument } from 'langium';
|
|
2
2
|
import { DefaultWorkspaceManager } from 'langium';
|
|
3
3
|
import type { WorkspaceFolder } from 'vscode-languageserver';
|
|
4
4
|
import { URI } from 'vscode-uri';
|
|
5
5
|
import type { LikeC4SharedServices } from '../module';
|
|
6
6
|
export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
7
|
+
private services;
|
|
7
8
|
private documentFactory;
|
|
8
|
-
private projects;
|
|
9
9
|
constructor(services: LikeC4SharedServices);
|
|
10
10
|
/**
|
|
11
11
|
* Load all additional documents that shall be visible in the context of the given workspace
|
|
@@ -14,10 +14,9 @@ export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
|
14
14
|
*/
|
|
15
15
|
protected loadAdditionalDocuments(folders: WorkspaceFolder[], collector: (document: LangiumDocument) => void): Promise<void>;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* This is necessary to ensure that the project config files are loaded and processed correctly.
|
|
17
|
+
* Determine whether the given folder entry shall be included while indexing the workspace.
|
|
19
18
|
*/
|
|
20
|
-
protected
|
|
19
|
+
protected includeEntry(_workspaceFolder: WorkspaceFolder, entry: FileSystemNode, fileExtensions: string[]): boolean;
|
|
21
20
|
workspace(): any;
|
|
22
21
|
get workspaceUri(): URI;
|
|
23
22
|
get workspaceURL(): URL;
|
|
@@ -2,42 +2,75 @@ import { hasAtLeast, invariant } from "@likec4/core";
|
|
|
2
2
|
import { DefaultWorkspaceManager } from "langium";
|
|
3
3
|
import { URI } from "vscode-uri";
|
|
4
4
|
import * as BuiltIn from "../likec4lib.js";
|
|
5
|
+
import { logError } from "../logger.js";
|
|
5
6
|
export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
6
|
-
documentFactory;
|
|
7
|
-
projects;
|
|
8
7
|
constructor(services) {
|
|
9
8
|
super(services);
|
|
9
|
+
this.services = services;
|
|
10
10
|
this.documentFactory = services.workspace.LangiumDocumentFactory;
|
|
11
|
-
this.projects = services.workspace.ProjectsManager;
|
|
12
11
|
}
|
|
12
|
+
documentFactory;
|
|
13
13
|
/**
|
|
14
14
|
* Load all additional documents that shall be visible in the context of the given workspace
|
|
15
15
|
* folders and add them to the collector. This can be used to include built-in libraries of
|
|
16
16
|
* your language, which can be either loaded from provided files or constructed in memory.
|
|
17
17
|
*/
|
|
18
18
|
async loadAdditionalDocuments(folders, collector) {
|
|
19
|
+
const projects = this.services.workspace.ProjectsManager;
|
|
20
|
+
for (const folder of folders) {
|
|
21
|
+
try {
|
|
22
|
+
const content = await this.fileSystemProvider.readDirectory(URI.parse(folder.uri));
|
|
23
|
+
for (const entry of content) {
|
|
24
|
+
try {
|
|
25
|
+
await projects.loadConfigFile(entry);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
logError(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
logError(error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
19
34
|
collector(this.documentFactory.fromString(BuiltIn.Content, URI.parse(BuiltIn.Uri)));
|
|
20
35
|
await super.loadAdditionalDocuments(folders, collector);
|
|
21
36
|
}
|
|
37
|
+
// /**
|
|
38
|
+
// * We override the default implementation to process project config files during the traversal.
|
|
39
|
+
// * This is necessary to ensure that the project config files are loaded and processed correctly.
|
|
40
|
+
// */
|
|
41
|
+
// protected override async traverseFolder(
|
|
42
|
+
// workspaceFolder: WorkspaceFolder,
|
|
43
|
+
// folderPath: URI,
|
|
44
|
+
// fileExtensions: string[],
|
|
45
|
+
// collector: (document: LangiumDocument) => void,
|
|
46
|
+
// ): Promise<void> {
|
|
47
|
+
// // Then load other files
|
|
48
|
+
// for (const entry of nonConfigFiles) {
|
|
49
|
+
// try {
|
|
50
|
+
// if (this.includeEntry(workspaceFolder, entry, fileExtensions)) {
|
|
51
|
+
// if (entry.isDirectory) {
|
|
52
|
+
// await this.traverseFolder(workspaceFolder, entry.uri, fileExtensions, collector)
|
|
53
|
+
// } else if (entry.isFile) {
|
|
54
|
+
// const document = await this.langiumDocuments.getOrCreateDocument(entry.uri)
|
|
55
|
+
// collector(document)
|
|
56
|
+
// }
|
|
57
|
+
// }
|
|
58
|
+
// } catch (error) {
|
|
59
|
+
// logError(error)
|
|
60
|
+
// }
|
|
61
|
+
// }
|
|
62
|
+
// }
|
|
22
63
|
/**
|
|
23
|
-
*
|
|
24
|
-
* This is necessary to ensure that the project config files are loaded and processed correctly.
|
|
64
|
+
* Determine whether the given folder entry shall be included while indexing the workspace.
|
|
25
65
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
await this.traverseFolder(workspaceFolder, entry.uri, fileExtensions, collector);
|
|
35
|
-
} else if (entry.isFile) {
|
|
36
|
-
const document = await this.langiumDocuments.getOrCreateDocument(entry.uri);
|
|
37
|
-
collector(document);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}));
|
|
66
|
+
includeEntry(_workspaceFolder, entry, fileExtensions) {
|
|
67
|
+
if (this.services.workspace.ProjectsManager.isConfigFile(entry)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (entry.isFile) {
|
|
71
|
+
return !this.services.workspace.ProjectsManager.checkIfExcluded(entry.uri);
|
|
72
|
+
}
|
|
73
|
+
return super.includeEntry(_workspaceFolder, entry, fileExtensions);
|
|
41
74
|
}
|
|
42
75
|
workspace() {
|
|
43
76
|
if (this.folders && hasAtLeast(this.folders, 1)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@likec4/language-server",
|
|
3
3
|
"description": "LikeC4 Language Server",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.28.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -109,24 +109,26 @@
|
|
|
109
109
|
"p-debounce": "^4.0.0",
|
|
110
110
|
"pretty-ms": "^9.2.0",
|
|
111
111
|
"remeda": "^2.21.2",
|
|
112
|
+
"@types/picomatch": "^4.0.0",
|
|
113
|
+
"picomatch": "^4.0.2",
|
|
112
114
|
"strip-indent": "^4.0.0",
|
|
113
115
|
"tsx": "~4.19.3",
|
|
114
|
-
"turbo": "^2.
|
|
115
|
-
"type-fest": "^4.
|
|
116
|
-
"typescript": "^5.8.
|
|
116
|
+
"turbo": "^2.5.0",
|
|
117
|
+
"type-fest": "^4.39.1",
|
|
118
|
+
"typescript": "^5.8.3",
|
|
117
119
|
"ufo": "^1.5.4",
|
|
118
120
|
"unbuild": "^3.5.0",
|
|
119
121
|
"valibot": "^1.0.0",
|
|
120
|
-
"vitest": "^3.
|
|
122
|
+
"vitest": "^3.1.1",
|
|
121
123
|
"vscode-languageserver": "9.0.1",
|
|
122
124
|
"vscode-languageserver-types": "3.17.5",
|
|
123
125
|
"vscode-languageserver-protocol": "3.17.5",
|
|
124
126
|
"which": "^5.0.0",
|
|
125
|
-
"@likec4/core": "1.
|
|
126
|
-
"@likec4/icons": "1.
|
|
127
|
-
"@likec4/
|
|
128
|
-
"@likec4/
|
|
129
|
-
"@likec4/tsconfig": "1.
|
|
127
|
+
"@likec4/core": "1.28.0",
|
|
128
|
+
"@likec4/icons": "1.28.0",
|
|
129
|
+
"@likec4/log": "1.28.0",
|
|
130
|
+
"@likec4/layouts": "1.28.0",
|
|
131
|
+
"@likec4/tsconfig": "1.28.0"
|
|
130
132
|
},
|
|
131
133
|
"scripts": {
|
|
132
134
|
"typecheck": "tsc --noEmit",
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ast, elementExpressionFromPredicate } from "../ast.js";
|
|
2
|
-
import { tryOrLog } from "./_shared.js";
|
|
3
|
-
export const dynamicViewRulePredicate = (_services) => {
|
|
4
|
-
return tryOrLog((predicate, accept) => {
|
|
5
|
-
const expr = elementExpressionFromPredicate(predicate.value);
|
|
6
|
-
switch (true) {
|
|
7
|
-
case ast.isElementKindExpression(expr):
|
|
8
|
-
case ast.isElementTagExpression(expr):
|
|
9
|
-
case ast.isWildcardExpression(expr): {
|
|
10
|
-
accept("warning", `Predicate is ignored, as not supported in dynamic views`, {
|
|
11
|
-
node: predicate
|
|
12
|
-
});
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { nonexhaustive } from "@likec4/core";
|
|
2
|
-
import { AstUtils } from "langium";
|
|
3
|
-
import { ast } from "../../ast.js";
|
|
4
|
-
import { tryOrLog } from "../_shared.js";
|
|
5
|
-
export const elementPredicateWithChecks = (_services) => {
|
|
6
|
-
return tryOrLog((el, accept) => {
|
|
7
|
-
const container = AstUtils.getContainerOfType(el, ast.isViewRulePredicate);
|
|
8
|
-
if (ast.isExcludePredicate(container)) {
|
|
9
|
-
accept("error", 'Invalid usage inside "exclude"', {
|
|
10
|
-
node: el
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
const subject = ast.isElementPredicateWhere(el.subject) ? el.subject.subject : el.subject;
|
|
14
|
-
switch (true) {
|
|
15
|
-
case ast.isElementRef(subject):
|
|
16
|
-
case ast.isElementDescedantsExpression(subject):
|
|
17
|
-
case ast.isExpandElementExpression(subject):
|
|
18
|
-
case ast.isWildcardExpression(subject):
|
|
19
|
-
return;
|
|
20
|
-
case ast.isElementKindExpression(subject):
|
|
21
|
-
case ast.isElementTagExpression(subject):
|
|
22
|
-
accept("error", "Invalid target (expect reference to specific element)", {
|
|
23
|
-
node: el,
|
|
24
|
-
property: "subject"
|
|
25
|
-
});
|
|
26
|
-
return;
|
|
27
|
-
default:
|
|
28
|
-
nonexhaustive(subject);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { AstUtils } from "langium";
|
|
2
|
-
import { ast } from "../../ast.js";
|
|
3
|
-
import { tryOrLog } from "../_shared.js";
|
|
4
|
-
export const expandElementExprChecks = (_services) => {
|
|
5
|
-
return tryOrLog((el, accept) => {
|
|
6
|
-
if (AstUtils.hasContainerOfType(el, ast.isRelationExpression)) {
|
|
7
|
-
accept("warning", `Redundant usage, expand predicate resolves parent element only when used in relations`, {
|
|
8
|
-
node: el
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type ValidationCheck } from 'langium';
|
|
2
|
-
import { ast } from '../../ast';
|
|
3
|
-
import type { LikeC4Services } from '../../module';
|
|
4
|
-
export declare const relationExprChecks: (services: LikeC4Services) => ValidationCheck<ast.RelationExpr>;
|
|
5
|
-
export declare const fqnRefExprChecks: (services: LikeC4Services) => ValidationCheck<ast.FqnRefExpr>;
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { FqnExpr, FqnRef } from "@likec4/core";
|
|
2
|
-
import { AstUtils } from "langium";
|
|
3
|
-
import { isNonNullish, isNullish } from "remeda";
|
|
4
|
-
import { ast } from "../../ast.js";
|
|
5
|
-
import { tryOrLog } from "../_shared.js";
|
|
6
|
-
export const relationExprChecks = (services) => {
|
|
7
|
-
const ModelParser = services.likec4.ModelParser;
|
|
8
|
-
return tryOrLog((node, accept) => {
|
|
9
|
-
if (node.$container.$type !== "DeploymentViewRulePredicateExpression") {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
const predicate = AstUtils.getContainerOfType(node, ast.isDeploymentViewRulePredicate);
|
|
13
|
-
if (!predicate || predicate.isInclude !== true) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
const doc = AstUtils.getDocument(node);
|
|
17
|
-
const parser = ModelParser.forDocument(doc);
|
|
18
|
-
const ModelRefOnlyExclude = "Model reference is allowed in exclude predicate only";
|
|
19
|
-
if (ast.isDirectedRelationExpr(node)) {
|
|
20
|
-
if (FqnExpr.isModelRef(parser.parseFqnExpr(node.source.from))) {
|
|
21
|
-
accept("error", ModelRefOnlyExclude, {
|
|
22
|
-
node: node.source,
|
|
23
|
-
property: "from"
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
if (FqnExpr.isModelRef(parser.parseFqnExpr(node.target))) {
|
|
27
|
-
accept("error", ModelRefOnlyExclude, {
|
|
28
|
-
node,
|
|
29
|
-
property: "target"
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
let expr;
|
|
35
|
-
if (ast.isIncomingRelationExpr(node)) {
|
|
36
|
-
expr = node.to;
|
|
37
|
-
} else if (ast.isOutgoingRelationExpr(node)) {
|
|
38
|
-
expr = node.from;
|
|
39
|
-
} else {
|
|
40
|
-
expr = node.inout.to;
|
|
41
|
-
}
|
|
42
|
-
if (FqnExpr.isModelRef(parser.parseFqnExpr(expr))) {
|
|
43
|
-
accept("error", ModelRefOnlyExclude, {
|
|
44
|
-
node
|
|
45
|
-
});
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
};
|
|
50
|
-
export const fqnRefExprChecks = (services) => {
|
|
51
|
-
const ModelParser = services.likec4.ModelParser;
|
|
52
|
-
return tryOrLog((node, accept) => {
|
|
53
|
-
const referenceTo = node.ref.value.ref;
|
|
54
|
-
if (isNullish(referenceTo)) {
|
|
55
|
-
accept("error", "Invalid empty reference", {
|
|
56
|
-
node
|
|
57
|
-
});
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const doc = AstUtils.getDocument(node);
|
|
61
|
-
const expr = ModelParser.forDocument(doc).parseFqnRefExpr(node);
|
|
62
|
-
if (node.$container.$type === "DeploymentViewRulePredicateExpression") {
|
|
63
|
-
if (FqnExpr.isModelRef(expr)) {
|
|
64
|
-
accept("error", "Deployment view predicate must reference deployment model", {
|
|
65
|
-
node
|
|
66
|
-
});
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (FqnExpr.isDeploymentRef(expr) && FqnRef.isInsideInstanceRef(expr.ref)) {
|
|
70
|
-
accept("error", "Must reference deployment nodes or instances, but not internals", {
|
|
71
|
-
node
|
|
72
|
-
});
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!ast.isDeploymentNode(referenceTo) && isNonNullish(node.selector)) {
|
|
77
|
-
accept("warning", `Selector '${node.selector}' applies to deployment nodes only, ignored here`, {
|
|
78
|
-
node,
|
|
79
|
-
property: "selector"
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
};
|