From 27f90f966100bdfa74d19533ea720cfba92cfa29 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 11 Jul 2025 14:27:55 -0700 Subject: [PATCH] tools: always replace explicit tab in apply_patch insertions (#223) Closes https://github.com/microsoft/vscode/issues/254783 --- .../extension/tools/node/applyPatch/parser.ts | 12 +++---- .../tools/test/node/applyPatch/parser.spec.ts | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/extensions/copilot/src/extension/tools/node/applyPatch/parser.ts b/extensions/copilot/src/extension/tools/node/applyPatch/parser.ts index 53478d120c1..b42a11f865e 100644 --- a/extensions/copilot/src/extension/tools/node/applyPatch/parser.ts +++ b/extensions/copilot/src/extension/tools/node/applyPatch/parser.ts @@ -408,19 +408,17 @@ export class Parser { for (const ch of nextSection.chunks) { ch.origIndex += match.line; + ch.insLines = ch.insLines.map(replace_explicit_tabs); + if (this.fixIndentationDuringMatch) { ch.insLines = ch.insLines.map(ins => isFalsyOrWhitespace(ins) ? ins : additionalIndentation + transformIndentation(ins, srcIndentStyle, targetIndentStyle)); } if (match.fuzz & Fuzz.NormalizedExplicitTab) { - action.chunks.push({ - delLines: ch.delLines.map(replace_explicit_tabs), - insLines: ch.insLines.map(replace_explicit_tabs), - origIndex: ch.origIndex, - }); - } else { - action.chunks.push(ch); + ch.delLines = ch.delLines.map(replace_explicit_tabs); } + + action.chunks.push(ch); } index = match.line + nextSection.nextChunkContext.length; this.index = nextSection.endPatchIndex; diff --git a/extensions/copilot/src/extension/tools/test/node/applyPatch/parser.spec.ts b/extensions/copilot/src/extension/tools/test/node/applyPatch/parser.spec.ts index aa310c1402c..16f2699cfb6 100644 --- a/extensions/copilot/src/extension/tools/test/node/applyPatch/parser.spec.ts +++ b/extensions/copilot/src/extension/tools/test/node/applyPatch/parser.spec.ts @@ -286,6 +286,38 @@ suite('applyPatch parser', () => { ]); }); + it('always normalizes explicit \\t tab chars in replacement', () => { + // 4.1 likes to explicitly put tabs as `\\t` in its patches + const input = `*** Begin Patch\n*** Update File: a.txt\n@@\n-hello\n+\\t\\tworld\n*** End Patch`; + + expect(text_to_patch(input, { + 'a.txt': new StringTextDocumentWithLanguageId('hello', 'text/plain') + })).toMatchInlineSnapshot(` + [ + { + "actions": { + "a.txt": { + "chunks": [ + { + "delLines": [ + "hello", + ], + "insLines": [ + " world", + ], + "origIndex": 0, + }, + ], + "movePath": undefined, + "type": "update", + }, + }, + }, + 0, + ] + `); + }); + suite('corpus', () => { const corpusPath = path.join(__dirname, 'corpus');