From 4b17870049659b777c736c55bc933f0af08b941a Mon Sep 17 00:00:00 2001 From: Will Chen Date: Mon, 8 Dec 2025 11:21:07 -0800 Subject: [PATCH] =?UTF-8?q?Warn=20(not=20error)=20on=20identical=20search-?= =?UTF-8?q?replace=20blocks=20and=20include=20searc=E2=80=A6=20(#1899)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …h-replace failure in error message Addresses part of #1898 --- > [!NOTE] > Treat identical SEARCH/REPLACE as a no-op warning and propagate detailed applySearchReplace errors (incl. fuzzy match stats) to the UI; add scoped logging and update tests/snapshots. > > - **Processors**: > - `src/pro/main/ipc/processors/search_replace_processor.ts`: Log a warning (not error) when SEARCH and REPLACE blocks are identical; add scoped logger; keep content unchanged. > - `src/ipc/processors/response_processor.ts`: Include detailed failure reason from `applySearchReplace` in dry-run issues. > - **Tests & Snapshots**: > - `search_replace_processor.spec.ts`: Update test to expect success when blocks are identical. > - `e2e-tests/...turbo-edits-v2...snapshot`: Reflect detailed error message with fuzzy match similarity/threshold. > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit a394d297d5561ada3bdd197dbb4e6aca6928ad99. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- ## Summary by cubic Warn instead of error when search and replace blocks are identical, and include the specific failure reason in search-replace error messages for clearer feedback. - **Bug Fixes** - Treat identical search/replace blocks as a no-op: log a warning and return success. - Bubble up detailed failure reasons to the UI (e.g., no match and fuzzy similarity/threshold). - Add scoped logging to the processor and update the e2e snapshot to reflect new error messaging. Written for commit a394d297d5561ada3bdd197dbb4e6aca6928ad99. Summary will update automatically on new commits. --- ...c.ts_turbo-edits-v2---search-replace-fallback-1.txt | 4 ++-- src/ipc/processors/response_processor.ts | 3 ++- .../ipc/processors/search_replace_processor.spec.ts | 10 +++++----- .../main/ipc/processors/search_replace_processor.ts | 10 +++++----- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/e2e-tests/snapshots/turbo_edits_v2.spec.ts_turbo-edits-v2---search-replace-fallback-1.txt b/e2e-tests/snapshots/turbo_edits_v2.spec.ts_turbo-edits-v2---search-replace-fallback-1.txt index b31deb2..61f5574 100644 --- a/e2e-tests/snapshots/turbo_edits_v2.spec.ts_turbo-edits-v2---search-replace-fallback-1.txt +++ b/e2e-tests/snapshots/turbo_edits_v2.spec.ts_turbo-edits-v2---search-replace-fallback-1.txt @@ -18,7 +18,7 @@ }, { "role": "user", - "content": "There was an issue with the following `dyad-search-replace` tags. Make sure you use `dyad-read` to read the latest version of the file and then trying to do search & replace again.\n \nFile path: src/pages/Index.tsx\nError: Unable to apply search-replace to file" + "content": "There was an issue with the following `dyad-search-replace` tags. Make sure you use `dyad-read` to read the latest version of the file and then trying to do search & replace again.\n \nFile path: src/pages/Index.tsx\nError: Unable to apply search-replace to file because: Search block did not match any content in the target file. Best fuzzy match had similarity of 0.0% (threshold: 90.0%)" }, { "role": "assistant", @@ -26,7 +26,7 @@ }, { "role": "user", - "content": "There was an issue with the following `dyad-search-replace` tags. Please fix the errors by generating the code changes using `dyad-write` tags instead.\n \nFile path: src/pages/Index.tsx\nError: Unable to apply search-replace to file" + "content": "There was an issue with the following `dyad-search-replace` tags. Please fix the errors by generating the code changes using `dyad-write` tags instead.\n \nFile path: src/pages/Index.tsx\nError: Unable to apply search-replace to file because: Search block did not match any content in the target file. Best fuzzy match had similarity of 0.0% (threshold: 90.0%)" } ], "stream": true, diff --git a/src/ipc/processors/response_processor.ts b/src/ipc/processors/response_processor.ts index 18c0a37..11267e3 100644 --- a/src/ipc/processors/response_processor.ts +++ b/src/ipc/processors/response_processor.ts @@ -78,7 +78,8 @@ export async function dryRunSearchReplace({ if (!result.success || typeof result.content !== "string") { issues.push({ filePath, - error: "Unable to apply search-replace to file", + error: + "Unable to apply search-replace to file because: " + result.error, }); continue; } diff --git a/src/pro/main/ipc/processors/search_replace_processor.spec.ts b/src/pro/main/ipc/processors/search_replace_processor.spec.ts index fab9ac8..2f15660 100644 --- a/src/pro/main/ipc/processors/search_replace_processor.spec.ts +++ b/src/pro/main/ipc/processors/search_replace_processor.spec.ts @@ -229,8 +229,8 @@ function example() { expect(content).not.toContain("start();"); }); - it("errors when SEARCH and REPLACE blocks are identical", () => { - const original = ["x", "y", "z"].join("\n"); + it("not an error when SEARCH and REPLACE blocks are identical", () => { + const original = ["x", "middle", "z"].join("\n"); const diff = ` <<<<<<< SEARCH middle @@ -238,9 +238,9 @@ middle middle >>>>>>> REPLACE `; - const { success, error } = applySearchReplace(original, diff); - expect(success).toBe(false); - expect(error).toMatch(/Search and replace blocks are identical/i); + const { success, content } = applySearchReplace(original, diff); + expect(success).toBe(true); + expect(content).toBe(original); }); it("errors when SEARCH block matches multiple locations (ambiguous)", () => { diff --git a/src/pro/main/ipc/processors/search_replace_processor.ts b/src/pro/main/ipc/processors/search_replace_processor.ts index 8b19d74..edc1d5d 100644 --- a/src/pro/main/ipc/processors/search_replace_processor.ts +++ b/src/pro/main/ipc/processors/search_replace_processor.ts @@ -3,6 +3,9 @@ import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser"; import { distance } from "fastest-levenshtein"; import { normalizeString } from "@/utils/text_normalization"; +import log from "electron-log"; + +const logger = log.scope("search_replace_processor"); // Minimum similarity threshold for fuzzy matching (0 to 1, where 1 is exact match) const FUZZY_MATCH_THRESHOLD = 0.9; @@ -201,12 +204,9 @@ export function applySearchReplace( }; } - // If search and replace are identical, it's a no-op and should be treated as an error + // If search and replace are identical, it's a no-op and is just treated as a warning if (searchLines.join("\n") === replaceLines.join("\n")) { - return { - success: false, - error: "Search and replace blocks are identical", - }; + logger.warn("Search and replace blocks are identical"); } let matchIndex = -1;