diff --git a/news/2 Fixes/8003.md b/news/2 Fixes/8003.md new file mode 100644 index 000000000000..898b7701b358 --- /dev/null +++ b/news/2 Fixes/8003.md @@ -0,0 +1 @@ +Make a spinner appear during executing a cell. diff --git a/news/2 Fixes/8039.md b/news/2 Fixes/8039.md new file mode 100644 index 000000000000..2c974d201eaf --- /dev/null +++ b/news/2 Fixes/8039.md @@ -0,0 +1 @@ +Minimize the GPU impact of the interactive window and the notebook editor. diff --git a/src/client/datascience/interactive-common/interactiveBase.ts b/src/client/datascience/interactive-common/interactiveBase.ts index 126fe652feac..764e44796dd9 100644 --- a/src/client/datascience/interactive-common/interactiveBase.ts +++ b/src/client/datascience/interactive-common/interactiveBase.ts @@ -346,7 +346,7 @@ export abstract class InteractiveBase extends WebViewHost { if (this.notebook && !this.restartingKernel) { - const status = this.statusProvider.set(localize.DataScience.interruptKernelStatus(), undefined, undefined, this); + const status = this.statusProvider.set(localize.DataScience.interruptKernelStatus(), true, undefined, undefined, this); const settings = this.configuration.getSettings(); const interruptTimeout = settings.datascience.jupyterInterruptTimeout; @@ -463,7 +463,7 @@ export abstract class InteractiveBase extends WebViewHost { - const result = this.statusProvider.set(message, undefined, undefined, this); + protected setStatus = (message: string, showInWebView: boolean): Disposable => { + const result = this.statusProvider.set(message, showInWebView, undefined, undefined, this); this.potentiallyUnfinishedStatus.push(result); return result; } @@ -733,7 +733,7 @@ export abstract class InteractiveBase extends WebViewHost { // Status depends upon if we're about to connect to existing server or not. const status = (await this.jupyterExecution.getServer(await this.getNotebookOptions())) ? - this.setStatus(localize.DataScience.connectingToJupyter()) : this.setStatus(localize.DataScience.startingJupyter()); + this.setStatus(localize.DataScience.connectingToJupyter(), true) : this.setStatus(localize.DataScience.startingJupyter(), true); // Check to see if we support ipykernel or not try { @@ -899,7 +899,7 @@ export abstract class InteractiveBase extends WebViewHost { - const status = this.setStatus(localize.DataScience.startingJupyter()); + const status = this.setStatus(localize.DataScience.startingJupyter(), true); try { // Not the same as reload, we need to actually wait for the server. await this.stopServer(); diff --git a/src/client/datascience/interactive-ipynb/nativeEditor.ts b/src/client/datascience/interactive-ipynb/nativeEditor.ts index 0fd244724c16..e51c32444aa1 100644 --- a/src/client/datascience/interactive-ipynb/nativeEditor.ts +++ b/src/client/datascience/interactive-ipynb/nativeEditor.ts @@ -467,10 +467,10 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { * @private * @memberof NativeEditor */ - private async updateVersionInfoInNotebook(): Promise{ + private async updateVersionInfoInNotebook(): Promise { // Use the active interpreter const usableInterpreter = await this.jupyterExecution.getUsableJupyterPython(); - if (usableInterpreter && usableInterpreter.version && this.notebookJson.metadata && this.notebookJson.metadata.language_info){ + if (usableInterpreter && usableInterpreter.version && this.notebookJson.metadata && this.notebookJson.metadata.language_info) { this.notebookJson.metadata.language_info.version = `${usableInterpreter.version.major}.${usableInterpreter.version.minor}.${usableInterpreter.version.patch}`; } } @@ -580,11 +580,11 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { * @memberof NativeEditor */ private async getStoredContents(): Promise { - const data = this.globalStorage.get<{contents?: string; lastModifiedTimeMs?: number}>(this.getStorageKey()); + const data = this.globalStorage.get<{ contents?: string; lastModifiedTimeMs?: number }>(this.getStorageKey()); // Check whether the file has been modified since the last time the contents were saved. - if (data && data.lastModifiedTimeMs && !this.isUntitled && this.file.scheme === 'file'){ + if (data && data.lastModifiedTimeMs && !this.isUntitled && this.file.scheme === 'file') { const stat = await this.fileSystem.stat(this.file.fsPath); - if (stat.mtime > data.lastModifiedTimeMs){ + if (stat.mtime > data.lastModifiedTimeMs) { return; } } @@ -605,7 +605,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { const key = this.getStorageKey(); // Keep track of the time when this data was saved. // This way when we retrieve the data we can compare it against last modified date of the file. - await this.globalStorage.update(key, {contents, lastModifiedTimeMs: Date.now()}); + await this.globalStorage.update(key, { contents, lastModifiedTimeMs: Date.now() }); } private async close(): Promise { @@ -757,7 +757,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor { @captureTelemetry(Telemetry.ConvertToPythonFile, undefined, false) private async export(cells: ICell[]): Promise { - const status = this.setStatus(localize.DataScience.convertingToPythonFile()); + const status = this.setStatus(localize.DataScience.convertingToPythonFile(), false); // First generate a temporary notebook with these cells. let tempFile: TemporaryFile | undefined; try { diff --git a/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts b/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts index 4933f6dde117..80dc782bdd0a 100644 --- a/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts +++ b/src/client/datascience/interactive-window/interactiveWindowCommandListener.ts @@ -351,7 +351,7 @@ export class InteractiveWindowCommandListener implements IDataScienceCommandList private waitForStatus(promise: () => Promise, format: string, file?: string, canceled?: () => void, interactiveWindow?: IInteractiveBase): Promise { const message = file ? format.format(file) : format; - return this.statusProvider.waitWithStatus(promise, message, undefined, canceled, interactiveWindow); + return this.statusProvider.waitWithStatus(promise, message, true, undefined, canceled, interactiveWindow); } @captureTelemetry(Telemetry.ImportNotebook, { scope: 'command' }, false) diff --git a/src/client/datascience/statusProvider.ts b/src/client/datascience/statusProvider.ts index eb011870de97..5aa1c2718a1a 100644 --- a/src/client/datascience/statusProvider.ts +++ b/src/client/datascience/statusProvider.ts @@ -55,9 +55,9 @@ export class StatusProvider implements IStatusProvider { constructor(@inject(IApplicationShell) private applicationShell: IApplicationShell) { } - public set(message: string, timeout?: number, cancel?: () => void, panel?: IInteractiveBase): Disposable { + public set(message: string, showInWebView: boolean, timeout?: number, cancel?: () => void, panel?: IInteractiveBase): Disposable { // Start our progress - this.incrementCount(panel); + this.incrementCount(showInWebView, panel); // Create a StatusItem that will return our promise const statusItem = new StatusItem(message, () => this.decrementCount(panel), timeout); @@ -82,9 +82,9 @@ export class StatusProvider implements IStatusProvider { return statusItem; } - public async waitWithStatus(promise: () => Promise, message: string, timeout?: number, cancel?: () => void, panel?: IInteractiveBase): Promise { + public async waitWithStatus(promise: () => Promise, message: string, showInWebView: boolean, timeout?: number, cancel?: () => void, panel?: IInteractiveBase): Promise { // Create a status item and wait for our promise to either finish or reject - const status = this.set(message, timeout, cancel, panel); + const status = this.set(message, showInWebView, timeout, cancel, panel); let result: T; try { result = await promise(); @@ -94,9 +94,9 @@ export class StatusProvider implements IStatusProvider { return result; } - private incrementCount = (panel?: IInteractiveBase) => { + private incrementCount = (showInWebView: boolean, panel?: IInteractiveBase) => { if (this.statusCount === 0) { - if (panel) { + if (panel && showInWebView) { panel.startProgress(); } } diff --git a/src/client/datascience/types.ts b/src/client/datascience/types.ts index 187452885686..194f8eeb0c0e 100644 --- a/src/client/datascience/types.ts +++ b/src/client/datascience/types.ts @@ -383,10 +383,10 @@ export const IStatusProvider = Symbol('IStatusProvider'); export interface IStatusProvider { // call this function to set the new status on the active // interactive window. Dispose of the returned object when done. - set(message: string, timeout?: number, canceled?: () => void, interactivePanel?: IInteractiveBase): Disposable; + set(message: string, showInWebView: boolean, timeout?: number, canceled?: () => void, interactivePanel?: IInteractiveBase): Disposable; // call this function to wait for a promise while displaying status - waitWithStatus(promise: () => Promise, message: string, timeout?: number, canceled?: () => void, interactivePanel?: IInteractiveBase): Promise; + waitWithStatus(promise: () => Promise, message: string, showInWebView: boolean, timeout?: number, canceled?: () => void, interactivePanel?: IInteractiveBase): Promise; } export interface IJupyterCommand { diff --git a/src/datascience-ui/history-react/index.html b/src/datascience-ui/history-react/index.html index 9e9b10e5a0f5..e157945c4a3a 100644 --- a/src/datascience-ui/history-react/index.html +++ b/src/datascience-ui/history-react/index.html @@ -254,6 +254,7 @@ --vscode-titleBar-activeForeground: #333333; --vscode-titleBar-inactiveBackground: rgba(221, 221, 221, 0.6); --vscode-titleBar-inactiveForeground: rgba(51, 51, 51, 0.6); +--code-comment-color: green; --vscode-widget-shadow: #a8a8a8; } body { diff --git a/src/datascience-ui/interactive-common/common.css b/src/datascience-ui/interactive-common/common.css index c0143002b257..83ece134dcdb 100644 --- a/src/datascience-ui/interactive-common/common.css +++ b/src/datascience-ui/interactive-common/common.css @@ -388,24 +388,25 @@ body, html { grid-column: 1; font-weight: bold; color: var(--code-comment-color); - display:flex; - width: 16px; - height: 16px; + display:block; + width: 8px; + height: 8px; + white-space: nowrap; +} + +@keyframes execution-spin { + from { transform: rotate(0); } + to { transform: rotate(360deg); } } + .execution-count-busy-svg { - animation-name: spin; - animation-duration: 4000ms; - animation-iteration-count: infinite; - animation-timing-function: linear; - transform-origin: 50% 50%; - width: 16px; - height: 16px; + animation: execution-spin 4s linear infinite; } .execution-count-busy-polyline { fill: none; stroke: var(--code-comment-color); - stroke-width: 5; + stroke-width: 1; } @keyframes spin { diff --git a/src/datascience-ui/interactive-common/executionCount.tsx b/src/datascience-ui/interactive-common/executionCount.tsx index 072b4eb93bbe..bdb0a98164c7 100644 --- a/src/datascience-ui/interactive-common/executionCount.tsx +++ b/src/datascience-ui/interactive-common/executionCount.tsx @@ -20,7 +20,7 @@ export class ExecutionCount extends React.Component { return this.props.isBusy ? ( -
[]
+
[]
) : (
{`[${this.props.count}]`}
diff --git a/src/datascience-ui/interactive-common/mainState.ts b/src/datascience-ui/interactive-common/mainState.ts index dd0d9146aeb0..386519a1f7e2 100644 --- a/src/datascience-ui/interactive-common/mainState.ts +++ b/src/datascience-ui/interactive-common/mainState.ts @@ -92,7 +92,7 @@ export function generateTestState(inputBlockToggled: (id: string) => void, fileP return { cellVMs: generateVMs(inputBlockToggled, filePath, editable), editCellVM: createEditableCellVM(1), - busy: true, + busy: false, skipNextScroll: false, undoStack: [], redoStack: [], diff --git a/src/datascience-ui/native-editor/index.html b/src/datascience-ui/native-editor/index.html index b818f2d73959..b1fd99dd034f 100644 --- a/src/datascience-ui/native-editor/index.html +++ b/src/datascience-ui/native-editor/index.html @@ -254,6 +254,7 @@ --vscode-titleBar-activeForeground: #333333; --vscode-titleBar-inactiveBackground: rgba(221, 221, 221, 0.6); --vscode-titleBar-inactiveForeground: rgba(51, 51, 51, 0.6); +--code-comment-color: black; --vscode-widget-shadow: #a8a8a8; } body { diff --git a/src/test/datascience/interactiveWindowCommandListener.unit.test.ts b/src/test/datascience/interactiveWindowCommandListener.unit.test.ts index 648708b8f7e1..293ff9baf1c9 100644 --- a/src/test/datascience/interactiveWindowCommandListener.unit.test.ts +++ b/src/test/datascience/interactiveWindowCommandListener.unit.test.ts @@ -54,13 +54,13 @@ function createTypeMoq(tag: string): TypeMoq.IMock { } class MockStatusProvider implements IStatusProvider { - public set(_message: string, _timeout?: number, _cancel?: () => void, _panel?: IInteractiveBase): Disposable { + public set(_message: string, _inweb: boolean, _timeout?: number, _cancel?: () => void, _panel?: IInteractiveBase): Disposable { return { dispose: noop }; } - public waitWithStatus(promise: () => Promise, _message: string, _timeout?: number, _canceled?: () => void, _panel?: IInteractiveBase): Promise { + public waitWithStatus(promise: () => Promise, _message: string, _inweb: boolean, _timeout?: number, _canceled?: () => void, _panel?: IInteractiveBase): Promise { return promise(); }