From dfa019e7f443bf9997ed2ac2556f597a59e4bf77 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Feb 2025 17:34:39 -0500 Subject: [PATCH] Truncate results from search_files to 500 chars max --- .changeset/stale-cooks-help.md | 5 ++ src/services/ripgrep/__tests__/index.test.ts | 51 ++++++++++++++++++++ src/services/ripgrep/index.ts | 30 ++++++++++-- 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 .changeset/stale-cooks-help.md create mode 100644 src/services/ripgrep/__tests__/index.test.ts diff --git a/.changeset/stale-cooks-help.md b/.changeset/stale-cooks-help.md new file mode 100644 index 0000000000..8c9c714738 --- /dev/null +++ b/.changeset/stale-cooks-help.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Truncate search_file output to avoid crashing the extension diff --git a/src/services/ripgrep/__tests__/index.test.ts b/src/services/ripgrep/__tests__/index.test.ts new file mode 100644 index 0000000000..7c3549a827 --- /dev/null +++ b/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...]") + }) +}) diff --git a/src/services/ripgrep/index.ts b/src/services/ripgrep/index.ts index b48c60b5b2..770c897e52 100644 --- a/src/services/ripgrep/index.ts +++ b/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 { 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) {