From 541c97d33ac96973ff1645a62e80f2609a490e03 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 20 Jan 2023 11:26:35 -0600 Subject: [PATCH] Show breakpoint decorations in notebook For #169190 --- .../browser/breakpointEditorContribution.ts | 4 +- .../contrib/debug/notebookDebugDecorations.ts | 51 +++++++++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index be26cfbfa7fe9..6a2db0c047e39 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -17,6 +17,7 @@ import { dispose, disposeIfDisposable, IDisposable } from 'vs/base/common/lifecy import * as env from 'vs/base/common/platform'; import severity from 'vs/base/common/severity'; import { noBreakWhitespace } from 'vs/base/common/strings'; +import { ThemeIcon } from 'vs/base/common/themables'; import { withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -34,7 +35,6 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { ILabelService } from 'vs/platform/label/common/label'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; -import { ThemeIcon } from 'vs/base/common/themables'; import { getBreakpointMessageAndIcon } from 'vs/workbench/contrib/debug/browser/breakpointsView'; import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; import * as icons from 'vs/workbench/contrib/debug/browser/debugIcons'; @@ -794,7 +794,7 @@ registerThemingParticipant((theme, collector) => { } }); -const debugIconBreakpointForeground = registerColor('debugIcon.breakpointForeground', { dark: '#E51400', light: '#E51400', hcDark: '#E51400', hcLight: '#E51400' }, nls.localize('debugIcon.breakpointForeground', 'Icon color for breakpoints.')); +export const debugIconBreakpointForeground = registerColor('debugIcon.breakpointForeground', { dark: '#E51400', light: '#E51400', hcDark: '#E51400', hcLight: '#E51400' }, nls.localize('debugIcon.breakpointForeground', 'Icon color for breakpoints.')); const debugIconBreakpointDisabledForeground = registerColor('debugIcon.breakpointDisabledForeground', { dark: '#848484', light: '#848484', hcDark: '#848484', hcLight: '#848484' }, nls.localize('debugIcon.breakpointDisabledForeground', 'Icon color for disabled breakpoints.')); const debugIconBreakpointUnverifiedForeground = registerColor('debugIcon.breakpointUnverifiedForeground', { dark: '#848484', light: '#848484', hcDark: '#848484', hcLight: '#848484' }, nls.localize('debugIcon.breakpointUnverifiedForeground', 'Icon color for unverified breakpoints.')); const debugIconBreakpointCurrentStackframeForeground = registerColor('debugIcon.breakpointCurrentStackframeForeground', { dark: '#FFCC00', light: '#BE8700', hcDark: '#FFCC00', hcLight: '#BE8700' }, nls.localize('debugIcon.breakpointCurrentStackframeForeground', 'Icon color for the current breakpoint stack frame.')); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts b/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts index 621ec15d4c8f2..5b5d462fa562b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts @@ -5,6 +5,8 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IRange, Range } from 'vs/editor/common/core/range'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { debugIconBreakpointForeground } from 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; import { focusedStackFrameColor, topStackFrameColor } from 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; import { INotebookCellDecorationOptions, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, NotebookOverviewRulerLane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -33,16 +35,16 @@ export class PausedCellDecorationContribution extends Disposable implements INot ) { super(); - this._register(_debugService.getModel().onDidChangeCallStack(() => this.onDidChangeCallStack())); - this._register(_debugService.getViewModel().onDidFocusStackFrame(() => this.onDidChangeCallStack())); + this._register(_debugService.getModel().onDidChangeCallStack(() => this.updateExecutionDecorations())); + this._register(_debugService.getViewModel().onDidFocusStackFrame(() => this.updateExecutionDecorations())); this._register(_notebookExecutionStateService.onDidChangeCellExecution(e => { if (e.affectsNotebook(this._notebookEditor.textModel!.uri)) { - this.onDidChangeCallStack(); + this.updateExecutionDecorations(); } })); } - private onDidChangeCallStack(): void { + private updateExecutionDecorations(): void { const exes = this._notebookExecutionStateService.getCellExecutionsByHandleForNotebook(this._notebookEditor.textModel!.uri); const topFrameCellsAndRanges: ICellAndRange[] = []; @@ -141,3 +143,44 @@ export class PausedCellDecorationContribution extends Disposable implements INot } registerNotebookContribution(PausedCellDecorationContribution.id, PausedCellDecorationContribution); + +export class NotebookBreakpointDecorations extends Disposable implements INotebookEditorContribution { + static id: string = 'workbench.notebook.debug.notebookBreakpointDecorations'; + + private _currentDecorations: string[] = []; + + constructor( + private readonly _notebookEditor: INotebookEditor, + @IDebugService private readonly _debugService: IDebugService, + @IConfigurationService private readonly _configService: IConfigurationService, + ) { + super(); + this._register(_debugService.getModel().onDidChangeBreakpoints(() => this.updateDecorations())); + this._register(_configService.onDidChangeConfiguration(e => e.affectsConfiguration('debug.showBreakpointsInOverviewRuler') && this.updateDecorations())); + } + + private updateDecorations(): void { + const enabled = this._configService.getValue('debug.showBreakpointsInOverviewRuler'); + const newDecorations = enabled ? + this._debugService.getModel().getBreakpoints().map(breakpoint => { + const parsed = CellUri.parse(breakpoint.uri); + if (!parsed || parsed.notebook.toString() !== this._notebookEditor.textModel!.uri.toString()) { + return null; + } + + const options: INotebookCellDecorationOptions = { + overviewRuler: { + color: debugIconBreakpointForeground, + includeOutput: false, + modelRanges: [new Range(breakpoint.lineNumber, 0, breakpoint.lineNumber, 0)], + position: NotebookOverviewRulerLane.Left + } + }; + return { handle: parsed.handle, options }; + }).filter(x => !!x) as INotebookDeltaDecoration[] + : []; + this._currentDecorations = this._notebookEditor.deltaCellDecorations(this._currentDecorations, newDecorations); + } +} + +registerNotebookContribution(NotebookBreakpointDecorations.id, NotebookBreakpointDecorations);