diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index bd7c4e0e56f03..c129b52c32808 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -6,7 +6,7 @@ import * as resources from 'vs/base/common/resources'; import * as dom from 'vs/base/browser/dom'; import { IAction } from 'vs/base/common/actions'; -import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugModel, IDataBreakpoint, BREAKPOINTS_VIEW_ID, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_EXCEPTION_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, IBaseBreakpoint, IBreakpointEditorContribution, BREAKPOINT_EDITOR_CONTRIBUTION_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugModel, IDataBreakpoint, BREAKPOINTS_VIEW_ID, CONTEXT_BREAKPOINT_ITEM_TYPE, CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_IN_DEBUG_MODE, IBaseBreakpoint, IBreakpointEditorContribution, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_INPUT_FOCUSED } from 'vs/workbench/contrib/debug/common/debug'; import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -24,7 +24,7 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; +import { ViewPane, ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService, ContextKeyEqualsExpr, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Gesture } from 'vs/base/browser/touch'; @@ -61,6 +61,11 @@ export function getExpandedBodySize(model: IDebugModel, countLimit: number): num } type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint; +interface InputBoxData { + breakpoint: IFunctionBreakpoint | IExceptionBreakpoint; + type: 'condition' | 'hitCount' | 'name'; +} + export class BreakpointsView extends ViewPane { private list!: WorkbenchList; @@ -68,7 +73,9 @@ export class BreakpointsView extends ViewPane { private ignoreLayout = false; private menu: IMenu; private breakpointItemType: IContextKey; - private exceptionBreakpointSupportsCondition: IContextKey; + private breakpointSupportsCondition: IContextKey; + private _inputBoxData: InputBoxData | undefined; + breakpointInputFocused: IContextKey; constructor( options: IViewletViewOptions, @@ -92,30 +99,25 @@ export class BreakpointsView extends ViewPane { this.menu = menuService.createMenu(MenuId.DebugBreakpointsContext, contextKeyService); this._register(this.menu); this.breakpointItemType = CONTEXT_BREAKPOINT_ITEM_TYPE.bindTo(contextKeyService); - this.exceptionBreakpointSupportsCondition = CONTEXT_EXCEPTION_BREAKPOINT_SUPPORTS_CONDITION.bindTo(contextKeyService); + this.breakpointSupportsCondition = CONTEXT_BREAKPOINT_SUPPORTS_CONDITION.bindTo(contextKeyService); + this.breakpointInputFocused = CONTEXT_BREAKPOINT_INPUT_FOCUSED.bindTo(contextKeyService); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); - this._register(this.debugService.getViewModel().onDidSelectBreakpoint(breakpoint => { - if (breakpoint) { - // Only react when a new breakpoint is selected - to reduce refresh, since other times a refresh is not needed - this.onBreakpointsChange(); - } - })); } - public renderBody(container: HTMLElement): void { + renderBody(container: HTMLElement): void { super.renderBody(container); this.element.classList.add('debug-pane'); container.classList.add('debug-breakpoints'); - const delegate = new BreakpointsDelegate(this.debugService); + const delegate = new BreakpointsDelegate(this); this.list = >this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [ - this.instantiationService.createInstance(BreakpointsRenderer), - new ExceptionBreakpointsRenderer(this.menu, this.debugService), - new ExceptionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService), - this.instantiationService.createInstance(FunctionBreakpointsRenderer), + this.instantiationService.createInstance(BreakpointsRenderer, this.menu, this.breakpointSupportsCondition), + new ExceptionBreakpointsRenderer(this.menu, this.breakpointSupportsCondition, this.debugService), + new ExceptionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService), + this.instantiationService.createInstance(FunctionBreakpointsRenderer, this.menu, this.breakpointSupportsCondition), this.instantiationService.createInstance(DataBreakpointsRenderer), - new FunctionBreakpointInputRenderer(this.debugService, this.contextViewService, this.themeService, this.labelService) + new FunctionBreakpointInputRenderer(this, this.debugService, this.contextViewService, this.themeService, this.labelService) ], { identityProvider: { getId: (element: IEnablement) => element.getId() }, multipleSelectionSupport: false, @@ -153,9 +155,9 @@ export class BreakpointsView extends ViewPane { if (e.element instanceof Breakpoint) { openBreakpointSource(e.element, e.sideBySide, e.editorOptions.preserveFocus || false, e.editorOptions.pinned || !e.editorOptions.preserveFocus, this.debugService, this.editorService); } - if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.debugService.getViewModel().getSelectedBreakpoint()) { + if (e.browserEvent instanceof MouseEvent && e.browserEvent.detail === 2 && e.element instanceof FunctionBreakpoint && e.element !== this.inputBoxData?.breakpoint) { // double click - this.debugService.getViewModel().setSelectedBreakpoint(e.element); + this.renderInputBox({ breakpoint: e.element, type: 'name' }); } })); @@ -173,13 +175,23 @@ export class BreakpointsView extends ViewPane { })); } - public focus(): void { + focus(): void { super.focus(); if (this.list) { this.list.domFocus(); } } + renderInputBox(data: InputBoxData | undefined): void { + this._inputBoxData = data; + this.onBreakpointsChange(); + this._inputBoxData = undefined; + } + + get inputBoxData(): InputBoxData | undefined { + return this._inputBoxData; + } + protected layoutBody(height: number, width: number): void { if (this.ignoreLayout) { return; @@ -202,7 +214,9 @@ export class BreakpointsView extends ViewPane { const type = element instanceof Breakpoint ? 'breakpoint' : element instanceof ExceptionBreakpoint ? 'exceptionBreakpoint' : element instanceof FunctionBreakpoint ? 'functionBreakpoint' : element instanceof DataBreakpoint ? 'dataBreakpoint' : undefined; this.breakpointItemType.set(type); - this.exceptionBreakpointSupportsCondition.set(element instanceof ExceptionBreakpoint && element.supportsCondition); + const session = this.debugService.getViewModel().focusedSession; + const conditionNotSupported = (element instanceof ExceptionBreakpoint && !element.supportsCondition) || (session && session.capabilities.supportsConditionalBreakpoints); + this.breakpointSupportsCondition.set(!conditionNotSupported); const secondary: IAction[] = []; const actionsDisposable = createAndFillInContextMenuActions(this.menu, { arg: e.element, shouldForwardArgs: false }, { primary: [], secondary }, g => /^inline/.test(g)); @@ -251,7 +265,7 @@ export class BreakpointsView extends ViewPane { class BreakpointsDelegate implements IListVirtualDelegate { - constructor(private debugService: IDebugService) { + constructor(private view: BreakpointsView) { // noop } @@ -264,16 +278,16 @@ class BreakpointsDelegate implements IListVirtualDelegate { return BreakpointsRenderer.ID; } if (element instanceof FunctionBreakpoint) { - const selected = this.debugService.getViewModel().getSelectedBreakpoint(); - if (!element.name || (selected && selected.getId() === element.getId())) { + const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint; + if (!element.name || (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId())) { return FunctionBreakpointInputRenderer.ID; } return FunctionBreakpointsRenderer.ID; } if (element instanceof ExceptionBreakpoint) { - const selected = this.debugService.getViewModel().getSelectedBreakpoint(); - if (selected && selected.getId() === element.getId()) { + const inputBoxBreakpoint = this.view.inputBoxData?.breakpoint; + if (inputBoxBreakpoint && inputBoxBreakpoint.getId() === element.getId()) { return ExceptionBreakpointInputRenderer.ID; } return ExceptionBreakpointsRenderer.ID; @@ -309,26 +323,31 @@ interface IExceptionBreakpointTemplateData extends IBaseBreakpointTemplateData { condition: HTMLElement; } +interface IFunctionBreakpointTemplateData extends IBaseBreakpointWithIconTemplateData { + condition: HTMLElement; +} + interface IFunctionBreakpointInputTemplateData { inputBox: InputBox; checkbox: HTMLInputElement; icon: HTMLElement; breakpoint: IFunctionBreakpoint; - reactedOnEvent: boolean; toDispose: IDisposable[]; + type: 'hitCount' | 'condition' | 'name'; } interface IExceptionBreakpointInputTemplateData { inputBox: InputBox; checkbox: HTMLInputElement; breakpoint: IExceptionBreakpoint; - reactedOnEvent: boolean; toDispose: IDisposable[]; } class BreakpointsRenderer implements IListRenderer { constructor( + private menu: IMenu, + private breakpointSupportsCondition: IContextKey, @IDebugService private readonly debugService: IDebugService, @ILabelService private readonly labelService: ILabelService ) { @@ -348,6 +367,7 @@ class BreakpointsRenderer implements IListRenderer { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); })); @@ -358,6 +378,8 @@ class BreakpointsRenderer implements IListRenderer /^inline/.test(g))); + data.actionBar.clear(); + data.actionBar.push(primary, { icon: true, label: false }); + } + + disposeElement(_element: IBreakpoint, _index: number, templateData: IBreakpointTemplateData): void { + dispose(templateData.elementDisposable); } disposeTemplate(templateData: IBreakpointTemplateData): void { @@ -395,6 +428,7 @@ class ExceptionBreakpointsRenderer implements IListRenderer, private debugService: IDebugService ) { // noop @@ -437,6 +471,7 @@ class ExceptionBreakpointsRenderer implements IListRenderer /^inline/.test(g))); data.actionBar.clear(); data.actionBar.push(primary, { icon: true, label: false }); @@ -451,9 +486,11 @@ class ExceptionBreakpointsRenderer implements IListRenderer { +class FunctionBreakpointsRenderer implements IListRenderer { constructor( + private menu: IMenu, + private breakpointSupportsCondition: IContextKey, @IDebugService private readonly debugService: IDebugService, @ILabelService private readonly labelService: ILabelService ) { @@ -466,13 +503,16 @@ class FunctionBreakpointsRenderer implements IListRenderer { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); })); @@ -481,11 +521,15 @@ class FunctionBreakpointsRenderer implements IListRenderer /^inline/.test(g))); + data.actionBar.clear(); + data.actionBar.push(primary, { icon: true, label: false }); } - disposeTemplate(templateData: IBaseBreakpointWithIconTemplateData): void { + disposeElement(_element: IFunctionBreakpoint, _index: number, templateData: IFunctionBreakpointTemplateData): void { + dispose(templateData.elementDisposable); + } + + disposeTemplate(templateData: IFunctionBreakpointTemplateData): void { dispose(templateData.toDispose); } } @@ -566,13 +624,12 @@ class DataBreakpointsRenderer implements IListRenderer { constructor( + private view: BreakpointsView, private debugService: IDebugService, private contextViewService: IContextViewService, private themeService: IThemeService, private labelService: ILabelService - ) { - // noop - } + ) { } static readonly ID = 'functionbreakpointinput'; @@ -589,22 +646,33 @@ class FunctionBreakpointInputRenderer implements IListRenderer { - if (!template.reactedOnEvent) { - template.reactedOnEvent = true; - this.debugService.getViewModel().setSelectedBreakpoint(undefined); - if (inputBox.value && (renamed || template.breakpoint.name)) { - this.debugService.renameFunctionBreakpoint(template.breakpoint.getId(), renamed ? inputBox.value : template.breakpoint.name); + const wrapUp = (success: boolean) => { + this.view.breakpointInputFocused.set(false); + const id = template.breakpoint.getId(); + + if (success) { + if (template.type === 'name') { + this.debugService.updateFunctionBreakpoint(id, { name: inputBox.value }); + } + if (template.type === 'condition') { + this.debugService.updateFunctionBreakpoint(id, { condition: inputBox.value }); + } + if (template.type === 'hitCount') { + this.debugService.updateFunctionBreakpoint(id, { hitCondition: inputBox.value }); + } + } else { + if (template.type === 'name' && !template.breakpoint.name) { + this.debugService.removeFunctionBreakpoints(id); } else { - this.debugService.removeFunctionBreakpoints(template.breakpoint.getId()); + this.view.renderInputBox(undefined); } } }; @@ -634,7 +702,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { data.inputBox.focus(); data.inputBox.select(); @@ -656,6 +739,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { constructor( + private view: BreakpointsView, private debugService: IDebugService, private contextViewService: IContextViewService, private themeService: IThemeService @@ -677,6 +761,7 @@ class ExceptionBreakpointInputRenderer implements IListRenderer { - if (!template.reactedOnEvent) { - template.reactedOnEvent = true; - this.debugService.getViewModel().setSelectedBreakpoint(undefined); - let newCondition = template.breakpoint.condition; - if (success) { - newCondition = inputBox.value !== '' ? inputBox.value : undefined; - } - this.debugService.setExceptionBreakpointCondition(template.breakpoint, newCondition); + this.view.breakpointInputFocused.set(false); + let newCondition = template.breakpoint.condition; + if (success) { + newCondition = inputBox.value !== '' ? inputBox.value : undefined; } + this.debugService.setExceptionBreakpointCondition(template.breakpoint, newCondition); }; toDispose.push(dom.addStandardDisposableListener(inputBox.inputElement, 'keydown', (e: IKeyboardEvent) => { @@ -720,7 +802,6 @@ class ExceptionBreakpointInputRenderer implements IListRenderer { constructor() { super({ - id: 'workbench.action.debug.editExceptionBreakpointCondition', + id: 'debug.editBreakpoint', + viewId: BREAKPOINTS_VIEW_ID, title: localize('editCondition', "Edit Condition..."), icon: Codicon.edit, + precondition: CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, menu: [{ id: MenuId.DebugBreakpointsContext, group: 'navigation', - order: 10, - when: ContextKeyExpr.and(CONTEXT_EXCEPTION_BREAKPOINT_SUPPORTS_CONDITION, CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('exceptionBreakpoint')) + order: 10 }, { id: MenuId.DebugBreakpointsContext, group: 'inline', - order: 10, + order: 10 }] }); } - async run(accessor: ServicesAccessor, breakpoint: ExceptionBreakpoint): Promise { + async runInView(accessor: ServicesAccessor, view: BreakpointsView, breakpoint: ExceptionBreakpoint | Breakpoint | FunctionBreakpoint): Promise { const debugService = accessor.get(IDebugService); - debugService.getViewModel().setSelectedBreakpoint(breakpoint); + const editorService = accessor.get(IEditorService); + if (breakpoint instanceof Breakpoint) { + const editor = await openBreakpointSource(breakpoint, false, false, true, debugService, editorService); + if (editor) { + const codeEditor = editor.getControl(); + if (isCodeEditor(codeEditor)) { + codeEditor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(breakpoint.lineNumber, breakpoint.column); + } + } + } else { + view.renderInputBox({ breakpoint, type: 'condition' }); + } } }); -registerAction2(class extends Action2 { +registerAction2(class extends ViewAction { constructor() { super({ - id: 'debug.editBreakpoint', - title: localize('editBreakpoint', "Edit Breakpoint..."), + id: 'debug.editFunctionBreakpoint', + viewId: BREAKPOINTS_VIEW_ID, + title: localize('editBreakpoint', "Edit Function Breakpoint..."), menu: [{ id: MenuId.DebugBreakpointsContext, - group: 'navigation', + group: '1_breakpoints', order: 10, - when: ContextKeyExpr.or(CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('breakpoint'), CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('functionBreakpoint')) + when: CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('functionBreakpoint') }] }); } - async run(accessor: ServicesAccessor, breakpoint: ExceptionBreakpoint): Promise { - const debugService = accessor.get(IDebugService); - const editorService = accessor.get(IEditorService); - if (breakpoint instanceof Breakpoint) { - const editor = await openBreakpointSource(breakpoint, false, false, true, debugService, editorService); - if (editor) { - const codeEditor = editor.getControl(); - if (isCodeEditor(codeEditor)) { - codeEditor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(breakpoint.lineNumber, breakpoint.column); - } - } - } else { - debugService.getViewModel().setSelectedBreakpoint(breakpoint); - } + runInView(_accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IFunctionBreakpoint) { + view.renderInputBox({ breakpoint, type: 'name' }); + } +}); + +registerAction2(class extends ViewAction { + constructor() { + super({ + id: 'debug.editFunctionBreakpointHitCount', + viewId: BREAKPOINTS_VIEW_ID, + title: localize('editHitCount', "Edit Hit Count..."), + precondition: CONTEXT_BREAKPOINT_SUPPORTS_CONDITION, + menu: [{ + id: MenuId.DebugBreakpointsContext, + group: 'navigation', + order: 20, + when: CONTEXT_BREAKPOINT_ITEM_TYPE.isEqualTo('functionBreakpoint') + }] + }); + } + + runInView(_accessor: ServicesAccessor, view: BreakpointsView, breakpoint: IFunctionBreakpoint) { + view.renderInputBox({ breakpoint, type: 'hitCount' }); } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index ad88803c84c70..3b353cd611863 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -9,7 +9,7 @@ import { List } from 'vs/base/browser/ui/list/listWidget'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -525,7 +525,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.removeBreakpoint', weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_SELECTED.toNegated()), + when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_INPUT_FOCUSED.toNegated()), primary: KeyCode.Delete, mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace }, handler: (accessor) => { diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 6753ea9c7f350..30b922bf352f2 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -900,12 +900,11 @@ export class DebugService implements IDebugService { } addFunctionBreakpoint(name?: string, id?: string): void { - const newFunctionBreakpoint = this.model.addFunctionBreakpoint(name || '', id); - this.viewModel.setSelectedBreakpoint(newFunctionBreakpoint); + this.model.addFunctionBreakpoint(name || '', id); } - async renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { - this.model.renameFunctionBreakpoint(id, newFunctionName); + async updateFunctionBreakpoint(id: string, update: { name?: string, hitCondition?: string, condition?: string }): Promise { + this.model.updateFunctionBreakpoint(id, update); this.debugStorage.storeBreakpoints(this.model); await this.sendFunctionBreakpoints(); } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index e72a367d5a744..5487c7f7e11db 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -319,7 +319,7 @@ } .debug-pane .debug-breakpoints .breakpoint > .file-path, -.debug-pane .debug-breakpoints .breakpoint.exception > .condition { +.debug-pane .debug-breakpoints .breakpoint > .condition { opacity: 0.7; margin-left: 0.9em; flex: 1; diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 3b42925105060..835ee034693aa 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -50,11 +50,11 @@ export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey('wat export const CONTEXT_WATCH_EXPRESSIONS_EXIST = new RawContextKey('watchExpressionsExist', false); export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey('variablesFocused', true); export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey('expressionSelected', false); -export const CONTEXT_BREAKPOINT_SELECTED = new RawContextKey('breakpointSelected', false); +export const CONTEXT_BREAKPOINT_INPUT_FOCUSED = new RawContextKey('breakpointInputFocused', false); export const CONTEXT_CALLSTACK_ITEM_TYPE = new RawContextKey('callStackItemType', undefined); export const CONTEXT_WATCH_ITEM_TYPE = new RawContextKey('watchItemType', undefined); export const CONTEXT_BREAKPOINT_ITEM_TYPE = new RawContextKey('breakpointItemType', undefined); -export const CONTEXT_EXCEPTION_BREAKPOINT_SUPPORTS_CONDITION = new RawContextKey('exceptionBreakpointSupportsCondition', false); +export const CONTEXT_BREAKPOINT_SUPPORTS_CONDITION = new RawContextKey('breakpointSupportsCondition', false); export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey('loadedScriptsSupported', false); export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey('loadedScriptsItemType', undefined); export const CONTEXT_FOCUSED_SESSION_IS_ATTACH = new RawContextKey('focusedSessionIsAttach', false); @@ -444,9 +444,7 @@ export interface IViewModel extends ITreeElement { readonly focusedStackFrame: IStackFrame | undefined; getSelectedExpression(): IExpression | undefined; - getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined; setSelectedExpression(expression: IExpression | undefined): void; - setSelectedBreakpoint(functionBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void; updateViews(): void; isMultiSessionView(): boolean; @@ -454,7 +452,6 @@ export interface IViewModel extends ITreeElement { onDidFocusSession: Event; onDidFocusStackFrame: Event<{ stackFrame: IStackFrame | undefined, explicit: boolean }>; onDidSelectExpression: Event; - onDidSelectBreakpoint: Event; onWillUpdateViews: Event; } @@ -852,10 +849,10 @@ export interface IDebugService { addFunctionBreakpoint(name?: string, id?: string): void; /** - * Renames an already existing function breakpoint. + * Updates an already existing function breakpoint. * Notifies debug adapter of breakpoint changes. */ - renameFunctionBreakpoint(id: string, newFunctionName: string): Promise; + updateFunctionBreakpoint(id: string, update: { name?: string, hitCondition?: string, condition?: string }): Promise; /** * Removes all function breakpoints. If id is passed only removes the function breakpoint with the passed id. diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 5927016f99f11..cf1382723251e 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -1244,10 +1244,18 @@ export class DebugModel implements IDebugModel { return newFunctionBreakpoint; } - renameFunctionBreakpoint(id: string, name: string): void { + updateFunctionBreakpoint(id: string, update: { name?: string, hitCondition?: string, condition?: string }): void { const functionBreakpoint = this.functionBreakpoints.find(fbp => fbp.getId() === id); if (functionBreakpoint) { - functionBreakpoint.name = name; + if (typeof update.name === 'string') { + functionBreakpoint.name = update.name; + } + if (typeof update.condition === 'string') { + functionBreakpoint.condition = update.condition; + } + if (typeof update.hitCondition === 'string') { + functionBreakpoint.hitCondition = update.hitCondition; + } this._onDidChangeBreakpoints.fire({ changed: [functionBreakpoint], sessionOnly: false }); } } diff --git a/src/vs/workbench/contrib/debug/common/debugViewModel.ts b/src/vs/workbench/contrib/debug/common/debugViewModel.ts index 95edb422d4d12..a17e611856009 100644 --- a/src/vs/workbench/contrib/debug/common/debugViewModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, IExceptionBreakpoint, CONTEXT_MULTI_SESSION_DEBUG } from 'vs/workbench/contrib/debug/common/debug'; +import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, IDebugSession, IThread, IExpression, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_MULTI_SESSION_DEBUG } from 'vs/workbench/contrib/debug/common/debug'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils'; @@ -16,14 +16,11 @@ export class ViewModel implements IViewModel { private _focusedSession: IDebugSession | undefined; private _focusedThread: IThread | undefined; private selectedExpression: IExpression | undefined; - private selectedBreakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined; private readonly _onDidFocusSession = new Emitter(); private readonly _onDidFocusStackFrame = new Emitter<{ stackFrame: IStackFrame | undefined, explicit: boolean }>(); private readonly _onDidSelectExpression = new Emitter(); - private readonly _onDidSelectBreakpoint = new Emitter(); private readonly _onWillUpdateViews = new Emitter(); private expressionSelectedContextKey!: IContextKey; - private breakpointSelectedContextKey!: IContextKey; private loadedScriptsSupportedContextKey!: IContextKey; private stepBackSupportedContextKey!: IContextKey; private focusedSessionIsAttach!: IContextKey; @@ -36,7 +33,6 @@ export class ViewModel implements IViewModel { constructor(private contextKeyService: IContextKeyService) { contextKeyService.bufferChangeEvents(() => { this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService); - this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService); this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService); this.stepBackSupportedContextKey = CONTEXT_STEP_BACK_SUPPORTED.bindTo(contextKeyService); this.focusedSessionIsAttach = CONTEXT_FOCUSED_SESSION_IS_ATTACH.bindTo(contextKeyService); @@ -113,14 +109,6 @@ export class ViewModel implements IViewModel { return this._onDidSelectExpression.event; } - get onDidSelectBreakpoint(): Event { - return this._onDidSelectBreakpoint.event; - } - - getSelectedBreakpoint(): IFunctionBreakpoint | IExceptionBreakpoint | undefined { - return this.selectedBreakpoint; - } - updateViews(): void { this._onWillUpdateViews.fire(); } @@ -129,12 +117,6 @@ export class ViewModel implements IViewModel { return this._onWillUpdateViews.event; } - setSelectedBreakpoint(breakpoint: IFunctionBreakpoint | IExceptionBreakpoint | undefined): void { - this.selectedBreakpoint = breakpoint; - this.breakpointSelectedContextKey.set(!!breakpoint); - this._onDidSelectBreakpoint.fire(breakpoint); - } - isMultiSessionView(): boolean { return !!this.multiSessionDebug.get(); } diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index 1a2bed97418c2..fec15f39fa825 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -153,8 +153,8 @@ suite('Debug - Breakpoints', () => { test('function breakpoints', () => { model.addFunctionBreakpoint('foo', '1'); model.addFunctionBreakpoint('bar', '2'); - model.renameFunctionBreakpoint('1', 'fooUpdated'); - model.renameFunctionBreakpoint('2', 'barUpdated'); + model.updateFunctionBreakpoint('1', { name: 'fooUpdated' }); + model.updateFunctionBreakpoint('2', { name: 'barUpdated' }); const functionBps = model.getFunctionBreakpoints(); assert.equal(functionBps[0].name, 'fooUpdated'); diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index 48abf8237e5a0..a5be81b2c1d5a 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -24,29 +24,29 @@ export const mockUriIdentityService = new UriIdentityService(fileService); export class MockDebugService implements IDebugService { - public _serviceBrand: undefined; + _serviceBrand: undefined; - public get state(): State { + get state(): State { throw new Error('not implemented'); } - public get onWillNewSession(): Event { + get onWillNewSession(): Event { throw new Error('not implemented'); } - public get onDidNewSession(): Event { + get onDidNewSession(): Event { throw new Error('not implemented'); } - public get onDidEndSession(): Event { + get onDidEndSession(): Event { throw new Error('not implemented'); } - public get onDidChangeState(): Event { + get onDidChangeState(): Event { throw new Error('not implemented'); } - public getConfigurationManager(): IConfigurationManager { + getConfigurationManager(): IConfigurationManager { throw new Error('not implemented'); } @@ -58,7 +58,7 @@ export class MockDebugService implements IDebugService { throw new Error('Method not implemented.'); } - public focusStackFrame(focusedStackFrame: IStackFrame): Promise { + focusStackFrame(focusedStackFrame: IStackFrame): Promise { throw new Error('not implemented'); } @@ -66,23 +66,23 @@ export class MockDebugService implements IDebugService { throw new Error('not implemented'); } - public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): Promise { + addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): Promise { throw new Error('not implemented'); } - public updateBreakpoints(uri: uri, data: Map, sendOnResourceSaved: boolean): Promise { + updateBreakpoints(uri: uri, data: Map, sendOnResourceSaved: boolean): Promise { throw new Error('not implemented'); } - public enableOrDisableBreakpoints(enabled: boolean): Promise { + enableOrDisableBreakpoints(enabled: boolean): Promise { throw new Error('not implemented'); } - public setBreakpointsActivated(): Promise { + setBreakpointsActivated(): Promise { throw new Error('not implemented'); } - public removeBreakpoints(): Promise { + removeBreakpoints(): Promise { throw new Error('not implemented'); } @@ -90,15 +90,15 @@ export class MockDebugService implements IDebugService { throw new Error('Method not implemented.'); } - public addFunctionBreakpoint(): void { } + addFunctionBreakpoint(): void { } - public moveWatchExpression(id: string, position: number): void { } + moveWatchExpression(id: string, position: number): void { } - public renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { + updateFunctionBreakpoint(id: string, update: { name?: string, hitCondition?: string, condition?: string }): Promise { throw new Error('not implemented'); } - public removeFunctionBreakpoints(id?: string): Promise { + removeFunctionBreakpoints(id?: string): Promise { throw new Error('not implemented'); } @@ -109,47 +109,47 @@ export class MockDebugService implements IDebugService { throw new Error('Method not implemented.'); } - public addReplExpression(name: string): Promise { + addReplExpression(name: string): Promise { throw new Error('not implemented'); } - public removeReplExpressions(): void { } + removeReplExpressions(): void { } - public addWatchExpression(name?: string): Promise { + addWatchExpression(name?: string): Promise { throw new Error('not implemented'); } - public renameWatchExpression(id: string, newName: string): Promise { + renameWatchExpression(id: string, newName: string): Promise { throw new Error('not implemented'); } - public removeWatchExpressions(id?: string): void { } + removeWatchExpressions(id?: string): void { } - public startDebugging(launch: ILaunch, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise { + startDebugging(launch: ILaunch, configOrName?: IConfig | string, options?: IDebugSessionOptions): Promise { return Promise.resolve(true); } - public restartSession(): Promise { + restartSession(): Promise { throw new Error('not implemented'); } - public stopSession(): Promise { + stopSession(): Promise { throw new Error('not implemented'); } - public getModel(): IDebugModel { + getModel(): IDebugModel { throw new Error('not implemented'); } - public getViewModel(): IViewModel { + getViewModel(): IViewModel { throw new Error('not implemented'); } - public logToRepl(session: IDebugSession, value: string): void { } + logToRepl(session: IDebugSession, value: string): void { } - public sourceIsNotAvailable(uri: uri): void { } + sourceIsNotAvailable(uri: uri): void { } - public tryToAutoFocusStackFrame(thread: IThread): Promise { + tryToAutoFocusStackFrame(thread: IThread): Promise { throw new Error('not implemented'); } } @@ -391,14 +391,14 @@ export class MockRawSession { disconnected = false; sessionLengthInSeconds: number = 0; - public readyForBreakpoints = true; - public emittedStopped = true; + readyForBreakpoints = true; + emittedStopped = true; - public getLengthInSeconds(): number { + getLengthInSeconds(): number { return 100; } - public stackTrace(args: DebugProtocol.StackTraceArguments): Promise { + stackTrace(args: DebugProtocol.StackTraceArguments): Promise { return Promise.resolve({ seq: 1, type: 'response', @@ -416,19 +416,19 @@ export class MockRawSession { }); } - public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise { + exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise { throw new Error('not implemented'); } - public launchOrAttach(args: IConfig): Promise { + launchOrAttach(args: IConfig): Promise { throw new Error('not implemented'); } - public scopes(args: DebugProtocol.ScopesArguments): Promise { + scopes(args: DebugProtocol.ScopesArguments): Promise { throw new Error('not implemented'); } - public variables(args: DebugProtocol.VariablesArguments): Promise { + variables(args: DebugProtocol.VariablesArguments): Promise { throw new Error('not implemented'); } @@ -436,87 +436,87 @@ export class MockRawSession { return Promise.resolve(null!); } - public custom(request: string, args: any): Promise { + custom(request: string, args: any): Promise { throw new Error('not implemented'); } - public terminate(restart = false): Promise { + terminate(restart = false): Promise { throw new Error('not implemented'); } - public disconnect(restart?: boolean): Promise { + disconnect(restart?: boolean): Promise { throw new Error('not implemented'); } - public threads(): Promise { + threads(): Promise { throw new Error('not implemented'); } - public stepIn(args: DebugProtocol.StepInArguments): Promise { + stepIn(args: DebugProtocol.StepInArguments): Promise { throw new Error('not implemented'); } - public stepOut(args: DebugProtocol.StepOutArguments): Promise { + stepOut(args: DebugProtocol.StepOutArguments): Promise { throw new Error('not implemented'); } - public stepBack(args: DebugProtocol.StepBackArguments): Promise { + stepBack(args: DebugProtocol.StepBackArguments): Promise { throw new Error('not implemented'); } - public continue(args: DebugProtocol.ContinueArguments): Promise { + continue(args: DebugProtocol.ContinueArguments): Promise { throw new Error('not implemented'); } - public reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise { + reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise { throw new Error('not implemented'); } - public pause(args: DebugProtocol.PauseArguments): Promise { + pause(args: DebugProtocol.PauseArguments): Promise { throw new Error('not implemented'); } - public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise { + terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise { throw new Error('not implemented'); } - public setVariable(args: DebugProtocol.SetVariableArguments): Promise { + setVariable(args: DebugProtocol.SetVariableArguments): Promise { throw new Error('not implemented'); } - public restartFrame(args: DebugProtocol.RestartFrameArguments): Promise { + restartFrame(args: DebugProtocol.RestartFrameArguments): Promise { throw new Error('not implemented'); } - public completions(args: DebugProtocol.CompletionsArguments): Promise { + completions(args: DebugProtocol.CompletionsArguments): Promise { throw new Error('not implemented'); } - public next(args: DebugProtocol.NextArguments): Promise { + next(args: DebugProtocol.NextArguments): Promise { throw new Error('not implemented'); } - public source(args: DebugProtocol.SourceArguments): Promise { + source(args: DebugProtocol.SourceArguments): Promise { throw new Error('not implemented'); } - public loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise { + loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise { throw new Error('not implemented'); } - public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise { + setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise { throw new Error('not implemented'); } - public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise { + setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise { throw new Error('not implemented'); } - public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { + setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { throw new Error('not implemented'); } - public readonly onDidStop: Event = null!; + readonly onDidStop: Event = null!; } export class MockDebugAdapter extends AbstractDebugAdapter {