Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/debugCommands.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { getWindowId } from '../../../../base/browser/dom.js';
7
import { List } from '../../../../base/browser/ui/list/listWidget.js';
8
import { mainWindow } from '../../../../base/browser/window.js';
9
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
10
import { DisposableStore } from '../../../../base/common/lifecycle.js';
11
import { deepClone } from '../../../../base/common/objects.js';
12
import { isWeb, isWindows } from '../../../../base/common/platform.js';
13
import { ICodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js';
14
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
15
import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js';
16
import { ITextResourcePropertiesService } from '../../../../editor/common/services/textResourceConfiguration.js';
17
import * as nls from '../../../../nls.js';
18
import { ILocalizedString } from '../../../../platform/action/common/action.js';
19
import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js';
20
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
21
import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
22
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
23
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
24
import { InputFocusedContext } from '../../../../platform/contextkey/common/contextkeys.js';
25
import { IExtensionHostDebugService } from '../../../../platform/debug/common/extensionHostDebug.js';
26
import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
27
import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
28
import { IListService } from '../../../../platform/list/browser/listService.js';
29
import { INotificationService } from '../../../../platform/notification/common/notification.js';
30
import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js';
31
import { ActiveEditorContext, PanelFocusContext, ResourceContextKey } from '../../../common/contextkeys.js';
32
import { ViewContainerLocation } from '../../../common/views.js';
33
import { IEditorService } from '../../../services/editor/common/editorService.js';
34
import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js';
35
import { IViewsService } from '../../../services/views/common/viewsService.js';
36
import { ChatContextKeys } from '../../chat/common/chatContextKeys.js';
37
import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js';
38
import { TEXT_FILE_EDITOR_ID } from '../../files/common/files.js';
39
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';
40
import { Breakpoint, DataBreakpoint, Expression, FunctionBreakpoint, Thread, Variable } from '../common/debugModel.js';
41
import { saveAllBeforeDebugStart } from '../common/debugUtils.js';
42
import { showLoadedScriptMenu } from '../common/loadedScriptsPicker.js';
43
import { openBreakpointSource } from './breakpointsView.js';
44
import { showDebugSessionMenu } from './debugSessionPicker.js';
45
46
export const ADD_CONFIGURATION_ID = 'debug.addConfiguration';
47
export const COPY_ADDRESS_ID = 'editor.debug.action.copyAddress';
48
export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint';
49
export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint';
50
export const COPY_STACK_TRACE_ID = 'debug.copyStackTrace';
51
export const REVERSE_CONTINUE_ID = 'workbench.action.debug.reverseContinue';
52
export const STEP_BACK_ID = 'workbench.action.debug.stepBack';
53
export const RESTART_SESSION_ID = 'workbench.action.debug.restart';
54
export const TERMINATE_THREAD_ID = 'workbench.action.debug.terminateThread';
55
export const STEP_OVER_ID = 'workbench.action.debug.stepOver';
56
export const STEP_INTO_ID = 'workbench.action.debug.stepInto';
57
export const STEP_INTO_TARGET_ID = 'workbench.action.debug.stepIntoTarget';
58
export const STEP_OUT_ID = 'workbench.action.debug.stepOut';
59
export const PAUSE_ID = 'workbench.action.debug.pause';
60
export const DISCONNECT_ID = 'workbench.action.debug.disconnect';
61
export const DISCONNECT_AND_SUSPEND_ID = 'workbench.action.debug.disconnectAndSuspend';
62
export const STOP_ID = 'workbench.action.debug.stop';
63
export const RESTART_FRAME_ID = 'workbench.action.debug.restartFrame';
64
export const CONTINUE_ID = 'workbench.action.debug.continue';
65
export const FOCUS_REPL_ID = 'workbench.debug.action.focusRepl';
66
export const JUMP_TO_CURSOR_ID = 'debug.jumpToCursor';
67
export const FOCUS_SESSION_ID = 'workbench.action.debug.focusProcess';
68
export const SELECT_AND_START_ID = 'workbench.action.debug.selectandstart';
69
export const SELECT_DEBUG_CONSOLE_ID = 'workbench.action.debug.selectDebugConsole';
70
export const SELECT_DEBUG_SESSION_ID = 'workbench.action.debug.selectDebugSession';
71
export const DEBUG_CONFIGURE_COMMAND_ID = 'workbench.action.debug.configure';
72
export const DEBUG_START_COMMAND_ID = 'workbench.action.debug.start';
73
export const DEBUG_RUN_COMMAND_ID = 'workbench.action.debug.run';
74
export const EDIT_EXPRESSION_COMMAND_ID = 'debug.renameWatchExpression';
75
export const COPY_WATCH_EXPRESSION_COMMAND_ID = 'debug.copyWatchExpression';
76
export const SET_EXPRESSION_COMMAND_ID = 'debug.setWatchExpression';
77
export const REMOVE_EXPRESSION_COMMAND_ID = 'debug.removeWatchExpression';
78
export const NEXT_DEBUG_CONSOLE_ID = 'workbench.action.debug.nextConsole';
79
export const PREV_DEBUG_CONSOLE_ID = 'workbench.action.debug.prevConsole';
80
export const SHOW_LOADED_SCRIPTS_ID = 'workbench.action.debug.showLoadedScripts';
81
export const CALLSTACK_TOP_ID = 'workbench.action.debug.callStackTop';
82
export const CALLSTACK_BOTTOM_ID = 'workbench.action.debug.callStackBottom';
83
export const CALLSTACK_UP_ID = 'workbench.action.debug.callStackUp';
84
export const CALLSTACK_DOWN_ID = 'workbench.action.debug.callStackDown';
85
export const ADD_TO_WATCH_ID = 'debug.addToWatchExpressions';
86
export const COPY_EVALUATE_PATH_ID = 'debug.copyEvaluatePath';
87
export const COPY_VALUE_ID = 'workbench.debug.viewlet.action.copyValue';
88
export const BREAK_WHEN_VALUE_CHANGES_ID = 'debug.breakWhenValueChanges';
89
export const BREAK_WHEN_VALUE_IS_ACCESSED_ID = 'debug.breakWhenValueIsAccessed';
90
export const BREAK_WHEN_VALUE_IS_READ_ID = 'debug.breakWhenValueIsRead';
91
export const TOGGLE_EXCEPTION_BREAKPOINTS_ID = 'debug.toggleExceptionBreakpoints';
92
export const ATTACH_TO_CURRENT_CODE_RENDERER = 'debug.attachToCurrentCodeRenderer';
93
94
export const DEBUG_COMMAND_CATEGORY: ILocalizedString = nls.localize2('debug', 'Debug');
95
export const RESTART_LABEL = nls.localize2('restartDebug', "Restart");
96
export const STEP_OVER_LABEL = nls.localize2('stepOverDebug', "Step Over");
97
export const STEP_INTO_LABEL = nls.localize2('stepIntoDebug', "Step Into");
98
export const STEP_INTO_TARGET_LABEL = nls.localize2('stepIntoTargetDebug', "Step Into Target");
99
export const STEP_OUT_LABEL = nls.localize2('stepOutDebug', "Step Out");
100
export const PAUSE_LABEL = nls.localize2('pauseDebug', "Pause");
101
export const DISCONNECT_LABEL = nls.localize2('disconnect', "Disconnect");
102
export const DISCONNECT_AND_SUSPEND_LABEL = nls.localize2('disconnectSuspend', "Disconnect and Suspend");
103
export const STOP_LABEL = nls.localize2('stop', "Stop");
104
export const CONTINUE_LABEL = nls.localize2('continueDebug', "Continue");
105
export const FOCUS_SESSION_LABEL = nls.localize2('focusSession', "Focus Session");
106
export const SELECT_AND_START_LABEL = nls.localize2('selectAndStartDebugging', "Select and Start Debugging");
107
export const DEBUG_CONFIGURE_LABEL = nls.localize('openLaunchJson', "Open '{0}'", 'launch.json');
108
export const DEBUG_START_LABEL = nls.localize2('startDebug', "Start Debugging");
109
export const DEBUG_RUN_LABEL = nls.localize2('startWithoutDebugging', "Start Without Debugging");
110
export const NEXT_DEBUG_CONSOLE_LABEL = nls.localize2('nextDebugConsole', "Focus Next Debug Console");
111
export const PREV_DEBUG_CONSOLE_LABEL = nls.localize2('prevDebugConsole', "Focus Previous Debug Console");
112
export const OPEN_LOADED_SCRIPTS_LABEL = nls.localize2('openLoadedScript', "Open Loaded Script...");
113
export const CALLSTACK_TOP_LABEL = nls.localize2('callStackTop', "Navigate to Top of Call Stack");
114
export const CALLSTACK_BOTTOM_LABEL = nls.localize2('callStackBottom', "Navigate to Bottom of Call Stack");
115
export const CALLSTACK_UP_LABEL = nls.localize2('callStackUp', "Navigate Up Call Stack");
116
export const CALLSTACK_DOWN_LABEL = nls.localize2('callStackDown', "Navigate Down Call Stack");
117
export const COPY_EVALUATE_PATH_LABEL = nls.localize2('copyAsExpression', "Copy as Expression");
118
export const COPY_VALUE_LABEL = nls.localize2('copyValue', "Copy Value");
119
export const COPY_ADDRESS_LABEL = nls.localize2('copyAddress', "Copy Address");
120
export const ADD_TO_WATCH_LABEL = nls.localize2('addToWatchExpressions', "Add to Watch");
121
122
export const SELECT_DEBUG_CONSOLE_LABEL = nls.localize2('selectDebugConsole', "Select Debug Console");
123
export const SELECT_DEBUG_SESSION_LABEL = nls.localize2('selectDebugSession', "Select Debug Session");
124
125
export const DEBUG_QUICK_ACCESS_PREFIX = 'debug ';
126
export const DEBUG_CONSOLE_QUICK_ACCESS_PREFIX = 'debug consoles ';
127
128
let dataBreakpointInfoResponse: IDataBreakpointInfoResponse | undefined;
129
130
export function setDataBreakpointInfoResponse(resp: IDataBreakpointInfoResponse | undefined) {
131
dataBreakpointInfoResponse = resp;
132
}
133
134
interface CallStackContext {
135
sessionId: string;
136
threadId: string;
137
frameId: string;
138
}
139
140
function isThreadContext(obj: any): obj is CallStackContext {
141
return obj && typeof obj.sessionId === 'string' && typeof obj.threadId === 'string';
142
}
143
144
async function getThreadAndRun(accessor: ServicesAccessor, sessionAndThreadId: CallStackContext | unknown, run: (thread: IThread) => Promise<void>): Promise<void> {
145
const debugService = accessor.get(IDebugService);
146
let thread: IThread | undefined;
147
if (isThreadContext(sessionAndThreadId)) {
148
const session = debugService.getModel().getSession(sessionAndThreadId.sessionId);
149
if (session) {
150
thread = session.getAllThreads().find(t => t.getId() === sessionAndThreadId.threadId);
151
}
152
} else if (isSessionContext(sessionAndThreadId)) {
153
const session = debugService.getModel().getSession(sessionAndThreadId.sessionId);
154
if (session) {
155
const threads = session.getAllThreads();
156
thread = threads.length > 0 ? threads[0] : undefined;
157
}
158
}
159
160
if (!thread) {
161
thread = debugService.getViewModel().focusedThread;
162
if (!thread) {
163
const focusedSession = debugService.getViewModel().focusedSession;
164
const threads = focusedSession ? focusedSession.getAllThreads() : undefined;
165
thread = threads && threads.length ? threads[0] : undefined;
166
}
167
}
168
169
if (thread) {
170
await run(thread);
171
}
172
}
173
174
function isStackFrameContext(obj: any): obj is CallStackContext {
175
return obj && typeof obj.sessionId === 'string' && typeof obj.threadId === 'string' && typeof obj.frameId === 'string';
176
}
177
178
function getFrame(debugService: IDebugService, context: CallStackContext | unknown): IStackFrame | undefined {
179
if (isStackFrameContext(context)) {
180
const session = debugService.getModel().getSession(context.sessionId);
181
if (session) {
182
const thread = session.getAllThreads().find(t => t.getId() === context.threadId);
183
if (thread) {
184
return thread.getCallStack().find(sf => sf.getId() === context.frameId);
185
}
186
}
187
} else {
188
return debugService.getViewModel().focusedStackFrame;
189
}
190
191
return undefined;
192
}
193
194
function isSessionContext(obj: any): obj is CallStackContext {
195
return obj && typeof obj.sessionId === 'string';
196
}
197
198
async function changeDebugConsoleFocus(accessor: ServicesAccessor, next: boolean) {
199
const debugService = accessor.get(IDebugService);
200
const viewsService = accessor.get(IViewsService);
201
const sessions = debugService.getModel().getSessions(true).filter(s => s.hasSeparateRepl());
202
let currSession = debugService.getViewModel().focusedSession;
203
204
let nextIndex = 0;
205
if (sessions.length > 0 && currSession) {
206
while (currSession && !currSession.hasSeparateRepl()) {
207
currSession = currSession.parentSession;
208
}
209
210
if (currSession) {
211
const currIndex = sessions.indexOf(currSession);
212
if (next) {
213
nextIndex = (currIndex === (sessions.length - 1) ? 0 : (currIndex + 1));
214
} else {
215
nextIndex = (currIndex === 0 ? (sessions.length - 1) : (currIndex - 1));
216
}
217
}
218
}
219
await debugService.focusStackFrame(undefined, undefined, sessions[nextIndex], { explicit: true });
220
221
if (!viewsService.isViewVisible(REPL_VIEW_ID)) {
222
await viewsService.openView(REPL_VIEW_ID, true);
223
}
224
}
225
226
async function navigateCallStack(debugService: IDebugService, down: boolean) {
227
const frame = debugService.getViewModel().focusedStackFrame;
228
if (frame) {
229
230
let callStack = frame.thread.getCallStack();
231
let index = callStack.findIndex(elem => elem.frameId === frame.frameId);
232
let nextVisibleFrame;
233
if (down) {
234
if (index >= callStack.length - 1) {
235
if ((<Thread>frame.thread).reachedEndOfCallStack) {
236
goToTopOfCallStack(debugService);
237
return;
238
} else {
239
await debugService.getModel().fetchCallstack(frame.thread, 20);
240
callStack = frame.thread.getCallStack();
241
index = callStack.findIndex(elem => elem.frameId === frame.frameId);
242
}
243
}
244
nextVisibleFrame = findNextVisibleFrame(true, callStack, index);
245
} else {
246
if (index <= 0) {
247
goToBottomOfCallStack(debugService);
248
return;
249
}
250
nextVisibleFrame = findNextVisibleFrame(false, callStack, index);
251
}
252
253
if (nextVisibleFrame) {
254
debugService.focusStackFrame(nextVisibleFrame, undefined, undefined, { preserveFocus: false });
255
}
256
}
257
}
258
259
async function goToBottomOfCallStack(debugService: IDebugService) {
260
const thread = debugService.getViewModel().focusedThread;
261
if (thread) {
262
await debugService.getModel().fetchCallstack(thread);
263
const callStack = thread.getCallStack();
264
if (callStack.length > 0) {
265
const nextVisibleFrame = findNextVisibleFrame(false, callStack, 0); // must consider the next frame up first, which will be the last frame
266
if (nextVisibleFrame) {
267
debugService.focusStackFrame(nextVisibleFrame, undefined, undefined, { preserveFocus: false });
268
}
269
}
270
}
271
}
272
273
function goToTopOfCallStack(debugService: IDebugService) {
274
const thread = debugService.getViewModel().focusedThread;
275
276
if (thread) {
277
debugService.focusStackFrame(thread.getTopStackFrame(), undefined, undefined, { preserveFocus: false });
278
}
279
}
280
281
/**
282
* Finds next frame that is not skipped by SkipFiles. Skips frame at index and starts searching at next.
283
* Must satisfy `0 <= startIndex <= callStack - 1`
284
* @param down specifies whether to search downwards if the current file is skipped.
285
* @param callStack the call stack to search
286
* @param startIndex the index to start the search at
287
*/
288
function findNextVisibleFrame(down: boolean, callStack: readonly IStackFrame[], startIndex: number) {
289
290
if (startIndex >= callStack.length) {
291
startIndex = callStack.length - 1;
292
} else if (startIndex < 0) {
293
startIndex = 0;
294
}
295
296
let index = startIndex;
297
298
let currFrame;
299
do {
300
if (down) {
301
if (index === callStack.length - 1) {
302
index = 0;
303
} else {
304
index++;
305
}
306
} else {
307
if (index === 0) {
308
index = callStack.length - 1;
309
} else {
310
index--;
311
}
312
}
313
314
currFrame = callStack[index];
315
if (!isFrameDeemphasized(currFrame)) {
316
return currFrame;
317
}
318
} while (index !== startIndex); // end loop when we've just checked the start index, since that should be the last one checked
319
320
return undefined;
321
}
322
323
// These commands are used in call stack context menu, call stack inline actions, command palette, debug toolbar, mac native touch bar
324
// 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
325
// 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 thread
326
// Same for stackFrame commands and session commands.
327
CommandsRegistry.registerCommand({
328
id: COPY_STACK_TRACE_ID,
329
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
330
const textResourcePropertiesService = accessor.get(ITextResourcePropertiesService);
331
const clipboardService = accessor.get(IClipboardService);
332
const debugService = accessor.get(IDebugService);
333
const frame = getFrame(debugService, context);
334
if (frame) {
335
const eol = textResourcePropertiesService.getEOL(frame.source.uri);
336
await clipboardService.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(eol));
337
}
338
}
339
});
340
341
CommandsRegistry.registerCommand({
342
id: REVERSE_CONTINUE_ID,
343
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
344
await getThreadAndRun(accessor, context, thread => thread.reverseContinue());
345
}
346
});
347
348
CommandsRegistry.registerCommand({
349
id: STEP_BACK_ID,
350
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
351
const contextKeyService = accessor.get(IContextKeyService);
352
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
353
await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepBack('instruction'));
354
} else {
355
await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepBack());
356
}
357
}
358
});
359
360
CommandsRegistry.registerCommand({
361
id: TERMINATE_THREAD_ID,
362
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
363
await getThreadAndRun(accessor, context, thread => thread.terminate());
364
}
365
});
366
367
CommandsRegistry.registerCommand({
368
id: JUMP_TO_CURSOR_ID,
369
handler: async (accessor: ServicesAccessor) => {
370
const debugService = accessor.get(IDebugService);
371
const stackFrame = debugService.getViewModel().focusedStackFrame;
372
const editorService = accessor.get(IEditorService);
373
const activeEditorControl = editorService.activeTextEditorControl;
374
const notificationService = accessor.get(INotificationService);
375
const quickInputService = accessor.get(IQuickInputService);
376
377
if (stackFrame && isCodeEditor(activeEditorControl) && activeEditorControl.hasModel()) {
378
const position = activeEditorControl.getPosition();
379
const resource = activeEditorControl.getModel().uri;
380
const source = stackFrame.thread.session.getSourceForUri(resource);
381
if (source) {
382
const response = await stackFrame.thread.session.gotoTargets(source.raw, position.lineNumber, position.column);
383
const targets = response?.body.targets;
384
if (targets && targets.length) {
385
let id = targets[0].id;
386
if (targets.length > 1) {
387
const picks = targets.map(t => ({ label: t.label, _id: t.id }));
388
const pick = await quickInputService.pick(picks, { placeHolder: nls.localize('chooseLocation', "Choose the specific location") });
389
if (!pick) {
390
return;
391
}
392
393
id = pick._id;
394
}
395
396
return await stackFrame.thread.session.goto(stackFrame.thread.threadId, id).catch(e => notificationService.warn(e));
397
}
398
}
399
}
400
401
return notificationService.warn(nls.localize('noExecutableCode', "No executable code is associated at the current cursor position."));
402
}
403
});
404
405
406
CommandsRegistry.registerCommand({
407
id: CALLSTACK_TOP_ID,
408
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
409
const debugService = accessor.get(IDebugService);
410
goToTopOfCallStack(debugService);
411
}
412
});
413
414
CommandsRegistry.registerCommand({
415
id: CALLSTACK_BOTTOM_ID,
416
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
417
const debugService = accessor.get(IDebugService);
418
await goToBottomOfCallStack(debugService);
419
}
420
});
421
422
CommandsRegistry.registerCommand({
423
id: CALLSTACK_UP_ID,
424
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
425
const debugService = accessor.get(IDebugService);
426
navigateCallStack(debugService, false);
427
}
428
});
429
430
CommandsRegistry.registerCommand({
431
id: CALLSTACK_DOWN_ID,
432
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
433
const debugService = accessor.get(IDebugService);
434
navigateCallStack(debugService, true);
435
}
436
});
437
438
MenuRegistry.appendMenuItem(MenuId.EditorContext, {
439
command: {
440
id: JUMP_TO_CURSOR_ID,
441
title: nls.localize('jumpToCursor', "Jump to Cursor"),
442
category: DEBUG_COMMAND_CATEGORY
443
},
444
when: ContextKeyExpr.and(CONTEXT_JUMP_TO_CURSOR_SUPPORTED, EditorContextKeys.editorTextFocus),
445
group: 'debug',
446
order: 3
447
});
448
449
KeybindingsRegistry.registerCommandAndKeybindingRule({
450
id: NEXT_DEBUG_CONSOLE_ID,
451
weight: KeybindingWeight.WorkbenchContrib + 1,
452
when: CONTEXT_IN_DEBUG_REPL,
453
primary: KeyMod.CtrlCmd | KeyCode.PageDown,
454
mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketRight },
455
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
456
changeDebugConsoleFocus(accessor, true);
457
}
458
});
459
460
KeybindingsRegistry.registerCommandAndKeybindingRule({
461
id: PREV_DEBUG_CONSOLE_ID,
462
weight: KeybindingWeight.WorkbenchContrib + 1,
463
when: CONTEXT_IN_DEBUG_REPL,
464
primary: KeyMod.CtrlCmd | KeyCode.PageUp,
465
mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketLeft },
466
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
467
changeDebugConsoleFocus(accessor, false);
468
}
469
});
470
471
KeybindingsRegistry.registerCommandAndKeybindingRule({
472
id: RESTART_SESSION_ID,
473
weight: KeybindingWeight.WorkbenchContrib,
474
primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5,
475
when: CONTEXT_IN_DEBUG_MODE,
476
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
477
const debugService = accessor.get(IDebugService);
478
const configurationService = accessor.get(IConfigurationService);
479
let session: IDebugSession | undefined;
480
if (isSessionContext(context)) {
481
session = debugService.getModel().getSession(context.sessionId);
482
} else {
483
session = debugService.getViewModel().focusedSession;
484
}
485
486
if (!session) {
487
const { launch, name } = debugService.getConfigurationManager().selectedConfiguration;
488
await debugService.startDebugging(launch, name, { noDebug: false, startedByUser: true });
489
} else {
490
const showSubSessions = configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
491
// Stop should be sent to the root parent session
492
while (!showSubSessions && session.lifecycleManagedByParent && session.parentSession) {
493
session = session.parentSession;
494
}
495
session.removeReplExpressions();
496
await debugService.restartSession(session);
497
}
498
}
499
});
500
501
KeybindingsRegistry.registerCommandAndKeybindingRule({
502
id: STEP_OVER_ID,
503
weight: KeybindingWeight.WorkbenchContrib,
504
primary: KeyCode.F10,
505
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
506
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
507
const contextKeyService = accessor.get(IContextKeyService);
508
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
509
await getThreadAndRun(accessor, context, (thread: IThread) => thread.next('instruction'));
510
} else {
511
await getThreadAndRun(accessor, context, (thread: IThread) => thread.next());
512
}
513
}
514
});
515
516
// Windows browsers use F11 for full screen, thus use alt+F11 as the default shortcut
517
const STEP_INTO_KEYBINDING = (isWeb && isWindows) ? (KeyMod.Alt | KeyCode.F11) : KeyCode.F11;
518
519
KeybindingsRegistry.registerCommandAndKeybindingRule({
520
id: STEP_INTO_ID,
521
weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging
522
primary: STEP_INTO_KEYBINDING,
523
// Use a more flexible when clause to not allow full screen command to take over when F11 pressed a lot of times
524
when: CONTEXT_DEBUG_STATE.notEqualsTo('inactive'),
525
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
526
const contextKeyService = accessor.get(IContextKeyService);
527
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
528
await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn('instruction'));
529
} else {
530
await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn());
531
}
532
}
533
});
534
535
KeybindingsRegistry.registerCommandAndKeybindingRule({
536
id: STEP_OUT_ID,
537
weight: KeybindingWeight.WorkbenchContrib,
538
primary: KeyMod.Shift | KeyCode.F11,
539
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
540
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
541
const contextKeyService = accessor.get(IContextKeyService);
542
if (CONTEXT_DISASSEMBLY_VIEW_FOCUS.getValue(contextKeyService)) {
543
await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut('instruction'));
544
} else {
545
await getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut());
546
}
547
}
548
});
549
550
KeybindingsRegistry.registerCommandAndKeybindingRule({
551
id: PAUSE_ID,
552
weight: KeybindingWeight.WorkbenchContrib + 2, // take priority over focus next part while we are debugging
553
primary: KeyCode.F6,
554
when: CONTEXT_DEBUG_STATE.isEqualTo('running'),
555
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
556
await getThreadAndRun(accessor, context, thread => thread.pause());
557
}
558
});
559
560
561
KeybindingsRegistry.registerCommandAndKeybindingRule({
562
id: STEP_INTO_TARGET_ID,
563
primary: STEP_INTO_KEYBINDING | KeyMod.CtrlCmd,
564
when: ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')),
565
weight: KeybindingWeight.WorkbenchContrib,
566
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
567
const quickInputService = accessor.get(IQuickInputService);
568
const debugService = accessor.get(IDebugService);
569
const session = debugService.getViewModel().focusedSession;
570
const frame = debugService.getViewModel().focusedStackFrame;
571
if (!frame || !session) {
572
return;
573
}
574
575
const editor = await accessor.get(IEditorService).openEditor({
576
resource: frame.source.uri,
577
options: { revealIfOpened: true }
578
});
579
580
let codeEditor: ICodeEditor | undefined;
581
if (editor) {
582
const ctrl = editor?.getControl();
583
if (isCodeEditor(ctrl)) {
584
codeEditor = ctrl;
585
}
586
}
587
588
interface ITargetItem extends IQuickPickItem {
589
target: DebugProtocol.StepInTarget;
590
}
591
592
const disposables = new DisposableStore();
593
const qp = disposables.add(quickInputService.createQuickPick<ITargetItem>());
594
qp.busy = true;
595
qp.show();
596
597
disposables.add(qp.onDidChangeActive(([item]) => {
598
if (codeEditor && item && item.target.line !== undefined) {
599
codeEditor.revealLineInCenterIfOutsideViewport(item.target.line);
600
codeEditor.setSelection({
601
startLineNumber: item.target.line,
602
startColumn: item.target.column || 1,
603
endLineNumber: item.target.endLine || item.target.line,
604
endColumn: item.target.endColumn || item.target.column || 1,
605
});
606
}
607
}));
608
609
disposables.add(qp.onDidAccept(() => {
610
if (qp.activeItems.length) {
611
session.stepIn(frame.thread.threadId, qp.activeItems[0].target.id);
612
}
613
}));
614
615
disposables.add(qp.onDidHide(() => disposables.dispose()));
616
617
session.stepInTargets(frame.frameId).then(targets => {
618
qp.busy = false;
619
if (targets?.length) {
620
qp.items = targets?.map(target => ({ target, label: target.label }));
621
} else {
622
qp.placeholder = nls.localize('editor.debug.action.stepIntoTargets.none', "No step targets available");
623
}
624
});
625
}
626
});
627
628
async function stopHandler(accessor: ServicesAccessor, _: string, context: CallStackContext | unknown, disconnect: boolean, suspend?: boolean): Promise<void> {
629
const debugService = accessor.get(IDebugService);
630
let session: IDebugSession | undefined;
631
if (isSessionContext(context)) {
632
session = debugService.getModel().getSession(context.sessionId);
633
} else {
634
session = debugService.getViewModel().focusedSession;
635
}
636
637
const configurationService = accessor.get(IConfigurationService);
638
const showSubSessions = configurationService.getValue<IDebugConfiguration>('debug').showSubSessionsInToolBar;
639
// Stop should be sent to the root parent session
640
while (!showSubSessions && session && session.lifecycleManagedByParent && session.parentSession) {
641
session = session.parentSession;
642
}
643
644
await debugService.stopSession(session, disconnect, suspend);
645
}
646
647
KeybindingsRegistry.registerCommandAndKeybindingRule({
648
id: DISCONNECT_ID,
649
weight: KeybindingWeight.WorkbenchContrib,
650
primary: KeyMod.Shift | KeyCode.F5,
651
when: ContextKeyExpr.and(CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_IN_DEBUG_MODE),
652
handler: (accessor, _, context) => stopHandler(accessor, _, context, true)
653
});
654
655
CommandsRegistry.registerCommand({
656
id: DISCONNECT_AND_SUSPEND_ID,
657
handler: (accessor, _, context) => stopHandler(accessor, _, context, true, true)
658
});
659
660
KeybindingsRegistry.registerCommandAndKeybindingRule({
661
id: STOP_ID,
662
weight: KeybindingWeight.WorkbenchContrib,
663
primary: KeyMod.Shift | KeyCode.F5,
664
when: ContextKeyExpr.and(CONTEXT_FOCUSED_SESSION_IS_ATTACH.toNegated(), CONTEXT_IN_DEBUG_MODE),
665
handler: (accessor, _, context) => stopHandler(accessor, _, context, false)
666
});
667
668
CommandsRegistry.registerCommand({
669
id: RESTART_FRAME_ID,
670
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
671
const debugService = accessor.get(IDebugService);
672
const notificationService = accessor.get(INotificationService);
673
const frame = getFrame(debugService, context);
674
if (frame) {
675
try {
676
await frame.restart();
677
} catch (e) {
678
notificationService.error(e);
679
}
680
}
681
}
682
});
683
684
KeybindingsRegistry.registerCommandAndKeybindingRule({
685
id: CONTINUE_ID,
686
weight: KeybindingWeight.WorkbenchContrib + 10, // Use a stronger weight to get priority over start debugging F5 shortcut
687
primary: KeyCode.F5,
688
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
689
handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => {
690
await getThreadAndRun(accessor, context, thread => thread.continue());
691
}
692
});
693
694
CommandsRegistry.registerCommand({
695
id: SHOW_LOADED_SCRIPTS_ID,
696
handler: async (accessor) => {
697
await showLoadedScriptMenu(accessor);
698
}
699
});
700
701
CommandsRegistry.registerCommand({
702
id: 'debug.startFromConfig',
703
handler: async (accessor, config: IConfig) => {
704
const debugService = accessor.get(IDebugService);
705
await debugService.startDebugging(undefined, config);
706
}
707
});
708
709
CommandsRegistry.registerCommand({
710
id: FOCUS_SESSION_ID,
711
handler: async (accessor: ServicesAccessor, session: IDebugSession) => {
712
const debugService = accessor.get(IDebugService);
713
const editorService = accessor.get(IEditorService);
714
const stoppedChildSession = debugService.getModel().getSessions().find(s => s.parentSession === session && s.state === State.Stopped);
715
if (stoppedChildSession && session.state !== State.Stopped) {
716
session = stoppedChildSession;
717
}
718
await debugService.focusStackFrame(undefined, undefined, session, { explicit: true });
719
const stackFrame = debugService.getViewModel().focusedStackFrame;
720
if (stackFrame) {
721
await stackFrame.openInEditor(editorService, true);
722
}
723
}
724
});
725
726
CommandsRegistry.registerCommand({
727
id: SELECT_AND_START_ID,
728
handler: async (accessor: ServicesAccessor, debugType: string | unknown, debugStartOptions?: { noDebug?: boolean }) => {
729
const quickInputService = accessor.get(IQuickInputService);
730
const debugService = accessor.get(IDebugService);
731
732
if (debugType) {
733
const configManager = debugService.getConfigurationManager();
734
const dynamicProviders = await configManager.getDynamicProviders();
735
for (const provider of dynamicProviders) {
736
if (provider.type === debugType) {
737
const pick = await provider.pick();
738
if (pick) {
739
await configManager.selectConfiguration(pick.launch, pick.config.name, pick.config, { type: provider.type });
740
debugService.startDebugging(pick.launch, pick.config, { noDebug: debugStartOptions?.noDebug, startedByUser: true });
741
742
return;
743
}
744
}
745
}
746
}
747
748
quickInputService.quickAccess.show(DEBUG_QUICK_ACCESS_PREFIX);
749
}
750
});
751
752
CommandsRegistry.registerCommand({
753
id: SELECT_DEBUG_CONSOLE_ID,
754
handler: async (accessor: ServicesAccessor) => {
755
const quickInputService = accessor.get(IQuickInputService);
756
quickInputService.quickAccess.show(DEBUG_CONSOLE_QUICK_ACCESS_PREFIX);
757
}
758
});
759
760
CommandsRegistry.registerCommand({
761
id: SELECT_DEBUG_SESSION_ID,
762
handler: async (accessor: ServicesAccessor) => {
763
showDebugSessionMenu(accessor, SELECT_AND_START_ID);
764
}
765
});
766
767
KeybindingsRegistry.registerCommandAndKeybindingRule({
768
id: DEBUG_START_COMMAND_ID,
769
weight: KeybindingWeight.WorkbenchContrib,
770
primary: KeyCode.F5,
771
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.isEqualTo('inactive')),
772
handler: async (accessor: ServicesAccessor, debugStartOptions?: { config?: Partial<IConfig>; noDebug?: boolean }) => {
773
const debugService = accessor.get(IDebugService);
774
await saveAllBeforeDebugStart(accessor.get(IConfigurationService), accessor.get(IEditorService));
775
const { launch, name, getConfig } = debugService.getConfigurationManager().selectedConfiguration;
776
const config = await getConfig();
777
const configOrName = config ? Object.assign(deepClone(config), debugStartOptions?.config) : name;
778
await debugService.startDebugging(launch, configOrName, { noDebug: debugStartOptions?.noDebug, startedByUser: true }, false);
779
}
780
});
781
782
KeybindingsRegistry.registerCommandAndKeybindingRule({
783
id: DEBUG_RUN_COMMAND_ID,
784
weight: KeybindingWeight.WorkbenchContrib,
785
primary: KeyMod.CtrlCmd | KeyCode.F5,
786
mac: { primary: KeyMod.WinCtrl | KeyCode.F5 },
787
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))),
788
handler: async (accessor: ServicesAccessor) => {
789
const commandService = accessor.get(ICommandService);
790
await commandService.executeCommand(DEBUG_START_COMMAND_ID, { noDebug: true });
791
}
792
});
793
794
KeybindingsRegistry.registerCommandAndKeybindingRule({
795
id: 'debug.toggleBreakpoint',
796
weight: KeybindingWeight.WorkbenchContrib + 5,
797
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, InputFocusedContext.toNegated()),
798
primary: KeyCode.Space,
799
handler: (accessor) => {
800
const listService = accessor.get(IListService);
801
const debugService = accessor.get(IDebugService);
802
const list = listService.lastFocusedList;
803
if (list instanceof List) {
804
const focused = <IEnablement[]>list.getFocusedElements();
805
if (focused && focused.length) {
806
debugService.enableOrDisableBreakpoints(!focused[0].enabled, focused[0]);
807
}
808
}
809
}
810
});
811
812
KeybindingsRegistry.registerCommandAndKeybindingRule({
813
id: 'debug.enableOrDisableBreakpoint',
814
weight: KeybindingWeight.WorkbenchContrib,
815
primary: undefined,
816
when: EditorContextKeys.editorTextFocus,
817
handler: (accessor) => {
818
const debugService = accessor.get(IDebugService);
819
const editorService = accessor.get(IEditorService);
820
const control = editorService.activeTextEditorControl;
821
if (isCodeEditor(control)) {
822
const model = control.getModel();
823
if (model) {
824
const position = control.getPosition();
825
if (position) {
826
const bps = debugService.getModel().getBreakpoints({ uri: model.uri, lineNumber: position.lineNumber });
827
if (bps.length) {
828
debugService.enableOrDisableBreakpoints(!bps[0].enabled, bps[0]);
829
}
830
}
831
}
832
}
833
}
834
});
835
836
KeybindingsRegistry.registerCommandAndKeybindingRule({
837
id: EDIT_EXPRESSION_COMMAND_ID,
838
weight: KeybindingWeight.WorkbenchContrib + 5,
839
when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,
840
primary: KeyCode.F2,
841
mac: { primary: KeyCode.Enter },
842
handler: (accessor: ServicesAccessor, expression: Expression | unknown) => {
843
const debugService = accessor.get(IDebugService);
844
if (!(expression instanceof Expression)) {
845
const listService = accessor.get(IListService);
846
const focused = listService.lastFocusedList;
847
if (focused) {
848
const elements = focused.getFocus();
849
if (Array.isArray(elements) && elements[0] instanceof Expression) {
850
expression = elements[0];
851
}
852
}
853
}
854
855
if (expression instanceof Expression) {
856
debugService.getViewModel().setSelectedExpression(expression, false);
857
}
858
}
859
});
860
861
CommandsRegistry.registerCommand({
862
id: SET_EXPRESSION_COMMAND_ID,
863
handler: async (accessor: ServicesAccessor, expression: Expression | unknown) => {
864
const debugService = accessor.get(IDebugService);
865
if (expression instanceof Expression || expression instanceof Variable) {
866
debugService.getViewModel().setSelectedExpression(expression, true);
867
}
868
}
869
});
870
871
KeybindingsRegistry.registerCommandAndKeybindingRule({
872
id: 'debug.setVariable',
873
weight: KeybindingWeight.WorkbenchContrib + 5,
874
when: CONTEXT_VARIABLES_FOCUSED,
875
primary: KeyCode.F2,
876
mac: { primary: KeyCode.Enter },
877
handler: (accessor) => {
878
const listService = accessor.get(IListService);
879
const debugService = accessor.get(IDebugService);
880
const focused = listService.lastFocusedList;
881
882
if (focused) {
883
const elements = focused.getFocus();
884
if (Array.isArray(elements) && elements[0] instanceof Variable) {
885
debugService.getViewModel().setSelectedExpression(elements[0], false);
886
}
887
}
888
}
889
});
890
891
KeybindingsRegistry.registerCommandAndKeybindingRule({
892
id: REMOVE_EXPRESSION_COMMAND_ID,
893
weight: KeybindingWeight.WorkbenchContrib,
894
when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_EXPRESSION_SELECTED.toNegated()),
895
primary: KeyCode.Delete,
896
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
897
handler: (accessor: ServicesAccessor, expression: Expression | unknown) => {
898
const debugService = accessor.get(IDebugService);
899
900
if (expression instanceof Expression) {
901
debugService.removeWatchExpressions(expression.getId());
902
return;
903
}
904
905
const listService = accessor.get(IListService);
906
const focused = listService.lastFocusedList;
907
if (focused) {
908
let elements = focused.getFocus();
909
if (Array.isArray(elements) && elements[0] instanceof Expression) {
910
const selection = focused.getSelection();
911
if (selection && selection.indexOf(elements[0]) >= 0) {
912
elements = selection;
913
}
914
elements.forEach((e: Expression) => debugService.removeWatchExpressions(e.getId()));
915
}
916
}
917
}
918
});
919
920
CommandsRegistry.registerCommand({
921
id: BREAK_WHEN_VALUE_CHANGES_ID,
922
handler: async (accessor: ServicesAccessor) => {
923
const debugService = accessor.get(IDebugService);
924
if (dataBreakpointInfoResponse) {
925
await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'write' });
926
}
927
}
928
});
929
930
CommandsRegistry.registerCommand({
931
id: BREAK_WHEN_VALUE_IS_ACCESSED_ID,
932
handler: async (accessor: ServicesAccessor) => {
933
const debugService = accessor.get(IDebugService);
934
if (dataBreakpointInfoResponse) {
935
await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'readWrite' });
936
}
937
}
938
});
939
940
CommandsRegistry.registerCommand({
941
id: BREAK_WHEN_VALUE_IS_READ_ID,
942
handler: async (accessor: ServicesAccessor) => {
943
const debugService = accessor.get(IDebugService);
944
if (dataBreakpointInfoResponse) {
945
await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'read' });
946
}
947
}
948
});
949
950
KeybindingsRegistry.registerCommandAndKeybindingRule({
951
id: 'debug.removeBreakpoint',
952
weight: KeybindingWeight.WorkbenchContrib,
953
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_INPUT_FOCUSED.toNegated()),
954
primary: KeyCode.Delete,
955
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
956
handler: (accessor) => {
957
const listService = accessor.get(IListService);
958
const debugService = accessor.get(IDebugService);
959
const list = listService.lastFocusedList;
960
961
if (list instanceof List) {
962
const focused = list.getFocusedElements();
963
const element = focused.length ? focused[0] : undefined;
964
if (element instanceof Breakpoint) {
965
debugService.removeBreakpoints(element.getId());
966
} else if (element instanceof FunctionBreakpoint) {
967
debugService.removeFunctionBreakpoints(element.getId());
968
} else if (element instanceof DataBreakpoint) {
969
debugService.removeDataBreakpoints(element.getId());
970
}
971
}
972
}
973
});
974
975
KeybindingsRegistry.registerCommandAndKeybindingRule({
976
id: 'debug.installAdditionalDebuggers',
977
weight: KeybindingWeight.WorkbenchContrib,
978
when: undefined,
979
primary: undefined,
980
handler: async (accessor, query: string) => {
981
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
982
let searchFor = `@category:debuggers`;
983
if (typeof query === 'string') {
984
searchFor += ` ${query}`;
985
}
986
return extensionsWorkbenchService.openSearch(searchFor);
987
}
988
});
989
990
registerAction2(class AddConfigurationAction extends Action2 {
991
constructor() {
992
super({
993
id: ADD_CONFIGURATION_ID,
994
title: nls.localize2('addConfiguration', "Add Configuration..."),
995
category: DEBUG_COMMAND_CATEGORY,
996
f1: true,
997
menu: {
998
id: MenuId.EditorContent,
999
when: ContextKeyExpr.and(
1000
ContextKeyExpr.regex(ResourceContextKey.Path.key, /\.vscode[/\\]launch\.json$/),
1001
ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID))
1002
}
1003
});
1004
}
1005
1006
async run(accessor: ServicesAccessor, launchUri: string): Promise<void> {
1007
const manager = accessor.get(IDebugService).getConfigurationManager();
1008
1009
const launch = manager.getLaunches().find(l => l.uri.toString() === launchUri) || manager.selectedConfiguration.launch;
1010
if (launch) {
1011
const { editor, created } = await launch.openConfigFile({ preserveFocus: false });
1012
if (editor && !created) {
1013
const codeEditor = <ICodeEditor>editor.getControl();
1014
if (codeEditor) {
1015
await codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID)?.addLaunchConfiguration();
1016
}
1017
}
1018
}
1019
}
1020
});
1021
1022
const inlineBreakpointHandler = (accessor: ServicesAccessor) => {
1023
const debugService = accessor.get(IDebugService);
1024
const editorService = accessor.get(IEditorService);
1025
const control = editorService.activeTextEditorControl;
1026
if (isCodeEditor(control)) {
1027
const position = control.getPosition();
1028
if (position && control.hasModel() && debugService.canSetBreakpointsIn(control.getModel())) {
1029
const modelUri = control.getModel().uri;
1030
const breakpointAlreadySet = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri })
1031
.some(bp => (bp.sessionAgnosticData.column === position.column || (!bp.column && position.column <= 1)));
1032
1033
if (!breakpointAlreadySet) {
1034
debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column > 1 ? position.column : undefined }]);
1035
}
1036
}
1037
}
1038
};
1039
1040
KeybindingsRegistry.registerCommandAndKeybindingRule({
1041
weight: KeybindingWeight.WorkbenchContrib,
1042
primary: KeyMod.Shift | KeyCode.F9,
1043
when: EditorContextKeys.editorTextFocus,
1044
id: TOGGLE_INLINE_BREAKPOINT_ID,
1045
handler: inlineBreakpointHandler
1046
});
1047
1048
MenuRegistry.appendMenuItem(MenuId.EditorContext, {
1049
command: {
1050
id: TOGGLE_INLINE_BREAKPOINT_ID,
1051
title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint"),
1052
category: DEBUG_COMMAND_CATEGORY
1053
},
1054
when: ContextKeyExpr.and(
1055
CONTEXT_IN_DEBUG_MODE,
1056
PanelFocusContext.toNegated(),
1057
EditorContextKeys.editorTextFocus,
1058
ChatContextKeys.inChatSession.toNegated()),
1059
group: 'debug',
1060
order: 1
1061
});
1062
1063
KeybindingsRegistry.registerCommandAndKeybindingRule({
1064
id: 'debug.openBreakpointToSide',
1065
weight: KeybindingWeight.WorkbenchContrib,
1066
when: CONTEXT_BREAKPOINTS_FOCUSED,
1067
primary: KeyMod.CtrlCmd | KeyCode.Enter,
1068
secondary: [KeyMod.Alt | KeyCode.Enter],
1069
handler: (accessor) => {
1070
const listService = accessor.get(IListService);
1071
const list = listService.lastFocusedList;
1072
if (list instanceof List) {
1073
const focus = list.getFocusedElements();
1074
if (focus.length && focus[0] instanceof Breakpoint) {
1075
return openBreakpointSource(focus[0], true, false, true, accessor.get(IDebugService), accessor.get(IEditorService));
1076
}
1077
}
1078
1079
return undefined;
1080
}
1081
});
1082
1083
registerAction2(class ToggleExceptionBreakpointsAction extends Action2 {
1084
constructor() {
1085
super({
1086
id: TOGGLE_EXCEPTION_BREAKPOINTS_ID,
1087
title: nls.localize2('toggleExceptionBreakpoints', "Toggle Exception Breakpoints"),
1088
category: DEBUG_COMMAND_CATEGORY,
1089
f1: true,
1090
precondition: CONTEXT_DEBUGGERS_AVAILABLE
1091
});
1092
}
1093
1094
async run(accessor: ServicesAccessor): Promise<void> {
1095
const debugService = accessor.get(IDebugService);
1096
const quickInputService = accessor.get(IQuickInputService);
1097
1098
// Get the focused session or the first available session
1099
const debugModel = debugService.getModel();
1100
const session = debugService.getViewModel().focusedSession || debugModel.getSessions()[0];
1101
const exceptionBreakpoints = session ? debugModel.getExceptionBreakpointsForSession(session.getId()) : debugModel.getExceptionBreakpoints();
1102
if (exceptionBreakpoints.length === 0) {
1103
return;
1104
}
1105
1106
// If only one exception breakpoint type, toggle it directly
1107
if (exceptionBreakpoints.length === 1) {
1108
const breakpoint = exceptionBreakpoints[0];
1109
await debugService.enableOrDisableBreakpoints(!breakpoint.enabled, breakpoint);
1110
return;
1111
}
1112
1113
// Multiple exception breakpoint types - show quickpick for selection
1114
interface IExceptionBreakpointItem extends IQuickPickItem {
1115
breakpoint: IExceptionBreakpoint;
1116
}
1117
1118
const disposables = new DisposableStore();
1119
const quickPick = disposables.add(quickInputService.createQuickPick<IExceptionBreakpointItem>());
1120
quickPick.placeholder = nls.localize('selectExceptionBreakpointsPlaceholder', "Pick enabled exception breakpoints");
1121
quickPick.canSelectMany = true;
1122
quickPick.matchOnDescription = true;
1123
quickPick.matchOnDetail = true;
1124
1125
// Create quickpick items from exception breakpoints
1126
quickPick.items = exceptionBreakpoints.map(bp => ({
1127
label: bp.label,
1128
description: bp.description,
1129
picked: bp.enabled,
1130
breakpoint: bp
1131
}));
1132
1133
quickPick.selectedItems = quickPick.items.filter(item => item.picked);
1134
1135
disposables.add(quickPick.onDidAccept(() => {
1136
const selectedItems = quickPick.selectedItems;
1137
const toEnable: IExceptionBreakpoint[] = [];
1138
const toDisable: IExceptionBreakpoint[] = [];
1139
1140
// Determine which breakpoints need to be toggled
1141
for (const bp of exceptionBreakpoints) {
1142
const isSelected = selectedItems.some(item => item.breakpoint === bp);
1143
if (isSelected && !bp.enabled) {
1144
toEnable.push(bp);
1145
} else if (!isSelected && bp.enabled) {
1146
toDisable.push(bp);
1147
}
1148
}
1149
1150
// Toggle the breakpoints
1151
const promises: Promise<void>[] = [];
1152
for (const bp of toEnable) {
1153
promises.push(debugService.enableOrDisableBreakpoints(true, bp));
1154
}
1155
for (const bp of toDisable) {
1156
promises.push(debugService.enableOrDisableBreakpoints(false, bp));
1157
}
1158
1159
Promise.all(promises).then(() => disposables.dispose());
1160
}));
1161
1162
disposables.add(quickPick.onDidHide(() => disposables.dispose()));
1163
quickPick.show();
1164
}
1165
});
1166
1167
// When there are no debug extensions, open the debug viewlet when F5 is pressed so the user can read the limitations
1168
KeybindingsRegistry.registerCommandAndKeybindingRule({
1169
id: 'debug.openView',
1170
weight: KeybindingWeight.WorkbenchContrib,
1171
when: CONTEXT_DEBUGGERS_AVAILABLE.toNegated(),
1172
primary: KeyCode.F5,
1173
secondary: [KeyMod.CtrlCmd | KeyCode.F5],
1174
handler: async (accessor) => {
1175
const paneCompositeService = accessor.get(IPaneCompositePartService);
1176
await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true);
1177
}
1178
});
1179
1180
registerAction2(class extends Action2 {
1181
constructor() {
1182
super({
1183
id: ATTACH_TO_CURRENT_CODE_RENDERER,
1184
title: nls.localize2('attachToCurrentCodeRenderer', "Attach to Current Code Renderer"),
1185
});
1186
}
1187
1188
override async run(accessor: ServicesAccessor): Promise<any> {
1189
const env = accessor.get(IEnvironmentService);
1190
if (!env.isExtensionDevelopment && !env.extensionTestsLocationURI) {
1191
throw new Error('Refusing to attach to renderer outside of development context');
1192
}
1193
1194
const windowId = getWindowId(mainWindow);
1195
const extDebugService = accessor.get(IExtensionHostDebugService);
1196
const result = await extDebugService.attachToCurrentWindowRenderer(windowId);
1197
1198
return result;
1199
}
1200
});
1201
1202