Skip to content

Commit f61d244

Browse files
committed
fix: resolve GSOD issues with multiple VSCode windows
- Made terminal shell execution handlers non-blocking using setImmediate - Added proper error handling and recovery for terminal operations - Increased diff editor timeout from 10s to 15s with retry mechanism - Enhanced terminal isolation to prevent cross-task interference - Added comprehensive tests for concurrent terminal operations Fixes #8949
1 parent 4a096e1 commit f61d244

File tree

3 files changed

+637
-128
lines changed

3 files changed

+637
-128
lines changed

src/integrations/editor/DiffViewProvider.ts

Lines changed: 114 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -485,10 +485,13 @@ export class DiffViewProvider {
485485
return new Promise<vscode.TextEditor>((resolve, reject) => {
486486
const fileName = path.basename(uri.fsPath)
487487
const fileExists = this.editType === "modify"
488-
const DIFF_EDITOR_TIMEOUT = 10_000 // ms
488+
const DIFF_EDITOR_TIMEOUT = 15_000 // Increased from 10s to 15s to accommodate slower systems
489+
const MAX_RETRIES = 2 // Allow up to 2 retries on timeout
489490

491+
let currentRetry = 0
490492
let timeoutId: NodeJS.Timeout | undefined
491-
const disposables: vscode.Disposable[] = []
493+
let disposables: vscode.Disposable[] = []
494+
let isResolved = false
492495

493496
const cleanup = () => {
494497
if (timeoutId) {
@@ -499,77 +502,122 @@ export class DiffViewProvider {
499502
disposables.length = 0
500503
}
501504

502-
// Set timeout for the entire operation
503-
timeoutId = setTimeout(() => {
504-
cleanup()
505-
reject(
506-
new Error(
507-
`Failed to open diff editor for ${uri.fsPath} within ${DIFF_EDITOR_TIMEOUT / 1000} seconds. The editor may be blocked or VS Code may be unresponsive.`,
508-
),
505+
const attemptOpenDiffEditor = () => {
506+
// Set timeout for the current attempt
507+
timeoutId = setTimeout(() => {
508+
if (!isResolved) {
509+
cleanup()
510+
511+
// Try retrying if we haven't exceeded MAX_RETRIES
512+
if (currentRetry < MAX_RETRIES) {
513+
currentRetry++
514+
console.warn(
515+
`[DiffViewProvider] Diff editor timeout for ${uri.fsPath}, retrying... (attempt ${currentRetry + 1}/${MAX_RETRIES + 1})`,
516+
)
517+
// Reset and retry
518+
disposables = []
519+
attemptOpenDiffEditor()
520+
} else {
521+
// All retries exhausted
522+
isResolved = true
523+
reject(
524+
new Error(
525+
`Failed to open diff editor for ${uri.fsPath} after ${MAX_RETRIES + 1} attempts (${DIFF_EDITOR_TIMEOUT / 1000}s timeout each). The editor may be blocked or VS Code may be unresponsive.`,
526+
),
527+
)
528+
}
529+
}
530+
}, DIFF_EDITOR_TIMEOUT)
531+
532+
// Listen for document open events - more efficient than scanning all tabs
533+
disposables.push(
534+
vscode.workspace.onDidOpenTextDocument(async (document) => {
535+
if (isResolved) return
536+
537+
// Only match file:// scheme documents to avoid git diffs
538+
if (document.uri.scheme === "file" && arePathsEqual(document.uri.fsPath, uri.fsPath)) {
539+
// Wait a tick for the editor to be available
540+
await new Promise((r) => setTimeout(r, 50)) // Slightly longer wait
541+
542+
// Find the editor for this document
543+
const editor = vscode.window.visibleTextEditors.find(
544+
(e) =>
545+
e.document.uri.scheme === "file" &&
546+
arePathsEqual(e.document.uri.fsPath, uri.fsPath),
547+
)
548+
549+
if (editor && !isResolved) {
550+
isResolved = true
551+
cleanup()
552+
resolve(editor)
553+
}
554+
}
555+
}),
509556
)
510-
}, DIFF_EDITOR_TIMEOUT)
511-
512-
// Listen for document open events - more efficient than scanning all tabs
513-
disposables.push(
514-
vscode.workspace.onDidOpenTextDocument(async (document) => {
515-
// Only match file:// scheme documents to avoid git diffs
516-
if (document.uri.scheme === "file" && arePathsEqual(document.uri.fsPath, uri.fsPath)) {
517-
// Wait a tick for the editor to be available
518-
await new Promise((r) => setTimeout(r, 0))
519-
520-
// Find the editor for this document
521-
const editor = vscode.window.visibleTextEditors.find(
522-
(e) => e.document.uri.scheme === "file" && arePathsEqual(e.document.uri.fsPath, uri.fsPath),
523-
)
524557

525-
if (editor) {
558+
// Also listen for visible editor changes as a fallback
559+
disposables.push(
560+
vscode.window.onDidChangeVisibleTextEditors((editors) => {
561+
if (isResolved) return
562+
563+
const editor = editors.find((e) => {
564+
const isFileScheme = e.document.uri.scheme === "file"
565+
const pathMatches = arePathsEqual(e.document.uri.fsPath, uri.fsPath)
566+
return isFileScheme && pathMatches
567+
})
568+
if (editor && !isResolved) {
569+
isResolved = true
526570
cleanup()
527571
resolve(editor)
528572
}
529-
}
530-
}),
531-
)
532-
533-
// Also listen for visible editor changes as a fallback
534-
disposables.push(
535-
vscode.window.onDidChangeVisibleTextEditors((editors) => {
536-
const editor = editors.find((e) => {
537-
const isFileScheme = e.document.uri.scheme === "file"
538-
const pathMatches = arePathsEqual(e.document.uri.fsPath, uri.fsPath)
539-
return isFileScheme && pathMatches
540-
})
541-
if (editor) {
542-
cleanup()
543-
resolve(editor)
544-
}
545-
}),
546-
)
573+
}),
574+
)
547575

548-
// Pre-open the file as a text document to ensure it doesn't open in preview mode
549-
// This fixes issues with files that have custom editor associations (like markdown preview)
550-
vscode.window
551-
.showTextDocument(uri, { preview: false, viewColumn: vscode.ViewColumn.Active, preserveFocus: true })
552-
.then(() => {
553-
// Execute the diff command after ensuring the file is open as text
554-
return vscode.commands.executeCommand(
555-
"vscode.diff",
556-
vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({
557-
query: Buffer.from(this.originalContent ?? "").toString("base64"),
558-
}),
559-
uri,
560-
`${fileName}: ${fileExists ? `${DIFF_VIEW_LABEL_CHANGES}` : "New File"} (Editable)`,
561-
{ preserveFocus: true },
562-
)
576+
// Pre-open the file as a text document to ensure it doesn't open in preview mode
577+
// This fixes issues with files that have custom editor associations (like markdown preview)
578+
// Use setImmediate to avoid blocking other operations
579+
setImmediate(() => {
580+
if (isResolved) return
581+
582+
vscode.window
583+
.showTextDocument(uri, {
584+
preview: false,
585+
viewColumn: vscode.ViewColumn.Active,
586+
preserveFocus: true,
587+
})
588+
.then(() => {
589+
if (isResolved) return
590+
591+
// Execute the diff command after ensuring the file is open as text
592+
return vscode.commands.executeCommand(
593+
"vscode.diff",
594+
vscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({
595+
query: Buffer.from(this.originalContent ?? "").toString("base64"),
596+
}),
597+
uri,
598+
`${fileName}: ${fileExists ? `${DIFF_VIEW_LABEL_CHANGES}` : "New File"} (Editable)`,
599+
{ preserveFocus: true },
600+
)
601+
})
602+
.then(
603+
() => {
604+
// Command executed successfully, now wait for the editor to appear
605+
},
606+
(err: any) => {
607+
if (!isResolved) {
608+
isResolved = true
609+
cleanup()
610+
reject(
611+
new Error(`Failed to execute diff command for ${uri.fsPath}: ${err.message}`),
612+
)
613+
}
614+
},
615+
)
563616
})
564-
.then(
565-
() => {
566-
// Command executed successfully, now wait for the editor to appear
567-
},
568-
(err: any) => {
569-
cleanup()
570-
reject(new Error(`Failed to execute diff command for ${uri.fsPath}: ${err.message}`))
571-
},
572-
)
617+
}
618+
619+
// Start the first attempt
620+
attemptOpenDiffEditor()
573621
})
574622
}
575623

0 commit comments

Comments
 (0)