@likec4/language-server 1.41.0 → 1.42.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/browser/package.json +4 -0
- package/browser-worker/package.json +4 -0
- package/dist/LikeC4LanguageServices.d.ts +1 -1
- package/dist/LikeC4LanguageServices.mjs +3 -2
- package/dist/Rpc.mjs +30 -24
- package/dist/ast.d.ts +1 -7
- package/dist/ast.mjs +0 -10
- package/dist/bundled.mjs +4118 -3653
- package/dist/documentation/documentation-provider.mjs +1 -1
- package/dist/filesystem/FileSystemWatcher.d.ts +2 -2
- package/dist/filesystem/index.d.ts +1 -1
- package/dist/formatting/LikeC4Formatter.mjs +41 -10
- package/dist/formatting/utils.d.ts +3 -3
- package/dist/formatting/utils.mjs +1 -1
- package/dist/generated/ast.d.ts +35 -16
- package/dist/generated/ast.mjs +69 -26
- package/dist/generated/grammar.mjs +1 -1
- package/dist/lsp/CompletionProvider.mjs +1 -1
- package/dist/lsp/DocumentLinkProvider.d.ts +1 -1
- package/dist/lsp/DocumentLinkProvider.mjs +1 -1
- package/dist/lsp/DocumentSymbolProvider.mjs +1 -1
- package/dist/mcp/NoopLikeC4MCPServer.d.ts +1 -1
- package/dist/mcp/NoopLikeC4MCPServer.mjs +1 -1
- package/dist/mcp/server/StdioLikeC4MCPServer.mjs +4 -1
- package/dist/mcp/server/StreamableLikeC4MCPServer.mjs +3 -3
- package/dist/mcp/server/WithMCPServer.mjs +2 -2
- package/dist/mcp/tools/_common.mjs +2 -2
- package/dist/model/builder/MergedSpecification.d.ts +3 -3
- package/dist/model/builder/MergedSpecification.mjs +13 -39
- package/dist/model/builder/buildModel.mjs +14 -17
- package/dist/model/model-builder.d.ts +1 -1
- package/dist/model/model-builder.mjs +12 -9
- package/dist/model/model-locator.d.ts +5 -0
- package/dist/model/model-locator.mjs +40 -3
- package/dist/model/model-parser-where.mjs +1 -2
- package/dist/model/model-parser.d.ts +19 -2
- package/dist/model/parser/Base.mjs +8 -8
- package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
- package/dist/model/parser/DeploymentModelParser.mjs +7 -7
- package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
- package/dist/model/parser/FqnRefParser.d.ts +2 -0
- package/dist/model/parser/FqnRefParser.mjs +16 -11
- package/dist/model/parser/GlobalsParser.d.ts +8 -2
- package/dist/model/parser/ModelParser.d.ts +1 -0
- package/dist/model/parser/ModelParser.mjs +16 -11
- package/dist/model/parser/PredicatesParser.d.ts +1 -0
- package/dist/model/parser/SpecificationParser.mjs +4 -4
- package/dist/model/parser/ViewsParser.d.ts +12 -2
- package/dist/model/parser/ViewsParser.mjs +123 -31
- package/dist/model-change/ModelChanges.d.ts +1 -1
- package/dist/module.mjs +3 -2
- package/dist/protocol.d.ts +28 -4
- package/dist/references/scope-computation.mjs +2 -3
- package/dist/references/scope-provider.d.ts +2 -2
- package/dist/references/scope-provider.mjs +8 -15
- package/dist/test/testServices.d.ts +2 -0
- package/dist/test/testServices.mjs +10 -11
- package/dist/utils/disposable.mjs +2 -2
- package/dist/utils/index.mjs +1 -1
- package/dist/validation/_shared.d.ts +1 -1
- package/dist/validation/deployment-checks.d.ts +1 -1
- package/dist/validation/deployment-checks.mjs +4 -1
- package/dist/validation/dynamic-view.d.ts +3 -2
- package/dist/validation/dynamic-view.mjs +21 -2
- package/dist/validation/element-ref.d.ts +2 -2
- package/dist/validation/element-ref.mjs +1 -1
- package/dist/validation/imports.d.ts +0 -1
- package/dist/validation/imports.mjs +0 -5
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.mjs +19 -13
- package/dist/validation/view-predicates/relation-with.d.ts +1 -1
- package/dist/validation/view.d.ts +1 -1
- package/dist/view-utils/index.d.ts +0 -1
- package/dist/view-utils/index.mjs +0 -1
- package/dist/views/likec4-views.d.ts +6 -0
- package/dist/views/likec4-views.mjs +31 -18
- package/dist/workspace/ProjectsManager.d.ts +23 -31
- package/dist/workspace/ProjectsManager.mjs +78 -89
- package/dist/workspace/WorkspaceManager.mjs +1 -1
- package/likec4lib/package.json +4 -0
- package/package.json +25 -29
- package/protocol/package.json +4 -0
- package/dist/view-utils/resolve-relative-paths.d.ts +0 -2
- package/dist/view-utils/resolve-relative-paths.mjs +0 -78
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isLikeC4Config,
|
|
3
|
+
validateProjectConfig
|
|
4
|
+
} from "@likec4/config";
|
|
2
5
|
import { BiMap, delay, invariant, memoizeProp, nonNullable } from "@likec4/core/utils";
|
|
3
|
-
import { loggable } from "@likec4/log";
|
|
4
6
|
import { deepEqual } from "fast-equals";
|
|
5
7
|
import {
|
|
6
8
|
interruptAndCheck,
|
|
7
9
|
URI,
|
|
8
10
|
WorkspaceCache
|
|
9
11
|
} from "langium";
|
|
10
|
-
import PQueue from "p-queue";
|
|
11
12
|
import picomatch from "picomatch";
|
|
12
|
-
import { hasAtLeast, isNullish,
|
|
13
|
+
import { hasAtLeast, isNullish, map, pipe, prop, sortBy } from "remeda";
|
|
13
14
|
import {
|
|
14
15
|
joinRelativeURL,
|
|
15
16
|
parseFilename,
|
|
@@ -31,6 +32,14 @@ export function ProjectFolder(folder) {
|
|
|
31
32
|
folder = normalizeUri(folder);
|
|
32
33
|
return withTrailingSlash(folder);
|
|
33
34
|
}
|
|
35
|
+
const DefaultProject = {
|
|
36
|
+
id: "default",
|
|
37
|
+
config: {
|
|
38
|
+
name: "default",
|
|
39
|
+
exclude: ["**/node_modules/**"]
|
|
40
|
+
},
|
|
41
|
+
exclude: picomatch("**/node_modules/**", { dot: true })
|
|
42
|
+
};
|
|
34
43
|
export class ProjectsManager {
|
|
35
44
|
constructor(services) {
|
|
36
45
|
this.services = services;
|
|
@@ -40,15 +49,7 @@ export class ProjectsManager {
|
|
|
40
49
|
* The global project ID used for all documents
|
|
41
50
|
* that are not part of a specific project.
|
|
42
51
|
*/
|
|
43
|
-
static DefaultProjectId =
|
|
44
|
-
static DefaultProject = {
|
|
45
|
-
id: ProjectsManager.DefaultProjectId,
|
|
46
|
-
config: {
|
|
47
|
-
name: ProjectsManager.DefaultProjectId,
|
|
48
|
-
exclude: ["**/node_modules/**"]
|
|
49
|
-
},
|
|
50
|
-
exclude: picomatch("**/node_modules/**", { dot: true })
|
|
51
|
-
};
|
|
52
|
+
static DefaultProjectId = DefaultProject.id;
|
|
52
53
|
/**
|
|
53
54
|
* Configured default project ID.
|
|
54
55
|
* (it is used in CLI and Vite plugin)
|
|
@@ -57,21 +58,14 @@ export class ProjectsManager {
|
|
|
57
58
|
/**
|
|
58
59
|
* The mapping between project config files and project IDs.
|
|
59
60
|
*/
|
|
60
|
-
projectIdToFolder = new BiMap();
|
|
61
|
+
#projectIdToFolder = new BiMap();
|
|
61
62
|
/**
|
|
62
63
|
* Registered projects.
|
|
63
64
|
* Sorted descending by the number of segments in the folder path.
|
|
64
65
|
* This ensures that the most specific project is used for a document.
|
|
65
66
|
*/
|
|
66
|
-
|
|
67
|
-
excludedDocuments = /* @__PURE__ */ new WeakMap();
|
|
68
|
-
get defaultGlobalProject() {
|
|
69
|
-
return ProjectsManager.DefaultProject;
|
|
70
|
-
}
|
|
71
|
-
reloadProjectsLimiter = new PQueue({
|
|
72
|
-
concurrency: 1,
|
|
73
|
-
timeout: 2e4
|
|
74
|
-
});
|
|
67
|
+
#projects = [];
|
|
68
|
+
#excludedDocuments = /* @__PURE__ */ new WeakMap();
|
|
75
69
|
/**
|
|
76
70
|
* Returns:
|
|
77
71
|
* - configured default project ID if set
|
|
@@ -83,10 +77,10 @@ export class ProjectsManager {
|
|
|
83
77
|
if (this.#defaultProjectId) {
|
|
84
78
|
return this.#defaultProjectId;
|
|
85
79
|
}
|
|
86
|
-
if (this.
|
|
80
|
+
if (this.#projects.length > 1) {
|
|
87
81
|
return void 0;
|
|
88
82
|
}
|
|
89
|
-
return this
|
|
83
|
+
return this.#projects[0]?.id ?? ProjectsManager.DefaultProjectId;
|
|
90
84
|
}
|
|
91
85
|
set defaultProjectId(id) {
|
|
92
86
|
if (id === this.#defaultProjectId) {
|
|
@@ -97,15 +91,15 @@ export class ProjectsManager {
|
|
|
97
91
|
this.#defaultProjectId = void 0;
|
|
98
92
|
return;
|
|
99
93
|
}
|
|
100
|
-
invariant(this.
|
|
94
|
+
invariant(this.#projects.find((p) => p.id === id), `Project "${id}" not found`);
|
|
101
95
|
logger.debug`set default project ID to ${id}`;
|
|
102
96
|
this.#defaultProjectId = id;
|
|
103
97
|
}
|
|
104
98
|
get all() {
|
|
105
|
-
if (hasAtLeast(this
|
|
99
|
+
if (hasAtLeast(this.#projects, 1)) {
|
|
106
100
|
const ids = [
|
|
107
|
-
...map(this
|
|
108
|
-
|
|
101
|
+
...map(this.#projects, prop("id")),
|
|
102
|
+
DefaultProject.id
|
|
109
103
|
];
|
|
110
104
|
if (this.#defaultProjectId) {
|
|
111
105
|
const idx = ids.findIndex((p) => p === this.#defaultProjectId);
|
|
@@ -116,11 +110,11 @@ export class ProjectsManager {
|
|
|
116
110
|
}
|
|
117
111
|
return ids;
|
|
118
112
|
}
|
|
119
|
-
return [
|
|
113
|
+
return [DefaultProject.id];
|
|
120
114
|
}
|
|
121
115
|
getProject(arg) {
|
|
122
116
|
const id = typeof arg === "string" ? arg : arg.likec4ProjectId || this.belongsTo(arg);
|
|
123
|
-
if (id ===
|
|
117
|
+
if (id === DefaultProject.id) {
|
|
124
118
|
let folderUri2;
|
|
125
119
|
try {
|
|
126
120
|
folderUri2 = this.services.workspace.WorkspaceManager.workspaceUri;
|
|
@@ -130,14 +124,14 @@ export class ProjectsManager {
|
|
|
130
124
|
}
|
|
131
125
|
return {
|
|
132
126
|
id: ProjectsManager.DefaultProjectId,
|
|
133
|
-
config:
|
|
127
|
+
config: DefaultProject.config,
|
|
134
128
|
folderUri: folderUri2
|
|
135
129
|
};
|
|
136
130
|
}
|
|
137
131
|
const {
|
|
138
132
|
config,
|
|
139
133
|
folderUri
|
|
140
|
-
} = nonNullable(this.
|
|
134
|
+
} = nonNullable(this.#projects.find((p) => p.id === id), `Project "${id}" not found`);
|
|
141
135
|
return {
|
|
142
136
|
id,
|
|
143
137
|
folderUri,
|
|
@@ -154,16 +148,16 @@ export class ProjectsManager {
|
|
|
154
148
|
return this.defaultProjectId ?? ProjectsManager.DefaultProjectId;
|
|
155
149
|
}
|
|
156
150
|
if (projectId) {
|
|
157
|
-
invariant(this
|
|
151
|
+
invariant(this.#projectIdToFolder.has(projectId), `Project ID ${projectId} is not registered`);
|
|
158
152
|
return projectId;
|
|
159
153
|
}
|
|
160
154
|
return nonNullable(
|
|
161
155
|
this.defaultProjectId,
|
|
162
|
-
() => `Specify exact project, known: [${[...this
|
|
156
|
+
() => `Specify exact project, known: [${[...this.#projectIdToFolder.keys()].join(", ")}]`
|
|
163
157
|
);
|
|
164
158
|
}
|
|
165
159
|
hasMultipleProjects() {
|
|
166
|
-
return this.
|
|
160
|
+
return this.#projects.length > 1;
|
|
167
161
|
}
|
|
168
162
|
/**
|
|
169
163
|
* Checks if the specified document should be excluded from processing.
|
|
@@ -174,10 +168,10 @@ export class ProjectsManager {
|
|
|
174
168
|
const project = this.findProjectForDocument(docUriAsString);
|
|
175
169
|
return project.exclude ? project.exclude(withoutProtocol(docUriAsString)) : false;
|
|
176
170
|
}
|
|
177
|
-
let isExcluded = this
|
|
171
|
+
let isExcluded = this.#excludedDocuments.get(document);
|
|
178
172
|
if (isExcluded === void 0) {
|
|
179
173
|
isExcluded = this.checkIfExcluded(document.uri);
|
|
180
|
-
this
|
|
174
|
+
this.#excludedDocuments.set(document, isExcluded);
|
|
181
175
|
}
|
|
182
176
|
return isExcluded;
|
|
183
177
|
}
|
|
@@ -190,7 +184,7 @@ export class ProjectsManager {
|
|
|
190
184
|
const filename = parseFilename(entry.toString(), { strict: false })?.toLowerCase();
|
|
191
185
|
const isConfigFile = !!filename && isLikeC4Config(filename);
|
|
192
186
|
if (isConfigFile) {
|
|
193
|
-
if (
|
|
187
|
+
if (DefaultProject.exclude(entry.path)) {
|
|
194
188
|
logger.debug`exclude config file ${entry.path}`;
|
|
195
189
|
return false;
|
|
196
190
|
}
|
|
@@ -202,20 +196,15 @@ export class ProjectsManager {
|
|
|
202
196
|
*
|
|
203
197
|
* @param entry The file system entry to check
|
|
204
198
|
*/
|
|
205
|
-
async
|
|
206
|
-
if (
|
|
207
|
-
return void 0;
|
|
208
|
-
}
|
|
209
|
-
if (this.isConfigFile(entry.uri)) {
|
|
199
|
+
async registerConfigFile(configFile) {
|
|
200
|
+
if (this.isConfigFile(configFile)) {
|
|
210
201
|
try {
|
|
211
|
-
return await this.registerProject(
|
|
202
|
+
return await this.registerProject(configFile);
|
|
212
203
|
} catch (error) {
|
|
213
204
|
this.services.lsp.Connection?.window.showErrorMessage(
|
|
214
|
-
`LikeC4: Failed to register project at ${
|
|
215
|
-
|
|
216
|
-
${loggable(error)}`
|
|
205
|
+
`LikeC4: Failed to register project at ${configFile.fsPath}`
|
|
217
206
|
);
|
|
218
|
-
logger.warn("Failed to register project at {uri}", { uri:
|
|
207
|
+
logger.warn("Failed to register project at {uri}", { uri: configFile.fsPath, error });
|
|
219
208
|
return void 0;
|
|
220
209
|
}
|
|
221
210
|
}
|
|
@@ -228,22 +217,28 @@ ${loggable(error)}`
|
|
|
228
217
|
async registerProject(opts) {
|
|
229
218
|
if (URI.isUri(opts)) {
|
|
230
219
|
const configFile = opts;
|
|
231
|
-
const
|
|
220
|
+
const config = await this.services.workspace.FileSystemProvider.loadProjectConfig(configFile);
|
|
232
221
|
const path = joinRelativeURL(configFile.path, "..");
|
|
233
|
-
const
|
|
234
|
-
return
|
|
222
|
+
const folderUri = configFile.with({ path });
|
|
223
|
+
return this._registerProject({ config, folderUri });
|
|
235
224
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
225
|
+
return this._registerProject(opts);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Registers (or reloads) likec4 project by config file or config object.
|
|
229
|
+
* If there is some project registered at same folder, it will be reloaded.
|
|
230
|
+
*/
|
|
231
|
+
_registerProject(opts) {
|
|
232
|
+
const config = validateProjectConfig(opts.config);
|
|
233
|
+
const folder = ProjectFolder(opts.folderUri);
|
|
234
|
+
let project = this.#projects.find((p) => p.folder === folder);
|
|
240
235
|
if (project && deepEqual(project.config, config)) {
|
|
241
236
|
return project;
|
|
242
237
|
}
|
|
243
|
-
let mustReset =
|
|
238
|
+
let mustReset = false;
|
|
244
239
|
let id;
|
|
245
240
|
if (!project) {
|
|
246
|
-
if (this
|
|
241
|
+
if (this.#projectIdToFolder.has(config.name)) {
|
|
247
242
|
logger.warn`Project "${config.name}" already exists, generating unique ID`;
|
|
248
243
|
}
|
|
249
244
|
id = this.uniqueProjectId(config.name);
|
|
@@ -253,17 +248,18 @@ ${loggable(error)}`
|
|
|
253
248
|
folder,
|
|
254
249
|
folderUri: URI.parse(folder)
|
|
255
250
|
};
|
|
256
|
-
mustReset = this.
|
|
257
|
-
this
|
|
258
|
-
[...this
|
|
251
|
+
mustReset = this.#projects.some((p) => p.folder.startsWith(folder) || folder.startsWith(p.folder));
|
|
252
|
+
this.#projects = pipe(
|
|
253
|
+
[...this.#projects, project],
|
|
259
254
|
sortBy(
|
|
260
255
|
[({ folder: folder2 }) => withoutProtocol(folder2).split("/").length, "desc"]
|
|
261
256
|
)
|
|
262
257
|
);
|
|
263
258
|
logger.info`register project ${project.id} folder: ${folder}`;
|
|
264
259
|
} else {
|
|
260
|
+
mustReset = true;
|
|
265
261
|
if (project.config.name !== config.name) {
|
|
266
|
-
this
|
|
262
|
+
this.#projectIdToFolder.delete(project.id);
|
|
267
263
|
logger.info`unregister project ${project.id} folder: ${folder}`;
|
|
268
264
|
id = this.uniqueProjectId(config.name);
|
|
269
265
|
project.id = id;
|
|
@@ -275,11 +271,11 @@ ${loggable(error)}`
|
|
|
275
271
|
project.config = config;
|
|
276
272
|
}
|
|
277
273
|
if (isNullish(config.exclude)) {
|
|
278
|
-
project.exclude =
|
|
274
|
+
project.exclude = DefaultProject.exclude;
|
|
279
275
|
} else if (hasAtLeast(config.exclude, 1)) {
|
|
280
276
|
project.exclude = picomatch(config.exclude, { dot: true });
|
|
281
277
|
}
|
|
282
|
-
this
|
|
278
|
+
this.#projectIdToFolder.set(project.id, folder);
|
|
283
279
|
if (mustReset) {
|
|
284
280
|
this.resetProjectIds();
|
|
285
281
|
}
|
|
@@ -289,24 +285,14 @@ ${loggable(error)}`
|
|
|
289
285
|
const documentUri = normalizeUri(document);
|
|
290
286
|
return this.findProjectForDocument(documentUri).id;
|
|
291
287
|
}
|
|
292
|
-
async reloadProjects(
|
|
288
|
+
async reloadProjects() {
|
|
293
289
|
const folders = this.services.workspace.WorkspaceManager.workspaceFolders;
|
|
294
290
|
if (!folders) {
|
|
295
291
|
logger.warn("No workspace folders found");
|
|
296
292
|
return;
|
|
297
293
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
this.reloadProjectsLimiter.add(async () => {
|
|
303
|
-
await delay(100);
|
|
304
|
-
});
|
|
305
|
-
this.reloadProjectsLimiter.add(async () => {
|
|
306
|
-
if (token) {
|
|
307
|
-
await interruptAndCheck(token);
|
|
308
|
-
}
|
|
309
|
-
logger.debug`reload projects`;
|
|
294
|
+
logger.debug`schedule reload projects`;
|
|
295
|
+
await this.services.workspace.WorkspaceLock.write(async (cancelToken) => {
|
|
310
296
|
const configFiles = [];
|
|
311
297
|
for (const folder of folders) {
|
|
312
298
|
try {
|
|
@@ -320,47 +306,50 @@ ${loggable(error)}`
|
|
|
320
306
|
logger.error("Failed to scanProjectFiles, {folder}", { folder: folder.uri, error });
|
|
321
307
|
}
|
|
322
308
|
}
|
|
323
|
-
if (configFiles.length === 0 && this.
|
|
309
|
+
if (configFiles.length === 0 && this.#projects.length !== 0) {
|
|
324
310
|
logger.warning("No config files found, but some projects were registered before");
|
|
325
311
|
}
|
|
326
|
-
|
|
327
|
-
|
|
312
|
+
await delay(150);
|
|
313
|
+
await interruptAndCheck(cancelToken);
|
|
314
|
+
logger.debug`start reload projects`;
|
|
315
|
+
this.#projects = [];
|
|
316
|
+
this.#projectIdToFolder.clear();
|
|
328
317
|
for (const entry of configFiles) {
|
|
329
318
|
try {
|
|
330
|
-
await this.
|
|
319
|
+
await this.registerConfigFile(entry.uri);
|
|
331
320
|
} catch (error) {
|
|
332
321
|
logger.error("Failed to load config file {uri}", { uri: entry.uri.toString(), error });
|
|
333
322
|
}
|
|
334
323
|
}
|
|
335
324
|
this.resetProjectIds();
|
|
336
|
-
await this.rebuidDocuments();
|
|
325
|
+
await this.rebuidDocuments(cancelToken);
|
|
337
326
|
});
|
|
338
327
|
}
|
|
339
328
|
uniqueProjectId(name) {
|
|
340
329
|
let id = name;
|
|
341
330
|
let i = 1;
|
|
342
|
-
while (this
|
|
331
|
+
while (this.#projectIdToFolder.has(id)) {
|
|
343
332
|
id = `${name}-${i++}`;
|
|
344
333
|
}
|
|
345
334
|
return id;
|
|
346
335
|
}
|
|
347
336
|
resetProjectIds() {
|
|
348
|
-
if (this.#defaultProjectId && !this
|
|
337
|
+
if (this.#defaultProjectId && !this.#projectIdToFolder.has(this.#defaultProjectId)) {
|
|
349
338
|
this.#defaultProjectId = void 0;
|
|
350
339
|
}
|
|
351
340
|
this.mappingsToProject.clear();
|
|
352
|
-
this
|
|
341
|
+
this.#excludedDocuments = /* @__PURE__ */ new WeakMap();
|
|
353
342
|
this.services.workspace.LangiumDocuments.resetProjectIds();
|
|
354
343
|
}
|
|
355
|
-
async rebuidDocuments() {
|
|
344
|
+
async rebuidDocuments(cancelToken) {
|
|
356
345
|
const docs = this.services.workspace.LangiumDocuments.all.map((d) => d.uri).toArray();
|
|
357
346
|
logger.info("invalidate and rebuild all {docs} documents", { docs: docs.length });
|
|
358
|
-
await this.services.workspace.DocumentBuilder.update(docs, []);
|
|
347
|
+
await this.services.workspace.DocumentBuilder.update(docs, [], cancelToken);
|
|
359
348
|
}
|
|
360
349
|
findProjectForDocument(documentUri) {
|
|
361
350
|
return this.mappingsToProject.get(documentUri, () => {
|
|
362
|
-
const project = this.
|
|
363
|
-
return project ??
|
|
351
|
+
const project = this.#projects.find(({ folder }) => documentUri.startsWith(folder));
|
|
352
|
+
return project ?? DefaultProject;
|
|
364
353
|
});
|
|
365
354
|
}
|
|
366
355
|
// The mapping between document URIs and their corresponding project ID
|
|
@@ -36,7 +36,7 @@ export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
|
|
|
36
36
|
const projects = this.services.workspace.ProjectsManager;
|
|
37
37
|
for (const entry of configFiles) {
|
|
38
38
|
try {
|
|
39
|
-
await projects.
|
|
39
|
+
await projects.registerConfigFile(entry.uri);
|
|
40
40
|
} catch (error) {
|
|
41
41
|
logWarnError(error);
|
|
42
42
|
}
|
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.42.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bugs": "https://github.com/likec4/likec4/issues",
|
|
7
7
|
"homepage": "https://likec4.dev",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
|
11
11
|
"dist",
|
|
12
|
+
"**/package.json",
|
|
12
13
|
"!**/__mocks__/",
|
|
13
14
|
"!**/__tests__/",
|
|
14
15
|
"!**/*.spec.*",
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
"directory": "packages/language-server"
|
|
21
22
|
},
|
|
22
23
|
"engines": {
|
|
23
|
-
"node": "
|
|
24
|
+
"node": "^20.19.0 || >=22.18.0"
|
|
24
25
|
},
|
|
25
26
|
"engineStrict": true,
|
|
26
27
|
"type": "module",
|
|
@@ -31,17 +32,9 @@
|
|
|
31
32
|
"exports": {
|
|
32
33
|
".": {
|
|
33
34
|
"sources": "./src/index.ts",
|
|
34
|
-
"
|
|
35
|
-
"sources": "./src/index.ts",
|
|
35
|
+
"default": {
|
|
36
36
|
"types": "./dist/index.d.ts",
|
|
37
|
-
"import": "./dist/index.mjs",
|
|
38
37
|
"default": "./dist/index.mjs"
|
|
39
|
-
},
|
|
40
|
-
"default": {
|
|
41
|
-
"sources": "./src/browser.ts",
|
|
42
|
-
"types": "./dist/browser.d.ts",
|
|
43
|
-
"import": "./dist/browser.mjs",
|
|
44
|
-
"default": "./dist/browser.mjs"
|
|
45
38
|
}
|
|
46
39
|
},
|
|
47
40
|
"./likec4lib": {
|
|
@@ -83,43 +76,42 @@
|
|
|
83
76
|
"access": "public"
|
|
84
77
|
},
|
|
85
78
|
"dependencies": {
|
|
86
|
-
"@hpcc-js/wasm-graphviz": "1.12.
|
|
79
|
+
"@hpcc-js/wasm-graphviz": "1.12.1",
|
|
87
80
|
"bundle-require": "^5.1.0",
|
|
88
81
|
"esbuild": "0.25.9"
|
|
89
82
|
},
|
|
90
83
|
"devDependencies": {
|
|
91
|
-
"@hono/node-server": "^1.19.
|
|
84
|
+
"@hono/node-server": "^1.19.3",
|
|
92
85
|
"@modelcontextprotocol/sdk": "^1.17.5",
|
|
93
86
|
"@msgpack/msgpack": "^3.1.2",
|
|
94
87
|
"@smithy/util-base64": "^4.0.0",
|
|
95
|
-
"@types/chroma-js": "^3.1.1",
|
|
96
88
|
"@types/natural-compare-lite": "^1.4.2",
|
|
97
89
|
"@types/node": "~20.19.11",
|
|
98
90
|
"@types/picomatch": "^4.0.2",
|
|
99
91
|
"@types/vscode": "^1.84.0",
|
|
100
92
|
"@types/which": "^3.0.4",
|
|
101
93
|
"chokidar": "^4.0.3",
|
|
102
|
-
"chroma-js": "^3.1.2",
|
|
103
94
|
"defu": "^6.1.4",
|
|
104
95
|
"esm-env": "^1.2.2",
|
|
105
96
|
"fast-equals": "^5.2.2",
|
|
106
97
|
"fdir": "6.4.0",
|
|
107
98
|
"fetch-to-node": "^2.1.0",
|
|
108
|
-
"hono": "^4.9.
|
|
99
|
+
"hono": "^4.9.8",
|
|
109
100
|
"indent-string": "^5.0.0",
|
|
110
101
|
"json5": "^2.2.3",
|
|
111
102
|
"langium": "3.5.0",
|
|
112
103
|
"langium-cli": "3.5.2",
|
|
113
104
|
"natural-compare-lite": "^1.4.0",
|
|
105
|
+
"oxlint": "1.18.0",
|
|
114
106
|
"p-debounce": "4.0.0",
|
|
115
107
|
"p-queue": "8.1.1",
|
|
116
108
|
"p-timeout": "6.1.4",
|
|
117
109
|
"picomatch": "^4.0.3",
|
|
118
110
|
"pretty-ms": "^9.2.0",
|
|
119
|
-
"remeda": "^2.
|
|
111
|
+
"remeda": "^2.31.1",
|
|
120
112
|
"strip-indent": "^4.0.0",
|
|
121
|
-
"tsx": "4.20.
|
|
122
|
-
"turbo": "2.5.
|
|
113
|
+
"tsx": "4.20.6",
|
|
114
|
+
"turbo": "2.5.8",
|
|
123
115
|
"type-fest": "^4.41.0",
|
|
124
116
|
"typescript": "5.9.2",
|
|
125
117
|
"ufo": "1.6.1",
|
|
@@ -131,13 +123,14 @@
|
|
|
131
123
|
"vscode-languageserver-types": "3.17.5",
|
|
132
124
|
"vscode-uri": "3.1.0",
|
|
133
125
|
"which": "^5.0.0",
|
|
134
|
-
"zod": "3.25.76",
|
|
135
|
-
"@likec4/core": "1.
|
|
136
|
-
"@likec4/config": "1.
|
|
137
|
-
"@likec4/layouts": "1.
|
|
138
|
-
"@likec4/log": "1.
|
|
139
|
-
"@likec4/icons": "1.
|
|
140
|
-
"@likec4/tsconfig": "1.
|
|
126
|
+
"zod": "^3.25.76",
|
|
127
|
+
"@likec4/core": "1.42.0",
|
|
128
|
+
"@likec4/config": "1.42.0",
|
|
129
|
+
"@likec4/layouts": "1.42.0",
|
|
130
|
+
"@likec4/log": "1.42.0",
|
|
131
|
+
"@likec4/icons": "1.42.0",
|
|
132
|
+
"@likec4/tsconfig": "1.42.0",
|
|
133
|
+
"@likec4/devops": "1.42.0"
|
|
141
134
|
},
|
|
142
135
|
"scripts": {
|
|
143
136
|
"typecheck": "tsc -b --verbose",
|
|
@@ -148,12 +141,15 @@
|
|
|
148
141
|
"watch:ts": "tsc --watch",
|
|
149
142
|
"generate": "langium generate && tsx scripts/generate-icons.ts",
|
|
150
143
|
"dev": "run-p \"watch:*\"",
|
|
151
|
-
"lint": "
|
|
144
|
+
"lint": "oxlint --type-aware",
|
|
145
|
+
"lint:fix": "oxlint --type-aware --fix",
|
|
152
146
|
"lint:package": "pnpx publint ./package.tgz",
|
|
153
|
-
"clean": "
|
|
147
|
+
"clean": "likec4ops clean contrib src/generated src/generated-lib",
|
|
154
148
|
"test": "vitest run --no-isolate",
|
|
155
149
|
"test-dbg": "vitest run --no-isolate -t formating",
|
|
156
150
|
"vitest:ui": "vitest --no-isolate --ui",
|
|
157
151
|
"test:watch": "vitest"
|
|
158
|
-
}
|
|
152
|
+
},
|
|
153
|
+
"types": "dist/index.d.ts",
|
|
154
|
+
"module": "dist/index.mjs"
|
|
159
155
|
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { compareNatural, invariant } from "@likec4/core/utils";
|
|
2
|
-
import { filter, hasAtLeast, isTruthy, map, pipe, unique } from "remeda";
|
|
3
|
-
import { parsePath } from "ufo";
|
|
4
|
-
function commonAncestorPath(views, sep = "/") {
|
|
5
|
-
const uniqURIs = pipe(
|
|
6
|
-
views,
|
|
7
|
-
map((v) => v.docUri),
|
|
8
|
-
filter(isTruthy),
|
|
9
|
-
unique()
|
|
10
|
-
);
|
|
11
|
-
if (uniqURIs.length === 0) return "";
|
|
12
|
-
if (uniqURIs.length === 1) {
|
|
13
|
-
const parts2 = parsePath(uniqURIs[0]).pathname.split(sep);
|
|
14
|
-
if (parts2.length <= 1) return sep;
|
|
15
|
-
parts2.pop();
|
|
16
|
-
return parts2.join(sep) + sep;
|
|
17
|
-
}
|
|
18
|
-
invariant(hasAtLeast(uniqURIs, 2), "Expected at least 2 unique URIs");
|
|
19
|
-
const [baseUri, ...tail] = uniqURIs;
|
|
20
|
-
const parts = parsePath(baseUri).pathname.split(sep);
|
|
21
|
-
let endOfPrefix = parts.length;
|
|
22
|
-
for (const uri of tail) {
|
|
23
|
-
if (uri === baseUri) {
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
const compare = parsePath(uri).pathname.split(sep);
|
|
27
|
-
for (let i = 0; i < endOfPrefix; i++) {
|
|
28
|
-
if (compare[i] !== parts[i]) {
|
|
29
|
-
endOfPrefix = i;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (endOfPrefix === 0) return "";
|
|
33
|
-
}
|
|
34
|
-
const prefix = parts.slice(0, endOfPrefix).join(sep);
|
|
35
|
-
return prefix.endsWith(sep) ? prefix : prefix + sep;
|
|
36
|
-
}
|
|
37
|
-
export function resolveRelativePaths(views) {
|
|
38
|
-
const sep = "/";
|
|
39
|
-
const commonPrefix = commonAncestorPath(views, sep);
|
|
40
|
-
return views.map((view) => {
|
|
41
|
-
if (!view.docUri) {
|
|
42
|
-
return {
|
|
43
|
-
view,
|
|
44
|
-
parts: []
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
let path = parsePath(view.docUri).pathname;
|
|
48
|
-
if (commonPrefix.length > 0) {
|
|
49
|
-
invariant(
|
|
50
|
-
path.startsWith(commonPrefix),
|
|
51
|
-
`Expect path "${path}" to start with common prefix: "${commonPrefix}"`
|
|
52
|
-
);
|
|
53
|
-
path = path.slice(commonPrefix.length);
|
|
54
|
-
} else {
|
|
55
|
-
path = path.includes(sep) ? path.slice(path.lastIndexOf(sep) + 1) : path;
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
view,
|
|
59
|
-
parts: path.split(sep)
|
|
60
|
-
};
|
|
61
|
-
}).sort((a, b) => {
|
|
62
|
-
if (a.parts.length !== b.parts.length) {
|
|
63
|
-
return a.parts.length - b.parts.length;
|
|
64
|
-
}
|
|
65
|
-
for (let i = 0; i < a.parts.length; i++) {
|
|
66
|
-
const compare = compareNatural(a.parts[i], b.parts[i]);
|
|
67
|
-
if (compare !== 0) {
|
|
68
|
-
return compare;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return 0;
|
|
72
|
-
}).map(({ parts, view }) => {
|
|
73
|
-
return {
|
|
74
|
-
...view,
|
|
75
|
-
relativePath: parts.join(sep)
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
|
-
}
|