Path: blob/main/src/vs/workbench/contrib/debug/browser/debugCommands.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { getWindowId } from '../../../../base/browser/dom.js';6import { List } from '../../../../base/browser/ui/list/listWidget.js';7import { mainWindow } from '../../../../base/browser/window.js';8import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';9import { DisposableStore } from '../../../../base/common/lifecycle.js';10import { deepClone } from '../../../../base/common/objects.js';11import { isWeb, isWindows } from '../../../../base/common/platform.js';12import { ICodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js';13import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';14import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js';15import { ITextResourcePropertiesService } from '../../../../editor/common/services/textResourceConfiguration.js';16import * as nls from '../../../../nls.js';17import { ILocalizedString } from '../../../../platform/action/common/action.js';18import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js';19import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';20import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';21import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';22import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';23import { InputFocusedContext } from '../../../../platform/contextkey/common/contextkeys.js';24import { IExtensionHostDebugService } from '../../../../platform/debug/common/extensionHostDebug.js';25import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';26import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';27import { IListService } from '../../../../platform/list/browser/listService.js';28import { INotificationService } from '../../../../platform/notification/common/notification.js';29import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js';30import { ActiveEditorContext, PanelFocusContext, ResourceContextKey } from '../../../common/contextkeys.js';31import { ViewContainerLocation } from '../../../common/views.js';32import { IEditorService } from '../../../services/editor/common/editorService.js';33import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js';34import { IViewsService } from '../../../services/views/common/viewsService.js';35import { ChatContextKeys } from '../../chat/common/chatContextKeys.js';36import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js';37import { TEXT_FILE_EDITOR_ID } from '../../files/common/files.js';38import { 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';39import { Breakpoint, DataBreakpoint, Expression, FunctionBreakpoint, Thread, Variable } from '../common/debugModel.js';40import { saveAllBeforeDebugStart } from '../common/debugUtils.js';41import { showLoadedScriptMenu } from '../common/loadedScriptsPicker.js';42import { openBreakpointSource } from './breakpointsView.js';43import { showDebugSessionMenu } from './debugSessionPicker.js';4445export const ADD_CONFIGURATION_ID = 'debug.addConfiguration';46export const COPY_ADDRESS_ID = 'editor.debug.action.copyAddress';47export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint';48export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint';49export const COPY_STACK_TRACE_ID = 'debug.copyStackTrace';50export const REVERSE_CONTINUE_ID = 'workbench.action.debug.reverseContinue';51export const STEP_BACK_ID = 'workbench.action.debug.stepBack';52export const RESTART_SESSION_ID = 'workbench.action.debug.restart';53export const TERMINATE_THREAD_ID = 'workbench.action.debug.terminateThread';54export const STEP_OVER_ID = 'workbench.action.debug.stepOver';55export const STEP_INTO_ID = 'workbench.action.debug.stepInto';56export const STEP_INTO_TARGET_ID = 'workbench.action.debug.stepIntoTarget';57export const STEP_OUT_ID = 'workbench.action.debug.stepOut';58export const PAUSE_ID = 'workbench.action.debug.pause';59export const DISCONNECT_ID = 'workbench.action.debug.disconnect';60export const DISCONNECT_AND_SUSPEND_ID = 'workbench.action.debug.disconnectAndSuspend';61export const STOP_ID = 'workbench.action.debug.stop';62export const RESTART_FRAME_ID = 'workbench.action.debug.restartFrame';63export const CONTINUE_ID = 'workbench.action.debug.continue';64export const FOCUS_REPL_ID = 'workbench.debug.action.focusRepl';65export const JUMP_TO_CURSOR_ID = 'debug.jumpToCursor';66export const FOCUS_SESSION_ID = 'workbench.action.debug.focusProcess';67export const SELECT_AND_START_ID = 'workbench.action.debug.selectandstart';68export const SELECT_DEBUG_CONSOLE_ID = 'workbench.action.debug.selectDebugConsole';69export const SELECT_DEBUG_SESSION_ID = 'workbench.action.debug.selectDebugSession';70export const DEBUG_CONFIGURE_COMMAND_ID = 'workbench.action.debug.configure';71export const DEBUG_START_COMMAND_ID = 'workbench.action.debug.start';72export const DEBUG_RUN_COMMAND_ID = 'workbench.action.debug.run';73export const EDIT_EXPRESSION_COMMAND_ID = 'debug.renameWatchExpression';74export const COPY_WATCH_EXPRESSION_COMMAND_ID = 'debug.copyWatchExpression';75export const SET_EXPRESSION_COMMAND_ID = 'debug.setWatchExpression';76export const REMOVE_EXPRESSION_COMMAND_ID = 'debug.removeWatchExpression';77export const NEXT_DEBUG_CONSOLE_ID = 'workbench.action.debug.nextConsole';78export const PREV_DEBUG_CONSOLE_ID = 'workbench.action.debug.prevConsole';79export const SHOW_LOADED_SCRIPTS_ID = 'workbench.action.debug.showLoadedScripts';80export const CALLSTACK_TOP_ID = 'workbench.action.debug.callStackTop';81export const CALLSTACK_BOTTOM_ID = 'workbench.action.debug.callStackBottom';82export const CALLSTACK_UP_ID = 'workbench.action.debug.callStackUp';83export const CALLSTACK_DOWN_ID = 'workbench.action.debug.callStackDown';84export const ADD_TO_WATCH_ID = 'debug.addToWatchExpressions';85export const COPY_EVALUATE_PATH_ID = 'debug.copyEvaluatePath';86export const COPY_VALUE_ID = 'workbench.debug.viewlet.action.copyValue';87export const BREAK_WHEN_VALUE_CHANGES_ID = 'debug.breakWhenValueChanges';88export const BREAK_WHEN_VALUE_IS_ACCESSED_ID = 'debug.breakWhenValueIsAccessed';89export const BREAK_WHEN_VALUE_IS_READ_ID = 'debug.breakWhenValueIsRead';90export const TOGGLE_EXCEPTION_BREAKPOINTS_ID = 'debug.toggleExceptionBreakpoints';91export const ATTACH_TO_CURRENT_CODE_RENDERER = 'debug.attachToCurrentCodeRenderer';9293export const DEBUG_COMMAND_CATEGORY: ILocalizedString = nls.localize2('debug', 'Debug');94export const RESTART_LABEL = nls.localize2('restartDebug', "Restart");95export const STEP_OVER_LABEL = nls.localize2('stepOverDebug', "Step Over");96export const STEP_INTO_LABEL = nls.localize2('stepIntoDebug', "Step Into");97export const STEP_INTO_TARGET_LABEL = nls.localize2('stepIntoTargetDebug', "Step Into Target");98export const STEP_OUT_LABEL = nls.localize2('stepOutDebug', "Step Out");99export const PAUSE_LABEL = nls.localize2('pauseDebug', "Pause");100export const DISCONNECT_LABEL = nls.localize2('disconnect', "Disconnect");101export const DISCONNECT_AND_SUSPEND_LABEL = nls.localize2('disconnectSuspend', "Disconnect and Suspend");102export const STOP_LABEL = nls.localize2('stop', "Stop");103export const CONTINUE_LABEL = nls.localize2('continueDebug', "Continue");104export const FOCUS_SESSION_LABEL = nls.localize2('focusSession', "Focus Session");105export const SELECT_AND_START_LABEL = nls.localize2('selectAndStartDebugging', "Select and Start Debugging");106export const DEBUG_CONFIGURE_LABEL = nls.localize('openLaunchJson', "Open '{0}'", 'launch.json');107export const DEBUG_START_LABEL = nls.localize2('startDebug', "Start Debugging");108export const DEBUG_RUN_LABEL = nls.localize2('startWithoutDebugging', "Start Without Debugging");109export const NEXT_DEBUG_CONSOLE_LABEL = nls.localize2('nextDebugConsole', "Focus Next Debug Console");110export const PREV_DEBUG_CONSOLE_LABEL = nls.localize2('prevDebugConsole', "Focus Previous Debug Console");111export const OPEN_LOADED_SCRIPTS_LABEL = nls.localize2('openLoadedScript', "Open Loaded Script...");112export const CALLSTACK_TOP_LABEL = nls.localize2('callStackTop', "Navigate to Top of Call Stack");113export const CALLSTACK_BOTTOM_LABEL = nls.localize2('callStackBottom', "Navigate to Bottom of Call Stack");114export const CALLSTACK_UP_LABEL = nls.localize2('callStackUp', "Navigate Up Call Stack");115export const CALLSTACK_DOWN_LABEL = nls.localize2('callStackDown', "Navigate Down Call Stack");116export const COPY_EVALUATE_PATH_LABEL = nls.localize2('copyAsExpression', "Copy as Expression");117export const COPY_VALUE_LABEL = nls.localize2('copyValue', "Copy Value");118export const COPY_ADDRESS_LABEL = nls.localize2('copyAddress', "Copy Address");119export const ADD_TO_WATCH_LABEL = nls.localize2('addToWatchExpressions', "Add to Watch");120121export const SELECT_DEBUG_CONSOLE_LABEL = nls.localize2('selectDebugConsole', "Select Debug Console");122export const SELECT_DEBUG_SESSION_LABEL = nls.localize2('selectDebugSession', "Select Debug Session");123124export const DEBUG_QUICK_ACCESS_PREFIX = 'debug ';125export const DEBUG_CONSOLE_QUICK_ACCESS_PREFIX = 'debug consoles ';126127let dataBreakpointInfoResponse: IDataBreakpointInfoResponse | undefined;128129export function setDataBreakpointInfoResponse(resp: IDataBreakpointInfoResponse | undefined) {130dataBreakpointInfoResponse = resp;131}132133interface CallStackContext {134sessionId: string;135threadId: string;136frameId: string;137}138139function isThreadContext(obj: any): obj is CallStackContext {140return obj && typeof obj.sessionId === 'string' && typeof obj.threadId === 'string';141}142143async function getThreadAndRun(accessor: ServicesAccessor, sessionAndThreadId: CallStackContext | unknown, run: (thread: IThread) => Promise<void>): Promise<void> {144const debugService = accessor.get(IDebugService);145let thread: IThread | undefined;146if (isThreadContext(sessionAndThreadId)) {147const session = debugService.getModel().getSession(sessionAndThreadId.sessionId);148if (session) {149thread = session.getAllThreads().find(t => t.getId() === sessionAndThreadId.threadId);150}151} else if (isSessionContext(sessionAndThreadId)) {152const session = debugService.getModel().getSession(sessionAndThreadId.sessionId);153if (session) {154const threads = session.getAllThreads();155thread = threads.length > 0 ? threads[0] : undefined;156}157}158159if (!thread) {160thread = debugService.getViewModel().focusedThread;161if (!thread) {162const focusedSession = debugService.getViewModel().focusedSession;163const threads = focusedSession ? focusedSession.getAllThreads() : undefined;164thread = threads && threads.length ? threads[0] : undefined;165}166}167168if (thread) {169await run(thread);170}171}172173function isStackFrameContext(obj: any): obj is CallStackContext {174return obj && typeof obj.sessionId === 'string' && typeof obj.threadId === 'string' && typeof obj.frameId === 'string';175}176177function getFrame(debugService: IDebugService, context: CallStackContext | unknown): IStackFrame | undefined {178if (isStackFrameContext(context)) {179const session = debugService.getModel().getSession(context.sessionId);180if (session) {181const thread = session.getAllThreads().find(t => t.getId() === context.threadId);182if (thread) {183return thread.getCallStack().find(sf => sf.getId() === context.frameId);184}185}186} else {187return debugService.getViewModel().focusedStackFrame;188}189190return undefined;191}192193function isSessionContext(obj: any): obj is CallStackContext {194return obj && typeof obj.sessionId === 'string';195}196197async function changeDebugConsoleFocus(accessor: ServicesAccessor, next: boolean) {198const debugService = accessor.get(IDebugService);199const viewsService = accessor.get(IViewsService);200const sessions = debugService.getModel().getSessions(true).filter(s => s.hasSeparateRepl());201let currSession = debugService.getViewModel().focusedSession;202203let nextIndex = 0;204if (sessions.length > 0 && currSession) {205while (currSession && !currSession.hasSeparateRepl()) {206currSession = currSession.parentSession;207}208209if (currSession) {210const currIndex = sessions.indexOf(currSession);211if (next) {212nextIndex = (currIndex === (sessions.length - 1) ? 0 : (currIndex + 1));213} else {214nextIndex = (currIndex === 0 ? (sessions.length - 1) : (currIndex - 1));215}216}217}218await debugService.focusStackFrame(undefined, undefined, sessions[nextIndex], { explicit: true });219220if (!viewsService.isViewVisible(REPL_VIEW_ID)) {221await viewsService.openView(REPL_VIEW_ID, true);222}223}224225async function navigateCallStack(debugService: IDebugService, down: boolean) {226const frame = debugService.getViewModel().focusedStackFrame;227if (frame) {228229let callStack = frame.thread.getCallStack();230let index = callStack.findIndex(elem => elem.frameId === frame.frameId);231let nextVisibleFrame;232if (down) {233if (index >= callStack.length - 1) {234if ((<Thread>frame.thread).reachedEndOfCallStack) {235goToTopOfCallStack(debugService);236return;237} else {238await debugService.getModel().fetchCallstack(frame.thread, 20);239callStack = frame.thread.getCallStack();240index = callStack.findIndex(elem => elem.frameId === frame.frameId);241}242}243nextVisibleFrame = findNextVisibleFrame(true, callStack, index);244} else {245if (index <= 0) {246goToBottomOfCallStack(debugService);247return;248}249nextVisibleFrame = findNextVisibleFrame(false, callStack, index);250}251252if (nextVisibleFrame) {253debugService.focusStackFrame(nextVisibleFrame, undefined, undefined, { preserveFocus: false });254}255}256}257258async function goToBottomOfCallStack(debugService: IDebugService) {259const thread = debugService.getViewModel().focusedThread;260if (thread) {261await debugService.getModel().fetchCallstack(thread);262const callStack = thread.getCallStack();263if (callStack.length > 0) {264const nextVisibleFrame = findNextVisibleFrame(false, callStack, 0); // must consider the next frame up first, which will be the last frame265if (nextVisibleFrame) {266debugService.focusStackFrame(nextVisibleFrame, undefined, undefined, { preserveFocus: false });267}268}269}270}271272function goToTopOfCallStack(debugService: IDebugService) {273const thread = debugService.getViewModel().focusedThread;274275if (thread) {276debugService.focusStackFrame(thread.getTopStackFrame(), undefined, undefined, { preserveFocus: false });277}278}279280/**281* Finds next frame that is not skipped by SkipFiles. Skips frame at index and starts searching at next.282* Must satisfy `0 <= startIndex <= callStack - 1`283* @param down specifies whether to search downwards if the current file is skipped.284* @param callStack the call stack to search285* @param startIndex the index to start the search at286*/287function findNextVisibleFrame(down: boolean, callStack: readonly IStackFrame[], startIndex: number) {288289if (startIndex >= callStack.length) {290startIndex = callStack.length - 1;291} else if (startIndex < 0) {292startIndex = 0;293}294295let index = startIndex;296297let currFrame;298do {299if (down) {300if (index === callStack.length - 1) {301index = 0;302} else {303index++;304}305} else {306if (index === 0) {307index = callStack.length - 1;308} else {309index--;310}311}312313currFrame = callStack[index];314if (!isFrameDeemphasized(currFrame)) {315return currFrame;316}317} while (index !== startIndex); // end loop when we've just checked the start index, since that should be the last one checked318319return undefined;320}321322// These commands are used in call stack context menu, call stack inline actions, command palette, debug toolbar, mac native touch bar323// When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id324// Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command palette) we do not pass any id and just take whatever is the focussed thread325// Same for stackFrame commands and session commands.326CommandsRegistry.registerCommand({327id: COPY_STACK_TRACE_ID,328handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {329const textResourcePropertiesService = accessor.get(ITextResourcePropertiesService);330const clipboardService = accessor.get(IClipboardService);331const debugService = accessor.get(IDebugService);332const frame = getFrame(debugService, context);333if (frame) {334const eol = textResourcePropertiesService.getEOL(frame.source.uri);335await clipboardService.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(eol));336}337}338});339340CommandsRegistry.registerCommand({341id: REVERSE_CONTINUE_ID,342handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {343await getThreadAndRun(accessor, context, thread => thread.reverseContinue());344}345});346347CommandsRegistry.registerCommand({348id: STEP_BACK_ID,349handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {350const contextKeyService = accessor.get(IContextKeyService);351if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {352await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepBack('instruction'));353} else {354await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepBack());355}356}357});358359CommandsRegistry.registerCommand({360id: TERMINATE_THREAD_ID,361handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {362await getThreadAndRun(accessor, context, thread => thread.terminate());363}364});365366CommandsRegistry.registerCommand({367id: JUMP_TO_CURSOR_ID,368handler: async (accessor: ServicesAccessor) => {369const debugService = accessor.get(IDebugService);370const stackFrame = debugService.getViewModel().focusedStackFrame;371const editorService = accessor.get(IEditorService);372const activeEditorControl = editorService.activeTextEditorControl;373const notificationService = accessor.get(INotificationService);374const quickInputService = accessor.get(IQuickInputService);375376if (stackFrame && isCodeEditor(activeEditorControl) && activeEditorControl.hasModel()) {377const position = activeEditorControl.getPosition();378const resource = activeEditorControl.getModel().uri;379const source = stackFrame.thread.session.getSourceForUri(resource);380if (source) {381const response = await stackFrame.thread.session.gotoTargets(source.raw, position.lineNumber, position.column);382const targets = response?.body.targets;383if (targets && targets.length) {384let id = targets[0].id;385if (targets.length > 1) {386const picks = targets.map(t => ({ label: t.label, _id: t.id }));387const pick = await quickInputService.pick(picks, { placeHolder: nls.localize('chooseLocation', "Choose the specific location") });388if (!pick) {389return;390}391392id = pick._id;393}394395return await stackFrame.thread.session.goto(stackFrame.thread.threadId, id).catch(e => notificationService.warn(e));396}397}398}399400return notificationService.warn(nls.localize('noExecutableCode', "No executable code is associated at the current cursor position."));401}402});403404405CommandsRegistry.registerCommand({406id: CALLSTACK_TOP_ID,407handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {408const debugService = accessor.get(IDebugService);409goToTopOfCallStack(debugService);410}411});412413CommandsRegistry.registerCommand({414id: CALLSTACK_BOTTOM_ID,415handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {416const debugService = accessor.get(IDebugService);417await goToBottomOfCallStack(debugService);418}419});420421CommandsRegistry.registerCommand({422id: CALLSTACK_UP_ID,423handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {424const debugService = accessor.get(IDebugService);425navigateCallStack(debugService, false);426}427});428429CommandsRegistry.registerCommand({430id: CALLSTACK_DOWN_ID,431handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {432const debugService = accessor.get(IDebugService);433navigateCallStack(debugService, true);434}435});436437MenuRegistry.appendMenuItem(MenuId.EditorContext, {438command: {439id: JUMP_TO_CURSOR_ID,440title: nls.localize('jumpToCursor', "Jump to Cursor"),441category: DEBUG_COMMAND_CATEGORY442},443when: ContextKeyExpr.and(CONTEXT_JUMP_TO_CURSOR_SUPPORTED, EditorContextKeys.editorTextFocus),444group: 'debug',445order: 3446});447448KeybindingsRegistry.registerCommandAndKeybindingRule({449id: NEXT_DEBUG_CONSOLE_ID,450weight: KeybindingWeight.WorkbenchContrib + 1,451when: CONTEXT_IN_DEBUG_REPL,452primary: KeyMod.CtrlCmd | KeyCode.PageDown,453mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketRight },454handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {455changeDebugConsoleFocus(accessor, true);456}457});458459KeybindingsRegistry.registerCommandAndKeybindingRule({460id: PREV_DEBUG_CONSOLE_ID,461weight: KeybindingWeight.WorkbenchContrib + 1,462when: CONTEXT_IN_DEBUG_REPL,463primary: KeyMod.CtrlCmd | KeyCode.PageUp,464mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketLeft },465handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {466changeDebugConsoleFocus(accessor, false);467}468});469470KeybindingsRegistry.registerCommandAndKeybindingRule({471id: RESTART_SESSION_ID,472weight: KeybindingWeight.WorkbenchContrib,473primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5,474when: CONTEXT_IN_DEBUG_MODE,475handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {476const debugService = accessor.get(IDebugService);477const configurationService = accessor.get(IConfigurationService);478let session: IDebugSession | undefined;479if (isSessionContext(context)) {480session = debugService.getModel().getSession(context.sessionId);481} else {482session = debugService.getViewModel().focusedSession;483}484485if (!session) {486const { launch, name } = debugService.getConfigurationManager().selectedConfiguration;487await debugService.startDebugging(launch, name, { noDebug: false, startedByUser: true });488} else {489const showSubSessions = configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;490// Stop should be sent to the root parent session491while (!showSubSessions && session.lifecycleManagedByParent && session.parentSession) {492session = session.parentSession;493}494session.removeReplExpressions();495await debugService.restartSession(session);496}497}498});499500KeybindingsRegistry.registerCommandAndKeybindingRule({501id: STEP_OVER_ID,502weight: KeybindingWeight.WorkbenchContrib,503primary: KeyCode.F10,504when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),505handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {506const contextKeyService = accessor.get(IContextKeyService);507if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {508await getThreadAndRun(accessor, context, (thread: IThread) => thread.next('instruction'));509} else {510await getThreadAndRun(accessor, context, (thread: IThread) => thread.next());511}512}513});514515// Windows browsers use F11 for full screen, thus use alt+F11 as the default shortcut516const STEP_INTO_KEYBINDING = (isWeb && isWindows) ? (KeyMod.Alt | KeyCode.F11) : KeyCode.F11;517518KeybindingsRegistry.registerCommandAndKeybindingRule({519id: STEP_INTO_ID,520weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging521primary: STEP_INTO_KEYBINDING,522// Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times523when: CONTEXT_DEBUG_STATE.notEqualsTo('inactive'),524handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {525const contextKeyService = accessor.get(IContextKeyService);526if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {527await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn('instruction'));528} else {529await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn());530}531}532});533534KeybindingsRegistry.registerCommandAndKeybindingRule({535id: STEP_OUT_ID,536weight: KeybindingWeight.WorkbenchContrib,537primary: KeyMod.Shift | KeyCode.F11,538when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),539handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {540const contextKeyService = accessor.get(IContextKeyService);541if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {542await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut('instruction'));543} else {544await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut());545}546}547});548549KeybindingsRegistry.registerCommandAndKeybindingRule({550id: PAUSE_ID,551weight: KeybindingWeight.WorkbenchContrib + 2, // take priority over focus next part while we are debugging552primary: KeyCode.F6,553when: CONTEXT_DEBUG_STATE.isEqualTo('running'),554handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {555await getThreadAndRun(accessor, context, thread => thread.pause());556}557});558559560KeybindingsRegistry.registerCommandAndKeybindingRule({561id: STEP_INTO_TARGET_ID,562primary: STEP_INTO_KEYBINDING | KeyMod.CtrlCmd,563when: ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')),564weight: KeybindingWeight.WorkbenchContrib,565handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {566const quickInputService = accessor.get(IQuickInputService);567const debugService = accessor.get(IDebugService);568const session = debugService.getViewModel().focusedSession;569const frame = debugService.getViewModel().focusedStackFrame;570if (!frame || !session) {571return;572}573574const editor = await accessor.get(IEditorService).openEditor({575resource: frame.source.uri,576options: { revealIfOpened: true }577});578579let codeEditor: ICodeEditor | undefined;580if (editor) {581const ctrl = editor?.getControl();582if (isCodeEditor(ctrl)) {583codeEditor = ctrl;584}585}586587interface ITargetItem extends IQuickPickItem {588target: DebugProtocol.StepInTarget;589}590591const disposables = new DisposableStore();592const qp = disposables.add(quickInputService.createQuickPick<ITargetItem>());593qp.busy = true;594qp.show();595596disposables.add(qp.onDidChangeActive(([item]) => {597if (codeEditor && item && item.target.line !== undefined) {598codeEditor.revealLineInCenterIfOutsideViewport(item.target.line);599codeEditor.setSelection({600startLineNumber: item.target.line,601startColumn: item.target.column || 1,602endLineNumber: item.target.endLine || item.target.line,603endColumn: item.target.endColumn || item.target.column || 1,604});605}606}));607608disposables.add(qp.onDidAccept(() => {609if (qp.activeItems.length) {610session.stepIn(frame.thread.threadId, qp.activeItems[0].target.id);611}612}));613614disposables.add(qp.onDidHide(() => disposables.dispose()));615616session.stepInTargets(frame.frameId).then(targets => {617qp.busy = false;618if (targets?.length) {619qp.items = targets?.map(target => ({ target, label: target.label }));620} else {621qp.placeholder = nls.localize('editor.debug.action.stepIntoTargets.none', "No step targets available");622}623});624}625});626627async function stopHandler(accessor: ServicesAccessor, _: string, context: CallStackContext | unknown, disconnect: boolean, suspend?: boolean): Promise<void> {628const debugService = accessor.get(IDebugService);629let session: IDebugSession | undefined;630if (isSessionContext(context)) {631session = debugService.getModel().getSession(context.sessionId);632} else {633session = debugService.getViewModel().focusedSession;634}635636const configurationService = accessor.get(IConfigurationService);637const showSubSessions = configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;638// Stop should be sent to the root parent session639while (!showSubSessions && session && session.lifecycleManagedByParent && session.parentSession) {640session = session.parentSession;641}642643await debugService.stopSession(session, disconnect, suspend);644}645646KeybindingsRegistry.registerCommandAndKeybindingRule({647id: DISCONNECT_ID,648weight: KeybindingWeight.WorkbenchContrib,649primary: KeyMod.Shift | KeyCode.F5,650when: ContextKeyExpr.and(CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_IN_DEBUG_MODE),651handler: (accessor, _, context) => stopHandler(accessor, _, context, true)652});653654CommandsRegistry.registerCommand({655id: DISCONNECT_AND_SUSPEND_ID,656handler: (accessor, _, context) => stopHandler(accessor, _, context, true, true)657});658659KeybindingsRegistry.registerCommandAndKeybindingRule({660id: STOP_ID,661weight: KeybindingWeight.WorkbenchContrib,662primary: KeyMod.Shift | KeyCode.F5,663when: ContextKeyExpr.and(CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated(), CONTEXT_IN_DEBUG_MODE),664handler: (accessor, _, context) => stopHandler(accessor, _, context, false)665});666667CommandsRegistry.registerCommand({668id: RESTART_FRAME_ID,669handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {670const debugService = accessor.get(IDebugService);671const notificationService = accessor.get(INotificationService);672const frame = getFrame(debugService, context);673if (frame) {674try {675await frame.restart();676} catch (e) {677notificationService.error(e);678}679}680}681});682683KeybindingsRegistry.registerCommandAndKeybindingRule({684id: CONTINUE_ID,685weight: KeybindingWeight.WorkbenchContrib + 10, // Use a stronger weight to get priority over start debugging F5 shortcut686primary: KeyCode.F5,687when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),688handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {689await getThreadAndRun(accessor, context, thread => thread.continue());690}691});692693CommandsRegistry.registerCommand({694id: SHOW_LOADED_SCRIPTS_ID,695handler: async (accessor) => {696await showLoadedScriptMenu(accessor);697}698});699700CommandsRegistry.registerCommand({701id: 'debug.startFromConfig',702handler: async (accessor, config: IConfig) => {703const debugService = accessor.get(IDebugService);704await debugService.startDebugging(undefined, config);705}706});707708CommandsRegistry.registerCommand({709id: FOCUS_SESSION_ID,710handler: async (accessor: ServicesAccessor, session: IDebugSession) => {711const debugService = accessor.get(IDebugService);712const editorService = accessor.get(IEditorService);713const stoppedChildSession = debugService.getModel().getSessions().find(s => s.parentSession === session && s.state === State.Stopped);714if (stoppedChildSession && session.state !== State.Stopped) {715session = stoppedChildSession;716}717await debugService.focusStackFrame(undefined, undefined, session, { explicit: true });718const stackFrame = debugService.getViewModel().focusedStackFrame;719if (stackFrame) {720await stackFrame.openInEditor(editorService, true);721}722}723});724725CommandsRegistry.registerCommand({726id: SELECT_AND_START_ID,727handler: async (accessor: ServicesAccessor, debugType: string | unknown, debugStartOptions?: { noDebug?: boolean }) => {728const quickInputService = accessor.get(IQuickInputService);729const debugService = accessor.get(IDebugService);730731if (debugType) {732const configManager = debugService.getConfigurationManager();733const dynamicProviders = await configManager.getDynamicProviders();734for (const provider of dynamicProviders) {735if (provider.type === debugType) {736const pick = await provider.pick();737if (pick) {738await configManager.selectConfiguration(pick.launch, pick.config.name, pick.config, { type: provider.type });739debugService.startDebugging(pick.launch, pick.config, { noDebug: debugStartOptions?.noDebug, startedByUser: true });740741return;742}743}744}745}746747quickInputService.quickAccess.show(DEBUG_QUICK_ACCESS_PREFIX);748}749});750751CommandsRegistry.registerCommand({752id: SELECT_DEBUG_CONSOLE_ID,753handler: async (accessor: ServicesAccessor) => {754const quickInputService = accessor.get(IQuickInputService);755quickInputService.quickAccess.show(DEBUG_CONSOLE_QUICK_ACCESS_PREFIX);756}757});758759CommandsRegistry.registerCommand({760id: SELECT_DEBUG_SESSION_ID,761handler: async (accessor: ServicesAccessor) => {762showDebugSessionMenu(accessor, SELECT_AND_START_ID);763}764});765766KeybindingsRegistry.registerCommandAndKeybindingRule({767id: DEBUG_START_COMMAND_ID,768weight: KeybindingWeight.WorkbenchContrib,769primary: KeyCode.F5,770when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.isEqualTo('inactive')),771handler: async (accessor: ServicesAccessor, debugStartOptions?: { config?: Partial<IConfig>; noDebug?: boolean }) => {772const debugService = accessor.get(IDebugService);773await saveAllBeforeDebugStart(accessor.get(IConfigurationService), accessor.get(IEditorService));774const { launch, name, getConfig } = debugService.getConfigurationManager().selectedConfiguration;775const config = await getConfig();776const configOrName = config ? Object.assign(deepClone(config), debugStartOptions?.config) : name;777await debugService.startDebugging(launch, configOrName, { noDebug: debugStartOptions?.noDebug, startedByUser: true }, false);778}779});780781KeybindingsRegistry.registerCommandAndKeybindingRule({782id: DEBUG_RUN_COMMAND_ID,783weight: KeybindingWeight.WorkbenchContrib,784primary: KeyMod.CtrlCmd | KeyCode.F5,785mac: { primary: KeyMod.WinCtrl | KeyCode.F5 },786when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))),787handler: async (accessor: ServicesAccessor) => {788const commandService = accessor.get(ICommandService);789await commandService.executeCommand(DEBUG_START_COMMAND_ID, { noDebug: true });790}791});792793KeybindingsRegistry.registerCommandAndKeybindingRule({794id: 'debug.toggleBreakpoint',795weight: KeybindingWeight.WorkbenchContrib + 5,796when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, InputFocusedContext.toNegated()),797primary: KeyCode.Space,798handler: (accessor) => {799const listService = accessor.get(IListService);800const debugService = accessor.get(IDebugService);801const list = listService.lastFocusedList;802if (list instanceof List) {803const focused = <IEnablement[]>list.getFocusedElements();804if (focused && focused.length) {805debugService.enableOrDisableBreakpoints(!focused[0].enabled, focused[0]);806}807}808}809});810811KeybindingsRegistry.registerCommandAndKeybindingRule({812id: 'debug.enableOrDisableBreakpoint',813weight: KeybindingWeight.WorkbenchContrib,814primary: undefined,815when: EditorContextKeys.editorTextFocus,816handler: (accessor) => {817const debugService = accessor.get(IDebugService);818const editorService = accessor.get(IEditorService);819const control = editorService.activeTextEditorControl;820if (isCodeEditor(control)) {821const model = control.getModel();822if (model) {823const position = control.getPosition();824if (position) {825const bps = debugService.getModel().getBreakpoints({ uri: model.uri, lineNumber: position.lineNumber });826if (bps.length) {827debugService.enableOrDisableBreakpoints(!bps[0].enabled, bps[0]);828}829}830}831}832}833});834835KeybindingsRegistry.registerCommandAndKeybindingRule({836id: EDIT_EXPRESSION_COMMAND_ID,837weight: KeybindingWeight.WorkbenchContrib + 5,838when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,839primary: KeyCode.F2,840mac: { primary: KeyCode.Enter },841handler: (accessor: ServicesAccessor, expression: Expression | unknown) => {842const debugService = accessor.get(IDebugService);843if (!(expression instanceof Expression)) {844const listService = accessor.get(IListService);845const focused = listService.lastFocusedList;846if (focused) {847const elements = focused.getFocus();848if (Array.isArray(elements) && elements[0] instanceof Expression) {849expression = elements[0];850}851}852}853854if (expression instanceof Expression) {855debugService.getViewModel().setSelectedExpression(expression, false);856}857}858});859860CommandsRegistry.registerCommand({861id: SET_EXPRESSION_COMMAND_ID,862handler: async (accessor: ServicesAccessor, expression: Expression | unknown) => {863const debugService = accessor.get(IDebugService);864if (expression instanceof Expression || expression instanceof Variable) {865debugService.getViewModel().setSelectedExpression(expression, true);866}867}868});869870KeybindingsRegistry.registerCommandAndKeybindingRule({871id: 'debug.setVariable',872weight: KeybindingWeight.WorkbenchContrib + 5,873when: CONTEXT_VARIABLES_FOCUSED,874primary: KeyCode.F2,875mac: { primary: KeyCode.Enter },876handler: (accessor) => {877const listService = accessor.get(IListService);878const debugService = accessor.get(IDebugService);879const focused = listService.lastFocusedList;880881if (focused) {882const elements = focused.getFocus();883if (Array.isArray(elements) && elements[0] instanceof Variable) {884debugService.getViewModel().setSelectedExpression(elements[0], false);885}886}887}888});889890KeybindingsRegistry.registerCommandAndKeybindingRule({891id: REMOVE_EXPRESSION_COMMAND_ID,892weight: KeybindingWeight.WorkbenchContrib,893when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_EXPRESSION_SELECTED.toNegated()),894primary: KeyCode.Delete,895mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },896handler: (accessor: ServicesAccessor, expression: Expression | unknown) => {897const debugService = accessor.get(IDebugService);898899if (expression instanceof Expression) {900debugService.removeWatchExpressions(expression.getId());901return;902}903904const listService = accessor.get(IListService);905const focused = listService.lastFocusedList;906if (focused) {907let elements = focused.getFocus();908if (Array.isArray(elements) && elements[0] instanceof Expression) {909const selection = focused.getSelection();910if (selection && selection.indexOf(elements[0]) >= 0) {911elements = selection;912}913elements.forEach((e: Expression) => debugService.removeWatchExpressions(e.getId()));914}915}916}917});918919CommandsRegistry.registerCommand({920id: BREAK_WHEN_VALUE_CHANGES_ID,921handler: async (accessor: ServicesAccessor) => {922const debugService = accessor.get(IDebugService);923if (dataBreakpointInfoResponse) {924await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'write' });925}926}927});928929CommandsRegistry.registerCommand({930id: BREAK_WHEN_VALUE_IS_ACCESSED_ID,931handler: async (accessor: ServicesAccessor) => {932const debugService = accessor.get(IDebugService);933if (dataBreakpointInfoResponse) {934await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'readWrite' });935}936}937});938939CommandsRegistry.registerCommand({940id: BREAK_WHEN_VALUE_IS_READ_ID,941handler: async (accessor: ServicesAccessor) => {942const debugService = accessor.get(IDebugService);943if (dataBreakpointInfoResponse) {944await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'read' });945}946}947});948949KeybindingsRegistry.registerCommandAndKeybindingRule({950id: 'debug.removeBreakpoint',951weight: KeybindingWeight.WorkbenchContrib,952when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_INPUT_FOCUSED.toNegated()),953primary: KeyCode.Delete,954mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },955handler: (accessor) => {956const listService = accessor.get(IListService);957const debugService = accessor.get(IDebugService);958const list = listService.lastFocusedList;959960if (list instanceof List) {961const focused = list.getFocusedElements();962const element = focused.length ? focused[0] : undefined;963if (element instanceof Breakpoint) {964debugService.removeBreakpoints(element.getId());965} else if (element instanceof FunctionBreakpoint) {966debugService.removeFunctionBreakpoints(element.getId());967} else if (element instanceof DataBreakpoint) {968debugService.removeDataBreakpoints(element.getId());969}970}971}972});973974KeybindingsRegistry.registerCommandAndKeybindingRule({975id: 'debug.installAdditionalDebuggers',976weight: KeybindingWeight.WorkbenchContrib,977when: undefined,978primary: undefined,979handler: async (accessor, query: string) => {980const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);981let searchFor = `@category:debuggers`;982if (typeof query === 'string') {983searchFor += ` ${query}`;984}985return extensionsWorkbenchService.openSearch(searchFor);986}987});988989registerAction2(class AddConfigurationAction extends Action2 {990constructor() {991super({992id: ADD_CONFIGURATION_ID,993title: nls.localize2('addConfiguration', "Add Configuration..."),994category: DEBUG_COMMAND_CATEGORY,995f1: true,996menu: {997id: MenuId.EditorContent,998when: ContextKeyExpr.and(999ContextKeyExpr.regex(ResourceContextKey.Path.key, /\.vscode[/\\]launch\.json$/),1000ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID))1001}1002});1003}10041005async run(accessor: ServicesAccessor, launchUri: string): Promise<void> {1006const manager = accessor.get(IDebugService).getConfigurationManager();10071008const launch = manager.getLaunches().find(l => l.uri.toString() === launchUri) || manager.selectedConfiguration.launch;1009if (launch) {1010const { editor, created } = await launch.openConfigFile({ preserveFocus: false });1011if (editor && !created) {1012const codeEditor = <ICodeEditor>editor.getControl();1013if (codeEditor) {1014await codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID)?.addLaunchConfiguration();1015}1016}1017}1018}1019});10201021const inlineBreakpointHandler = (accessor: ServicesAccessor) => {1022const debugService = accessor.get(IDebugService);1023const editorService = accessor.get(IEditorService);1024const control = editorService.activeTextEditorControl;1025if (isCodeEditor(control)) {1026const position = control.getPosition();1027if (position && control.hasModel() && debugService.canSetBreakpointsIn(control.getModel())) {1028const modelUri = control.getModel().uri;1029const breakpointAlreadySet = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri })1030.some(bp => (bp.sessionAgnosticData.column === position.column || (!bp.column && position.column <= 1)));10311032if (!breakpointAlreadySet) {1033debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column > 1 ? position.column : undefined }]);1034}1035}1036}1037};10381039KeybindingsRegistry.registerCommandAndKeybindingRule({1040weight: KeybindingWeight.WorkbenchContrib,1041primary: KeyMod.Shift | KeyCode.F9,1042when: EditorContextKeys.editorTextFocus,1043id: TOGGLE_INLINE_BREAKPOINT_ID,1044handler: inlineBreakpointHandler1045});10461047MenuRegistry.appendMenuItem(MenuId.EditorContext, {1048command: {1049id: TOGGLE_INLINE_BREAKPOINT_ID,1050title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint"),1051category: DEBUG_COMMAND_CATEGORY1052},1053when: ContextKeyExpr.and(1054CONTEXT_IN_DEBUG_MODE,1055PanelFocusContext.toNegated(),1056EditorContextKeys.editorTextFocus,1057ChatContextKeys.inChatSession.toNegated()),1058group: 'debug',1059order: 11060});10611062KeybindingsRegistry.registerCommandAndKeybindingRule({1063id: 'debug.openBreakpointToSide',1064weight: KeybindingWeight.WorkbenchContrib,1065when: CONTEXT_BREAKPOINTS_FOCUSED,1066primary: KeyMod.CtrlCmd | KeyCode.Enter,1067secondary: [KeyMod.Alt | KeyCode.Enter],1068handler: (accessor) => {1069const listService = accessor.get(IListService);1070const list = listService.lastFocusedList;1071if (list instanceof List) {1072const focus = list.getFocusedElements();1073if (focus.length && focus[0] instanceof Breakpoint) {1074return openBreakpointSource(focus[0], true, false, true, accessor.get(IDebugService), accessor.get(IEditorService));1075}1076}10771078return undefined;1079}1080});10811082registerAction2(class ToggleExceptionBreakpointsAction extends Action2 {1083constructor() {1084super({1085id: TOGGLE_EXCEPTION_BREAKPOINTS_ID,1086title: nls.localize2('toggleExceptionBreakpoints', "Toggle Exception Breakpoints"),1087category: DEBUG_COMMAND_CATEGORY,1088f1: true,1089precondition: CONTEXT_DEBUGGERS_AVAILABLE1090});1091}10921093async run(accessor: ServicesAccessor): Promise<void> {1094const debugService = accessor.get(IDebugService);1095const quickInputService = accessor.get(IQuickInputService);10961097// Get the focused session or the first available session1098const debugModel = debugService.getModel();1099const session = debugService.getViewModel().focusedSession || debugModel.getSessions()[0];1100const exceptionBreakpoints = session ? debugModel.getExceptionBreakpointsForSession(session.getId()) : debugModel.getExceptionBreakpoints();1101if (exceptionBreakpoints.length === 0) {1102return;1103}11041105// If only one exception breakpoint type, toggle it directly1106if (exceptionBreakpoints.length === 1) {1107const breakpoint = exceptionBreakpoints[0];1108await debugService.enableOrDisableBreakpoints(!breakpoint.enabled, breakpoint);1109return;1110}11111112// Multiple exception breakpoint types - show quickpick for selection1113interface IExceptionBreakpointItem extends IQuickPickItem {1114breakpoint: IExceptionBreakpoint;1115}11161117const disposables = new DisposableStore();1118const quickPick = disposables.add(quickInputService.createQuickPick<IExceptionBreakpointItem>());1119quickPick.placeholder = nls.localize('selectExceptionBreakpointsPlaceholder', "Pick enabled exception breakpoints");1120quickPick.canSelectMany = true;1121quickPick.matchOnDescription = true;1122quickPick.matchOnDetail = true;11231124// Create quickpick items from exception breakpoints1125quickPick.items = exceptionBreakpoints.map(bp => ({1126label: bp.label,1127description: bp.description,1128picked: bp.enabled,1129breakpoint: bp1130}));11311132quickPick.selectedItems = quickPick.items.filter(item => item.picked);11331134disposables.add(quickPick.onDidAccept(() => {1135const selectedItems = quickPick.selectedItems;1136const toEnable: IExceptionBreakpoint[] = [];1137const toDisable: IExceptionBreakpoint[] = [];11381139// Determine which breakpoints need to be toggled1140for (const bp of exceptionBreakpoints) {1141const isSelected = selectedItems.some(item => item.breakpoint === bp);1142if (isSelected && !bp.enabled) {1143toEnable.push(bp);1144} else if (!isSelected && bp.enabled) {1145toDisable.push(bp);1146}1147}11481149// Toggle the breakpoints1150const promises: Promise<void>[] = [];1151for (const bp of toEnable) {1152promises.push(debugService.enableOrDisableBreakpoints(true, bp));1153}1154for (const bp of toDisable) {1155promises.push(debugService.enableOrDisableBreakpoints(false, bp));1156}11571158Promise.all(promises).then(() => disposables.dispose());1159}));11601161disposables.add(quickPick.onDidHide(() => disposables.dispose()));1162quickPick.show();1163}1164});11651166// When there are no debug extensions, open the debug viewlet when F5 is pressed so the user can read the limitations1167KeybindingsRegistry.registerCommandAndKeybindingRule({1168id: 'debug.openView',1169weight: KeybindingWeight.WorkbenchContrib,1170when: CONTEXT_DEBUGGERS_AVAILABLE.toNegated(),1171primary: KeyCode.F5,1172secondary: [KeyMod.CtrlCmd | KeyCode.F5],1173handler: async (accessor) => {1174const paneCompositeService = accessor.get(IPaneCompositePartService);1175await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true);1176}1177});11781179registerAction2(class extends Action2 {1180constructor() {1181super({1182id: ATTACH_TO_CURRENT_CODE_RENDERER,1183title: nls.localize2('attachToCurrentCodeRenderer', "Attach to Current Code Renderer"),1184});1185}11861187override async run(accessor: ServicesAccessor): Promise<any> {1188const env = accessor.get(IEnvironmentService);1189if (!env.isExtensionDevelopment && !env.extensionTestsLocationURI) {1190throw new Error('Refusing to attach to renderer outside of development context');1191}11921193const windowId = getWindowId(mainWindow);1194const extDebugService = accessor.get(IExtensionHostDebugService);1195const result = await extDebugService.attachToCurrentWindowRenderer(windowId);11961197return result;1198}1199});120012011202