Merge pull request #270461 from microsoft/brchen/fix-debugger-focus-session

fix: debugger is unresponsive to repl evaluation after switching
This commit is contained in:
Bryan Chen
2025-10-14 10:20:11 -07:00
committed by GitHub
3 changed files with 27 additions and 13 deletions
@@ -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) {
@@ -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<Repl> {
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
@@ -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';
@@ -391,3 +391,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;
}