From 3643025b3aaa69c9e37948eaa6c1c41585914a85 Mon Sep 17 00:00:00 2001 From: Bryan Chen Date: Wed, 8 Oct 2025 17:43:26 -0700 Subject: [PATCH] fix: debugger is unresponsive to repl evaluation after switching --- .../contrib/debug/browser/debugCommands.ts | 7 ++---- .../workbench/contrib/debug/browser/repl.ts | 9 ++----- .../contrib/debug/common/debugUtils.ts | 24 ++++++++++++++++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 7714b20cab7..9d644774d9f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -38,7 +38,7 @@ import { IExtensionsWorkbenchService } from '../../extensions/common/extensions. import { TEXT_FILE_EDITOR_ID } from '../../files/common/files.js'; import { CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_DEBUG_STATE, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DISASSEMBLY_VIEW_FOCUS, CONTEXT_EXPRESSION_SELECTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_REPL, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_VARIABLES_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, DataBreakpointSetType, EDITOR_CONTRIBUTION_ID, getStateLabel, IConfig, IDataBreakpointInfoResponse, IDebugConfiguration, IDebugEditorContribution, IDebugService, IDebugSession, IEnablement, IExceptionBreakpoint, isFrameDeemphasized, IStackFrame, IThread, REPL_VIEW_ID, State, VIEWLET_ID } from '../common/debug.js'; import { Breakpoint, DataBreakpoint, Expression, FunctionBreakpoint, Thread, Variable } from '../common/debugModel.js'; -import { saveAllBeforeDebugStart } from '../common/debugUtils.js'; +import { saveAllBeforeDebugStart, resolveChildSession } from '../common/debugUtils.js'; import { showLoadedScriptMenu } from '../common/loadedScriptsPicker.js'; import { openBreakpointSource } from './breakpointsView.js'; import { showDebugSessionMenu } from './debugSessionPicker.js'; @@ -711,10 +711,7 @@ CommandsRegistry.registerCommand({ handler: async (accessor: ServicesAccessor, session: IDebugSession) => { const debugService = accessor.get(IDebugService); const editorService = accessor.get(IEditorService); - const stoppedChildSession = debugService.getModel().getSessions().find(s => s.parentSession === session && s.state === State.Stopped); - if (stoppedChildSession && session.state !== State.Stopped) { - session = stoppedChildSession; - } + session = resolveChildSession(session, debugService.getModel().getSessions()); await debugService.focusStackFrame(undefined, undefined, session, { explicit: true }); const stackFrame = debugService.getViewModel().focusedStackFrame; if (stackFrame) { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 532f70db7dd..59b4f8c5db9 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -69,6 +69,7 @@ import { AccessibilityCommandId } from '../../accessibility/common/accessibility import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from '../../codeEditor/browser/simpleEditorOptions.js'; import { CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_REPL, CONTEXT_MULTI_SESSION_REPL, DEBUG_SCHEME, IDebugConfiguration, IDebugService, IDebugSession, IReplConfiguration, IReplElement, IReplOptions, REPL_VIEW_ID, State, getStateLabel } from '../common/debug.js'; import { Variable } from '../common/debugModel.js'; +import { resolveChildSession } from '../common/debugUtils.js'; import { ReplEvaluationResult, ReplGroup } from '../common/replModel.js'; import { FocusSessionActionViewItem } from './debugActionViewItems.js'; import { DEBUG_COMMAND_CATEGORY, FOCUS_REPL_ID } from './debugCommands.js'; @@ -1047,13 +1048,7 @@ registerAction2(class extends ViewAction { const debugService = accessor.get(IDebugService); // If session is already the focused session we need to manualy update the tree since view model will not send a focused change event if (session && session.state !== State.Inactive && session !== debugService.getViewModel().focusedSession) { - if (session.state !== State.Stopped) { - // Focus child session instead if it is stopped #112595 - const stopppedChildSession = debugService.getModel().getSessions().find(s => s.parentSession === session && s.state === State.Stopped); - if (stopppedChildSession) { - session = stopppedChildSession; - } - } + session = resolveChildSession(session, debugService.getModel().getSessions()); await debugService.focusStackFrame(undefined, undefined, session, { explicit: true }); } // Need to select the session in the view since the focussed session might not have changed diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 7eb37eaf10d..2a51f3fca64 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { equalsIgnoreCase } from '../../../../base/common/strings.js'; -import { IDebuggerContribution, IDebugSession, IConfigPresentation } from './debug.js'; +import { IDebuggerContribution, IDebugSession, IConfigPresentation, State } from './debug.js'; import { URI as uri } from '../../../../base/common/uri.js'; import { isAbsolute } from '../../../../base/common/path.js'; import { deepClone } from '../../../../base/common/objects.js'; @@ -392,3 +392,25 @@ export async function saveAllBeforeDebugStart(configurationService: IConfigurati export const sourcesEqual = (a: DebugProtocol.Source | undefined, b: DebugProtocol.Source | undefined): boolean => !a || !b ? a === b : a.name === b.name && a.path === b.path && a.sourceReference === b.sourceReference; + +/** + * Resolves the best child session to focus when a parent session is selected. + * Always prefer child sessions over parent wrapper sessions to ensure console responsiveness. + * Fixes issue #152407: Using debug console picker when not paused leaves console unresponsive. + */ +export function resolveChildSession(session: IDebugSession, allSessions: readonly IDebugSession[]): IDebugSession { + // Always focus child session instead of parent wrapper session #152407 + const childSessions = allSessions.filter(s => s.parentSession === session); + if (childSessions.length > 0) { + // Prefer stopped child session if available #112595 + const stoppedChildSession = childSessions.find(s => s.state === State.Stopped); + if (stoppedChildSession) { + return stoppedChildSession; + } else { + // If no stopped child, focus the first available child session + return childSessions[0]; + } + } + // Return the original session if it has no children + return session; +}