Skip to content

Commit 6181b0c

Browse files
committed
fix: preserve cursor position when applying diffs
- Check if file is already active editor before calling showTextDocument - Skip showTextDocument call when file is already open to preserve viewport - Add tests for the new behavior to ensure viewport preservation - Fixes #6853
1 parent 2a105a5 commit 6181b0c

File tree

2 files changed

+85
-6
lines changed

2 files changed

+85
-6
lines changed

src/integrations/editor/DiffViewProvider.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,13 @@ export class DiffViewProvider {
207207
await updatedDocument.save()
208208
}
209209

210-
await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false, preserveFocus: true })
210+
// Only show the document if it's not already the active editor
211+
// This preserves the viewport position when the file is already open
212+
const activeEditor = vscode.window.activeTextEditor
213+
const fileUri = vscode.Uri.file(absolutePath)
214+
if (!activeEditor || !arePathsEqual(activeEditor.document.uri.fsPath, absolutePath)) {
215+
await vscode.window.showTextDocument(fileUri, { preview: false, preserveFocus: true })
216+
}
211217
await this.closeAllDiffViews()
212218

213219
// Getting diagnostics before and after the file edit is a better approach than
@@ -661,11 +667,17 @@ export class DiffViewProvider {
661667
// Open the document to ensure diagnostics are loaded
662668
// When openFile is false (PREVENT_FOCUS_DISRUPTION enabled), we only open in memory
663669
if (openFile) {
664-
// Show the document in the editor
665-
await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), {
666-
preview: false,
667-
preserveFocus: true,
668-
})
670+
// Only show the document if it's not already the active editor
671+
// This preserves the viewport position when the file is already open
672+
const activeEditor = vscode.window.activeTextEditor
673+
const fileUri = vscode.Uri.file(absolutePath)
674+
if (!activeEditor || !arePathsEqual(activeEditor.document.uri.fsPath, absolutePath)) {
675+
// Show the document in the editor
676+
await vscode.window.showTextDocument(fileUri, {
677+
preview: false,
678+
preserveFocus: true,
679+
})
680+
}
669681
} else {
670682
// Just open the document in memory to trigger diagnostics without showing it
671683
const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(absolutePath))

src/integrations/editor/__tests__/DiffViewProvider.spec.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,8 @@ describe("DiffViewProvider", () => {
362362
// Mock vscode functions
363363
vi.mocked(vscode.window.showTextDocument).mockResolvedValue({} as any)
364364
vi.mocked(vscode.languages.getDiagnostics).mockReturnValue([])
365+
// Mock activeTextEditor as null by default
366+
vi.mocked(vscode.window).activeTextEditor = null as any
365367
})
366368

367369
it("should write content directly to file without opening diff view", async () => {
@@ -390,6 +392,26 @@ describe("DiffViewProvider", () => {
390392
expect(result.finalContent).toBe("new content")
391393
})
392394

395+
it("should not call showTextDocument when file is already active editor", async () => {
396+
// Mock active editor with the same file
397+
vi.mocked(vscode.window).activeTextEditor = {
398+
document: {
399+
uri: { fsPath: `${mockCwd}/test.ts` },
400+
},
401+
} as any
402+
403+
vi.mocked(vscode.window.showTextDocument).mockClear()
404+
405+
await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 1000)
406+
407+
// Verify file was written
408+
const fs = await import("fs/promises")
409+
expect(fs.writeFile).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content", "utf-8")
410+
411+
// Verify showTextDocument was NOT called since file is already active
412+
expect(vscode.window.showTextDocument).not.toHaveBeenCalled()
413+
})
414+
393415
it("should not open file when openWithoutFocus is false", async () => {
394416
await diffViewProvider.saveDirectly("test.ts", "new content", false, true, 1000)
395417

@@ -456,6 +478,8 @@ describe("DiffViewProvider", () => {
456478
// Mock vscode functions
457479
vi.mocked(vscode.window.showTextDocument).mockResolvedValue({} as any)
458480
vi.mocked(vscode.languages.getDiagnostics).mockReturnValue([])
481+
// Mock activeTextEditor as null by default
482+
vi.mocked(vscode.window).activeTextEditor = null as any
459483
})
460484

461485
it("should apply diagnostic delay when diagnosticsEnabled is true", async () => {
@@ -516,5 +540,48 @@ describe("DiffViewProvider", () => {
516540
expect(mockDelay).toHaveBeenCalledWith(5000)
517541
expect(vscode.languages.getDiagnostics).toHaveBeenCalled()
518542
})
543+
544+
it("should not call showTextDocument when file is already active editor", async () => {
545+
// Mock active editor with the same file
546+
vi.mocked(vscode.window).activeTextEditor = {
547+
document: {
548+
uri: { fsPath: `${mockCwd}/test.ts` },
549+
},
550+
} as any
551+
552+
// Mock closeAllDiffViews
553+
;(diffViewProvider as any).closeAllDiffViews = vi.fn().mockResolvedValue(undefined)
554+
555+
vi.mocked(vscode.window.showTextDocument).mockClear()
556+
557+
await diffViewProvider.saveChanges(true, 1000)
558+
559+
// Verify showTextDocument was NOT called since file is already active
560+
expect(vscode.window.showTextDocument).not.toHaveBeenCalled()
561+
// Verify closeAllDiffViews was still called
562+
expect((diffViewProvider as any).closeAllDiffViews).toHaveBeenCalled()
563+
})
564+
565+
it("should call showTextDocument when active editor is different file", async () => {
566+
// Mock active editor with a different file
567+
vi.mocked(vscode.window).activeTextEditor = {
568+
document: {
569+
uri: { fsPath: `${mockCwd}/different.ts` },
570+
},
571+
} as any
572+
573+
// Mock closeAllDiffViews
574+
;(diffViewProvider as any).closeAllDiffViews = vi.fn().mockResolvedValue(undefined)
575+
576+
vi.mocked(vscode.window.showTextDocument).mockClear()
577+
578+
await diffViewProvider.saveChanges(true, 1000)
579+
580+
// Verify showTextDocument WAS called since active editor is different file
581+
expect(vscode.window.showTextDocument).toHaveBeenCalledWith(
582+
expect.objectContaining({ fsPath: `${mockCwd}/test.ts` }),
583+
{ preview: false, preserveFocus: true },
584+
)
585+
})
519586
})
520587
})

0 commit comments

Comments
 (0)