This commit is contained in:
rebornix 2020-11-17 11:33:10 -08:00
parent 3184dca0bc
commit 8a11346573
6 changed files with 139 additions and 12 deletions

View file

@ -211,6 +211,15 @@
max-width: 100%;
}
.monaco-workbench .notebookOverlay .output-show-more-container {
position: absolute;
}
.monaco-workbench .notebookOverlay .output-show-more-container p {
padding: 8px 8px 0 8px;
margin: 0px;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .menu {
position: absolute;
left: 0;

View file

@ -91,6 +91,8 @@ export interface CodeCellLayoutInfo {
readonly totalHeight: number;
readonly outputContainerOffset: number;
readonly outputTotalHeight: number;
readonly outputShowMoreContainerHeight: number;
readonly outputShowMoreContainerOffset: number;
readonly indicatorHeight: number;
readonly bottomToolbarOffset: number;
readonly layoutState: CodeCellLayoutState;
@ -99,6 +101,7 @@ export interface CodeCellLayoutInfo {
export interface CodeCellLayoutChangeEvent {
editorHeight?: boolean;
outputHeight?: boolean;
outputShowMoreContainerHeight?: number;
totalHeight?: boolean;
outerWidth?: number;
font?: BareFontInfo;
@ -558,6 +561,7 @@ export interface CodeCellRenderTemplate extends BaseCellRenderTemplate {
runButtonContainer: HTMLElement;
executionOrderLabel: HTMLElement;
outputContainer: HTMLElement;
outputShowMoreContainer: HTMLElement;
focusSinkElement: HTMLElement;
editor: ICodeEditor;
progressBar: ProgressBar;

View file

@ -1955,12 +1955,17 @@ registerThemingParticipant((theme, collector) => {
const link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(`.notebookOverlay .output a,
.notebookOverlay .cell.markdown a { color: ${link};} `);
.notebookOverlay .cell.markdown a,
.notebookOverlay .output-show-more-container a
{ color: ${link};} `);
}
const activeLink = theme.getColor(textLinkActiveForeground);
if (activeLink) {
collector.addRule(`.notebookOverlay .output a:hover,
.notebookOverlay .cell .output a:active { color: ${activeLink}; }`);
.notebookOverlay .cell .output a:active,
.notebookOverlay .output-show-more-container a:active
{ color: ${activeLink}; }`);
}
const shortcut = theme.getColor(textPreformatForeground);
if (shortcut) {
@ -1984,6 +1989,7 @@ registerThemingParticipant((theme, collector) => {
if (containerBackground) {
collector.addRule(`.notebookOverlay .output { background-color: ${containerBackground}; }`);
collector.addRule(`.notebookOverlay .output-element { background-color: ${containerBackground}; }`);
collector.addRule(`.notebookOverlay .output-show-more-container { background-color: ${containerBackground}; }`);
}
const editorBackgroundColor = theme.getColor(editorBackground);
@ -2150,6 +2156,9 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`);
collector.addRule(`.notebookOverlay .output-show-more-container { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .output-show-more-container { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`);
collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`);
collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; margin: 0px ${Math.floor(CELL_RUN_GUTTER - 20) / 2}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`);

View file

@ -727,6 +727,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
const cellRunState = new RunStateRenderer(statusBar.cellRunStatusContainer, runToolbar, this.instantiationService);
const outputContainer = DOM.append(container, $('.output'));
const outputShowMoreContainer = DOM.append(container, $('.output-show-more-container'));
const focusIndicatorRight = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-right'));
@ -761,6 +762,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
runButtonContainer,
executionOrderLabel,
outputContainer,
outputShowMoreContainer,
editor,
disposables,
elementDisposables: new DisposableStore(),
@ -843,6 +845,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
templateData.focusIndicatorRight.style.height = `${element.layoutInfo.indicatorHeight}px`;
templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`;
templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`;
templateData.outputShowMoreContainer.style.top = `${element.layoutInfo.outputShowMoreContainerOffset}px`;
templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP}px`;
}

View file

@ -5,14 +5,19 @@
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { raceCancellation } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IDimension } from 'vs/editor/common/editorCommon';
import { format } from 'vs/base/common/jsonFormatter';
import { applyEdits } from 'vs/base/common/jsonEdit';
import { IModeService } from 'vs/editor/common/services/modeService';
import * as nls from 'vs/nls';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
@ -20,7 +25,9 @@ import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/r
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { BUILTIN_RENDERER_ID, CellOutputKind, CellUri, IInsetRenderOutput, IProcessedOutput, IRenderOutput, ITransformedDisplayOutputDto, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
const OUTPUT_COUNT_LIMIT = 500;
interface IMimeTypeRenderer extends IQuickPickItem {
index: number;
}
@ -236,6 +243,8 @@ export class CodeCell extends Disposable {
private templateData: CodeCellRenderTemplate,
@INotebookService private notebookService: INotebookService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IOpenerService readonly openerService: IOpenerService,
@ITextFileService readonly textFileService: ITextFileService,
@IModeService private readonly _modeService: IModeService
) {
super();
@ -378,8 +387,9 @@ export class CodeCell extends Disposable {
});
let prevElement: HTMLElement | undefined = undefined;
const outputsToRender = this.viewCell.outputs.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputs.length));
[...this.viewCell.outputs].reverse().forEach(output => {
outputsToRender.reverse().forEach(output => {
if (this.outputEntries.has(output)) {
// already exist
prevElement = this.outputEntries.get(output)!.domNode;
@ -392,6 +402,13 @@ export class CodeCell extends Disposable {
prevElement = this.outputEntries.get(output)?.domNode;
});
if (this.viewCell.outputs.length > OUTPUT_COUNT_LIMIT) {
this.templateData.outputShowMoreContainer.style.display = 'block';
this.viewCell.updateOutputShowMoreContainerHeight(46);
} else {
this.templateData.outputShowMoreContainer.style.display = 'none';
}
const editorHeight = templateData.editor!.getContentHeight();
viewCell.editorHeight = editorHeight;
@ -476,7 +493,8 @@ export class CodeCell extends Disposable {
this.templateData.outputContainer!.style.display = 'block';
// there are outputs, we need to calcualte their sizes and trigger relayout
// @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance
for (let index = 0; index < this.viewCell.outputs.length; index++) {
const outputsToRender = this.viewCell.outputs.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputs.length));
for (let index = 0; index < outputsToRender.length; index++) {
const currOutput = this.viewCell.outputs[index];
// always add to the end
@ -484,6 +502,11 @@ export class CodeCell extends Disposable {
}
viewCell.editorHeight = editorHeight;
if (this.viewCell.outputs.length > OUTPUT_COUNT_LIMIT) {
this.templateData.outputShowMoreContainer.style.display = 'block';
this.viewCell.updateOutputShowMoreContainerHeight(46);
}
if (layoutCache) {
this.relayoutCellDebounced();
} else {
@ -496,10 +519,74 @@ export class CodeCell extends Disposable {
this.templateData.outputContainer!.style.display = 'none';
}
this.templateData.outputShowMoreContainer.innerText = '';
this.templateData.outputShowMoreContainer.appendChild(this.generateShowMoreElement());
// this.templateData.outputShowMoreContainer.style.top = `${this.viewCell.layoutInfo.outputShowMoreContainerOffset}px`;
if (this.viewCell.outputs.length < OUTPUT_COUNT_LIMIT) {
this.templateData.outputShowMoreContainer.style.display = 'none';
this.viewCell.updateOutputShowMoreContainerHeight(0);
}
// Need to do this after the intial renderOutput
updateForCollapseState();
}
generateShowMoreElement(): any {
const md: IMarkdownString = {
value: `There are more than ${OUTPUT_COUNT_LIMIT} outputs, [show more ...](command:workbench.action.openLargeOutput)`,
isTrusted: true,
supportThemeIcons: true
};
const element = renderMarkdown(md, {
actionHandler: {
callback: (content) => {
if (content === 'command:workbench.action.openLargeOutput') {
const content = JSON.stringify(this.viewCell.outputs.map(output => {
switch (output.outputKind) {
case CellOutputKind.Text:
return {
outputKind: 'text',
text: output.text
};
case CellOutputKind.Error:
return {
outputKind: 'error',
ename: output.ename,
evalue: output.evalue,
traceback: output.traceback
};
case CellOutputKind.Rich:
return {
data: output.data,
metadata: output.metadata
};
}
}));
const edits = format(content, undefined, {});
const metadataSource = applyEdits(content, edits);
return this.textFileService.untitled.resolve({
associatedResource: undefined,
mode: 'json',
initialValue: metadataSource
}).then(model => {
const resource = model.resource;
this.openerService.open(resource);
});
}
return;
},
disposeables: new DisposableStore()
}
});
element.classList.add('output-show-more');
return element;
}
private viewUpdate(): void {
if (this.viewCell.metadata?.inputCollapsed && this.viewCell.metadata.outputCollapsed) {
this.viewUpdateAllCollapsed();

View file

@ -89,6 +89,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
editorWidth: initialNotebookLayoutInfo ? this.computeEditorWidth(initialNotebookLayoutInfo!.width) : 0,
outputContainerOffset: 0,
outputTotalHeight: 0,
outputShowMoreContainerHeight: 0,
outputShowMoreContainerOffset: 0,
totalHeight: 0,
indicatorHeight: 0,
bottomToolbarOffset: 0,
@ -103,6 +105,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
layoutChange(state: CodeCellLayoutChangeEvent) {
// recompute
this._ensureOutputsTop();
const outputShowMoreContainerHeight = state.outputShowMoreContainerHeight ? state.outputShowMoreContainerHeight : this._layoutInfo.outputShowMoreContainerHeight;
let outputTotalHeight = this.metadata?.outputCollapsed ? COLLAPSED_INDICATOR_HEIGHT : this._outputsTop!.getTotalValue();
if (!this.metadata?.inputCollapsed) {
@ -117,17 +120,18 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
} else if (state.editorHeight || this._layoutInfo.layoutState === CodeCellLayoutState.Measured) {
// Editor has been measured
editorHeight = this._editorHeight;
totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight);
totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight, outputShowMoreContainerHeight);
newState = CodeCellLayoutState.Measured;
} else {
editorHeight = this.estimateEditorHeight(state.font?.lineHeight);
totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight);
totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight, outputShowMoreContainerHeight);
newState = CodeCellLayoutState.Estimated;
}
const statusbarHeight = this.getEditorStatusbarHeight();
const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight;
const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight;
const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight;
const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
@ -137,6 +141,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
editorWidth,
outputContainerOffset,
outputTotalHeight,
outputShowMoreContainerHeight,
outputShowMoreContainerOffset,
totalHeight,
indicatorHeight,
bottomToolbarOffset,
@ -144,9 +150,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
};
} else {
outputTotalHeight = this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight;
const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight;
const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight;
const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT;
const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight;
const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight + outputShowMoreContainerHeight;
const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
@ -156,6 +163,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
editorWidth,
outputContainerOffset,
outputTotalHeight,
outputShowMoreContainerHeight,
outputShowMoreContainerOffset,
totalHeight,
indicatorHeight,
bottomToolbarOffset,
@ -183,6 +192,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
editorWidth: this._layoutInfo.editorWidth,
outputContainerOffset: this._layoutInfo.outputContainerOffset,
outputTotalHeight: this._layoutInfo.outputTotalHeight,
outputShowMoreContainerHeight: this._layoutInfo.outputShowMoreContainerHeight,
outputShowMoreContainerOffset: this._layoutInfo.outputShowMoreContainerOffset,
totalHeight: totalHeight,
indicatorHeight: this._layoutInfo.indicatorHeight,
bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset,
@ -203,7 +214,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
getHeight(lineHeight: number) {
if (this._layoutInfo.layoutState === CodeCellLayoutState.Uninitialized) {
const editorHeight = this.estimateEditorHeight(lineHeight);
return this.computeTotalHeight(editorHeight, 0);
return this.computeTotalHeight(editorHeight, 0, 0);
} else {
return this._layoutInfo.totalHeight;
}
@ -213,8 +224,8 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
return this.lineCount * lineHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING;
}
private computeTotalHeight(editorHeight: number, outputsTotalHeight: number): number {
return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN;
private computeTotalHeight(editorHeight: number, outputsTotalHeight: number, outputShowMoreContainerHeight: number): number {
return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + outputShowMoreContainerHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN;
}
/**
@ -240,6 +251,10 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
this.editState = CellEditState.Preview;
}
updateOutputShowMoreContainerHeight(height: number) {
this.layoutChange({ outputShowMoreContainerHeight: height });
}
updateOutputHeight(index: number, height: number) {
if (index >= this._outputCollection.length) {
throw new Error('Output index out of range!');