testScratchQuickFixes
Quick fixes in a scratch file: a codebase name it doesn't import yet is imported from where it is, and a name it imports is edited here too, added to the file.
import { plainEnv, tcmBin, tempDir, withChildren } from "#codebase/tcmts/cli/test/e2e/harness"; import { client } from "#codebase/tcmts/cli/test/e2e/lsp"; import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; export const testScratchQuickFixes = () => withChildren(async (keep) => { const dir = tempDir("lsp fixes"); const tcm = (...args: string[]) => spawnSync(process.execPath, [tcmBin(), ...args], { cwd: dir, env: plainEnv(), encoding: "utf8" }); tcm("init"); fs.writeFileSync(path.join(dir, "scratch.ts"), `// tcm: namespace geo\nexport type Point = { x: number; y: number };\nexport const norm = (p: Point): number => Math.hypot(p.x, p.y);\n`); assert.equal(tcm("add").status, 0); const c = client(keep); await c.request("initialize", { processId: null, rootUri: pathToFileURL(dir).href, capabilities: {}, clientInfo: { name: "Visual Studio Code" } }); c.notify("initialized", {}); // `norm` isn't imported: importing it is offered, after the namespace line const file = path.join(dir, "scratch.ts"); const uri = pathToFileURL(file).href; const unimported = `// tcm: namespace app\nexport const far = (p: { x: number; y: number }): boolean => norm(p) > 10;\n`; fs.writeFileSync(file, unimported); c.notify("textDocument/didOpen", { textDocument: { uri, languageId: "typescript", version: 1, text: unimported } }); const fixes = await c.request("textDocument/codeAction", { textDocument: { uri }, range: { start: { line: 1, character: 62 }, end: { line: 1, character: 66 } }, context: { diagnostics: [] } }); const fix = fixes.find((a: any) => a.title === `Import norm from "#codebase/geo"`); assert.ok(fix, JSON.stringify(fixes)); assert.deepEqual(fix.edit.changes[uri], [{ range: { start: { line: 1, character: 0 }, end: { line: 1, character: 0 } }, newText: `import { norm } from "#codebase/geo";\n` }]); // with it imported, on the name: edit it here too, and it's added to the file const imported = `// tcm: namespace app\nimport { norm } from "#codebase/geo";\nexport const far = (p: { x: number; y: number }): boolean => norm(p) > 10;\n`; fs.writeFileSync(file, imported); c.notify("textDocument/didChange", { textDocument: { uri, version: 2 }, contentChanges: [{ text: imported }] }); const actions = await c.request("textDocument/codeAction", { textDocument: { uri }, range: { start: { line: 2, character: 63 }, end: { line: 2, character: 63 } }, context: { diagnostics: [] } }); const edit = actions.find((a: any) => a.title === "Edit geo.norm here too"); assert.deepEqual(edit?.command, { title: "Edit geo.norm here too", command: "tcm.editThisToo", arguments: [uri, "geo.norm"] }); assert.equal(await c.request("workspace/executeCommand", { command: "tcm.editInto", arguments: [uri, "geo.norm"] }), uri); const now = fs.readFileSync(file, "utf8"); assert.match(now, /export const far = [\s\S]*\/\/ tcm: path geo\.norm\nexport const norm = \(p: Point\): number => Math\.hypot/); assert.match(now, /^import type \{ Point \} from "#codebase\/geo";$/m); })