client
uses node:child_process
A minimal LSP client: framed JSON-RPC over the server's stdio.
import type { Keep } from "#codebase/tcmts/cli/test/e2e/contributions"; import { plainEnv, tcmBin } from "#codebase/tcmts/cli/test/e2e/harness"; import type { Msg } from "#codebase/tcmts/cli/test/e2e/lsp"; import { spawn } from "node:child_process"; import os from "node:os"; export function client(keep: Keep) { const child = keep(spawn(process.execPath, [tcmBin(), "lsp"], { cwd: os.tmpdir(), env: plainEnv() })); const notes: Msg[] = []; const pending = new Map<number, (m: Msg) => void>(); let stderr = ""; child.stderr.setEncoding("utf8").on("data", (d: string) => (stderr += d)); let buf = Buffer.alloc(0); child.stdout.on("data", (chunk: Buffer) => { buf = Buffer.concat([buf, chunk]); for (;;) { const sep = buf.indexOf("\r\n\r\n"); if (sep < 0) return; const len = Number(/Content-Length: (\d+)/.exec(buf.subarray(0, sep).toString())![1]); if (buf.length < sep + 4 + len) return; const m = JSON.parse(buf.subarray(sep + 4, sep + 4 + len).toString()) as Msg; buf = buf.subarray(sep + 4 + len); if (m.id !== undefined && !m.method) pending.get(m.id as number)?.(m); else if (m.method && m.id !== undefined) write({ id: m.id, result: null }); // e.g. workspace/codeLens/refresh else notes.push(m); } }); const write = (m: Msg) => { const body = Buffer.from(JSON.stringify({ jsonrpc: "2.0", ...m })); child.stdin.write(`Content-Length: ${body.length}\r\n\r\n`); child.stdin.write(body); }; let id = 0; const request = (method: string, params: unknown) => new Promise<any>((resolve, reject) => { const n = ++id; pending.set(n, (m) => (m.error ? reject(new Error(JSON.stringify(m.error))) : resolve(m.result))); write({ id: n, method, params }); }); const notify = (method: string, params: unknown) => write({ method, params }); const waitFor = async <T>(find: () => T | undefined, ms = 20000): Promise<T> => { for (const end = Date.now() + ms; Date.now() < end; await new Promise((r) => setTimeout(r, 50))) { const v = find(); if (v) return v; } throw new Error(`timed out; stderr:\n${stderr}\nnotifications:\n${JSON.stringify(notes, null, 1)}`); }; return { request, notify, notes, waitFor, child }; }
uses
- tcmts.cli.test.e2e.contributions.Keep
- tcmts.cli.test.e2e.harness.plainEnv
- tcmts.cli.test.e2e.harness.tcmBin
- tcmts.cli.test.e2e.lsp.Msg
used by
- tcmts.cli.test.e2e.lsp.testAppsAndDataInEditor
- tcmts.cli.test.e2e.lsp.testChangesInEditor
- tcmts.cli.test.e2e.lsp.testCodeSearchInEditor
- tcmts.cli.test.e2e.lsp.testCodebaseInEditor
- tcmts.cli.test.e2e.lsp.testDebugging
- tcmts.cli.test.e2e.lsp.testLanguageServer
- tcmts.cli.test.e2e.lsp.testPackagesInEditor
- tcmts.cli.test.e2e.lsp.testPages
- tcmts.cli.test.e2e.lsp.testReleasesInEditor
- tcmts.cli.test.e2e.lsp.testScratchQuickFixes
- tcmts.cli.test.e2e.lsp.testVscodeRegistersNoServerCommands