fix: resolve diff editor timeout issue when file is pre-opened

- Add immediate check for visible diff editor after command execution
- Handles cases where document open events may not fire for pre-opened files
- Fixes timeout error "Failed to open diff editor within 10 seconds"
- Add test to verify the fix works correctly

Fixes #6199
This commit is contained in:
Roo Code 2025-07-25 05:06:22 +00:00
parent b1406342a8
commit 3d9d0ae73d
2 changed files with 79 additions and 7 deletions

View file

@ -187,7 +187,10 @@ export class DiffViewProvider {
}
}
async saveChanges(diagnosticsEnabled: boolean = true, writeDelayMs: number = DEFAULT_WRITE_DELAY_MS): Promise<{
async saveChanges(
diagnosticsEnabled: boolean = true,
writeDelayMs: number = DEFAULT_WRITE_DELAY_MS,
): Promise<{
newProblemsMessage: string | undefined
userEdits: string | undefined
finalContent: string | undefined
@ -222,22 +225,22 @@ export class DiffViewProvider {
// and can address them accordingly. If problems don't change immediately after
// applying a fix, won't be notified, which is generally fine since the
// initial fix is usually correct and it may just take time for linters to catch up.
let newProblemsMessage = ""
if (diagnosticsEnabled) {
// Add configurable delay to allow linters time to process and clean up issues
// like unused imports (especially important for Go and other languages)
// Ensure delay is non-negative
const safeDelayMs = Math.max(0, writeDelayMs)
try {
await delay(safeDelayMs)
} catch (error) {
// Log error but continue - delay failure shouldn't break the save operation
console.warn(`Failed to apply write delay: ${error}`)
}
const postDiagnostics = vscode.languages.getDiagnostics()
// Get diagnostic settings from state
@ -551,8 +554,21 @@ export class DiffViewProvider {
)
})
.then(
() => {
// Command executed successfully, now wait for the editor to appear
async () => {
// Command executed successfully, now check if the diff editor is already visible
// This handles cases where the document was pre-opened and events might not fire
await new Promise((r) => setTimeout(r, 100)) // Small delay to ensure UI updates
// Check if the diff editor is already visible
const diffEditor = vscode.window.visibleTextEditors.find((e) =>
arePathsEqual(e.document.uri.fsPath, uri.fsPath),
)
if (diffEditor) {
cleanup()
resolve(diffEditor)
}
// If not found immediately, the event listeners will handle it
},
(err: any) => {
cleanup()

View file

@ -267,6 +267,62 @@ describe("DiffViewProvider", () => {
"Failed to execute diff command for /mock/cwd/test.md: Cannot open file",
)
})
it("should resolve immediately if diff editor is already visible after command", async () => {
// Setup
const mockEditor = {
document: {
uri: { fsPath: `${mockCwd}/test.md` },
getText: vi.fn().mockReturnValue(""),
lineCount: 0,
},
selection: {
active: { line: 0, character: 0 },
anchor: { line: 0, character: 0 },
},
edit: vi.fn().mockResolvedValue(true),
revealRange: vi.fn(),
}
// Mock showTextDocument
vi.mocked(vscode.window.showTextDocument).mockResolvedValue(mockEditor as any)
// Mock executeCommand to succeed
vi.mocked(vscode.commands.executeCommand).mockResolvedValue(undefined)
// Mock workspace.onDidOpenTextDocument - don't trigger callback
vi.mocked(vscode.workspace.onDidOpenTextDocument).mockReturnValue({ dispose: vi.fn() })
// Mock window.onDidChangeVisibleTextEditors - don't trigger callback
vi.mocked(vscode.window.onDidChangeVisibleTextEditors).mockReturnValue({ dispose: vi.fn() })
// Mock window.visibleTextEditors to return our editor immediately after command
vi.mocked(vscode.window).visibleTextEditors = [mockEditor as any]
// Set up for file
;(diffViewProvider as any).editType = "modify"
// Execute open - should complete successfully
await diffViewProvider.open("test.md")
// Verify that showTextDocument was called
expect(vscode.window.showTextDocument).toHaveBeenCalledWith(
expect.objectContaining({ fsPath: `${mockCwd}/test.md` }),
{ preview: false, viewColumn: vscode.ViewColumn.Active, preserveFocus: true },
)
// Verify that the diff command was executed
expect(vscode.commands.executeCommand).toHaveBeenCalledWith(
"vscode.diff",
expect.any(Object),
expect.any(Object),
`test.md: ${DIFF_VIEW_LABEL_CHANGES} (Editable)`,
{ preserveFocus: true },
)
// Verify that the activeDiffEditor was set
expect((diffViewProvider as any).activeDiffEditor).toBe(mockEditor)
})
})
describe("closeAllDiffViews method", () => {