mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
fix: ripgrep search result handling and formatting (#1831)
Problem:
- Previous implementation incorrectly grouped context lines with matches
- Context lines were split into beforeContext/afterContext arrays, making it difficult to maintain proper line order
- Output format was inconsistent with read_file output
- Non-contiguous search results were sometimes broken or incorrectly grouped
- Example of incorrect output:
# bench/bundle-test/rollup.config.mjs
16 | file: 'dist/index-wasm.min.mjs',
17 | format: 'es',
24 | output: {
----
25 | file: 'dist/index-lite.min.mjs',
26 | format: 'es',
----
# packages/rehype/package.json
----
10 | it('run', async () => {
27 | "files": [
28 | "dist"
----
Solution:
- Replace separate context arrays with a single 'lines' array containing both matches and context
- Add isMatch flag to distinguish between match and context lines
- Improve contiguity detection by checking if line numbers are sequential
- Standardize output format to match read_file output
- Properly handle non-contiguous search results by creating new result groups
- Example of correct output:
# bench/bundle-test/rollup.config.mjs
16 | file: 'dist/index-wasm.min.mjs',
17 | format: 'es',
----
24 | output: {
25 | file: 'dist/index-lite.min.mjs',
26 | format: 'es',
----
This change ensures search results are properly grouped and displayed with the correct context.
Signed-off-by: Eric Wheeler <roo-code@z.ewheeler.org>
Co-authored-by: Eric Wheeler <roo-code@z.ewheeler.org>
This commit is contained in:
parent
4a0c0b1116
commit
9fadd3dbe2
1 changed files with 63 additions and 54 deletions
|
|
@ -50,20 +50,21 @@ rel/path/to/helper.ts
|
|||
const isWindows = /^win/.test(process.platform)
|
||||
const binName = isWindows ? "rg.exe" : "rg"
|
||||
|
||||
interface ContextResult {
|
||||
line: number
|
||||
text: string
|
||||
interface SearchFileResult {
|
||||
file: string
|
||||
searchResults: SearchResult[]
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
file: string
|
||||
line: number
|
||||
column: number
|
||||
text: string
|
||||
beforeContext: ContextResult[]
|
||||
afterContext: ContextResult[]
|
||||
lines: SearchLineResult[]
|
||||
}
|
||||
|
||||
interface SearchLineResult {
|
||||
line: number
|
||||
text: string
|
||||
isMatch: boolean
|
||||
column?: number
|
||||
}
|
||||
// Constants
|
||||
const MAX_RESULTS = 300
|
||||
const MAX_LINE_LENGTH = 500
|
||||
|
|
@ -157,43 +158,50 @@ export async function regexSearchFiles(
|
|||
console.error("Error executing ripgrep:", error)
|
||||
return "No results found"
|
||||
}
|
||||
const results: SearchResult[] = []
|
||||
|
||||
const results: SearchFileResult[] = []
|
||||
let currentResult: Partial<SearchResult> | null = null
|
||||
let currentFile: SearchFileResult | null = null
|
||||
|
||||
output.split("\n").forEach((line) => {
|
||||
if (line) {
|
||||
try {
|
||||
const parsed = JSON.parse(line)
|
||||
if (parsed.type === "match") {
|
||||
if (currentResult) {
|
||||
results.push(currentResult as SearchResult)
|
||||
if (parsed.type === "begin") {
|
||||
currentFile = {
|
||||
file: parsed.data.path.text.toString(),
|
||||
searchResults: [],
|
||||
}
|
||||
|
||||
// Safety check: truncate extremely long lines to prevent excessive output
|
||||
const matchText = parsed.data.lines.text
|
||||
const truncatedMatch = truncateLine(matchText)
|
||||
|
||||
currentResult = {
|
||||
file: parsed.data.path.text,
|
||||
} else if (parsed.type === "end") {
|
||||
// Reset the current result when a new file is encountered
|
||||
results.push(currentFile as SearchFileResult)
|
||||
currentFile = null
|
||||
} else if ((parsed.type === "match" || parsed.type === "context") && currentFile) {
|
||||
const line = {
|
||||
line: parsed.data.line_number,
|
||||
column: parsed.data.submatches[0].start,
|
||||
text: truncatedMatch,
|
||||
beforeContext: [],
|
||||
afterContext: [],
|
||||
}
|
||||
} else if (parsed.type === "context" && currentResult) {
|
||||
// Apply the same truncation logic to context lines
|
||||
const contextText = parsed.data.lines.text
|
||||
const truncatedContext = truncateLine(contextText)
|
||||
let contextResult: ContextResult = {
|
||||
line: parsed.data.line_number,
|
||||
text: truncatedContext,
|
||||
text: truncateLine(parsed.data.lines.text),
|
||||
isMatch: parsed.type === "match",
|
||||
...(parsed.type === "match" && { column: parsed.data.absolute_offset }),
|
||||
}
|
||||
|
||||
if (parsed.data.line_number < currentResult.line!) {
|
||||
currentResult.beforeContext!.push(contextResult)
|
||||
const lastResult = currentFile.searchResults[currentFile.searchResults.length - 1]
|
||||
if (lastResult?.lines.length > 0) {
|
||||
const lastLine = lastResult.lines[lastResult.lines.length - 1]
|
||||
|
||||
// If this line is contiguous with the last result, add to it
|
||||
if (parsed.data.line_number <= lastLine.line + 1) {
|
||||
lastResult.lines.push(line)
|
||||
} else {
|
||||
// Otherwise create a new result
|
||||
currentFile.searchResults.push({
|
||||
lines: [line],
|
||||
})
|
||||
}
|
||||
} else {
|
||||
currentResult.afterContext!.push(contextResult)
|
||||
// First line in file
|
||||
currentFile.searchResults.push({
|
||||
lines: [line],
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -202,9 +210,7 @@ export async function regexSearchFiles(
|
|||
}
|
||||
})
|
||||
|
||||
if (currentResult) {
|
||||
results.push(currentResult as SearchResult)
|
||||
}
|
||||
// console.log(results)
|
||||
|
||||
// Filter results using RooIgnoreController if provided
|
||||
const filteredResults = rooIgnoreController
|
||||
|
|
@ -214,40 +220,43 @@ export async function regexSearchFiles(
|
|||
return formatResults(filteredResults, cwd)
|
||||
}
|
||||
|
||||
function formatResults(results: SearchResult[], cwd: string): string {
|
||||
function formatResults(fileResults: SearchFileResult[], cwd: string): string {
|
||||
const groupedResults: { [key: string]: SearchResult[] } = {}
|
||||
|
||||
let totalResults = fileResults.reduce((sum, file) => sum + file.searchResults.length, 0)
|
||||
let output = ""
|
||||
if (results.length >= MAX_RESULTS) {
|
||||
if (totalResults >= MAX_RESULTS) {
|
||||
output += `Showing first ${MAX_RESULTS} of ${MAX_RESULTS}+ results. Use a more specific search if necessary.\n\n`
|
||||
} else {
|
||||
output += `Found ${results.length === 1 ? "1 result" : `${results.length.toLocaleString()} results`}.\n\n`
|
||||
output += `Found ${totalResults === 1 ? "1 result" : `${totalResults.toLocaleString()} results`}.\n\n`
|
||||
}
|
||||
|
||||
// Group results by file name
|
||||
results.slice(0, MAX_RESULTS).forEach((result) => {
|
||||
const relativeFilePath = path.relative(cwd, result.file)
|
||||
fileResults.slice(0, MAX_RESULTS).forEach((file) => {
|
||||
const relativeFilePath = path.relative(cwd, file.file)
|
||||
if (!groupedResults[relativeFilePath]) {
|
||||
groupedResults[relativeFilePath] = []
|
||||
|
||||
groupedResults[relativeFilePath].push(...file.searchResults)
|
||||
}
|
||||
groupedResults[relativeFilePath].push(result)
|
||||
})
|
||||
|
||||
for (const [filePath, fileResults] of Object.entries(groupedResults)) {
|
||||
output += `${filePath.toPosix()}\n││ ││----\n`
|
||||
output += `# ${filePath.toPosix()}\n`
|
||||
|
||||
fileResults.forEach((result, index) => {
|
||||
const allLines = [...result.beforeContext, result, ...result.afterContext]
|
||||
allLines.forEach((line) => {
|
||||
output += `││ ${line.line} ││${line.text?.trimEnd() ?? ""}\n`
|
||||
})
|
||||
|
||||
if (index < fileResults.length - 1) {
|
||||
output += "││ ││----\n"
|
||||
fileResults.forEach((result) => {
|
||||
// Only show results with at least one line
|
||||
if (result.lines.length > 0) {
|
||||
// Show all lines in the result
|
||||
result.lines.forEach((line) => {
|
||||
const lineNumber = String(line.line).padStart(3, " ")
|
||||
output += `${lineNumber} | ${line.text.trimEnd()}\n`
|
||||
})
|
||||
output += "----\n"
|
||||
}
|
||||
})
|
||||
|
||||
output += "││ ││----\n\n"
|
||||
output += "\n"
|
||||
}
|
||||
|
||||
return output.trim()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue