Show cell execution state when collapsed. Fix #131200

This commit is contained in:
Rob Lourens 2022-02-11 13:32:07 -08:00
parent 1d9e655b5a
commit 679fc19368
4 changed files with 134 additions and 5 deletions

View file

@ -341,11 +341,26 @@
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container {
display: flex;
align-items: center;
position: relative;
box-sizing: border-box;
width: 100%;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .collapsed-execution-icon {
line-height: normal;
margin-left: 6px;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .collapsed-execution-icon .codicon-notebook-state-success {
color: var(--notebook-cell-status-icon-success);
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .collapsed-execution-icon .codicon-notebook-state-error {
color: var(--notebook-cell-status-icon-error);
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview {
padding: 0px 8px;
display: flex;

View file

@ -3072,6 +3072,17 @@ registerThemingParticipant((theme, collector) => {
}
`);
const cellStatusIconSuccessColor = theme.getColor(cellStatusIconSuccess);
const cellStatusIconErrorColor = theme.getColor(cellStatusIconError);
const cellStatusIconRunningColor = theme.getColor(cellStatusIconRunning);
collector.addRule(`
:root {
--notebook-cell-status-icon-success: ${cellStatusIconSuccessColor};
--notebook-cell-status-icon-error: ${cellStatusIconErrorColor};
--notebook-cell-status-icon-running: ${cellStatusIconRunningColor};
}
`);
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.notebookOverlay .cell.markdown a,

View file

@ -8,12 +8,12 @@ import { raceCancellation } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Codicon, CSSIcon } from 'vs/base/common/codicons';
import { Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IDimension } from 'vs/editor/common/core/dimension';
import { IReadonlyTextBuffer } from 'vs/editor/common/model';
import { tokenizeToStringSync } from 'vs/editor/common/languages/textToHtmlTokenizer';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { tokenizeToStringSync } from 'vs/editor/common/languages/textToHtmlTokenizer';
import { IReadonlyTextBuffer } from 'vs/editor/common/model';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -24,6 +24,7 @@ import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/ce
import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput';
import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart';
import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets';
import { CodeCellExecutionIcon } from 'vs/workbench/contrib/notebook/browser/view/cellParts/codeCellExecutionIcon';
import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
@ -38,6 +39,8 @@ export class CodeCell extends Disposable {
private _isDisposed: boolean = false;
private readonly cellParts: CellPart[];
private _collapsedExecutionIcon: CodeCellExecutionIcon;
constructor(
private readonly notebookEditor: IActiveNotebookEditorDelegate,
private readonly viewCell: CodeCellViewModel,
@ -125,6 +128,11 @@ export class CodeCell extends Disposable {
this.cellParts.forEach(cellPart => cellPart.prepareLayout());
}));
const executionItemElement = DOM.append(this.templateData.cellInputCollapsedContainer, DOM.$('.collapsed-execution-icon'));
this._register(toDisposable(() => {
executionItemElement.parentElement?.removeChild(executionItemElement);
}));
this._collapsedExecutionIcon = this.instantiationService.createInstance(CodeCellExecutionIcon, this.notebookEditor, this.viewCell, executionItemElement);
this.updateForCollapseState();
this._register(Event.runAndSubscribe(viewCell.onDidChangeOutputs, this.updateForOutputs.bind(this)));
@ -387,10 +395,11 @@ export class CodeCell extends Disposable {
// remove input preview
this._removeInputCollapsePreview();
this._collapsedExecutionIcon.setVisibility(true);
// update preview
const richEditorText = this._getRichText(this.viewCell.textBuffer, this.viewCell.language);
const element = DOM.$('div');
element.classList.add('cell-collapse-preview');
const element = DOM.$('div.cell-collapse-preview');
DOM.safeInnerHtml(element, richEditorText);
this.templateData.cellInputCollapsedContainer.appendChild(element);
const expandIcon = DOM.$('span.expandInputIcon');
@ -407,6 +416,7 @@ export class CodeCell extends Disposable {
}
private _showInput() {
this._collapsedExecutionIcon.setVisibility(false);
DOM.show(this.templateData.editorPart);
DOM.hide(this.templateData.cellInputCollapsedContainer);
}

View file

@ -0,0 +1,93 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { Disposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { errorStateIcon, executingStateIcon, pendingStateIcon, successStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
import { NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
interface IExecutionItem {
text: string;
tooltip?: string;
}
export class CodeCellExecutionIcon extends Disposable {
private _visible = false;
constructor(
_notebookEditor: INotebookEditorDelegate,
private readonly _cell: ICellViewModel,
private readonly _element: HTMLElement,
@INotebookExecutionStateService private _executionStateService: INotebookExecutionStateService,
) {
super();
this._update();
this._register(this._executionStateService.onDidChangeCellExecution(e => {
if (e.affectsCell(this._cell.uri)) {
this._update();
}
}));
this._register(this._cell.model.onDidChangeInternalMetadata(() => this._update()));
}
setVisibility(visible: boolean): void {
this._visible = visible;
this._update();
}
private _update() {
if (!this._visible) {
return;
}
const runState = this._executionStateService.getCellExecution(this._cell.uri);
const item = this._getItemForState(runState, this._cell.model.internalMetadata);
if (item) {
this._element.style.display = '';
DOM.reset(this._element, ...renderLabelWithIcons(item.text));
this._element.title = item.tooltip ?? '';
} else {
this._element.style.display = 'none';
DOM.reset(this._element);
}
}
private _getItemForState(runState: INotebookCellExecution | undefined, internalMetadata: NotebookCellInternalMetadata): IExecutionItem | undefined {
const state = runState?.state;
const { lastRunSuccess } = internalMetadata;
if (!state && lastRunSuccess) {
return <IExecutionItem>{
text: `$(${successStateIcon.id})`,
tooltip: localize('notebook.cell.status.success', "Success"),
};
} else if (!state && lastRunSuccess === false) {
return <IExecutionItem>{
text: `$(${errorStateIcon.id})`,
tooltip: localize('notebook.cell.status.failed', "Failed"),
};
} else if (state === NotebookCellExecutionState.Pending) {
return <IExecutionItem>{
text: `$(${pendingStateIcon.id})`,
tooltip: localize('notebook.cell.status.pending', "Pending"),
};
} else if (state === NotebookCellExecutionState.Executing) {
const icon = runState?.isPaused ?
executingStateIcon :
ThemeIcon.modify(executingStateIcon, 'spin');
return <IExecutionItem>{
text: `$(${icon.id})`,
tooltip: localize('notebook.cell.status.executing', "Executing"),
};
}
return;
}
}