Skip to content

Commit

Permalink
Add support for conditional exception breakpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
pisv committed Jun 7, 2023
1 parent c42adc2 commit 39da366
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 6 deletions.
8 changes: 8 additions & 0 deletions packages/debug/src/browser/breakpoint/breakpoint-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
}
}

updateExceptionBreakpoint(filter: string, options: Partial<Pick<ExceptionBreakpoint, 'condition' | 'enabled'>>): void {
const breakpoint = this.getExceptionBreakpoint(filter);
if (breakpoint) {
Object.assign(breakpoint, options);
this.fireOnDidChangeMarkers(BreakpointManager.EXCEPTION_URI);
}
}

protected functionBreakpoints: FunctionBreakpoint[] = [];

getFunctionBreakpoints(): FunctionBreakpoint[] {
Expand Down
2 changes: 2 additions & 0 deletions packages/debug/src/browser/breakpoint/breakpoint-marker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,14 @@ export namespace BreakpointMarker {

export interface ExceptionBreakpoint {
enabled: boolean;
condition?: string;
raw: DebugProtocol.ExceptionBreakpointsFilter;
}
export namespace ExceptionBreakpoint {
export function create(data: DebugProtocol.ExceptionBreakpointsFilter, origin?: ExceptionBreakpoint): ExceptionBreakpoint {
return {
enabled: origin ? origin.enabled : false,
condition: origin ? origin.condition : undefined,
raw: {
...(origin && origin.raw),
...data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { DebugBreakpoint } from './model/debug-breakpoint';
import { nls } from '@theia/core/lib/common/nls';
import { DebugInstructionBreakpoint } from './model/debug-instruction-breakpoint';
import { DebugConfiguration } from '../common/debug-configuration';
import { DebugExceptionBreakpoint } from './view/debug-exception-breakpoint';

export namespace DebugMenus {
export const DEBUG = [...MAIN_MENU_BAR, '6_debug'];
Expand Down Expand Up @@ -212,6 +213,11 @@ export namespace DebugCommands {
originalLabel: 'Edit Logpoint...',
label: nlsEditBreakpoint('Logpoint')
}, '', DEBUG_CATEGORY_KEY);
export const EDIT_BREAKPOINT_CONDITION = Command.toLocalizedCommand({
id: 'debug.breakpoint.editCondition',
category: DEBUG_CATEGORY,
label: 'Edit Condition...'
}, '', DEBUG_CATEGORY_KEY);
export const REMOVE_BREAKPOINT = Command.toLocalizedCommand({
id: 'debug.breakpoint.remove',
category: DEBUG_CATEGORY,
Expand Down Expand Up @@ -596,7 +602,8 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi

registerMenuActions(DebugBreakpointsWidget.EDIT_MENU,
DebugCommands.EDIT_BREAKPOINT,
DebugCommands.EDIT_LOGPOINT
DebugCommands.EDIT_LOGPOINT,
DebugCommands.EDIT_BREAKPOINT_CONDITION
);
registerMenuActions(DebugBreakpointsWidget.REMOVE_MENU,
DebugCommands.REMOVE_BREAKPOINT,
Expand Down Expand Up @@ -787,6 +794,16 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
isEnabled: () => !!this.selectedLogpoint,
isVisible: () => !!this.selectedLogpoint
});
registry.registerCommand(DebugCommands.EDIT_BREAKPOINT_CONDITION, {
execute: async () => {
const { selectedExceptionBreakpoint } = this;
if (selectedExceptionBreakpoint) {
await selectedExceptionBreakpoint.editCondition();
}
},
isEnabled: () => !!this.selectedExceptionBreakpoint?.data.raw.supportsCondition,
isVisible: () => !!this.selectedExceptionBreakpoint?.data.raw.supportsCondition
});
registry.registerCommand(DebugCommands.REMOVE_BREAKPOINT, {
execute: () => {
const selectedBreakpoint = this.selectedSettableBreakpoint;
Expand Down Expand Up @@ -1216,6 +1233,11 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
return this.selectedAnyBreakpoint;
}
}
get selectedExceptionBreakpoint(): DebugExceptionBreakpoint | undefined {
const { breakpoints } = this;
const selectedElement = breakpoints && breakpoints.selectedElement;
return selectedElement instanceof DebugExceptionBreakpoint ? selectedElement : undefined;
}

get selectedSettableBreakpoint(): DebugFunctionBreakpoint | DebugInstructionBreakpoint | DebugSourceBreakpoint | undefined {
const selected = this.selectedAnyBreakpoint;
Expand Down
19 changes: 16 additions & 3 deletions packages/debug/src/browser/debug-session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -693,13 +693,26 @@ export class DebugSession implements CompositeTreeElement {
}

protected async sendExceptionBreakpoints(): Promise<void> {
const filters = [];
const args: DebugProtocol.SetExceptionBreakpointsArguments = {
filters: []
};
if (this.capabilities.supportsExceptionFilterOptions) {
args.filterOptions = [];
}
const { filters, filterOptions } = args;
for (const breakpoint of this.breakpoints.getExceptionBreakpoints()) {
if (breakpoint.enabled) {
filters.push(breakpoint.raw.filter);
if (filterOptions) {
filterOptions.push({
filterId: breakpoint.raw.filter,
condition: breakpoint.condition
});
} else {
filters.push(breakpoint.raw.filter);
}
}
}
await this.sendRequest('setExceptionBreakpoints', { filters });
await this.sendRequest('setExceptionBreakpoints', args);
}

protected async sendFunctionBreakpoints(affectedUri: URI): Promise<void> {
Expand Down
29 changes: 27 additions & 2 deletions packages/debug/src/browser/view/debug-exception-breakpoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import * as React from '@theia/core/shared/react';
import { TreeElement } from '@theia/core/lib/browser/source-tree';
import { BreakpointManager } from '../breakpoint/breakpoint-manager';
import { ExceptionBreakpoint } from '../breakpoint/breakpoint-marker';
import { SingleTextInputDialog } from '@theia/core/lib/browser/dialogs';
import { TREE_NODE_INFO_CLASS } from '@theia/core/lib/browser';
import { nls } from '@theia/core';

export class DebugExceptionBreakpoint implements TreeElement {

Expand All @@ -31,13 +34,35 @@ export class DebugExceptionBreakpoint implements TreeElement {
}

render(): React.ReactNode {
return <div title={this.data.raw.label} className='theia-source-breakpoint'>
return <div title={this.data.raw.description || this.data.raw.label} className='theia-source-breakpoint'>
<span className='theia-debug-breakpoint-icon' />
<input type='checkbox' checked={this.data.enabled} onChange={this.toggle} />
<span className='line-info'>{this.data.raw.label}</span>
<span className='line-info'>
<span className='name'>{this.data.raw.label} </span>
{this.data.condition &&
<span title={nls.localizeByDefault('Expression condition: {0}', this.data.condition)}
className={'path ' + TREE_NODE_INFO_CLASS}>{this.data.condition} </span>}
</span>
</div>;
}

protected toggle = () => this.breakpoints.toggleExceptionBreakpoint(this.data.raw.filter);

async editCondition(): Promise<void> {
const inputDialog = new SingleTextInputDialog({
title: this.data.raw.label,
placeholder: this.data.raw.conditionDescription,
initialValue: this.data.condition
});
let condition = await inputDialog.open();
if (condition === undefined) {
return;
}
if (condition === '') {
condition = undefined;
}
if (condition !== this.data.condition) {
this.breakpoints.updateExceptionBreakpoint(this.data.raw.filter, { condition });
}
}
}

0 comments on commit 39da366

Please sign in to comment.