Skip to content

Commit 66fe9d8

Browse files
committed
fix: Preserve tab state (pinning/position) during diff
1 parent 7927236 commit 66fe9d8

File tree

1 file changed

+154
-35
lines changed

1 file changed

+154
-35
lines changed

src/integrations/editor/DiffViewProvider.ts

Lines changed: 154 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class DiffViewProvider {
1919
private documentWasOpen = false
2020
private originalViewColumn?: vscode.ViewColumn // Store the original view column
2121
private userFocusedEditorInfo?: { uri: vscode.Uri; viewColumn: vscode.ViewColumn } // Store user's focus before diff
22+
private originalTabState?: { uri: vscode.Uri; isPinned: boolean; viewColumn: vscode.ViewColumn; index: number } // Store original tab state if open
2223
private relPath?: string
2324
private newContent?: string
2425
private activeDiffEditor?: vscode.TextEditor
@@ -67,10 +68,23 @@ export class DiffViewProvider {
6768
(tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath),
6869
)
6970
for (const tab of tabs) {
71+
// Store state BEFORE closing
72+
if (tab.input instanceof vscode.TabInputText) {
73+
// Ensure it's a text tab to access URI safely
74+
this.originalTabState = {
75+
uri: tab.input.uri,
76+
isPinned: tab.isPinned,
77+
viewColumn: tab.group.viewColumn,
78+
index: tab.group.tabs.indexOf(tab), // Correct way to get the index
79+
}
80+
this.documentWasOpen = true // Set flag indicating we found an open tab state
81+
console.log("Original tab state saved:", this.originalTabState) // Optional: for debugging
82+
}
83+
7084
if (!tab.isDirty) {
7185
await vscode.window.tabGroups.close(tab)
7286
}
73-
this.documentWasOpen = true
87+
// Removed this.documentWasOpen = true from here as it's set when state is saved
7488
}
7589

7690
// Store the currently focused editor before opening the diff
@@ -174,9 +188,13 @@ export class DiffViewProvider {
174188
await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false })
175189
await this.closeAllDiffViews()
176190

177-
// If the original document was open, try to focus it.
178-
// VS Code should handle showing the updated content automatically since the file was saved.
179-
await this._focusOriginalDocument(absolutePath, this.originalViewColumn)
191+
// Restore original tab state if it existed, otherwise handle focus normally
192+
if (this.originalTabState) {
193+
await this._restoreOriginalTabState()
194+
} else {
195+
// Fallback to old focus logic only if no original tab state was saved (e.g., new file)
196+
await this._focusOriginalDocument(absolutePath, undefined) // Pass undefined as viewColumn is part of originalTabState now
197+
}
180198

