|
|
@@ -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
|
|
|
+ lines: SearchLineResult[]
|
|
|
+}
|
|
|
+
|
|
|
+interface SearchLineResult {
|
|
|
line: number
|
|
|
- column: number
|
|
|
text: string
|
|
|
- beforeContext: ContextResult[]
|
|
|
- afterContext: ContextResult[]
|
|
|
+ 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,
|
|
|
- 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 = {
|
|
|
+ } 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,
|
|
|
- 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`
|
|
|
-
|
|
|
- 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"
|
|
|
+ output += `# ${filePath.toPosix()}\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()
|