@likec4/language-server 1.25.0 → 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/bin/likec4-language-server.mjs +1 -1
- 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 +21 -12
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { BiMap, invariant, nonNullable } from "@likec4/core";
|
|
2
|
+
import { URI, WorkspaceCache } from "langium";
|
|
3
|
+
import { hasAtLeast, map, pipe, prop, sortBy } from "remeda";
|
|
4
|
+
import { hasProtocol, joinRelativeURL, parseFilename, withoutProtocol, withProtocol } from "ufo";
|
|
5
|
+
import { parseConfigJson } from "../config/index.js";
|
|
6
|
+
import { logger as mainLogger } from "../logger.js";
|
|
7
|
+
const logger = mainLogger.getChild("ProjectsManager");
|
|
8
|
+
export class ProjectsManager {
|
|
9
|
+
constructor(services) {
|
|
10
|
+
this.services = services;
|
|
11
|
+
logger.debug`created`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The global project ID used for all documents
|
|
15
|
+
* that are not part of a specific project.
|
|
16
|
+
*/
|
|
17
|
+
static DefaultProjectId = "default";
|
|
18
|
+
static ConfigFileNames = [
|
|
19
|
+
".likec4rc",
|
|
20
|
+
".likec4.config.json",
|
|
21
|
+
"likec4.config.json"
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* The mapping between project config files and project IDs.
|
|
25
|
+
*/
|
|
26
|
+
projectIdToFolder = new BiMap();
|
|
27
|
+
// The mapping between document URIs and their corresponding project IDs.
|
|
28
|
+
_mappingsToProject;
|
|
29
|
+
/**
|
|
30
|
+
* Registered projects.
|
|
31
|
+
* Sorted descending by the number of segments in the folder path.
|
|
32
|
+
* This ensures that the most specific project is used for a document.
|
|
33
|
+
*/
|
|
34
|
+
_projects = [];
|
|
35
|
+
get defaultProjectId() {
|
|
36
|
+
if (this._projects.length > 1) {
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
return this._projects[0]?.id ?? ProjectsManager.DefaultProjectId;
|
|
40
|
+
}
|
|
41
|
+
get all() {
|
|
42
|
+
if (hasAtLeast(this._projects, 1)) {
|
|
43
|
+
return [
|
|
44
|
+
...map(this._projects, prop("id")),
|
|
45
|
+
ProjectsManager.DefaultProjectId
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
return [ProjectsManager.DefaultProjectId];
|
|
49
|
+
}
|
|
50
|
+
getProject(projectId) {
|
|
51
|
+
if (projectId === ProjectsManager.DefaultProjectId) {
|
|
52
|
+
const folder = this.services.workspace.WorkspaceManager.workspaceUri;
|
|
53
|
+
return {
|
|
54
|
+
folder,
|
|
55
|
+
config: {
|
|
56
|
+
name: ProjectsManager.DefaultProjectId
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const project = nonNullable(this._projects.find(({ id }) => id === projectId), `Project "${projectId}" not found`);
|
|
61
|
+
return {
|
|
62
|
+
folder: URI.parse(project.folder),
|
|
63
|
+
config: project.config
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
ensureProjectId(projectId) {
|
|
67
|
+
if (projectId === ProjectsManager.DefaultProjectId) {
|
|
68
|
+
return projectId;
|
|
69
|
+
}
|
|
70
|
+
if (projectId) {
|
|
71
|
+
invariant(this.projectIdToFolder.has(projectId), `Project ID ${projectId} is not registered`);
|
|
72
|
+
return projectId;
|
|
73
|
+
}
|
|
74
|
+
return nonNullable(
|
|
75
|
+
this.defaultProjectId,
|
|
76
|
+
() => `Specify exact project, known: [${[...this.projectIdToFolder.keys()].join(", ")}]`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
hasMultipleProjects() {
|
|
80
|
+
return this._projects.length > 1;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Checks if the provided file system entry is a valid project config file.
|
|
84
|
+
*
|
|
85
|
+
* @param entry The file system entry to check
|
|
86
|
+
* @returns {boolean} Returns true if the entry is a valid config file, false otherwise.
|
|
87
|
+
*/
|
|
88
|
+
async loadConfigFile(entry) {
|
|
89
|
+
if (entry.isDirectory) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const filename = parseFilename(entry.uri.fsPath, { strict: false });
|
|
93
|
+
if (!filename) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (ProjectsManager.ConfigFileNames.includes(filename)) {
|
|
97
|
+
await this.registerProject(entry.uri);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
async registerProject(opts) {
|
|
103
|
+
if (URI.isUri(opts)) {
|
|
104
|
+
const configFile = opts;
|
|
105
|
+
const cfg = await this.services.workspace.FileSystemProvider.readFile(configFile);
|
|
106
|
+
const config2 = parseConfigJson(cfg);
|
|
107
|
+
const path = joinRelativeURL(configFile.path, "..");
|
|
108
|
+
const folderUri2 = configFile.with({ path });
|
|
109
|
+
return this.registerProject({ config: config2, folderUri: folderUri2 });
|
|
110
|
+
}
|
|
111
|
+
const { config, folderUri } = opts;
|
|
112
|
+
const id = config.name;
|
|
113
|
+
if (this._projects.some(({ id: existingId }) => existingId === id)) {
|
|
114
|
+
throw new Error(`Project ID ${id} already registered`);
|
|
115
|
+
}
|
|
116
|
+
let folder;
|
|
117
|
+
if (URI.isUri(folderUri)) {
|
|
118
|
+
folder = folderUri.toString();
|
|
119
|
+
} else {
|
|
120
|
+
folder = hasProtocol(folderUri) ? folderUri : withProtocol(folderUri, "file://");
|
|
121
|
+
}
|
|
122
|
+
this._projects = pipe(
|
|
123
|
+
[...this._projects, { folder, config, id }],
|
|
124
|
+
sortBy(
|
|
125
|
+
[({ folder: folder2 }) => withoutProtocol(folder2).split("/").length, "desc"]
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
this.projectIdToFolder.set(id, folder);
|
|
129
|
+
logger.debug`registered project ${id} folder: ${folder})`;
|
|
130
|
+
}
|
|
131
|
+
belongsTo(document) {
|
|
132
|
+
let documentUri;
|
|
133
|
+
if (typeof document === "string") {
|
|
134
|
+
documentUri = hasProtocol(document) ? document : withProtocol(document, "file://");
|
|
135
|
+
} else if (URI.isUri(document)) {
|
|
136
|
+
documentUri = document.toString();
|
|
137
|
+
} else {
|
|
138
|
+
documentUri = document.uri.toString();
|
|
139
|
+
}
|
|
140
|
+
return this.mappingsToProject.get(documentUri, () => this.getProjectId(documentUri));
|
|
141
|
+
}
|
|
142
|
+
getProjectId(documentUri) {
|
|
143
|
+
const project = this._projects.find(({ folder }) => documentUri.toString().startsWith(folder));
|
|
144
|
+
return project?.id ?? ProjectsManager.DefaultProjectId;
|
|
145
|
+
}
|
|
146
|
+
get mappingsToProject() {
|
|
147
|
+
this._mappingsToProject ??= new WorkspaceCache(this.services);
|
|
148
|
+
return this._mappingsToProject;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import type { LangiumDocument } from 'langium';
|
|
2
2
|
import { DefaultWorkspaceManager } from 'langium';
|
|
3
|
-
import type { LangiumSharedServices } from 'langium/lsp';
|
|
4
3
|
import type { WorkspaceFolder } from 'vscode-languageserver';
|
|
5
4
|
import { URI } from 'vscode-uri';
|
|
5
|
+
import type { LikeC4SharedServices } from '../module';
|
|
6
6
|
export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
7
7
|
private documentFactory;
|
|
8
|
-
|
|
8
|
+
private projects;
|
|
9
|
+
constructor(services: LikeC4SharedServices);
|
|
9
10
|
/**
|
|
10
11
|
* Load all additional documents that shall be visible in the context of the given workspace
|
|
11
12
|
* folders and add them to the collector. This can be used to include built-in libraries of
|
|
12
13
|
* your language, which can be either loaded from provided files or constructed in memory.
|
|
13
14
|
*/
|
|
14
15
|
protected loadAdditionalDocuments(folders: WorkspaceFolder[], collector: (document: LangiumDocument) => void): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* We override the default implementation to process project config files during the traversal.
|
|
18
|
+
* This is necessary to ensure that the project config files are loaded and processed correctly.
|
|
19
|
+
*/
|
|
20
|
+
protected traverseFolder(workspaceFolder: WorkspaceFolder, folderPath: URI, fileExtensions: string[], collector: (document: LangiumDocument) => void): Promise<void>;
|
|
15
21
|
workspace(): any;
|
|
16
22
|
get workspaceUri(): URI;
|
|
17
23
|
get workspaceURL(): URL;
|
|
@@ -4,9 +4,11 @@ import { URI } from "vscode-uri";
|
|
|
4
4
|
import * as BuiltIn from "../likec4lib.js";
|
|
5
5
|
export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
6
6
|
documentFactory;
|
|
7
|
+
projects;
|
|
7
8
|
constructor(services) {
|
|
8
9
|
super(services);
|
|
9
10
|
this.documentFactory = services.workspace.LangiumDocumentFactory;
|
|
11
|
+
this.projects = services.workspace.ProjectsManager;
|
|
10
12
|
}
|
|
11
13
|
/**
|
|
12
14
|
* Load all additional documents that shall be visible in the context of the given workspace
|
|
@@ -17,6 +19,26 @@ export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
|
17
19
|
collector(this.documentFactory.fromString(BuiltIn.Content, URI.parse(BuiltIn.Uri)));
|
|
18
20
|
await super.loadAdditionalDocuments(folders, collector);
|
|
19
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* We override the default implementation to process project config files during the traversal.
|
|
24
|
+
* This is necessary to ensure that the project config files are loaded and processed correctly.
|
|
25
|
+
*/
|
|
26
|
+
async traverseFolder(workspaceFolder, folderPath, fileExtensions, collector) {
|
|
27
|
+
const content = await this.fileSystemProvider.readDirectory(folderPath);
|
|
28
|
+
await Promise.all(content.map(async (entry) => {
|
|
29
|
+
if (await this.projects.loadConfigFile(entry)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (this.includeEntry(workspaceFolder, entry, fileExtensions)) {
|
|
33
|
+
if (entry.isDirectory) {
|
|
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
|
+
}));
|
|
41
|
+
}
|
|
20
42
|
workspace() {
|
|
21
43
|
if (this.folders && hasAtLeast(this.folders, 1)) {
|
|
22
44
|
return this.folders[0];
|
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.26.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -42,6 +42,14 @@
|
|
|
42
42
|
"default": "./dist/browser.js"
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
+
"./config": {
|
|
46
|
+
"sources": "./src/config/index.ts",
|
|
47
|
+
"default": {
|
|
48
|
+
"types": "./dist/config/index.d.ts",
|
|
49
|
+
"import": "./dist/config/index.js",
|
|
50
|
+
"default": "./dist/config/index.js"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
45
53
|
"./likec4lib": {
|
|
46
54
|
"sources": "./src/likec4lib.ts",
|
|
47
55
|
"default": {
|
|
@@ -84,9 +92,9 @@
|
|
|
84
92
|
"@hpcc-js/wasm-graphviz": "1.7.0",
|
|
85
93
|
"@msgpack/msgpack": "^3.1.0",
|
|
86
94
|
"@smithy/util-base64": "^4.0.0",
|
|
87
|
-
"@types/node": "^20.17.
|
|
95
|
+
"@types/node": "^20.17.23",
|
|
88
96
|
"@types/which": "^3.0.4",
|
|
89
|
-
"@vitest/coverage-v8": "^3.0.
|
|
97
|
+
"@vitest/coverage-v8": "^3.0.8",
|
|
90
98
|
"esm-env": "^1.2.2",
|
|
91
99
|
"fast-equals": "^5.2.2",
|
|
92
100
|
"fdir": "^6.4.3",
|
|
@@ -97,25 +105,26 @@
|
|
|
97
105
|
"natural-compare-lite": "^1.4.0",
|
|
98
106
|
"p-debounce": "^4.0.0",
|
|
99
107
|
"pretty-ms": "^9.2.0",
|
|
100
|
-
"remeda": "^2.
|
|
108
|
+
"remeda": "^2.21.0",
|
|
101
109
|
"strip-indent": "^4.0.0",
|
|
102
110
|
"tsx": "~4.19.3",
|
|
103
|
-
"turbo": "^2.4.
|
|
111
|
+
"turbo": "^2.4.4",
|
|
104
112
|
"type-fest": "4.34.1",
|
|
105
|
-
"typescript": "5.
|
|
113
|
+
"typescript": "5.8.2",
|
|
106
114
|
"ufo": "^1.5.4",
|
|
107
115
|
"unbuild": "^3.3.1",
|
|
108
|
-
"
|
|
116
|
+
"valibot": "^1.0.0-rc.3",
|
|
117
|
+
"vitest": "^3.0.8",
|
|
109
118
|
"vscode-jsonrpc": "8.2.0",
|
|
110
119
|
"vscode-languageserver": "9.0.1",
|
|
111
120
|
"vscode-languageserver-types": "3.17.5",
|
|
112
121
|
"vscode-uri": "3.1.0",
|
|
113
122
|
"which": "^5.0.0",
|
|
114
|
-
"@likec4/core": "1.
|
|
115
|
-
"@likec4/
|
|
116
|
-
"@likec4/
|
|
117
|
-
"@likec4/
|
|
118
|
-
"@likec4/
|
|
123
|
+
"@likec4/core": "1.26.0",
|
|
124
|
+
"@likec4/icons": "1.26.0",
|
|
125
|
+
"@likec4/layouts": "1.26.0",
|
|
126
|
+
"@likec4/log": "1.26.0",
|
|
127
|
+
"@likec4/tsconfig": "1.26.0"
|
|
119
128
|
},
|
|
120
129
|
"scripts": {
|
|
121
130
|
"typecheck": "tsc --noEmit",
|