Unverified 提交 4b178700 authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

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. -->
上级 1b678041
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
}, },
{ {
"role": "user", "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", "role": "assistant",
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
}, },
{ {
"role": "user", "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, "stream": true,
......
...@@ -78,7 +78,8 @@ export async function dryRunSearchReplace({ ...@@ -78,7 +78,8 @@ export async function dryRunSearchReplace({
if (!result.success || typeof result.content !== "string") { if (!result.success || typeof result.content !== "string") {
issues.push({ issues.push({
filePath, filePath,
error: "Unable to apply search-replace to file", error:
"Unable to apply search-replace to file because: " + result.error,
}); });
continue; continue;
} }
......
...@@ -229,8 +229,8 @@ function example() { ...@@ -229,8 +229,8 @@ function example() {
expect(content).not.toContain("start();"); expect(content).not.toContain("start();");
}); });
it("errors when SEARCH and REPLACE blocks are identical", () => { it("not an error when SEARCH and REPLACE blocks are identical", () => {
const original = ["x", "y", "z"].join("\n"); const original = ["x", "middle", "z"].join("\n");
const diff = ` const diff = `
<<<<<<< SEARCH <<<<<<< SEARCH
middle middle
...@@ -238,9 +238,9 @@ middle ...@@ -238,9 +238,9 @@ middle
middle middle
>>>>>>> REPLACE >>>>>>> REPLACE
`; `;
const { success, error } = applySearchReplace(original, diff); const { success, content } = applySearchReplace(original, diff);
expect(success).toBe(false); expect(success).toBe(true);
expect(error).toMatch(/Search and replace blocks are identical/i); expect(content).toBe(original);
}); });
it("errors when SEARCH block matches multiple locations (ambiguous)", () => { it("errors when SEARCH block matches multiple locations (ambiguous)", () => {
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser"; import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser";
import { distance } from "fastest-levenshtein"; import { distance } from "fastest-levenshtein";
import { normalizeString } from "@/utils/text_normalization"; 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) // Minimum similarity threshold for fuzzy matching (0 to 1, where 1 is exact match)
const FUZZY_MATCH_THRESHOLD = 0.9; const FUZZY_MATCH_THRESHOLD = 0.9;
...@@ -201,12 +204,9 @@ export function applySearchReplace( ...@@ -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")) { if (searchLines.join("\n") === replaceLines.join("\n")) {
return { logger.warn("Search and replace blocks are identical");
success: false,
error: "Search and replace blocks are identical",
};
} }
let matchIndex = -1; let matchIndex = -1;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论