pi-lsp-lite 0.3.2 → 0.4.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/index.ts +12 -6
- package/package.json +1 -1
- package/src/client.ts +48 -11
- package/src/config.ts +24 -12
- package/src/format.ts +43 -12
- package/src/languages.ts +11 -0
- package/src/server-manager.ts +2 -2
- package/src/util.ts +6 -1
- package/.github/workflows/ci.yml +0 -30
- package/.github/workflows/integration.yml +0 -79
- package/.github/workflows/release.yml +0 -34
package/index.ts
CHANGED
|
@@ -4,9 +4,10 @@ import { languageForFile, checkExtensionOverlaps, builtinLanguages, type Languag
|
|
|
4
4
|
import { formatDiagnostics } from "./src/format.js";
|
|
5
5
|
import { DiagnosticSeverity } from "vscode-languageserver-protocol";
|
|
6
6
|
import { loadConfig, writeGlobalConfig, readGlobalConfig } from "./src/config.js";
|
|
7
|
-
import { fileUri, which } from "./src/util.js";
|
|
7
|
+
import { fileUri, which, isInsideCwd } from "./src/util.js";
|
|
8
8
|
import { installRegistry } from "./src/install-registry.js";
|
|
9
|
-
import { resolve
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
import { realpath } from "node:fs/promises";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
11
12
|
|
|
12
13
|
export default function (pi: ExtensionAPI) {
|
|
@@ -38,16 +39,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
38
39
|
const rawPath = event.input?.path;
|
|
39
40
|
const filePath = typeof rawPath === "string" ? rawPath : undefined;
|
|
40
41
|
if (!filePath) return;
|
|
42
|
+
if (event.isError) return;
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
let absolutePath: string;
|
|
45
|
+
try {
|
|
46
|
+
absolutePath = await realpath(resolve(ctx.cwd, filePath));
|
|
47
|
+
} catch {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!isInsideCwd(absolutePath, ctx.cwd)) return;
|
|
45
51
|
const langConfig = languageForFile(absolutePath, servers);
|
|
46
52
|
if (!langConfig) return;
|
|
47
53
|
|
|
48
54
|
try {
|
|
49
55
|
const result = await manager.handleEdit(absolutePath, langConfig, ctx.cwd);
|
|
50
|
-
const formatted = formatDiagnostics(filePath, result);
|
|
56
|
+
const formatted = formatDiagnostics(filePath, result, ctx.cwd);
|
|
51
57
|
if (!formatted) return;
|
|
52
58
|
|
|
53
59
|
ctx.ui.notify(formatted.trim(), "warning");
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface OtherFileDiagnostics {
|
|
|
21
21
|
uri: string;
|
|
22
22
|
errorCount: number;
|
|
23
23
|
warningCount: number;
|
|
24
|
+
firstDiagnostic?: { severity: number; line: number; col: number; message: string; source?: string };
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export interface DiagnosticResult {
|
|
@@ -53,6 +54,28 @@ function countDiagnostics(diags: Diagnostic[]): { errors: number; warnings: numb
|
|
|
53
54
|
return { errors, warnings };
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
function diagnosticFingerprint(d: Diagnostic): string {
|
|
58
|
+
return `${d.severity}:${d.range.start.line}:${d.range.start.character}:${d.message}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function fingerprintSet(diags: Diagnostic[]): Set<string> {
|
|
62
|
+
const set = new Set<string>();
|
|
63
|
+
for (const d of diags) {
|
|
64
|
+
if (d.severity === DiagnosticSeverity.Error || d.severity === DiagnosticSeverity.Warning) {
|
|
65
|
+
set.add(diagnosticFingerprint(d));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return set;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function setsEqual(a: Set<string>, b: Set<string>): boolean {
|
|
72
|
+
if (a.size !== b.size) return false;
|
|
73
|
+
for (const v of a) {
|
|
74
|
+
if (!b.has(v)) return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
56
79
|
export function createLspClient(child: ChildProcess): LspClient {
|
|
57
80
|
if (!child.stdout || !child.stdin) {
|
|
58
81
|
throw new Error("LSP child process must be spawned with stdio: pipe");
|
|
@@ -152,10 +175,10 @@ export function createLspClient(child: ChildProcess): LspClient {
|
|
|
152
175
|
async waitForDiagnostics(uri: string, timeoutMs: number): Promise<DiagnosticResult> {
|
|
153
176
|
const targetGen = uriGeneration.get(uri) ?? 0;
|
|
154
177
|
|
|
155
|
-
const preSnapshot = new Map<string,
|
|
178
|
+
const preSnapshot = new Map<string, Set<string>>();
|
|
156
179
|
for (const [trackedUri, entry] of diagnosticsMap) {
|
|
157
180
|
if (trackedUri !== uri) {
|
|
158
|
-
preSnapshot.set(trackedUri,
|
|
181
|
+
preSnapshot.set(trackedUri, fingerprintSet(entry.diagnostics));
|
|
159
182
|
}
|
|
160
183
|
}
|
|
161
184
|
|
|
@@ -163,13 +186,27 @@ export function createLspClient(child: ChildProcess): LspClient {
|
|
|
163
186
|
const result: OtherFileDiagnostics[] = [];
|
|
164
187
|
for (const [trackedUri, entry] of diagnosticsMap) {
|
|
165
188
|
if (trackedUri === uri) continue;
|
|
189
|
+
const postFp = fingerprintSet(entry.diagnostics);
|
|
190
|
+
const preFp = preSnapshot.get(trackedUri) ?? new Set();
|
|
191
|
+
if (setsEqual(postFp, preFp)) continue;
|
|
166
192
|
const post = countDiagnostics(entry.diagnostics);
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
193
|
+
const first =
|
|
194
|
+
entry.diagnostics.find((d) => d.severity === DiagnosticSeverity.Error) ??
|
|
195
|
+
entry.diagnostics.find((d) => d.severity === DiagnosticSeverity.Warning);
|
|
196
|
+
result.push({
|
|
197
|
+
uri: trackedUri,
|
|
198
|
+
errorCount: post.errors,
|
|
199
|
+
warningCount: post.warnings,
|
|
200
|
+
...(first && {
|
|
201
|
+
firstDiagnostic: {
|
|
202
|
+
severity: first.severity ?? DiagnosticSeverity.Error,
|
|
203
|
+
line: first.range.start.line,
|
|
204
|
+
col: first.range.start.character,
|
|
205
|
+
message: first.message,
|
|
206
|
+
...(first.source && { source: first.source }),
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
});
|
|
173
210
|
}
|
|
174
211
|
return result;
|
|
175
212
|
};
|
|
@@ -214,9 +251,9 @@ export function createLspClient(child: ChildProcess): LspClient {
|
|
|
214
251
|
// edited file is valid but dependents break
|
|
215
252
|
crossFileCallback = (changedUri: string) => {
|
|
216
253
|
if (settled || changedUri === uri) return;
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
254
|
+
const preFp = preSnapshot.get(changedUri) ?? new Set<string>();
|
|
255
|
+
const postFp = fingerprintSet(diagnosticsMap.get(changedUri)?.diagnostics ?? []);
|
|
256
|
+
if (!setsEqual(preFp, postFp)) {
|
|
220
257
|
clearTimeout(timeout);
|
|
221
258
|
resetQuiescence();
|
|
222
259
|
}
|
package/src/config.ts
CHANGED
|
@@ -66,7 +66,7 @@ function validateOverride(id: string, raw: unknown): ServerConfigOverride | null
|
|
|
66
66
|
console.error(`[pi-lsp-lite] config "${id}": extensions must be a non-empty string array, skipping`);
|
|
67
67
|
return null;
|
|
68
68
|
}
|
|
69
|
-
override.extensions =
|
|
69
|
+
override.extensions = raw.extensions.map((e) => e.toLowerCase());
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
if (raw.command !== undefined) {
|
|
@@ -74,7 +74,7 @@ function validateOverride(id: string, raw: unknown): ServerConfigOverride | null
|
|
|
74
74
|
console.error(`[pi-lsp-lite] config "${id}": command must be a non-empty string, skipping`);
|
|
75
75
|
return null;
|
|
76
76
|
}
|
|
77
|
-
override.command = raw.command
|
|
77
|
+
override.command = raw.command;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (raw.args !== undefined) {
|
|
@@ -82,7 +82,7 @@ function validateOverride(id: string, raw: unknown): ServerConfigOverride | null
|
|
|
82
82
|
console.error(`[pi-lsp-lite] config "${id}": args must be a string array, skipping`);
|
|
83
83
|
return null;
|
|
84
84
|
}
|
|
85
|
-
override.args = raw.args
|
|
85
|
+
override.args = raw.args;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
if (raw.rootPatterns !== undefined) {
|
|
@@ -90,7 +90,7 @@ function validateOverride(id: string, raw: unknown): ServerConfigOverride | null
|
|
|
90
90
|
console.error(`[pi-lsp-lite] config "${id}": rootPatterns must be a string array, skipping`);
|
|
91
91
|
return null;
|
|
92
92
|
}
|
|
93
|
-
override.rootPatterns = raw.rootPatterns
|
|
93
|
+
override.rootPatterns = raw.rootPatterns;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
if (raw.diagnosticTimeout !== undefined) {
|
|
@@ -150,12 +150,18 @@ async function findProjectConfig(cwd: string): Promise<UserConfig | null> {
|
|
|
150
150
|
|
|
151
151
|
type ConfigSource = "global" | "project";
|
|
152
152
|
|
|
153
|
+
interface MergeResult {
|
|
154
|
+
servers: LanguageServerConfig[];
|
|
155
|
+
perServerTimeouts: Map<string, number>;
|
|
156
|
+
}
|
|
157
|
+
|
|
153
158
|
function mergeConfigs(
|
|
154
159
|
base: LanguageServerConfig[],
|
|
155
160
|
overrides: Record<string, ServerConfigOverride>,
|
|
156
161
|
source: ConfigSource,
|
|
157
|
-
):
|
|
162
|
+
): MergeResult {
|
|
158
163
|
const result = new Map<string, LanguageServerConfig>();
|
|
164
|
+
const perServerTimeouts = new Map<string, number>();
|
|
159
165
|
|
|
160
166
|
for (const server of base) {
|
|
161
167
|
result.set(server.id, { ...server });
|
|
@@ -170,9 +176,17 @@ function mergeConfigs(
|
|
|
170
176
|
continue;
|
|
171
177
|
}
|
|
172
178
|
|
|
179
|
+
if (override.diagnosticTimeout !== undefined) {
|
|
180
|
+
perServerTimeouts.set(id, override.diagnosticTimeout);
|
|
181
|
+
}
|
|
182
|
+
|
|
173
183
|
const existing = result.get(id);
|
|
174
184
|
if (existing) {
|
|
175
185
|
const { disabled: _, diagnosticTimeout: __, ...lspFields } = override;
|
|
186
|
+
if (source === "project" && lspFields.command !== undefined) {
|
|
187
|
+
console.error(`[pi-lsp-lite] project config cannot override "command" for server "${id}" — ignoring (use global config instead)`);
|
|
188
|
+
delete lspFields.command;
|
|
189
|
+
}
|
|
176
190
|
const defined = Object.fromEntries(
|
|
177
191
|
Object.entries(lspFields).filter(([, v]) => v !== undefined),
|
|
178
192
|
);
|
|
@@ -197,7 +211,7 @@ function mergeConfigs(
|
|
|
197
211
|
}
|
|
198
212
|
}
|
|
199
213
|
|
|
200
|
-
return Array.from(result.values());
|
|
214
|
+
return { servers: Array.from(result.values()), perServerTimeouts };
|
|
201
215
|
}
|
|
202
216
|
|
|
203
217
|
export function globalConfigFilePath(globalConfigPath?: string): string {
|
|
@@ -275,12 +289,10 @@ export async function loadConfig(cwd: string, globalConfigPath?: string): Promis
|
|
|
275
289
|
for (const [layer, source] of layers) {
|
|
276
290
|
if (!layer) continue;
|
|
277
291
|
if (layer.servers && isPlainObject(layer.servers)) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
perServerTimeout.set(id, override.diagnosticTimeout);
|
|
283
|
-
}
|
|
292
|
+
const merged = mergeConfigs(servers, layer.servers as Record<string, ServerConfigOverride>, source);
|
|
293
|
+
servers = merged.servers;
|
|
294
|
+
for (const [id, timeout] of merged.perServerTimeouts) {
|
|
295
|
+
perServerTimeout.set(id, timeout);
|
|
284
296
|
}
|
|
285
297
|
}
|
|
286
298
|
if (layer.diagnosticTimeout !== undefined) {
|
package/src/format.ts
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { DiagnosticSeverity } from "vscode-languageserver-protocol";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { relative } from "node:path";
|
|
2
4
|
import type { DiagnosticResult } from "./client.js";
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
const MAX_DIAGNOSTICS_PER_FILE = 50;
|
|
7
|
+
|
|
8
|
+
export function formatDiagnostics(filePath: string, result: DiagnosticResult, cwd?: string): string {
|
|
9
|
+
const allRelevant = result.diagnostics.filter(
|
|
6
10
|
(d) => d.severity === DiagnosticSeverity.Error || d.severity === DiagnosticSeverity.Warning,
|
|
7
11
|
);
|
|
8
12
|
|
|
9
|
-
if (
|
|
10
|
-
|
|
13
|
+
if (allRelevant.length === 0 && result.status === "ok" && result.otherFiles.length === 0) return "";
|
|
14
|
+
|
|
15
|
+
if (result.status === "unavailable") {
|
|
16
|
+
return `\n⚠ LSP diagnostics unavailable for ${filePath} (server missing or failed to start)`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const truncated = allRelevant.length > MAX_DIAGNOSTICS_PER_FILE;
|
|
20
|
+
const relevant = truncated ? allRelevant.slice(0, MAX_DIAGNOSTICS_PER_FILE) : allRelevant;
|
|
11
21
|
|
|
12
22
|
const retryNote = result.status === "timeout" && result.retryAttempts > 0
|
|
13
23
|
? ` after ${result.retryAttempts} ${result.retryAttempts === 1 ? "retry" : "retries"}`
|
|
14
24
|
: "";
|
|
15
25
|
|
|
16
26
|
if (relevant.length === 0 && result.status === "ok" && result.otherFiles.length > 0) {
|
|
17
|
-
return `\n⚠ LSP diagnostics for ${filePath}: no issues${otherFilesFooter(result)}`;
|
|
27
|
+
return `\n⚠ LSP diagnostics for ${filePath}: no issues${otherFilesFooter(result, cwd)}`;
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
const lines = relevant.map((d) => {
|
|
@@ -25,8 +35,11 @@ export function formatDiagnostics(filePath: string, result: DiagnosticResult): s
|
|
|
25
35
|
return ` ${severity} ${line}:${col} ${source}${d.message}`;
|
|
26
36
|
});
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
const
|
|
38
|
+
let errorCount = 0;
|
|
39
|
+
for (const d of allRelevant) {
|
|
40
|
+
if (d.severity === DiagnosticSeverity.Error) errorCount++;
|
|
41
|
+
}
|
|
42
|
+
const warnCount = allRelevant.length - errorCount;
|
|
30
43
|
|
|
31
44
|
const summary = [
|
|
32
45
|
errorCount > 0 ? `${errorCount} error${errorCount > 1 ? "s" : ""}` : "",
|
|
@@ -36,12 +49,30 @@ export function formatDiagnostics(filePath: string, result: DiagnosticResult): s
|
|
|
36
49
|
.filter(Boolean)
|
|
37
50
|
.join(", ");
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
const truncatedNote = truncated ? `\n ... and ${allRelevant.length - MAX_DIAGNOSTICS_PER_FILE} more` : "";
|
|
53
|
+
|
|
54
|
+
return `\n⚠ LSP diagnostics for ${filePath} (${summary}):\n${lines.join("\n")}${truncatedNote}${otherFilesFooter(result, cwd)}`;
|
|
40
55
|
}
|
|
41
56
|
|
|
42
|
-
function otherFilesFooter(result: DiagnosticResult): string {
|
|
57
|
+
function otherFilesFooter(result: DiagnosticResult, cwd?: string): string {
|
|
43
58
|
if (result.otherFiles.length === 0) return "";
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
const lines = result.otherFiles.map((f) => {
|
|
60
|
+
let path: string;
|
|
61
|
+
try {
|
|
62
|
+
const abs = fileURLToPath(f.uri);
|
|
63
|
+
path = cwd ? relative(cwd, abs) : abs;
|
|
64
|
+
} catch {
|
|
65
|
+
path = f.uri;
|
|
66
|
+
}
|
|
67
|
+
const counts = [
|
|
68
|
+
f.errorCount > 0 ? `${f.errorCount} error${f.errorCount > 1 ? "s" : ""}` : "",
|
|
69
|
+
f.warningCount > 0 ? `${f.warningCount} warning${f.warningCount > 1 ? "s" : ""}` : "",
|
|
70
|
+
].filter(Boolean).join(", ");
|
|
71
|
+
if (!f.firstDiagnostic) return ` ${path} (${counts})`;
|
|
72
|
+
const d = f.firstDiagnostic;
|
|
73
|
+
const sev = d.severity === DiagnosticSeverity.Error ? "error" : "warning";
|
|
74
|
+
const src = d.source ? `[${d.source}] ` : "";
|
|
75
|
+
return ` ${path} (${counts}): ${sev} ${d.line + 1}:${d.col + 1} ${src}${d.message}`;
|
|
76
|
+
});
|
|
77
|
+
return `\n${lines.join("\n")}`;
|
|
47
78
|
}
|
package/src/languages.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface LanguageServerConfig {
|
|
|
6
6
|
rootPatterns: string[];
|
|
7
7
|
diagnosticTimeout?: number;
|
|
8
8
|
maxRetries?: number;
|
|
9
|
+
languageIds?: Record<string, string>;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export const builtinLanguages: LanguageServerConfig[] = [
|
|
@@ -32,6 +33,7 @@ export const builtinLanguages: LanguageServerConfig[] = [
|
|
|
32
33
|
args: ["--stdio"],
|
|
33
34
|
rootPatterns: ["tsconfig.json", "package.json"],
|
|
34
35
|
diagnosticTimeout: 30_000,
|
|
36
|
+
languageIds: { ".tsx": "typescriptreact", ".js": "javascript", ".jsx": "javascriptreact" },
|
|
35
37
|
},
|
|
36
38
|
{
|
|
37
39
|
id: "python",
|
|
@@ -48,6 +50,7 @@ export const builtinLanguages: LanguageServerConfig[] = [
|
|
|
48
50
|
args: [],
|
|
49
51
|
rootPatterns: ["compile_commands.json", "CMakeLists.txt", ".clangd"],
|
|
50
52
|
diagnosticTimeout: 15_000,
|
|
53
|
+
languageIds: { ".c": "c", ".h": "c", ".cc": "cpp", ".cxx": "cpp", ".hpp": "cpp", ".hxx": "cpp" },
|
|
51
54
|
},
|
|
52
55
|
];
|
|
53
56
|
|
|
@@ -56,6 +59,14 @@ export function languageForFile(path: string, configs: LanguageServerConfig[]):
|
|
|
56
59
|
return configs.find((lang) => lang.extensions.some((ext) => lower.endsWith(ext)));
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
export function languageIdForFile(filePath: string, config: LanguageServerConfig): string {
|
|
63
|
+
if (config.languageIds) {
|
|
64
|
+
const ext = filePath.toLowerCase().match(/\.[^.]+$/)?.[0];
|
|
65
|
+
if (ext && config.languageIds[ext]) return config.languageIds[ext];
|
|
66
|
+
}
|
|
67
|
+
return config.id;
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
export function checkExtensionOverlaps(configs: LanguageServerConfig[]): string[] {
|
|
60
71
|
const warnings: string[] = [];
|
|
61
72
|
const seen = new Map<string, string>();
|
package/src/server-manager.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn, type ChildProcess } from "node:child_process";
|
|
2
2
|
import { which, fileUri, findWorkspaceRoot } from "./util.js";
|
|
3
3
|
import { createLspClient, type LspClient, type DiagnosticResult } from "./client.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type LanguageServerConfig, languageIdForFile } from "./languages.js";
|
|
5
5
|
import type { Diagnostic } from "vscode-languageserver-protocol";
|
|
6
6
|
import { DEFAULT_DIAGNOSTIC_TIMEOUT, DEFAULT_DOCUMENT_IDLE_TIMEOUT, DEFAULT_MAX_RETRIES } from "./config.js";
|
|
7
7
|
import { readFile } from "node:fs/promises";
|
|
@@ -221,7 +221,7 @@ export function createServerManager(options: ServerManagerOptions = {}): ServerM
|
|
|
221
221
|
if (server.openDocuments.has(uri)) {
|
|
222
222
|
server.client.didChange(uri, content);
|
|
223
223
|
} else {
|
|
224
|
-
server.client.didOpen(uri, server.config
|
|
224
|
+
server.client.didOpen(uri, languageIdForFile(filePath, server.config), content);
|
|
225
225
|
}
|
|
226
226
|
server.openDocuments.set(uri, Date.now());
|
|
227
227
|
|
package/src/util.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { access, constants } from "node:fs/promises";
|
|
2
|
-
import { join, dirname } from "node:path";
|
|
2
|
+
import { join, dirname, relative, isAbsolute } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
|
|
5
5
|
export function fileUri(absolutePath: string): string {
|
|
@@ -42,3 +42,8 @@ export async function findWorkspaceRoot(filePath: string, rootPatterns: string[]
|
|
|
42
42
|
}
|
|
43
43
|
return cwd;
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
export function isInsideCwd(absolutePath: string, cwd: string): boolean {
|
|
47
|
+
const rel = relative(cwd, absolutePath);
|
|
48
|
+
return !!rel && !rel.startsWith("..") && !isAbsolute(rel);
|
|
49
|
+
}
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
permissions:
|
|
10
|
-
contents: read
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
check:
|
|
14
|
-
name: typecheck + unit tests
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v6
|
|
18
|
-
|
|
19
|
-
- uses: actions/setup-node@v6
|
|
20
|
-
with:
|
|
21
|
-
node-version: 20
|
|
22
|
-
cache: npm
|
|
23
|
-
|
|
24
|
-
- run: npm ci
|
|
25
|
-
|
|
26
|
-
- name: typecheck
|
|
27
|
-
run: npm run check
|
|
28
|
-
|
|
29
|
-
- name: unit tests
|
|
30
|
-
run: npm test
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
name: Integration Tests
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches: [main]
|
|
6
|
-
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
gopls:
|
|
12
|
-
name: gopls
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/checkout@v6
|
|
16
|
-
- uses: actions/setup-node@v6
|
|
17
|
-
with:
|
|
18
|
-
node-version: 20
|
|
19
|
-
cache: npm
|
|
20
|
-
- uses: actions/setup-go@v6
|
|
21
|
-
with:
|
|
22
|
-
go-version: stable
|
|
23
|
-
- run: go install golang.org/x/tools/gopls@latest
|
|
24
|
-
- run: npm ci
|
|
25
|
-
- run: INTEGRATION=1 npx tsx --test test/*.test.ts test/integration/gopls.test.ts
|
|
26
|
-
|
|
27
|
-
rust-analyzer:
|
|
28
|
-
name: rust-analyzer
|
|
29
|
-
runs-on: ubuntu-latest
|
|
30
|
-
steps:
|
|
31
|
-
- uses: actions/checkout@v6
|
|
32
|
-
- uses: actions/setup-node@v6
|
|
33
|
-
with:
|
|
34
|
-
node-version: 20
|
|
35
|
-
cache: npm
|
|
36
|
-
- run: |
|
|
37
|
-
rustup update stable
|
|
38
|
-
rustup component add rust-analyzer
|
|
39
|
-
- run: npm ci
|
|
40
|
-
- run: INTEGRATION=1 npx tsx --test test/*.test.ts test/integration/rust-analyzer.test.ts
|
|
41
|
-
|
|
42
|
-
typescript:
|
|
43
|
-
name: typescript-language-server
|
|
44
|
-
runs-on: ubuntu-latest
|
|
45
|
-
steps:
|
|
46
|
-
- uses: actions/checkout@v6
|
|
47
|
-
- uses: actions/setup-node@v6
|
|
48
|
-
with:
|
|
49
|
-
node-version: 20
|
|
50
|
-
cache: npm
|
|
51
|
-
- run: npm install -g typescript-language-server typescript
|
|
52
|
-
- run: npm ci
|
|
53
|
-
- run: INTEGRATION=1 npx tsx --test test/*.test.ts test/integration/typescript.test.ts
|
|
54
|
-
|
|
55
|
-
pylsp:
|
|
56
|
-
name: pylsp
|
|
57
|
-
runs-on: ubuntu-latest
|
|
58
|
-
steps:
|
|
59
|
-
- uses: actions/checkout@v6
|
|
60
|
-
- uses: actions/setup-node@v6
|
|
61
|
-
with:
|
|
62
|
-
node-version: 20
|
|
63
|
-
cache: npm
|
|
64
|
-
- run: pip install 'python-lsp-server[all]'
|
|
65
|
-
- run: npm ci
|
|
66
|
-
- run: INTEGRATION=1 npx tsx --test test/*.test.ts test/integration/pylsp.test.ts
|
|
67
|
-
|
|
68
|
-
clangd:
|
|
69
|
-
name: clangd
|
|
70
|
-
runs-on: ubuntu-latest
|
|
71
|
-
steps:
|
|
72
|
-
- uses: actions/checkout@v6
|
|
73
|
-
- uses: actions/setup-node@v6
|
|
74
|
-
with:
|
|
75
|
-
node-version: 20
|
|
76
|
-
cache: npm
|
|
77
|
-
- run: sudo apt-get update -qq && sudo apt-get install -y -qq clangd
|
|
78
|
-
- run: npm ci
|
|
79
|
-
- run: INTEGRATION=1 npx tsx --test test/*.test.ts test/integration/clangd.test.ts
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
name: Publish to npm
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read
|
|
9
|
-
id-token: write
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
publish:
|
|
13
|
-
name: publish to npm
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
environment: public
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v6
|
|
18
|
-
|
|
19
|
-
- uses: actions/setup-node@v6
|
|
20
|
-
with:
|
|
21
|
-
node-version: 24
|
|
22
|
-
registry-url: https://registry.npmjs.org
|
|
23
|
-
cache: npm
|
|
24
|
-
|
|
25
|
-
- run: npm ci
|
|
26
|
-
|
|
27
|
-
- name: typecheck
|
|
28
|
-
run: npm run check
|
|
29
|
-
|
|
30
|
-
- name: unit tests
|
|
31
|
-
run: npm test
|
|
32
|
-
|
|
33
|
-
- name: publish
|
|
34
|
-
run: npm publish --access public
|