/** * Explicit integration coverage for the platform rg bundled by @vscode/ripgrep. * This suite is excluded from the default test script. */ import { test } from "node:test"; import assert from "node:assert/strict"; import childProcess from "node:child_process"; import { syncBuiltinESMExports } from "node:module"; import { lstat, mkdir, mkdtemp, readFile, readlink, rm, stat, symlink, writeFile, } from "node:fs/promises"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; import { makeGrepOverrideWithBackend } from "../pi/grep-tool.ts"; import { makeEditOverride } from "../pi/edit-tool.ts"; import { makeReadOverride } from "../pi/read-tool.ts"; import { makeWriteOverride } from "../pi/write-tool.ts"; import { makeReplaceTool } from "../pi/replace-tool.ts"; import { COMMON_RG_ARGS, MAX_RG_RECORD_BYTES, runRgPaths } from "../pi/rg-process.ts"; import { DEFAULT_OUTPUT_BUDGETS } from "../pi/budgets.ts"; import { computeLineHash } from "../core/hash.ts"; import { callTool } from "../pi/tool-call.testing.ts"; import { DEFAULT_CONFIG } from "../pi/config.ts"; import { canonicalPath } from "../pi/path.ts"; import { reportOf } from "../pi/report.ts"; import { grepCounts } from "../pi/grep-output.testing.ts"; import { hostCompiles, unsupportedByHost } from "../testing/regex-host.testing.ts"; const invoke = ( tool: any, toolCallId: string, args: unknown, signal?: AbortSignal, onUpdate?: unknown, ctx?: unknown, ) => callTool(tool, args, { toolCallId, signal, onUpdate, ctx }); test("real rg emits anchored matches from a temporary directory", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-integration-")); try { const file = join(directory, "fixture.ts"); await writeFile(file, "needle\nother\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const pattern of ["needle", ["needle"]]) { const result: any = await invoke(tool, "0", { pattern, literal: true }, undefined, undefined); assert.match(result.content[0].text, /fixture\.ts · 1 match/); assert.match(result.content[0].text, /1#[0-9A-Z]+│needle/); } } finally { await rm(directory, { recursive: true, force: true }); } }); test("real rg finds whitespace-only literal queries and OR alternatives", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-whitespace-")); try { const file = join(directory, "fixture.txt"); await writeFile(file, "plain word\nfirst\tsecond\nboth \tforms\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const { pattern, expected } of [ { pattern: " ", expected: 2 }, { pattern: "\t", expected: 2 }, { pattern: [" ", "\t"], expected: 3 }, ]) { const result = await invoke( tool, "0", { pattern, path: file, literal: true, outputMode: "count" }, undefined, undefined, ); assert.equal(grepCounts(result).matches, expected); assert.equal(grepCounts(result).files, 1); } const lineBreaks = await invoke( tool, "0", { pattern: "\n", path: file, literal: true, multiline: true, outputMode: "count" }, undefined, undefined, ); assert.equal(grepCounts(lineBreaks).matches, 3); assert.equal(grepCounts(lineBreaks).files, 1); } finally { await rm(directory, { recursive: true, force: true }); } }); test("real rg uses smart-case across OR patterns and Unicode matches", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-case-")); try { await writeFile(join(directory, "fixture.ts"), "FOO abc\nfoo BAR\nfoo bar\nK zip\nk zip\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const lower = await invoke( tool, "0", { literal: true, pattern: ["foo", "bar"] }, undefined, undefined, ); assert.match(lower.content[0].text, /fixture\.ts · 3 matches/); assert.match(lower.content[0].text, /│FOO abc/); const mixed = await invoke( tool, "0", { literal: true, pattern: ["foo", "BAR"] }, undefined, undefined, ); assert.match(mixed.content[0].text, /fixture\.ts · 2 matches/); assert.doesNotMatch(mixed.content[0].text, /│FOO abc/); const unicode = await invoke(tool, "0", { literal: true, pattern: "k" }, undefined, undefined); assert.match(unicode.content[0].text, /fixture\.ts · 1 match/); assert.match(unicode.content[0].text, /│k zip/); assert.doesNotMatch(unicode.content[0].text, /│K zip/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("explicit ignoreCase overrides smart-case for regex and literal queries", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-case-override-")); try { const file = join(directory, "fixture.txt"); await writeFile(file, "FOO\nfoo\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const sensitive = await invoke( tool, "0", { literal: true, pattern: "foo", path: file, ignoreCase: false }, undefined, undefined, ); assert.match(sensitive.content[0].text, /fixture\.txt · 1 match/); assert.match(sensitive.content[0].text, /2#[0-9A-Z]+│foo/); const insensitive = await invoke( tool, "0", { pattern: "FOO", path: file, ignoreCase: true, literal: true }, undefined, undefined, ); assert.match(insensitive.content[0].text, /fixture\.txt · 2 matches/); const regex = await invoke( tool, "0", { pattern: "^FOO$", path: file, ignoreCase: true, literal: false }, undefined, undefined, ); assert.match(regex.content[0].text, /fixture\.txt · 2 matches/); if (!hostCompiles("(?i:a)")) return; const inlineSensitive = await invoke( tool, "0", { pattern: "(?-i:^FOO$)", path: file, ignoreCase: true, literal: false }, undefined, undefined, ); assert.match(inlineSensitive.content[0].text, /fixture\.txt · 1 match/); const inlineInsensitive = await invoke( tool, "0", { pattern: "(?i:^FOO$)", path: file, ignoreCase: false, literal: false }, undefined, undefined, ); assert.match(inlineInsensitive.content[0].text, /fixture\.txt · 2 matches/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("owned rg processes ignore RIPGREP_CONFIG_PATH", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-config-")); const previous = process.env.RIPGREP_CONFIG_PATH; try { const config = join(directory, "ripgreprc"); await writeFile( config, "--ignore-case\n--fixed-strings\n--invert-match\n--pcre2\n--multiline\n", ); await writeFile(join(directory, "fixture.ts"), "FOO\nfoo\nbar\n"); process.env.RIPGREP_CONFIG_PATH = config; const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const result: any = await invoke( tool, "0", { literal: false, pattern: ["^foo$", "foo"] }, undefined, undefined, ); assert.match(result.content[0].text, /fixture\.ts · 2 matches/); assert.match(result.content[0].text, /│foo/); assert.match(result.content[0].text, /│FOO/); assert.doesNotMatch(result.content[0].text, /│bar/); const lineOnly = await invoke(tool, "0", { pattern: "foo\\nbar", literal: false }); assert.equal(lineOnly.isError, undefined); assert.equal(grepCounts(lineOnly).matches, 0); const multiline = await invoke( tool, "0", { pattern: "foo\\nbar", literal: false, multiline: true }, undefined, undefined, ); assert.match(multiline.content[0].text, /fixture\.ts · 2 matches/); } finally { if (previous === undefined) delete process.env.RIPGREP_CONFIG_PATH; else process.env.RIPGREP_CONFIG_PATH = previous; await rm(directory, { recursive: true, force: true }); } }); test("real rg separates exact text from regex", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-regex-")); try { await writeFile( join(directory, "fixture.ts"), "FOO\nfoo\nfoo0\nfoo(0)\nqueueTool(\nfoo(?=bar)\n", ); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const pattern of ["(?i:^foo$)", "(?foo)$"].filter((source) => hostCompiles(source), )) { const result = await invoke(tool, "regex", { pattern, literal: false }); assert.match(result.content[0].text, /│foo/); } const regex = await invoke(tool, "regex-punctuation", { pattern: "foo(0)", literal: false }); assert.match(regex.content[0].text, /│foo0/); assert.doesNotMatch(regex.content[0].text, /│foo\(0\)/); for (const pattern of ["foo(0)", "queueTool(", "foo(?=bar)"]) { const result = await invoke(tool, "literal", { pattern, literal: true }); assert.ok(result.content[0].text.includes(`│${pattern}`)); } const missing = await invoke(tool, "no-match", { pattern: "missing(", literal: true }); assert.equal(grepCounts(missing).matches, 0); assert.equal(missing.isError, undefined); } finally { await rm(directory, { recursive: true, force: true }); } }); test("real rg keeps CRLF line-end anchors with regex OR matching", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-crlf-")); try { await writeFile(join(directory, "fixture.ts"), "alpha beta drop\r\nalpha beta\r\nalpha only"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const ending = await invoke( tool, "0", { pattern: "beta$", literal: false }, undefined, undefined, ); assert.match(ending.content[0].text, /fixture\.ts · 1 match/); assert.match(ending.content[0].text, /2#[0-9A-Z]+│alpha beta$/); const alternatives = await invoke( tool, "0", { pattern: ["beta$", "alpha only"], literal: false }, undefined, undefined, ); assert.match(alternatives.content[0].text, /fixture\.ts · 2 matches/); assert.match(alternatives.content[0].text, /3#[0-9A-Z]+│alpha only$/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("real rg finds a late OR match and respects the line limit", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-limit-")); try { await writeFile( join(directory, "fixture.ts"), "foo skip\n".repeat(4096) + "foo keep\nfoo keep later\n", ); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const result = await invoke( tool, "0", { pattern: ["foo keep", "missing"], literal: true, limit: 1 }, undefined, undefined, ); assert.match(result.content[0].text, /4097#[0-9A-Z]+│foo keep$/m); assert.doesNotMatch(result.content[0].text, /foo keep later|4098#/); assert.equal(grepCounts(result).matches, 1); assert.equal(reportOf(result, "grep")?.matchLimit, 1); } finally { await rm(directory, { recursive: true, force: true }); } }); test("shared rg runner stops and cleans up after limits, callback failures, and cancellation", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-process-")); const spawn = childProcess.spawn; const children: ReturnType[] = []; const closed = new Set>(); let missingExecutable = false; try { t.mock.method(childProcess, "spawn", (...args: Parameters) => { const child = spawn( missingExecutable ? join(directory, "missing-rg") : process.execPath, args[1], args[2], ); children.push(child); child.once("close", () => closed.add(child)); return child; }); syncBuiltinESMExports(); const fixture = fileURLToPath( new URL("../testing/rg-line-listing.fixture.mjs", import.meta.url), ); const processTimeoutMs = 5_000; const args = [ fixture, JSON.stringify([Buffer.from("first\0second\0").toString("base64")]), "wait", ]; for (const outcome of ["limit", "callback", "cancel"] as const) { const controller = new AbortController(); const consumed: string[] = []; let aliveAtCallback = false; const run = runRgPaths( args, AbortSignal.any([controller.signal, AbortSignal.timeout(processTimeoutMs)]), (path) => { consumed.push(path); const child = children.at(-1); aliveAtCallback = child?.exitCode === null && child.signalCode === null; if (outcome === "callback") throw new Error("callback failed"); if (outcome === "cancel") controller.abort(); return outcome !== "limit"; }, ); if (outcome === "limit") assert.equal((await run).stopped, true); else await assert.rejects(run, outcome === "callback" ? /callback failed/ : /Operation aborted/); assert.deepEqual(consumed, ["first"]); assert.equal(aliveAtCallback, true); const child = children.at(-1); assert.ok(child && closed.has(child), "runner must await child close"); assert.ok(child.exitCode !== null || child.signalCode !== null); } missingExecutable = true; await assert.rejects( runRgPaths([], undefined, () => true), /Failed to run ripgrep/, ); } finally { for (const child of children) if (!closed.has(child)) child.kill("SIGKILL"); t.mock.restoreAll(); syncBuiltinESMExports(); await rm(directory, { recursive: true, force: true }); } }); test("directory searches respect ignores but explicit files and hidden files remain searchable", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-ignore-")); try { await writeFile(join(directory, ".ignore"), "ignored.txt\n"); const ignored = join(directory, "ignored.txt"); await writeFile(ignored, "needle\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const normal = await invoke( tool, "0", { literal: true, pattern: "needle" }, undefined, undefined, ); assert.equal(grepCounts(normal).matches, 0); const explicit = await invoke( tool, "0", { literal: true, pattern: "needle", path: ignored }, undefined, undefined, ); assert.match(explicit.content[0].text, /ignored\.txt · 1 match/); const excluded = await invoke( tool, "0", { literal: true, pattern: "needle", path: ignored, glob: "!ignored.txt" }, undefined, undefined, ); assert.equal(grepCounts(excluded).matches, 0); await writeFile(join(directory, ".hidden.txt"), "needle\n"); const hidden = await invoke( tool, "0", { literal: true, pattern: "needle" }, undefined, undefined, ); assert.match(hidden.content[0].text, /\.hidden\.txt · 1 match/); assert.doesNotMatch(hidden.content[0].text, /ignored\.txt · 1 match/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("gitignored directory → root traversal skips it and an explicit directory path searches it", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-ignore-scope-")); t.after(() => rm(directory, { recursive: true, force: true })); const ignored = join(directory, "ignored"); await mkdir(join(directory, ".git")); await mkdir(ignored); await writeFile(join(directory, ".gitignore"), "ignored/\n"); await writeFile(join(ignored, "x.txt"), "needle\n"); await writeFile(join(directory, "a.txt"), "needle\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const [path, expected] of [ [directory, "a.txt"], [ignored, "ignored/x.txt"], ]) { const result = await callTool(tool, { path, literal: true, pattern: "needle", outputMode: "files", }); assert.equal(result.isError, undefined, JSON.stringify(reportOf(result, "grep"))); assert.equal(grepCounts(result).matches, 1); assert.equal(result.content[0].type, "text"); assert.equal(result.content[0].text.replace(/\\/g, "/"), expected); } }); test("named directory → nested ignores, .git and links still limit traversal", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-nested-scope-")); t.after(() => rm(directory, { recursive: true, force: true })); await mkdir(join(directory, ".git")); await writeFile(join(directory, ".gitignore"), "node_modules/\ndist/\n*.map\n"); for (const file of [ "node_modules/foo/index.js", "node_modules/foo/dist/bundle.js", "node_modules/foo/index.js.map", "named/index.js", "named/nested/.git/config", "target/linked.js", ]) { await mkdir(dirname(join(directory, file)), { recursive: true }); await writeFile(join(directory, file), "needle\n"); } const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const { path, expected, linked } of [ { path: "node_modules/foo", expected: "node_modules/foo/index.js" }, { path: "node_modules/foo/dist", expected: "node_modules/foo/dist/bundle.js" }, { path: "node_modules/foo/index.js.map", expected: "node_modules/foo/index.js.map" }, { path: "named", expected: "named/index.js" }, { path: "named", expected: "named/index.js", linked: true }, ]) { await t.test(`${path}${linked ? " with nested link" : ""}`, async (t) => { if (linked) { try { await symlink( join(directory, "target"), join(directory, "named/nested/link"), process.platform === "win32" ? "junction" : "dir", ); } catch (error) { if ( error instanceof Error && "code" in error && ["EPERM", "EACCES", "ENOTSUP"].includes(String(error.code)) ) { t.skip("symbolic link creation unavailable"); return; } throw error; } } const result = await callTool(tool, { path: [path], literal: true, pattern: ["needle"], outputMode: "files", }); assert.equal(result.isError, undefined, JSON.stringify(reportOf(result, "grep"))); assert.equal(grepCounts(result).matches, 1); assert.equal(result.content[0].type, "text"); assert.equal(result.content[0].text.replace(/\\/g, "/"), expected); }); } }); test("directory traversal with caller globs → excludes .git while an explicit .git path remains searchable", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-git-scope-")); t.after(() => rm(directory, { recursive: true, force: true })); const git = join(directory, ".git"); await mkdir(git); await writeFile(join(git, "x.txt"), "needle\n"); await writeFile(join(directory, "a.txt"), "needle\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const multiline of [false, true]) { for (const glob of [undefined, "*"]) { for (const [path, expected] of [ [directory, "a.txt"], [git, ".git/x.txt"], ]) { const result = await callTool(tool, { path, glob, multiline, literal: true, pattern: "needle", outputMode: "files", }); assert.equal(result.isError, undefined, JSON.stringify(reportOf(result, "grep"))); assert.equal(grepCounts(result).matches, 1); assert.equal(result.content[0].type, "text"); assert.equal(result.content[0].text.replace(/\\/g, "/"), expected); } } } }); test("explicit file with different case and a glob → admits the same file on a case-insensitive filesystem", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-case-admission-")); t.after(() => rm(directory, { recursive: true, force: true })); const file = join(directory, "MiXeD.txt"); const requested = join(directory, "mixed.txt"); await writeFile(file, "needle\n"); let alias; try { alias = await stat(requested, { bigint: true }); } catch (error) { if (error instanceof Error && "code" in error && error.code === "ENOENT") return t.skip("Temporary filesystem is case-sensitive"); throw error; } const original = await stat(file, { bigint: true }); assert.equal(alias.dev, original.dev); assert.equal(alias.ino, original.ino); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const result = await callTool(tool, { path: requested, glob: "*.txt", literal: true, pattern: "needle", }); assert.equal(result.isError, undefined, JSON.stringify(reportOf(result, "grep"))); assert.equal(grepCounts(result).matches, 1); assert.equal(grepCounts(result).files, 1); assert.equal(result.content[0].type, "text"); assert.match(result.content[0].text, /1#[0-9A-Z]+│needle/); assert.equal(reportOf(result, "grep")?.diagnostics, undefined); }); test("explicit file with a dangling sibling link and a glob → returns complete matches without listing diagnostics", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-dangling-sibling-")); t.after(() => rm(directory, { recursive: true, force: true })); const file = join(directory, "fixture.txt"); const link = join(directory, "broken.txt"); await writeFile(file, "needle\n"); try { await symlink( join(directory, "missing"), link, process.platform === "win32" ? "junction" : "dir", ); } catch (error) { if ( error instanceof Error && "code" in error && typeof error.code === "string" && ["EPERM", "EACCES", "ENOTSUP"].includes(error.code) ) return t.skip("Symbolic link creation unavailable"); throw error; } await assert.rejects(stat(link), { code: "ENOENT" }); let listingDiagnostics = ""; const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, { runRgPaths: async (...args) => { const listed = await runRgPaths(...args); listingDiagnostics = listed.stderr; return listed; }, }); const result = await callTool(tool, { path: file, glob: "*.txt", literal: true, pattern: "needle", }); assert.match(listingDiagnostics, /broken\.txt/); assert.equal(result.isError, undefined, JSON.stringify(reportOf(result, "grep"))); assert.equal(grepCounts(result).matches, 1); assert.equal(reportOf(result, "grep")?.diagnostics, undefined); }); test("explicit file paths obey ordered glob filters without inheriting ignore rules", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-file-glob-")); try { const kept = join(directory, "keep.ts"); const excluded = join(directory, "drop.test.ts"); const ignored = join(directory, "ignored.ts"); await writeFile(join(directory, ".ignore"), "ignored.ts\n"); await Promise.all([kept, excluded, ignored].map((path) => writeFile(path, "needle\n"))); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const result: any = await invoke( tool, "0", { literal: true, pattern: "needle", path: [kept, excluded, ignored], glob: ["*.ts", "!**/*.test.ts"], outputMode: "files", }, undefined, undefined, ); const files = new Set(result.content[0].text.split("\n")); assert.deepEqual(files, new Set(["keep.ts", "ignored.ts"])); const none: any = await invoke( tool, "0", { literal: true, pattern: "needle", path: excluded, glob: "!**/*.test.ts" }, undefined, undefined, ); assert.equal(grepCounts(none).matches, 0); } finally { await rm(directory, { recursive: true, force: true }); } }); test("directory traversal skips symlinks but explicit linked directories and files remain searchable", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-follow-")); try { const root = join(directory, "root"); const target = join(directory, "target"); const link = join(root, "link"); const secondLink = join(root, "second-link"); await mkdir(root); await mkdir(target); const targetFile = join(target, "linked.txt"); await writeFile(targetFile, "needle\n"); try { await symlink(target, link, process.platform === "win32" ? "junction" : "dir"); await symlink(target, secondLink, process.platform === "win32" ? "junction" : "dir"); } catch (error: any) { if (["EPERM", "EACCES", "ENOTSUP"].includes(error?.code)) return t.skip("symbolic link creation unavailable"); throw error; } const grep = makeGrepOverrideWithBackend(root, DEFAULT_CONFIG, {}); const hidden: any = await invoke( grep, "0", { literal: true, pattern: "needle" }, undefined, undefined, ); assert.equal(grepCounts(hidden).matches, 0); const linkedDirectory = await callTool(grep, { path: link, literal: true, pattern: "needle", outputMode: "files", }); assert.equal( linkedDirectory.isError, undefined, JSON.stringify(reportOf(linkedDirectory, "grep")), ); assert.equal(grepCounts(linkedDirectory).matches, 1); assert.equal(linkedDirectory.content[0].type, "text"); assert.equal(linkedDirectory.content[0].text.replace(/\\/g, "/"), "link/linked.txt"); const found: any = await invoke( grep, "0", { literal: true, pattern: "needle", path: join(link, "linked.txt") }, undefined, undefined, ); const [header, anchored] = found.content[0].text.split("\n"); const resultPath = header.slice(0, header.lastIndexOf(" · ")); assert.equal(resultPath.replace(/\\/g, "/"), "link/linked.txt"); assert.equal(found.content[0].text.match(/ · 1 match/g)?.length, 1); const anchor = anchored.slice(0, anchored.indexOf("│")); const edit: any = makeEditOverride(root, DEFAULT_CONFIG); await invoke( edit, "0", { path: resultPath, edits: [{ action: "replace", anchor, body: ["updated"] }] }, undefined, undefined, ); assert.equal(await readFile(targetFile, "utf8"), "updated\n"); assert.equal((await lstat(link)).isSymbolicLink(), true); assert.equal(await readlink(link), target); assert.equal((await lstat(secondLink)).isSymbolicLink(), true); } finally { await rm(directory, { recursive: true, force: true }); } }); test("unbounded lookbehind → native JS matches after rg discovery", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-lookbehind-")); t.after(() => rm(directory, { recursive: true, force: true })); await writeFile(join(directory, "fixture.txt"), "aaab\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const result = await invoke(tool, "0", { pattern: "(?<=a+)b", literal: false }); assert.equal(result.isError, undefined, JSON.stringify(reportOf(result, "grep"))); assert.equal(grepCounts(result).matches, 1); }); test("line-based grep uses context for display but never matches across CRLF", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-lines-")); try { await writeFile(join(directory, "fixture.txt"), "alpha\r\nbeta\r\ngamma\r\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const context = await invoke( tool, "0", { literal: true, pattern: "alpha", context: 1 }, undefined, undefined, ); assert.match(context.content[0].text, /fixture\.txt · 1 match/); assert.match(context.content[0].text, /1#[0-9A-Z]+│alpha/); assert.match(context.content[0].text, /2#[0-9A-Z]+│beta/); assert.doesNotMatch(context.content[0].text, /3#[0-9A-Z]+│gamma/); for (const [pattern, literal] of [ ["alpha\\nbeta", false], ["alpha\nbeta", true], ] as const) { const result = await invoke(tool, "0", { pattern, literal }); assert.equal(result.isError, undefined); assert.equal(grepCounts(result).matches, 0); } const limited = await invoke( tool, "0", { literal: true, pattern: ["alpha", "beta"], outputMode: "count", limit: 1 }, undefined, undefined, ); assert.match(limited.content[0].text, /fixture\.txt: 1/); assert.equal(reportOf(limited, "grep")?.matchLimit, 1); } finally { await rm(directory, { recursive: true, force: true }); } }); test("multiline grep anchors CRLF spans and counts overlapping physical lines once", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-multiline-")); try { const file = join(directory, "fixture.txt"); await writeFile(file, "alpha\r\nbeta\r\ngamma\r\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const regex = await invoke( tool, "0", { pattern: "alpha\\nbeta", path: file, literal: false, multiline: true }, undefined, undefined, ); assert.match(regex.content[0].text, /fixture\.txt · 2 matches/); assert.match(regex.content[0].text, /1#[0-9A-Z]+│alpha/); assert.match(regex.content[0].text, /2#[0-9A-Z]+│beta/); const literal = await invoke( tool, "0", { pattern: "alpha\nbeta", path: file, literal: true, multiline: true }, undefined, undefined, ); assert.match(literal.content[0].text, /fixture\.txt · 2 matches/); const overlapping = await invoke( tool, "0", { pattern: ["alpha\\nbeta", "beta\\ngamma"], path: file, multiline: true, literal: false }, undefined, undefined, ); assert.match(overlapping.content[0].text, /fixture\.txt · 3 matches/); const bounded = await invoke( tool, "0", { pattern: "alpha[\\w\\s]*gamma", literal: false, path: file, multiline: true, outputMode: "count", limit: 2, }, undefined, undefined, ); assert.match(bounded.content[0].text, /fixture\.txt: 2/); assert.equal(grepCounts(bounded).matches, 2); assert.equal(grepCounts(bounded).files, 1); assert.equal(reportOf(bounded, "grep")?.matchLimit, 2); const files = await invoke( tool, "0", { pattern: "alpha\\nbeta", literal: false, path: file, multiline: true, outputMode: "files", limit: 1, }, undefined, undefined, ); assert.equal(files.content[0].text, "fixture.txt"); assert.equal(reportOf(files, "grep")?.matchLimit, 1); const boundary = await invoke( tool, "0", { pattern: "alpha\\n", path: file, multiline: true, literal: false }, undefined, undefined, ); assert.match(boundary.content[0].text, /fixture\.txt · 1 match/); const edit: any = makeEditOverride(directory, DEFAULT_CONFIG); const anchor = regex.content[0].text.match(/(2#[0-9A-Z]+)│beta/)?.[1]; assert.ok(anchor); await invoke( edit, "0", { path: file, edits: [{ action: "replace", anchor, body: ["updated"] }] }, undefined, undefined, ); assert.equal(await readFile(file, "utf8"), "alpha\r\nupdated\r\ngamma\r\n"); } finally { await rm(directory, { recursive: true, force: true }); } }); test("multiline grep preserves BOM and standalone CR while normalizing CRLF", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-multiline-cr-")); try { const file = join(directory, "fixture.txt"); await writeFile(file, "\uFEFFalpha\rsolo\r\nbeta\r\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const result = await invoke( tool, "0", { pattern: "alpha\\rsolo\\nbeta", path: file, multiline: true, literal: false }, undefined, undefined, ); assert.match(result.content[0].text, /fixture\.txt · 2 matches/); assert.match(result.content[0].text, /1#[0-9A-Z]+│\uFEFFalpha␍solo/); assert.match(result.content[0].text, /2#[0-9A-Z]+│beta/); const literal = await invoke( tool, "0", { pattern: "alpha\rsolo\r\nbeta", path: file, multiline: true, literal: true }, undefined, undefined, ); assert.match(literal.content[0].text, /fixture\.txt · 2 matches/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("multiline grep anchors empty lines and line-start zero-width matches", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-multiline-empty-")); try { const file = join(directory, "fixture.txt"); await writeFile(file, "alpha\r\n\r\nbeta\r\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const span = await invoke( tool, "0", { pattern: "alpha\\n\\nbeta", path: file, multiline: true, literal: false }, undefined, undefined, ); assert.match(span.content[0].text, /fixture\.txt · 3 matches/); assert.match(span.content[0].text, /2#[0-9A-Z]+│\n3#[0-9A-Z]+│beta/); const starts = await invoke( tool, "0", { pattern: "^", path: file, multiline: true, literal: false }, undefined, undefined, ); assert.match(starts.content[0].text, /fixture\.txt · 3 matches/); assert.match(starts.content[0].text, /1#[0-9A-Z]+│alpha/); assert.match(starts.content[0].text, /3#[0-9A-Z]+│beta/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("multiline zero-width EOF stays on the last existing physical line", { skip: unsupportedByHost(["(?-m:$)"]), }, async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-multiline-eof-")); try { const file = join(directory, "fixture.txt"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const content of ["last line", "last line\n"]) { await writeFile(file, content); const result = await invoke( tool, "0", { pattern: "(?-m:$)", path: file, multiline: true, literal: false }, undefined, undefined, ); if (content.endsWith("\n")) { assert.equal(grepCounts(result).matches, 0); } else { assert.match(result.content[0].text, /fixture\.txt · 1 match/); assert.match(result.content[0].text, /1#[0-9A-Z]+│last line/); } } } finally { await rm(directory, { recursive: true, force: true }); } }); test("files and count modes aggregate only matching lines inside the limit", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-output-modes-")); try { const first = join(directory, "a.txt"); const second = join(directory, "b.txt"); await writeFile(first, "needle needle\nneedle\n"); await writeFile(second, "needle\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const query = { literal: true, pattern: "needle", path: [first, second], limit: 1 }; const files = await invoke( tool, "files", { ...query, outputMode: "files" }, undefined, undefined, ); assert.match(files.content[0].text, /^(?:a|b)\.txt$/); assert.equal(reportOf(files, "grep")?.matchLimit, 1); const limited = await invoke( tool, "count", { ...query, outputMode: "count" }, undefined, undefined, ); assert.equal(grepCounts(limited).matches, 1); assert.equal(grepCounts(limited).files, 1); const complete = await invoke( tool, "count-all", { ...query, outputMode: "count", limit: 10 }, undefined, undefined, ); assert.equal(grepCounts(complete).matches, 3); assert.equal(grepCounts(complete).files, 2); const contextual = await invoke( tool, "content", { ...query, path: first, outputMode: "content", context: 1 }, undefined, undefined, ); assert.match(contextual.content[0].text, /a\.txt · 1 match/); assert.match(contextual.content[0].text, /2#[0-9A-Z]+│needle/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("real rg accepts wildcard-only regexes and preserves limits and literal mode", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-wildcard-")); try { await writeFile(join(directory, "fixture.txt"), "first\n\n.*\n"); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const [pattern, expected] of [ ["[\\w.*]*", 3], ["^[\\w.*]+$", 2], ["[\\w.*]?", 3], ] as const) { const result: any = await invoke( tool, "count", { pattern, literal: false, outputMode: "count" }, undefined, undefined, ); assert.equal(grepCounts(result).matches, expected); assert.equal(grepCounts(result).files, 1); } const limited: any = await invoke( tool, "limited", { pattern: "[\\w.*]*", literal: false, limit: 1 }, undefined, undefined, ); assert.match(limited.content[0].text, /1#[0-9A-Z]+│first/); assert.doesNotMatch(limited.content[0].text, /[23]#[0-9A-Z]+│/); const literal: any = await invoke( tool, "literal", { pattern: ".*", literal: true }, undefined, undefined, ); assert.match(literal.content[0].text, /3#[0-9A-Z]+│\.\*/); } finally { await rm(directory, { recursive: true, force: true }); } }); test("tools share physical lines and anchors across text representations", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-text-integration-")); const call = (tool: any, params: any) => invoke(tool, "0", params, undefined, undefined); const rows = (result: any): string[] => result.content[0].text.split("\n").filter((line: string) => /^\d+#/.test(line)); try { const file = join(directory, "fixture.txt"); for (const [index, before] of [ "a\nold\n", "a\r\nold\r\n", "\uFEFFa\r\nold\nkeep\r\n", "a\rb\nold\n", "a\rb\nold", ].entries()) { await call(makeWriteOverride(directory), { path: file, content: before, mode: index === 0 ? "create" : "overwrite", }); assert.equal(await readFile(file, "utf8"), before); const read = await call(makeReadOverride(directory, DEFAULT_CONFIG), { path: file }); const grep = await call(makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}), { literal: true, path: file, pattern: "old", context: 2, }); assert.deepEqual(rows(grep), rows(read)); if (before.includes("a\rb")) assert.match(rows(read)[0], /│a␍b$/); const anchor = rows(grep)[1].split("│")[0]; const edit = await call(makeEditOverride(directory, DEFAULT_CONFIG), { path: file, edits: [{ action: "replace", anchor, body: ["new"] }], }); assert.equal(await readFile(file, "utf8"), before.replace("old", "new")); assert.match(edit.details.diff, /^-2 old/m); assert.match(edit.details.diff, /^\+2 new/m); assert.ok(!edit.details.diff.includes("\r")); await call(makeReplaceTool(directory, DEFAULT_CONFIG), { path: file, replacements: [{ literal: true, find: "new", replace: "next" }], }); assert.equal(await readFile(file, "utf8"), before.replace("old", "next")); } } finally { await rm(directory, { recursive: true, force: true }); } }); test("long-line previews preserve full-line anchors across literal and JavaScript regex queries", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-preview-")); try { const long = "😀界".repeat(220) + "NEEDLE" + "tail".repeat(200); const context = "x" + "😀".repeat(400); await writeFile(join(directory, "long.txt"), `${long}\r\nfollow\r\n${context}\r\n`); const tool = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const params of [ { literal: true, pattern: "NEEDLE" }, { pattern: ["NEEDLE", "tail"], literal: true }, { pattern: "NEEDLE(?:tail)+", literal: false }, ]) { const result: any = await invoke( tool, "preview", { ...params, context: 2 }, undefined, undefined, ); const output = result.content[0].text; const row = output.split("\n").find((line: string) => line.startsWith("1#")); assert.ok(row.startsWith(`1#${computeLineHash(1, long, 4)}│[partial, columns `)); assert.ok(row.includes("NEEDLE")); assert.equal(Buffer.from(row).toString("utf8"), row); assert.ok( output.includes( `3#${computeLineHash(3, context, 4)}│[partial, columns 1-${DEFAULT_OUTPUT_BUDGETS.grepPreviewUnits - 1}]`, ), ); assert.equal( reportOf(result, "grep")?.linePreviewLimit, DEFAULT_OUTPUT_BUDGETS.grepPreviewUnits, ); } } finally { await rm(directory, { recursive: true, force: true }); } }); test("all text tools share logical CRLF matching, anchors, and mutation separators", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-shared-text-")); try { const path = join(directory, "mixed.txt"); const before = "\uFEFFhead\r\nalpha\r\nbeta\nstand\rCR\r\r\nlast"; const write = makeWriteOverride(directory); const read = makeReadOverride(directory, DEFAULT_CONFIG); const grep = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); await invoke(write, "write", { path, content: before, mode: "create" }, undefined, undefined, { cwd: directory, } as ExtensionContext); assert.equal(await readFile(path, "utf8"), before); const observed = await invoke(read, "read", { path }, undefined, undefined, { cwd: directory, } as ExtensionContext); assert.ok(observed.content[0].type === "text"); const rows = observed.content[0].text .split("\n") .filter((line: string) => /^\d+#[0-9A-Z]+│/.test(line)); for (const query of [ { pattern: ["alpha", "beta"], literal: true }, { pattern: "alpha|beta", literal: false }, ]) { const found = await invoke(grep, "grep", { path, ...query }, undefined, undefined); assert.ok(found.content[0].text.includes(rows[1]), JSON.stringify(query)); assert.ok(found.content[0].text.includes(rows[2]), JSON.stringify(query)); } const contextual = await invoke( grep, "grep", { literal: true, path, pattern: "alpha", context: 1 }, undefined, undefined, ); assert.match(contextual.content[0].text, /mixed\.txt · 1 match/); assert.ok(contextual.content[0].text.includes(rows[1])); assert.ok(contextual.content[0].text.includes(rows[2])); const carriage = await invoke( grep, "grep", { path, pattern: "\\r", literal: false }, undefined, undefined, ); assert.ok(carriage.content[0].text.includes(rows[3])); assert.ok(!carriage.content[0].text.includes(rows[1])); const standaloneEnd = await invoke( grep, "grep", { path, pattern: "CR\\r$", literal: false }, undefined, undefined, ); assert.ok(standaloneEnd.content[0].text.includes(rows[3])); const expected = "\uFEFFhead\r\nA\r\nB\r\nC\nstand\rCR\r\r\nlast"; for (const mode of ["edit", "literal", "regex"]) { await invoke( write, "reset", { path, content: before, mode: "overwrite" }, undefined, undefined, { cwd: directory, } as ExtensionContext, ); if (mode === "edit") { await invoke( makeEditOverride(directory, DEFAULT_CONFIG), "edit", { path, edits: [ { action: "replace", anchor: rows[1].split("│")[0], end: rows[2].split("│")[0], body: ["A", "B", "C"], }, ], }, undefined, undefined, { cwd: directory } as ExtensionContext, ); } else { await invoke( makeReplaceTool(directory, DEFAULT_CONFIG), "replace", { path, replacements: [ { find: mode === "regex" ? "alpha\\nbeta" : "alpha\r\nbeta", replace: "A\nB\nC", literal: mode !== "regex", }, ], }, undefined, undefined, { cwd: directory } as ExtensionContext, ); } assert.equal(await readFile(path, "utf8"), expected, mode); } // Whole-file write remains the explicit representation boundary, including EOL conversion. await invoke( write, "convert", { path, content: "alpha\nbeta\n", mode: "overwrite" }, undefined, undefined, { cwd: directory, } as ExtensionContext, ); assert.equal(await readFile(path, "utf8"), "alpha\nbeta\n"); } finally { await rm(directory, { recursive: true, force: true }); } }); test("visible source escapes remain readable and searchable while real CRLF stays preserved", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-source-escapes-")); try { const path = join(directory, "escapes.ts"); const code = 'const eol = "\\r\\n";\r\n'; await invoke( makeWriteOverride(directory), "write", { path, content: code, mode: "create" }, undefined, undefined, { cwd: directory } as ExtensionContext, ); const read = await invoke( makeReadOverride(directory, DEFAULT_CONFIG), "read", { path }, undefined, undefined, { cwd: directory, } as ExtensionContext, ); assert.ok(read.content[0].type === "text"); assert.ok(read.content[0].text.includes(String.raw`const eol = "\r\n";`)); const grep = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const query of [ { pattern: String.raw`\r\n`, literal: true }, { pattern: String.raw`\\r\\n`, literal: false }, ]) { const result = await invoke(grep, "grep", { path, ...query }, undefined, undefined); assert.ok(result.content[0].text.includes(String.raw`const eol = "\r\n";`)); } const replace = makeReplaceTool(directory, DEFAULT_CONFIG); await invoke( replace, "literal", { path, replacements: [{ literal: true, find: String.raw`\r\n`, replace: String.raw`\n` }] }, undefined, undefined, { cwd: directory } as ExtensionContext, ); assert.equal(await readFile(path, "utf8"), 'const eol = "\\n";\r\n'); await invoke( replace, "regex", { path, replacements: [{ find: String.raw`\\n`, replace: String.raw`\r\n`, literal: false }], }, undefined, undefined, { cwd: directory } as ExtensionContext, ); assert.equal(await readFile(path, "utf8"), code); } finally { await rm(directory, { recursive: true, force: true }); } }); test("grep retains original paths and ignores unmatched invalid UTF-8 across many files", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-many-files-")); try { await Promise.all( Array.from({ length: 70 }, (_, i) => writeFile(join(directory, `file ${i}.txt`), "--\r\n")), ); await writeFile(join(directory, "invalid.txt"), Buffer.from([0xc3, 0x28])); const grep = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const result = await invoke( grep, "grep", { path: directory, pattern: "--", literal: true, outputMode: "count", limit: 100 }, undefined, undefined, ); assert.equal((result.content[0].text.match(/file \d+\.txt: 1/g) ?? []).length, 70); assert.doesNotMatch(result.content[0].text, /invalid\.txt|UNSUPPORTED_ENCODING/); assert.doesNotMatch(result.content[0].text, /hashline-grep-/); assert.equal(reportOf(result, "grep")?.diagnostics, undefined); assert.equal(reportOf(result, "grep")?.scanLimit, undefined); } finally { await rm(directory, { recursive: true, force: true }); } }); test("NUL files are silently skipped across search scopes and output modes", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-nul-")); try { const binary = join(directory, "binary.txt"); const source = Buffer.concat([ Buffer.from("needle\r\n"), Buffer.alloc(65536, 97), Buffer.from("\0needle\n"), ]); await writeFile(binary, source); await writeFile(join(directory, "valid.txt"), "needle\n"); const grep = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const outputMode of ["content", "files", "count"] as const) { for (const multiline of [false, true]) { const skipped = await invoke( grep, "binary", { path: binary, pattern: "needle", literal: true, outputMode, multiline }, undefined, undefined, ); assert.equal(grepCounts(skipped).matches, 0); assert.equal(reportOf(skipped, "grep")?.diagnostics, undefined); const result = await invoke( grep, "directory", { path: directory, pattern: "needle", literal: true, outputMode, multiline, limit: 1 }, undefined, undefined, ); assert.match(result.content[0].text, /valid\.txt/); assert.doesNotMatch(result.content[0].text, /binary\.txt|UNSUPPORTED_TEXT/); assert.equal(reportOf(result, "grep")?.diagnostics, undefined); assert.equal(reportOf(result, "grep")?.scanLimit, undefined); } } assert.deepEqual(await readFile(binary), source); } finally { await rm(directory, { recursive: true, force: true }); } }); test("invalid UTF-8 matches use plain previews while valid files retain edit anchors", async () => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-preview-")); try { const grep = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); const invalid = join(directory, "invalid.txt"); for (const separator of ["\n", "\r\n"]) { const source = Buffer.concat([ Buffer.from(`before${separator}needle `), Buffer.from([0xff]), Buffer.from(`${separator}after${separator}`), ]); await writeFile(invalid, source); const result = await invoke( grep, "preview", { path: invalid, pattern: "needle", literal: true, context: 1 }, undefined, undefined, ); assert.match(result.content[0].text, /1│before\n2│needle �\n3│after/); assert.deepEqual(reportOf(result, "grep")?.invalidUtf8, ["invalid.txt"]); assert.doesNotMatch(result.content[0].text, /\d+#/); assert.equal(reportOf(result, "grep")?.diagnostics, undefined); assert.equal(reportOf(result, "grep")?.scanLimit, undefined); assert.deepEqual(await readFile(invalid), source); } await writeFile(join(directory, "valid.txt"), "needle\n"); const mixed = await invoke( grep, "mixed", { path: directory, pattern: "needle", literal: true }, undefined, undefined, ); assert.match(mixed.content[0].text, /valid\.txt · 1 match\n1#[A-Z0-9]+│needle/); assert.match(mixed.content[0].text, /invalid\.txt · 1 match\n2│needle �/); for (const outputMode of ["files", "count"] as const) { const result = await invoke( grep, "summary", { path: directory, pattern: "needle", literal: true, outputMode }, undefined, undefined, ); assert.match(result.content[0].text, /invalid\.txt/); assert.match(result.content[0].text, /valid\.txt/); assert.equal(reportOf(result, "grep")?.diagnostics, undefined); assert.equal(reportOf(result, "grep")?.scanLimit, undefined); } const multiline = await invoke( grep, "multiline", { path: invalid, pattern: "needle[\\w ]*\\nafter", literal: false, multiline: true }, undefined, undefined, ); // The explicit class excludes the replacement character in the lossy view. assert.equal(grepCounts(multiline).matches, 0); } finally { await rm(directory, { recursive: true, force: true }); } }); test("grep paths with input shorthand or literal separators still edit the searched file", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-path-roundtrip-")); try { const cases = [ { name: "@config.txt", other: "config.txt" }, { name: "space\u00a0file.txt", other: "space file.txt" }, ...(process.platform === "win32" ? [] : [{ name: "dir\\target.txt", other: "dir/target.txt" }]), ]; for (const { name, other } of cases) { await t.test(name, async () => { const file = join(directory, name); const otherFile = join(directory, other); await mkdir(dirname(otherFile), { recursive: true }); await writeFile(file, "needle\n"); await writeFile(otherFile, "needle\n"); const grep = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); let content = ""; let returnedPath = ""; for (const outputMode of ["content", "files", "count"] as const) { const result = await invoke(grep, "path", { path: pathToFileURL(file).href, pattern: "needle", literal: true, outputMode, }); const text = result.content[0].text; const firstLine = text.split("\n")[0]; returnedPath = outputMode === "content" ? firstLine.slice(0, firstLine.lastIndexOf(" · ")) : outputMode === "count" ? firstLine.slice(0, firstLine.lastIndexOf(": ")) : firstLine; assert.equal(canonicalPath(directory, returnedPath), file, outputMode); if (outputMode === "content") content = text; } const anchor = content.match(/(1#[0-9A-Z]+)│needle/)?.[1]; assert.ok(anchor); await invoke(makeEditOverride(directory, DEFAULT_CONFIG), "edit", { path: returnedPath, edits: [{ action: "replace", anchor, body: ["updated"] }], }); assert.equal(await readFile(file, "utf8"), "updated\n"); assert.equal(await readFile(otherFile, "utf8"), "needle\n"); }); } } finally { await rm(directory, { recursive: true, force: true }); } }); test("literal LF and CRLF queries match original valid and invalid UTF-8 files alike", async (t) => { const directory = await mkdtemp(join(tmpdir(), "hl-grep-raw-crlf-")); try { await writeFile(join(directory, "valid.txt"), "alpha\r\nbeta\r\n"); await writeFile( join(directory, "invalid.txt"), Buffer.concat([Buffer.from("alpha\r\nbeta\r\n"), Buffer.from([0xff])]), ); const grep = makeGrepOverrideWithBackend(directory, DEFAULT_CONFIG, {}); for (const outputMode of ["content", "files", "count"] as const) { await t.test(outputMode, async () => { const result = await invoke(grep, "crlf", { path: directory, pattern: "alpha\r\nbeta", literal: true, multiline: true, outputMode, }); assert.match(result.content[0].text, /(?:^|\n)valid\.txt/); assert.match(result.content[0].text, /(?:^|\n)invalid\.txt/); if (outputMode === "content") { assert.match(result.content[0].text, /1│alpha\n2│beta/); assert.match(result.content[0].text, /1#[0-9A-Z]+│alpha\n2#[0-9A-Z]+│beta/); } else if (outputMode === "count") { assert.equal(grepCounts(result).matches, 4); assert.equal(grepCounts(result).files, 2); } assert.equal(reportOf(result, "grep")?.diagnostics, undefined); assert.equal(reportOf(result, "grep")?.scanLimit, undefined); const lf = await invoke(grep, "lf", { path: directory, pattern: "alpha\nbeta", literal: true, multiline: true, outputMode, }); assert.match(lf.content[0].text, /valid\.txt/); assert.match(lf.content[0].text, /invalid\.txt/); }); } } finally { await rm(directory, { recursive: true, force: true }); } }); test("oversized candidate path record → bounded runner rejects before invoking its consumer", async (t) => { const spawn = childProcess.spawn; t.mock.method(childProcess, "spawn", (...args: Parameters) => spawn(process.execPath, args[1], args[2]), ); syncBuiltinESMExports(); t.after(() => { t.mock.restoreAll(); syncBuiltinESMExports(); }); let called = false; await assert.rejects( runRgPaths( [ "--input-type=module", "--eval", "process.stdout.write(Buffer.alloc(Number(process.argv[1]), 97))", String(MAX_RG_RECORD_BYTES + 1), ], AbortSignal.timeout(10_000), () => { called = true; return true; }, ), (error: unknown) => error instanceof Error && error.message.includes("record exceeds"), ); assert.equal(called, false); });