From 2d76e758eb1a971c477a6f161f8cdedbe61fc70c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Jun 2021 17:04:21 -0500 Subject: [PATCH] Don't show cell animations/timer while paused --- .../breakpoints/notebookBreakpoints.ts | 58 ++++++++++++++++++- .../executionStatusBarItemController.ts | 15 +++-- .../browser/view/renderers/cellRenderer.ts | 2 +- .../contrib/notebook/common/notebookCommon.ts | 1 + .../notebook/common/notebookOptions.ts | 2 +- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts b/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts index 9d53f119f69e3..76de82bc30e1d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints.ts @@ -7,11 +7,13 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; import { isEqual } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IDebugService } from 'vs/workbench/contrib/debug/common/debug'; +import { Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -97,3 +99,57 @@ class NotebookBreakpoints extends Disposable implements IWorkbenchContribution { } Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookBreakpoints, LifecyclePhase.Restored); + +class NotebookCellPausing extends Disposable implements IWorkbenchContribution { + private readonly _pausedCells = new Set(); + + constructor( + @IDebugService _debugService: IDebugService, + @INotebookService private readonly _notebookService: INotebookService + ) { + super(); + + this._register(_debugService.getModel().onDidChangeCallStack(async () => { + const newPausedCells = new Set(); + for (const session of _debugService.getModel().getSessions()) { + for (const thread of session.getAllThreads()) { + const callStack = thread.getCallStack(); + if (!callStack.length) { + await (thread as Thread).fetchCallStack(); + } + + thread.getCallStack().forEach(sf => { + const parsed = CellUri.parse(sf.source.uri); + if (parsed) { + newPausedCells.add(sf.source.uri.toString()); + this.editIsPaused(sf.source.uri, true); + } + }); + } + } + + for (const uri of this._pausedCells) { + if (!newPausedCells.has(uri)) { + this.editIsPaused(URI.parse(uri), false); + this._pausedCells.delete(uri); + } + } + + newPausedCells.forEach(cell => this._pausedCells.add(cell)); + })); + } + + private editIsPaused(cellUri: URI, isPaused: boolean) { + const parsed = CellUri.parse(cellUri); + if (parsed) { + const notebookModel = this._notebookService.getNotebookTextModel(parsed.notebook); + notebookModel?.applyEdits([{ + editType: CellEditType.PartialInternalMetadata, + handle: parsed.handle, + internalMetadata: { isPaused }, + }], true, undefined, () => undefined, undefined); + } + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(NotebookCellPausing, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts index 355f5b11824c8..64e0a424e0fbf 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts @@ -16,7 +16,7 @@ import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/brow import { cellStatusIconError, cellStatusIconSuccess } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { getCodeCellExecutionContextKeyService } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem, NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class NotebookStatusBarController extends Disposable { private readonly _visibleCells = new Map(); @@ -104,11 +104,11 @@ class ExecutionStateCellStatusBarItem extends Disposable { * Returns undefined if there should be no change, and an empty array if all items should be removed. */ private _getItemsForCell(cell: ICellViewModel): INotebookCellStatusBarItem[] | undefined { - if (this._currentExecutingStateTimer) { + if (this._currentExecutingStateTimer && !cell.internalMetadata.isPaused) { return; } - const item = this._getItemForState(cell.internalMetadata.runState, cell.internalMetadata.lastRunSuccess); + const item = this._getItemForState(cell.internalMetadata); // Show the execution spinner for a minimum time if (cell.internalMetadata.runState === NotebookCellExecutionState.Executing) { @@ -123,7 +123,8 @@ class ExecutionStateCellStatusBarItem extends Disposable { return item ? [item] : []; } - private _getItemForState(runState: NotebookCellExecutionState | undefined, lastRunSuccess: boolean | undefined): INotebookCellStatusBarItem | undefined { + private _getItemForState(internalMetadata: NotebookCellInternalMetadata): INotebookCellStatusBarItem | undefined { + const { runState, lastRunSuccess, isPaused } = internalMetadata; if (!runState && lastRunSuccess) { return { text: '$(notebook-state-success)', @@ -149,7 +150,7 @@ class ExecutionStateCellStatusBarItem extends Disposable { }; } else if (runState === NotebookCellExecutionState.Executing) { return { - text: '$(notebook-state-executing~spin)', + text: `$(notebook-state-executing${isPaused ? '' : '~spin'})`, tooltip: localize('notebook.cell.status.executing', "Executing"), alignment: CellStatusbarAlignment.Left, priority: Number.MAX_SAFE_INTEGER @@ -196,7 +197,9 @@ class TimerCellStatusBarItem extends Disposable { private async _update() { let item: INotebookCellStatusBarItem | undefined; const state = this._cell.internalMetadata.runState; - if (state === NotebookCellExecutionState.Executing) { + if (this._cell.internalMetadata.isPaused) { + item = undefined; + } else if (state === NotebookCellExecutionState.Executing) { const startTime = this._cell.internalMetadata.runStartTime; const adjustment = this._cell.internalMetadata.runStartTimeAdjustment; if (typeof startTime === 'number') { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index f4329f5a5ee18..d326acbcc29fe 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -911,7 +911,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const internalMetadata = element.internalMetadata; this.updateExecutionOrder(internalMetadata, templateData); - if (internalMetadata.runState === NotebookCellExecutionState.Executing) { + if (internalMetadata.runState === NotebookCellExecutionState.Executing && !internalMetadata.isPaused) { templateData.progressBar.infinite().show(500); } else { templateData.progressBar.hide(); diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 5f00e200e52b2..0552b7ea24bdc 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -92,6 +92,7 @@ export interface NotebookCellInternalMetadata { runStartTime?: number; runStartTimeAdjustment?: number; runEndTime?: number; + isPaused?: boolean; } export type TransientCellMetadata = { [K in keyof NotebookCellMetadata]?: boolean }; diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index 431cf29cedf97..755ccfc166f55 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -485,7 +485,7 @@ export class NotebookOptions { } setCellBreakpointMarginActive(active: boolean) { - this._layoutConfiguration.cellBreakpointMarginActive = active; + this._layoutConfiguration = { ...this._layoutConfiguration, ...{ cellBreakpointMarginActive: active } }; this._onDidChangeOptions.fire({ cellBreakpointMargin: true }); }