Explorar o código

Truncate results from search_files to 500 chars max

Matt Rubens hai 10 meses
pai
achega
dfa019e7f4

+ 5 - 0
.changeset/stale-cooks-help.md

@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Truncate search_file output to avoid crashing the extension

+ 51 - 0
src/services/ripgrep/__tests__/index.test.ts

@@ -0,0 +1,51 @@
+// npx jest src/services/ripgrep/__tests__/index.test.ts
+
+import { describe, expect, it } from "@jest/globals"
+import { truncateLine } from "../index"
+
+describe("Ripgrep line truncation", () => {
+	// The default MAX_LINE_LENGTH is 500 in the implementation
+	const MAX_LINE_LENGTH = 500
+
+	it("should truncate lines longer than MAX_LINE_LENGTH", () => {
+		const longLine = "a".repeat(600) // Line longer than MAX_LINE_LENGTH
+		const truncated = truncateLine(longLine)
+
+		expect(truncated).toContain("[truncated...]")
+		expect(truncated.length).toBeLessThan(longLine.length)
+		expect(truncated.length).toEqual(MAX_LINE_LENGTH + " [truncated...]".length)
+	})
+
+	it("should not truncate lines shorter than MAX_LINE_LENGTH", () => {
+		const shortLine = "Short line of text"
+		const truncated = truncateLine(shortLine)
+
+		expect(truncated).toEqual(shortLine)
+		expect(truncated).not.toContain("[truncated...]")
+	})
+
+	it("should correctly truncate a line at exactly MAX_LINE_LENGTH characters", () => {
+		const exactLine = "a".repeat(MAX_LINE_LENGTH)
+		const exactPlusOne = exactLine + "x"
+
+		// Should not truncate when exactly MAX_LINE_LENGTH
+		expect(truncateLine(exactLine)).toEqual(exactLine)
+
+		// Should truncate when exceeding MAX_LINE_LENGTH by even 1 character
+		expect(truncateLine(exactPlusOne)).toContain("[truncated...]")
+	})
+
+	it("should handle empty lines without errors", () => {
+		expect(truncateLine("")).toEqual("")
+	})
+
+	it("should allow custom maximum length", () => {
+		const customLength = 100
+		const line = "a".repeat(customLength + 50)
+
+		const truncated = truncateLine(line, customLength)
+
+		expect(truncated.length).toEqual(customLength + " [truncated...]".length)
+		expect(truncated).toContain("[truncated...]")
+	})
+})

+ 26 - 4
src/services/ripgrep/index.ts

@@ -58,7 +58,19 @@ interface SearchResult {
 	afterContext: string[]
 }
 
+// Constants
 const MAX_RESULTS = 300
+const MAX_LINE_LENGTH = 500
+
+/**
+ * Truncates a line if it exceeds the maximum length
+ * @param line The line to truncate
+ * @param maxLength The maximum allowed length (defaults to MAX_LINE_LENGTH)
+ * @returns The truncated line, or the original line if it's shorter than maxLength
+ */
+export function truncateLine(line: string, maxLength: number = MAX_LINE_LENGTH): string {
+	return line.length > maxLength ? line.substring(0, maxLength) + " [truncated...]" : line
+}
 
 async function getBinPath(vscodeAppRoot: string): Promise<string | undefined> {
 	const checkPath = async (pkgFolder: string) => {
@@ -140,7 +152,8 @@ export async function regexSearchFiles(
 	let output: string
 	try {
 		output = await execRipgrep(rgPath, args)
-	} catch {
+	} catch (error) {
+		console.error("Error executing ripgrep:", error)
 		return "No results found"
 	}
 	const results: SearchResult[] = []
@@ -154,19 +167,28 @@ export async function regexSearchFiles(
 					if (currentResult) {
 						results.push(currentResult as SearchResult)
 					}
+
+					// 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,
-						match: parsed.data.lines.text,
+						match: 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)
+
 					if (parsed.data.line_number < currentResult.line!) {
-						currentResult.beforeContext!.push(parsed.data.lines.text)
+						currentResult.beforeContext!.push(truncatedContext)
 					} else {
-						currentResult.afterContext!.push(parsed.data.lines.text)
+						currentResult.afterContext!.push(truncatedContext)
 					}
 				}
 			} catch (error) {