Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/debugEditorActions.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 { getDomNodePagePosition } from '../../../../base/browser/dom.js';
7
import { Action } from '../../../../base/common/actions.js';
8
import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
9
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
10
import { EditorAction, IActionOptions, registerEditorAction } from '../../../../editor/browser/editorExtensions.js';
11
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
12
import { Position } from '../../../../editor/common/core/position.js';
13
import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js';
14
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
15
import { MessageController } from '../../../../editor/contrib/message/browser/messageController.js';
16
import * as nls from '../../../../nls.js';
17
import { ILocalizedString } from '../../../../platform/action/common/action.js';
18
import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
19
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
20
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
21
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
22
import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
23
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
24
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
25
import { PanelFocusContext } from '../../../common/contextkeys.js';
26
import { ChatContextKeys } from '../../chat/common/chatContextKeys.js';
27
import { openBreakpointSource } from './breakpointsView.js';
28
import { DisassemblyView, IDisassembledInstructionEntry } from './disassemblyView.js';
29
import { Repl } from './repl.js';
30
import { BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_DISASSEMBLY_VIEW_FOCUS, CONTEXT_EXCEPTION_WIDGET_VISIBLE, CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE, CONTEXT_IN_DEBUG_MODE, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IDebugConfiguration, IDebugEditorContribution, IDebugService, REPL_VIEW_ID, WATCH_VIEW_ID } from '../common/debug.js';
31
import { getEvaluatableExpressionAtPosition } from '../common/debugUtils.js';
32
import { DisassemblyViewInput } from '../common/disassemblyViewInput.js';
33
import { IEditorService } from '../../../services/editor/common/editorService.js';
34
import { IViewsService } from '../../../services/views/common/viewsService.js';
35
import { TOGGLE_BREAKPOINT_ID } from '../../../../workbench/contrib/debug/browser/debugCommands.js';
36
37
class ToggleBreakpointAction extends Action2 {
38
constructor() {
39
super({
40
id: TOGGLE_BREAKPOINT_ID,
41
title: {
42
...nls.localize2('toggleBreakpointAction', "Debug: Toggle Breakpoint"),
43
mnemonicTitle: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint"),
44
},
45
f1: true,
46
precondition: CONTEXT_DEBUGGERS_AVAILABLE,
47
keybinding: {
48
when: ContextKeyExpr.or(EditorContextKeys.editorTextFocus, CONTEXT_DISASSEMBLY_VIEW_FOCUS),
49
primary: KeyCode.F9,
50
weight: KeybindingWeight.EditorContrib
51
},
52
menu: {
53
id: MenuId.MenubarDebugMenu,
54
when: CONTEXT_DEBUGGERS_AVAILABLE,
55
group: '4_new_breakpoint',
56
order: 1
57
}
58
});
59
}
60
61
async run(accessor: ServicesAccessor, entry?: IDisassembledInstructionEntry): Promise<void> {
62
const editorService = accessor.get(IEditorService);
63
const debugService = accessor.get(IDebugService);
64
65
const activePane = editorService.activeEditorPane;
66
if (activePane instanceof DisassemblyView) {
67
const location = entry ? activePane.getAddressAndOffset(entry) : activePane.focusedAddressAndOffset;
68
if (location) {
69
const bps = debugService.getModel().getInstructionBreakpoints();
70
const toRemove = bps.find(bp => bp.address === location.address);
71
if (toRemove) {
72
debugService.removeInstructionBreakpoints(toRemove.instructionReference, toRemove.offset);
73
} else {
74
debugService.addInstructionBreakpoint({ instructionReference: location.reference, offset: location.offset, address: location.address, canPersist: false });
75
}
76
}
77
return;
78
}
79
80
const codeEditorService = accessor.get(ICodeEditorService);
81
const editor = codeEditorService.getFocusedCodeEditor() || codeEditorService.getActiveCodeEditor();
82
if (editor?.hasModel()) {
83
const modelUri = editor.getModel().uri;
84
const canSet = debugService.canSetBreakpointsIn(editor.getModel());
85
// Does not account for multi line selections, Set to remove multiple cursor on the same line
86
const lineNumbers = [...new Set(editor.getSelections().map(s => s.getPosition().lineNumber))];
87
88
await Promise.all(lineNumbers.map(async line => {
89
const bps = debugService.getModel().getBreakpoints({ lineNumber: line, uri: modelUri });
90
if (bps.length) {
91
await Promise.all(bps.map(bp => debugService.removeBreakpoints(bp.getId())));
92
} else if (canSet) {
93
await debugService.addBreakpoints(modelUri, [{ lineNumber: line }]);
94
}
95
}));
96
}
97
}
98
}
99
100
class ConditionalBreakpointAction extends EditorAction {
101
constructor() {
102
super({
103
id: 'editor.debug.action.conditionalBreakpoint',
104
label: nls.localize2('conditionalBreakpointEditorAction', "Debug: Add Conditional Breakpoint..."),
105
precondition: CONTEXT_DEBUGGERS_AVAILABLE,
106
menuOpts: {
107
menuId: MenuId.MenubarNewBreakpointMenu,
108
title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint..."),
109
group: '1_breakpoints',
110
order: 1,
111
when: CONTEXT_DEBUGGERS_AVAILABLE
112
}
113
});
114
}
115
116
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
117
const debugService = accessor.get(IDebugService);
118
119
const position = editor.getPosition();
120
if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) {
121
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)?.showBreakpointWidget(position.lineNumber, undefined, BreakpointWidgetContext.CONDITION);
122
}
123
}
124
}
125
126
class LogPointAction extends EditorAction {
127
128
constructor() {
129
super({
130
id: 'editor.debug.action.addLogPoint',
131
label: nls.localize2('logPointEditorAction', "Debug: Add Logpoint..."),
132
precondition: CONTEXT_DEBUGGERS_AVAILABLE,
133
menuOpts: [
134
{
135
menuId: MenuId.MenubarNewBreakpointMenu,
136
title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint..."),
137
group: '1_breakpoints',
138
order: 4,
139
when: CONTEXT_DEBUGGERS_AVAILABLE,
140
}
141
]
142
});
143
}
144
145
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
146
const debugService = accessor.get(IDebugService);
147
148
const position = editor.getPosition();
149
if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) {
150
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)?.showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE);
151
}
152
}
153
}
154
155
class TriggerByBreakpointAction extends EditorAction {
156
157
constructor() {
158
super({
159
id: 'editor.debug.action.triggerByBreakpoint',
160
label: nls.localize('triggerByBreakpointEditorAction', "Debug: Add Triggered Breakpoint..."),
161
precondition: CONTEXT_DEBUGGERS_AVAILABLE,
162
alias: 'Debug: Triggered Breakpoint...',
163
menuOpts: [
164
{
165
menuId: MenuId.MenubarNewBreakpointMenu,
166
title: nls.localize({ key: 'miTriggerByBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Triggered Breakpoint..."),
167
group: '1_breakpoints',
168
order: 4,
169
when: CONTEXT_DEBUGGERS_AVAILABLE,
170
}
171
]
172
});
173
}
174
175
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
176
const debugService = accessor.get(IDebugService);
177
178
const position = editor.getPosition();
179
if (position && editor.hasModel() && debugService.canSetBreakpointsIn(editor.getModel())) {
180
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)?.showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.TRIGGER_POINT);
181
}
182
}
183
}
184
185
class EditBreakpointAction extends EditorAction {
186
constructor() {
187
super({
188
id: 'editor.debug.action.editBreakpoint',
189
label: nls.localize('EditBreakpointEditorAction', "Debug: Edit Breakpoint"),
190
alias: 'Debug: Edit Existing Breakpoint',
191
precondition: CONTEXT_DEBUGGERS_AVAILABLE,
192
menuOpts: {
193
menuId: MenuId.MenubarNewBreakpointMenu,
194
title: nls.localize({ key: 'miEditBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Edit Breakpoint"),
195
group: '1_breakpoints',
196
order: 1,
197
when: CONTEXT_DEBUGGERS_AVAILABLE
198
}
199
});
200
}
201
202
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
203
const debugService = accessor.get(IDebugService);
204
205
const position = editor.getPosition();
206
const debugModel = debugService.getModel();
207
if (!(editor.hasModel() && position)) {
208
return;
209
}
210
211
const lineBreakpoints = debugModel.getBreakpoints({ lineNumber: position.lineNumber });
212
if (lineBreakpoints.length === 0) {
213
return;
214
}
215
216
const breakpointDistances = lineBreakpoints.map(b => {
217
if (!b.column) {
218
return position.column;
219
}
220
221
return Math.abs(b.column - position.column);
222
});
223
const closestBreakpointIndex = breakpointDistances.indexOf(Math.min(...breakpointDistances));
224
const closestBreakpoint = lineBreakpoints[closestBreakpointIndex];
225
226
editor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)?.showBreakpointWidget(closestBreakpoint.lineNumber, closestBreakpoint.column);
227
}
228
}
229
230
class OpenDisassemblyViewAction extends Action2 {
231
232
public static readonly ID = 'debug.action.openDisassemblyView';
233
234
constructor() {
235
super({
236
id: OpenDisassemblyViewAction.ID,
237
title: {
238
...nls.localize2('openDisassemblyView', "Open Disassembly View"),
239
mnemonicTitle: nls.localize({ key: 'miDisassemblyView', comment: ['&& denotes a mnemonic'] }, "&&DisassemblyView"),
240
},
241
precondition: CONTEXT_FOCUSED_STACK_FRAME_HAS_INSTRUCTION_POINTER_REFERENCE,
242
menu: [
243
{
244
id: MenuId.EditorContext,
245
group: 'debug',
246
order: 5,
247
when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus, CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED, CONTEXT_LANGUAGE_SUPPORTS_DISASSEMBLE_REQUEST)
248
},
249
{
250
id: MenuId.DebugCallStackContext,
251
group: 'z_commands',
252
order: 50,
253
when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), CONTEXT_CALLSTACK_ITEM_TYPE.isEqualTo('stackFrame'), CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED)
254
},
255
{
256
id: MenuId.CommandPalette,
257
when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), CONTEXT_DISASSEMBLE_REQUEST_SUPPORTED)
258
}
259
]
260
});
261
}
262
263
run(accessor: ServicesAccessor): void {
264
const editorService = accessor.get(IEditorService);
265
editorService.openEditor(DisassemblyViewInput.instance, { pinned: true, revealIfOpened: true });
266
}
267
}
268
269
class ToggleDisassemblyViewSourceCodeAction extends Action2 {
270
271
public static readonly ID = 'debug.action.toggleDisassemblyViewSourceCode';
272
public static readonly configID: string = 'debug.disassemblyView.showSourceCode';
273
274
constructor() {
275
super({
276
id: ToggleDisassemblyViewSourceCodeAction.ID,
277
title: {
278
...nls.localize2('toggleDisassemblyViewSourceCode', "Toggle Source Code in Disassembly View"),
279
mnemonicTitle: nls.localize({ key: 'mitogglesource', comment: ['&& denotes a mnemonic'] }, "&&ToggleSource"),
280
},
281
metadata: {
282
description: nls.localize2('toggleDisassemblyViewSourceCodeDescription', 'Shows or hides source code in disassembly')
283
},
284
f1: true,
285
});
286
}
287
288
run(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]): void {
289
const configService = accessor.get(IConfigurationService);
290
if (configService) {
291
const value = configService.getValue<IDebugConfiguration>('debug').disassemblyView.showSourceCode;
292
configService.updateValue(ToggleDisassemblyViewSourceCodeAction.configID, !value);
293
}
294
}
295
}
296
297
export class RunToCursorAction extends EditorAction {
298
299
public static readonly ID = 'editor.debug.action.runToCursor';
300
public static readonly LABEL: ILocalizedString = nls.localize2('runToCursor', "Run to Cursor");
301
302
constructor() {
303
super({
304
id: RunToCursorAction.ID,
305
label: RunToCursorAction.LABEL.value,
306
alias: 'Debug: Run to Cursor',
307
precondition: ContextKeyExpr.and(
308
CONTEXT_DEBUGGERS_AVAILABLE,
309
PanelFocusContext.toNegated(),
310
ContextKeyExpr.or(EditorContextKeys.editorTextFocus, CONTEXT_DISASSEMBLY_VIEW_FOCUS),
311
ChatContextKeys.inChatSession.negate()
312
),
313
contextMenuOpts: {
314
group: 'debug',
315
order: 2,
316
when: CONTEXT_IN_DEBUG_MODE
317
}
318
});
319
}
320
321
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
322
const position = editor.getPosition();
323
if (!(editor.hasModel() && position)) {
324
return;
325
}
326
const uri = editor.getModel().uri;
327
328
const debugService = accessor.get(IDebugService);
329
const viewModel = debugService.getViewModel();
330
const uriIdentityService = accessor.get(IUriIdentityService);
331
332
let column: number | undefined = undefined;
333
const focusedStackFrame = viewModel.focusedStackFrame;
334
if (focusedStackFrame && uriIdentityService.extUri.isEqual(focusedStackFrame.source.uri, uri) && focusedStackFrame.range.startLineNumber === position.lineNumber) {
335
// If the cursor is on a line different than the one the debugger is currently paused on, then send the breakpoint on the line without a column
336
// otherwise set it at the precise column #102199
337
column = position.column;
338
}
339
await debugService.runTo(uri, position.lineNumber, column);
340
}
341
}
342
343
export class SelectionToReplAction extends EditorAction {
344
345
public static readonly ID = 'editor.debug.action.selectionToRepl';
346
public static readonly LABEL: ILocalizedString = nls.localize2('evaluateInDebugConsole', "Evaluate in Debug Console");
347
348
constructor() {
349
super({
350
id: SelectionToReplAction.ID,
351
label: SelectionToReplAction.LABEL.value,
352
alias: 'Debug: Evaluate in Console',
353
precondition: ContextKeyExpr.and(
354
CONTEXT_IN_DEBUG_MODE,
355
EditorContextKeys.editorTextFocus,
356
ChatContextKeys.inChatSession.negate()),
357
contextMenuOpts: {
358
group: 'debug',
359
order: 0
360
}
361
});
362
}
363
364
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
365
const debugService = accessor.get(IDebugService);
366
const viewsService = accessor.get(IViewsService);
367
const viewModel = debugService.getViewModel();
368
const session = viewModel.focusedSession;
369
if (!editor.hasModel() || !session) {
370
return;
371
}
372
373
const selection = editor.getSelection();
374
let text: string;
375
if (selection.isEmpty()) {
376
text = editor.getModel().getLineContent(selection.selectionStartLineNumber).trim();
377
} else {
378
text = editor.getModel().getValueInRange(selection);
379
}
380
381
const replView = await viewsService.openView(REPL_VIEW_ID, false) as Repl | undefined;
382
replView?.sendReplInput(text);
383
}
384
}
385
386
export class SelectionToWatchExpressionsAction extends EditorAction {
387
388
public static readonly ID = 'editor.debug.action.selectionToWatch';
389
public static readonly LABEL: ILocalizedString = nls.localize2('addToWatch', "Add to Watch");
390
391
constructor() {
392
super({
393
id: SelectionToWatchExpressionsAction.ID,
394
label: SelectionToWatchExpressionsAction.LABEL.value,
395
alias: 'Debug: Add to Watch',
396
precondition: ContextKeyExpr.and(
397
CONTEXT_IN_DEBUG_MODE,
398
EditorContextKeys.editorTextFocus,
399
ChatContextKeys.inChatSession.negate()),
400
contextMenuOpts: {
401
group: 'debug',
402
order: 1
403
}
404
});
405
}
406
407
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
408
const debugService = accessor.get(IDebugService);
409
const viewsService = accessor.get(IViewsService);
410
const languageFeaturesService = accessor.get(ILanguageFeaturesService);
411
if (!editor.hasModel()) {
412
return;
413
}
414
415
let expression: string | undefined = undefined;
416
417
const model = editor.getModel();
418
const selection = editor.getSelection();
419
420
if (!selection.isEmpty()) {
421
expression = model.getValueInRange(selection);
422
} else {
423
const position = editor.getPosition();
424
const evaluatableExpression = await getEvaluatableExpressionAtPosition(languageFeaturesService, model, position);
425
if (!evaluatableExpression) {
426
return;
427
}
428
expression = evaluatableExpression.matchingExpression;
429
}
430
431
if (!expression) {
432
return;
433
}
434
435
await viewsService.openView(WATCH_VIEW_ID);
436
debugService.addWatchExpression(expression);
437
}
438
}
439
440
class ShowDebugHoverAction extends EditorAction {
441
442
constructor() {
443
super({
444
id: 'editor.debug.action.showDebugHover',
445
label: nls.localize2('showDebugHover', "Debug: Show Hover"),
446
precondition: CONTEXT_IN_DEBUG_MODE,
447
kbOpts: {
448
kbExpr: EditorContextKeys.editorTextFocus,
449
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyI),
450
weight: KeybindingWeight.EditorContrib
451
}
452
});
453
}
454
455
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
456
const position = editor.getPosition();
457
if (!position || !editor.hasModel()) {
458
return;
459
}
460
461
return editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID)?.showHover(position, true);
462
}
463
}
464
465
const NO_TARGETS_MESSAGE = nls.localize('editor.debug.action.stepIntoTargets.notAvailable', "Step targets are not available here");
466
467
class StepIntoTargetsAction extends EditorAction {
468
469
public static readonly ID = 'editor.debug.action.stepIntoTargets';
470
public static readonly LABEL = nls.localize({ key: 'stepIntoTargets', comment: ['Step Into Targets lets the user step into an exact function he or she is interested in.'] }, "Step Into Target");
471
472
constructor() {
473
super({
474
id: StepIntoTargetsAction.ID,
475
label: StepIntoTargetsAction.LABEL,
476
alias: 'Debug: Step Into Target',
477
precondition: ContextKeyExpr.and(CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus),
478
contextMenuOpts: {
479
group: 'debug',
480
order: 1.5
481
}
482
});
483
}
484
485
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
486
const debugService = accessor.get(IDebugService);
487
const contextMenuService = accessor.get(IContextMenuService);
488
const uriIdentityService = accessor.get(IUriIdentityService);
489
const session = debugService.getViewModel().focusedSession;
490
const frame = debugService.getViewModel().focusedStackFrame;
491
const selection = editor.getSelection();
492
493
const targetPosition = selection?.getPosition() || (frame && { lineNumber: frame.range.startLineNumber, column: frame.range.startColumn });
494
495
if (!session || !frame || !editor.hasModel() || !uriIdentityService.extUri.isEqual(editor.getModel().uri, frame.source.uri)) {
496
if (targetPosition) {
497
MessageController.get(editor)?.showMessage(NO_TARGETS_MESSAGE, targetPosition);
498
}
499
return;
500
}
501
502
503
const targets = await session.stepInTargets(frame.frameId);
504
if (!targets?.length) {
505
MessageController.get(editor)?.showMessage(NO_TARGETS_MESSAGE, targetPosition!);
506
return;
507
}
508
509
// If there is a selection, try to find the best target with a position to step into.
510
if (selection) {
511
const positionalTargets: { start: Position; end?: Position; target: DebugProtocol.StepInTarget }[] = [];
512
for (const target of targets) {
513
if (target.line) {
514
positionalTargets.push({
515
start: new Position(target.line, target.column || 1),
516
end: target.endLine ? new Position(target.endLine, target.endColumn || 1) : undefined,
517
target
518
});
519
}
520
}
521
522
positionalTargets.sort((a, b) => b.start.lineNumber - a.start.lineNumber || b.start.column - a.start.column);
523
524
const needle = selection.getPosition();
525
526
// Try to find a target with a start and end that is around the cursor
527
// position. Or, if none, whatever is before the cursor.
528
const best = positionalTargets.find(t => t.end && needle.isBefore(t.end) && t.start.isBeforeOrEqual(needle)) || positionalTargets.find(t => t.end === undefined && t.start.isBeforeOrEqual(needle));
529
if (best) {
530
session.stepIn(frame.thread.threadId, best.target.id);
531
return;
532
}
533
}
534
535
// Otherwise, show a context menu and have the user pick a target
536
editor.revealLineInCenterIfOutsideViewport(frame.range.startLineNumber);
537
const cursorCoords = editor.getScrolledVisiblePosition(targetPosition!);
538
const editorCoords = getDomNodePagePosition(editor.getDomNode());
539
const x = editorCoords.left + cursorCoords.left;
540
const y = editorCoords.top + cursorCoords.top + cursorCoords.height;
541
542
contextMenuService.showContextMenu({
543
getAnchor: () => ({ x, y }),
544
getActions: () => {
545
return targets.map(t => new Action(`stepIntoTarget:${t.id}`, t.label, undefined, true, () => session.stepIn(frame.thread.threadId, t.id)));
546
}
547
});
548
}
549
}
550
551
class GoToBreakpointAction extends EditorAction {
552
constructor(private isNext: boolean, opts: IActionOptions) {
553
super(opts);
554
}
555
556
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<any> {
557
const debugService = accessor.get(IDebugService);
558
const editorService = accessor.get(IEditorService);
559
const uriIdentityService = accessor.get(IUriIdentityService);
560
561
if (editor.hasModel()) {
562
const currentUri = editor.getModel().uri;
563
const currentLine = editor.getPosition().lineNumber;
564
//Breakpoints returned from `getBreakpoints` are already sorted.
565
const allEnabledBreakpoints = debugService.getModel().getBreakpoints({ enabledOnly: true });
566
567
//Try to find breakpoint in current file
568
let moveBreakpoint =
569
this.isNext
570
? allEnabledBreakpoints.filter(bp => uriIdentityService.extUri.isEqual(bp.uri, currentUri) && bp.lineNumber > currentLine).shift()
571
: allEnabledBreakpoints.filter(bp => uriIdentityService.extUri.isEqual(bp.uri, currentUri) && bp.lineNumber < currentLine).pop();
572
573
//Try to find breakpoints in following files
574
if (!moveBreakpoint) {
575
moveBreakpoint =
576
this.isNext
577
? allEnabledBreakpoints.filter(bp => bp.uri.toString() > currentUri.toString()).shift()
578
: allEnabledBreakpoints.filter(bp => bp.uri.toString() < currentUri.toString()).pop();
579
}
580
581
//Move to first or last possible breakpoint
582
if (!moveBreakpoint && allEnabledBreakpoints.length) {
583
moveBreakpoint = this.isNext ? allEnabledBreakpoints[0] : allEnabledBreakpoints[allEnabledBreakpoints.length - 1];
584
}
585
586
if (moveBreakpoint) {
587
return openBreakpointSource(moveBreakpoint, false, true, false, debugService, editorService);
588
}
589
}
590
}
591
}
592
593
class GoToNextBreakpointAction extends GoToBreakpointAction {
594
constructor() {
595
super(true, {
596
id: 'editor.debug.action.goToNextBreakpoint',
597
label: nls.localize2('goToNextBreakpoint', "Debug: Go to Next Breakpoint"),
598
precondition: CONTEXT_DEBUGGERS_AVAILABLE
599
});
600
}
601
}
602
603
class GoToPreviousBreakpointAction extends GoToBreakpointAction {
604
constructor() {
605
super(false, {
606
id: 'editor.debug.action.goToPreviousBreakpoint',
607
label: nls.localize2('goToPreviousBreakpoint', "Debug: Go to Previous Breakpoint"),
608
precondition: CONTEXT_DEBUGGERS_AVAILABLE
609
});
610
}
611
}
612
613
class CloseExceptionWidgetAction extends EditorAction {
614
615
constructor() {
616
super({
617
id: 'editor.debug.action.closeExceptionWidget',
618
label: nls.localize2('closeExceptionWidget', "Close Exception Widget"),
619
precondition: CONTEXT_EXCEPTION_WIDGET_VISIBLE,
620
kbOpts: {
621
primary: KeyCode.Escape,
622
weight: KeybindingWeight.EditorContrib
623
}
624
});
625
}
626
627
async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
628
const contribution = editor.getContribution<IDebugEditorContribution>(EDITOR_CONTRIBUTION_ID);
629
contribution?.closeExceptionWidget();
630
}
631
}
632
633
registerAction2(OpenDisassemblyViewAction);
634
registerAction2(ToggleDisassemblyViewSourceCodeAction);
635
registerAction2(ToggleBreakpointAction);
636
registerEditorAction(ConditionalBreakpointAction);
637
registerEditorAction(LogPointAction);
638
registerEditorAction(TriggerByBreakpointAction);
639
registerEditorAction(EditBreakpointAction);
640
registerEditorAction(RunToCursorAction);
641
registerEditorAction(StepIntoTargetsAction);
642
registerEditorAction(SelectionToReplAction);
643
registerEditorAction(SelectionToWatchExpressionsAction);
644
registerEditorAction(ShowDebugHoverAction);
645
registerEditorAction(GoToNextBreakpointAction);
646
registerEditorAction(GoToPreviousBreakpointAction);
647
registerEditorAction(CloseExceptionWidgetAction);
648
649