181199
/*
182200
Getting diagnostics before and after the file edit is a better approach than
@@ -260,10 +278,13 @@ export class DiffViewProvider {
260278
// Close the diff view first
261279
await this.closeAllDiffViews()
262280

263-
// If the document was originally open, ensure it's focused.
264-
// The revert logic already applied the original content and saved.
265-
await this._focusOriginalDocument(absolutePath, this.originalViewColumn)
266-
281+
// Restore original tab state if it existed, otherwise handle focus normally
282+
if (this.originalTabState) {
283+
await this._restoreOriginalTabState()
284+
} else {
285+
// Fallback to old focus logic only if no original tab state was saved (e.g., new file)
286+
await this._focusOriginalDocument(absolutePath, undefined) // Pass undefined as viewColumn is part of originalTabState now
287+
}
267288
}
268289
// edit is done
269290
await this.reset()
@@ -310,16 +331,61 @@ export class DiffViewProvider {
310331
const disposable = vscode.window.onDidChangeActiveTextEditor((editor) => {
311332
if (editor && arePathsEqual(editor.document.uri.fsPath, uri.fsPath)) {
312333
disposable.dispose()
313-
// Diff editor is now active, resolve the promise
314-
resolve(editor)
334+
// Diff editor is now active
335+
// Pin the diff editor if the original tab was pinned
336+
const pinAndMoveIfNeeded = async () => {
337+
if (this.originalTabState?.isPinned) {
338+
try {
339+
await vscode.commands.executeCommand("workbench.action.pinEditor")
340+
console.log("Diff editor pinned.")
341+
// Add a small delay after pinning before moving
342+
await new Promise((resolve) => setTimeout(resolve, 50))
343+
344+
// Move the pinned diff editor to the original index
345+
const targetGroup = vscode.window.tabGroups.all.find(
346+
(group) => group.viewColumn === this.originalTabState?.viewColumn,
347+
)
348+
const index = this.originalTabState.index
349+
if (targetGroup && index >= 0) {
350+
// Check if index is still valid after potential async operations
351+
if (index < targetGroup.tabs.length) {
352+
await vscode.commands.executeCommand("moveActiveEditor", {
353+
to: "position",
354+
value: index + 1, // 1-based index
355+
})
356+
console.log(`Diff editor moved to index ${index}.`)
357+
} else {
358+
console.warn(
359+
`Diff editor move skipped: Index ${index} out of bounds after pinning/delay.`,
360+
)
361+
}
362+
} else {
363+
console.warn(
364+
`Could not move diff editor: Invalid index (${index}) or target group not found.`,
365+
)
366+
}
367+
} catch (err) {
368+
console.error("Failed to pin or move diff editor:", err)
369+
}
370+
}
371+
// Resolve the promise regardless of pin/move success
372+
resolve(editor)
373+
}
374+
pinAndMoveIfNeeded() // Execute async pin/move logic
315375
}
316376
})
317377
const options: vscode.TextDocumentShowOptions = {
318378
// preserveFocus: true, // Removed to prevent focus issues
319379
}
320-
if (this.originalViewColumn !== undefined) {
380+
// Use viewColumn from originalTabState if available
381+
if (this.originalTabState?.viewColumn !== undefined) {
382+
options.viewColumn = this.originalTabState.viewColumn
383+
} else if (this.originalViewColumn !== undefined) {
384+
// Fallback to originalViewColumn if originalTabState is not set (e.g., file wasn't open)
321385
options.viewColumn = this.originalViewColumn
322386
}
387+
388+
// Execute the diff command first
323389
vscode.commands.executeCommand(
324390
"vscode.diff",
325391
vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({
@@ -381,7 +447,7 @@ export class DiffViewProvider {
381447

382448
private async _focusOriginalDocument(
383449
absolutePath: string,
384-
viewColumn: vscode.ViewColumn | undefined, // Note: viewColumn here is the original view column of the *modified* file's tab, if it was open
450+
_viewColumn: vscode.ViewColumn | undefined, // Prefixed as unused now
385451
): Promise<void> {
386452
let focusRestoredOrHandled = false
387453

@@ -421,34 +487,86 @@ export class DiffViewProvider {
421487
}
422488
}
423489

424-
// Fallback logic for *existing* documents (only runs if focus restoration failed AND it wasn't a new file)
425-
if (!focusRestoredOrHandled && this.documentWasOpen && viewColumn) {
426-
console.log("Executing fallback logic for existing document as primary focus restore failed.")
427-
// Fallback 1: Try to focus the editor tab corresponding to the *modified* file
428-
const originalEditor = vscode.window.visibleTextEditors.find(
429-
(editor) => arePathsEqual(editor.document.uri.fsPath, absolutePath) && editor.viewColumn === viewColumn,
490+
// Fallback logic for existing documents is now handled by _restoreOriginalTabState
491+
// Keep the final log message for clarity
492+
else if (!focusRestoredOrHandled) {
493+
console.log(
494+
"No specific focus action taken by _focusOriginalDocument (focus restore might have succeeded, or it was a new file handled above).",
430495
)
431-
if (originalEditor) {
432-
const position = new vscode.Position(0, 0)
433-
originalEditor.revealRange(new vscode.Range(position, position), vscode.TextEditorRevealType.AtTop)
434-
console.log("Focus set to modified file's original editor (fallback 1):", absolutePath)
496+
}
497+
}
498+
499+
private async _restoreOriginalTabState(): Promise<void> {
500+
if (!this.originalTabState) {
501+
console.log("No original tab state to restore.")
502+
return
503+
}
504+
505+
console.log("Attempting to restore original tab state:", this.originalTabState)
506+
const { uri, viewColumn, isPinned, index } = this.originalTabState
507+
508+
try {
509+
// 1. Show the document in the correct view column
510+
// Prefix 'editor' as it's not directly used after assignment (focus happens implicitly)
511+
const _editor = await vscode.window.showTextDocument(uri, {
512+
viewColumn: viewColumn,
513+
preview: false, // Ensure it's not a preview tab
514+
preserveFocus: false, // Ensure this editor gets focus initially for commands
515+
})
516+
console.log("Document shown:", uri.fsPath)
517+
518+
// Small delay to allow VS Code to potentially update tab state after showing
519+
await new Promise((resolve) => setTimeout(resolve, 100)) // 100ms delay
520+
521+
// 2. Pin the editor if necessary
522+
if (isPinned) {
523+
await vscode.commands.executeCommand("workbench.action.pinEditor")
524+
console.log("Editor pinned.")
525+
// Another small delay might be needed after pinning before moving
526+
await new Promise((resolve) => setTimeout(resolve, 50))
527+
}
528+
529+
// 3. Move the editor to the original index
530+
// Ensure the target index is valid. VS Code's move command is 1-based.
531+
// We need to find the current group to check tab count.
532+
const targetGroup = vscode.window.tabGroups.all.find((group) => group.viewColumn === viewColumn)
533+
if (targetGroup && index >= 0 && index < targetGroup.tabs.length) {
534+
// The 'moveActiveEditor' command uses 1-based index for 'value'
535+
await vscode.commands.executeCommand("moveActiveEditor", { to: "position", value: index + 1 })
536+
console.log(`Editor moved to index ${index}.`)
435537
} else {
436-
// Fallback 2: Open the modified file if its editor wasn't found
538+
console.warn(`Could not move editor: Invalid index (${index}) or target group not found.`)
539+
}
540+
541+
// 4. Restore original user focus if it was different from the restored tab
542+
if (this.userFocusedEditorInfo && !arePathsEqual(this.userFocusedEditorInfo.uri.fsPath, uri.fsPath)) {
437543
try {
438-
await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), {
439-
preview: false,
440-
viewColumn: viewColumn,
441-
preserveFocus: false, // Force focus
544+
await vscode.window.showTextDocument(this.userFocusedEditorInfo.uri, {
545+
viewColumn: this.userFocusedEditorInfo.viewColumn,
546+
preserveFocus: false, // Force focus back
442547
})
443-
console.log("Opened modified file's editor (fallback 2):", absolutePath)
444-
} catch (error) {
445-
console.error("Failed to show modified document (fallback 2):", error)
548+
console.log(
549+
"Focus restored to originally focused editor (after tab state restore):",
550+
this.userFocusedEditorInfo.uri.fsPath,
551+
)
552+
} catch (focusError) {
553+
console.warn("Failed to restore original user focus after tab state restore:", focusError)
554+
// If restoring original focus fails, at least the target tab should be focused.
555+
await vscode.window.showTextDocument(uri, { viewColumn: viewColumn, preserveFocus: false })
446556
}
557+
} else {
558+
// Ensure the restored tab keeps focus if no other editor was focused or if it was the focused one
559+
await vscode.window.showTextDocument(uri, { viewColumn: viewColumn, preserveFocus: false })
560+
console.log("Focus kept on restored tab:", uri.fsPath)
561+
}
562+
} catch (error) {
563+
console.error("Error restoring original tab state:", error)
564+
// Fallback: Just try to show the document without state restoration
565+
try {
566+
await vscode.window.showTextDocument(uri, { viewColumn: viewColumn, preview: false })
567+
} catch (fallbackError) {
568+
console.error("Fallback showTextDocument also failed:", fallbackError)
447569
}
448-
} else if (!focusRestoredOrHandled) {
449-
console.log(
450-
"No specific focus action taken (focus restore might have succeeded, or it was a new file handled above, or fallbacks for existing file didn't apply).",
451-
)
452570
}
453571
}
454572

@@ -459,7 +577,8 @@ export class DiffViewProvider {
459577
this.originalContent = undefined
460578
this.createdDirs = []
461579
this.documentWasOpen = false
462-
this.originalViewColumn = undefined // Reset stored view column
580+
this.originalViewColumn = undefined // Reset stored view column - Keep for potential fallback? Replaced by originalTabState.viewColumn mostly.
581+
this.originalTabState = undefined // Reset stored tab state
463582
this.userFocusedEditorInfo = undefined // Reset stored user focus info
464583
this.activeDiffEditor = undefined
465584
this.fadedOverlayController = undefined

0 commit comments

Comments
 (0)