fix(grep): format files_with_matches output as clean file paths

This commit is contained in:
YeonGyu-Kim
2026-02-22 15:19:26 +09:00
parent b175c11b35
commit c806a35e49
2 changed files with 132 additions and 2 deletions

View File

@@ -0,0 +1,123 @@
/// <reference types="bun-types" />
import { describe, expect, test } from "bun:test"
import { formatGrepResult } from "./result-formatter"
import type { GrepResult } from "./types"
describe("formatGrepResult", () => {
describe("#given grep result has error", () => {
describe("#when formatting result", () => {
test("#then returns error message", () => {
const result: GrepResult = {
matches: [],
totalMatches: 0,
filesSearched: 0,
truncated: false,
error: "ripgrep failed",
}
const formatted = formatGrepResult(result)
expect(formatted).toBe("Error: ripgrep failed")
})
})
})
describe("#given grep result has no matches", () => {
describe("#when formatting result", () => {
test("#then returns no matches message", () => {
const result: GrepResult = {
matches: [],
totalMatches: 0,
filesSearched: 0,
truncated: false,
}
const formatted = formatGrepResult(result)
expect(formatted).toBe("No matches found")
})
})
})
describe("#given grep result is files-with-matches mode", () => {
describe("#when formatting result", () => {
test("#then prints only file paths", () => {
const result: GrepResult = {
matches: [
{ file: "src/foo.ts", line: 0, text: "" },
{ file: "src/bar.ts", line: 0, text: "" },
{ file: "src/baz.ts", line: 0, text: "" },
],
totalMatches: 3,
filesSearched: 3,
truncated: false,
}
const formatted = formatGrepResult(result)
expect(formatted).toBe(
"Found 3 match(es) in 3 file(s)\n\n" +
"src/foo.ts\n\n" +
"src/bar.ts\n\n" +
"src/baz.ts\n",
)
})
})
})
describe("#given grep result is content mode", () => {
describe("#when formatting result", () => {
test("#then prints line numbers and content", () => {
const result: GrepResult = {
matches: [
{ file: "src/foo.ts", line: 10, text: " function hello() {" },
{ file: "src/foo.ts", line: 25, text: " function world() {" },
{ file: "src/bar.ts", line: 5, text: ' import { hello } from "./foo"' },
],
totalMatches: 3,
filesSearched: 2,
truncated: false,
}
const formatted = formatGrepResult(result)
expect(formatted).toBe(
"Found 3 match(es) in 2 file(s)\n\n" +
"src/foo.ts\n" +
" 10: function hello() {\n" +
" 25: function world() {\n\n" +
"src/bar.ts\n" +
' 5: import { hello } from "./foo"\n',
)
})
})
})
describe("#given grep result has mixed file-only and content matches", () => {
describe("#when formatting result", () => {
test("#then skips file-only placeholders and prints valid content matches", () => {
const result: GrepResult = {
matches: [
{ file: "src/foo.ts", line: 0, text: "" },
{ file: "src/foo.ts", line: 10, text: " function hello() {" },
{ file: "src/bar.ts", line: 0, text: "" },
],
totalMatches: 3,
filesSearched: 2,
truncated: false,
}
const formatted = formatGrepResult(result)
expect(formatted).toBe(
"Found 3 match(es) in 2 file(s)\n\n" +
"src/foo.ts\n" +
" 10: function hello() {\n\n" +
"src/bar.ts\n",
)
})
})
})
})

View File

@@ -10,6 +10,7 @@ export function formatGrepResult(result: GrepResult): string {
}
const lines: string[] = []
const isFilesOnlyMode = result.matches.every((match) => match.line === 0 && match.text.trim() === "")
lines.push(`Found ${result.totalMatches} match(es) in ${result.filesSearched} file(s)`)
if (result.truncated) {
@@ -26,8 +27,14 @@ export function formatGrepResult(result: GrepResult): string {
for (const [file, matches] of byFile) {
lines.push(file)
if (!isFilesOnlyMode) {
for (const match of matches) {
lines.push(` ${match.line}: ${match.text.trim()}`)
const trimmedText = match.text.trim()
if (match.line === 0 && trimmedText === "") {
continue
}
lines.push(` ${match.line}: ${trimmedText}`)
}
}
lines.push("")
}