Просмотр исходного кода

Stop appending newlines to files when applying diffs

Matt Rubens 1 год назад
Родитель
Сommit
413f10650f

+ 11 - 10
src/integrations/editor/DiffViewProvider.ts

@@ -124,17 +124,18 @@ export class DiffViewProvider {
 				edit.delete(document.uri, new vscode.Range(this.streamedLines.length, 0, document.lineCount, 0))
 				await vscode.workspace.applyEdit(edit)
 			}
-			// Add empty last line if original content had one
+			// Preserve empty last line if original content had one
 			const hasEmptyLastLine = this.originalContent?.endsWith("\n")
-			if (hasEmptyLastLine) {
-				const accumulatedLines = accumulatedContent.split("\n")
-				if (accumulatedLines[accumulatedLines.length - 1] !== "") {
-					accumulatedContent += "\n"
-				}
+			if (hasEmptyLastLine && !accumulatedContent.endsWith("\n")) {
+				accumulatedContent += "\n"
 			}
-			// Clear all decorations at the end (before applying final edit)
-			this.fadedOverlayController.clear()
-			this.activeLineController.clear()
+			// Apply the final content
+			const finalEdit = new vscode.WorkspaceEdit()
+			finalEdit.replace(document.uri, new vscode.Range(0, 0, document.lineCount, 0), accumulatedContent)
+						await vscode.workspace.applyEdit(finalEdit)
+						// Clear all decorations at the end (after applying final edit)
+						this.fadedOverlayController.clear()
+						this.activeLineController.clear()
 		}
 	}
 
@@ -351,4 +352,4 @@ export class DiffViewProvider {
 		this.streamedLines = []
 		this.preDiagnostics = []
 	}
-}
+}

+ 100 - 0
src/integrations/editor/__tests__/DiffViewProvider.test.ts

@@ -0,0 +1,100 @@
+import { DiffViewProvider } from '../DiffViewProvider';
+import * as vscode from 'vscode';
+
+// Mock vscode
+jest.mock('vscode', () => ({
+	workspace: {
+		applyEdit: jest.fn(),
+	},
+	window: {
+		createTextEditorDecorationType: jest.fn(),
+	},
+	WorkspaceEdit: jest.fn().mockImplementation(() => ({
+		replace: jest.fn(),
+		delete: jest.fn(),
+	})),
+	Range: jest.fn(),
+	Position: jest.fn(),
+	Selection: jest.fn(),
+	TextEditorRevealType: {
+		InCenter: 2,
+	},
+}));
+
+// Mock DecorationController
+jest.mock('../DecorationController', () => ({
+	DecorationController: jest.fn().mockImplementation(() => ({
+		setActiveLine: jest.fn(),
+		updateOverlayAfterLine: jest.fn(),
+		clear: jest.fn(),
+	})),
+}));
+
+describe('DiffViewProvider', () => {
+	let diffViewProvider: DiffViewProvider;
+	const mockCwd = '/mock/cwd';
+	let mockWorkspaceEdit: { replace: jest.Mock; delete: jest.Mock };
+
+	beforeEach(() => {
+		jest.clearAllMocks();
+		mockWorkspaceEdit = {
+			replace: jest.fn(),
+			delete: jest.fn(),
+		};
+		(vscode.WorkspaceEdit as jest.Mock).mockImplementation(() => mockWorkspaceEdit);
+
+		diffViewProvider = new DiffViewProvider(mockCwd);
+		// Mock the necessary properties and methods
+		(diffViewProvider as any).relPath = 'test.txt';
+		(diffViewProvider as any).activeDiffEditor = {
+			document: {
+				uri: { fsPath: `${mockCwd}/test.txt` },
+				getText: jest.fn(),
+				lineCount: 10,
+			},
+			selection: {
+				active: { line: 0, character: 0 },
+				anchor: { line: 0, character: 0 },
+			},
+			edit: jest.fn().mockResolvedValue(true),
+			revealRange: jest.fn(),
+		};
+		(diffViewProvider as any).activeLineController = { setActiveLine: jest.fn(), clear: jest.fn() };
+		(diffViewProvider as any).fadedOverlayController = { updateOverlayAfterLine: jest.fn(), clear: jest.fn() };
+	});
+
+	describe('update method', () => {
+		it('should preserve empty last line when original content has one', async () => {
+			(diffViewProvider as any).originalContent = 'Original content\n';
+			await diffViewProvider.update('New content', true);
+
+			expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
+				expect.anything(),
+				expect.anything(),
+				'New content\n'
+			);
+		});
+
+		it('should not add extra newline when accumulated content already ends with one', async () => {
+			(diffViewProvider as any).originalContent = 'Original content\n';
+			await diffViewProvider.update('New content\n', true);
+
+			expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
+				expect.anything(),
+				expect.anything(),
+				'New content\n'
+			);
+		});
+
+		it('should not add newline when original content does not end with one', async () => {
+			(diffViewProvider as any).originalContent = 'Original content';
+			await diffViewProvider.update('New content', true);
+
+			expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
+				expect.anything(),
+				expect.anything(),
+				'New content'
+			);
+		});
+	});
+});