Files
pi-lsp/cli.ts
2026-04-25 21:06:15 -04:00

62 lines
1.9 KiB
TypeScript
Executable File

#!/usr/bin/env -S npx tsx
import * as path from "node:path";
import { startClientForFile } from "./src/client.ts";
import { isLspCommand, listCommands, runCommand } from "./src/commands.ts";
import { pickServer } from "./src/root.ts";
// Usage
function usage(): never {
process.stderr.write(
`Usage: cli.ts <file> <lsp_command> <req_data_json>\n` +
`Commands: ${listCommands().join(", ")}\n` +
`Example:\n` +
` cli.ts backend/api/server.go hover '{"position":{"line":223,"character":22}}'\n`,
);
process.exit(2);
}
async function main() {
const [, , fileArg, cmdArg, jsonArg] = process.argv;
if (!fileArg || !cmdArg || jsonArg === undefined) usage();
if (!isLspCommand(cmdArg)) {
process.stderr.write(
`Unknown command "${cmdArg}". Known: ${listCommands().join(", ")}\n`,
);
process.exit(2);
}
// Parse Request JSON
let params: Record<string, unknown>;
try {
params = jsonArg.trim() === "" ? {} : JSON.parse(jsonArg);
} catch (err) {
process.stderr.write(
`Invalid JSON for req_data_json: ${(err as Error).message}\n`,
);
process.exit(2);
}
const filePath = path.resolve(fileArg);
const server = pickServer(filePath);
const { client, uri } = await startClientForFile(server, filePath);
try {
const result = await runCommand(cmdArg, client, uri, params);
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
} finally {
// Fire-And-Forget Shutdown - With `gopls -remote=auto` (and similar)
// the spawned process is a thin client to a background daemon; a
// graceful shutdown can hang the parent. Kick it off but don't wait.
void client.dispose();
}
// Hard Exit - Any lingering handles (LSP stdio, daemon stubs) would keep
// the event loop alive. For a short-lived CLI we just exit.
process.exit(0);
}
main().catch((err) => {
process.stderr.write(`${(err as Error).stack ?? err}\n`);
process.exit(1);
});