Stop appending newlines to files when applying diffs

This commit is contained in:
Matt Rubens
2024-12-11 12:34:53 -05:00
parent ba8bed90d8
commit 413f10650f
2 changed files with 111 additions and 10 deletions

View File

@@ -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 = []
}
}
}

View File

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