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 <[email protected]>
  • Loading branch information
akosyakov committed Jul 25, 2019
1 parent 9c4fd33 commit 038dc54
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 62 deletions.
13 changes: 10 additions & 3 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 @@ -96,7 +103,7 @@ export class BreakpointManager extends MarkerManager<SourceBreakpoint> {
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
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
2 changes: 1 addition & 1 deletion 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 Down
72 changes: 50 additions & 22 deletions packages/debug/src/browser/editor/debug-editor-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { DebugEditor } from './debug-editor';
import { DebugHoverWidget, createDebugHoverWidgetContainer } from './debug-hover-widget';
import { DebugBreakpointWidget } from './debug-breakpoint-widget';
import { DebugExceptionWidget } from './debug-exception-widget';
import { DebugProtocol } from 'vscode-debugprotocol';

export const DebugEditorModelFactory = Symbol('DebugEditorModelFactory');
export type DebugEditorModelFactory = (editor: DebugEditor) => DebugEditorModel;
Expand Down Expand Up @@ -192,7 +193,8 @@ export class DebugEditorModel implements Disposable {
}
protected createBreakpointDecoration(breakpoint: SourceBreakpoint): monaco.editor.IModelDeltaDecoration {
const lineNumber = breakpoint.raw.line;
const range = new monaco.Range(lineNumber, 1, lineNumber, 2);
const column = breakpoint.raw.column;
const range = typeof column === 'number' ? new monaco.Range(lineNumber, column, lineNumber, column + 1) : new monaco.Range(lineNumber, 1, lineNumber, 2);
return {
range,
options: {
Expand All @@ -218,14 +220,16 @@ export class DebugEditorModel implements Disposable {
}
protected createCurrentBreakpointDecoration(breakpoint: DebugSourceBreakpoint): monaco.editor.IModelDeltaDecoration {
const lineNumber = breakpoint.line;
const range = new monaco.Range(lineNumber, 1, lineNumber, 1);
const column = breakpoint.column;
const range = typeof column === 'number' ? new monaco.Range(lineNumber, column, lineNumber, column + 1) : new monaco.Range(lineNumber, 1, lineNumber, 1)
const { className, message } = breakpoint.getDecoration();
return {
range,
options: {
glyphMarginClassName: className,
glyphMarginHoverMessage: message.map(value => ({ value })),
stickiness: DebugEditorModel.STICKINESS
stickiness: DebugEditorModel.STICKINESS,
beforeContentClassName: typeof column === 'number' ? `theia-debug-breakpoint-column ${className}-column` : undefined
}
};
}
Expand Down Expand Up @@ -257,9 +261,10 @@ export class DebugEditorModel implements Disposable {
const range = this.editor.getControl().getModel().getDecorationRange(decoration);
if (range && !lines.has(range.startLineNumber)) {
const line = range.startLineNumber;
const column = range.startColumn;
const oldRange = this.breakpointRanges.get(decoration);
const oldBreakpoint = oldRange && this.breakpoints.getBreakpoint(uri, oldRange.startLineNumber);
const breakpoint = SourceBreakpoint.create(uri, { line, column: 1 }, oldBreakpoint);
const oldBreakpoint = oldRange && this.breakpoints.getInlineBreakpoint(uri, oldRange.startLineNumber, oldRange.startColumn);
const breakpoint = SourceBreakpoint.create(uri, { line, column }, oldBreakpoint);
breakpoints.push(breakpoint);
lines.add(line);
}
Expand All @@ -271,39 +276,62 @@ export class DebugEditorModel implements Disposable {
get position(): monaco.Position {
return this._position || this.editor.getControl().getPosition();
}

get breakpoint(): DebugSourceBreakpoint | undefined {
return this.getBreakpoint();
return this.inlineBreakpoint || this.getLineBreakpoints()[0];
}
protected getBreakpoint(position: monaco.Position = this.position) {
return this.sessions.getBreakpoint(this.uri, position.lineNumber);

get inlineBreakpoint(): DebugSourceBreakpoint | undefined {
return this.getInlineBreakpoint();
}

protected getInlineBreakpoint(position: monaco.Position = this.position): DebugSourceBreakpoint | undefined {
return this.sessions.getInlineBreakpoint(this.uri, position.lineNumber, position.column);
}

protected getLineBreakpoints(position: monaco.Position = this.position): DebugSourceBreakpoint[] {
return this.sessions.getLineBreakpoints(this.uri, position.lineNumber);
}

protected addBreakpoint(raw: DebugProtocol.SourceBreakpoint): void {
this.breakpoints.addBreakpoint(SourceBreakpoint.create(this.uri, raw));
}

toggleBreakpoint(): void {
this.doToggleBreakpoint();
}
protected doToggleBreakpoint(position: monaco.Position = this.position) {
const breakpoint = this.getBreakpoint(position);
if (breakpoint) {
breakpoint.remove();
const { lineNumber } = position;
const breakpoints = this.getLineBreakpoints(position);
if (breakpoints.length) {
for (const breakpoint of breakpoints) {
breakpoint.remove();
}
} else {
this.breakpoints.addBreakpoint(SourceBreakpoint.create(this.uri, {
line: position.lineNumber,
column: 1
}));
this.addBreakpoint({ line: lineNumber });
}
}

addInlineBreakpoint(): void {
const { position } = this;
const { lineNumber, column } = position;
const breakpoint = this.getInlineBreakpoint(position);
if (breakpoint) {
return;
}
this.addBreakpoint({ line: lineNumber, column });
}

acceptBreakpoint(): void {
const { position, values } = this.breakpointWidget;
if (position && values) {
const breakpoint = this.getBreakpoint(position);
const breakpoint = position.column > 0 ? this.getInlineBreakpoint(position) : this.getLineBreakpoints(position)[0];
if (breakpoint) {
breakpoint.updateOrigins(values);
} else {
this.breakpoints.addBreakpoint(SourceBreakpoint.create(this.uri, {
line: position.lineNumber,
column: 1,
...values
}));
const { lineNumber } = position;
const column = position.column > 0 ? position.column : undefined;
this.addBreakpoint({ line: lineNumber, column, ...values });
}
this.breakpointWidget.hide();
}
Expand Down Expand Up @@ -342,7 +370,7 @@ export class DebugEditorModel implements Disposable {
protected createHintDecorations(event: monaco.editor.IEditorMouseEvent): monaco.editor.IModelDeltaDecoration[] {
if (event.target && event.target.type === monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN) {
const lineNumber = event.target.position.lineNumber;
if (!!this.sessions.getBreakpoint(this.uri, lineNumber)) {
if (this.getLineBreakpoints(event.target.position).length) {
return [];
}
return [{
Expand Down
19 changes: 19 additions & 0 deletions packages/debug/src/browser/editor/debug-editor-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ export class DebugEditorService {
return breakpoint && breakpoint.enabled;
}

get inlineBreakpoint(): DebugSourceBreakpoint | undefined {
return this.model && this.model.inlineBreakpoint;
}

get anyBreakpoint(): DebugSourceBreakpoint | undefined {
return this.model && this.model.breakpoint;
}
Expand All @@ -112,13 +116,28 @@ export class DebugEditorService {
model.toggleBreakpoint();
}
}

removeBreakpoint(): void {
const { anyBreakpoint } = this;
if (anyBreakpoint) {
anyBreakpoint.remove();
}
}

setBreakpointEnabled(enabled: boolean): void {
const { anyBreakpoint } = this;
if (anyBreakpoint) {
anyBreakpoint.setEnabled(enabled);
}
}

addInlineBreakpoint(): void {
const { model } = this;
if (model) {
model.addInlineBreakpoint();
}
}

showHover(): void {
const { model } = this;
if (model) {
Expand Down
Loading

0 comments on commit 038dc54

Please sign in to comment.