Warn (not error) on identical search-replace blocks and include searc… (#1899)

…h-replace failure in error message

Addresses part of #1898 

<!-- CURSOR_SUMMARY -->
---

> [!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.
> 
> <sup>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).</sup>
<!-- /CURSOR_SUMMARY -->



<!-- This is an auto-generated description by cubic. -->
---
## 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.

<sup>Written for commit a394d297d5561ada3bdd197dbb4e6aca6928ad99.
Summary will update automatically on new commits.</sup>

<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Will Chen
2025-12-08 11:21:07 -08:00
committed by GitHub
parent 1b678041ab
commit 4b17870049
4 changed files with 14 additions and 13 deletions

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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)", () => {

View File

@@ -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;