fix: limit \r\n escape preprocessing to anchor/find params only

normalizeContentLineEndings 在 8e637db7 引入的 \r\n escape 预处理对
所有调用路径生效,导致 workspaceWrite 写入 C 源码时破坏 \r\n 字符串常量。

修复:增加 unescapeEscapeSequences 参数(默认 false),仅在
workspaceReplace find、workspaceInsertAfter anchor、diagnosticDetails
三个 anchor 匹配路径启用 escape 预处理。

- workspaceWrite content: 不启用(保留原始 \r\n)
- workspaceReplace replace text: 不启用
- workspaceInsertAfter insertText: 不启用
- applyUpdateHunks output: 不启用(已是规范化内容)

Fixes #1027
This commit is contained in:
Codex Agent
2026-06-07 13:37:31 +08:00
parent 3dd7b2d1da
commit c66bced0fa
+6 -6
View File
@@ -419,7 +419,7 @@ async function workspaceReplace(args: any) {
assertExpectedSha(before, args.expectedSha, relativePath);
const find = requiredRawString(args.find, "find");
const replace = rawString(args.replace ?? args.replacement);
const normalizedFind = normalizeContentLineEndings(find, "\n", false);
const normalizedFind = normalizeContentLineEndings(find, "\n", false, true);
const normalizedReplace = normalizeContentLineEndings(replace, "\n", false);
const normalizedContent = before.normalizedContent;
const occurrences = countOccurrences(normalizedContent, normalizedFind);
@@ -443,7 +443,7 @@ async function workspaceInsertAfter(args: any) {
assertExpectedSha(before, args.expectedSha, relativePath);
const anchor = requiredRawString(args.anchor, "anchor");
const insertText = requiredRawString(args.line ?? args.insert ?? args.text, "line");
const normalizedAnchor = normalizeContentLineEndings(anchor, "\n", false);
const normalizedAnchor = normalizeContentLineEndings(anchor, "\n", false, true);
const normalizedInsert = normalizeContentLineEndings(insertText, "\n", false);
const lines = splitNormalizedLines(before.normalizedContent, before.hadFinalNewline);
const anchorLines = splitNormalizedLines(normalizedAnchor, false);
@@ -611,7 +611,7 @@ function splitNormalizedLines(content: string, hadFinalNewline: boolean) {
}
function diagnosticDetails(fileState: any, expected: string, relativePath: string) {
const expectedLines = splitNormalizedLines(normalizeContentLineEndings(expected, "\n", false), false).filter((line) => line.length > 0);
const expectedLines = splitNormalizedLines(normalizeContentLineEndings(expected, "\n", false, true), false).filter((line) => line.length > 0);
const normalizedLines = splitNormalizedLines(fileState.normalizedContent, fileState.hadFinalNewline);
const candidates = expectedLines.slice(0, 4).flatMap((line) => candidateLines(normalizedLines, line)).slice(0, 12);
return {
@@ -700,9 +700,9 @@ function lineEndingFromArgs(args: any, fallback: string) {
throw cliError("invalid_line_ending", "lineEnding must be preserve, lf, or crlf", { lineEnding: value });
}
function normalizeContentLineEndings(content: string, lineEnding: string, finalNewline: boolean) {
const unescaped = String(content).replace(/\\r\\n/gu, "\r\n").replace(/\\n/gu, "\n").replace(/\\r(?!\n)/gu, "\r");
const normalized = unescaped.replace(/\r\n?/gu, "\n");
function normalizeContentLineEndings(content: string, lineEnding: string, finalNewline: boolean, unescapeEscapeSequences = false) {
const base = unescapeEscapeSequences ? String(content).replace(/\\r\\n/gu, "\r\n").replace(/\\n/gu, "\n").replace(/\\r(?!\n)/gu, "\r") : String(content);
const normalized = base.replace(/\r\n?/gu, "\n");
const withFinal = finalNewline && !normalized.endsWith("\n") ? `${normalized}\n` : normalized;
return lineEnding === "\r\n" ? withFinal.replace(/\n/gu, "\r\n") : withFinal;
}