Skip to content

Commit 3ce7ccf

Browse files
authored
debug: initial support of breakpoint modes (microsoft#205251)
This supports breakpoint modes on exception, instruction, and source breakpoints. It doesn't yet do data breakpoints since I was having trouble figuring out a good user flow for that. You can test this on the `connor4312/breakpoint-modes` branch of mock-debug, which I'll merge in after the next DAP release. ![](https://memes.peet.io/img/24-02-68cd0222-8ef5-4d39-aa51-81ba5b8c2405.png)
1 parent e587755 commit 3ce7ccf

19 files changed

+734
-284
lines changed

src/vs/workbench/api/browser/mainThreadDebugService.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,15 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
217217
column: l.character > 0 ? l.character + 1 : undefined, // a column value of 0 results in an omitted column attribute; see #46784
218218
condition: l.condition,
219219
hitCondition: l.hitCondition,
220-
logMessage: l.logMessage
220+
logMessage: l.logMessage,
221+
mode: l.mode,
221222
}
222223
);
223224
this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps);
224225
} else if (dto.type === 'function') {
225-
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id);
226+
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id, dto.mode);
226227
} else if (dto.type === 'data') {
227-
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType);
228+
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType, dto.mode);
228229
}
229230
}
230231
return Promise.resolve();

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2277,11 +2277,13 @@ export interface IBreakpointDto {
22772277
condition?: string;
22782278
hitCondition?: string;
22792279
logMessage?: string;
2280+
mode?: string;
22802281
}
22812282

22822283
export interface IFunctionBreakpointDto extends IBreakpointDto {
22832284
type: 'function';
22842285
functionName: string;
2286+
mode?: string;
22852287
}
22862288

22872289
export interface IDataBreakpointDto extends IBreakpointDto {
@@ -2291,6 +2293,7 @@ export interface IDataBreakpointDto extends IBreakpointDto {
22912293
label: string;
22922294
accessTypes?: DebugProtocol.DataBreakpointAccessType[];
22932295
accessType: DebugProtocol.DataBreakpointAccessType;
2296+
mode?: string;
22942297
}
22952298

22962299
export interface ISourceBreakpointDto extends IBreakpointDto {
@@ -2317,6 +2320,7 @@ export interface ISourceMultiBreakpointDto {
23172320
logMessage?: string;
23182321
line: number;
23192322
character: number;
2323+
mode?: string;
23202324
}[];
23212325
}
23222326

src/vs/workbench/api/common/extHostDebugService.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
427427
hitCondition: bp.hitCondition,
428428
logMessage: bp.logMessage,
429429
line: bp.location.range.start.line,
430-
character: bp.location.range.start.character
430+
character: bp.location.range.start.character,
431+
mode: bp.mode,
431432
});
432433
} else if (bp instanceof FunctionBreakpoint) {
433434
dtos.push({
@@ -437,7 +438,8 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
437438
hitCondition: bp.hitCondition,
438439
logMessage: bp.logMessage,
439440
condition: bp.condition,
440-
functionName: bp.functionName
441+
functionName: bp.functionName,
442+
mode: bp.mode,
441443
});
442444
}
443445
}
@@ -713,12 +715,12 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
713715
if (id && !this._breakpoints.has(id)) {
714716
let bp: Breakpoint;
715717
if (bpd.type === 'function') {
716-
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
718+
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage, bpd.mode);
717719
} else if (bpd.type === 'data') {
718-
bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage);
720+
bp = new DataBreakpoint(bpd.label, bpd.dataId, bpd.canPersist, bpd.enabled, bpd.hitCondition, bpd.condition, bpd.logMessage, bpd.mode);
719721
} else {
720722
const uri = URI.revive(bpd.uri);
721-
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
723+
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage, bpd.mode);
722724
}
723725
setBreakpointId(bp, id);
724726
this._breakpoints.set(id, bp);

