mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
truncate long outputs
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { ActivationFunction, OutputItem, RendererContext } from 'vscode-notebook-renderer';
|
||||
import { appendOutput, createOutputContent, scrollableClass } from './textHelper';
|
||||
import { HtmlRenderingHook, IDisposable, IRichRenderContext, JavaScriptRenderingHook, RenderOptions } from './rendererTypes';
|
||||
import { appendScrollableOutput, createOutputContent, scrollableClass } from './textHelper';
|
||||
import { HtmlRenderingHook, IDisposable, IRichRenderContext, JavaScriptRenderingHook, OutputWithAppend, RenderOptions } from './rendererTypes';
|
||||
import { ttPolicy } from './htmlHelper';
|
||||
|
||||
function clearContainer(container: HTMLElement) {
|
||||
@@ -265,10 +265,6 @@ function scrollingEnabled(output: OutputItem, options: RenderOptions) {
|
||||
metadata.scrollable : options.outputScrolling;
|
||||
}
|
||||
|
||||
interface OutputWithAppend extends OutputItem {
|
||||
appendedText?(): string | undefined;
|
||||
}
|
||||
|
||||
// div.cell_container
|
||||
// div.output_container
|
||||
// div.output.output-stream <-- outputElement parameter
|
||||
@@ -302,7 +298,7 @@ function renderStream(outputInfo: OutputWithAppend, outputElement: HTMLElement,
|
||||
if (existingContent && contentParent) {
|
||||
// appending output only in scrollable ouputs currently
|
||||
if (appendedText && outputScrolling) {
|
||||
appendOutput(existingContent, appendedText, false);
|
||||
appendScrollableOutput(existingContent, outputInfo.id, appendedText, outputInfo.text(), false);
|
||||
}
|
||||
else {
|
||||
const newContent = createContent(outputInfo, ctx, outputScrolling, error);
|
||||
|
||||
@@ -35,3 +35,7 @@ export interface RenderOptions {
|
||||
}
|
||||
|
||||
export type IRichRenderContext = RendererContext<void> & { readonly settings: RenderOptions; readonly onDidChangeSettings: Event<RenderOptions> };
|
||||
|
||||
export interface OutputWithAppend extends OutputItem {
|
||||
appendedText?(): string | undefined;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { activate } from '..';
|
||||
import { OutputItem, RendererApi } from 'vscode-notebook-renderer';
|
||||
import { IDisposable, IRichRenderContext, RenderOptions } from '../rendererTypes';
|
||||
import { RendererApi } from 'vscode-notebook-renderer';
|
||||
import { IDisposable, IRichRenderContext, OutputWithAppend, RenderOptions } from '../rendererTypes';
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
const dom = new JSDOM();
|
||||
@@ -116,10 +116,13 @@ suite('Notebook builtin output renderer', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function createOutputItem(text: string, mime: string, id: string = '123'): OutputItem {
|
||||
function createOutputItem(text: string, mime: string, id: string = '123', appendedText?: string): OutputWithAppend {
|
||||
return {
|
||||
id: id,
|
||||
mime: mime,
|
||||
appendedText() {
|
||||
return appendedText;
|
||||
},
|
||||
text() {
|
||||
return text;
|
||||
},
|
||||
@@ -177,9 +180,9 @@ suite('Notebook builtin output renderer', () => {
|
||||
assert.ok(renderer, 'Renderer not created');
|
||||
|
||||
const outputElement = new OutputHtml().getFirstOuputElement();
|
||||
const outputItem = createOutputItem('content', 'text/plain');
|
||||
const outputItem = createOutputItem('content', mimeType);
|
||||
await renderer!.renderOutputItem(outputItem, outputElement);
|
||||
const outputItem2 = createOutputItem('replaced content', 'text/plain');
|
||||
const outputItem2 = createOutputItem('replaced content', mimeType);
|
||||
await renderer!.renderOutputItem(outputItem2, outputElement);
|
||||
|
||||
const inserted = outputElement.firstChild as HTMLElement;
|
||||
@@ -189,6 +192,59 @@ suite('Notebook builtin output renderer', () => {
|
||||
|
||||
});
|
||||
|
||||
test('Append streaming output', async () => {
|
||||
const context = createContext({ outputWordWrap: false, outputScrolling: false });
|
||||
const renderer = await activate(context);
|
||||
assert.ok(renderer, 'Renderer not created');
|
||||
|
||||
const outputElement = new OutputHtml().getFirstOuputElement();
|
||||
const outputItem = createOutputItem('content', stdoutMimeType, '123', 'ignoredAppend');
|
||||
await renderer!.renderOutputItem(outputItem, outputElement);
|
||||
const outputItem2 = createOutputItem('content\nappended', stdoutMimeType, '\nappended');
|
||||
await renderer!.renderOutputItem(outputItem2, outputElement);
|
||||
|
||||
const inserted = outputElement.firstChild as HTMLElement;
|
||||
assert.ok(inserted.innerHTML.indexOf('>content</') !== -1, `Previous content should still exist: ${outputElement.innerHTML}`);
|
||||
assert.ok(inserted.innerHTML.indexOf('ignoredAppend') === -1, `Append value should not be used on first render: ${outputElement.innerHTML}`);
|
||||
assert.ok(inserted.innerHTML.indexOf('>appended</') !== -1, `Content was not appended to output element: ${outputElement.innerHTML}`);
|
||||
assert.ok(inserted.innerHTML.indexOf('>content</') === inserted.innerHTML.lastIndexOf('>content</'), `Original content should not be duplicated: ${outputElement.innerHTML}`);
|
||||
});
|
||||
|
||||
test('Append large streaming outputs', async () => {
|
||||
const context = createContext({ outputWordWrap: false, outputScrolling: false });
|
||||
const renderer = await activate(context);
|
||||
assert.ok(renderer, 'Renderer not created');
|
||||
|
||||
const outputElement = new OutputHtml().getFirstOuputElement();
|
||||
const lotsOfLines = new Array(4998).fill('line').join('\n') + 'endOfInitialContent';
|
||||
const firstOuput = lotsOfLines + 'expected1';
|
||||
const outputItem = createOutputItem(firstOuput, stdoutMimeType, '123');
|
||||
await renderer!.renderOutputItem(outputItem, outputElement);
|
||||
const appended = '\n' + lotsOfLines + 'expectedAppend';
|
||||
const outputItem2 = createOutputItem(firstOuput + appended, stdoutMimeType, appended);
|
||||
await renderer!.renderOutputItem(outputItem2, outputElement);
|
||||
|
||||
const inserted = outputElement.firstChild as HTMLElement;
|
||||
assert.ok(inserted.innerHTML.indexOf('>expected1</') !== -1, `Last bit of previous content should still exist: ${outputElement.innerHTML}`);
|
||||
assert.ok(inserted.innerHTML.indexOf('>expectedAppend</') !== -1, `Content was not appended to output element: ${outputElement.innerHTML}`);
|
||||
});
|
||||
|
||||
test('Streaming outputs larger than the line limit are truncated', async () => {
|
||||
const context = createContext({ outputWordWrap: false, outputScrolling: false });
|
||||
const renderer = await activate(context);
|
||||
assert.ok(renderer, 'Renderer not created');
|
||||
|
||||
const outputElement = new OutputHtml().getFirstOuputElement();
|
||||
const lotsOfLines = new Array(11000).fill('line').join('\n') + 'endOfInitialContent';
|
||||
const firstOuput = 'shouldBeTruncated' + lotsOfLines + 'expected1';
|
||||
const outputItem = createOutputItem(firstOuput, stdoutMimeType, '123');
|
||||
await renderer!.renderOutputItem(outputItem, outputElement);
|
||||
|
||||
const inserted = outputElement.firstChild as HTMLElement;
|
||||
assert.ok(inserted.innerHTML.indexOf('>endOfInitialContent</') !== -1, `Last bit of content should exist: ${outputElement.innerHTML}`);
|
||||
assert.ok(inserted.innerHTML.indexOf('>shouldBeTruncated</') === -1, `Beginning content should be truncated: ${outputElement.innerHTML}`);
|
||||
});
|
||||
|
||||
test(`Render with wordwrap and scrolling for error output`, async () => {
|
||||
const context = createContext({ outputWordWrap: true, outputScrolling: true });
|
||||
const renderer = await activate(context);
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { handleANSIOutput } from './ansi';
|
||||
|
||||
export const scrollableClass = 'scrollable';
|
||||
|
||||
const softScrollableLineLimit = 5000;
|
||||
const hardScrollableLineLimit = 8000;
|
||||
|
||||
/**
|
||||
* Output is Truncated. View as a [scrollable element] or open in a [text editor]. Adjust cell output [settings...]
|
||||
*/
|
||||
@@ -91,11 +93,11 @@ function truncatedArrayOfString(id: string, buffer: string[], linesLimit: number
|
||||
|
||||
function scrollableArrayOfString(id: string, buffer: string[], trustHtml: boolean) {
|
||||
const element = document.createElement('div');
|
||||
if (buffer.length > 5000) {
|
||||
if (buffer.length > softScrollableLineLimit) {
|
||||
element.appendChild(generateNestedViewAllElement(id));
|
||||
}
|
||||
|
||||
element.appendChild(handleANSIOutput(buffer.slice(-5000).join('\n'), trustHtml));
|
||||
element.appendChild(handleANSIOutput(buffer.slice(-1 * softScrollableLineLimit).join('\n'), trustHtml));
|
||||
|
||||
return element;
|
||||
}
|
||||
@@ -111,8 +113,26 @@ export function createOutputContent(id: string, outputText: string, linesLimit:
|
||||
}
|
||||
}
|
||||
|
||||
export function appendOutput(element: HTMLElement, outputText: string, trustHtml: boolean) {
|
||||
const buffer = outputText.split(/\r\n|\r|\n/g);
|
||||
const newContent = handleANSIOutput(buffer.join('\n'), trustHtml);
|
||||
element.appendChild(newContent);
|
||||
const outputLengths: Record<string, number> = {};
|
||||
|
||||
export function appendScrollableOutput(element: HTMLElement, id: string, appended: string, fullText: string, trustHtml: boolean) {
|
||||
if (!outputLengths[id]) {
|
||||
outputLengths[id] = 0;
|
||||
}
|
||||
|
||||
const buffer = appended.split(/\r\n|\r|\n/g);
|
||||
const appendedLength = buffer.length + outputLengths[id];
|
||||
// Allow the output to grow to the hard limit then replace it with the last softLimit number of lines if it grows too large
|
||||
if (buffer.length + outputLengths[id] > hardScrollableLineLimit) {
|
||||
const fullBuffer = fullText.split(/\r\n|\r|\n/g);
|
||||
outputLengths[id] = Math.min(fullBuffer.length, softScrollableLineLimit);
|
||||
const newElement = scrollableArrayOfString(id, fullBuffer.slice(-1 * softScrollableLineLimit), trustHtml);
|
||||
newElement.setAttribute('output-item-id', id);
|
||||
element.replaceWith();
|
||||
}
|
||||
else {
|
||||
element.appendChild(handleANSIOutput(buffer.join('\n'), trustHtml));
|
||||
outputLengths[id] = appendedLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user