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
5237 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/actions/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, resolveChildSession } 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, _: unknown, 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
session = resolveChildSession(session, debugService.getModel().getSessions());
715
await debugService.focusStackFrame(undefined, undefined, session, { explicit: true });
716
const stackFrame = debugService.getViewModel().focusedStackFrame;
717
if (stackFrame) {
718
await stackFrame.openInEditor(editorService, true);
719
}
720
}
721
});
722
723
CommandsRegistry.registerCommand({
724
id: SELECT_AND_START_ID,
725
handler: async (accessor: ServicesAccessor, debugType: string | unknown, debugStartOptions?: { noDebug?: boolean }) => {
726
const quickInputService = accessor.get(IQuickInputService);
727
const debugService = accessor.get(IDebugService);
728
729
if (debugType) {
730
const configManager = debugService.getConfigurationManager();
731
const dynamicProviders = await configManager.getDynamicProviders();
732
for (const provider of dynamicProviders) {
733
if (provider.type === debugType) {
734
const pick = await provider.pick();
735
if (pick) {
736
await configManager.selectConfiguration(pick.launch, pick.config.name, pick.config, { type: provider.type });
737
debugService.startDebugging(pick.launch, pick.config, { noDebug: debugStartOptions?.noDebug, startedByUser: true });
738
739
return;
740
}
741
}
742
}
743
}
744
745
quickInputService.quickAccess.show(DEBUG_QUICK_ACCESS_PREFIX);
746
}
747
});
748
749
CommandsRegistry.registerCommand({
750
id: SELECT_DEBUG_CONSOLE_ID,
751
handler: async (accessor: ServicesAccessor) => {
752
const quickInputService = accessor.get(IQuickInputService);
753
quickInputService.quickAccess.show(DEBUG_CONSOLE_QUICK_ACCESS_PREFIX);
754
}
755
});
756
757
CommandsRegistry.registerCommand({
758
id: SELECT_DEBUG_SESSION_ID,
759
handler: async (accessor: ServicesAccessor) => {
760
showDebugSessionMenu(accessor, SELECT_AND_START_ID);
761
}
762
});
763
764
KeybindingsRegistry.registerCommandAndKeybindingRule({
765
id: DEBUG_START_COMMAND_ID,
766
weight: KeybindingWeight.WorkbenchContrib,
767
primary: KeyCode.F5,
768
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.isEqualTo('inactive')),
769
handler: async (accessor: ServicesAccessor, debugStartOptions?: { config?: Partial<IConfig>; noDebug?: boolean }) => {
770
const debugService = accessor.get(IDebugService);
771
await saveAllBeforeDebugStart(accessor.get(IConfigurationService), accessor.get(IEditorService));
772
const { launch, name, getConfig } = debugService.getConfigurationManager().selectedConfiguration;
773
const config = await getConfig();
774
const configOrName = config ? Object.assign(deepClone(config), debugStartOptions?.config) : name;
775
await debugService.startDebugging(launch, configOrName, { noDebug: debugStartOptions?.noDebug, startedByUser: true }, false);
776
}
777
});
778
779
KeybindingsRegistry.registerCommandAndKeybindingRule({
780
id: DEBUG_RUN_COMMAND_ID,
781
weight: KeybindingWeight.WorkbenchContrib,
782
primary: KeyMod.CtrlCmd | KeyCode.F5,
783
mac: { primary: KeyMod.WinCtrl | KeyCode.F5 },
784
when: ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing))),
785
handler: async (accessor: ServicesAccessor) => {
786
const commandService = accessor.get(ICommandService);
787
await commandService.executeCommand(DEBUG_START_COMMAND_ID, { noDebug: true });
788
}
789
});
790
791
KeybindingsRegistry.registerCommandAndKeybindingRule({
792
id: 'debug.toggleBreakpoint',
793
weight: KeybindingWeight.WorkbenchContrib + 5,
794
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, InputFocusedContext.toNegated()),
795
primary: KeyCode.Space,
796
handler: (accessor) => {
797
const listService = accessor.get(IListService);
798
const debugService = accessor.get(IDebugService);
799
const list = listService.lastFocusedList;
800
if (list instanceof List) {
801
const focused = <IEnablement[]>list.getFocusedElements();
802
if (focused && focused.length) {
803
debugService.enableOrDisableBreakpoints(!focused[0].enabled, focused[0]);
804
}
805
}
806
}
807
});
808
809
KeybindingsRegistry.registerCommandAndKeybindingRule({
810
id: 'debug.enableOrDisableBreakpoint',
811
weight: KeybindingWeight.WorkbenchContrib,
812
primary: undefined,
813
when: EditorContextKeys.editorTextFocus,
814
handler: (accessor) => {
815
const debugService = accessor.get(IDebugService);
816
const editorService = accessor.get(IEditorService);
817
const control = editorService.activeTextEditorControl;
818
if (isCodeEditor(control)) {
819
const model = control.getModel();
820
if (model) {
821
const position = control.getPosition();
822
if (position) {
823
const bps = debugService.getModel().getBreakpoints({ uri: model.uri, lineNumber: position.lineNumber });
824
if (bps.length) {
825
debugService.enableOrDisableBreakpoints(!bps[0].enabled, bps[0]);
826
}
827
}
828
}
829
}
830
}
831
});
832
833
KeybindingsRegistry.registerCommandAndKeybindingRule({
834
id: EDIT_EXPRESSION_COMMAND_ID,
835
weight: KeybindingWeight.WorkbenchContrib + 5,
836
when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED,
837
primary: KeyCode.F2,
838
mac: { primary: KeyCode.Enter },
839
handler: (accessor: ServicesAccessor, expression: Expression | unknown) => {
840
const debugService = accessor.get(IDebugService);
841
if (!(expression instanceof Expression)) {
842
const listService = accessor.get(IListService);
843
const focused = listService.lastFocusedList;
844
if (focused) {
845
const elements = focused.getFocus();
846
if (Array.isArray(elements) && elements[0] instanceof Expression) {
847
expression = elements[0];
848
}
849
}
850
}
851
852
if (expression instanceof Expression) {
853
debugService.getViewModel().setSelectedExpression(expression, false);
854
}
855
}
856
});
857
858
CommandsRegistry.registerCommand({
859
id: SET_EXPRESSION_COMMAND_ID,
860
handler: async (accessor: ServicesAccessor, expression: Expression | unknown) => {
861
const debugService = accessor.get(IDebugService);
862
if (expression instanceof Expression || expression instanceof Variable) {
863
debugService.getViewModel().setSelectedExpression(expression, true);
864
}
865
}
866
});
867
868
KeybindingsRegistry.registerCommandAndKeybindingRule({
869
id: 'debug.setVariable',
870
weight: KeybindingWeight.WorkbenchContrib + 5,
871
when: CONTEXT_VARIABLES_FOCUSED,
872
primary: KeyCode.F2,
873
mac: { primary: KeyCode.Enter },
874
handler: (accessor) => {
875
const listService = accessor.get(IListService);
876
const debugService = accessor.get(IDebugService);
877
const focused = listService.lastFocusedList;
878
879
if (focused) {
880
const elements = focused.getFocus();
881
if (Array.isArray(elements) && elements[0] instanceof Variable) {
882
debugService.getViewModel().setSelectedExpression(elements[0], false);
883
}
884
}
885
}
886
});
887
888
KeybindingsRegistry.registerCommandAndKeybindingRule({
889
id: REMOVE_EXPRESSION_COMMAND_ID,
890
weight: KeybindingWeight.WorkbenchContrib,
891
when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_EXPRESSION_SELECTED.toNegated()),
892
primary: KeyCode.Delete,
893
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
894
handler: (accessor: ServicesAccessor, expression: Expression | unknown) => {
895
const debugService = accessor.get(IDebugService);
896
897
if (expression instanceof Expression) {
898
debugService.removeWatchExpressions(expression.getId());
899
return;
900
}
901
902
const listService = accessor.get(IListService);
903
const focused = listService.lastFocusedList;
904
if (focused) {
905
let elements = focused.getFocus();
906
if (Array.isArray(elements) && elements[0] instanceof Expression) {
907
const selection = focused.getSelection();
908
if (selection && selection.indexOf(elements[0]) >= 0) {
909
elements = selection;
910
}
911
elements.forEach((e: Expression) => debugService.removeWatchExpressions(e.getId()));
912
}
913
}
914
}
915
});
916
917
CommandsRegistry.registerCommand({
918
id: BREAK_WHEN_VALUE_CHANGES_ID,
919
handler: async (accessor: ServicesAccessor) => {
920
const debugService = accessor.get(IDebugService);
921
if (dataBreakpointInfoResponse) {
922
await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'write' });
923
}
924
}
925
});
926
927
CommandsRegistry.registerCommand({
928
id: BREAK_WHEN_VALUE_IS_ACCESSED_ID,
929
handler: async (accessor: ServicesAccessor) => {
930
const debugService = accessor.get(IDebugService);
931
if (dataBreakpointInfoResponse) {
932
await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'readWrite' });
933
}
934
}
935
});
936
937
CommandsRegistry.registerCommand({
938
id: BREAK_WHEN_VALUE_IS_READ_ID,
939
handler: async (accessor: ServicesAccessor) => {
940
const debugService = accessor.get(IDebugService);
941
if (dataBreakpointInfoResponse) {
942
await debugService.addDataBreakpoint({ description: dataBreakpointInfoResponse.description, src: { type: DataBreakpointSetType.Variable, dataId: dataBreakpointInfoResponse.dataId! }, canPersist: !!dataBreakpointInfoResponse.canPersist, accessTypes: dataBreakpointInfoResponse.accessTypes, accessType: 'read' });
943
}
944
}
945
});
946
947
KeybindingsRegistry.registerCommandAndKeybindingRule({
948
id: 'debug.removeBreakpoint',
949
weight: KeybindingWeight.WorkbenchContrib,
950
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_INPUT_FOCUSED.toNegated()),
951
primary: KeyCode.Delete,
952
mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace },
953
handler: (accessor) => {
954
const listService = accessor.get(IListService);
955
const debugService = accessor.get(IDebugService);
956
const list = listService.lastFocusedList;
957
958
if (list instanceof List) {
959
const focused = list.getFocusedElements();
960
const element = focused.length ? focused[0] : undefined;
961
if (element instanceof Breakpoint) {
962
debugService.removeBreakpoints(element.getId());
963
} else if (element instanceof FunctionBreakpoint) {
964
debugService.removeFunctionBreakpoints(element.getId());
965
} else if (element instanceof DataBreakpoint) {
966
debugService.removeDataBreakpoints(element.getId());
967
}
968
}
969
}
970
});
971
972
KeybindingsRegistry.registerCommandAndKeybindingRule({
973
id: 'debug.installAdditionalDebuggers',
974
weight: KeybindingWeight.WorkbenchContrib,
975
when: undefined,
976
primary: undefined,
977
handler: async (accessor, query: string) => {
978
const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService);
979
let searchFor = `@category:debuggers`;
980
if (typeof query === 'string') {
981
searchFor += ` ${query}`;
982
}
983
return extensionsWorkbenchService.openSearch(searchFor);
984
}
985
});
986
987
registerAction2(class AddConfigurationAction extends Action2 {
988
constructor() {
989
super({
990
id: ADD_CONFIGURATION_ID,
991
title: nls.localize2('addConfiguration', "Add Configuration..."),
992
category: DEBUG_COMMAND_CATEGORY,
993
f1: true,
994
menu: {
995
id: MenuId.EditorContent,
996
when: ContextKeyExpr.and(
997
ContextKeyExpr.regex(ResourceContextKey.Path.key, /\.vscode[/\\]launch\.json$/),
998
ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID))
999
}
1000
});
1001
}
1002
1003
async run(accessor: ServicesAccessor, launchUri: string): Promise<void> {
1004
const manager = accessor.get(IDebugService).getConfigurationManager();
1005
1006
const launch = manager.getLaunches().find(l => l.uri.toString() === launchUri) || manager.selectedConfiguration.launch;
1007
if (launch) {
1008
const { editor, created } = await launch.openConfigFile({ preserveFocus: false });
1009
if (editor && !created) {
1010
const codeEditor = <ICodeEditor>editor.getControl();
1011
if (codeEditor) {
1012
await codeEditor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID)?.addLaunchConfiguration();
1013
}
1014
}
1015
}
1016
}
1017
});
1018
1019
const inlineBreakpointHandler = (accessor: ServicesAccessor) => {
1020
const debugService = accessor.get(IDebugService);
1021
const editorService = accessor.get(IEditorService);
1022
const control = editorService.activeTextEditorControl;
1023
if (isCodeEditor(control)) {
1024
const position = control.getPosition();
1025
if (position && control.hasModel() && debugService.canSetBreakpointsIn(control.getModel())) {
1026
const modelUri = control.getModel().uri;
1027
const breakpointAlreadySet = debugService.getModel().getBreakpoints({ lineNumber: position.lineNumber, uri: modelUri })
1028
.some(bp => (bp.sessionAgnosticData.column === position.column || (!bp.column && position.column <= 1)));
1029
1030
if (!breakpointAlreadySet) {
1031
debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column > 1 ? position.column : undefined }]);
1032
}
1033
}
1034
}
1035
};
1036
1037
KeybindingsRegistry.registerCommandAndKeybindingRule({
1038
weight: KeybindingWeight.WorkbenchContrib,
1039
primary: KeyMod.Shift | KeyCode.F9,
1040
when: EditorContextKeys.editorTextFocus,
1041
id: TOGGLE_INLINE_BREAKPOINT_ID,
1042
handler: inlineBreakpointHandler
1043
});
1044
1045
MenuRegistry.appendMenuItem(MenuId.EditorContext, {
1046
command: {
1047
id: TOGGLE_INLINE_BREAKPOINT_ID,
1048
title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint"),
1049
category: DEBUG_COMMAND_CATEGORY
1050
},
1051
when: ContextKeyExpr.and(
1052
CONTEXT_IN_DEBUG_MODE,
1053
PanelFocusContext.toNegated(),
1054
EditorContextKeys.editorTextFocus,
1055
ChatContextKeys.inChatSession.toNegated()),
1056
group: 'debug',
1057
order: 1
1058
});
1059
1060
KeybindingsRegistry.registerCommandAndKeybindingRule({
1061
id: 'debug.openBreakpointToSide',
1062
weight: KeybindingWeight.WorkbenchContrib,
1063
when: CONTEXT_BREAKPOINTS_FOCUSED,
1064
primary: KeyMod.CtrlCmd | KeyCode.Enter,
1065
secondary: [KeyMod.Alt | KeyCode.Enter],
1066
handler: (accessor) => {
1067
const listService = accessor.get(IListService);
1068
const list = listService.lastFocusedList;
1069
if (list instanceof List) {
1070
const focus = list.getFocusedElements();
1071
if (focus.length && focus[0] instanceof Breakpoint) {
1072
return openBreakpointSource(focus[0], true, false, true, accessor.get(IDebugService), accessor.get(IEditorService));
1073
}
1074
}
1075
1076
return undefined;
1077
}
1078
});
1079
1080
registerAction2(class ToggleExceptionBreakpointsAction extends Action2 {
1081
constructor() {
1082
super({
1083
id: TOGGLE_EXCEPTION_BREAKPOINTS_ID,
1084
title: nls.localize2('toggleExceptionBreakpoints', "Toggle Exception Breakpoints"),
1085
category: DEBUG_COMMAND_CATEGORY,
1086
f1: true,
1087
precondition: CONTEXT_DEBUGGERS_AVAILABLE
1088
});
1089
}
1090
1091
async run(accessor: ServicesAccessor): Promise<void> {
1092
const debugService = accessor.get(IDebugService);
1093
const quickInputService = accessor.get(IQuickInputService);
1094
1095
// Get the focused session or the first available session
1096
const debugModel = debugService.getModel();
1097
const session = debugService.getViewModel().focusedSession || debugModel.getSessions()[0];
1098
const exceptionBreakpoints = session ? debugModel.getExceptionBreakpointsForSession(session.getId()) : debugModel.getExceptionBreakpoints();
1099
if (exceptionBreakpoints.length === 0) {
1100
return;
1101
}
1102
1103
// If only one exception breakpoint type, toggle it directly
1104
if (exceptionBreakpoints.length === 1) {
1105
const breakpoint = exceptionBreakpoints[0];
1106
await debugService.enableOrDisableBreakpoints(!breakpoint.enabled, breakpoint);
1107
return;
1108
}
1109
1110
// Multiple exception breakpoint types - show quickpick for selection
1111
interface IExceptionBreakpointItem extends IQuickPickItem {
1112
breakpoint: IExceptionBreakpoint;
1113
}
1114
1115
const disposables = new DisposableStore();
1116
const quickPick = disposables.add(quickInputService.createQuickPick<IExceptionBreakpointItem>());
1117
quickPick.placeholder = nls.localize('selectExceptionBreakpointsPlaceholder', "Pick enabled exception breakpoints");
1118
quickPick.canSelectMany = true;
1119
quickPick.matchOnDescription = true;
1120
quickPick.matchOnDetail = true;
1121
1122
// Create quickpick items from exception breakpoints
1123
quickPick.items = exceptionBreakpoints.map(bp => ({
1124
label: bp.label,
1125
description: bp.description,
1126
picked: bp.enabled,
1127
breakpoint: bp
1128
}));
1129
1130
quickPick.selectedItems = quickPick.items.filter(item => item.picked);
1131
1132
disposables.add(quickPick.onDidAccept(() => {
1133
const selectedItems = quickPick.selectedItems;
1134
const toEnable: IExceptionBreakpoint[] = [];
1135
const toDisable: IExceptionBreakpoint[] = [];
1136
1137
// Determine which breakpoints need to be toggled
1138
for (const bp of exceptionBreakpoints) {
1139
const isSelected = selectedItems.some(item => item.breakpoint === bp);
1140
if (isSelected && !bp.enabled) {
1141
toEnable.push(bp);
1142
} else if (!isSelected && bp.enabled) {
1143
toDisable.push(bp);
1144
}
1145
}
1146
1147
// Toggle the breakpoints
1148
const promises: Promise<void>[] = [];
1149
for (const bp of toEnable) {
1150
promises.push(debugService.enableOrDisableBreakpoints(true, bp));
1151
}
1152
for (const bp of toDisable) {
1153
promises.push(debugService.enableOrDisableBreakpoints(false, bp));
1154
}
1155
1156
Promise.all(promises).then(() => disposables.dispose());
1157
}));
1158
1159
disposables.add(quickPick.onDidHide(() => disposables.dispose()));
1160
quickPick.show();
1161
}
1162
});
1163
1164
// When there are no debug extensions, open the debug viewlet when F5 is pressed so the user can read the limitations
1165
KeybindingsRegistry.registerCommandAndKeybindingRule({
1166
id: 'debug.openView',
1167
weight: KeybindingWeight.WorkbenchContrib,
1168
when: CONTEXT_DEBUGGERS_AVAILABLE.toNegated(),
1169
primary: KeyCode.F5,
1170
secondary: [KeyMod.CtrlCmd | KeyCode.F5],
1171
handler: async (accessor) => {
1172
const paneCompositeService = accessor.get(IPaneCompositePartService);
1173
await paneCompositeService.openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true);
1174
}
1175
});
1176
1177
registerAction2(class extends Action2 {
1178
constructor() {
1179
super({
1180
id: ATTACH_TO_CURRENT_CODE_RENDERER,
1181
title: nls.localize2('attachToCurrentCodeRenderer', "Attach to Current Code Renderer"),
1182
});
1183
}
1184
1185
override async run(accessor: ServicesAccessor): Promise<any> {
1186
const env = accessor.get(IEnvironmentService);
1187
if (!env.isExtensionDevelopment && !env.extensionTestsLocationURI) {
1188
throw new Error('Refusing to attach to renderer outside of development context');
1189
}
1190
1191
const windowId = getWindowId(mainWindow);
1192
const extDebugService = accessor.get(IExtensionHostDebugService);
1193
const result = await extDebugService.attachToCurrentWindowRenderer(windowId);
1194
1195
return result;
1196
}
1197
});
1198
1199