@trim21/personal-pi-extensions 0.0.299 → 0.0.301
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/package.json +3 -1
- package/src/aft/ast-edit.ts +6 -7
- package/src/aft/bridge.ts +9 -16
- package/src/claude-code/files.ts +168 -137
- package/src/lib/lsp/adapter.ts +41 -0
- package/src/lib/lsp/adapters/clangd.ts +26 -0
- package/src/lib/lsp/adapters/index.ts +11 -0
- package/src/lib/lsp/adapters/pyright.ts +50 -0
- package/src/lib/lsp/adapters/ruff.ts +27 -0
- package/src/lib/lsp/adapters/typescript.ts +34 -0
- package/src/lib/lsp/bin.ts +88 -0
- package/src/lib/lsp/client.ts +693 -0
- package/src/lib/lsp/diagnostic.ts +35 -0
- package/src/lib/lsp/language.ts +125 -0
- package/src/lib/lsp/launch.ts +22 -0
- package/src/lib/lsp/lsp.ts +289 -0
- package/src/opencode/{read.ts → files.ts} +276 -31
- package/src/opencode/index.ts +13 -14
- package/src/spawn-agent.ts +6 -4
- package/src/opencode/edit.ts +0 -169
- package/src/opencode/write.ts +0 -116
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript / JavaScript LSP 服务器(typescript-language-server)。
|
|
3
|
+
* tsserver 路径解析自项目工作区的 node_modules,语言服务器二进制同样
|
|
4
|
+
* 工作区优先、PATH 兜底。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { type LspServerAdapter, nearestRoot } from "../adapter.js";
|
|
8
|
+
import { findBinaryInWorkspace, findModuleInWorkspace, which } from "../bin.js";
|
|
9
|
+
import { spawnProcess } from "../launch.js";
|
|
10
|
+
|
|
11
|
+
const LOCK_FILES = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lock", "bun.lockb"];
|
|
12
|
+
|
|
13
|
+
export class TypescriptAdapter implements LspServerAdapter {
|
|
14
|
+
readonly id = "typescript";
|
|
15
|
+
readonly extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"];
|
|
16
|
+
|
|
17
|
+
findRoot(file: string, cwd: string): Promise<string> {
|
|
18
|
+
return nearestRoot(LOCK_FILES, file, cwd);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async spawn(root: string, cwd: string) {
|
|
22
|
+
// typescript-language-server 依赖项目里的 tsserver(本身不带)
|
|
23
|
+
const tsserver = await findModuleInWorkspace("typescript/lib/tsserver.js", root, cwd);
|
|
24
|
+
if (!tsserver) return;
|
|
25
|
+
const bin =
|
|
26
|
+
(await findBinaryInWorkspace("typescript-language-server", root, cwd)) ??
|
|
27
|
+
which("typescript-language-server");
|
|
28
|
+
if (!bin) return;
|
|
29
|
+
return {
|
|
30
|
+
process: spawnProcess(bin, ["--stdio"], { cwd: root }),
|
|
31
|
+
initialization: { tsserver: { path: tsserver } },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工作区优先的二进制 / 模块查找。
|
|
3
|
+
*
|
|
4
|
+
* 查找顺序(对每个候选目录逐级向上到 stopDir 为止):
|
|
5
|
+
* node_modules/.bin/<cmd> → .venv/bin/<cmd> → venv/bin/<cmd>
|
|
6
|
+
* 全部落空后由调用方回退到 PATH(which)。
|
|
7
|
+
* 这样项目用 uv / pnpm 装的服务器(.venv、node_modules)优先于系统级安装。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { delimiter, dirname, join, normalize } from "node:path";
|
|
12
|
+
|
|
13
|
+
export function exists(p: string): boolean {
|
|
14
|
+
return existsSync(p);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** 从 fromDir 逐级向上(含 stopDir 本身)收集目录,到文件系统根为止。 */
|
|
18
|
+
export function walkUp(fromDir: string, stopDir: string): string[] {
|
|
19
|
+
const dirs: string[] = [];
|
|
20
|
+
let current = normalize(fromDir);
|
|
21
|
+
const stop = normalize(stopDir);
|
|
22
|
+
for (;;) {
|
|
23
|
+
dirs.push(current);
|
|
24
|
+
if (current === stop) break;
|
|
25
|
+
const parent = dirname(current);
|
|
26
|
+
if (parent === current) break;
|
|
27
|
+
current = parent;
|
|
28
|
+
}
|
|
29
|
+
return dirs;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function binaryNames(cmd: string): string[] {
|
|
33
|
+
if (process.platform === "win32") {
|
|
34
|
+
const ext = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").toLowerCase().split(";");
|
|
35
|
+
return [cmd, ...ext.map((e) => cmd + e)];
|
|
36
|
+
}
|
|
37
|
+
return [cmd];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 从 fromDir 向上(到 stopDir)在项目工作区里找二进制。
|
|
42
|
+
* 候选位置:node_modules/.bin、.venv/bin、venv/bin。
|
|
43
|
+
*/
|
|
44
|
+
export function findBinaryInWorkspace(
|
|
45
|
+
cmd: string,
|
|
46
|
+
fromDir: string,
|
|
47
|
+
stopDir: string,
|
|
48
|
+
): Promise<string | undefined> {
|
|
49
|
+
for (const dir of walkUp(fromDir, stopDir)) {
|
|
50
|
+
const roots = [
|
|
51
|
+
join(dir, "node_modules", ".bin"),
|
|
52
|
+
join(dir, ".venv", "bin"),
|
|
53
|
+
join(dir, "venv", "bin"),
|
|
54
|
+
];
|
|
55
|
+
for (const root of roots) {
|
|
56
|
+
for (const name of binaryNames(cmd)) {
|
|
57
|
+
const candidate = join(root, name);
|
|
58
|
+
if (exists(candidate)) return Promise.resolve(candidate);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return Promise.resolve(undefined);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** 从 fromDir 向上(到 stopDir)找 node_modules/<modulePath>(如 typescript/lib/tsserver.js)。 */
|
|
66
|
+
export function findModuleInWorkspace(
|
|
67
|
+
modulePath: string,
|
|
68
|
+
fromDir: string,
|
|
69
|
+
stopDir: string,
|
|
70
|
+
): Promise<string | undefined> {
|
|
71
|
+
for (const dir of walkUp(fromDir, stopDir)) {
|
|
72
|
+
const candidate = join(dir, "node_modules", modulePath);
|
|
73
|
+
if (exists(candidate)) return Promise.resolve(candidate);
|
|
74
|
+
}
|
|
75
|
+
return Promise.resolve(undefined);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** PATH 查找(同步,逻辑简单;调用频率低,无需缓存)。 */
|
|
79
|
+
export function which(cmd: string): string | undefined {
|
|
80
|
+
const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
81
|
+
for (const dir of pathDirs) {
|
|
82
|
+
for (const name of binaryNames(cmd)) {
|
|
83
|
+
const candidate = join(dir, name);
|
|
84
|
+
if (existsSync(candidate)) return candidate;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|