in src/vs/workbench/contrib/debug/browser/debugCommands.ts [114:574]
export function registerCommands(): void {
// These commands are used in call stack context menu, call stack inline actions, command pallete, debug toolbar, mac native touch bar
// When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id
// Otherwise when it is executed "globaly"(using the touch bar, debug toolbar, command pallete) we do not pass any id and just take whatever is the focussed thread
// Same for stackFrame commands and session commands.
CommandsRegistry.registerCommand({
id: COPY_STACK_TRACE_ID,
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const textResourcePropertiesService = accessor.get(ITextResourcePropertiesService);
const clipboardService = accessor.get(IClipboardService);
let frame = getFrame(accessor.get(IDebugService), context);
if (frame) {
const eol = textResourcePropertiesService.getEOL(frame.source.uri);
await clipboardService.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(eol));
}
}
});
CommandsRegistry.registerCommand({
id: REVERSE_CONTINUE_ID,
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, thread => thread.reverseContinue());
}
});
CommandsRegistry.registerCommand({
id: STEP_BACK_ID,
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, thread => thread.stepBack());
}
});
CommandsRegistry.registerCommand({
id: TERMINATE_THREAD_ID,
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, thread => thread.terminate());
}
});
CommandsRegistry.registerCommand({
id: JUMP_TO_CURSOR_ID,
handler: async (accessor: ServicesAccessor) => {
const debugService = accessor.get(IDebugService);
const stackFrame = debugService.getViewModel().focusedStackFrame;
const editorService = accessor.get(IEditorService);
const activeEditorControl = editorService.activeTextEditorControl;
const notificationService = accessor.get(INotificationService);
const quickInputService = accessor.get(IQuickInputService);
if (stackFrame && isCodeEditor(activeEditorControl) && activeEditorControl.hasModel()) {
const position = activeEditorControl.getPosition();
const resource = activeEditorControl.getModel().uri;
const source = stackFrame.thread.session.getSourceForUri(resource);
if (source) {
const response = await stackFrame.thread.session.gotoTargets(source.raw, position.lineNumber, position.column);
const targets = response.body.targets;
if (targets.length) {
let id = targets[0].id;
if (targets.length > 1) {
const picks = targets.map(t => ({ label: t.label, _id: t.id }));
const pick = await quickInputService.pick(picks, { placeHolder: nls.localize('chooseLocation', "Choose the specific location") });
if (!pick) {
return;
}
id = pick._id;
}
return await stackFrame.thread.session.goto(stackFrame.thread.threadId, id).catch(e => notificationService.warn(e));
}
}
}
return notificationService.warn(nls.localize('noExecutableCode', "No executable code is associated at the current cursor position."));
}
});
MenuRegistry.appendMenuItem(MenuId.EditorContext, {
command: {
id: JUMP_TO_CURSOR_ID,
title: nls.localize('jumpToCursor', "Jump to Cursor"),
category: { value: nls.localize('debug', "Debug"), original: 'Debug' }
},
when: ContextKeyExpr.and(CONTEXT_JUMP_TO_CURSOR_SUPPORTED, EditorContextKeys.editorTextFocus),
group: 'debug',
order: 3
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: RESTART_SESSION_ID,
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5,
when: CONTEXT_IN_DEBUG_MODE,
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const debugService = accessor.get(IDebugService);
let session: IDebugSession | undefined;
if (isSessionContext(context)) {
session = debugService.getModel().getSession(context.sessionId);
} else {
session = debugService.getViewModel().focusedSession;
}
if (!session) {
const { launch, name } = debugService.getConfigurationManager().selectedConfiguration;
await debugService.startDebugging(launch, name, { noDebug: false });
} else {
session.removeReplExpressions();
await debugService.restartSession(session);
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: STEP_OVER_ID,
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.F10,
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, (thread: IThread) => thread.next());
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: STEP_INTO_ID,
weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging
primary: KeyCode.F11,
// Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times
when: CONTEXT_DEBUG_STATE.notEqualsTo('inactive'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn());
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: STEP_OUT_ID,
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyMod.Shift | KeyCode.F11,
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut());
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: PAUSE_ID,
weight: KeybindingWeight.WorkbenchContrib + 2, // take priority over focus next part while we are debugging
primary: KeyCode.F6,
when: CONTEXT_DEBUG_STATE.isEqualTo('running'),
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, thread => thread.pause());
}
});
CommandsRegistry.registerCommand({
id: DISCONNECT_ID,
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const debugService = accessor.get(IDebugService);
let session: IDebugSession | undefined;
if (isSessionContext(context)) {
session = debugService.getModel().getSession(context.sessionId);
} else {
session = debugService.getViewModel().focusedSession;
}
await debugService.stopSession(session);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: STOP_ID,
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyMod.Shift | KeyCode.F5,
when: CONTEXT_IN_DEBUG_MODE,
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const debugService = accessor.get(IDebugService);
let session: IDebugSession | undefined;
if (isSessionContext(context)) {
session = debugService.getModel().getSession(context.sessionId);
} else {
session = debugService.getViewModel().focusedSession;
}
const configurationService = accessor.get(IConfigurationService);
const showSubSessions = configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
// Stop should be sent to the root parent session
while (!showSubSessions && session && session.parentSession) {
session = session.parentSession;
}
await debugService.stopSession(session);
}
});
CommandsRegistry.registerCommand({
id: RESTART_FRAME_ID,
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
const debugService = accessor.get(IDebugService);
const notificationService = accessor.get(INotificationService);
let frame = getFrame(debugService, context);
if (frame) {
try {
await frame.restart();
} catch (e) {
notificationService.error(e);
}
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: CONTINUE_ID,
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyCode.F5,
when: CONTEXT_IN_DEBUG_MODE,
handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, thread => thread.continue());
}
});
CommandsRegistry.registerCommand({
id: FOCUS_REPL_ID,
handler: async (accessor) => {
const viewsService = accessor.get(IViewsService);
await viewsService.openView(REPL_VIEW_ID, true);
}
});
CommandsRegistry.registerCommand({
id: 'debug.startFromConfig',
handler: async (accessor, config: IConfig) => {
const debugService = accessor.get(IDebugService);
await debugService.startDebugging(undefined, config);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.toggleBreakpoint',
weight: KeybindingWeight.WorkbenchContrib + 5,
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, InputFocusedContext.toNegated()),
primary: KeyCode.Space,
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const list = listService.lastFocusedList;
if (list instanceof List) {
const focused = <IEnablement[]>list.getFocusedElements();
if (focused && focused.length) {
debugService.enableOrDisableBreakpoints(!focused[0].enabled, focused[0]);
}
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.enableOrDisableBreakpoint',
weight: KeybindingWeight.WorkbenchContrib,
primary: undefined,
when: EditorContextKeys.editorTextFocus,
handler: (accessor) => {
const debugService = accessor.get(IDebugService);
const editorService = accessor.get(IEditorService);
const control = editorService.activeTextEditorControl;
if (isCodeEditor(control)) {
const model = control.getModel();
if (model) {
const position = control.getPosition();
if (position) {
const bps = debugService.getModel().getBreakpoints({ uri: model.uri, lineNumber: position.lineNumber });
if (bps.length) {
debugService.enableOrDisableBreakpoints(!bps[0].enabled, bps[0]);
}
}
}
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.renameWatchExpression',
weight: KeybindingWeight.WorkbenchContrib + 5,
when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,
primary: KeyCode.F2,
mac: { primary: KeyCode.Enter },
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.lastFocusedList;
if (focused) {
const elements = focused.getFocus();
if (Array.isArray(elements) && elements[0] instanceof Expression) {
debugService.getViewModel().setSelectedExpression(elements[0]);
}
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.setVariable',
weight: KeybindingWeight.WorkbenchContrib + 5,
when: CONTEXT_VARIABLES_FOCUSED,
primary: KeyCode.F2,
mac: { primary: KeyCode.Enter },
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.lastFocusedList;
if (focused) {
const elements = focused.getFocus();
if (Array.isArray(elements) && elements[0] instanceof Variable) {
debugService.getViewModel().setSelectedExpression(elements[0]);
}
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.removeWatchExpression',
weight: KeybindingWeight.WorkbenchContrib,
when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_EXPRESSION_SELECTED.toNegated()),
primary: KeyCode.Delete,
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const focused = listService.lastFocusedList;
if (focused) {
let elements = focused.getFocus();
if (Array.isArray(elements) && elements[0] instanceof Expression) {
const selection = focused.getSelection();
if (selection && selection.indexOf(elements[0]) >= 0) {
elements = selection;
}
elements.forEach((e: Expression) => debugService.removeWatchExpressions(e.getId()));
}
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.removeBreakpoint',
weight: KeybindingWeight.WorkbenchContrib,
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_SELECTED.toNegated()),
primary: KeyCode.Delete,
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
handler: (accessor) => {
const listService = accessor.get(IListService);
const debugService = accessor.get(IDebugService);
const list = listService.lastFocusedList;
if (list instanceof List) {
const focused = list.getFocusedElements();
const element = focused.length ? focused[0] : undefined;
if (element instanceof Breakpoint) {
debugService.removeBreakpoints(element.getId());
} else if (element instanceof FunctionBreakpoint) {
debugService.removeFunctionBreakpoints(element.getId());
} else if (element instanceof DataBreakpoint) {
debugService.removeDataBreakpoints(element.getId());
}
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.installAdditionalDebuggers',
weight: KeybindingWeight.WorkbenchContrib,
when: undefined,
primary: undefined,
handler: async (accessor) => {
const viewletService = accessor.get(IViewletService);
const viewlet = (await viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer;
viewlet.search('tag:debuggers @sort:installs');
viewlet.focus();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: ADD_CONFIGURATION_ID,
weight: KeybindingWeight.WorkbenchContrib,
when: undefined,
primary: undefined,
handler: async (accessor, launchUri: string) => {
const manager = accessor.get(IDebugService).getConfigurationManager();
if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) {
accessor.get(INotificationService).info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration."));
return;
}
const launch = manager.getLaunches().find(l => l.uri.toString() === launchUri) || manager.selectedConfiguration.launch;
if (launch) {
const { editor, created } = await launch.openConfigFile(false);
if (editor && !created) {
const codeEditor = <ICodeEditor>editor.getControl();
if (codeEditor) {
await codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID).addLaunchConfiguration();
}
}
}
}
});
const inlineBreakpointHandler = (accessor: ServicesAccessor) => {
const debugService = accessor.get(IDebugService);
const editorService = accessor.get(IEditorService);
const control = editorService.activeTextEditorControl;
if (isCodeEditor(control)) {
const position = control.getPosition();
if (position && control.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(control.getModel())) {
const modelUri = control.getModel().uri;
const breakpointAlreadySet = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri })
.some(bp => (bp.sessionAgnosticData.column === position.column || (!bp.column && position.column <= 1)));
if (!breakpointAlreadySet) {
debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column > 1 ? position.column : undefined }], 'debugCommands.inlineBreakpointCommand');
}
}
}
};
KeybindingsRegistry.registerCommandAndKeybindingRule({
weight: KeybindingWeight.WorkbenchContrib,
primary: KeyMod.Shift | KeyCode.F9,
when: EditorContextKeys.editorTextFocus,
id: TOGGLE_INLINE_BREAKPOINT_ID,
handler: inlineBreakpointHandler
});
MenuRegistry.appendMenuItem(MenuId.EditorContext, {
command: {
id: TOGGLE_INLINE_BREAKPOINT_ID,
title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint"),
category: { value: nls.localize('debug', "Debug"), original: 'Debug' }
},
when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), EditorContextKeys.editorTextFocus),
group: 'debug',
order: 1
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'debug.openBreakpointToSide',
weight: KeybindingWeight.WorkbenchContrib,
when: CONTEXT_BREAKPOINTS_FOCUSED,
primary: KeyMod.CtrlCmd | KeyCode.Enter,
secondary: [KeyMod.Alt | KeyCode.Enter],
handler: (accessor) => {
const listService = accessor.get(IListService);
const list = listService.lastFocusedList;
if (list instanceof List) {
const focus = list.getFocusedElements();
if (focus.length && focus[0] instanceof Breakpoint) {
return openBreakpointSource(focus[0], true, false, accessor.get(IDebugService), accessor.get(IEditorService));
}
}
return undefined;
}
});
}