diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index 61098afe4f3..adfcf470bc3 100644 --- a/extensions/notebook-renderers/src/index.ts +++ b/extensions/notebook-renderers/src/index.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type { ActivationFunction, OutputItem, RendererContext } from 'vscode-notebook-renderer'; -import { truncatedArrayOfString } from './textHelper'; +import { insertOutput, truncatedArrayOfString } from './textHelper'; interface IDisposable { dispose(): void; @@ -153,7 +153,7 @@ function renderError(outputInfo: OutputItem, container: HTMLElement, ctx: Render container.classList.add('error'); } -function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boolean, ctx: RendererContext & { readonly settings: { readonly lineLimit: number } }): void { +function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boolean, ctx: RendererContext & { readonly settings: { readonly lineLimit: number; readonly outputScrolling: boolean } }): void { const outputContainer = container.parentElement; if (!outputContainer) { // should never happen @@ -170,7 +170,7 @@ function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boo const text = outputInfo.text(); const element = document.createElement('span'); - truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, element); + insertOutput(outputInfo.id, [text], ctx.settings.lineLimit, true, element); outputElement.appendChild(element); return; } @@ -180,7 +180,7 @@ function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boo element.classList.add('output-stream'); const text = outputInfo.text(); - truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, element); + insertOutput(outputInfo.id, [text], ctx.settings.lineLimit, true, element); while (container.firstChild) { container.removeChild(container.firstChild); } @@ -205,7 +205,7 @@ export const activate: ActivationFunction = (ctx) => { const htmlHooks = new Set(); const jsHooks = new Set(); - const latestContext = ctx as (RendererContext & { readonly settings: { readonly lineLimit: number } }); + const latestContext = ctx as (RendererContext & { readonly settings: { readonly lineLimit: number; readonly outputScrolling: boolean } }); const style = document.createElement('style'); style.textContent = ` @@ -225,6 +225,9 @@ export const activate: ActivationFunction = (ctx) => { } span.output-stream { display: inline-block; + width: 100%; + overflow-y: var(--notebook-output-overflow-y); + max-height: 500px; } .output-plaintext .code-bold, .output-stream .code-bold, diff --git a/extensions/notebook-renderers/src/textHelper.ts b/extensions/notebook-renderers/src/textHelper.ts index 36edb961f1f..6bd93b360dd 100644 --- a/extensions/notebook-renderers/src/textHelper.ts +++ b/extensions/notebook-renderers/src/textHelper.ts @@ -13,9 +13,9 @@ function generateViewMoreElement(outputId: string) { second.textContent = 'size limit'; second.href = `command:workbench.action.openSettings?%5B%22notebook.output.textLineLimit%22%5D`; const third = document.createElement('span'); - third.textContent = '. Open the full output data'; + third.textContent = '. Enable scrolling in the settings, or open the full output data '; const forth = document.createElement('a'); - forth.textContent = ' in a text editor'; + forth.textContent = 'in a text editor'; forth.href = `command:workbench.action.openLargeOutput?${outputId}`; container.appendChild(first); container.appendChild(second); @@ -49,3 +49,17 @@ export function truncatedArrayOfString(id: string, outputs: string[], linesLimit container.appendChild(div2); div2.appendChild(handleANSIOutput(buffer.slice(lineCount - 5).join('\n'))); } + +function scrollableArrayOfString(outputs: string[], container: HTMLElement) { + const buffer = outputs.join('\n').split(/\r\n|\r|\n/g); + const spanElement = handleANSIOutput(buffer.slice(0, 5000).join('\n')); + container.appendChild(spanElement); +} + +export function insertOutput(id: string, outputs: string[], linesLimit: number, scrollable: boolean, container: HTMLElement) { + if (scrollable) { + scrollableArrayOfString(outputs, container); + } else { + truncatedArrayOfString(id, outputs, linesLimit, container); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 23e427b6ed5..e3a8c13a9f0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -97,6 +97,7 @@ interface BacklayerWebviewOptions { readonly outputFontFamily: string; readonly markupFontSize: number; readonly outputLineHeight: number; + readonly outputScrolling: boolean; } export class BackLayerWebView extends Themable { @@ -233,6 +234,7 @@ export class BackLayerWebView extends Themable { key: 'notebook.error.rendererNotFound', comment: ['$0 is a placeholder for the mime type'] }, "No renderer found for '$0' a"), + 'notebook-output-overflow-y': this.options.outputScrolling ? 'scroll' : 'visible', }; } @@ -241,7 +243,7 @@ export class BackLayerWebView extends Themable { const preloadsData = this.getStaticPreloadsData(); const preloadScript = preloadsScriptStr( this.options, - { dragAndDropEnabled: this.options.dragAndDropEnabled }, + { dragAndDropEnabled: this.options.dragAndDropEnabled }, // add other options renderersData, preloadsData, this.workspaceTrustManagementService.isWorkspaceTrusted(), diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 9cf0175219e..e27d9214e98 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -926,7 +926,8 @@ export const NotebookSetting = { outputLineHeight: 'notebook.outputLineHeight', outputFontSize: 'notebook.outputFontSize', outputFontFamily: 'notebook.outputFontFamily', - kernelPickerType: 'notebook.kernelPicker.type' + kernelPickerMRU: 'notebook.experimental.kernelPicker.mru', + outputScrolling: 'notebook.outputScrolling', } as const; export const enum CellStatusbarAlignment { diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index 63661031729..19786b0af3f 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -70,6 +70,7 @@ export interface NotebookLayoutConfiguration { editorOptionsCustomizations: any | undefined; focusIndicatorGap: number; interactiveWindowCollapseCodeCells: InteractiveWindowCollapseCodeCells; + outputScrolling: boolean; } export interface NotebookOptionsChangeEvent { @@ -147,6 +148,7 @@ export class NotebookOptions extends Disposable { const editorOptionsCustomizations = this.configurationService.getValue(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells: InteractiveWindowCollapseCodeCells = this.configurationService.getValue(NotebookSetting.interactiveWindowCollapseCodeCells); const outputLineHeight = this._computeOutputLineHeight(); + const outputScrolling = this.configurationService.getValue(NotebookSetting.outputScrolling); this._layoutConfiguration = { ...(compactView ? compactConfigConstants : defaultConfigConstants), @@ -183,7 +185,8 @@ export class NotebookOptions extends Disposable { editorOptionsCustomizations, focusIndicatorGap: 3, interactiveWindowCollapseCodeCells, - markdownFoldHintHeight: 22 + markdownFoldHintHeight: 22, + outputScrolling: outputScrolling }; this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -566,6 +569,7 @@ export class NotebookOptions extends Disposable { outputFontFamily: this._layoutConfiguration.outputFontFamily, markupFontSize: this._layoutConfiguration.markupFontSize, outputLineHeight: this._layoutConfiguration.outputLineHeight, + outputScrolling: this._layoutConfiguration.outputScrolling, }; } @@ -584,6 +588,7 @@ export class NotebookOptions extends Disposable { outputFontFamily: this._layoutConfiguration.outputFontFamily, markupFontSize: this._layoutConfiguration.markupFontSize, outputLineHeight: this._layoutConfiguration.outputLineHeight, + outputScrolling: this._layoutConfiguration.outputScrolling, }; }