Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/browser/breakpointsView.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 * as dom from '../../../../base/browser/dom.js';
7
import { IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';
8
import { Gesture } from '../../../../base/browser/touch.js';
9
import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js';
10
import { AriaRole } from '../../../../base/browser/ui/aria/aria.js';
11
import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js';
12
import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js';
13
import { InputBox } from '../../../../base/browser/ui/inputbox/inputBox.js';
14
import { IListContextMenuEvent, IListRenderer, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';
15
import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js';
16
import { Orientation } from '../../../../base/browser/ui/splitview/splitview.js';
17
import { Action } from '../../../../base/common/actions.js';
18
import { equals } from '../../../../base/common/arrays.js';
19
import { RunOnceScheduler } from '../../../../base/common/async.js';
20
import { Codicon } from '../../../../base/common/codicons.js';
21
import { MarkdownString } from '../../../../base/common/htmlContent.js';
22
import { KeyCode } from '../../../../base/common/keyCodes.js';
23
import { DisposableStore, dispose } from '../../../../base/common/lifecycle.js';
24
import * as resources from '../../../../base/common/resources.js';
25
import { ThemeIcon } from '../../../../base/common/themables.js';
26
import { Constants } from '../../../../base/common/uint.js';
27
import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js';
28
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
29
import { ILanguageService } from '../../../../editor/common/languages/language.js';
30
import { localize, localize2 } from '../../../../nls.js';
31
import { getActionBarActions, getContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
32
import { Action2, IMenu, IMenuService, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
33
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
34
import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
35
import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js';
36
import { TextEditorSelectionRevealType } from '../../../../platform/editor/common/editor.js';
37
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
38
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
39
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
40
import { ILabelService } from '../../../../platform/label/common/label.js';
41
import { WorkbenchList } from '../../../../platform/list/browser/listService.js';
42
import { INotificationService } from '../../../../platform/notification/common/notification.js';
43
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
44
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js';
45
import { defaultInputBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js';
46
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
47
import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js';
48
import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js';
49
import { IEditorPane } from '../../../common/editor.js';
50
import { IViewDescriptorService } from '../../../common/views.js';
51
import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js';
52
import { IViewsService } from '../../../services/views/common/viewsService.js';
53
import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_HAS_MODES, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED, DEBUG_SCHEME, DataBreakpointSetType, DataBreakpointSource, DebuggerString, IBaseBreakpoint, IBreakpoint, IBreakpointEditorContribution, IBreakpointUpdateData, IDataBreakpoint, IDataBreakpointInfoResponse, IDebugModel, IDebugService, IEnablement, IExceptionBreakpoint, IFunctionBreakpoint, IInstructionBreakpoint, State } from '../common/debug.js';
54
import { Breakpoint, DataBreakpoint, ExceptionBreakpoint, FunctionBreakpoint, InstructionBreakpoint } from '../common/debugModel.js';
55
import { DisassemblyViewInput } from '../common/disassemblyViewInput.js';
56
import * as icons from './debugIcons.js';
57
import { DisassemblyView } from './disassemblyView.js';
58
59
const $ = dom.$;
60
61
function createCheckbox(disposables: DisposableStore): HTMLInputElement {
62
const checkbox = <HTMLInputElement>$('input');
63
checkbox.type = 'checkbox';
64
checkbox.tabIndex = -1;
65
disposables.add(Gesture.ignoreTarget(checkbox));
66
67
return checkbox;
68
}
69
70
const MAX_VISIBLE_BREAKPOINTS = 9;
71
export function getExpandedBodySize(model: IDebugModel, sessionId: string | undefined, countLimit: number): number {
72
const length = model.getBreakpoints().length + model.getExceptionBreakpointsForSession(sessionId).length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length + model.getInstructionBreakpoints().length;
73
return Math.min(countLimit, length) * 22;
74
}
75
type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint | IInstructionBreakpoint;
76
77
interface InputBoxData {
78
breakpoint: IFunctionBreakpoint | IExceptionBreakpoint | IDataBreakpoint;
79
type: 'condition' | 'hitCount' | 'name';
80
}
81
82
function getModeKindForBreakpoint(breakpoint: IBreakpoint) {
83
const kind = breakpoint instanceof Breakpoint ? 'source' : breakpoint instanceof InstructionBreakpoint ? 'instruction' : 'exception';
84
return kind;
85
}
86
87
export class BreakpointsView extends ViewPane {
88
89
private list!: WorkbenchList<BreakpointItem>;
90
private needsRefresh = false;
91
private needsStateChange = false;
92
private ignoreLayout = false;
93
private menu: IMenu;
94
private breakpointItemType: IContextKey<string | undefined>;
95
private breakpointIsDataBytes: IContextKey<boolean | undefined>;
96
private breakpointHasMultipleModes: IContextKey<boolean>;
97
private breakpointSupportsCondition: IContextKey<boolean>;
98
private _inputBoxData: InputBoxData | undefined;
99
breakpointInputFocused: IContextKey<boolean>;
100
private autoFocusedIndex = -1;
101
102
private hintContainer: IconLabel | undefined;
103
private hintDelayer: RunOnceScheduler;
104
105
constructor(
106
options: IViewletViewOptions,
107
@IContextMenuService contextMenuService: IContextMenuService,
108
@IDebugService private readonly debugService: IDebugService,
109
@IKeybindingService keybindingService: IKeybindingService,
110
@IInstantiationService instantiationService: IInstantiationService,
111
@IThemeService themeService: IThemeService,
112
@IEditorService private readonly editorService: IEditorService,
113
@IContextViewService private readonly contextViewService: IContextViewService,
114
@IConfigurationService configurationService: IConfigurationService,
115
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
116
@IContextKeyService contextKeyService: IContextKeyService,
117
@IOpenerService openerService: IOpenerService,
118
@ILabelService private readonly labelService: ILabelService,
119
@IMenuService menuService: IMenuService,
120
@IHoverService hoverService: IHoverService,
121
@ILanguageService private readonly languageService: ILanguageService,
122
) {
123
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService);
124
125
this.menu = menuService.createMenu(MenuId.DebugBreakpointsContext, contextKeyService);
126
this._register(this.menu);
127
this.breakpointItemType = CONTEXT_BREAKPOINT_ITEM_TYPE.bindTo(contextKeyService);
128
this.breakpointIsDataBytes = CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES.bindTo(contextKeyService);
129
this.breakpointHasMultipleModes = CONTEXT_BREAKPOINT_HAS_MODES.bindTo(contextKeyService);
130
this.breakpointSupportsCondition = CONTEXT_BREAKPOINT_SUPPORTS_CONDITION.bindTo(contextKeyService);
131
this.breakpointInputFocused = CONTEXT_BREAKPOINT_INPUT_FOCUSED.bindTo(contextKeyService);
132
this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange()));
133
this._register(this.debugService.getViewModel().onDidFocusSession(() => this.onBreakpointsChange()));
134
this._register(this.debugService.onDidChangeState(() => this.onStateChange()));
135
this.hintDelayer = this._register(new RunOnceScheduler(() => this.updateBreakpointsHint(true), 4000));
136
}
137
138
protected override renderBody(container: HTMLElement): void {
139
super.renderBody(container);
140
141
this.element.classList.add('debug-pane');
142
container.classList.add('debug-breakpoints');
143
const delegate = new BreakpointsDelegate(this);
144
145
this.list = this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [
146
this.instantiationService.createInstance(BreakpointsRenderer, this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType),
147
new ExceptionBreakpointsRenderer(this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType, this.debugService, this.hoverService),
148
new ExceptionBreakpointInputRenderer(this, this.debugService, this.contextViewService),
149
this.instantiationService.createInstance(FunctionBreakpointsRenderer, this.menu, this.breakpointSupportsCondition, this.breakpointItemType),
150
new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.hoverService, this.labelService),
151
this.instantiationService.createInstance(DataBreakpointsRenderer, this.menu, this.breakpointHasMultipleModes, this.breakpointSupportsCondition, this.breakpointItemType, this.breakpointIsDataBytes),
152
new DataBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.hoverService, this.labelService),
153
this.instantiationService.createInstance(InstructionBreakpointsRenderer),
154
], {
155
identityProvider: { getId: (element: IEnablement) => element.getId() },
156
multipleSelectionSupport: false,
157
keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: (e: IEnablement) => e },
158
accessibilityProvider: new BreakpointsAccessibilityProvider(this.debugService, this.labelService),
159
overrideStyles: this.getLocationBasedColors().listOverrideStyles
160
}) as WorkbenchList<BreakpointItem>;
161
162
CONTEXT_BREAKPOINTS_FOCUSED.bindTo(this.list.contextKeyService);
163
164
this._register(this.list.onContextMenu(this.onListContextMenu, this));
165
166
this._register(this.list.onMouseMiddleClick(async ({ element }) => {
167
if (element instanceof Breakpoint) {
168
await this.debugService.removeBreakpoints(element.getId());
169
} else if (element instanceof FunctionBreakpoint) {
170
await this.debugService.removeFunctionBreakpoints(element.getId());
171
} else if (element instanceof DataBreakpoint) {
172
await this.debugService.removeDataBreakpoints(element.getId());
173
} else if (element instanceof InstructionBreakpoint) {
174
await this.debugService.removeInstructionBreakpoints(element.instructionReference, element.offset);
175
}
176
}));
177
178
this._register(this.list.onDidOpen(async e => {
179
if (!e.element) {
180
return;
181
}
182
183
if (dom.isMouseEvent(e.browserEvent) && e.browserEvent.button === 1) { // middle click
184
return;
185
}
186
187
if (e.element instanceof Breakpoint) {
188
openBreakpointSource(e.element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService);
189
}
190
if (e.element instanceof InstructionBreakpoint) {
191
const disassemblyView = await this.editorService.openEditor(DisassemblyViewInput.instance);
192
// Focus on double click
193
(disassemblyView as DisassemblyView).goToInstructionAndOffset(e.element.instructionReference, e.element.offset, dom.isMouseEvent(e.browserEvent) && e.browserEvent.detail === 2);
194
}
195
if (dom.isMouseEvent(e.browserEvent) && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.inputBoxData?.breakpoint) {
196
// double click
197
this.renderInputBox({ breakpoint: e.element, type: 'name' });
198
}
199
}));
200
201
this.list.splice(0, this.list.length, this.elements);
202
203
this._register(this.onDidChangeBodyVisibility(visible => {
204
if (visible) {
205
if (this.needsRefresh) {
206
this.onBreakpointsChange();
207
}
208
209
if (this.needsStateChange) {
210
this.onStateChange();
211
}
212
}
213
}));
214
215
const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!)!;
216
this._register(containerModel.onDidChangeAllViewDescriptors(() => {
217
this.updateSize();
218
}));
219
}
220
221
protected override renderHeaderTitle(container: HTMLElement, title: string): void {
222
super.renderHeaderTitle(container, title);
223
224
const iconLabelContainer = dom.append(container, $('span.breakpoint-warning'));
225
this.hintContainer = this._register(new IconLabel(iconLabelContainer, {
226
supportIcons: true, hoverDelegate: {
227
showHover: (options, focus?) => this.hoverService.showInstantHover({ content: options.content, target: this.hintContainer!.element }, focus),
228
delay: <number>this.configurationService.getValue('workbench.hover.delay')
229
}
230
}));
231
dom.hide(this.hintContainer.element);
232
}
233
234
override focus(): void {
235
super.focus();
236
this.list?.domFocus();
237
}
238
239
renderInputBox(data: InputBoxData | undefined): void {
240
this._inputBoxData = data;
241
this.onBreakpointsChange();
242
this._inputBoxData = undefined;
243
}
244
245
get inputBoxData(): InputBoxData | undefined {
246
return this._inputBoxData;
247
}
248
249
protected override layoutBody(height: number, width: number): void {
250
if (this.ignoreLayout) {
251
return;
252
}
253
254
super.layoutBody(height, width);
255
this.list?.layout(height, width);
256
try {
257
this.ignoreLayout = true;
258
this.updateSize();
259
} finally {
260
this.ignoreLayout = false;
261
}
262
}
263
264
private onListContextMenu(e: IListContextMenuEvent<IEnablement>): void {
265
const element = e.element;
266
const type = element instanceof Breakpoint ? 'breakpoint' : element instanceof ExceptionBreakpoint ? 'exceptionBreakpoint' :
267
element instanceof FunctionBreakpoint ? 'functionBreakpoint' : element instanceof DataBreakpoint ? 'dataBreakpoint' :
268
element instanceof InstructionBreakpoint ? 'instructionBreakpoint' : undefined;
269
this.breakpointItemType.set(type);
270
const session = this.debugService.getViewModel().focusedSession;
271
const conditionSupported = element instanceof ExceptionBreakpoint ? element.supportsCondition : (!session || !!session.capabilities.supportsConditionalBreakpoints);
272
this.breakpointSupportsCondition.set(conditionSupported);
273
this.breakpointIsDataBytes.set(element instanceof DataBreakpoint && element.src.type === DataBreakpointSetType.Address);
274
this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes(getModeKindForBreakpoint(element as IBreakpoint)).length > 1);
275
276
const { secondary } = getContextMenuActions(this.menu.getActions({ arg: e.element, shouldForwardArgs: false }), 'inline');
277
278
this.contextMenuService.showContextMenu({
279
getAnchor: () => e.anchor,
280
getActions: () => secondary,
281
getActionsContext: () => element
282
});
283
}
284
285
private updateSize(): void {
286
const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!)!;
287
288
// Adjust expanded body size
289
const sessionId = this.debugService.getViewModel().focusedSession?.getId();
290
this.minimumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel(), sessionId, MAX_VISIBLE_BREAKPOINTS) : 170;
291
this.maximumBodySize = this.orientation === Orientation.VERTICAL && containerModel.visibleViewDescriptors.length > 1 ? getExpandedBodySize(this.debugService.getModel(), sessionId, Number.POSITIVE_INFINITY) : Number.POSITIVE_INFINITY;
292
}
293
294
private updateBreakpointsHint(delayed = false): void {
295
if (!this.hintContainer) {
296
return;
297
}
298
299
const currentType = this.debugService.getViewModel().focusedSession?.configuration.type;
300
const dbg = currentType ? this.debugService.getAdapterManager().getDebugger(currentType) : undefined;
301
const message = dbg?.strings?.[DebuggerString.UnverifiedBreakpoints];
302
const debuggerHasUnverifiedBps = message && this.debugService.getModel().getBreakpoints().filter(bp => {
303
if (bp.verified || !bp.enabled) {
304
return false;
305
}
306
307
const langId = this.languageService.guessLanguageIdByFilepathOrFirstLine(bp.uri);
308
return langId && dbg.interestedInLanguage(langId);
309
});
310
311
if (message && debuggerHasUnverifiedBps?.length && this.debugService.getModel().areBreakpointsActivated()) {
312
if (delayed) {
313
const mdown = new MarkdownString(undefined, { isTrusted: true }).appendMarkdown(message);
314
this.hintContainer.setLabel('$(warning)', undefined, { title: { markdown: mdown, markdownNotSupportedFallback: message } });
315
dom.show(this.hintContainer.element);
316
} else {
317
this.hintDelayer.schedule();
318
}
319
} else {
320
dom.hide(this.hintContainer.element);
321
}
322
}
323
324
private onBreakpointsChange(): void {
325
if (this.isBodyVisible()) {
326
this.updateSize();
327
if (this.list) {
328
const lastFocusIndex = this.list.getFocus()[0];
329
// Check whether focused element was removed
330
const needsRefocus = lastFocusIndex && !this.elements.includes(this.list.element(lastFocusIndex));
331
this.list.splice(0, this.list.length, this.elements);
332
this.needsRefresh = false;
333
if (needsRefocus) {
334
this.list.focusNth(Math.min(lastFocusIndex, this.list.length - 1));
335
}
336
}
337
this.updateBreakpointsHint();
338
} else {
339
this.needsRefresh = true;
340
}
341
}
342
343
private onStateChange(): void {
344
if (this.isBodyVisible()) {
345
this.needsStateChange = false;
346
const thread = this.debugService.getViewModel().focusedThread;
347
let found = false;
348
if (thread && thread.stoppedDetails && thread.stoppedDetails.hitBreakpointIds && thread.stoppedDetails.hitBreakpointIds.length > 0) {
349
const hitBreakpointIds = thread.stoppedDetails.hitBreakpointIds;
350
const elements = this.elements;
351
const index = elements.findIndex(e => {
352
const id = e.getIdFromAdapter(thread.session.getId());
353
return typeof id === 'number' && hitBreakpointIds.indexOf(id) !== -1;
354
});
355
if (index >= 0) {
356
this.list.setFocus([index]);
357
this.list.setSelection([index]);
358
found = true;
359
this.autoFocusedIndex = index;
360
}
361
}
362
if (!found) {
363
// Deselect breakpoint in breakpoint view when no longer stopped on it #125528
364
const focus = this.list.getFocus();
365
const selection = this.list.getSelection();
366
if (this.autoFocusedIndex >= 0 && equals(focus, selection) && focus.indexOf(this.autoFocusedIndex) >= 0) {
367
this.list.setFocus([]);
368
this.list.setSelection([]);
369
}
370
this.autoFocusedIndex = -1;
371
}
372
this.updateBreakpointsHint();
373
} else {
374
this.needsStateChange = true;
375
}
376
}
377
378
private get elements(): BreakpointItem[] {
379
const model = this.debugService.getModel();
380
const sessionId = this.debugService.getViewModel().focusedSession?.getId();
381
const elements = (<ReadonlyArray<IEnablement>>model.getExceptionBreakpointsForSession(sessionId)).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()).concat(model.getInstructionBreakpoints());
382
383
return elements as BreakpointItem[];
384
}
385
}
386
387
class BreakpointsDelegate implements IListVirtualDelegate<BreakpointItem> {
388
389
constructor(private view: BreakpointsView) {
390
// noop
391
}
392
393
getHeight(_element: BreakpointItem): number {
394
return 22;
395
}
396
397
getTemplateId(element: BreakpointItem): string {
398
if (element instanceof Breakpoint) {
399
return BreakpointsRenderer.ID;
400
}
401
if (element instanceof FunctionBreakpoint) {
402
const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint;
403
if (!element.name || (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId())) {
404
return FunctionBreakpointInputRenderer.ID;
405
}
406
407
return FunctionBreakpointsRenderer.ID;
408
}
409
if (element instanceof ExceptionBreakpoint) {
410
const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint;
411
if (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId()) {
412
return ExceptionBreakpointInputRenderer.ID;
413
}
414
return ExceptionBreakpointsRenderer.ID;
415
}
416
if (element instanceof DataBreakpoint) {
417
const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint;
418
if (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId()) {
419
return DataBreakpointInputRenderer.ID;
420
}
421
422
return DataBreakpointsRenderer.ID;
423
}
424
if (element instanceof InstructionBreakpoint) {
425
return InstructionBreakpointsRenderer.ID;
426
}
427
428
return '';
429
}
430
}
431
432
interface IBaseBreakpointTemplateData {
433
breakpoint: HTMLElement;
434
name: HTMLElement;
435
checkbox: HTMLInputElement;
436
context: BreakpointItem;
437
actionBar: ActionBar;
438
templateDisposables: DisposableStore;
439
elementDisposables: DisposableStore;
440
badge: HTMLElement;
441
}
442
443
interface IBaseBreakpointWithIconTemplateData extends IBaseBreakpointTemplateData {
444
icon: HTMLElement;
445
}
446
447
interface IBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
448
filePath: HTMLElement;
449
}
450
451
interface IExceptionBreakpointTemplateData extends IBaseBreakpointTemplateData {
452
condition: HTMLElement;
453
}
454
455
interface IFunctionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
456
condition: HTMLElement;
457
}
458
459
interface IDataBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
460
accessType: HTMLElement;
461
condition: HTMLElement;
462
}
463
464
interface IInstructionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData {
465
address: HTMLElement;
466
}
467
468
interface IFunctionBreakpointInputTemplateData {
469
inputBox: InputBox;
470
checkbox: HTMLInputElement;
471
icon: HTMLElement;
472
breakpoint: IFunctionBreakpoint;
473
templateDisposables: DisposableStore;
474
elementDisposables: DisposableStore;
475
type: 'hitCount' | 'condition' | 'name';
476
updating?: boolean;
477
}
478
479
interface IDataBreakpointInputTemplateData {
480
inputBox: InputBox;
481
checkbox: HTMLInputElement;
482
icon: HTMLElement;
483
breakpoint: IDataBreakpoint;
484
elementDisposables: DisposableStore;
485
templateDisposables: DisposableStore;
486
type: 'hitCount' | 'condition' | 'name';
487
updating?: boolean;
488
}
489
490
interface IExceptionBreakpointInputTemplateData {
491
inputBox: InputBox;
492
checkbox: HTMLInputElement;
493
currentBreakpoint?: IExceptionBreakpoint;
494
templateDisposables: DisposableStore;
495
elementDisposables: DisposableStore;
496
}
497
498
const breakpointIdToActionBarDomeNode = new Map<string, HTMLElement>();
499
class BreakpointsRenderer implements IListRenderer<IBreakpoint, IBreakpointTemplateData> {
500
501
constructor(
502
private menu: IMenu,
503
private breakpointHasMultipleModes: IContextKey<boolean>,
504
private breakpointSupportsCondition: IContextKey<boolean>,
505
private breakpointItemType: IContextKey<string | undefined>,
506
@IDebugService private readonly debugService: IDebugService,
507
@IHoverService private readonly hoverService: IHoverService,
508
@ILabelService private readonly labelService: ILabelService
509
) {
510
// noop
511
}
512
513
static readonly ID = 'breakpoints';
514
515
get templateId() {
516
return BreakpointsRenderer.ID;
517
}
518
519
renderTemplate(container: HTMLElement): IBreakpointTemplateData {
520
const data: IBreakpointTemplateData = Object.create(null);
521
data.elementDisposables = new DisposableStore();
522
data.templateDisposables = new DisposableStore();
523
data.templateDisposables.add(data.elementDisposables);
524
525
data.breakpoint = dom.append(container, $('.breakpoint'));
526
527
data.icon = $('.icon');
528
data.checkbox = createCheckbox(data.templateDisposables);
529
530
data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
531
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
532
}));
533
534
dom.append(data.breakpoint, data.icon);
535
dom.append(data.breakpoint, data.checkbox);
536
537
data.name = dom.append(data.breakpoint, $('span.name'));
538
539
data.filePath = dom.append(data.breakpoint, $('span.file-path'));
540
data.actionBar = new ActionBar(data.breakpoint);
541
data.templateDisposables.add(data.actionBar);
542
const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));
543
data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));
544
545
return data;
546
}
547
548
renderElement(breakpoint: IBreakpoint, index: number, data: IBreakpointTemplateData): void {
549
data.context = breakpoint;
550
data.breakpoint.classList.toggle('disabled', !this.debugService.getModel().areBreakpointsActivated());
551
552
data.name.textContent = resources.basenameOrAuthority(breakpoint.uri);
553
let badgeContent = breakpoint.lineNumber.toString();
554
if (breakpoint.column) {
555
badgeContent += `:${breakpoint.column}`;
556
}
557
if (breakpoint.modeLabel) {
558
badgeContent = `${breakpoint.modeLabel}: ${badgeContent}`;
559
}
560
data.badge.textContent = badgeContent;
561
data.filePath.textContent = this.labelService.getUriLabel(resources.dirname(breakpoint.uri), { relative: true });
562
data.checkbox.checked = breakpoint.enabled;
563
564
const { message, icon } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService, this.debugService.getModel());
565
data.icon.className = ThemeIcon.asClassName(icon);
566
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, breakpoint.message || message || ''));
567
568
const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
569
if (debugActive && !breakpoint.verified) {
570
data.breakpoint.classList.add('disabled');
571
}
572
573
const session = this.debugService.getViewModel().focusedSession;
574
this.breakpointSupportsCondition.set(!session || !!session.capabilities.supportsConditionalBreakpoints);
575
this.breakpointItemType.set('breakpoint');
576
this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes('source').length > 1);
577
const { primary } = getActionBarActions(this.menu.getActions({ arg: breakpoint, shouldForwardArgs: true }), 'inline');
578
data.actionBar.clear();
579
data.actionBar.push(primary, { icon: true, label: false });
580
breakpointIdToActionBarDomeNode.set(breakpoint.getId(), data.actionBar.domNode);
581
}
582
583
584
585
disposeElement(a: any, index: number, template: IBreakpointTemplateData): void {
586
template.elementDisposables.clear();
587
}
588
589
disposeTemplate(templateData: IBreakpointTemplateData): void {
590
templateData.templateDisposables.dispose();
591
}
592
}
593
594
class ExceptionBreakpointsRenderer implements IListRenderer<IExceptionBreakpoint, IExceptionBreakpointTemplateData> {
595
596
constructor(
597
private menu: IMenu,
598
private breakpointHasMultipleModes: IContextKey<boolean>,
599
private breakpointSupportsCondition: IContextKey<boolean>,
600
private breakpointItemType: IContextKey<string | undefined>,
601
private debugService: IDebugService,
602
private readonly hoverService: IHoverService,
603
) {
604
// noop
605
}
606
607
static readonly ID = 'exceptionbreakpoints';
608
609
get templateId() {
610
return ExceptionBreakpointsRenderer.ID;
611
}
612
613
renderTemplate(container: HTMLElement): IExceptionBreakpointTemplateData {
614
const data: IExceptionBreakpointTemplateData = Object.create(null);
615
data.elementDisposables = new DisposableStore();
616
data.templateDisposables = new DisposableStore();
617
data.templateDisposables.add(data.elementDisposables);
618
data.breakpoint = dom.append(container, $('.breakpoint'));
619
620
data.checkbox = createCheckbox(data.templateDisposables);
621
data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
622
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
623
}));
624
625
dom.append(data.breakpoint, data.checkbox);
626
627
data.name = dom.append(data.breakpoint, $('span.name'));
628
data.condition = dom.append(data.breakpoint, $('span.condition'));
629
data.breakpoint.classList.add('exception');
630
631
data.actionBar = new ActionBar(data.breakpoint);
632
data.templateDisposables.add(data.actionBar);
633
const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));
634
data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));
635
636
return data;
637
}
638
639
renderElement(exceptionBreakpoint: IExceptionBreakpoint, index: number, data: IExceptionBreakpointTemplateData): void {
640
data.context = exceptionBreakpoint;
641
data.name.textContent = exceptionBreakpoint.label || `${exceptionBreakpoint.filter} exceptions`;
642
const exceptionBreakpointtitle = exceptionBreakpoint.verified ? (exceptionBreakpoint.description || data.name.textContent) : exceptionBreakpoint.message || localize('unverifiedExceptionBreakpoint', "Unverified Exception Breakpoint");
643
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, exceptionBreakpointtitle));
644
data.breakpoint.classList.toggle('disabled', !exceptionBreakpoint.verified);
645
data.checkbox.checked = exceptionBreakpoint.enabled;
646
data.condition.textContent = exceptionBreakpoint.condition || '';
647
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.condition, localize('expressionCondition', "Expression condition: {0}", exceptionBreakpoint.condition)));
648
649
if (exceptionBreakpoint.modeLabel) {
650
data.badge.textContent = exceptionBreakpoint.modeLabel;
651
data.badge.style.display = 'block';
652
} else {
653
data.badge.style.display = 'none';
654
}
655
656
this.breakpointSupportsCondition.set((exceptionBreakpoint as ExceptionBreakpoint).supportsCondition);
657
this.breakpointItemType.set('exceptionBreakpoint');
658
this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes('exception').length > 1);
659
const { primary } = getActionBarActions(this.menu.getActions({ arg: exceptionBreakpoint, shouldForwardArgs: true }), 'inline');
660
data.actionBar.clear();
661
data.actionBar.push(primary, { icon: true, label: false });
662
breakpointIdToActionBarDomeNode.set(exceptionBreakpoint.getId(), data.actionBar.domNode);
663
}
664
665
disposeElement(element: IExceptionBreakpoint, index: number, templateData: IExceptionBreakpointTemplateData): void {
666
templateData.elementDisposables.clear();
667
}
668
669
disposeTemplate(templateData: IExceptionBreakpointTemplateData): void {
670
templateData.templateDisposables.dispose();
671
}
672
}
673
674
class FunctionBreakpointsRenderer implements IListRenderer<FunctionBreakpoint, IFunctionBreakpointTemplateData> {
675
676
constructor(
677
private menu: IMenu,
678
private breakpointSupportsCondition: IContextKey<boolean>,
679
private breakpointItemType: IContextKey<string | undefined>,
680
@IDebugService private readonly debugService: IDebugService,
681
@IHoverService private readonly hoverService: IHoverService,
682
@ILabelService private readonly labelService: ILabelService
683
) {
684
// noop
685
}
686
687
static readonly ID = 'functionbreakpoints';
688
689
get templateId() {
690
return FunctionBreakpointsRenderer.ID;
691
}
692
693
renderTemplate(container: HTMLElement): IFunctionBreakpointTemplateData {
694
const data: IFunctionBreakpointTemplateData = Object.create(null);
695
data.elementDisposables = new DisposableStore();
696
data.templateDisposables = new DisposableStore();
697
data.templateDisposables.add(data.elementDisposables);
698
data.breakpoint = dom.append(container, $('.breakpoint'));
699
700
data.icon = $('.icon');
701
data.checkbox = createCheckbox(data.templateDisposables);
702
data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
703
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
704
}));
705
706
dom.append(data.breakpoint, data.icon);
707
dom.append(data.breakpoint, data.checkbox);
708
709
data.name = dom.append(data.breakpoint, $('span.name'));
710
data.condition = dom.append(data.breakpoint, $('span.condition'));
711
712
data.actionBar = new ActionBar(data.breakpoint);
713
data.templateDisposables.add(data.actionBar);
714
const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));
715
data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));
716
717
return data;
718
}
719
720
renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IFunctionBreakpointTemplateData): void {
721
data.context = functionBreakpoint;
722
data.name.textContent = functionBreakpoint.name;
723
const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService, this.debugService.getModel());
724
data.icon.className = ThemeIcon.asClassName(icon);
725
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ? message : ''));
726
data.checkbox.checked = functionBreakpoint.enabled;
727
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, message ? message : ''));
728
if (functionBreakpoint.condition && functionBreakpoint.hitCondition) {
729
data.condition.textContent = localize('expressionAndHitCount', "Condition: {0} | Hit Count: {1}", functionBreakpoint.condition, functionBreakpoint.hitCondition);
730
} else {
731
data.condition.textContent = functionBreakpoint.condition || functionBreakpoint.hitCondition || '';
732
}
733
734
if (functionBreakpoint.modeLabel) {
735
data.badge.textContent = functionBreakpoint.modeLabel;
736
data.badge.style.display = 'block';
737
} else {
738
data.badge.style.display = 'none';
739
}
740
741
// Mark function breakpoints as disabled if deactivated or if debug type does not support them #9099
742
const session = this.debugService.getViewModel().focusedSession;
743
data.breakpoint.classList.toggle('disabled', (session && !session.capabilities.supportsFunctionBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());
744
if (session && !session.capabilities.supportsFunctionBreakpoints) {
745
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, localize('functionBreakpointsNotSupported', "Function breakpoints are not supported by this debug type")));
746
}
747
748
this.breakpointSupportsCondition.set(!session || !!session.capabilities.supportsConditionalBreakpoints);
749
this.breakpointItemType.set('functionBreakpoint');
750
const { primary } = getActionBarActions(this.menu.getActions({ arg: functionBreakpoint, shouldForwardArgs: true }), 'inline');
751
data.actionBar.clear();
752
data.actionBar.push(primary, { icon: true, label: false });
753
breakpointIdToActionBarDomeNode.set(functionBreakpoint.getId(), data.actionBar.domNode);
754
}
755
756
disposeElement(element: FunctionBreakpoint, index: number, templateData: IFunctionBreakpointTemplateData): void {
757
templateData.elementDisposables.clear();
758
}
759
760
disposeTemplate(templateData: IFunctionBreakpointTemplateData): void {
761
templateData.templateDisposables.dispose();
762
}
763
}
764
765
class DataBreakpointsRenderer implements IListRenderer<DataBreakpoint, IDataBreakpointTemplateData> {
766
767
constructor(
768
private menu: IMenu,
769
private breakpointHasMultipleModes: IContextKey<boolean>,
770
private breakpointSupportsCondition: IContextKey<boolean>,
771
private breakpointItemType: IContextKey<string | undefined>,
772
private breakpointIsDataBytes: IContextKey<boolean | undefined>,
773
@IDebugService private readonly debugService: IDebugService,
774
@IHoverService private readonly hoverService: IHoverService,
775
@ILabelService private readonly labelService: ILabelService
776
) {
777
// noop
778
}
779
780
static readonly ID = 'databreakpoints';
781
782
get templateId() {
783
return DataBreakpointsRenderer.ID;
784
}
785
786
renderTemplate(container: HTMLElement): IDataBreakpointTemplateData {
787
const data: IDataBreakpointTemplateData = Object.create(null);
788
data.breakpoint = dom.append(container, $('.breakpoint'));
789
data.elementDisposables = new DisposableStore();
790
data.templateDisposables = new DisposableStore();
791
data.templateDisposables.add(data.elementDisposables);
792
793
data.icon = $('.icon');
794
data.checkbox = createCheckbox(data.templateDisposables);
795
data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
796
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
797
}));
798
799
dom.append(data.breakpoint, data.icon);
800
dom.append(data.breakpoint, data.checkbox);
801
802
data.name = dom.append(data.breakpoint, $('span.name'));
803
data.accessType = dom.append(data.breakpoint, $('span.access-type'));
804
data.condition = dom.append(data.breakpoint, $('span.condition'));
805
806
data.actionBar = new ActionBar(data.breakpoint);
807
data.templateDisposables.add(data.actionBar);
808
const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));
809
data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));
810
811
return data;
812
}
813
814
renderElement(dataBreakpoint: DataBreakpoint, _index: number, data: IDataBreakpointTemplateData): void {
815
data.context = dataBreakpoint;
816
data.name.textContent = dataBreakpoint.description;
817
const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint, this.labelService, this.debugService.getModel());
818
data.icon.className = ThemeIcon.asClassName(icon);
819
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ? message : ''));
820
data.checkbox.checked = dataBreakpoint.enabled;
821
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, message ? message : ''));
822
823
if (dataBreakpoint.modeLabel) {
824
data.badge.textContent = dataBreakpoint.modeLabel;
825
data.badge.style.display = 'block';
826
} else {
827
data.badge.style.display = 'none';
828
}
829
830
// Mark data breakpoints as disabled if deactivated or if debug type does not support them
831
const session = this.debugService.getViewModel().focusedSession;
832
data.breakpoint.classList.toggle('disabled', (session && !session.capabilities.supportsDataBreakpoints) || !this.debugService.getModel().areBreakpointsActivated());
833
if (session && !session.capabilities.supportsDataBreakpoints) {
834
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, localize('dataBreakpointsNotSupported', "Data breakpoints are not supported by this debug type")));
835
}
836
if (dataBreakpoint.accessType) {
837
const accessType = dataBreakpoint.accessType === 'read' ? localize('read', "Read") : dataBreakpoint.accessType === 'write' ? localize('write', "Write") : localize('access', "Access");
838
data.accessType.textContent = accessType;
839
} else {
840
data.accessType.textContent = '';
841
}
842
if (dataBreakpoint.condition && dataBreakpoint.hitCondition) {
843
data.condition.textContent = localize('expressionAndHitCount', "Condition: {0} | Hit Count: {1}", dataBreakpoint.condition, dataBreakpoint.hitCondition);
844
} else {
845
data.condition.textContent = dataBreakpoint.condition || dataBreakpoint.hitCondition || '';
846
}
847
848
this.breakpointSupportsCondition.set(!session || !!session.capabilities.supportsConditionalBreakpoints);
849
this.breakpointHasMultipleModes.set(this.debugService.getModel().getBreakpointModes('data').length > 1);
850
this.breakpointItemType.set('dataBreakpoint');
851
this.breakpointIsDataBytes.set(dataBreakpoint.src.type === DataBreakpointSetType.Address);
852
const { primary } = getActionBarActions(this.menu.getActions({ arg: dataBreakpoint, shouldForwardArgs: true }), 'inline');
853
data.actionBar.clear();
854
data.actionBar.push(primary, { icon: true, label: false });
855
breakpointIdToActionBarDomeNode.set(dataBreakpoint.getId(), data.actionBar.domNode);
856
this.breakpointIsDataBytes.reset();
857
}
858
859
disposeElement(element: DataBreakpoint, index: number, templateData: IDataBreakpointTemplateData): void {
860
templateData.elementDisposables.clear();
861
}
862
863
disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void {
864
templateData.templateDisposables.dispose();
865
}
866
}
867
868
class InstructionBreakpointsRenderer implements IListRenderer<IInstructionBreakpoint, IInstructionBreakpointTemplateData> {
869
870
constructor(
871
@IDebugService private readonly debugService: IDebugService,
872
@IHoverService private readonly hoverService: IHoverService,
873
@ILabelService private readonly labelService: ILabelService
874
) {
875
// noop
876
}
877
878
static readonly ID = 'instructionBreakpoints';
879
880
get templateId() {
881
return InstructionBreakpointsRenderer.ID;
882
}
883
884
renderTemplate(container: HTMLElement): IInstructionBreakpointTemplateData {
885
const data: IInstructionBreakpointTemplateData = Object.create(null);
886
data.elementDisposables = new DisposableStore();
887
data.templateDisposables = new DisposableStore();
888
data.templateDisposables.add(data.elementDisposables);
889
data.breakpoint = dom.append(container, $('.breakpoint'));
890
891
data.icon = $('.icon');
892
data.checkbox = createCheckbox(data.templateDisposables);
893
data.templateDisposables.add(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => {
894
this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context);
895
}));
896
897
dom.append(data.breakpoint, data.icon);
898
dom.append(data.breakpoint, data.checkbox);
899
900
data.name = dom.append(data.breakpoint, $('span.name'));
901
902
data.address = dom.append(data.breakpoint, $('span.file-path'));
903
data.actionBar = new ActionBar(data.breakpoint);
904
data.templateDisposables.add(data.actionBar);
905
const badgeContainer = dom.append(data.breakpoint, $('.badge-container'));
906
data.badge = dom.append(badgeContainer, $('span.line-number.monaco-count-badge'));
907
908
return data;
909
}
910
911
renderElement(breakpoint: IInstructionBreakpoint, index: number, data: IInstructionBreakpointTemplateData): void {
912
data.context = breakpoint;
913
data.breakpoint.classList.toggle('disabled', !this.debugService.getModel().areBreakpointsActivated());
914
915
data.name.textContent = '0x' + breakpoint.address.toString(16);
916
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.name, localize('debug.decimal.address', "Decimal Address: {0}", breakpoint.address.toString())));
917
data.checkbox.checked = breakpoint.enabled;
918
919
const { message, icon } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), breakpoint, this.labelService, this.debugService.getModel());
920
data.icon.className = ThemeIcon.asClassName(icon);
921
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.breakpoint, breakpoint.message || message || ''));
922
923
const debugActive = this.debugService.state === State.Running || this.debugService.state === State.Stopped;
924
if (debugActive && !breakpoint.verified) {
925
data.breakpoint.classList.add('disabled');
926
}
927
928
if (breakpoint.modeLabel) {
929
data.badge.textContent = breakpoint.modeLabel;
930
data.badge.style.display = 'block';
931
} else {
932
data.badge.style.display = 'none';
933
}
934
}
935
936
937
disposeElement(element: IInstructionBreakpoint, index: number, templateData: IInstructionBreakpointTemplateData): void {
938
templateData.elementDisposables.clear();
939
}
940
941
disposeTemplate(templateData: IInstructionBreakpointTemplateData): void {
942
templateData.templateDisposables.dispose();
943
}
944
}
945
946
class FunctionBreakpointInputRenderer implements IListRenderer<IFunctionBreakpoint, IFunctionBreakpointInputTemplateData> {
947
948
constructor(
949
private view: BreakpointsView,
950
private debugService: IDebugService,
951
private contextViewService: IContextViewService,
952
private readonly hoverService: IHoverService,
953
private labelService: ILabelService
954
) { }
955
956
static readonly ID = 'functionbreakpointinput';
957
958
get templateId() {
959
return FunctionBreakpointInputRenderer.ID;
960
}
961
962
renderTemplate(container: HTMLElement): IFunctionBreakpointInputTemplateData {
963
const template: IFunctionBreakpointInputTemplateData = Object.create(null);
964
const toDispose = new DisposableStore();
965
966
const breakpoint = dom.append(container, $('.breakpoint'));
967
template.icon = $('.icon');
968
template.checkbox = createCheckbox(toDispose);
969
970
dom.append(breakpoint, template.icon);
971
dom.append(breakpoint, template.checkbox);
972
this.view.breakpointInputFocused.set(true);
973
const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));
974
975
976
const inputBox = new InputBox(inputBoxContainer, this.contextViewService, { inputBoxStyles: defaultInputBoxStyles });
977
978
toDispose.add(inputBox);
979
980
const wrapUp = (success: boolean) => {
981
template.updating = true;
982
try {
983
this.view.breakpointInputFocused.set(false);
984
const id = template.breakpoint.getId();
985
986
if (success) {
987
if (template.type === 'name') {
988
this.debugService.updateFunctionBreakpoint(id, { name: inputBox.value });
989
}
990
if (template.type === 'condition') {
991
this.debugService.updateFunctionBreakpoint(id, { condition: inputBox.value });
992
}
993
if (template.type === 'hitCount') {
994
this.debugService.updateFunctionBreakpoint(id, { hitCondition: inputBox.value });
995
}
996
} else {
997
if (template.type === 'name' && !template.breakpoint.name) {
998
this.debugService.removeFunctionBreakpoints(id);
999
} else {
1000
this.view.renderInputBox(undefined);
1001
}
1002
}
1003
} finally {
1004
template.updating = false;
1005
}
1006
};
1007
1008
toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
1009
const isEscape = e.equals(KeyCode.Escape);
1010
const isEnter = e.equals(KeyCode.Enter);
1011
if (isEscape || isEnter) {
1012
e.preventDefault();
1013
e.stopPropagation();
1014
wrapUp(isEnter);
1015
}
1016
}));
1017
toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
1018
if (!template.updating) {
1019
wrapUp(!!inputBox.value);
1020
}
1021
}));
1022
1023
template.inputBox = inputBox;
1024
template.elementDisposables = new DisposableStore();
1025
template.templateDisposables = toDispose;
1026
template.templateDisposables.add(template.elementDisposables);
1027
return template;
1028
}
1029
1030
renderElement(functionBreakpoint: FunctionBreakpoint, _index: number, data: IFunctionBreakpointInputTemplateData): void {
1031
data.breakpoint = functionBreakpoint;
1032
data.type = this.view.inputBoxData?.type || 'name'; // If there is no type set take the 'name' as the default
1033
const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), functionBreakpoint, this.labelService, this.debugService.getModel());
1034
1035
data.icon.className = ThemeIcon.asClassName(icon);
1036
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ? message : ''));
1037
data.checkbox.checked = functionBreakpoint.enabled;
1038
data.checkbox.disabled = true;
1039
data.inputBox.value = functionBreakpoint.name || '';
1040
1041
let placeholder = localize('functionBreakpointPlaceholder', "Function to break on");
1042
let ariaLabel = localize('functionBreakPointInputAriaLabel', "Type function breakpoint.");
1043
if (data.type === 'condition') {
1044
data.inputBox.value = functionBreakpoint.condition || '';
1045
placeholder = localize('functionBreakpointExpressionPlaceholder', "Break when expression evaluates to true");
1046
ariaLabel = localize('functionBreakPointExpresionAriaLabel', "Type expression. Function breakpoint will break when expression evaluates to true");
1047
} else if (data.type === 'hitCount') {
1048
data.inputBox.value = functionBreakpoint.hitCondition || '';
1049
placeholder = localize('functionBreakpointHitCountPlaceholder', "Break when hit count is met");
1050
ariaLabel = localize('functionBreakPointHitCountAriaLabel', "Type hit count. Function breakpoint will break when hit count is met.");
1051
}
1052
data.inputBox.setAriaLabel(ariaLabel);
1053
data.inputBox.setPlaceHolder(placeholder);
1054
1055
setTimeout(() => {
1056
data.inputBox.focus();
1057
data.inputBox.select();
1058
}, 0);
1059
}
1060
1061
disposeElement(element: IFunctionBreakpoint, index: number, templateData: IFunctionBreakpointInputTemplateData): void {
1062
templateData.elementDisposables.clear();
1063
}
1064
1065
disposeTemplate(templateData: IFunctionBreakpointInputTemplateData): void {
1066
templateData.templateDisposables.dispose();
1067
}
1068
}
1069
1070
class DataBreakpointInputRenderer implements IListRenderer<IDataBreakpoint, IDataBreakpointInputTemplateData> {
1071
1072
constructor(
1073
private view: BreakpointsView,
1074
private debugService: IDebugService,
1075
private contextViewService: IContextViewService,
1076
private readonly hoverService: IHoverService,
1077
private labelService: ILabelService
1078
) { }
1079
1080
static readonly ID = 'databreakpointinput';
1081
1082
get templateId() {
1083
return DataBreakpointInputRenderer.ID;
1084
}
1085
1086
renderTemplate(container: HTMLElement): IDataBreakpointInputTemplateData {
1087
const template: IDataBreakpointInputTemplateData = Object.create(null);
1088
const toDispose = new DisposableStore();
1089
1090
const breakpoint = dom.append(container, $('.breakpoint'));
1091
template.icon = $('.icon');
1092
template.checkbox = createCheckbox(toDispose);
1093
1094
dom.append(breakpoint, template.icon);
1095
dom.append(breakpoint, template.checkbox);
1096
this.view.breakpointInputFocused.set(true);
1097
const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));
1098
1099
1100
const inputBox = new InputBox(inputBoxContainer, this.contextViewService, { inputBoxStyles: defaultInputBoxStyles });
1101
toDispose.add(inputBox);
1102
1103
const wrapUp = (success: boolean) => {
1104
template.updating = true;
1105
try {
1106
this.view.breakpointInputFocused.set(false);
1107
const id = template.breakpoint.getId();
1108
1109
if (success) {
1110
if (template.type === 'condition') {
1111
this.debugService.updateDataBreakpoint(id, { condition: inputBox.value });
1112
}
1113
if (template.type === 'hitCount') {
1114
this.debugService.updateDataBreakpoint(id, { hitCondition: inputBox.value });
1115
}
1116
} else {
1117
this.view.renderInputBox(undefined);
1118
}
1119
} finally {
1120
template.updating = false;
1121
}
1122
};
1123
1124
toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
1125
const isEscape = e.equals(KeyCode.Escape);
1126
const isEnter = e.equals(KeyCode.Enter);
1127
if (isEscape || isEnter) {
1128
e.preventDefault();
1129
e.stopPropagation();
1130
wrapUp(isEnter);
1131
}
1132
}));
1133
toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
1134
if (!template.updating) {
1135
wrapUp(!!inputBox.value);
1136
}
1137
}));
1138
1139
template.inputBox = inputBox;
1140
template.elementDisposables = new DisposableStore();
1141
template.templateDisposables = toDispose;
1142
template.templateDisposables.add(template.elementDisposables);
1143
return template;
1144
}
1145
1146
renderElement(dataBreakpoint: DataBreakpoint, _index: number, data: IDataBreakpointInputTemplateData): void {
1147
data.breakpoint = dataBreakpoint;
1148
data.type = this.view.inputBoxData?.type || 'condition'; // If there is no type set take the 'condition' as the default
1149
const { icon, message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), dataBreakpoint, this.labelService, this.debugService.getModel());
1150
1151
data.icon.className = ThemeIcon.asClassName(icon);
1152
data.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), data.icon, message ?? ''));
1153
data.checkbox.checked = dataBreakpoint.enabled;
1154
data.checkbox.disabled = true;
1155
data.inputBox.value = '';
1156
let placeholder = '';
1157
let ariaLabel = '';
1158
if (data.type === 'condition') {
1159
data.inputBox.value = dataBreakpoint.condition || '';
1160
placeholder = localize('dataBreakpointExpressionPlaceholder', "Break when expression evaluates to true");
1161
ariaLabel = localize('dataBreakPointExpresionAriaLabel', "Type expression. Data breakpoint will break when expression evaluates to true");
1162
} else if (data.type === 'hitCount') {
1163
data.inputBox.value = dataBreakpoint.hitCondition || '';
1164
placeholder = localize('dataBreakpointHitCountPlaceholder', "Break when hit count is met");
1165
ariaLabel = localize('dataBreakPointHitCountAriaLabel', "Type hit count. Data breakpoint will break when hit count is met.");
1166
}
1167
data.inputBox.setAriaLabel(ariaLabel);
1168
data.inputBox.setPlaceHolder(placeholder);
1169
1170
setTimeout(() => {
1171
data.inputBox.focus();
1172
data.inputBox.select();
1173
}, 0);
1174
}
1175
1176
disposeElement(element: IDataBreakpoint, index: number, templateData: IDataBreakpointInputTemplateData): void {
1177
templateData.elementDisposables.clear();
1178
}
1179
1180
disposeTemplate(templateData: IDataBreakpointInputTemplateData): void {
1181
templateData.templateDisposables.dispose();
1182
}
1183
}
1184
1185
class ExceptionBreakpointInputRenderer implements IListRenderer<IExceptionBreakpoint, IExceptionBreakpointInputTemplateData> {
1186
1187
constructor(
1188
private view: BreakpointsView,
1189
private debugService: IDebugService,
1190
private contextViewService: IContextViewService,
1191
) {
1192
// noop
1193
}
1194
1195
static readonly ID = 'exceptionbreakpointinput';
1196
1197
get templateId() {
1198
return ExceptionBreakpointInputRenderer.ID;
1199
}
1200
1201
renderTemplate(container: HTMLElement): IExceptionBreakpointInputTemplateData {
1202
const toDispose = new DisposableStore();
1203
1204
const breakpoint = dom.append(container, $('.breakpoint'));
1205
breakpoint.classList.add('exception');
1206
const checkbox = createCheckbox(toDispose);
1207
1208
dom.append(breakpoint, checkbox);
1209
this.view.breakpointInputFocused.set(true);
1210
const inputBoxContainer = dom.append(breakpoint, $('.inputBoxContainer'));
1211
const inputBox = new InputBox(inputBoxContainer, this.contextViewService, {
1212
ariaLabel: localize('exceptionBreakpointAriaLabel', "Type exception breakpoint condition"),
1213
inputBoxStyles: defaultInputBoxStyles
1214
});
1215
1216
1217
toDispose.add(inputBox);
1218
const wrapUp = (success: boolean) => {
1219
if (!templateData.currentBreakpoint) {
1220
return;
1221
}
1222
1223
this.view.breakpointInputFocused.set(false);
1224
let newCondition = templateData.currentBreakpoint.condition;
1225
if (success) {
1226
newCondition = inputBox.value !== '' ? inputBox.value : undefined;
1227
}
1228
this.debugService.setExceptionBreakpointCondition(templateData.currentBreakpoint, newCondition);
1229
};
1230
1231
toDispose.add(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => {
1232
const isEscape = e.equals(KeyCode.Escape);
1233
const isEnter = e.equals(KeyCode.Enter);
1234
if (isEscape || isEnter) {
1235
e.preventDefault();
1236
e.stopPropagation();
1237
wrapUp(isEnter);
1238
}
1239
}));
1240
toDispose.add(dom.addDisposableListener(inputBox.inputElement, 'blur', () => {
1241
// Need to react with a timeout on the blur event due to possible concurent splices #56443
1242
setTimeout(() => {
1243
wrapUp(true);
1244
});
1245
}));
1246
1247
const elementDisposables = new DisposableStore();
1248
toDispose.add(elementDisposables);
1249
1250
const templateData: IExceptionBreakpointInputTemplateData = {
1251
inputBox,
1252
checkbox,
1253
templateDisposables: toDispose,
1254
elementDisposables: new DisposableStore(),
1255
};
1256
1257
return templateData;
1258
}
1259
1260
renderElement(exceptionBreakpoint: ExceptionBreakpoint, _index: number, data: IExceptionBreakpointInputTemplateData): void {
1261
const placeHolder = exceptionBreakpoint.conditionDescription || localize('exceptionBreakpointPlaceholder', "Break when expression evaluates to true");
1262
data.inputBox.setPlaceHolder(placeHolder);
1263
data.currentBreakpoint = exceptionBreakpoint;
1264
data.checkbox.checked = exceptionBreakpoint.enabled;
1265
data.checkbox.disabled = true;
1266
data.inputBox.value = exceptionBreakpoint.condition || '';
1267
setTimeout(() => {
1268
data.inputBox.focus();
1269
data.inputBox.select();
1270
}, 0);
1271
}
1272
1273
disposeElement(element: IExceptionBreakpoint, index: number, templateData: IExceptionBreakpointInputTemplateData): void {
1274
templateData.elementDisposables.clear();
1275
}
1276
1277
disposeTemplate(templateData: IExceptionBreakpointInputTemplateData): void {
1278
templateData.templateDisposables.dispose();
1279
}
1280
}
1281
1282
class BreakpointsAccessibilityProvider implements IListAccessibilityProvider<BreakpointItem> {
1283
1284
constructor(
1285
private readonly debugService: IDebugService,
1286
private readonly labelService: ILabelService
1287
) { }
1288
1289
getWidgetAriaLabel(): string {
1290
return localize('breakpoints', "Breakpoints");
1291
}
1292
1293
getRole(): AriaRole {
1294
return 'checkbox';
1295
}
1296
1297
isChecked(breakpoint: IEnablement) {
1298
return breakpoint.enabled;
1299
}
1300
1301
getAriaLabel(element: BreakpointItem): string | null {
1302
if (element instanceof ExceptionBreakpoint) {
1303
return element.toString();
1304
}
1305
1306
const { message } = getBreakpointMessageAndIcon(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), element as IBreakpoint | IDataBreakpoint | IFunctionBreakpoint, this.labelService, this.debugService.getModel());
1307
const toString = element.toString();
1308
1309
return message ? `${toString}, ${message}` : toString;
1310
}
1311
}
1312
1313
export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, pinned: boolean, debugService: IDebugService, editorService: IEditorService): Promise<IEditorPane | undefined> {
1314
if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) {
1315
return Promise.resolve(undefined);
1316
}
1317
1318
const selection = breakpoint.endLineNumber ? {
1319
startLineNumber: breakpoint.lineNumber,
1320
endLineNumber: breakpoint.endLineNumber,
1321
startColumn: breakpoint.column || 1,
1322
endColumn: breakpoint.endColumn || Constants.MAX_SAFE_SMALL_INTEGER
1323
} : {
1324
startLineNumber: breakpoint.lineNumber,
1325
startColumn: breakpoint.column || 1,
1326
endLineNumber: breakpoint.lineNumber,
1327
endColumn: breakpoint.column || Constants.MAX_SAFE_SMALL_INTEGER
1328
};
1329
1330
return editorService.openEditor({
1331
resource: breakpoint.uri,
1332
options: {
1333
preserveFocus,
1334
selection,
1335
revealIfOpened: true,
1336
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
1337
pinned
1338
}
1339
}, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
1340
}
1341
1342
export function getBreakpointMessageAndIcon(state: State, breakpointsActivated: boolean, breakpoint: BreakpointItem, labelService: ILabelService, debugModel: IDebugModel): { message?: string; icon: ThemeIcon; showAdapterUnverifiedMessage?: boolean } {
1343
const debugActive = state === State.Running || state === State.Stopped;
1344
1345
const breakpointIcon = breakpoint instanceof DataBreakpoint ? icons.dataBreakpoint : breakpoint instanceof FunctionBreakpoint ? icons.functionBreakpoint : breakpoint.logMessage ? icons.logBreakpoint : icons.breakpoint;
1346
1347
if (!breakpoint.enabled || !breakpointsActivated) {
1348
return {
1349
icon: breakpointIcon.disabled,
1350
message: breakpoint.logMessage ? localize('disabledLogpoint', "Disabled Logpoint") : localize('disabledBreakpoint', "Disabled Breakpoint"),
1351
};
1352
}
1353
1354
const appendMessage = (text: string): string => {
1355
return ('message' in breakpoint && breakpoint.message) ? text.concat(', ' + breakpoint.message) : text;
1356
};
1357
1358
if (debugActive && breakpoint instanceof Breakpoint && breakpoint.pending) {
1359
return {
1360
icon: icons.breakpoint.pending
1361
};
1362
}
1363
1364
if (debugActive && !breakpoint.verified) {
1365
return {
1366
icon: breakpointIcon.unverified,
1367
message: ('message' in breakpoint && breakpoint.message) ? breakpoint.message : (breakpoint.logMessage ? localize('unverifiedLogpoint', "Unverified Logpoint") : localize('unverifiedBreakpoint', "Unverified Breakpoint")),
1368
showAdapterUnverifiedMessage: true
1369
};
1370
}
1371
1372
if (breakpoint instanceof DataBreakpoint) {
1373
if (!breakpoint.supported) {
1374
return {
1375
icon: breakpointIcon.unverified,
1376
message: localize('dataBreakpointUnsupported', "Data breakpoints not supported by this debug type"),
1377
};
1378
}
1379
1380
return {
1381
icon: breakpointIcon.regular,
1382
message: breakpoint.message || localize('dataBreakpoint', "Data Breakpoint")
1383
};
1384
}
1385
1386
if (breakpoint instanceof FunctionBreakpoint) {
1387
if (!breakpoint.supported) {
1388
return {
1389
icon: breakpointIcon.unverified,
1390
message: localize('functionBreakpointUnsupported', "Function breakpoints not supported by this debug type"),
1391
};
1392
}
1393
const messages: string[] = [];
1394
messages.push(breakpoint.message || localize('functionBreakpoint', "Function Breakpoint"));
1395
if (breakpoint.condition) {
1396
messages.push(localize('expression', "Condition: {0}", breakpoint.condition));
1397
}
1398
if (breakpoint.hitCondition) {
1399
messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));
1400
}
1401
1402
return {
1403
icon: breakpointIcon.regular,
1404
message: appendMessage(messages.join('\n'))
1405
};
1406
}
1407
1408
if (breakpoint instanceof InstructionBreakpoint) {
1409
if (!breakpoint.supported) {
1410
return {
1411
icon: breakpointIcon.unverified,
1412
message: localize('instructionBreakpointUnsupported', "Instruction breakpoints not supported by this debug type"),
1413
};
1414
}
1415
const messages: string[] = [];
1416
if (breakpoint.message) {
1417
messages.push(breakpoint.message);
1418
} else if (breakpoint.instructionReference) {
1419
messages.push(localize('instructionBreakpointAtAddress', "Instruction breakpoint at address {0}", breakpoint.instructionReference));
1420
} else {
1421
messages.push(localize('instructionBreakpoint', "Instruction breakpoint"));
1422
}
1423
1424
if (breakpoint.hitCondition) {
1425
messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));
1426
}
1427
1428
return {
1429
icon: breakpointIcon.regular,
1430
message: appendMessage(messages.join('\n'))
1431
};
1432
}
1433
1434
// can change this when all breakpoint supports dependent breakpoint condition
1435
let triggeringBreakpoint: IBreakpoint | undefined;
1436
if (breakpoint instanceof Breakpoint && breakpoint.triggeredBy) {
1437
triggeringBreakpoint = debugModel.getBreakpoints().find(bp => bp.getId() === breakpoint.triggeredBy);
1438
}
1439
1440
if (breakpoint.logMessage || breakpoint.condition || breakpoint.hitCondition || triggeringBreakpoint) {
1441
const messages: string[] = [];
1442
let icon = breakpoint.logMessage ? icons.logBreakpoint.regular : icons.conditionalBreakpoint.regular;
1443
if (!breakpoint.supported) {
1444
icon = icons.debugBreakpointUnsupported;
1445
messages.push(localize('breakpointUnsupported', "Breakpoints of this type are not supported by the debugger"));
1446
}
1447
1448
if (breakpoint.logMessage) {
1449
messages.push(localize('logMessage', "Log Message: {0}", breakpoint.logMessage));
1450
}
1451
if (breakpoint.condition) {
1452
messages.push(localize('expression', "Condition: {0}", breakpoint.condition));
1453
}
1454
if (breakpoint.hitCondition) {
1455
messages.push(localize('hitCount', "Hit Count: {0}", breakpoint.hitCondition));
1456
}
1457
if (triggeringBreakpoint) {
1458
messages.push(localize('triggeredBy', "Hit after breakpoint: {0}", `${labelService.getUriLabel(triggeringBreakpoint.uri, { relative: true })}: ${triggeringBreakpoint.lineNumber}`));
1459
}
1460
1461
return {
1462
icon,
1463
message: appendMessage(messages.join('\n'))
1464
};
1465
}
1466
1467
const message = ('message' in breakpoint && breakpoint.message) ? breakpoint.message : breakpoint instanceof Breakpoint && labelService ? labelService.getUriLabel(breakpoint.uri) : localize('breakpoint', "Breakpoint");
1468
return {
1469
icon: breakpointIcon.regular,
1470
message
1471
};
1472
}
1473
1474
registerAction2(class extends Action2 {
1475
constructor() {
1476
super({
1477
id: 'workbench.debug.viewlet.action.addFunctionBreakpointAction',
1478
title: {
1479
...localize2('addFunctionBreakpoint', "Add Function Breakpoint"),
1480
mnemonicTitle: localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint..."),
1481
},
1482
f1: true,
1483
icon: icons.watchExpressionsAddFuncBreakpoint,
1484
menu: [{
1485
id: MenuId.ViewTitle,
1486
group: 'navigation',
1487
order: 10,
1488
when: ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID)
1489
}, {
1490
id: MenuId.MenubarNewBreakpointMenu,
1491
group: '1_breakpoints',
1492
order: 3,
1493
when: CONTEXT_DEBUGGERS_AVAILABLE
1494
}]
1495
});
1496
}
1497
1498
async run(accessor: ServicesAccessor): Promise<void> {
1499
const debugService = accessor.get(IDebugService);
1500
const viewService = accessor.get(IViewsService);
1501
await viewService.openView(BREAKPOINTS_VIEW_ID);
1502
debugService.addFunctionBreakpoint();
1503
}
1504
});
1505
1506
abstract class MemoryBreakpointAction extends Action2 {
1507
async run(accessor: ServicesAccessor, existingBreakpoint?: IDataBreakpoint): Promise<void> {
1508
const debugService = accessor.get(IDebugService);
1509
const session = debugService.getViewModel().focusedSession;
1510
if (!session) {
1511
return;
1512
}
1513
1514
let defaultValue = undefined;
1515
if (existingBreakpoint && existingBreakpoint.src.type === DataBreakpointSetType.Address) {
1516
defaultValue = `${existingBreakpoint.src.address} + ${existingBreakpoint.src.bytes}`;
1517
}
1518
1519
const quickInput = accessor.get(IQuickInputService);
1520
const notifications = accessor.get(INotificationService);
1521
const range = await this.getRange(quickInput, defaultValue);
1522
if (!range) {
1523
return;
1524
}
1525
1526
let info: IDataBreakpointInfoResponse | undefined;
1527
try {
1528
info = await session.dataBytesBreakpointInfo(range.address, range.bytes);
1529
} catch (e) {
1530
notifications.error(localize('dataBreakpointError', "Failed to set data breakpoint at {0}: {1}", range.address, e.message));
1531
}
1532
1533
if (!info?.dataId) {
1534
return;
1535
}
1536
1537
let accessType: DebugProtocol.DataBreakpointAccessType = 'write';
1538
if (info.accessTypes && info.accessTypes?.length > 1) {
1539
const accessTypes = info.accessTypes.map(type => ({ label: type }));
1540
const selectedAccessType = await quickInput.pick(accessTypes, { placeHolder: localize('dataBreakpointAccessType', "Select the access type to monitor") });
1541
if (!selectedAccessType) {
1542
return;
1543
}
1544
1545
accessType = selectedAccessType.label;
1546
}
1547
1548
const src: DataBreakpointSource = { type: DataBreakpointSetType.Address, ...range };
1549
if (existingBreakpoint) {
1550
await debugService.removeDataBreakpoints(existingBreakpoint.getId());
1551
}
1552
1553
await debugService.addDataBreakpoint({
1554
description: info.description,
1555
src,
1556
canPersist: true,
1557
accessTypes: info.accessTypes,
1558
accessType: accessType,
1559
initialSessionData: { session, dataId: info.dataId }
1560
});
1561
}
1562
1563
private getRange(quickInput: IQuickInputService, defaultValue?: string) {
1564
return new Promise<{ address: string; bytes: number } | undefined>(resolve => {
1565
const disposables = new DisposableStore();
1566
const input = disposables.add(quickInput.createInputBox());
1567
input.prompt = localize('dataBreakpointMemoryRangePrompt', "Enter a memory range in which to break");
1568
input.placeholder = localize('dataBreakpointMemoryRangePlaceholder', 'Absolute range (0x1234 - 0x1300) or range of bytes after an address (0x1234 + 0xff)');
1569
if (defaultValue) {
1570
input.value = defaultValue;
1571
input.valueSelection = [0, defaultValue.length];
1572
}
1573
disposables.add(input.onDidChangeValue(e => {
1574
const err = this.parseAddress(e, false);
1575
input.validationMessage = err?.error;
1576
}));
1577
disposables.add(input.onDidAccept(() => {
1578
const r = this.parseAddress(input.value, true);
1579
if ('error' in r) {
1580
input.validationMessage = r.error;
1581
} else {
1582
resolve(r);
1583
}
1584
input.dispose();
1585
}));
1586
disposables.add(input.onDidHide(() => {
1587
resolve(undefined);
1588
disposables.dispose();
1589
}));
1590
input.ignoreFocusOut = true;
1591
input.show();
1592
});
1593
}
1594
1595
private parseAddress(range: string, isFinal: false): { error: string } | undefined;
1596
private parseAddress(range: string, isFinal: true): { error: string } | { address: string; bytes: number };
1597
private parseAddress(range: string, isFinal: boolean): { error: string } | { address: string; bytes: number } | undefined {
1598
const parts = /^(\S+)\s*(?:([+-])\s*(\S+))?/.exec(range);
1599
if (!parts) {
1600
return { error: localize('dataBreakpointAddrFormat', 'Address should be a range of numbers the form "[Start] - [End]" or "[Start] + [Bytes]"') };
1601
}
1602
1603
const isNum = (e: string) => isFinal ? /^0x[0-9a-f]*|[0-9]*$/i.test(e) : /^0x[0-9a-f]+|[0-9]+$/i.test(e);
1604
const [, startStr, sign = '+', endStr = '1'] = parts;
1605
1606
for (const n of [startStr, endStr]) {
1607
if (!isNum(n)) {
1608
return { error: localize('dataBreakpointAddrStartEnd', 'Number must be a decimal integer or hex value starting with \"0x\", got {0}', n) };
1609
}
1610
}
1611
1612
if (!isFinal) {
1613
return;
1614
}
1615
1616
const start = BigInt(startStr);
1617
const end = BigInt(endStr);
1618
const address = `0x${start.toString(16)}`;
1619
if (sign === '-') {
1620
return { address, bytes: Number(start - end) };
1621
}
1622
1623
return { address, bytes: Number(end) };
1624
}
1625
}
1626
1627
registerAction2(class extends MemoryBreakpointAction {
1628
constructor() {
1629
super({
1630
id: 'workbench.debug.viewlet.action.addDataBreakpointOnAddress',
1631
title: {
1632
...localize2('addDataBreakpointOnAddress', "Add Data Breakpoint at Address"),
1633
mnemonicTitle: localize({ key: 'miDataBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Data Breakpoint..."),
1634
},
1635
f1: true,
1636
icon: icons.watchExpressionsAddDataBreakpoint,
1637
menu: [{
1638
id: MenuId.ViewTitle,
1639
group: 'navigation',
1640
order: 11,
1641
when: ContextKeyExpr.and(CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED, ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID))
1642
}, {
1643
id: MenuId.MenubarNewBreakpointMenu,
1644
group: '1_breakpoints',
1645
order: 4,
1646
when: CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED
1647
}]
1648
});
1649
}
1650
});
1651
1652
registerAction2(class extends MemoryBreakpointAction {
1653
constructor() {
1654
super({
1655
id: 'workbench.debug.viewlet.action.editDataBreakpointOnAddress',
1656
title: localize2('editDataBreakpointOnAddress', "Edit Address..."),
1657
menu: [{
1658
id: MenuId.DebugBreakpointsContext,
1659
when: ContextKeyExpr.and(CONTEXT_SET_DATA_BREAKPOINT_BYTES_SUPPORTED, CONTEXT_BREAKPOINT_ITEM_IS_DATA_BYTES),
1660
group: 'navigation',
1661
order: 15,
1662
}]
1663
});
1664
}
1665
});
1666
1667
registerAction2(class extends Action2 {
1668
constructor() {
1669
super({
1670
id: 'workbench.debug.viewlet.action.toggleBreakpointsActivatedAction',
1671
title: localize2('activateBreakpoints', 'Toggle Activate Breakpoints'),
1672
f1: true,
1673
icon: icons.breakpointsActivate,
1674
menu: {
1675
id: MenuId.ViewTitle,
1676
group: 'navigation',
1677
order: 20,
1678
when: ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID)
1679
}
1680
});
1681
}
1682
1683
run(accessor: ServicesAccessor): void {
1684
const debugService = accessor.get(IDebugService);
1685
debugService.setBreakpointsActivated(!debugService.getModel().areBreakpointsActivated());
1686
}
1687
});
1688
1689
registerAction2(class extends Action2 {
1690
constructor() {
1691
super({
1692
id: 'workbench.debug.viewlet.action.removeBreakpoint',
1693
title: localize('removeBreakpoint', "Remove Breakpoint"),
1694
icon: Codicon.removeClose,
1695
menu: [{
1696
id: MenuId.DebugBreakpointsContext,
1697
group: '3_modification',
1698
order: 10,
1699
when: CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint')
1700
}, {
1701
id: MenuId.DebugBreakpointsContext,
1702
group: 'inline',
1703
order: 20,
1704
when: CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint')
1705
}]
1706
});
1707
}
1708
1709
async run(accessor: ServicesAccessor, breakpoint: IBaseBreakpoint): Promise<void> {
1710
const debugService = accessor.get(IDebugService);
1711
if (breakpoint instanceof Breakpoint) {
1712
await debugService.removeBreakpoints(breakpoint.getId());
1713
} else if (breakpoint instanceof FunctionBreakpoint) {
1714
await debugService.removeFunctionBreakpoints(breakpoint.getId());
1715
} else if (breakpoint instanceof DataBreakpoint) {
1716
await debugService.removeDataBreakpoints(breakpoint.getId());
1717
} else if (breakpoint instanceof InstructionBreakpoint) {
1718
await debugService.removeInstructionBreakpoints(breakpoint.instructionReference, breakpoint.offset);
1719
}
1720
}
1721
});
1722
1723
registerAction2(class extends Action2 {
1724
constructor() {
1725
super({
1726
id: 'workbench.debug.viewlet.action.removeAllBreakpoints',
1727
title: {
1728
...localize2('removeAllBreakpoints', "Remove All Breakpoints"),
1729
mnemonicTitle: localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints"),
1730
},
1731
f1: true,
1732
icon: icons.breakpointsRemoveAll,
1733
menu: [{
1734
id: MenuId.ViewTitle,
1735
group: 'navigation',
1736
order: 30,
1737
when: ContextKeyExpr.equals('view', BREAKPOINTS_VIEW_ID)
1738
}, {
1739
id: MenuId.DebugBreakpointsContext,
1740
group: '3_modification',
1741
order: 20,
1742
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))
1743
}, {
1744
id: MenuId.MenubarDebugMenu,
1745
group: '5_breakpoints',
1746
order: 3,
1747
when: CONTEXT_DEBUGGERS_AVAILABLE
1748
}]
1749
});
1750
}
1751
1752
run(accessor: ServicesAccessor): void {
1753
const debugService = accessor.get(IDebugService);
1754
debugService.removeBreakpoints();
1755
debugService.removeFunctionBreakpoints();
1756
debugService.removeDataBreakpoints();
1757
debugService.removeInstructionBreakpoints();
1758
}
1759
});
1760
1761
registerAction2(class extends Action2 {
1762
constructor() {
1763
super({
1764
id: 'workbench.debug.viewlet.action.enableAllBreakpoints',
1765
title: {
1766
...localize2('enableAllBreakpoints', "Enable All Breakpoints"),
1767
mnemonicTitle: localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "&&Enable All Breakpoints"),
1768
},
1769
f1: true,
1770
precondition: CONTEXT_DEBUGGERS_AVAILABLE,
1771
menu: [{
1772
id: MenuId.DebugBreakpointsContext,
1773
group: 'z_commands',
1774
order: 10,
1775
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))
1776
}, {
1777
id: MenuId.MenubarDebugMenu,
1778
group: '5_breakpoints',
1779
order: 1,
1780
when: CONTEXT_DEBUGGERS_AVAILABLE
1781
}]
1782
});
1783
}
1784
1785
async run(accessor: ServicesAccessor): Promise<void> {
1786
const debugService = accessor.get(IDebugService);
1787
await debugService.enableOrDisableBreakpoints(true);
1788
}
1789
});
1790
1791
registerAction2(class extends Action2 {
1792
constructor() {
1793
super({
1794
id: 'workbench.debug.viewlet.action.disableAllBreakpoints',
1795
title: {
1796
...localize2('disableAllBreakpoints', "Disable All Breakpoints"),
1797
mnemonicTitle: localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints"),
1798
},
1799
f1: true,
1800
precondition: CONTEXT_DEBUGGERS_AVAILABLE,
1801
menu: [{
1802
id: MenuId.DebugBreakpointsContext,
1803
group: 'z_commands',
1804
order: 20,
1805
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))
1806
}, {
1807
id: MenuId.MenubarDebugMenu,
1808
group: '5_breakpoints',
1809
order: 2,
1810
when: CONTEXT_DEBUGGERS_AVAILABLE
1811
}]
1812
});
1813
}
1814
1815
async run(accessor: ServicesAccessor): Promise<void> {
1816
const debugService = accessor.get(IDebugService);
1817
await debugService.enableOrDisableBreakpoints(false);
1818
}
1819
});
1820
1821
registerAction2(class extends Action2 {
1822
constructor() {
1823
super({
1824
id: 'workbench.debug.viewlet.action.reapplyBreakpointsAction',
1825
title: localize2('reapplyAllBreakpoints', 'Reapply All Breakpoints'),
1826
f1: true,
1827
precondition: CONTEXT_IN_DEBUG_MODE,
1828
menu: [{
1829
id: MenuId.DebugBreakpointsContext,
1830
group: 'z_commands',
1831
order: 30,
1832
when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('exceptionBreakpoint'))
1833
}]
1834
});
1835
}
1836
1837
async run(accessor: ServicesAccessor): Promise<void> {
1838
const debugService = accessor.get(IDebugService);
1839
await debugService.setBreakpointsActivated(true);
1840
}
1841
});
1842
1843
registerAction2(class extends ViewAction<BreakpointsView> {
1844
constructor() {
1845
super({
1846
id: 'debug.editBreakpoint',
1847
viewId: BREAKPOINTS_VIEW_ID,
1848
title: localize('editCondition', "Edit Condition..."),
1849
icon: Codicon.edit,
1850
precondition: CONTEXT_BREAKPOINT_SUPPORTS_CONDITION,
1851
menu: [{
1852
id: MenuId.DebugBreakpointsContext,
1853
when: CONTEXT_BREAKPOINT_ITEM_TYPE.notEqualsTo('functionBreakpoint'),
1854
group: 'navigation',
1855
order: 10
1856
}, {
1857
id: MenuId.DebugBreakpointsContext,
1858
group: 'inline',
1859
order: 10
1860
}]
1861
});
1862
}
1863
1864
async runInView(accessor: ServicesAccessor, view: BreakpointsView, breakpoint: ExceptionBreakpoint | Breakpoint | FunctionBreakpoint | DataBreakpoint): Promise<void> {
1865
const debugService = accessor.get(IDebugService);
1866
const editorService = accessor.get(IEditorService);
1867
if (breakpoint instanceof Breakpoint) {
1868
const editor = await openBreakpointSource(breakpoint, false, false, true, debugService, editorService);
1869
if (editor) {
1870
const codeEditor = editor.getControl();
1871
if (isCodeEditor(codeEditor)) {
1872
codeEditor.getContribution<IBreakpointEditorContribution>(BREAKPOINT_EDITOR_CONTRIBUTION_ID)?.showBreakpointWidget(breakpoint.lineNumber, breakpoint.column);
1873
}
1874
}
1875
} else if (breakpoint instanceof FunctionBreakpoint) {
1876
const contextMenuService = accessor.get(IContextMenuService);
1877
const actions: Action[] = [new Action('breakpoint.editCondition', localize('editCondition', "Edit Condition..."), undefined, true, async () => view.renderInputBox({ breakpoint, type: 'condition' })),
1878
new Action('breakpoint.editCondition', localize('editHitCount', "Edit Hit Count..."), undefined, true, async () => view.renderInputBox({ breakpoint, type: 'hitCount' }))];
1879
const domNode = breakpointIdToActionBarDomeNode.get(breakpoint.getId());
1880
1881
if (domNode) {
1882
contextMenuService.showContextMenu({
1883
getActions: () => actions,
1884
getAnchor: () => domNode,
1885
onHide: () => dispose(actions)
1886
});
1887
}
1888
} else {
1889
view.renderInputBox({ breakpoint, type: 'condition' });
1890
}
1891
}
1892
});
1893
1894
1895
registerAction2(class extends ViewAction<BreakpointsView> {
1896
constructor() {
1897
super({
1898
id: 'debug.editFunctionBreakpoint',
1899
viewId: BREAKPOINTS_VIEW_ID,
1900
title: localize('editBreakpoint', "Edit Function Condition..."),
1901
menu: [{
1902
id: MenuId.DebugBreakpointsContext,
1903
group: 'navigation',
1904
order: 10,
1905
when: CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('functionBreakpoint')
1906
}]
1907
});
1908
}
1909
1910
runInView(_accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IFunctionBreakpoint) {
1911
view.renderInputBox({ breakpoint, type: 'name' });
1912
}
1913
});
1914
1915
registerAction2(class extends ViewAction<BreakpointsView> {
1916
constructor() {
1917
super({
1918
id: 'debug.editFunctionBreakpointHitCount',
1919
viewId: BREAKPOINTS_VIEW_ID,
1920
title: localize('editHitCount', "Edit Hit Count..."),
1921
precondition: CONTEXT_BREAKPOINT_SUPPORTS_CONDITION,
1922
menu: [{
1923
id: MenuId.DebugBreakpointsContext,
1924
group: 'navigation',
1925
order: 20,
1926
when: ContextKeyExpr.or(CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('functionBreakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('dataBreakpoint'))
1927
}]
1928
});
1929
}
1930
1931
runInView(_accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IFunctionBreakpoint) {
1932
view.renderInputBox({ breakpoint, type: 'hitCount' });
1933
}
1934
});
1935
1936
registerAction2(class extends ViewAction<BreakpointsView> {
1937
constructor() {
1938
super({
1939
id: 'debug.editBreakpointMode',
1940
viewId: BREAKPOINTS_VIEW_ID,
1941
title: localize('editMode', "Edit Mode..."),
1942
menu: [{
1943
id: MenuId.DebugBreakpointsContext,
1944
group: 'navigation',
1945
order: 20,
1946
when: ContextKeyExpr.and(
1947
CONTEXT_BREAKPOINT_HAS_MODES,
1948
ContextKeyExpr.or(CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('breakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('exceptionBreakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('instructionBreakpoint'))
1949
)
1950
}]
1951
});
1952
}
1953
1954
async runInView(accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IBreakpoint) {
1955
const debugService = accessor.get(IDebugService);
1956
const kind = getModeKindForBreakpoint(breakpoint);
1957
const modes = debugService.getModel().getBreakpointModes(kind);
1958
const picked = await accessor.get(IQuickInputService).pick(
1959
modes.map(mode => ({ label: mode.label, description: mode.description, mode: mode.mode })),
1960
{ placeHolder: localize('selectBreakpointMode', "Select Breakpoint Mode") }
1961
);
1962
1963
if (!picked) {
1964
return;
1965
}
1966
1967
if (kind === 'source') {
1968
const data = new Map<string, IBreakpointUpdateData>();
1969
data.set(breakpoint.getId(), { mode: picked.mode, modeLabel: picked.label });
1970
debugService.updateBreakpoints(breakpoint.originalUri, data, false);
1971
} else if (breakpoint instanceof InstructionBreakpoint) {
1972
debugService.removeInstructionBreakpoints(breakpoint.instructionReference, breakpoint.offset);
1973
debugService.addInstructionBreakpoint({ ...breakpoint.toJSON(), mode: picked.mode, modeLabel: picked.label });
1974
} else if (breakpoint instanceof ExceptionBreakpoint) {
1975
breakpoint.mode = picked.mode;
1976
breakpoint.modeLabel = picked.label;
1977
debugService.setExceptionBreakpointCondition(breakpoint, breakpoint.condition); // no-op to trigger a re-send
1978
}
1979
}
1980
});
1981
1982