src/vs/workbench/api/common/extHostTypes.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2918,8 +2918,9 @@ export class Breakpoint {
29182918
readonly condition?: string;
29192919
readonly hitCondition?: string;
29202920
readonly logMessage?: string;
2921+
readonly mode?: string;
29212922

2922-
protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
2923+
protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) {
29232924
this.enabled = typeof enabled === 'boolean' ? enabled : true;
29242925
if (typeof condition === 'string') {
29252926
this.condition = condition;
@@ -2930,6 +2931,9 @@ export class Breakpoint {
29302931
if (typeof logMessage === 'string') {
29312932
this.logMessage = logMessage;
29322933
}
2934+
if (typeof mode === 'string') {
2935+
this.mode = mode;
2936+
}
29332937
}
29342938

29352939
get id(): string {
@@ -2944,8 +2948,8 @@ export class Breakpoint {
29442948
export class SourceBreakpoint extends Breakpoint {
29452949
readonly location: Location;
29462950

2947-
constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
2948-
super(enabled, condition, hitCondition, logMessage);
2951+
constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) {
2952+
super(enabled, condition, hitCondition, logMessage, mode);
29492953
if (location === null) {
29502954
throw illegalArgument('location');
29512955
}
@@ -2957,8 +2961,8 @@ export class SourceBreakpoint extends Breakpoint {
29572961
export class FunctionBreakpoint extends Breakpoint {
29582962
readonly functionName: string;
29592963

2960-
constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
2961-
super(enabled, condition, hitCondition, logMessage);
2964+
constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) {
2965+
super(enabled, condition, hitCondition, logMessage, mode);
29622966
this.functionName = functionName;
29632967
}
29642968
}
@@ -2969,8 +2973,8 @@ export class DataBreakpoint extends Breakpoint {
29692973
readonly dataId: string;
29702974
readonly canPersist: boolean;
29712975

2972-
constructor(label: string, dataId: string, canPersist: boolean, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
2973-
super(enabled, condition, hitCondition, logMessage);
2976+
constructor(label: string, dataId: string, canPersist: boolean, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string, mode?: string) {
2977+
super(enabled, condition, hitCondition, logMessage, mode);
29742978
if (!dataId) {
29752979
throw illegalArgument('dataId');
29762980
}

src/vs/workbench/contrib/debug/browser/breakpointWidget.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
8585
private selectBreakpointContainer!: HTMLElement;
8686
private input!: IActiveCodeEditor;
8787
private selectBreakpointBox!: SelectBox;
88+
private selectModeBox?: SelectBox;
8889
private toDispose: lifecycle.IDisposable[];
8990
private conditionInput = '';
9091
private hitCountInput = '';
9192
private logMessageInput = '';
93+
private modeInput?: DebugProtocol.BreakpointMode;
9294
private breakpoint: IBreakpoint | undefined;
9395
private context: Context;
9496
private heightInPx: number | undefined;
@@ -216,6 +218,8 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
216218
this.updateContextInput();
217219
});
218220

221+
this.createModesInput(container);
222+
219223
this.inputContainer = $('.inputContainer');
220224
this.createBreakpointInput(dom.append(container, this.inputContainer));
221225

@@ -232,6 +236,33 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
232236
setTimeout(() => this.focusInput(), 150);
233237
}
234238

239+
private createModesInput(container: HTMLElement) {
240+
const modes = this.debugService.getModel().getBreakpointModes('source');
241+
if (modes.length <= 1) {
242+
return;
243+
}
244+
245+
const sb = this.selectModeBox = new SelectBox(
246+
[
247+
{ text: nls.localize('bpMode', 'Mode'), isDisabled: true },
248+
...modes.map(mode => ({ text: mode.label, description: mode.description })),
249+
],
250+
modes.findIndex(m => m.mode === this.breakpoint?.mode) + 1,
251+
this.contextViewService,
252+
defaultSelectBoxStyles,
253+
);
254+
this.toDispose.push(sb);
255+
this.toDispose.push(sb.onDidSelect(e => {
256+
this.modeInput = modes[e.index - 1];
257+
}));
258+
259+
const modeWrapper = $('.select-mode-container');
260+
const selectionWrapper = $('.select-box-container');
261+
dom.append(modeWrapper, selectionWrapper);
262+
sb.render(selectionWrapper);
263+
dom.append(container, modeWrapper);
264+
}
265+
235266
private createTriggerBreakpointInput(container: HTMLElement) {
236267
const breakpoints = this.debugService.getModel().getBreakpoints().filter(bp => bp !== this.breakpoint);
237268

@@ -404,10 +435,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
404435
if (success) {
405436
// if there is already a breakpoint on this location - remove it.
406437

407-
let condition = this.breakpoint && this.breakpoint.condition;
408-
let hitCondition = this.breakpoint && this.breakpoint.hitCondition;
409-
let logMessage = this.breakpoint && this.breakpoint.logMessage;
410-
let triggeredBy = this.breakpoint && this.breakpoint.triggeredBy;
438+
let condition = this.breakpoint?.condition;
439+
let hitCondition = this.breakpoint?.hitCondition;
440+
let logMessage = this.breakpoint?.logMessage;
441+
let triggeredBy = this.breakpoint?.triggeredBy;
442+
let mode = this.breakpoint?.mode;
443+
let modeLabel = this.breakpoint?.modeLabel;
411444

412445
this.rememberInput();
413446

@@ -420,6 +453,10 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
420453
if (this.logMessageInput || this.context === Context.LOG_MESSAGE) {
421454
logMessage = this.logMessageInput;
422455
}
456+
if (this.selectModeBox) {
457+
mode = this.modeInput?.mode;
458+
modeLabel = this.modeInput?.label;
459+
}
423460
if (this.context === Context.TRIGGER_POINT) {
424461
// currently, trigger points don't support additional conditions:
425462
condition = undefined;
@@ -434,7 +471,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
434471
condition,
435472
hitCondition,
436473
logMessage,
437-
triggeredBy
474+
triggeredBy,
475+
mode,
476+
modeLabel,
438477
});
439478
this.debugService.updateBreakpoints(this.breakpoint.originalUri, data, false).then(undefined, onUnexpectedError);
440479
} else {
@@ -447,7 +486,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi
447486
condition,
448487
hitCondition,
449488
logMessage,
450-
triggeredBy
489+
triggeredBy,
490+
mode,
491+
modeLabel,
451492
}]);
452493
}
453494
}

0 commit comments

Comments
 (0)