pi-lens 3.8.18 → 3.8.21
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/CHANGELOG.md +31 -0
- package/README.md +26 -17
- package/clients/dispatch/dispatcher.ts +53 -1
- package/clients/dispatch/integration.ts +37 -26
- package/clients/dispatch/plan.ts +26 -15
- package/clients/dispatch/runners/lsp.ts +6 -1
- package/clients/dispatch/runners/pyright.ts +4 -6
- package/clients/dispatch/runners/ruff.ts +52 -7
- package/clients/dispatch/runners/sqlfluff.ts +48 -1
- package/clients/dispatch/runners/yamllint.ts +50 -0
- package/clients/file-utils.ts +13 -2
- package/clients/formatters.ts +8 -4
- package/clients/installer/index.ts +371 -49
- package/clients/language-policy.ts +154 -0
- package/clients/language-profile.ts +167 -0
- package/clients/lsp/index.ts +81 -11
- package/clients/lsp/interactive-install.ts +35 -16
- package/clients/lsp/server.ts +357 -267
- package/clients/pipeline.ts +71 -40
- package/clients/runtime-context.ts +26 -0
- package/clients/runtime-coordinator.ts +30 -2
- package/clients/runtime-session.ts +293 -103
- package/clients/runtime-tool-result.ts +8 -10
- package/clients/runtime-turn.ts +21 -4
- package/clients/todo-scanner.ts +6 -1
- package/clients/type-coverage-client.ts +1 -1
- package/commands/booboo.ts +3 -1
- package/index.ts +15 -3
- package/package.json +1 -1
package/clients/lsp/server.ts
CHANGED
|
@@ -24,20 +24,154 @@ import {
|
|
|
24
24
|
|
|
25
25
|
export type RootFunction = (file: string) => Promise<string | undefined>;
|
|
26
26
|
|
|
27
|
+
export interface LSPSpawnOptions {
|
|
28
|
+
allowInstall?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
export interface LSPServerInfo {
|
|
28
32
|
id: string;
|
|
29
33
|
name: string;
|
|
30
34
|
extensions: string[];
|
|
31
35
|
root: RootFunction;
|
|
36
|
+
installPolicy?: "none" | "interactive" | "managed" | "package-manager";
|
|
32
37
|
spawn(
|
|
33
38
|
root: string,
|
|
39
|
+
options?: LSPSpawnOptions,
|
|
34
40
|
): Promise<
|
|
35
|
-
| {
|
|
41
|
+
| {
|
|
42
|
+
process: LSPProcess;
|
|
43
|
+
initialization?: Record<string, unknown>;
|
|
44
|
+
source?: "direct" | "managed" | "package-manager" | "interactive";
|
|
45
|
+
}
|
|
36
46
|
| undefined
|
|
37
47
|
>;
|
|
38
48
|
autoInstall?: () => Promise<boolean>;
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
function isLspInstallDisabled(): boolean {
|
|
52
|
+
return process.env.PI_LENS_DISABLE_LSP_INSTALL === "1";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function canInstall(allowInstall?: boolean): boolean {
|
|
56
|
+
return allowInstall !== false && !isLspInstallDisabled();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isCommandNotFoundError(error: unknown): boolean {
|
|
60
|
+
const msg = String(error);
|
|
61
|
+
return msg.includes("not found") || msg.includes("ENOENT");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function launchViaPackageManagerWithPolicy(
|
|
65
|
+
packageName: string,
|
|
66
|
+
args: string[],
|
|
67
|
+
options: { cwd: string; allowInstall?: boolean },
|
|
68
|
+
): Promise<LSPProcess | undefined> {
|
|
69
|
+
if (!canInstall(options.allowInstall)) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return launchViaPackageManager(packageName, args, options);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function nodeBinCandidates(root: string, baseName: string): string[] {
|
|
76
|
+
const localBase = path.join(root, "node_modules", ".bin", baseName);
|
|
77
|
+
if (process.platform === "win32") {
|
|
78
|
+
return [
|
|
79
|
+
`${localBase}.cmd`,
|
|
80
|
+
`${localBase}.ps1`,
|
|
81
|
+
`${localBase}.exe`,
|
|
82
|
+
localBase,
|
|
83
|
+
baseName,
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
return [localBase, baseName];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function launchWithDirectOrPackageManager(
|
|
90
|
+
directCommands: string[],
|
|
91
|
+
packageName: string,
|
|
92
|
+
args: string[],
|
|
93
|
+
options: { cwd: string; env?: NodeJS.ProcessEnv; allowInstall?: boolean },
|
|
94
|
+
): Promise<{ process: LSPProcess; source: "direct" | "package-manager" } | undefined> {
|
|
95
|
+
for (const command of directCommands) {
|
|
96
|
+
try {
|
|
97
|
+
const process = await launchLSP(command, args, options);
|
|
98
|
+
return { process, source: "direct" };
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (!isCommandNotFoundError(error)) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const process = await launchViaPackageManagerWithPolicy(packageName, args, {
|
|
107
|
+
cwd: options.cwd,
|
|
108
|
+
allowInstall: options.allowInstall,
|
|
109
|
+
});
|
|
110
|
+
if (!process) return undefined;
|
|
111
|
+
return { process, source: "package-manager" };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
type InitializationConfig = Record<string, unknown>;
|
|
115
|
+
|
|
116
|
+
interface InteractiveServerSpec {
|
|
117
|
+
id: string;
|
|
118
|
+
name: string;
|
|
119
|
+
extensions: string[];
|
|
120
|
+
root: RootFunction;
|
|
121
|
+
language: string;
|
|
122
|
+
command: string | ((root: string) => string);
|
|
123
|
+
args?: string[] | ((root: string) => string[]);
|
|
124
|
+
initialization?: InitializationConfig | ((root: string) => InitializationConfig);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createInteractiveServer(spec: InteractiveServerSpec): LSPServerInfo {
|
|
128
|
+
return {
|
|
129
|
+
id: spec.id,
|
|
130
|
+
name: spec.name,
|
|
131
|
+
installPolicy: "interactive",
|
|
132
|
+
extensions: spec.extensions,
|
|
133
|
+
root: spec.root,
|
|
134
|
+
async spawn(root, options) {
|
|
135
|
+
const command =
|
|
136
|
+
typeof spec.command === "function" ? spec.command(root) : spec.command;
|
|
137
|
+
const args =
|
|
138
|
+
typeof spec.args === "function"
|
|
139
|
+
? spec.args(root)
|
|
140
|
+
: spec.args || [];
|
|
141
|
+
const proc = await spawnWithInteractiveInstall(
|
|
142
|
+
spec.language,
|
|
143
|
+
command,
|
|
144
|
+
args,
|
|
145
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
146
|
+
async () => await launchLSP(command, args, { cwd: root }),
|
|
147
|
+
);
|
|
148
|
+
if (!proc) return undefined;
|
|
149
|
+
const initialization =
|
|
150
|
+
typeof spec.initialization === "function"
|
|
151
|
+
? spec.initialization(root)
|
|
152
|
+
: spec.initialization;
|
|
153
|
+
return { process: proc, source: "interactive", initialization };
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function PriorityRoot(
|
|
159
|
+
markerGroups: string[][],
|
|
160
|
+
excludePatterns?: string[],
|
|
161
|
+
stopDir?: string,
|
|
162
|
+
): RootFunction {
|
|
163
|
+
const resolvers = markerGroups.map((markers) =>
|
|
164
|
+
NearestRoot(markers, excludePatterns, stopDir),
|
|
165
|
+
);
|
|
166
|
+
return async (file: string) => {
|
|
167
|
+
for (const resolve of resolvers) {
|
|
168
|
+
const root = await resolve(file);
|
|
169
|
+
if (root) return root;
|
|
170
|
+
}
|
|
171
|
+
return undefined;
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
41
175
|
// --- Root Detection Helpers ---
|
|
42
176
|
|
|
43
177
|
import { dirname } from "node:path";
|
|
@@ -58,12 +192,15 @@ async function spawnWithInteractiveInstall(
|
|
|
58
192
|
language: string,
|
|
59
193
|
_command: string,
|
|
60
194
|
_args: string[],
|
|
61
|
-
options: { cwd: string },
|
|
195
|
+
options: { cwd: string; allowInstall?: boolean },
|
|
62
196
|
spawnFn: () => LSPProcess | Promise<LSPProcess>,
|
|
63
197
|
): Promise<LSPProcess | undefined> {
|
|
64
198
|
try {
|
|
65
199
|
return await spawnFn();
|
|
66
200
|
} catch (error) {
|
|
201
|
+
if (!canInstall(options.allowInstall)) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
67
204
|
// Check if this is a "command not found" error
|
|
68
205
|
const errorMsg = String(error);
|
|
69
206
|
if (!errorMsg.includes("not found") && !errorMsg.includes("ENOENT")) {
|
|
@@ -103,9 +240,9 @@ export function NearestRoot(
|
|
|
103
240
|
stopDir?: string,
|
|
104
241
|
): RootFunction {
|
|
105
242
|
return async (file: string): Promise<string | undefined> => {
|
|
106
|
-
let currentDir = path.dirname(file);
|
|
243
|
+
let currentDir = path.resolve(path.dirname(file));
|
|
107
244
|
const fsRoot = path.parse(currentDir).root;
|
|
108
|
-
const stop = stopDir
|
|
245
|
+
const stop = stopDir ? path.resolve(stopDir) : fsRoot;
|
|
109
246
|
|
|
110
247
|
while (currentDir !== fsRoot) {
|
|
111
248
|
// Bail out if we've reached the stop boundary
|
|
@@ -160,6 +297,7 @@ export const createRootDetector = NearestRoot;
|
|
|
160
297
|
export const TypeScriptServer: LSPServerInfo = {
|
|
161
298
|
id: "typescript",
|
|
162
299
|
name: "TypeScript Language Server",
|
|
300
|
+
installPolicy: "managed",
|
|
163
301
|
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
|
|
164
302
|
root: createRootDetector([
|
|
165
303
|
"package-lock.json",
|
|
@@ -169,9 +307,10 @@ export const TypeScriptServer: LSPServerInfo = {
|
|
|
169
307
|
"yarn.lock",
|
|
170
308
|
"package.json",
|
|
171
309
|
]),
|
|
172
|
-
async spawn(root) {
|
|
310
|
+
async spawn(root, options) {
|
|
173
311
|
const path = await import("node:path");
|
|
174
312
|
const fs = await import("node:fs/promises");
|
|
313
|
+
let source: "direct" | "managed" = "direct";
|
|
175
314
|
|
|
176
315
|
// Find typescript-language-server - prefer local project version
|
|
177
316
|
let lspPath: string | undefined;
|
|
@@ -201,7 +340,10 @@ export const TypeScriptServer: LSPServerInfo = {
|
|
|
201
340
|
|
|
202
341
|
// Fall back to auto-installed version
|
|
203
342
|
if (!lspPath) {
|
|
204
|
-
|
|
343
|
+
if (canInstall(options?.allowInstall)) {
|
|
344
|
+
lspPath = await ensureTool("typescript-language-server");
|
|
345
|
+
source = "managed";
|
|
346
|
+
}
|
|
205
347
|
if (!lspPath) {
|
|
206
348
|
console.error("[lsp] typescript-language-server not found");
|
|
207
349
|
return undefined;
|
|
@@ -242,6 +384,26 @@ export const TypeScriptServer: LSPServerInfo = {
|
|
|
242
384
|
}
|
|
243
385
|
}
|
|
244
386
|
|
|
387
|
+
if (!tsserverPath && canInstall(options?.allowInstall)) {
|
|
388
|
+
const tscPath = await ensureTool("typescript");
|
|
389
|
+
if (tscPath) {
|
|
390
|
+
const managedTsserverCandidates = [
|
|
391
|
+
path.join(path.dirname(tscPath), "..", "typescript", "lib", "tsserver.js"),
|
|
392
|
+
path.join(path.dirname(tscPath), "..", "..", "typescript", "lib", "tsserver.js"),
|
|
393
|
+
];
|
|
394
|
+
for (const checkPath of managedTsserverCandidates) {
|
|
395
|
+
try {
|
|
396
|
+
await fs.access(checkPath);
|
|
397
|
+
tsserverPath = checkPath;
|
|
398
|
+
source = "managed";
|
|
399
|
+
break;
|
|
400
|
+
} catch {
|
|
401
|
+
/* not found */
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
245
407
|
// Use absolute path and proper environment
|
|
246
408
|
const env = await getToolEnvironment();
|
|
247
409
|
const proc = await launchLSP(lspPath, ["--stdio"], {
|
|
@@ -254,6 +416,7 @@ export const TypeScriptServer: LSPServerInfo = {
|
|
|
254
416
|
|
|
255
417
|
return {
|
|
256
418
|
process: proc,
|
|
419
|
+
source,
|
|
257
420
|
initialization: tsserverPath
|
|
258
421
|
? { tsserver: { path: tsserverPath } }
|
|
259
422
|
: undefined,
|
|
@@ -264,8 +427,10 @@ export const TypeScriptServer: LSPServerInfo = {
|
|
|
264
427
|
export const PythonServer: LSPServerInfo = {
|
|
265
428
|
id: "python",
|
|
266
429
|
name: "Pyright Language Server",
|
|
430
|
+
installPolicy: "managed",
|
|
267
431
|
extensions: [".py", ".pyi"],
|
|
268
432
|
root: createRootDetector([
|
|
433
|
+
".git",
|
|
269
434
|
"pyproject.toml",
|
|
270
435
|
"setup.py",
|
|
271
436
|
"setup.cfg",
|
|
@@ -273,10 +438,11 @@ export const PythonServer: LSPServerInfo = {
|
|
|
273
438
|
"Pipfile",
|
|
274
439
|
"poetry.lock",
|
|
275
440
|
]),
|
|
276
|
-
async spawn(root) {
|
|
441
|
+
async spawn(root, options) {
|
|
277
442
|
const path = await import("node:path");
|
|
278
443
|
const fs = await import("node:fs/promises");
|
|
279
444
|
const env = await getToolEnvironment();
|
|
445
|
+
let source: "direct" | "managed" | "package-manager" = "direct";
|
|
280
446
|
|
|
281
447
|
// Strategy 1: Find pyright - prefer local project version
|
|
282
448
|
let pyrightPath: string | undefined;
|
|
@@ -301,7 +467,10 @@ export const PythonServer: LSPServerInfo = {
|
|
|
301
467
|
|
|
302
468
|
// Strategy 2: Fall back to auto-installed version
|
|
303
469
|
if (!pyrightPath) {
|
|
304
|
-
|
|
470
|
+
if (canInstall(options?.allowInstall)) {
|
|
471
|
+
pyrightPath = await ensureTool("pyright");
|
|
472
|
+
source = "managed";
|
|
473
|
+
}
|
|
305
474
|
if (!pyrightPath) {
|
|
306
475
|
console.error("[lsp] pyright not found, falling back to npx");
|
|
307
476
|
}
|
|
@@ -327,7 +496,9 @@ export const PythonServer: LSPServerInfo = {
|
|
|
327
496
|
try {
|
|
328
497
|
await fs.access(candidate);
|
|
329
498
|
langserverPath = candidate;
|
|
330
|
-
|
|
499
|
+
if (process.env.PI_LENS_DEBUG === "1") {
|
|
500
|
+
console.error(`[lsp] Found pyright-langserver: ${candidate}`);
|
|
501
|
+
}
|
|
331
502
|
break;
|
|
332
503
|
} catch {
|
|
333
504
|
/* not found */
|
|
@@ -344,12 +515,27 @@ export const PythonServer: LSPServerInfo = {
|
|
|
344
515
|
env,
|
|
345
516
|
});
|
|
346
517
|
} else {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
518
|
+
if (!canInstall(options?.allowInstall)) {
|
|
519
|
+
try {
|
|
520
|
+
proc = await launchLSP("pyright-langserver", ["--stdio"], {
|
|
521
|
+
cwd: root,
|
|
522
|
+
env,
|
|
523
|
+
});
|
|
524
|
+
} catch {
|
|
525
|
+
return undefined;
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
// Fallback to npx for auto-download
|
|
529
|
+
console.error("[lsp] Falling back to npx for pyright-langserver");
|
|
530
|
+
const managed = await launchViaPackageManagerWithPolicy(
|
|
531
|
+
"pyright-langserver",
|
|
532
|
+
["--stdio"],
|
|
533
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
534
|
+
);
|
|
535
|
+
if (!managed) return undefined;
|
|
536
|
+
proc = managed;
|
|
537
|
+
source = "package-manager";
|
|
538
|
+
}
|
|
353
539
|
}
|
|
354
540
|
|
|
355
541
|
// Detect virtual environment
|
|
@@ -377,7 +563,7 @@ export const PythonServer: LSPServerInfo = {
|
|
|
377
563
|
}
|
|
378
564
|
}
|
|
379
565
|
|
|
380
|
-
return { process: proc, initialization };
|
|
566
|
+
return { process: proc, initialization, source };
|
|
381
567
|
},
|
|
382
568
|
};
|
|
383
569
|
|
|
@@ -385,13 +571,14 @@ export const GoServer: LSPServerInfo = {
|
|
|
385
571
|
id: "go",
|
|
386
572
|
name: "gopls",
|
|
387
573
|
extensions: [".go"],
|
|
388
|
-
|
|
389
|
-
|
|
574
|
+
installPolicy: "interactive",
|
|
575
|
+
root: PriorityRoot([["go.work"], ["go.mod", "go.sum"]]),
|
|
576
|
+
async spawn(root, options) {
|
|
390
577
|
const proc = await spawnWithInteractiveInstall(
|
|
391
578
|
"go",
|
|
392
579
|
"gopls",
|
|
393
580
|
[],
|
|
394
|
-
{ cwd: root },
|
|
581
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
395
582
|
async () => await launchLSP("gopls", [], { cwd: root }),
|
|
396
583
|
);
|
|
397
584
|
// gopls works best with minimal initialization options
|
|
@@ -414,13 +601,14 @@ export const RustServer: LSPServerInfo = {
|
|
|
414
601
|
id: "rust",
|
|
415
602
|
name: "rust-analyzer",
|
|
416
603
|
extensions: [".rs"],
|
|
604
|
+
installPolicy: "interactive",
|
|
417
605
|
root: createRootDetector(["Cargo.toml", "Cargo.lock"]),
|
|
418
|
-
async spawn(root) {
|
|
606
|
+
async spawn(root, options) {
|
|
419
607
|
const proc = await spawnWithInteractiveInstall(
|
|
420
608
|
"rust",
|
|
421
609
|
"rust-analyzer",
|
|
422
610
|
[],
|
|
423
|
-
{ cwd: root },
|
|
611
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
424
612
|
async () => await launchLSP("rust-analyzer", [], { cwd: root }),
|
|
425
613
|
);
|
|
426
614
|
// rust-analyzer needs minimal initialization to avoid capability mismatches
|
|
@@ -443,165 +631,122 @@ export const RustServer: LSPServerInfo = {
|
|
|
443
631
|
export const RubyServer: LSPServerInfo = {
|
|
444
632
|
id: "ruby",
|
|
445
633
|
name: "Ruby LSP",
|
|
634
|
+
installPolicy: "interactive",
|
|
446
635
|
extensions: [".rb", ".rake", ".gemspec", ".ru"],
|
|
447
|
-
root:
|
|
448
|
-
async spawn(root) {
|
|
636
|
+
root: PriorityRoot([["Gemfile", ".ruby-version"], [".git"]]),
|
|
637
|
+
async spawn(root, options) {
|
|
449
638
|
// Try ruby-lsp first (prompts to install via gem if missing), fall back to solargraph
|
|
450
639
|
const proc = await spawnWithInteractiveInstall(
|
|
451
640
|
"ruby",
|
|
452
641
|
"ruby-lsp",
|
|
453
642
|
[],
|
|
454
|
-
{ cwd: root },
|
|
643
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
455
644
|
async () => {
|
|
456
645
|
try {
|
|
457
646
|
return await launchLSP("ruby-lsp", [], { cwd: root });
|
|
458
647
|
} catch {
|
|
459
|
-
|
|
648
|
+
const fallback = await launchWithDirectOrPackageManager(
|
|
649
|
+
nodeBinCandidates(root, "solargraph"),
|
|
650
|
+
"solargraph",
|
|
651
|
+
["stdio"],
|
|
652
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
653
|
+
);
|
|
654
|
+
if (!fallback) throw new Error("ENOENT: command not found");
|
|
655
|
+
return fallback.process;
|
|
460
656
|
}
|
|
461
657
|
},
|
|
462
658
|
);
|
|
463
|
-
return proc ? { process: proc } : undefined;
|
|
659
|
+
return proc ? { process: proc, source: "interactive" } : undefined;
|
|
464
660
|
},
|
|
465
661
|
};
|
|
466
662
|
|
|
467
663
|
export const PHPServer: LSPServerInfo = {
|
|
468
664
|
id: "php",
|
|
469
665
|
name: "Intelephense",
|
|
666
|
+
installPolicy: "package-manager",
|
|
470
667
|
extensions: [".php"],
|
|
471
668
|
root: createRootDetector(["composer.json", "composer.lock"]),
|
|
472
|
-
async spawn(root) {
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
669
|
+
async spawn(root, options) {
|
|
670
|
+
const launched = await launchWithDirectOrPackageManager(
|
|
671
|
+
nodeBinCandidates(root, "intelephense"),
|
|
672
|
+
"intelephense",
|
|
673
|
+
["--stdio"],
|
|
674
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
675
|
+
);
|
|
676
|
+
if (!launched) return undefined;
|
|
476
677
|
return {
|
|
477
|
-
process:
|
|
678
|
+
process: launched.process,
|
|
679
|
+
source: launched.source,
|
|
478
680
|
initialization: { storagePath: path.join(__dirname, ".intelephense") },
|
|
479
681
|
};
|
|
480
682
|
},
|
|
481
683
|
};
|
|
482
684
|
|
|
483
|
-
export const CSharpServer
|
|
685
|
+
export const CSharpServer = createInteractiveServer({
|
|
484
686
|
id: "csharp",
|
|
485
687
|
name: "csharp-ls",
|
|
486
688
|
extensions: [".cs"],
|
|
487
689
|
root: createRootDetector([".sln", ".csproj", ".slnx"]),
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
"csharp-ls",
|
|
492
|
-
[],
|
|
493
|
-
{ cwd: root },
|
|
494
|
-
async () => await launchLSP("csharp-ls", [], { cwd: root }),
|
|
495
|
-
);
|
|
496
|
-
return proc ? { process: proc } : undefined;
|
|
497
|
-
},
|
|
498
|
-
};
|
|
690
|
+
language: "csharp",
|
|
691
|
+
command: "csharp-ls",
|
|
692
|
+
});
|
|
499
693
|
|
|
500
|
-
export const FSharpServer
|
|
694
|
+
export const FSharpServer = createInteractiveServer({
|
|
501
695
|
id: "fsharp",
|
|
502
696
|
name: "FSAutocomplete",
|
|
503
697
|
extensions: [".fs", ".fsi", ".fsx"],
|
|
504
698
|
root: createRootDetector([".sln", ".fsproj"]),
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
"fsautocomplete",
|
|
509
|
-
[],
|
|
510
|
-
{ cwd: root },
|
|
511
|
-
async () => await launchLSP("fsautocomplete", [], { cwd: root }),
|
|
512
|
-
);
|
|
513
|
-
return proc ? { process: proc } : undefined;
|
|
514
|
-
},
|
|
515
|
-
};
|
|
699
|
+
language: "fsharp",
|
|
700
|
+
command: "fsautocomplete",
|
|
701
|
+
});
|
|
516
702
|
|
|
517
|
-
export const JavaServer
|
|
703
|
+
export const JavaServer = createInteractiveServer({
|
|
518
704
|
id: "java",
|
|
519
705
|
name: "JDT Language Server",
|
|
520
706
|
extensions: [".java"],
|
|
521
707
|
root: createRootDetector(["pom.xml", "build.gradle", ".classpath"]),
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
"java",
|
|
526
|
-
jdtlsPath,
|
|
527
|
-
[],
|
|
528
|
-
{ cwd: root },
|
|
529
|
-
async () => await launchLSP(jdtlsPath, [], { cwd: root }),
|
|
530
|
-
);
|
|
531
|
-
return proc ? { process: proc } : undefined;
|
|
532
|
-
},
|
|
533
|
-
};
|
|
708
|
+
language: "java",
|
|
709
|
+
command: () => process.env.JDTLS_PATH || "jdtls",
|
|
710
|
+
});
|
|
534
711
|
|
|
535
|
-
export const KotlinServer
|
|
712
|
+
export const KotlinServer = createInteractiveServer({
|
|
536
713
|
id: "kotlin",
|
|
537
714
|
name: "Kotlin Language Server",
|
|
538
715
|
extensions: [".kt", ".kts"],
|
|
539
716
|
root: createRootDetector(["build.gradle.kts", "build.gradle", "pom.xml"]),
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
"kotlin-language-server",
|
|
544
|
-
[],
|
|
545
|
-
{ cwd: root },
|
|
546
|
-
async () => await launchLSP("kotlin-language-server", [], { cwd: root }),
|
|
547
|
-
);
|
|
548
|
-
return proc ? { process: proc } : undefined;
|
|
549
|
-
},
|
|
550
|
-
};
|
|
717
|
+
language: "kotlin",
|
|
718
|
+
command: "kotlin-language-server",
|
|
719
|
+
});
|
|
551
720
|
|
|
552
|
-
export const SwiftServer
|
|
721
|
+
export const SwiftServer = createInteractiveServer({
|
|
553
722
|
id: "swift",
|
|
554
723
|
name: "SourceKit-LSP",
|
|
555
724
|
extensions: [".swift"],
|
|
556
725
|
root: createRootDetector(["Package.swift"]),
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
"sourcekit-lsp",
|
|
561
|
-
[],
|
|
562
|
-
{ cwd: root },
|
|
563
|
-
async () => await launchLSP("sourcekit-lsp", [], { cwd: root }),
|
|
564
|
-
);
|
|
565
|
-
return proc ? { process: proc } : undefined;
|
|
566
|
-
},
|
|
567
|
-
};
|
|
726
|
+
language: "swift",
|
|
727
|
+
command: "sourcekit-lsp",
|
|
728
|
+
});
|
|
568
729
|
|
|
569
|
-
export const DartServer
|
|
730
|
+
export const DartServer = createInteractiveServer({
|
|
570
731
|
id: "dart",
|
|
571
732
|
name: "Dart Analysis Server",
|
|
572
733
|
extensions: [".dart"],
|
|
573
734
|
root: createRootDetector(["pubspec.yaml"]),
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
["language-server", "--protocol=lsp"],
|
|
579
|
-
{ cwd: root },
|
|
580
|
-
async () =>
|
|
581
|
-
await launchLSP("dart", ["language-server", "--protocol=lsp"], { cwd: root }),
|
|
582
|
-
);
|
|
583
|
-
return proc ? { process: proc } : undefined;
|
|
584
|
-
},
|
|
585
|
-
};
|
|
735
|
+
language: "dart",
|
|
736
|
+
command: "dart",
|
|
737
|
+
args: ["language-server", "--protocol=lsp"],
|
|
738
|
+
});
|
|
586
739
|
|
|
587
|
-
export const LuaServer
|
|
740
|
+
export const LuaServer = createInteractiveServer({
|
|
588
741
|
id: "lua",
|
|
589
742
|
name: "Lua Language Server",
|
|
590
743
|
extensions: [".lua"],
|
|
591
744
|
root: createRootDetector([".luarc.json", ".luacheckrc"]),
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
"lua-language-server",
|
|
596
|
-
[],
|
|
597
|
-
{ cwd: root },
|
|
598
|
-
async () => await launchLSP("lua-language-server", [], { cwd: root }),
|
|
599
|
-
);
|
|
600
|
-
return proc ? { process: proc } : undefined;
|
|
601
|
-
},
|
|
602
|
-
};
|
|
745
|
+
language: "lua",
|
|
746
|
+
command: "lua-language-server",
|
|
747
|
+
});
|
|
603
748
|
|
|
604
|
-
export const CppServer
|
|
749
|
+
export const CppServer = createInteractiveServer({
|
|
605
750
|
id: "cpp",
|
|
606
751
|
name: "clangd",
|
|
607
752
|
extensions: [".c", ".cpp", ".cc", ".cxx", ".h", ".hpp"],
|
|
@@ -611,167 +756,99 @@ export const CppServer: LSPServerInfo = {
|
|
|
611
756
|
"CMakeLists.txt",
|
|
612
757
|
"Makefile",
|
|
613
758
|
]),
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
["--background-index"],
|
|
619
|
-
{ cwd: root },
|
|
620
|
-
async () => await launchLSP("clangd", ["--background-index"], { cwd: root }),
|
|
621
|
-
);
|
|
622
|
-
return proc ? { process: proc } : undefined;
|
|
623
|
-
},
|
|
624
|
-
};
|
|
759
|
+
language: "cpp",
|
|
760
|
+
command: "clangd",
|
|
761
|
+
args: ["--background-index"],
|
|
762
|
+
});
|
|
625
763
|
|
|
626
|
-
export const ZigServer
|
|
764
|
+
export const ZigServer = createInteractiveServer({
|
|
627
765
|
id: "zig",
|
|
628
766
|
name: "ZLS",
|
|
629
767
|
extensions: [".zig", ".zon"],
|
|
630
768
|
root: createRootDetector(["build.zig"]),
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
"zls",
|
|
635
|
-
[],
|
|
636
|
-
{ cwd: root },
|
|
637
|
-
async () => await launchLSP("zls", [], { cwd: root }),
|
|
638
|
-
);
|
|
639
|
-
return proc ? { process: proc } : undefined;
|
|
640
|
-
},
|
|
641
|
-
};
|
|
769
|
+
language: "zig",
|
|
770
|
+
command: "zls",
|
|
771
|
+
});
|
|
642
772
|
|
|
643
|
-
export const HaskellServer
|
|
773
|
+
export const HaskellServer = createInteractiveServer({
|
|
644
774
|
id: "haskell",
|
|
645
775
|
name: "Haskell Language Server",
|
|
646
776
|
extensions: [".hs", ".lhs"],
|
|
647
777
|
root: createRootDetector(["stack.yaml", "cabal.project", "*.cabal"]),
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
["--lsp"],
|
|
653
|
-
{ cwd: root },
|
|
654
|
-
async () =>
|
|
655
|
-
await launchLSP("haskell-language-server-wrapper", ["--lsp"], { cwd: root }),
|
|
656
|
-
);
|
|
657
|
-
return proc ? { process: proc } : undefined;
|
|
658
|
-
},
|
|
659
|
-
};
|
|
778
|
+
language: "haskell",
|
|
779
|
+
command: "haskell-language-server-wrapper",
|
|
780
|
+
args: ["--lsp"],
|
|
781
|
+
});
|
|
660
782
|
|
|
661
|
-
export const ElixirServer
|
|
783
|
+
export const ElixirServer = createInteractiveServer({
|
|
662
784
|
id: "elixir",
|
|
663
785
|
name: "ElixirLS",
|
|
664
786
|
extensions: [".ex", ".exs"],
|
|
665
787
|
root: createRootDetector(["mix.exs"]),
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
"elixir-ls",
|
|
670
|
-
[],
|
|
671
|
-
{ cwd: root },
|
|
672
|
-
async () => await launchLSP("elixir-ls", [], { cwd: root }),
|
|
673
|
-
);
|
|
674
|
-
return proc ? { process: proc } : undefined;
|
|
675
|
-
},
|
|
676
|
-
};
|
|
788
|
+
language: "elixir",
|
|
789
|
+
command: "elixir-ls",
|
|
790
|
+
});
|
|
677
791
|
|
|
678
|
-
export const GleamServer
|
|
792
|
+
export const GleamServer = createInteractiveServer({
|
|
679
793
|
id: "gleam",
|
|
680
794
|
name: "Gleam LSP",
|
|
681
795
|
extensions: [".gleam"],
|
|
682
796
|
root: createRootDetector(["gleam.toml"]),
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
["lsp"],
|
|
688
|
-
{ cwd: root },
|
|
689
|
-
async () => await launchLSP("gleam", ["lsp"], { cwd: root }),
|
|
690
|
-
);
|
|
691
|
-
return proc ? { process: proc } : undefined;
|
|
692
|
-
},
|
|
693
|
-
};
|
|
797
|
+
language: "gleam",
|
|
798
|
+
command: "gleam",
|
|
799
|
+
args: ["lsp"],
|
|
800
|
+
});
|
|
694
801
|
|
|
695
|
-
export const OCamlServer
|
|
802
|
+
export const OCamlServer = createInteractiveServer({
|
|
696
803
|
id: "ocaml",
|
|
697
804
|
name: "ocamllsp",
|
|
698
805
|
extensions: [".ml", ".mli"],
|
|
699
806
|
root: createRootDetector(["dune-project", "opam"]),
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
"ocamllsp",
|
|
704
|
-
[],
|
|
705
|
-
{ cwd: root },
|
|
706
|
-
async () => await launchLSP("ocamllsp", [], { cwd: root }),
|
|
707
|
-
);
|
|
708
|
-
return proc ? { process: proc } : undefined;
|
|
709
|
-
},
|
|
710
|
-
};
|
|
807
|
+
language: "ocaml",
|
|
808
|
+
command: "ocamllsp",
|
|
809
|
+
});
|
|
711
810
|
|
|
712
|
-
export const ClojureServer
|
|
811
|
+
export const ClojureServer = createInteractiveServer({
|
|
713
812
|
id: "clojure",
|
|
714
813
|
name: "Clojure LSP",
|
|
715
814
|
extensions: [".clj", ".cljs", ".cljc", ".edn"],
|
|
716
815
|
root: createRootDetector(["deps.edn", "project.clj"]),
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
"clojure-lsp",
|
|
721
|
-
[],
|
|
722
|
-
{ cwd: root },
|
|
723
|
-
async () => await launchLSP("clojure-lsp", [], { cwd: root }),
|
|
724
|
-
);
|
|
725
|
-
return proc ? { process: proc } : undefined;
|
|
726
|
-
},
|
|
727
|
-
};
|
|
816
|
+
language: "clojure",
|
|
817
|
+
command: "clojure-lsp",
|
|
818
|
+
});
|
|
728
819
|
|
|
729
|
-
export const TerraformServer
|
|
820
|
+
export const TerraformServer = createInteractiveServer({
|
|
730
821
|
id: "terraform",
|
|
731
822
|
name: "Terraform LSP",
|
|
732
823
|
extensions: [".tf", ".tfvars"],
|
|
733
824
|
root: createRootDetector([".terraform.lock.hcl"]),
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
["serve"],
|
|
739
|
-
{ cwd: root },
|
|
740
|
-
async () => await launchLSP("terraform-ls", ["serve"], { cwd: root }),
|
|
741
|
-
);
|
|
742
|
-
return proc ? { process: proc } : undefined;
|
|
743
|
-
},
|
|
744
|
-
};
|
|
825
|
+
language: "terraform",
|
|
826
|
+
command: "terraform-ls",
|
|
827
|
+
args: ["serve"],
|
|
828
|
+
});
|
|
745
829
|
|
|
746
|
-
export const NixServer
|
|
830
|
+
export const NixServer = createInteractiveServer({
|
|
747
831
|
id: "nix",
|
|
748
832
|
name: "nixd",
|
|
749
833
|
extensions: [".nix"],
|
|
750
834
|
root: createRootDetector(["flake.nix"]),
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
"nixd",
|
|
755
|
-
[],
|
|
756
|
-
{ cwd: root },
|
|
757
|
-
async () => await launchLSP("nixd", [], { cwd: root }),
|
|
758
|
-
);
|
|
759
|
-
return proc ? { process: proc } : undefined;
|
|
760
|
-
},
|
|
761
|
-
};
|
|
835
|
+
language: "nix",
|
|
836
|
+
command: "nixd",
|
|
837
|
+
});
|
|
762
838
|
|
|
763
839
|
export const BashServer: LSPServerInfo = {
|
|
764
840
|
id: "bash",
|
|
765
841
|
name: "Bash Language Server",
|
|
766
842
|
extensions: [".sh", ".bash", ".zsh"],
|
|
843
|
+
installPolicy: "interactive",
|
|
767
844
|
root: async () => process.cwd(),
|
|
768
|
-
async spawn() {
|
|
845
|
+
async spawn(_root, options) {
|
|
769
846
|
const cwd = process.cwd();
|
|
770
847
|
const proc = await spawnWithInteractiveInstall(
|
|
771
848
|
"bash",
|
|
772
849
|
"bash-language-server",
|
|
773
850
|
["start"],
|
|
774
|
-
{ cwd },
|
|
851
|
+
{ cwd, allowInstall: options?.allowInstall },
|
|
775
852
|
async () => await launchLSP("bash-language-server", ["start"], {}),
|
|
776
853
|
);
|
|
777
854
|
return proc ? { process: proc } : undefined;
|
|
@@ -781,16 +858,18 @@ export const BashServer: LSPServerInfo = {
|
|
|
781
858
|
export const DockerServer: LSPServerInfo = {
|
|
782
859
|
id: "docker",
|
|
783
860
|
name: "Dockerfile Language Server",
|
|
861
|
+
installPolicy: "package-manager",
|
|
784
862
|
extensions: [".dockerfile", "Dockerfile"],
|
|
785
|
-
root:
|
|
786
|
-
async spawn() {
|
|
787
|
-
|
|
788
|
-
|
|
863
|
+
root: PriorityRoot([["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"], [".git"]]),
|
|
864
|
+
async spawn(_root, options) {
|
|
865
|
+
const launched = await launchWithDirectOrPackageManager(
|
|
866
|
+
nodeBinCandidates(process.cwd(), "docker-langserver"),
|
|
789
867
|
"dockerfile-language-server-nodejs",
|
|
790
868
|
["--stdio"],
|
|
791
|
-
{},
|
|
869
|
+
{ cwd: process.cwd(), allowInstall: options?.allowInstall },
|
|
792
870
|
);
|
|
793
|
-
|
|
871
|
+
if (!launched) return undefined;
|
|
872
|
+
return { process: launched.process, source: launched.source };
|
|
794
873
|
},
|
|
795
874
|
};
|
|
796
875
|
|
|
@@ -798,14 +877,15 @@ export const YamlServer: LSPServerInfo = {
|
|
|
798
877
|
id: "yaml",
|
|
799
878
|
name: "YAML Language Server",
|
|
800
879
|
extensions: [".yaml", ".yml"],
|
|
801
|
-
|
|
802
|
-
|
|
880
|
+
installPolicy: "interactive",
|
|
881
|
+
root: PriorityRoot([[".yamllint", "yamllint.yml", "yamllint.yaml", "pyproject.toml"], [".git"]]),
|
|
882
|
+
async spawn(_root, options) {
|
|
803
883
|
const cwd = process.cwd();
|
|
804
884
|
const proc = await spawnWithInteractiveInstall(
|
|
805
885
|
"yaml",
|
|
806
886
|
"yaml-language-server",
|
|
807
887
|
["--stdio"],
|
|
808
|
-
{ cwd },
|
|
888
|
+
{ cwd, allowInstall: options?.allowInstall },
|
|
809
889
|
async () => await launchLSP("yaml-language-server", ["--stdio"], {}),
|
|
810
890
|
);
|
|
811
891
|
return proc ? { process: proc } : undefined;
|
|
@@ -816,14 +896,15 @@ export const JsonServer: LSPServerInfo = {
|
|
|
816
896
|
id: "json",
|
|
817
897
|
name: "VSCode JSON Language Server",
|
|
818
898
|
extensions: [".json", ".jsonc"],
|
|
819
|
-
|
|
820
|
-
|
|
899
|
+
installPolicy: "interactive",
|
|
900
|
+
root: PriorityRoot([["package.json", "tsconfig.json", "jsconfig.json"], [".git"]]),
|
|
901
|
+
async spawn(_root, options) {
|
|
821
902
|
const cwd = process.cwd();
|
|
822
903
|
const proc = await spawnWithInteractiveInstall(
|
|
823
904
|
"json",
|
|
824
905
|
"vscode-json-language-server",
|
|
825
906
|
["--stdio"],
|
|
826
|
-
{ cwd },
|
|
907
|
+
{ cwd, allowInstall: options?.allowInstall },
|
|
827
908
|
async () =>
|
|
828
909
|
await launchLSP("vscode-json-language-server", ["--stdio"], {}),
|
|
829
910
|
);
|
|
@@ -834,16 +915,18 @@ export const JsonServer: LSPServerInfo = {
|
|
|
834
915
|
export const PrismaServer: LSPServerInfo = {
|
|
835
916
|
id: "prisma",
|
|
836
917
|
name: "Prisma Language Server",
|
|
918
|
+
installPolicy: "package-manager",
|
|
837
919
|
extensions: [".prisma"],
|
|
838
920
|
root: createRootDetector(["prisma/schema.prisma"]),
|
|
839
|
-
async spawn(root) {
|
|
840
|
-
|
|
841
|
-
|
|
921
|
+
async spawn(root, options) {
|
|
922
|
+
const launched = await launchWithDirectOrPackageManager(
|
|
923
|
+
nodeBinCandidates(root, "prisma-language-server"),
|
|
842
924
|
"@prisma/language-server",
|
|
843
925
|
["--stdio"],
|
|
844
|
-
{ cwd: root },
|
|
926
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
845
927
|
);
|
|
846
|
-
|
|
928
|
+
if (!launched) return undefined;
|
|
929
|
+
return { process: launched.process, source: launched.source };
|
|
847
930
|
},
|
|
848
931
|
};
|
|
849
932
|
|
|
@@ -853,6 +936,7 @@ export const VueServer: LSPServerInfo = {
|
|
|
853
936
|
id: "vue",
|
|
854
937
|
name: "Vue Language Server",
|
|
855
938
|
extensions: [".vue"],
|
|
939
|
+
installPolicy: "package-manager",
|
|
856
940
|
root: createRootDetector([
|
|
857
941
|
"package-lock.json",
|
|
858
942
|
"bun.lockb",
|
|
@@ -860,16 +944,15 @@ export const VueServer: LSPServerInfo = {
|
|
|
860
944
|
"pnpm-lock.yaml",
|
|
861
945
|
"yarn.lock",
|
|
862
946
|
]),
|
|
863
|
-
async spawn(root) {
|
|
864
|
-
|
|
865
|
-
|
|
947
|
+
async spawn(root, options) {
|
|
948
|
+
const launched = await launchWithDirectOrPackageManager(
|
|
949
|
+
nodeBinCandidates(root, "vue-language-server"),
|
|
866
950
|
"@vue/language-server",
|
|
867
951
|
["--stdio"],
|
|
868
|
-
{
|
|
869
|
-
cwd: root,
|
|
870
|
-
},
|
|
952
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
871
953
|
);
|
|
872
|
-
|
|
954
|
+
if (!launched) return undefined;
|
|
955
|
+
return { process: launched.process, source: launched.source };
|
|
873
956
|
},
|
|
874
957
|
};
|
|
875
958
|
|
|
@@ -877,6 +960,7 @@ export const SvelteServer: LSPServerInfo = {
|
|
|
877
960
|
id: "svelte",
|
|
878
961
|
name: "Svelte Language Server",
|
|
879
962
|
extensions: [".svelte"],
|
|
963
|
+
installPolicy: "package-manager",
|
|
880
964
|
root: createRootDetector([
|
|
881
965
|
"package-lock.json",
|
|
882
966
|
"bun.lockb",
|
|
@@ -884,20 +968,22 @@ export const SvelteServer: LSPServerInfo = {
|
|
|
884
968
|
"pnpm-lock.yaml",
|
|
885
969
|
"yarn.lock",
|
|
886
970
|
]),
|
|
887
|
-
async spawn(root) {
|
|
888
|
-
|
|
889
|
-
|
|
971
|
+
async spawn(root, options) {
|
|
972
|
+
const launched = await launchWithDirectOrPackageManager(
|
|
973
|
+
[...nodeBinCandidates(root, "svelteserver"), ...nodeBinCandidates(root, "svelte-language-server")],
|
|
890
974
|
"svelte-language-server",
|
|
891
975
|
["--stdio"],
|
|
892
|
-
{ cwd: root },
|
|
976
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
893
977
|
);
|
|
894
|
-
|
|
978
|
+
if (!launched) return undefined;
|
|
979
|
+
return { process: launched.process, source: launched.source };
|
|
895
980
|
},
|
|
896
981
|
};
|
|
897
982
|
|
|
898
983
|
export const ESLintServer: LSPServerInfo = {
|
|
899
984
|
id: "eslint",
|
|
900
985
|
name: "ESLint Language Server",
|
|
986
|
+
installPolicy: "package-manager",
|
|
901
987
|
extensions: [".js", ".jsx", ".vue", ".svelte"], // Note: .ts/.tsx handled by TypeScript LSP + Biome
|
|
902
988
|
root: createRootDetector([
|
|
903
989
|
".eslintrc",
|
|
@@ -907,15 +993,17 @@ export const ESLintServer: LSPServerInfo = {
|
|
|
907
993
|
"eslint.config.mjs",
|
|
908
994
|
"package.json",
|
|
909
995
|
]),
|
|
910
|
-
async spawn(root) {
|
|
996
|
+
async spawn(root, options) {
|
|
911
997
|
// Try via package manager (npx) since it's not auto-installed
|
|
912
998
|
try {
|
|
913
|
-
const
|
|
999
|
+
const launched = await launchWithDirectOrPackageManager(
|
|
1000
|
+
nodeBinCandidates(root, "vscode-eslint-language-server"),
|
|
914
1001
|
"vscode-eslint-language-server",
|
|
915
1002
|
["--stdio"],
|
|
916
|
-
{ cwd: root },
|
|
1003
|
+
{ cwd: root, allowInstall: options?.allowInstall },
|
|
917
1004
|
);
|
|
918
|
-
|
|
1005
|
+
if (!launched) return undefined;
|
|
1006
|
+
return { process: launched.process, source: launched.source };
|
|
919
1007
|
} catch {
|
|
920
1008
|
// Fall back to global install message
|
|
921
1009
|
console.error(
|
|
@@ -929,16 +1017,18 @@ export const ESLintServer: LSPServerInfo = {
|
|
|
929
1017
|
export const CssServer: LSPServerInfo = {
|
|
930
1018
|
id: "css",
|
|
931
1019
|
name: "CSS Language Server",
|
|
1020
|
+
installPolicy: "package-manager",
|
|
932
1021
|
extensions: [".css", ".scss", ".sass", ".less"],
|
|
933
|
-
root:
|
|
934
|
-
async spawn() {
|
|
935
|
-
|
|
936
|
-
|
|
1022
|
+
root: PriorityRoot([["package.json", "postcss.config.js", "tailwind.config.js", "vite.config.ts"], [".git"]]),
|
|
1023
|
+
async spawn(_root, options) {
|
|
1024
|
+
const launched = await launchWithDirectOrPackageManager(
|
|
1025
|
+
nodeBinCandidates(process.cwd(), "vscode-css-language-server"),
|
|
937
1026
|
"vscode-css-languageserver",
|
|
938
1027
|
["--stdio"],
|
|
939
|
-
{},
|
|
1028
|
+
{ cwd: process.cwd(), allowInstall: options?.allowInstall },
|
|
940
1029
|
);
|
|
941
|
-
|
|
1030
|
+
if (!launched) return undefined;
|
|
1031
|
+
return { process: launched.process, source: launched.source };
|
|
942
1032
|
},
|
|
943
1033
|
};
|
|
944
1034
|
|