Skip to content

Commit

Permalink
[debug] fix ##3807: added inline breakpoints support
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosyakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Jul 26, 2019
1 parent 9c4fd33 commit 5413b27
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 96 deletions.
15 changes: 11 additions & 4 deletions packages/debug/src/browser/breakpoint/breakpoint-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,17 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
return result;
}

getBreakpoint(uri: URI, line: number): SourceBreakpoint | undefined {
const marker = this.findMarkers({
getLineBreakpoints(uri: URI, line: number): SourceBreakpoint[] {
return this.findMarkers({
uri,
dataFilter: breakpoint => breakpoint.raw.line === line
}).map(({ data }) => data);
}

getInlineBreakpoint(uri: URI, line: number, column: number): SourceBreakpoint | undefined {
const marker = this.findMarkers({
uri,
dataFilter: breakpoint => breakpoint.raw.line === line && breakpoint.raw.column === column
})[0];
return marker && marker.data;
}
Expand All @@ -90,13 +97,13 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
}

setBreakpoints(uri: URI, breakpoints: SourceBreakpoint[]): void {
this.setMarkers(uri, this.owner, breakpoints.sort((a, b) => a.raw.line - b.raw.line));
this.setMarkers(uri, this.owner, breakpoints.sort((a, b) => (a.raw.line - b.raw.line) || ((a.raw.column || 0) - (b.raw.column || 0))));
}

addBreakpoint(breakpoint: SourceBreakpoint): boolean {
const uri = new URI(breakpoint.uri);
const breakpoints = this.getBreakpoints(uri);
const newBreakpoints = breakpoints.filter(({ raw }) => raw.line !== breakpoint.raw.line);
const newBreakpoints = breakpoints.filter(({ raw }) => !(raw.line === breakpoint.raw.line && raw.column === breakpoint.raw.column));
if (breakpoints.length === newBreakpoints.length) {
newBreakpoints.push(breakpoint);
this.setBreakpoints(uri, newBreakpoints);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@ import { DebugWatchWidget } from './view/debug-watch-widget';
import { DebugWatchExpression } from './view/debug-watch-expression';
import { DebugWatchManager } from './debug-watch-manager';
import { DebugFunctionBreakpoint } from './model/debug-function-breakpoint';
import { DebugBreakpoint } from './model/debug-breakpoint';

export namespace DebugMenus {
export const DEBUG = [...MAIN_MENU_BAR, '6_debug'];
export const DEBUG_CONTROLS = [...DEBUG, 'a_controls'];
export const DEBUG_CONFIGURATION = [...DEBUG, 'b_configuration'];
export const DEBUG_THREADS = [...DEBUG, 'c_threads'];
export const DEBUG_SESSIONS = [...DEBUG, 'd_sessions'];
export const DEBUG_BREAKPOINTS = [...DEBUG, 'e_breakpoints'];
export const DEBUG_BREAKPOINT = [...DEBUG, 'e_breakpoint'];
export const DEBUG_NEW_BREAKPOINT = [...DEBUG_BREAKPOINT, 'a_new_breakpoint'];
export const DEBUG_BREAKPOINTS = [...DEBUG, 'f_breakpoints'];
}

export namespace DebugCommands {
Expand Down Expand Up @@ -140,6 +143,11 @@ export namespace DebugCommands {
category: DEBUG_CATEGORY,
label: 'Toggle Breakpoint',
};
export const INLINE_BREAKPOINT: Command = {
id: 'editor.debug.action.inlineBreakpoint',
category: DEBUG_CATEGORY,
label: 'Inline Breakpoint',
};
export const ADD_CONDITIONAL_BREAKPOINT: Command = {
id: 'debug.breakpoint.add.conditional',
category: DEBUG_CATEGORY,
Expand Down Expand Up @@ -503,8 +511,17 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
DebugCommands.CONTINUE_ALL,
DebugCommands.PAUSE_ALL
);
registerMenuActions(DebugMenus.DEBUG_BREAKPOINT,
DebugCommands.TOGGLE_BREAKPOINT
);
menus.registerSubmenu(DebugMenus.DEBUG_NEW_BREAKPOINT, 'New Breakpoint');
registerMenuActions(DebugMenus.DEBUG_NEW_BREAKPOINT,
DebugCommands.ADD_CONDITIONAL_BREAKPOINT,
DebugCommands.INLINE_BREAKPOINT,
DebugCommands.ADD_FUNCTION_BREAKPOINT,
DebugCommands.ADD_LOGPOINT,
);
registerMenuActions(DebugMenus.DEBUG_BREAKPOINTS,
DebugCommands.TOGGLE_BREAKPOINT,
DebugCommands.ENABLE_ALL_BREAKPOINTS,
DebugCommands.DISABLE_ALL_BREAKPOINTS,
DebugCommands.REMOVE_ALL_BREAKPOINTS
Expand Down Expand Up @@ -718,6 +735,10 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
execute: () => this.editors.toggleBreakpoint(),
isEnabled: () => !!this.editors.model
});
registry.registerCommand(DebugCommands.INLINE_BREAKPOINT, {
execute: () => this.editors.addInlineBreakpoint(),
isEnabled: () => !!this.editors.model && !this.editors.inlineBreakpoint
});
registry.registerCommand(DebugCommands.ADD_CONDITIONAL_BREAKPOINT, {
execute: () => this.editors.addBreakpoint('condition'),
isEnabled: () => !!this.editors.model && !this.editors.anyBreakpoint
Expand Down Expand Up @@ -745,13 +766,15 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
});
registry.registerCommand(DebugCommands.EDIT_BREAKPOINT, {
execute: async () => {
const { selectedBreakpoint } = this;
const { selectedBreakpoint, selectedFunctionBreakpoint } = this;
if (selectedBreakpoint) {
await this.editors.editBreakpoint(selectedBreakpoint);
} else if (selectedFunctionBreakpoint) {
await selectedFunctionBreakpoint.open();
}
},
isEnabled: () => !!this.selectedBreakpoint,
isVisible: () => !!this.selectedBreakpoint
isEnabled: () => !!this.selectedBreakpoint || !!this.selectedFunctionBreakpoint,
isVisible: () => !!this.selectedBreakpoint || !!this.selectedFunctionBreakpoint
});
registry.registerCommand(DebugCommands.EDIT_LOGPOINT, {
execute: async () => {
Expand All @@ -765,13 +788,13 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
});
registry.registerCommand(DebugCommands.REMOVE_BREAKPOINT, {
execute: () => {
const { selectedBreakpoint } = this;
const selectedBreakpoint = this.selectedBreakpoint || this.selectedFunctionBreakpoint;
if (selectedBreakpoint) {
selectedBreakpoint.remove();
}
},
isEnabled: () => !!this.selectedBreakpoint,
isVisible: () => !!this.selectedBreakpoint
isEnabled: () => !!this.selectedBreakpoint || !!this.selectedFunctionBreakpoint,
isVisible: () => !!this.selectedBreakpoint || !!this.selectedFunctionBreakpoint,
});
registry.registerCommand(DebugCommands.REMOVE_LOGPOINT, {
execute: () => {
Expand Down Expand Up @@ -856,7 +879,7 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
isVisible: () => !this.editors.anyBreakpoint
});
registry.registerCommand(DebugEditorContextCommands.REMOVE_BREAKPOINT, {
execute: () => this.editors.toggleBreakpoint(),
execute: () => this.editors.removeBreakpoint(),
isEnabled: () => !!this.editors.breakpoint,
isVisible: () => !!this.editors.breakpoint
});
Expand All @@ -876,7 +899,7 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
isVisible: () => !!this.editors.breakpointEnabled
});
registry.registerCommand(DebugEditorContextCommands.REMOVE_LOGPOINT, {
execute: () => this.editors.toggleBreakpoint(),
execute: () => this.editors.removeBreakpoint(),
isEnabled: () => !!this.editors.logpoint,
isVisible: () => !!this.editors.logpoint
});
Expand Down Expand Up @@ -1018,6 +1041,11 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
keybinding: 'f9',
context: EditorKeybindingContexts.editorTextFocus
});
keybindings.registerKeybinding({
command: DebugCommands.INLINE_BREAKPOINT.id,
keybinding: 'shift+f9',
context: EditorKeybindingContexts.editorTextFocus
});

keybindings.registerKeybinding({
command: DebugBreakpointWidgetCommands.ACCEPT.id,
Expand Down Expand Up @@ -1170,17 +1198,22 @@ export class DebugFrontendApplicationContribution extends AbstractViewContributi
const { currentWidget } = this.shell;
return currentWidget instanceof DebugBreakpointsWidget && currentWidget || undefined;
}
get selectedAnyBreakpoint(): DebugSourceBreakpoint | undefined {
get selectedAnyBreakpoint(): DebugBreakpoint | undefined {
const { breakpoints } = this;
return breakpoints && breakpoints.selectedElement instanceof DebugSourceBreakpoint && breakpoints.selectedElement || undefined;
const selectedElement = breakpoints && breakpoints.selectedElement;
return selectedElement instanceof DebugBreakpoint ? selectedElement : undefined;
}
get selectedBreakpoint(): DebugSourceBreakpoint | undefined {
const breakpoint = this.selectedAnyBreakpoint;
return breakpoint && !breakpoint.logMessage ? breakpoint : undefined;
return breakpoint && breakpoint instanceof DebugSourceBreakpoint && !breakpoint.logMessage ? breakpoint : undefined;
}
get selectedLogpoint(): DebugSourceBreakpoint | undefined {
const breakpoint = this.selectedAnyBreakpoint;
return breakpoint && !!breakpoint.logMessage ? breakpoint : undefined;
return breakpoint && breakpoint instanceof DebugSourceBreakpoint && !!breakpoint.logMessage ? breakpoint : undefined;
}
get selectedFunctionBreakpoint(): DebugFunctionBreakpoint | undefined {
const breakpoint = this.selectedAnyBreakpoint;
return breakpoint && breakpoint instanceof DebugFunctionBreakpoint ? breakpoint : undefined;
}

get variables(): DebugVariablesWidget | undefined {
Expand Down
7 changes: 6 additions & 1 deletion packages/debug/src/browser/debug-frontend-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import { DebugPrefixConfiguration } from './debug-prefix-configuration';
import { CommandContribution } from '@theia/core/lib/common/command';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { DebugWatchManager } from './debug-watch-manager';
import { MonacoEditorService } from '@theia/monaco/lib/browser/monaco-editor-service';
import { DebugBreakpointWidget } from './editor/debug-breakpoint-widget';

export default new ContainerModule((bind: interfaces.Bind) => {
bind(DebugCallStackItemTypeKey).toDynamicValue(({ container }) =>
Expand All @@ -66,7 +68,10 @@ export default new ContainerModule((bind: interfaces.Bind) => {
bind(DebugEditorModelFactory).toDynamicValue(({ container }) => <DebugEditorModelFactory>(editor =>
DebugEditorModel.createModel(container, editor)
)).inSingletonScope();
bind(DebugEditorService).toSelf().inSingletonScope();
bind(DebugEditorService).toSelf().inSingletonScope().onActivation((context, service) => {
context.container.get(MonacoEditorService).registerDecorationType(DebugBreakpointWidget.PLACEHOLDER_DECORATION, {});
return service;
});

bind(DebugSessionWidgetFactory).toDynamicValue(({ container }) =>
(options: DebugViewOptions) => DebugSessionWidget.createWidget(container, options)
Expand Down
17 changes: 14 additions & 3 deletions packages/debug/src/browser/debug-session-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,12 +368,23 @@ export class DebugSessionManager {
return this.breakpoints.findMarkers({ uri }).map(({ data }) => new DebugSourceBreakpoint(data, { labelProvider, breakpoints, editorManager }));
}

getBreakpoint(uri: URI, line: number): DebugSourceBreakpoint | undefined {
getLineBreakpoints(uri: URI, line: number): DebugSourceBreakpoint[] {
const session = this.currentSession;
if (session && session.state > DebugState.Initializing) {
return session.getSourceBreakpoints(uri).filter(breakpoint => breakpoint.line === line)[0];
return session.getSourceBreakpoints(uri).filter(breakpoint => breakpoint.line === line);
}
const origin = this.breakpoints.getBreakpoint(uri, line);
const { labelProvider, breakpoints, editorManager } = this;
return this.breakpoints.getLineBreakpoints(uri, line).map(origin =>
new DebugSourceBreakpoint(origin, { labelProvider, breakpoints, editorManager })
);
}

getInlineBreakpoint(uri: URI, line: number, column: number): DebugSourceBreakpoint | undefined {
const session = this.currentSession;
if (session && session.state > DebugState.Initializing) {
return session.getSourceBreakpoints(uri).filter(breakpoint => breakpoint.line === line && breakpoint.column === column)[0];
}
const origin = this.breakpoints.getInlineBreakpoint(uri, line, column);
const { labelProvider, breakpoints, editorManager } = this;
return origin && new DebugSourceBreakpoint(origin, { labelProvider, breakpoints, editorManager });
}
Expand Down
21 changes: 14 additions & 7 deletions packages/debug/src/browser/debug-session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ export class DebugSession implements CompositeTreeElement {
if (body.reason === 'new') {
if (raw.source && typeof raw.line === 'number') {
const uri = DebugSource.toUri(raw.source);
const origin = SourceBreakpoint.create(uri, { line: raw.line, column: 1 });
const origin = SourceBreakpoint.create(uri, { line: raw.line, column: raw.column });
if (this.breakpoints.addBreakpoint(origin)) {
const breakpoints = this.getSourceBreakpoints(uri);
const breakpoint = new DebugSourceBreakpoint(origin, this.asDebugBreakpointOptions());
Expand All @@ -536,7 +536,14 @@ export class DebugSession implements CompositeTreeElement {
const toUpdate = this.findBreakpoint(b => b.idFromAdapter === raw.id);
if (toUpdate) {
toUpdate.update({ raw });
this.fireDidChangeBreakpoints(toUpdate.uri);
if (toUpdate instanceof DebugSourceBreakpoint) {
const sourceBreakpoints = this.getSourceBreakpoints(toUpdate.uri);
// in order to dedup again if a debugger converted line breakpoint to inline breakpoint
// i.e. assigned a column to a line breakpoint
this.setSourceBreakpoints(toUpdate.uri, sourceBreakpoints);
} else {
this.fireDidChangeBreakpoints(toUpdate.uri);
}
}
}
} finally {
Expand Down Expand Up @@ -658,19 +665,19 @@ export class DebugSession implements CompositeTreeElement {
this.setBreakpoints(uri, distinct);
}
protected dedupSourceBreakpoints(all: DebugSourceBreakpoint[]): DebugSourceBreakpoint[] {
const lines = new Map<number, DebugSourceBreakpoint>();
const positions = new Map<string, DebugSourceBreakpoint>();
for (const breakpoint of all) {
let primary = lines.get(breakpoint.line) || breakpoint;
let primary = positions.get(breakpoint.renderPosition()) || breakpoint;
if (primary !== breakpoint) {
let secondary = breakpoint;
if (secondary.raw && secondary.raw.line === secondary.origin.raw.line) {
if (secondary.raw && secondary.raw.line === secondary.origin.raw.line && secondary.raw.column === secondary.origin.raw.column) {
[primary, secondary] = [breakpoint, primary];
}
primary.origins.push(...secondary.origins);
}
lines.set(primary.line, primary);
positions.set(primary.renderPosition(), primary);
}
return [...lines.values()];
return [...positions.values()];
}
protected *getAffectedUris(uri?: URI): IterableIterator<URI> {
if (uri) {
Expand Down
3 changes: 0 additions & 3 deletions packages/debug/src/browser/editor/debug-breakpoint-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,6 @@ export class DebugBreakpointWidget implements Disposable {
}

}

monaco.services.StaticServices.codeEditorService.get().registerDecorationType(DebugBreakpointWidget.PLACEHOLDER_DECORATION, {});

export namespace DebugBreakpointWidget {
export type Context = keyof Pick<DebugProtocol.SourceBreakpoint, 'condition' | 'hitCondition' | 'logMessage'>;
}
Loading

0 comments on commit 5413b27

Please sign in to comment.