Skip to content

Commit 6092a71

Browse files
authored
Merge pull request #73 from RooVetGit/stop_appending_newline_with_diff
Stop appending newlines to files when applying diffs
2 parents 26ff583 + 413f106 commit 6092a71

File tree

2 files changed

+111
-10
lines changed

2 files changed

+111
-10
lines changed

src/integrations/editor/DiffViewProvider.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,18 @@ export class DiffViewProvider {
124124
edit.delete(document.uri, new vscode.Range(this.streamedLines.length, 0, document.lineCount, 0))
125125
await vscode.workspace.applyEdit(edit)
126126
}
127-
// Add empty last line if original content had one
127+
// Preserve empty last line if original content had one
128128
const hasEmptyLastLine = this.originalContent?.endsWith("\n")
129-
if (hasEmptyLastLine) {
130-
const accumulatedLines = accumulatedContent.split("\n")
131-
if (accumulatedLines[accumulatedLines.length - 1] !== "") {
132-
accumulatedContent += "\n"
133-
}
129+
if (hasEmptyLastLine && !accumulatedContent.endsWith("\n")) {
130+
accumulatedContent += "\n"
134131
}
135-
// Clear all decorations at the end (before applying final edit)
136-
this.fadedOverlayController.clear()
137-
this.activeLineController.clear()
132+
// Apply the final content
133+
const finalEdit = new vscode.WorkspaceEdit()
134+
finalEdit.replace(document.uri, new vscode.Range(0, 0, document.lineCount, 0), accumulatedContent)
135+
await vscode.workspace.applyEdit(finalEdit)
136+
// Clear all decorations at the end (after applying final edit)
137+
this.fadedOverlayController.clear()
138+
this.activeLineController.clear()
138139
}
139140
}
140141

@@ -351,4 +352,4 @@ export class DiffViewProvider {
351352
this.streamedLines = []
352353
this.preDiagnostics = []
353354
}
354-
}
355+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { DiffViewProvider } from '../DiffViewProvider';
2+
import * as vscode from 'vscode';
3+
4+
// Mock vscode
5+
jest.mock('vscode', () => ({
6+
workspace: {
7+
applyEdit: jest.fn(),
8+
},
9+
window: {
10+
createTextEditorDecorationType: jest.fn(),
11+
},
12+
WorkspaceEdit: jest.fn().mockImplementation(() => ({
13+
replace: jest.fn(),
14+
delete: jest.fn(),
15+
})),
16+
Range: jest.fn(),
17+
Position: jest.fn(),
18+
Selection: jest.fn(),
19+
TextEditorRevealType: {
20+
InCenter: 2,
21+
},
22+
}));
23+
24+
// Mock DecorationController
25+
jest.mock('../DecorationController', () => ({
26+
DecorationController: jest.fn().mockImplementation(() => ({
27+
setActiveLine: jest.fn(),
28+
updateOverlayAfterLine: jest.fn(),
29+
clear: jest.fn(),
30+
})),
31+
}));
32+
33+
describe('DiffViewProvider', () => {
34+
let diffViewProvider: DiffViewProvider;
35+
const mockCwd = '/mock/cwd';
36+
let mockWorkspaceEdit: { replace: jest.Mock; delete: jest.Mock };
37+
38+
beforeEach(() => {
39+
jest.clearAllMocks();
40+
mockWorkspaceEdit = {
41+
replace: jest.fn(),
42+
delete: jest.fn(),
43+
};
44+
(vscode.WorkspaceEdit as jest.Mock).mockImplementation(() => mockWorkspaceEdit);
45+
46+
diffViewProvider = new DiffViewProvider(mockCwd);
47+
// Mock the necessary properties and methods
48+
(diffViewProvider as any).relPath = 'test.txt';
49+
(diffViewProvider as any).activeDiffEditor = {
50+
document: {
51+
uri: { fsPath: `${mockCwd}/test.txt` },
52+
getText: jest.fn(),
53+
lineCount: 10,
54+
},
55+
selection: {
56+
active: { line: 0, character: 0 },
57+
anchor: { line: 0, character: 0 },
58+
},
59+
edit: jest.fn().mockResolvedValue(true),
60+
revealRange: jest.fn(),
61+
};
62+
(diffViewProvider as any).activeLineController = { setActiveLine: jest.fn(), clear: jest.fn() };
63+
(diffViewProvider as any).fadedOverlayController = { updateOverlayAfterLine: jest.fn(), clear: jest.fn() };
64+
});
65+
66+
describe('update method', () => {
67+
it('should preserve empty last line when original content has one', async () => {
68+
(diffViewProvider as any).originalContent = 'Original content\n';
69+
await diffViewProvider.update('New content', true);
70+
71+
expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
72+
expect.anything(),
73+
expect.anything(),
74+
'New content\n'
75+
);
76+
});
77+
78+
it('should not add extra newline when accumulated content already ends with one', async () => {
79+
(diffViewProvider as any).originalContent = 'Original content\n';
80+
await diffViewProvider.update('New content\n', true);
81+
82+
expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
83+
expect.anything(),
84+
expect.anything(),
85+
'New content\n'
86+
);
87+
});
88+
89+
it('should not add newline when original content does not end with one', async () => {
90+
(diffViewProvider as any).originalContent = 'Original content';
91+
await diffViewProvider.update('New content', true);
92+
93+
expect(mockWorkspaceEdit.replace).toHaveBeenCalledWith(
94+
expect.anything(),
95+
expect.anything(),
96+
'New content'
97+
);
98+
});
99+
});
100+
});

0 commit comments

Comments
 (0)