mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 13:29:44 +00:00
Merge pull request #142584 from microsoft/rebornix/stream-output-renderer
Move streaming output rendering to renderer extension
This commit is contained in:
commit
8b4eaf7e00
|
@ -25,10 +25,17 @@
|
|||
"image/gif",
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/git",
|
||||
"image/svg+xml",
|
||||
"text/html",
|
||||
"application/javascript",
|
||||
"application/vnd.code.notebook.error"
|
||||
"application/vnd.code.notebook.error",
|
||||
"application/vnd.code.notebook.stdout",
|
||||
"application/x.notebook.stdout",
|
||||
"application/x.notebook.stream",
|
||||
"application/vnd.code.notebook.stderr",
|
||||
"application/x.notebook.stderr",
|
||||
"text/plain"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -3,13 +3,16 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { ActivationFunction, OutputItem } from 'vscode-notebook-renderer';
|
||||
import type { ActivationFunction, OutputItem, RendererContext } from 'vscode-notebook-renderer';
|
||||
import { handleANSIOutput } from './ansi';
|
||||
import { truncatedArrayOfString } from './textHelper';
|
||||
|
||||
interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function renderImage(outputInfo: OutputItem, element: HTMLElement): IDisposable {
|
||||
const blob = new Blob([outputInfo.data()], { type: outputInfo.mime });
|
||||
const src = URL.createObjectURL(blob);
|
||||
|
@ -76,21 +79,19 @@ function renderJavascript(outputInfo: OutputItem, container: HTMLElement): void
|
|||
domEval(element);
|
||||
}
|
||||
|
||||
function renderError(outputIfo: OutputItem, container: HTMLElement): void {
|
||||
function renderError(outputInfo: OutputItem, container: HTMLElement): void {
|
||||
const element = document.createElement('div');
|
||||
container.appendChild(element);
|
||||
type ErrorLike = Partial<Error>;
|
||||
|
||||
let err: ErrorLike;
|
||||
try {
|
||||
err = <ErrorLike>JSON.parse(outputIfo.text());
|
||||
err = <ErrorLike>JSON.parse(outputInfo.text());
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(err);
|
||||
|
||||
if (err.stack) {
|
||||
const stack = document.createElement('pre');
|
||||
stack.classList.add('traceback');
|
||||
|
@ -109,9 +110,70 @@ function renderError(outputIfo: OutputItem, container: HTMLElement): void {
|
|||
container.classList.add('error');
|
||||
}
|
||||
|
||||
function renderStream(outputInfo: OutputItem, container: HTMLElement, error: boolean, ctx: RendererContext<void> & { readonly settings: { readonly lineLimit: number } }): void {
|
||||
const outputContainer = container.parentElement;
|
||||
if (!outputContainer) {
|
||||
// should never happen
|
||||
return;
|
||||
}
|
||||
|
||||
const prev = outputContainer.previousSibling;
|
||||
if (prev) {
|
||||
// OutputItem in the same cell
|
||||
// check if the previous item is a stream
|
||||
const outputElement = (prev.firstChild as HTMLElement | null);
|
||||
if (outputElement && outputElement.getAttribute('output-mime-type') === outputInfo.mime) {
|
||||
// same stream
|
||||
const text = outputInfo.text();
|
||||
|
||||
const element = document.createElement('span');
|
||||
truncatedArrayOfString(outputInfo.id, [text], 30, element);
|
||||
outputElement.appendChild(element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const element = document.createElement('span');
|
||||
element.classList.add('output-stream');
|
||||
|
||||
const text = outputInfo.text();
|
||||
truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, element);
|
||||
container.appendChild(element);
|
||||
container.setAttribute('output-mime-type', outputInfo.mime);
|
||||
if (error) {
|
||||
container.classList.add('error');
|
||||
}
|
||||
}
|
||||
|
||||
function renderText(outputInfo: OutputItem, container: HTMLElement, ctx: RendererContext<void> & { readonly settings: { readonly lineLimit: number } }): void {
|
||||
const contentNode = document.createElement('div');
|
||||
contentNode.classList.add('.output-plaintext');
|
||||
const text = outputInfo.text();
|
||||
truncatedArrayOfString(outputInfo.id, [text], ctx.settings.lineLimit, contentNode);
|
||||
container.appendChild(contentNode);
|
||||
|
||||
}
|
||||
|
||||
export const activate: ActivationFunction<void> = (ctx) => {
|
||||
const disposables = new Map<string, IDisposable>();
|
||||
const latestContext = ctx as (RendererContext<void> & { readonly settings: { readonly lineLimit: number } });
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
.output-stream {
|
||||
line-height: 22px;
|
||||
font-family: var(--notebook-cell-output-font-family);
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
|
||||
font-size: var(--notebook-cell-output-font-size);
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
-ms-user-select: text;
|
||||
cursor: auto;
|
||||
}
|
||||
`;
|
||||
document.body.appendChild(style);
|
||||
return {
|
||||
renderOutputItem: (outputInfo, element) => {
|
||||
switch (outputInfo.mime) {
|
||||
|
@ -137,6 +199,7 @@ export const activate: ActivationFunction<void> = (ctx) => {
|
|||
case 'image/gif':
|
||||
case 'image/png':
|
||||
case 'image/jpeg':
|
||||
case 'image/git':
|
||||
{
|
||||
const disposable = renderImage(outputInfo, element);
|
||||
disposables.set(outputInfo.id, disposable);
|
||||
|
@ -146,6 +209,25 @@ export const activate: ActivationFunction<void> = (ctx) => {
|
|||
{
|
||||
renderError(outputInfo, element);
|
||||
}
|
||||
break;
|
||||
case 'application/vnd.code.notebook.stdout':
|
||||
case 'application/x.notebook.stdout':
|
||||
case 'application/x.notebook.stream':
|
||||
{
|
||||
renderStream(outputInfo, element, false, latestContext);
|
||||
}
|
||||
break;
|
||||
case 'application/vnd.code.notebook.stderr':
|
||||
case 'application/x.notebook.stderr':
|
||||
{
|
||||
renderStream(outputInfo, element, true, latestContext);
|
||||
}
|
||||
break;
|
||||
case 'text/plain':
|
||||
{
|
||||
renderText(outputInfo, element, latestContext);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
51
extensions/notebook-renderers/src/textHelper.ts
Normal file
51
extensions/notebook-renderers/src/textHelper.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { handleANSIOutput } from './ansi';
|
||||
|
||||
function generateViewMoreElement(outputId: string) {
|
||||
const container = document.createElement('span');
|
||||
const first = document.createElement('span');
|
||||
first.textContent = 'Output exceeds the ';
|
||||
const second = document.createElement('a');
|
||||
second.textContent = 'size limit';
|
||||
second.href = `command:workbench.action.openSettings?["notebook.output.textLineLimit"]`;
|
||||
const third = document.createElement('span');
|
||||
third.textContent = '. Open the full output data';
|
||||
const forth = document.createElement('a');
|
||||
forth.textContent = ' in a text editor';
|
||||
forth.href = `command:workbench.action.openLargeOutput?${outputId}`;
|
||||
container.appendChild(first);
|
||||
container.appendChild(second);
|
||||
container.appendChild(third);
|
||||
container.appendChild(forth);
|
||||
return container;
|
||||
}
|
||||
|
||||
export function truncatedArrayOfString(id: string, outputs: string[], linesLimit: number, container: HTMLElement) {
|
||||
let buffer = outputs.join('\n').split(/\r|\n|\r\n/g);
|
||||
let lineCount = buffer.length;
|
||||
|
||||
if (lineCount < linesLimit) {
|
||||
const spanElement = handleANSIOutput(buffer.slice(0, linesLimit).join('\n'));
|
||||
container.appendChild(spanElement);
|
||||
return;
|
||||
}
|
||||
|
||||
container.appendChild(generateViewMoreElement(id));
|
||||
|
||||
const div = document.createElement('div');
|
||||
container.appendChild(div);
|
||||
div.appendChild(handleANSIOutput(buffer.slice(0, linesLimit - 5).join('\n')));
|
||||
|
||||
// view more ...
|
||||
const viewMoreSpan = document.createElement('span');
|
||||
viewMoreSpan.innerText = '...';
|
||||
container.appendChild(viewMoreSpan);
|
||||
|
||||
const div2 = document.createElement('div');
|
||||
container.appendChild(div2);
|
||||
div2.appendChild(handleANSIOutput(buffer.slice(lineCount - 5).join('\n')));
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { IModelService } from 'vs/editor/common/services/model';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { ICellOutputViewModel, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookDelegateForOutput } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
|
||||
abstract class CodeRendererContrib extends Disposable implements IOutputTransformContribution {
|
||||
getType() {
|
||||
return RenderOutputType.Mainframe;
|
||||
}
|
||||
|
||||
abstract getMimetypes(): string[];
|
||||
|
||||
constructor(
|
||||
public notebookEditor: INotebookDelegateForOutput,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@ILanguageService private readonly languageService: ILanguageService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
abstract render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement): IRenderOutput;
|
||||
|
||||
protected _render(output: ICellOutputViewModel, container: HTMLElement, value: string, languageId: string): IRenderOutput {
|
||||
const disposable = new DisposableStore();
|
||||
const editor = this.instantiationService.createInstance(CodeEditorWidget, container, getOutputSimpleEditorOptions(), { isSimpleWidget: true, contributions: this.notebookEditor.creationOptions.cellEditorContributions });
|
||||
|
||||
if (output.cellViewModel instanceof CodeCellViewModel) {
|
||||
disposable.add(output.cellViewModel.viewContext.eventDispatcher.onDidChangeLayout(() => {
|
||||
const outputWidth = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).width;
|
||||
const fontInfo = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).fontInfo;
|
||||
const editorHeight = Math.min(16 * (fontInfo.lineHeight || 18), editor.getLayoutInfo().height);
|
||||
|
||||
editor.layout({ height: editorHeight, width: outputWidth });
|
||||
container.style.height = `${editorHeight + 8}px`;
|
||||
}));
|
||||
}
|
||||
|
||||
disposable.add(editor.onDidContentSizeChange(e => {
|
||||
const outputWidth = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).width;
|
||||
const fontInfo = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).fontInfo;
|
||||
const editorHeight = Math.min(16 * (fontInfo.lineHeight || 18), e.contentHeight);
|
||||
|
||||
editor.layout({ height: editorHeight, width: outputWidth });
|
||||
container.style.height = `${editorHeight + 8}px`;
|
||||
}));
|
||||
|
||||
const mode = this.languageService.createById(languageId);
|
||||
const textModel = this.modelService.createModel(value, mode, undefined, false);
|
||||
editor.setModel(textModel);
|
||||
|
||||
const width = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).width;
|
||||
const fontInfo = this.notebookEditor.getCellOutputLayoutInfo(output.cellViewModel).fontInfo;
|
||||
const height = Math.min(textModel.getLineCount(), 16) * (fontInfo.lineHeight || 18);
|
||||
|
||||
editor.layout({ height, width });
|
||||
|
||||
disposable.add(editor);
|
||||
disposable.add(textModel);
|
||||
|
||||
container.style.height = `${height + 8}px`;
|
||||
|
||||
return { type: RenderOutputType.Mainframe, initHeight: height, disposable };
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCodeRendererContribution extends Disposable {
|
||||
|
||||
constructor(@ILanguageService _languageService: ILanguageService) {
|
||||
super();
|
||||
|
||||
const registeredMimeTypes = new Map();
|
||||
const registerCodeRendererContrib = (mimeType: string, languageId: string) => {
|
||||
if (registeredMimeTypes.has(mimeType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
OutputRendererRegistry.registerOutputTransform(class extends CodeRendererContrib {
|
||||
getMimetypes() {
|
||||
return [mimeType];
|
||||
}
|
||||
|
||||
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement): IRenderOutput {
|
||||
const str = item.data.toString();
|
||||
return this._render(output, container, str, languageId);
|
||||
}
|
||||
});
|
||||
|
||||
registeredMimeTypes.set(mimeType, true);
|
||||
};
|
||||
|
||||
_languageService.getRegisteredLanguageIds().forEach(id => {
|
||||
registerCodeRendererContrib(`text/x-${id}`, id);
|
||||
});
|
||||
|
||||
this._register(_languageService.onDidChange(() => {
|
||||
_languageService.getRegisteredLanguageIds().forEach(id => {
|
||||
registerCodeRendererContrib(`text/x-${id}`, id);
|
||||
});
|
||||
}));
|
||||
|
||||
registerCodeRendererContrib('application/json', 'json');
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookCodeRendererContribution, LifecyclePhase.Restored);
|
||||
|
||||
|
||||
// --- utils ---
|
||||
|
||||
function getOutputSimpleEditorOptions(): IEditorConstructionOptions {
|
||||
return {
|
||||
dimension: { height: 0, width: 0 },
|
||||
readOnly: true,
|
||||
wordWrap: 'on',
|
||||
overviewRulerLanes: 0,
|
||||
glyphMargin: false,
|
||||
selectOnLineNumbers: false,
|
||||
hideCursorInOverviewRuler: true,
|
||||
selectionHighlight: false,
|
||||
lineDecorationsWidth: 0,
|
||||
overviewRulerBorder: false,
|
||||
scrollBeyondLastLine: false,
|
||||
renderLineHighlight: 'none',
|
||||
minimap: {
|
||||
enabled: false
|
||||
},
|
||||
lineNumbers: 'off',
|
||||
scrollbar: {
|
||||
alwaysConsumeMouseWheel: false
|
||||
},
|
||||
automaticLayout: true,
|
||||
};
|
||||
}
|
|
@ -3,14 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { CellEditState, IInsetRenderOutput, INotebookEditor, INotebookEditorContribution, INotebookEditorDelegate, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { BUILTIN_RENDERER_ID, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
|
||||
|
@ -105,14 +104,6 @@ class NotebookViewportContribution extends Disposable implements INotebookEditor
|
|||
return;
|
||||
}
|
||||
|
||||
if (pickedMimeTypeRenderer.rendererId === BUILTIN_RENDERER_ID) {
|
||||
const renderer = this._notebookEditor.getOutputRenderer().getContribution(pickedMimeTypeRenderer.mimeType);
|
||||
if (renderer?.getType() === RenderOutputType.Html) {
|
||||
const renderResult = renderer.render(output, output.model.outputs.filter(op => op.mime === pickedMimeTypeRenderer.mimeType)[0], DOM.$(''), this._notebookEditor.textModel.uri) as IInsetRenderOutput;
|
||||
this._notebookEditor.createOutput(viewCell, renderResult, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
|
||||
if (!renderer) {
|
||||
|
|
|
@ -9,10 +9,9 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
|||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { DiffElementViewModelBase, SideBySideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
|
||||
import { DiffSide, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
|
||||
import { ICellOutputViewModel, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets';
|
||||
import { ICellOutputViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { BUILTIN_RENDERER_ID, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
@ -28,7 +27,7 @@ interface IMimeTypeRenderer extends IQuickPickItem {
|
|||
export class OutputElement extends Disposable {
|
||||
readonly resizeListener = this._register(new DisposableStore());
|
||||
domNode!: HTMLElement;
|
||||
renderResult?: IRenderOutput;
|
||||
renderResult?: IInsetRenderOutput;
|
||||
|
||||
constructor(
|
||||
private _notebookEditor: INotebookTextDiffEditor,
|
||||
|
@ -46,7 +45,7 @@ export class OutputElement extends Disposable {
|
|||
|
||||
render(index: number, beforeElement?: HTMLElement) {
|
||||
const outputItemDiv = document.createElement('div');
|
||||
let result: IRenderOutput | undefined = undefined;
|
||||
let result: IInsetRenderOutput | undefined = undefined;
|
||||
|
||||
const [mimeTypes, pick] = this.output.resolveMimeTypes(this._notebookTextModel, undefined);
|
||||
const pickedMimeTypeRenderer = mimeTypes[pick];
|
||||
|
@ -80,14 +79,10 @@ export class OutputElement extends Disposable {
|
|||
|
||||
|
||||
if (mimeTypes.length !== 0) {
|
||||
if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) {
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
result = renderer
|
||||
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
|
||||
: this._notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this._notebookTextModel.uri,);
|
||||
} else {
|
||||
result = this._notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this._notebookTextModel.uri);
|
||||
}
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
result = renderer
|
||||
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
|
||||
: this._renderMissingRenderer(this.output, pickedMimeTypeRenderer.mimeType);
|
||||
|
||||
this.output.pickedMimeType = pickedMimeTypeRenderer;
|
||||
}
|
||||
|
@ -106,52 +101,43 @@ export class OutputElement extends Disposable {
|
|||
this._outputContainer.appendChild(outputItemDiv);
|
||||
}
|
||||
|
||||
if (result.type !== RenderOutputType.Mainframe) {
|
||||
// this.viewCell.selfSizeMonitoring = true;
|
||||
this._notebookEditor.createOutput(
|
||||
this._diffElementViewModel,
|
||||
this._nestedCell,
|
||||
result,
|
||||
() => this.getOutputOffsetInCell(index),
|
||||
this._diffElementViewModel instanceof SideBySideDiffElementViewModel
|
||||
? this._diffSide
|
||||
: this._diffElementViewModel.type === 'insert' ? DiffSide.Modified : DiffSide.Original
|
||||
);
|
||||
} else {
|
||||
outputItemDiv.classList.add('foreground', 'output-element');
|
||||
outputItemDiv.style.position = 'absolute';
|
||||
}
|
||||
if (result.type === RenderOutputType.Html || result.type === RenderOutputType.Extension) {
|
||||
return;
|
||||
this._notebookEditor.createOutput(
|
||||
this._diffElementViewModel,
|
||||
this._nestedCell,
|
||||
result,
|
||||
() => this.getOutputOffsetInCell(index),
|
||||
this._diffElementViewModel instanceof SideBySideDiffElementViewModel
|
||||
? this._diffSide
|
||||
: this._diffElementViewModel.type === 'insert' ? DiffSide.Modified : DiffSide.Original
|
||||
);
|
||||
}
|
||||
|
||||
private _renderMissingRenderer(viewModel: ICellOutputViewModel, preferredMimeType: string | undefined): IInsetRenderOutput {
|
||||
if (!viewModel.model.outputs.length) {
|
||||
return this._renderMessage(viewModel, nls.localize('empty', "Cell has no output"));
|
||||
}
|
||||
|
||||
if (!preferredMimeType) {
|
||||
const mimeTypes = viewModel.model.outputs.map(op => op.mime);
|
||||
const mimeTypesMessage = mimeTypes.join(', ');
|
||||
return this._renderMessage(viewModel, nls.localize('noRenderer.2', "No renderer could be found for output. It has the following mimetypes: {0}", mimeTypesMessage));
|
||||
}
|
||||
|
||||
return this._renderSearchForMimetype(viewModel, preferredMimeType);
|
||||
}
|
||||
|
||||
let clientHeight = Math.ceil(outputItemDiv.clientHeight);
|
||||
const elementSizeObserver = getResizesObserver(outputItemDiv, undefined, () => {
|
||||
if (this._outputContainer && document.body.contains(this._outputContainer)) {
|
||||
const height = Math.ceil(elementSizeObserver.getHeight());
|
||||
private _renderSearchForMimetype(viewModel: ICellOutputViewModel, mimeType: string): IInsetRenderOutput {
|
||||
const query = `@tag:notebookRenderer ${mimeType}`;
|
||||
return {
|
||||
type: RenderOutputType.Html,
|
||||
source: viewModel,
|
||||
htmlContent: `<p>No renderer could be found for mimetype "${mimeType}", but one might be available on the Marketplace.</p>
|
||||
<a href="command:workbench.extensions.search?%22${query}%22" class="monaco-button monaco-text-button" tabindex="0" role="button" style="padding: 8px; text-decoration: none; color: rgb(255, 255, 255); background-color: rgb(14, 99, 156); max-width: 200px;">Search Marketplace</a>`
|
||||
};
|
||||
}
|
||||
|
||||
if (clientHeight === height) {
|
||||
return;
|
||||
}
|
||||
|
||||
clientHeight = height;
|
||||
|
||||
const currIndex = this.getCellOutputCurrentIndex();
|
||||
if (currIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateHeight(currIndex, height);
|
||||
}
|
||||
});
|
||||
elementSizeObserver.startObserving();
|
||||
this.resizeListener.add(elementSizeObserver);
|
||||
this.updateHeight(index, clientHeight);
|
||||
|
||||
const top = this.getOutputOffsetInContainer(index);
|
||||
outputItemDiv.style.top = `${top}px`;
|
||||
private _renderMessage(viewModel: ICellOutputViewModel, message: string): IInsetRenderOutput {
|
||||
return { type: RenderOutputType.Html, source: viewModel, htmlContent: `<p>${message}</p>` };
|
||||
}
|
||||
|
||||
private async pickActiveMimeTypeRenderer(notebookTextModel: NotebookTextModel, viewModel: ICellOutputViewModel) {
|
||||
|
@ -206,11 +192,7 @@ export class OutputElement extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private generateRendererInfo(renderId: string | undefined): string {
|
||||
if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) {
|
||||
return nls.localize('builtinRenderInfo', "built-in");
|
||||
}
|
||||
|
||||
private generateRendererInfo(renderId: string): string {
|
||||
const renderInfo = this._notebookService.getRendererInfo(renderId);
|
||||
|
||||
if (renderInfo) {
|
||||
|
|
|
@ -12,7 +12,6 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no
|
|||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
|
@ -35,7 +34,6 @@ export interface INotebookTextDiffEditor {
|
|||
getOverflowContainerDomNode(): HTMLElement;
|
||||
getLayoutInfo(): NotebookLayoutInfo;
|
||||
layoutNotebookCell(cell: DiffElementViewModelBase, height: number): void;
|
||||
getOutputRenderer(): OutputRenderer;
|
||||
createOutput(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: IInsetRenderOutput, getOffset: () => number, diffSide: DiffSide): void;
|
||||
showInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, displayOutput: ICellOutputViewModel, diffSide: DiffSide): void;
|
||||
removeInset(cellDiffViewModel: DiffElementViewModelBase, cellViewModel: IDiffNestedCellViewModel, output: ICellOutputViewModel, diffSide: DiffSide): void;
|
||||
|
|
|
@ -32,7 +32,6 @@ import { CellUri, INotebookDiffEditorModel, INotebookDiffResult, NOTEBOOK_DIFF_E
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { IDiffChange, IDiffResult } from 'vs/base/common/diff/diff';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { SequencerByKey } from 'vs/base/common/async';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
|
@ -66,7 +65,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
protected _scopeContextKeyService!: IContextKeyService;
|
||||
private _model: INotebookDiffEditorModel | null = null;
|
||||
private readonly _modifiedResourceDisposableStore = this._register(new DisposableStore());
|
||||
private _outputRenderer: OutputRenderer;
|
||||
|
||||
get textModel() {
|
||||
return this._model?.modified.notebook;
|
||||
|
@ -108,7 +106,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
const editorOptions = this.configurationService.getValue<ICodeEditorOptions>('editor');
|
||||
this._fontInfo = FontMeasurements.readFontInfo(BareFontInfo.createFromRawSettings(editorOptions, PixelRatio.value));
|
||||
this._revealFirst = true;
|
||||
this._outputRenderer = this.instantiationService.createInstance(OutputRenderer, this);
|
||||
}
|
||||
|
||||
toggleNotebookCellSelection(cell: IGenericCellViewModel) {
|
||||
|
@ -793,10 +790,6 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD
|
|||
this._diffElementViewModels = [];
|
||||
}
|
||||
|
||||
getOutputRenderer(): OutputRenderer {
|
||||
return this._outputRenderer;
|
||||
}
|
||||
|
||||
deltaCellOutputContainerClassNames(diffSide: DiffSide, cellId: string, added: string[], removed: string[]) {
|
||||
if (diffSide === DiffSide.Original) {
|
||||
this._originalWebview?.deltaCellOutputContainerClassNames(cellId, added, removed);
|
||||
|
|
|
@ -86,7 +86,6 @@ import 'vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo'
|
|||
import 'vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/codeRenderer/codeRenderer';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/execute/executionEditorProgress';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/execute/execution';
|
||||
|
@ -95,7 +94,6 @@ import 'vs/workbench/contrib/notebook/browser/contrib/execute/execution';
|
|||
import 'vs/workbench/contrib/notebook/browser/diff/notebookDiffActions';
|
||||
|
||||
// Output renderers registration
|
||||
import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform';
|
||||
import { editorOptionsRegistry } from 'vs/editor/common/config/editorOptions';
|
||||
import { NotebookExecutionStateService } from 'vs/workbench/contrib/notebook/browser/notebookExecutionStateServiceImpl';
|
||||
import { NotebookExecutionService } from 'vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl';
|
||||
|
@ -396,7 +394,6 @@ class CellInfoContentProvider {
|
|||
|
||||
private _getResult(data: {
|
||||
notebook: URI;
|
||||
handle: number;
|
||||
outputId?: string | undefined;
|
||||
}, cell: NotebookCellTextModel) {
|
||||
let result: { content: string; mode: ILanguageSelection } | undefined = undefined;
|
||||
|
@ -438,7 +435,7 @@ class CellInfoContentProvider {
|
|||
}
|
||||
|
||||
const ref = await this._notebookModelResolverService.resolve(data.notebook);
|
||||
const cell = ref.object.notebook.cells.find(cell => cell.handle === data.handle);
|
||||
const cell = ref.object.notebook.cells.find(cell => !!cell.outputs.find(op => op.outputId === data.outputId));
|
||||
|
||||
if (!cell) {
|
||||
return null;
|
||||
|
|
|
@ -20,7 +20,7 @@ import { IEditorPane } from 'vs/workbench/common/editor';
|
|||
import { CellViewModelStateChangeEvent, NotebookCellStateChangedEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, IOrderedMimeType, IOutputItemDto, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { isCompositeNotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
|
@ -52,18 +52,10 @@ export const KERNEL_EXTENSIONS = new Map<string, string>([
|
|||
|
||||
//#region Output related types
|
||||
export const enum RenderOutputType {
|
||||
Mainframe,
|
||||
Html,
|
||||
Extension
|
||||
}
|
||||
|
||||
export interface IRenderMainframeOutput {
|
||||
type: RenderOutputType.Mainframe;
|
||||
supportAppend?: boolean;
|
||||
initHeight?: number;
|
||||
disposable?: IDisposable;
|
||||
}
|
||||
|
||||
export interface IRenderPlainHtmlOutput {
|
||||
type: RenderOutputType.Html;
|
||||
source: IDisplayOutputViewModel;
|
||||
|
@ -78,28 +70,6 @@ export interface IRenderOutputViaExtension {
|
|||
}
|
||||
|
||||
export type IInsetRenderOutput = IRenderPlainHtmlOutput | IRenderOutputViaExtension;
|
||||
export type IRenderOutput = IRenderMainframeOutput | IInsetRenderOutput;
|
||||
|
||||
export interface IOutputTransformContribution {
|
||||
getType(): RenderOutputType;
|
||||
getMimetypes(): string[];
|
||||
/**
|
||||
* Dispose this contribution.
|
||||
*/
|
||||
dispose(): void;
|
||||
|
||||
/**
|
||||
* Returns contents to place in the webview inset, or the {@link IRenderNoOutput}.
|
||||
* This call is allowed to have side effects, such as placing output
|
||||
* directly into the container element.
|
||||
*/
|
||||
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput;
|
||||
}
|
||||
|
||||
export interface IOutputRenderer {
|
||||
render(viewModel: ICellOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput;
|
||||
getContribution(preferredMimeType: string): IOutputTransformContribution | undefined;
|
||||
}
|
||||
|
||||
export interface ICellOutputViewModel extends IDisposable {
|
||||
cellViewModel: IGenericCellViewModel;
|
||||
|
@ -109,7 +79,6 @@ export interface ICellOutputViewModel extends IDisposable {
|
|||
model: ICellOutput;
|
||||
resolveMimeTypes(textModel: NotebookTextModel, kernelProvides: readonly string[] | undefined): [readonly IOrderedMimeType[], number];
|
||||
pickedMimeType: IOrderedMimeType | undefined;
|
||||
supportAppend(): boolean;
|
||||
hasMultiMimeType(): boolean;
|
||||
toRawJSON(): any;
|
||||
}
|
||||
|
@ -149,12 +118,6 @@ export interface ICommonCellInfo {
|
|||
cellUri: URI;
|
||||
}
|
||||
|
||||
export interface INotebookCellOutputLayoutInfo {
|
||||
width: number;
|
||||
height: number;
|
||||
fontInfo: FontInfo;
|
||||
}
|
||||
|
||||
export interface IFocusNotebookCellOptions {
|
||||
readonly skipReveal?: boolean;
|
||||
}
|
||||
|
@ -467,11 +430,6 @@ export interface INotebookEditor {
|
|||
|
||||
getVisibleRangesPlusViewportBelow(): ICellRange[];
|
||||
|
||||
/**
|
||||
* Fetch the output renderers for notebook outputs.
|
||||
*/
|
||||
getOutputRenderer(): IOutputRenderer;
|
||||
|
||||
/**
|
||||
* Focus the container of a cell (the monaco editor inside is not focused).
|
||||
*/
|
||||
|
|
|
@ -43,7 +43,7 @@ import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeg
|
|||
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { PANEL_BORDER, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors';
|
||||
import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, IActiveNotebookEditorDelegate, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IGenericCellViewModel, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, IActiveNotebookEditorDelegate, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookWebviewMessage, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { notebookDebug } from 'vs/workbench/contrib/notebook/browser/notebookLogger';
|
||||
|
@ -52,7 +52,6 @@ import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/vie
|
|||
import { CellDragAndDropController } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellDnd';
|
||||
import { NotebookCellList, NOTEBOOK_WEBVIEW_BOUNDARY } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
|
||||
import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView';
|
||||
import { CodeCellRenderer, MarkupCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer';
|
||||
import { IAckOutputHeight, IMarkupCellInitialization } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages';
|
||||
|
@ -66,7 +65,7 @@ import { NotebookEditorToolbar } from 'vs/workbench/contrib/notebook/browser/vie
|
|||
import { NotebookEditorContextKeys } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys';
|
||||
import { ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { BUILTIN_RENDERER_ID, CellKind, INotebookSearchOptions, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, INotebookSearchOptions, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
|
||||
import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
|
@ -332,8 +331,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
private readonly _editorFocus: IContextKey<boolean>;
|
||||
private readonly _outputFocus: IContextKey<boolean>;
|
||||
private readonly _editorEditable: IContextKey<boolean>;
|
||||
|
||||
private _outputRenderer: OutputRenderer;
|
||||
protected readonly _contributions = new Map<string, INotebookEditorContribution>();
|
||||
private _scrollBeyondLastLine: boolean;
|
||||
private readonly _insetModifyQueueByOutputId = new SequencerByKey<string>();
|
||||
|
@ -444,11 +441,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}
|
||||
}));
|
||||
|
||||
const that = this;
|
||||
this._outputRenderer = this._register(this.instantiationService.createInstance(OutputRenderer, {
|
||||
get creationOptions() { return that.creationOptions; },
|
||||
getCellOutputLayoutInfo: that._getCellOutputLayoutInfo.bind(that)
|
||||
}));
|
||||
this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');
|
||||
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
|
@ -2361,25 +2353,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
return;
|
||||
}
|
||||
|
||||
if (pickedMimeTypeRenderer.rendererId === BUILTIN_RENDERER_ID) {
|
||||
const renderer = this.getOutputRenderer().getContribution(pickedMimeTypeRenderer.mimeType);
|
||||
if (renderer?.getType() === RenderOutputType.Html) {
|
||||
const renderResult = renderer.render(output, output.model.outputs.filter(op => op.mime === pickedMimeTypeRenderer.mimeType)[0], DOM.$(''), this.textModel!.uri) as IInsetRenderOutput;
|
||||
if (!this._webview?.insetMapping.has(renderResult.source)) {
|
||||
const p = new Promise<void>(resolve => {
|
||||
this.onDidRenderOutput(e => {
|
||||
if (e.model === renderResult.source.model) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
this.createOutput(viewCell, renderResult, 0);
|
||||
await p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
|
||||
if (!renderer) {
|
||||
|
@ -2548,30 +2521,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
};
|
||||
}
|
||||
|
||||
private _getCellOutputLayoutInfo(cell: IGenericCellViewModel): INotebookCellOutputLayoutInfo {
|
||||
if (!this._list) {
|
||||
throw new Error('Editor is not initalized successfully');
|
||||
}
|
||||
|
||||
if (!this._fontInfo) {
|
||||
this._generateFontInfo();
|
||||
}
|
||||
|
||||
const {
|
||||
cellRunGutter,
|
||||
codeCellLeftMargin,
|
||||
cellRightMargin
|
||||
} = this._notebookOptions.getLayoutConfiguration();
|
||||
|
||||
const width = (this._dimension?.width ?? 0) - (codeCellLeftMargin + cellRunGutter + cellRightMargin) - 8 /** padding */ * 2;
|
||||
|
||||
return {
|
||||
width: Math.max(width, 0),
|
||||
height: this._dimension?.height ?? 0,
|
||||
fontInfo: this._fontInfo!
|
||||
};
|
||||
}
|
||||
|
||||
async createMarkupPreview(cell: MarkupCellViewModel) {
|
||||
if (!this._webview) {
|
||||
return;
|
||||
|
@ -2707,10 +2656,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}
|
||||
}
|
||||
|
||||
getOutputRenderer(): OutputRenderer {
|
||||
return this._outputRenderer;
|
||||
}
|
||||
|
||||
//#region --- webview IPC ----
|
||||
postMessage(message: any) {
|
||||
if (this._webview?.isResolved()) {
|
||||
|
|
|
@ -22,14 +22,13 @@ import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { INotebookEditorContribution, notebookRendererExtensionPoint, notebooksExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint';
|
||||
import { INotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookData, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions, NotebookExtensionDescription } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, NotebookData, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, TransientOptions, NotebookExtensionDescription } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { updateEditorTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
|
@ -277,7 +276,6 @@ export class NotebookOutputRendererInfoStore {
|
|||
|
||||
constructor(
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,
|
||||
) {
|
||||
this.preferredMimetypeMemento = new Memento('workbench.editor.notebook.preferredRenderer2', storageService);
|
||||
}
|
||||
|
@ -348,17 +346,6 @@ export class NotebookOutputRendererInfoStore {
|
|||
};
|
||||
}).filter(isDefined);
|
||||
|
||||
if (mimeTypeSupportedByCore(mimeType)) {
|
||||
renderers.push({
|
||||
score: ReuseOrder.BuiltIn,
|
||||
ordered: {
|
||||
mimeType,
|
||||
rendererId: BUILTIN_RENDERER_ID,
|
||||
isTrusted: mimeTypeIsAlwaysSecure(mimeType) || this.workspaceTrustManagementService.isWorkspaceTrusted()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (renderers.length === 0) {
|
||||
return [{ mimeType, rendererId: RENDERER_NOT_AVAILABLE, isTrusted: true }];
|
||||
}
|
||||
|
|
|
@ -24,15 +24,13 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
|||
import { ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSION_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/controller/coreActions';
|
||||
import { ICellOutputViewModel, ICellViewModel, IInsetRenderOutput, INotebookEditorDelegate, IRenderOutput, JUPYTER_EXTENSION_ID, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { ICellOutputViewModel, ICellViewModel, IInsetRenderOutput, INotebookEditorDelegate, JUPYTER_EXTENSION_ID, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { mimetypeIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellWidgets';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { BUILTIN_RENDERER_ID, CellUri, IOrderedMimeType, NotebookCellOutputsSplice, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellUri, IOrderedMimeType, NotebookCellOutputsSplice, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { OutputInnerContainerTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
|
||||
import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart';
|
||||
|
@ -43,7 +41,7 @@ interface IMimeTypeRenderer extends IQuickPickItem {
|
|||
}
|
||||
|
||||
interface IRenderResult {
|
||||
initRenderIsSynchronous: boolean;
|
||||
initRenderIsSynchronous: false;
|
||||
}
|
||||
|
||||
// DOM structure
|
||||
|
@ -67,22 +65,7 @@ export class CellOutputElement extends Disposable {
|
|||
|
||||
innerContainer?: HTMLElement;
|
||||
renderedOutputContainer!: HTMLElement;
|
||||
renderResult?: IRenderOutput;
|
||||
|
||||
public useDedicatedDOM: boolean = true;
|
||||
|
||||
private _height: number = -1;
|
||||
get domOffsetHeight() {
|
||||
if (this.useDedicatedDOM) {
|
||||
if (this._height === -1) {
|
||||
return this.innerContainer?.offsetHeight ?? 0;
|
||||
} else {
|
||||
return this._height;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
renderResult?: IInsetRenderOutput;
|
||||
|
||||
private readonly contextKeyService: IContextKeyService;
|
||||
|
||||
|
@ -98,7 +81,7 @@ export class CellOutputElement extends Disposable {
|
|||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IContextKeyService parentContextKeyService: IContextKeyService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService,
|
||||
@IPaneCompositePartService private readonly paneCompositeService: IPaneCompositePartService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -132,23 +115,11 @@ export class CellOutputElement extends Disposable {
|
|||
}
|
||||
|
||||
this.notebookEditor.removeInset(this.output);
|
||||
|
||||
if (this.renderResult && this.renderResult.type === RenderOutputType.Mainframe) {
|
||||
this.renderResult.disposable?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
forceReadDOM() {
|
||||
if (this.useDedicatedDOM && this.innerContainer) {
|
||||
this._height = this.innerContainer.offsetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
updateDOMTop(top: number) {
|
||||
if (this.useDedicatedDOM) {
|
||||
if (this.innerContainer) {
|
||||
this.innerContainer.style.top = `${top}px`;
|
||||
}
|
||||
if (this.innerContainer) {
|
||||
this.innerContainer.style.top = `${top}px`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,66 +149,25 @@ export class CellOutputElement extends Disposable {
|
|||
this.notebookEditor.removeInset(this.output);
|
||||
}
|
||||
|
||||
// this.output.pickedMimeType = pick;
|
||||
this.render(nextElement as HTMLElement);
|
||||
this._relayoutCell();
|
||||
}
|
||||
|
||||
// insert after previousSibling
|
||||
private _generateInnerOutputContainer(previousSibling: HTMLElement | undefined, pickedMimeTypeRenderer: IOrderedMimeType, forceBreakStreaming: boolean) {
|
||||
if (this.output.supportAppend() && !forceBreakStreaming) {
|
||||
// current output support append
|
||||
if (previousSibling) {
|
||||
if (this._divSupportAppend(previousSibling as HTMLElement | null, pickedMimeTypeRenderer.mimeType)) {
|
||||
this.useDedicatedDOM = false;
|
||||
this.innerContainer = previousSibling as HTMLElement;
|
||||
} else {
|
||||
this.useDedicatedDOM = true;
|
||||
this.innerContainer = DOM.$('.output-inner-container');
|
||||
if (previousSibling.nextElementSibling) {
|
||||
this.outputContainer.domNode.insertBefore(this.innerContainer, previousSibling.nextElementSibling);
|
||||
} else {
|
||||
this.outputContainer.domNode.appendChild(this.innerContainer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no previousSibling, append it to the very last
|
||||
if (this._divSupportAppend(this.outputContainer.domNode.lastChild as HTMLElement | null, pickedMimeTypeRenderer.mimeType)) {
|
||||
// last element allows append
|
||||
this.useDedicatedDOM = false;
|
||||
this.innerContainer = this.outputContainer.domNode.lastChild as HTMLElement;
|
||||
} else {
|
||||
this.useDedicatedDOM = true;
|
||||
this.innerContainer = DOM.$('.output-inner-container');
|
||||
this.outputContainer.domNode.appendChild(this.innerContainer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.useDedicatedDOM = true;
|
||||
this.innerContainer = DOM.$('.output-inner-container');
|
||||
private _generateInnerOutputContainer(previousSibling: HTMLElement | undefined, pickedMimeTypeRenderer: IOrderedMimeType) {
|
||||
this.innerContainer = DOM.$('.output-inner-container');
|
||||
|
||||
if (previousSibling && previousSibling.nextElementSibling) {
|
||||
this.outputContainer.domNode.insertBefore(this.innerContainer, previousSibling.nextElementSibling);
|
||||
} else if (this.useDedicatedDOM) {
|
||||
this.outputContainer.domNode.appendChild(this.innerContainer);
|
||||
}
|
||||
if (previousSibling && previousSibling.nextElementSibling) {
|
||||
this.outputContainer.domNode.insertBefore(this.innerContainer, previousSibling.nextElementSibling);
|
||||
} else {
|
||||
this.outputContainer.domNode.appendChild(this.innerContainer);
|
||||
}
|
||||
|
||||
this.innerContainer.setAttribute('output-mime-type', pickedMimeTypeRenderer.mimeType);
|
||||
return this.innerContainer;
|
||||
}
|
||||
|
||||
private _initHeightChecked = false;
|
||||
|
||||
probeHeight(index: number) {
|
||||
if (!this._initHeightChecked && this.renderResult?.type === RenderOutputType.Mainframe) {
|
||||
// postponed DOM read
|
||||
const offsetHeight = this.domOffsetHeight;
|
||||
this.viewCell.updateOutputHeight(index, offsetHeight, 'CellOutputElement#renderResultInitHeight');
|
||||
}
|
||||
}
|
||||
|
||||
render(previousSibling: HTMLElement | undefined, forceBreakStreaming: boolean = false): IRenderResult | undefined {
|
||||
render(previousSibling: HTMLElement | undefined): IRenderResult | undefined {
|
||||
const index = this.viewCell.outputsViewModels.indexOf(this.output);
|
||||
|
||||
if (this.viewCell.isOutputCollapsed || !this.notebookEditor.hasModel()) {
|
||||
|
@ -259,21 +189,15 @@ export class CellOutputElement extends Disposable {
|
|||
}
|
||||
|
||||
const pickedMimeTypeRenderer = mimeTypes[pick];
|
||||
|
||||
// generate an innerOutputContainer only when needed, for text streaming, it will reuse the previous element's container
|
||||
const innerContainer = this._generateInnerOutputContainer(previousSibling, pickedMimeTypeRenderer, forceBreakStreaming);
|
||||
const innerContainer = this._generateInnerOutputContainer(previousSibling, pickedMimeTypeRenderer);
|
||||
this._attachToolbar(innerContainer, notebookTextModel, this.notebookEditor.activeKernel, index, mimeTypes);
|
||||
|
||||
this.renderedOutputContainer = DOM.append(innerContainer, DOM.$('.rendered-output'));
|
||||
|
||||
if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) {
|
||||
const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
this.renderResult = renderer
|
||||
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
|
||||
: this.notebookEditor.getOutputRenderer().render(this.output, this.renderedOutputContainer, pickedMimeTypeRenderer.mimeType, notebookUri);
|
||||
} else {
|
||||
this.renderResult = this.notebookEditor.getOutputRenderer().render(this.output, this.renderedOutputContainer, pickedMimeTypeRenderer.mimeType, notebookUri);
|
||||
}
|
||||
const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
|
||||
this.renderResult = renderer
|
||||
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
|
||||
: this._renderMissingRenderer(this.output, pickedMimeTypeRenderer.mimeType);
|
||||
|
||||
this.output.pickedMimeType = pickedMimeTypeRenderer;
|
||||
|
||||
|
@ -282,92 +206,38 @@ export class CellOutputElement extends Disposable {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
if (this.renderResult.type !== RenderOutputType.Mainframe) {
|
||||
this.notebookEditor.createOutput(this.viewCell, this.renderResult, this.viewCell.getOutputOffset(index));
|
||||
innerContainer.classList.add('background');
|
||||
} else {
|
||||
innerContainer.classList.add('foreground', 'output-element');
|
||||
innerContainer.style.position = 'absolute';
|
||||
this.notebookEditor.createOutput(this.viewCell, this.renderResult, this.viewCell.getOutputOffset(index));
|
||||
innerContainer.classList.add('background');
|
||||
|
||||
return { initRenderIsSynchronous: false };
|
||||
}
|
||||
|
||||
private _renderMissingRenderer(viewModel: ICellOutputViewModel, preferredMimeType: string | undefined): IInsetRenderOutput {
|
||||
if (!viewModel.model.outputs.length) {
|
||||
return this._renderMessage(viewModel, nls.localize('empty', "Cell has no output"));
|
||||
}
|
||||
|
||||
if (this.renderResult.type === RenderOutputType.Html || this.renderResult.type === RenderOutputType.Extension) {
|
||||
// the output is rendered in the webview, which has resize listener internally
|
||||
// no-op
|
||||
return { initRenderIsSynchronous: false };
|
||||
if (!preferredMimeType) {
|
||||
const mimeTypes = viewModel.model.outputs.map(op => op.mime);
|
||||
const mimeTypesMessage = mimeTypes.join(', ');
|
||||
return this._renderMessage(viewModel, nls.localize('noRenderer.2', "No renderer could be found for output. It has the following mimetypes: {0}", mimeTypesMessage));
|
||||
}
|
||||
|
||||
if (!this.useDedicatedDOM) {
|
||||
// we only support text streaming, which is sync.
|
||||
return { initRenderIsSynchronous: true };
|
||||
}
|
||||
return this._renderSearchForMimetype(viewModel, preferredMimeType);
|
||||
}
|
||||
|
||||
let offsetHeight = 0;
|
||||
if (this.renderResult?.initHeight) {
|
||||
offsetHeight = this.renderResult.initHeight;
|
||||
this._initHeightChecked = true;
|
||||
} else {
|
||||
const outputIndex = this.viewCell.outputsViewModels.indexOf(this.output);
|
||||
const oldHeight = this.viewCell.getOutputHeight(outputIndex);
|
||||
if (oldHeight > 0) {
|
||||
offsetHeight = oldHeight;
|
||||
this._initHeightChecked = true;
|
||||
} else {
|
||||
this._initHeightChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
const dimension = {
|
||||
width: this.viewCell.layoutInfo.editorWidth,
|
||||
height: offsetHeight
|
||||
private _renderSearchForMimetype(viewModel: ICellOutputViewModel, mimeType: string): IInsetRenderOutput {
|
||||
const query = `@tag:notebookRenderer ${mimeType}`;
|
||||
return {
|
||||
type: RenderOutputType.Html,
|
||||
source: viewModel,
|
||||
htmlContent: `<p>No renderer could be found for mimetype "${mimeType}", but one might be available on the Marketplace.</p>
|
||||
<a href="command:workbench.extensions.search?%22${query}%22" class="monaco-button monaco-text-button" tabindex="0" role="button" style="padding: 8px; text-decoration: none; color: rgb(255, 255, 255); background-color: rgb(14, 99, 156); max-width: 200px;">Search Marketplace</a>`
|
||||
};
|
||||
|
||||
// let's use resize listener for them
|
||||
this._bindResizeListener(innerContainer, dimension);
|
||||
if (this._initHeightChecked) {
|
||||
this.viewCell.updateOutputHeight(index, offsetHeight, 'CellOutputElement#renderResultInitHeight');
|
||||
}
|
||||
const top = this.viewCell.getOutputOffsetInContainer(index);
|
||||
innerContainer.style.top = `${top}px`;
|
||||
return { initRenderIsSynchronous: this._initHeightChecked };
|
||||
}
|
||||
|
||||
private _bindResizeListener(innerContainer: HTMLElement, dimension: DOM.IDimension) {
|
||||
const elementSizeObserver = getResizesObserver(innerContainer, dimension, () => {
|
||||
if (this.outputContainer && document.body.contains(this.outputContainer.domNode)) {
|
||||
const height = elementSizeObserver.getHeight() + OutputInnerContainerTopPadding * 2;
|
||||
|
||||
if (dimension.height === height) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currIndex = this.viewCell.outputsViewModels.indexOf(this.output);
|
||||
if (currIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dimension = {
|
||||
width: this.viewCell.layoutInfo.editorWidth,
|
||||
height: height
|
||||
};
|
||||
|
||||
this._initHeightChecked = true;
|
||||
this._height = height;
|
||||
this._validateFinalOutputHeight(true);
|
||||
this.viewCell.updateOutputHeight(currIndex, height, 'CellOutputElement#outputResize');
|
||||
this._relayoutCell();
|
||||
}
|
||||
});
|
||||
|
||||
elementSizeObserver.startObserving();
|
||||
this._renderDisposableStore.add(elementSizeObserver);
|
||||
}
|
||||
|
||||
private _divSupportAppend(element: HTMLElement | null, mimeType: string) {
|
||||
if (element) {
|
||||
return element.getAttribute('output-mime-type') === mimeType;
|
||||
}
|
||||
|
||||
return false;
|
||||
private _renderMessage(viewModel: ICellOutputViewModel, message: string): IInsetRenderOutput {
|
||||
return { type: RenderOutputType.Html, source: viewModel, htmlContent: `<p>${message}</p>` };
|
||||
}
|
||||
|
||||
private async _attachToolbar(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, kernel: INotebookKernel | undefined, index: number, mimeTypes: readonly IOrderedMimeType[]) {
|
||||
|
@ -500,11 +370,7 @@ export class CellOutputElement extends Disposable {
|
|||
view?.search(`@id:${JUPYTER_EXTENSION_ID}`);
|
||||
}
|
||||
|
||||
private _generateRendererInfo(renderId: string | undefined): string {
|
||||
if (renderId === undefined || renderId === BUILTIN_RENDERER_ID) {
|
||||
return nls.localize('builtinRenderInfo', "built-in");
|
||||
}
|
||||
|
||||
private _generateRendererInfo(renderId: string): string {
|
||||
const renderInfo = this.notebookService.getRendererInfo(renderId);
|
||||
|
||||
if (renderInfo) {
|
||||
|
@ -541,10 +407,6 @@ export class CellOutputElement extends Disposable {
|
|||
clearTimeout(this._outputHeightTimer);
|
||||
}
|
||||
|
||||
if (this.renderResult && this.renderResult.type === RenderOutputType.Mainframe) {
|
||||
this.renderResult.disposable?.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -602,12 +464,6 @@ export class CellOutputContainer extends CellPart {
|
|||
}
|
||||
|
||||
prepareLayout() {
|
||||
this._outputEntries.forEach(entry => {
|
||||
const index = this.viewCell.outputsViewModels.indexOf(entry.model);
|
||||
if (index >= 0) {
|
||||
entry.element.probeHeight(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -659,13 +515,7 @@ export class CellOutputContainer extends CellPart {
|
|||
const viewHandler = this._outputEntries[index];
|
||||
const outputEntry = viewHandler.element;
|
||||
if (outputEntry.renderResult) {
|
||||
if (outputEntry.renderResult.type !== RenderOutputType.Mainframe) {
|
||||
this.notebookEditor.createOutput(this.viewCell, outputEntry.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index));
|
||||
} else if (!initRendering) {
|
||||
// force read otherwise the real height is updated in next frame through resize observer
|
||||
outputEntry.forceReadDOM();
|
||||
this.viewCell.updateOutputHeight(index, outputEntry.domOffsetHeight, 'CellOutputContainer#viewUpdateShowOutputs');
|
||||
}
|
||||
this.notebookEditor.createOutput(this.viewCell, outputEntry.renderResult as IInsetRenderOutput, this.viewCell.getOutputOffset(index));
|
||||
} else {
|
||||
outputEntry.render(undefined);
|
||||
}
|
||||
|
@ -723,8 +573,6 @@ export class CellOutputContainer extends CellPart {
|
|||
const secondGroupEntries = this._outputEntries.slice(splice.start + splice.deleteCount);
|
||||
let newlyInserted = this.viewCell.outputsViewModels.slice(splice.start, splice.start + splice.newOutputs.length);
|
||||
|
||||
let outputHasDynamicHeight = false;
|
||||
|
||||
// [...firstGroup, ...deletedEntries, ...secondGroupEntries] [...restInModel]
|
||||
// [...firstGroup, ...newlyInserted, ...secondGroupEntries, restInModel]
|
||||
if (firstGroupEntries.length + newlyInserted.length + secondGroupEntries.length > this.options.limit) {
|
||||
|
@ -744,10 +592,7 @@ export class CellOutputContainer extends CellPart {
|
|||
|
||||
// render newly inserted outputs
|
||||
for (let i = firstGroupEntries.length; i < this._outputEntries.length; i++) {
|
||||
const renderResult = this._outputEntries[i].element.render(undefined, i >= 1 && !this._outputEntries[i - 1].element.innerContainer);
|
||||
if (renderResult) {
|
||||
outputHasDynamicHeight = outputHasDynamicHeight || !renderResult.initRenderIsSynchronous;
|
||||
}
|
||||
this._outputEntries[i].element.render(undefined);
|
||||
}
|
||||
} else {
|
||||
// part of secondGroupEntries are pushed out of view
|
||||
|
@ -761,18 +606,6 @@ export class CellOutputContainer extends CellPart {
|
|||
// exclusive
|
||||
let reRenderRightBoundary = firstGroupEntries.length + newlyInserted.length;
|
||||
|
||||
for (let j = 0; j < secondGroupEntries.length; j++) {
|
||||
const entry = secondGroupEntries[j];
|
||||
if (!entry.element.useDedicatedDOM) {
|
||||
entry.element.detach();
|
||||
entry.element.dispose();
|
||||
secondGroupEntries[j] = new OutputEntryViewHandler(entry.model, this.instantiationService.createInstance(CellOutputElement, this.notebookEditor, this.viewCell, this, this.templateData.outputContainer, entry.model));
|
||||
reRenderRightBoundary++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const newlyInsertedEntries = newlyInserted.map(insert => {
|
||||
return new OutputEntryViewHandler(insert, this.instantiationService.createInstance(CellOutputElement, this.notebookEditor, this.viewCell, this, this.templateData.outputContainer, insert));
|
||||
});
|
||||
|
@ -781,10 +614,7 @@ export class CellOutputContainer extends CellPart {
|
|||
|
||||
for (let i = firstGroupEntries.length; i < reRenderRightBoundary; i++) {
|
||||
const previousSibling = i - 1 >= 0 && this._outputEntries[i - 1] && !!(this._outputEntries[i - 1].element.innerContainer?.parentElement) ? this._outputEntries[i - 1].element.innerContainer : undefined;
|
||||
const renderResult = this._outputEntries[i].element.render(previousSibling, i >= 1 && !this._outputEntries[i - 1].element.innerContainer);
|
||||
if (renderResult) {
|
||||
outputHasDynamicHeight = outputHasDynamicHeight || !renderResult.initRenderIsSynchronous;
|
||||
}
|
||||
this._outputEntries[i].element.render(previousSibling);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -796,18 +626,6 @@ export class CellOutputContainer extends CellPart {
|
|||
|
||||
let reRenderRightBoundary = firstGroupEntries.length + newlyInserted.length;
|
||||
|
||||
for (let j = 0; j < secondGroupEntries.length; j++) {
|
||||
const entry = secondGroupEntries[j];
|
||||
if (!entry.element.useDedicatedDOM) {
|
||||
entry.element.detach();
|
||||
entry.element.dispose();
|
||||
secondGroupEntries[j] = new OutputEntryViewHandler(entry.model, this.instantiationService.createInstance(CellOutputElement, this.notebookEditor, this.viewCell, this, this.templateData.outputContainer, entry.model));
|
||||
reRenderRightBoundary++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const newlyInsertedEntries = newlyInserted.map(insert => {
|
||||
return new OutputEntryViewHandler(insert, this.instantiationService.createInstance(CellOutputElement, this.notebookEditor, this.viewCell, this, this.templateData.outputContainer, insert));
|
||||
});
|
||||
|
@ -823,30 +641,14 @@ export class CellOutputContainer extends CellPart {
|
|||
|
||||
this._outputEntries = [...firstGroupEntries, ...newlyInsertedEntries, ...secondGroupEntries, ...outputsNewlyAvailable];
|
||||
|
||||
// if (firstGroupEntries.length + newlyInserted.length === this._outputEntries.length) {
|
||||
// // inserted at the very end
|
||||
// for (let i = firstGroupEntries.length; i < this._outputEntries.length; i++) {
|
||||
// const renderResult = this._outputEntries[i].entry.render();
|
||||
// if (renderResult) {
|
||||
// outputHasDynamicHeight = outputHasDynamicHeight || !renderResult.initRenderIsSynchronous;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
for (let i = firstGroupEntries.length; i < reRenderRightBoundary; i++) {
|
||||
const previousSibling = i - 1 >= 0 && this._outputEntries[i - 1] && !!(this._outputEntries[i - 1].element.innerContainer?.parentElement) ? this._outputEntries[i - 1].element.innerContainer : undefined;
|
||||
const renderResult = this._outputEntries[i].element.render(previousSibling, i >= 1 && !this._outputEntries[i - 1].element.innerContainer);
|
||||
if (renderResult) {
|
||||
outputHasDynamicHeight = outputHasDynamicHeight || !renderResult.initRenderIsSynchronous;
|
||||
}
|
||||
this._outputEntries[i].element.render(previousSibling);
|
||||
}
|
||||
|
||||
for (let i = 0; i < outputsNewlyAvailable.length; i++) {
|
||||
const renderResult = this._outputEntries[firstGroupEntries.length + newlyInserted.length + secondGroupEntries.length + i].element.render(undefined);
|
||||
if (renderResult) {
|
||||
outputHasDynamicHeight = outputHasDynamicHeight || !renderResult.initRenderIsSynchronous;
|
||||
}
|
||||
this._outputEntries[firstGroupEntries.length + newlyInserted.length + secondGroupEntries.length + i].element.render(undefined);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
if (this.viewCell.outputsViewModels.length > this.options.limit) {
|
||||
|
@ -865,7 +667,7 @@ export class CellOutputContainer extends CellPart {
|
|||
this._relayoutCell();
|
||||
// if it's clearing all outputs, or outputs are all rendered synchronously
|
||||
// shrink immediately as the final output height will be zero.
|
||||
this._validateFinalOutputHeight(!outputHasDynamicHeight || this.viewCell.outputsViewModels.length === 0);
|
||||
this._validateFinalOutputHeight(false || this.viewCell.outputsViewModels.length === 0);
|
||||
}
|
||||
|
||||
private _generateShowMoreElement(disposables: DisposableStore): HTMLElement {
|
||||
|
@ -879,7 +681,7 @@ export class CellOutputContainer extends CellPart {
|
|||
actionHandler: {
|
||||
callback: (content) => {
|
||||
if (content === 'command:workbench.action.openLargeOutput') {
|
||||
this.openerService.open(CellUri.generateCellOutputUri(this.notebookEditor.textModel!.uri, this.viewCell.handle));
|
||||
this.openerService.open(CellUri.generateCellOutputUri(this.notebookEditor.textModel!.uri));
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -14,7 +14,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICellOutputViewModel, ICellViewModel, IGenericCellViewModel, INotebookCellOutputLayoutInfo, INotebookEditorCreationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellExecutionPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution';
|
||||
import { CellFocusIndicator } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellFocusIndicator';
|
||||
import { CellProgressBar } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellProgressBar';
|
||||
|
@ -129,15 +129,3 @@ export interface CodeCellRenderTemplate extends BaseCellRenderTemplate {
|
|||
progressBar: CellProgressBar;
|
||||
cellExecution: CellExecutionPart;
|
||||
}
|
||||
|
||||
export function isCodeCellRenderTemplate(templateData: BaseCellRenderTemplate): templateData is CodeCellRenderTemplate {
|
||||
return !!(templateData as CodeCellRenderTemplate).runToolbar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notebook Editor Delegate for output rendering
|
||||
*/
|
||||
export interface INotebookDelegateForOutput {
|
||||
readonly creationOptions: INotebookEditorCreationOptions;
|
||||
getCellOutputLayoutInfo(cell: IGenericCellViewModel): INotebookCellOutputLayoutInfo;
|
||||
}
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICellOutputViewModel, IOutputRenderer, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookDelegateForOutput } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry';
|
||||
|
||||
export class OutputRenderer implements IOutputRenderer {
|
||||
|
||||
private readonly _richMimeTypeRenderers = new Map<string, IOutputTransformContribution>();
|
||||
|
||||
constructor(
|
||||
private readonly notebookEditor: INotebookDelegateForOutput,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ICommandService private readonly commandservice: ICommandService,
|
||||
) {
|
||||
}
|
||||
dispose(): void {
|
||||
dispose(this._richMimeTypeRenderers.values());
|
||||
this._richMimeTypeRenderers.clear();
|
||||
}
|
||||
|
||||
getContribution(preferredMimeType: string): IOutputTransformContribution | undefined {
|
||||
this._initialize();
|
||||
return this._richMimeTypeRenderers.get(preferredMimeType);
|
||||
}
|
||||
|
||||
private _initialize() {
|
||||
if (this._richMimeTypeRenderers.size) {
|
||||
return;
|
||||
}
|
||||
for (const desc of OutputRendererRegistry.getOutputTransformContributions()) {
|
||||
try {
|
||||
const contribution = this.instantiationService.createInstance(desc.ctor, this.notebookEditor);
|
||||
contribution.getMimetypes().forEach(mimetype => { this._richMimeTypeRenderers.set(mimetype, contribution); });
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _renderMessage(container: HTMLElement, message: string): IRenderOutput {
|
||||
const contentNode = document.createElement('p');
|
||||
contentNode.innerText = message;
|
||||
container.appendChild(contentNode);
|
||||
return { type: RenderOutputType.Mainframe };
|
||||
}
|
||||
|
||||
private _renderSearchForMimetype(container: HTMLElement, mimeType: string): IRenderOutput {
|
||||
const disposable = new DisposableStore();
|
||||
|
||||
const contentNode = document.createElement('p');
|
||||
contentNode.innerText = localize('noRenderer.1', "No renderer could be found for mimetype \"{0}\", but one might be available on the Marketplace.", mimeType);
|
||||
|
||||
const button = new Button(container);
|
||||
button.label = localize('noRenderer.search', 'Search Marketplace');
|
||||
button.element.style.maxWidth = `200px`;
|
||||
disposable.add(button.onDidClick(() => this.commandservice.executeCommand('workbench.extensions.search', `@tag:notebookRenderer ${mimeType}`)));
|
||||
disposable.add(button);
|
||||
|
||||
container.appendChild(contentNode);
|
||||
container.appendChild(button.element);
|
||||
|
||||
return {
|
||||
type: RenderOutputType.Mainframe,
|
||||
disposable,
|
||||
};
|
||||
}
|
||||
|
||||
render(viewModel: ICellOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput {
|
||||
this._initialize();
|
||||
if (!viewModel.model.outputs.length) {
|
||||
return this._renderMessage(container, localize('empty', "Cell has no output"));
|
||||
}
|
||||
if (!preferredMimeType) {
|
||||
const mimeTypes = viewModel.model.outputs.map(op => op.mime);
|
||||
const mimeTypesMessage = mimeTypes.join(', ');
|
||||
return this._renderMessage(container, localize('noRenderer.2', "No renderer could be found for output. It has the following mimetypes: {0}", mimeTypesMessage));
|
||||
}
|
||||
if (!preferredMimeType || !this._richMimeTypeRenderers.has(preferredMimeType)) {
|
||||
if (preferredMimeType) {
|
||||
return this._renderSearchForMimetype(container, preferredMimeType);
|
||||
}
|
||||
}
|
||||
const renderer = this._richMimeTypeRenderers.get(preferredMimeType);
|
||||
if (!renderer) {
|
||||
return this._renderSearchForMimetype(container, preferredMimeType);
|
||||
}
|
||||
const first = viewModel.model.outputs.find(op => op.mime === preferredMimeType);
|
||||
if (!first) {
|
||||
return this._renderMessage(container, localize('empty', "Cell has no output"));
|
||||
}
|
||||
|
||||
return renderer.render(viewModel, first, container, notebookUri);
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { BrandedService, IConstructorSignature } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IOutputTransformContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookDelegateForOutput } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
|
||||
export type IOutputTransformCtor = IConstructorSignature<IOutputTransformContribution, [INotebookDelegateForOutput]>;
|
||||
|
||||
export interface IOutputTransformDescription {
|
||||
ctor: IOutputTransformCtor;
|
||||
}
|
||||
|
||||
export const OutputRendererRegistry = new class NotebookRegistryImpl {
|
||||
|
||||
readonly #outputTransforms: IOutputTransformDescription[] = [];
|
||||
|
||||
registerOutputTransform<Services extends BrandedService[]>(ctor: { new(editor: INotebookDelegateForOutput, ...services: Services): IOutputTransformContribution }): void {
|
||||
this.#outputTransforms.push({ ctor: ctor as IOutputTransformCtor });
|
||||
}
|
||||
|
||||
getOutputTransformContributions(): IOutputTransformDescription[] {
|
||||
return this.#outputTransforms.slice(0);
|
||||
}
|
||||
};
|
|
@ -1,112 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Mimes } from 'vs/base/common/mime';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
import { ICellOutputViewModel, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookDelegateForOutput } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry';
|
||||
import { truncatedArrayOfString } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/textHelper';
|
||||
import { IOutputItemDto, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
|
||||
|
||||
class StreamRendererContrib extends Disposable implements IOutputTransformContribution {
|
||||
getType() {
|
||||
return RenderOutputType.Mainframe;
|
||||
}
|
||||
|
||||
getMimetypes() {
|
||||
return ['application/vnd.code.notebook.stdout', 'application/x.notebook.stdout', 'application/x.notebook.stream'];
|
||||
}
|
||||
|
||||
constructor(
|
||||
public notebookEditor: INotebookDelegateForOutput,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
|
||||
const disposables = new DisposableStore();
|
||||
const linkDetector = this.instantiationService.createInstance(LinkDetector);
|
||||
|
||||
const text = getStringValue(item);
|
||||
const contentNode = DOM.$('span.output-stream');
|
||||
const lineLimit = this.configurationService.getValue<number>(NotebookSetting.textOutputLineLimit) ?? 30;
|
||||
truncatedArrayOfString(notebookUri, output.cellViewModel, output.model.outputId, Math.max(lineLimit, 6), contentNode, [text], disposables, linkDetector, this.openerService, this.themeService);
|
||||
container.appendChild(contentNode);
|
||||
|
||||
return { type: RenderOutputType.Mainframe, disposable: disposables };
|
||||
}
|
||||
}
|
||||
|
||||
class StderrRendererContrib extends StreamRendererContrib {
|
||||
override getType() {
|
||||
return RenderOutputType.Mainframe;
|
||||
}
|
||||
|
||||
override getMimetypes() {
|
||||
return ['application/vnd.code.notebook.stderr', 'application/x.notebook.stderr'];
|
||||
}
|
||||
|
||||
override render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
|
||||
const result = super.render(output, item, container, notebookUri);
|
||||
container.classList.add('error');
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class PlainTextRendererContrib extends Disposable implements IOutputTransformContribution {
|
||||
getType() {
|
||||
return RenderOutputType.Mainframe;
|
||||
}
|
||||
|
||||
getMimetypes() {
|
||||
return [Mimes.text];
|
||||
}
|
||||
|
||||
constructor(
|
||||
public notebookEditor: INotebookDelegateForOutput,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement, notebookUri: URI): IRenderOutput {
|
||||
const disposables = new DisposableStore();
|
||||
const linkDetector = this.instantiationService.createInstance(LinkDetector);
|
||||
|
||||
const str = getStringValue(item);
|
||||
const contentNode = DOM.$('.output-plaintext');
|
||||
const lineLimit = this.configurationService.getValue<number>(NotebookSetting.textOutputLineLimit) ?? 30;
|
||||
truncatedArrayOfString(notebookUri, output.cellViewModel, output.model.outputId, Math.max(lineLimit, 6), contentNode, [str], disposables, linkDetector, this.openerService, this.themeService);
|
||||
container.appendChild(contentNode);
|
||||
|
||||
return { type: RenderOutputType.Mainframe, supportAppend: true, disposable: disposables };
|
||||
}
|
||||
}
|
||||
|
||||
OutputRendererRegistry.registerOutputTransform(PlainTextRendererContrib);
|
||||
OutputRendererRegistry.registerOutputTransform(StreamRendererContrib);
|
||||
OutputRendererRegistry.registerOutputTransform(StderrRendererContrib);
|
||||
|
||||
|
||||
// --- utils ---
|
||||
export function getStringValue(item: IOutputItemDto): string {
|
||||
return item.data.toString();
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { renderMarkdown } from 'vs/base/browser/markdownRenderer';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { DefaultEndOfLine, EndOfLinePreference, ITextBuffer } from 'vs/editor/common/model';
|
||||
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
import { IGenericCellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
|
||||
const SIZE_LIMIT = 65535;
|
||||
|
||||
function generateViewMoreElement(notebookUri: URI, cellViewModel: IGenericCellViewModel, outputId: string, disposables: DisposableStore, openerService: IOpenerService): HTMLElement {
|
||||
const md: IMarkdownString = {
|
||||
value: `Output exceeds the [size limit](command:workbench.action.openSettings?["notebook.output.textLineLimit"]). Open the full output data[ in a text editor](command:workbench.action.openLargeOutput?${outputId})`,
|
||||
isTrusted: true,
|
||||
supportThemeIcons: true
|
||||
};
|
||||
|
||||
const rendered = disposables.add(renderMarkdown(md, {
|
||||
actionHandler: {
|
||||
callback: (content) => {
|
||||
const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(content);
|
||||
if (ret && ret.length === 2) {
|
||||
const outputId = ret[1];
|
||||
openerService.open(CellUri.generateCellOutputUri(notebookUri, cellViewModel.handle, outputId));
|
||||
}
|
||||
|
||||
if (content.startsWith('command:workbench.action.openSettings')) {
|
||||
openerService.open(content, { allowCommands: true });
|
||||
}
|
||||
|
||||
return;
|
||||
},
|
||||
disposables: disposables
|
||||
}
|
||||
}));
|
||||
|
||||
rendered.element.classList.add('output-show-more');
|
||||
return rendered.element;
|
||||
}
|
||||
|
||||
export function truncatedArrayOfString(notebookUri: URI, cellViewModel: IGenericCellViewModel, outputId: string, linesLimit: number, container: HTMLElement, outputs: string[], disposables: DisposableStore, linkDetector: LinkDetector, openerService: IOpenerService, themeService: IThemeService) {
|
||||
const fullLen = outputs.reduce((p, c) => {
|
||||
return p + c.length;
|
||||
}, 0);
|
||||
|
||||
let buffer: ITextBuffer | undefined = undefined;
|
||||
|
||||
if (fullLen > SIZE_LIMIT) {
|
||||
// it's too large and we should find min(maxSizeLimit, maxLineLimit)
|
||||
const bufferBuilder = new PieceTreeTextBufferBuilder();
|
||||
outputs.forEach(output => bufferBuilder.acceptChunk(output));
|
||||
const factory = bufferBuilder.finish();
|
||||
buffer = factory.create(DefaultEndOfLine.LF).textBuffer;
|
||||
const sizeBufferLimitPosition = buffer.getPositionAt(SIZE_LIMIT);
|
||||
if (sizeBufferLimitPosition.lineNumber < linesLimit) {
|
||||
const truncatedText = buffer.getValueInRange(new Range(1, 1, sizeBufferLimitPosition.lineNumber, sizeBufferLimitPosition.column), EndOfLinePreference.TextDefined);
|
||||
container.appendChild(handleANSIOutput(truncatedText, linkDetector, themeService, undefined));
|
||||
// view more ...
|
||||
container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, outputId, disposables, openerService));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
const bufferBuilder = new PieceTreeTextBufferBuilder();
|
||||
outputs.forEach(output => bufferBuilder.acceptChunk(output));
|
||||
const factory = bufferBuilder.finish();
|
||||
buffer = factory.create(DefaultEndOfLine.LF).textBuffer;
|
||||
}
|
||||
|
||||
if (buffer.getLineCount() < linesLimit) {
|
||||
const lineCount = buffer.getLineCount();
|
||||
const fullRange = new Range(1, 1, lineCount, Math.max(1, buffer.getLineLastNonWhitespaceColumn(lineCount)));
|
||||
container.appendChild(handleANSIOutput(buffer.getValueInRange(fullRange, EndOfLinePreference.TextDefined), linkDetector, themeService, undefined));
|
||||
return;
|
||||
}
|
||||
|
||||
container.appendChild(generateViewMoreElement(notebookUri, cellViewModel, outputId, disposables, openerService));
|
||||
|
||||
const div = DOM.$('div');
|
||||
container.appendChild(div);
|
||||
div.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(1, 1, linesLimit - 5, buffer.getLineLastNonWhitespaceColumn(linesLimit - 5)), EndOfLinePreference.TextDefined), linkDetector, themeService, undefined));
|
||||
|
||||
// view more ...
|
||||
DOM.append(container, DOM.$('span' + Codicon.toolBarMore.cssSelector));
|
||||
|
||||
const lineCount = buffer.getLineCount();
|
||||
const div2 = DOM.$('div');
|
||||
container.appendChild(div2);
|
||||
div2.appendChild(handleANSIOutput(buffer.getValueInRange(new Range(lineCount - 5, 1, lineCount, buffer.getLineLastNonWhitespaceColumn(lineCount)), EndOfLinePreference.TextDefined), linkDetector, themeService, undefined));
|
||||
}
|
|
@ -35,11 +35,12 @@ import { CellEditState, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, I
|
|||
import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads';
|
||||
import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping';
|
||||
import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel';
|
||||
import { INotebookRendererInfo, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellUri, INotebookRendererInfo, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IWebviewElement, IWebviewService, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, IContentWidgetTopRequest, IControllerPreload, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, ToWebviewMessage } from './webviewMessages';
|
||||
|
||||
|
@ -126,6 +127,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ILanguageService private readonly languageService: ILanguageService,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -216,6 +218,7 @@ export class BackLayerWebView<T extends ICommonCellInfo> extends Disposable {
|
|||
{ dragAndDropEnabled: this.options.dragAndDropEnabled },
|
||||
renderersData,
|
||||
this.workspaceTrustManagementService.isWorkspaceTrusted(),
|
||||
this.configurationService.getValue<number>(NotebookSetting.textOutputLineLimit) ?? 30,
|
||||
this.nonce);
|
||||
|
||||
const enableCsp = this.configurationService.getValue('notebook.experimental.enableCsp');
|
||||
|
@ -527,6 +530,12 @@ var requirejs = (function() {
|
|||
}
|
||||
|
||||
if (matchesScheme(link, Schemas.command)) {
|
||||
const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(link);
|
||||
if (ret && ret.length === 2) {
|
||||
const outputId = ret[1];
|
||||
this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId));
|
||||
return;
|
||||
}
|
||||
console.warn('Command links are deprecated and will be removed, use messag passing instead: https://github.com/microsoft/vscode/issues/123601');
|
||||
}
|
||||
|
||||
|
@ -653,6 +662,22 @@ var requirejs = (function() {
|
|||
case 'clicked-link':
|
||||
{
|
||||
let linkToOpen: URI | string | undefined;
|
||||
if (matchesScheme(data.href, Schemas.command)) {
|
||||
const ret = /command\:workbench\.action\.openLargeOutput\?(.*)/.exec(data.href);
|
||||
if (ret && ret.length === 2) {
|
||||
const outputId = ret[1];
|
||||
const group = this.editorGroupService.activeGroup;
|
||||
|
||||
if (group) {
|
||||
if (group.activeEditor) {
|
||||
group.pinEditor(group.activeEditor);
|
||||
}
|
||||
}
|
||||
|
||||
this.openerService.open(CellUri.generateCellOutputUri(this.documentUri, outputId));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (matchesSomeScheme(data.href, Schemas.http, Schemas.https, Schemas.mailto, Schemas.command, Schemas.vscodeNotebookCell, Schemas.vscodeNotebook)) {
|
||||
linkToOpen = data.href;
|
||||
} else if (!/^[\w\-]+:/.test(data.href)) {
|
||||
|
|
|
@ -64,6 +64,7 @@ interface PreloadContext {
|
|||
readonly options: PreloadOptions;
|
||||
readonly rendererData: readonly RendererMetadata[];
|
||||
readonly isWorkspaceTrusted: boolean;
|
||||
readonly lineLimit: number;
|
||||
}
|
||||
|
||||
declare function __import(path: string): Promise<any>;
|
||||
|
@ -74,6 +75,7 @@ async function webviewPreloads(ctx: PreloadContext) {
|
|||
|
||||
let currentOptions = ctx.options;
|
||||
let isWorkspaceTrusted = ctx.isWorkspaceTrusted;
|
||||
let lineLimit = ctx.lineLimit;
|
||||
|
||||
const acquireVsCodeApi = globalThis.acquireVsCodeApi;
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
@ -202,6 +204,7 @@ async function webviewPreloads(ctx: PreloadContext) {
|
|||
postMessage?(message: unknown): void;
|
||||
onDidReceiveMessage?: Event<unknown>;
|
||||
readonly workspace: { readonly isTrusted: boolean };
|
||||
readonly settings: { readonly lineLimit: number };
|
||||
}
|
||||
|
||||
interface RendererModule {
|
||||
|
@ -1208,6 +1211,9 @@ async function webviewPreloads(ctx: PreloadContext) {
|
|||
getRenderer: async (id: string) => renderers.getRenderer(id)?.api,
|
||||
workspace: {
|
||||
get isTrusted() { return isWorkspaceTrusted; }
|
||||
},
|
||||
settings: {
|
||||
get lineLimit() { return lineLimit; },
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2103,12 +2109,13 @@ export interface RendererMetadata {
|
|||
readonly isBuiltin: boolean;
|
||||
}
|
||||
|
||||
export function preloadsScriptStr(styleValues: PreloadStyles, options: PreloadOptions, renderers: readonly RendererMetadata[], isWorkspaceTrusted: boolean, nonce: string) {
|
||||
export function preloadsScriptStr(styleValues: PreloadStyles, options: PreloadOptions, renderers: readonly RendererMetadata[], isWorkspaceTrusted: boolean, lineLimit: number, nonce: string) {
|
||||
const ctx: PreloadContext = {
|
||||
style: styleValues,
|
||||
options,
|
||||
rendererData: renderers,
|
||||
isWorkspaceTrusted,
|
||||
lineLimit,
|
||||
nonce,
|
||||
};
|
||||
// TS will try compiling `import()` in webviewPreloads, so use a helper function instead
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ICellOutputViewModel, IGenericCellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { ICellOutput, IOrderedMimeType, mimeTypeIsMergeable, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellOutput, IOrderedMimeType, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
|
||||
let handle = 0;
|
||||
|
@ -42,11 +42,6 @@ export class CellOutputViewModel extends Disposable implements ICellOutputViewMo
|
|||
return this._outputRawData.outputs.some(output => output.mime !== firstMimeType);
|
||||
}
|
||||
|
||||
supportAppend() {
|
||||
// if there is any mime type that's not mergeable then the whole output is not mergeable.
|
||||
return this._outputRawData.outputs.every(op => mimeTypeIsMergeable(op.mime));
|
||||
}
|
||||
|
||||
resolveMimeTypes(textModel: NotebookTextModel, kernelProvides: readonly string[] | undefined): [readonly IOrderedMimeType[], number] {
|
||||
const mimeTypes = this._notebookService.getOutputMimeTypeInfo(textModel, kernelProvides, this.model);
|
||||
let index = -1;
|
||||
|
|
|
@ -72,7 +72,6 @@ export const RENDERER_EQUIVALENT_EXTENSIONS: ReadonlyMap<string, ReadonlySet<str
|
|||
['ms-toolsai.jupyter-renderers', new Set(['jupyter-notebook', 'interactive'])],
|
||||
]);
|
||||
|
||||
export const BUILTIN_RENDERER_ID = '_builtin';
|
||||
export const RENDERER_NOT_AVAILABLE = '_notAvailable';
|
||||
|
||||
export type NotebookRendererEntrypoint = string | { extends: string; path: string };
|
||||
|
@ -535,27 +534,26 @@ export namespace CellUri {
|
|||
};
|
||||
}
|
||||
|
||||
export function generateCellOutputUri(notebook: URI, handle: number, outputId?: string) {
|
||||
export function generateCellOutputUri(notebook: URI, outputId?: string) {
|
||||
return notebook.with({
|
||||
scheme: Schemas.vscodeNotebookCellOutput,
|
||||
fragment: `ch${handle.toString().padStart(7, '0')},${outputId ?? ''},${notebook.scheme !== Schemas.file ? notebook.scheme : ''}`
|
||||
fragment: `op${outputId ?? ''},${notebook.scheme !== Schemas.file ? notebook.scheme : ''}`
|
||||
});
|
||||
}
|
||||
|
||||
export function parseCellOutputUri(uri: URI): { notebook: URI; handle: number; outputId?: string } | undefined {
|
||||
export function parseCellOutputUri(uri: URI): { notebook: URI; outputId?: string } | undefined {
|
||||
if (uri.scheme !== Schemas.vscodeNotebookCellOutput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = /^ch(\d{7,})\,([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?\,(.*)$/i.exec(uri.fragment);
|
||||
const match = /^op([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?\,(.*)$/i.exec(uri.fragment);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
const handle = Number(match[1]);
|
||||
const outputId = (match[2] && match[2] !== '') ? match[2] : undefined;
|
||||
const scheme = match[3];
|
||||
|
||||
const outputId = (match[1] && match[1] !== '') ? match[1] : undefined;
|
||||
const scheme = match[2];
|
||||
return {
|
||||
handle,
|
||||
outputId,
|
||||
notebook: uri.with({
|
||||
scheme: scheme || Schemas.file,
|
||||
|
@ -590,33 +588,6 @@ export namespace CellUri {
|
|||
}
|
||||
}
|
||||
|
||||
type MimeTypeInfo = {
|
||||
alwaysSecure?: boolean;
|
||||
supportedByCore?: boolean;
|
||||
mergeable?: boolean;
|
||||
};
|
||||
|
||||
const _mimeTypeInfo = new Map<string, MimeTypeInfo>([
|
||||
['image/git', { alwaysSecure: true, supportedByCore: true }],
|
||||
['application/json', { alwaysSecure: true, supportedByCore: true }],
|
||||
[Mimes.text, { alwaysSecure: true, supportedByCore: true }],
|
||||
['text/x-javascript', { alwaysSecure: true, supportedByCore: true }], // secure because rendered as text, not executed
|
||||
['application/vnd.code.notebook.stdout', { alwaysSecure: true, supportedByCore: true, mergeable: true }],
|
||||
['application/vnd.code.notebook.stderr', { alwaysSecure: true, supportedByCore: true, mergeable: true }],
|
||||
]);
|
||||
|
||||
export function mimeTypeIsAlwaysSecure(mimeType: string): boolean {
|
||||
return _mimeTypeInfo.get(mimeType)?.alwaysSecure ?? false;
|
||||
}
|
||||
|
||||
export function mimeTypeSupportedByCore(mimeType: string) {
|
||||
return _mimeTypeInfo.get(mimeType)?.supportedByCore ?? false;
|
||||
}
|
||||
|
||||
export function mimeTypeIsMergeable(mimeType: string): boolean {
|
||||
return _mimeTypeInfo.get(mimeType)?.mergeable ?? false;
|
||||
}
|
||||
|
||||
const normalizeSlashes = (str: string) => isWindows ? str.replace(/\//g, '\\') : str;
|
||||
|
||||
interface IMimeTypeWithMatcher {
|
||||
|
|
|
@ -144,7 +144,7 @@ export class NotebookOptions extends Disposable {
|
|||
cellBottomMargin: 6,
|
||||
cellRightMargin: 16,
|
||||
cellStatusBarHeight: 22,
|
||||
cellOutputPadding: 12,
|
||||
cellOutputPadding: 8,
|
||||
markdownPreviewPadding: 8,
|
||||
// bottomToolbarHeight: bottomToolbarHeight,
|
||||
// bottomToolbarGap: bottomToolbarGap,
|
||||
|
|
|
@ -1,244 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { FastDomNode } from 'vs/base/browser/fastDomNode';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { ICellOutputViewModel, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput';
|
||||
import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon';
|
||||
import { OutputRendererRegistry } from 'vs/workbench/contrib/notebook/browser/view/output/rendererRegistry';
|
||||
import { getStringValue } from 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { BUILTIN_RENDERER_ID, CellEditType, CellKind, IOutputDto, IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { setupInstantiationService, valueBytesFromString, withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor';
|
||||
|
||||
OutputRendererRegistry.registerOutputTransform(class implements IOutputTransformContribution {
|
||||
getType() { return RenderOutputType.Mainframe; }
|
||||
|
||||
getMimetypes() {
|
||||
return ['application/vnd.code.notebook.stdout', 'application/x.notebook.stdout', 'application/x.notebook.stream'];
|
||||
}
|
||||
|
||||
constructor() { }
|
||||
|
||||
render(output: ICellOutputViewModel, item: IOutputItemDto, container: HTMLElement): IRenderOutput {
|
||||
const text = getStringValue(item);
|
||||
const contentNode = DOM.$('span.output-stream');
|
||||
contentNode.textContent = text;
|
||||
container.appendChild(contentNode);
|
||||
return { type: RenderOutputType.Mainframe };
|
||||
}
|
||||
|
||||
dispose() { }
|
||||
});
|
||||
|
||||
suite('NotebookViewModel Outputs', async () => {
|
||||
|
||||
let disposables: DisposableStore;
|
||||
let instantiationService: TestInstantiationService;
|
||||
let openerService: IOpenerService;
|
||||
|
||||
suiteSetup(() => {
|
||||
disposables = new DisposableStore();
|
||||
instantiationService = setupInstantiationService(disposables);
|
||||
instantiationService.stub(INotebookService, new class extends mock<INotebookService>() {
|
||||
override getOutputMimeTypeInfo(textModel: NotebookTextModel, kernelProvides: [], output: IOutputDto) {
|
||||
if (output.outputId === 'output_id_err') {
|
||||
return [{
|
||||
mimeType: 'application/vnd.code.notebook.stderr',
|
||||
rendererId: BUILTIN_RENDERER_ID,
|
||||
isTrusted: true
|
||||
}];
|
||||
}
|
||||
return [{
|
||||
mimeType: 'application/vnd.code.notebook.stdout',
|
||||
rendererId: BUILTIN_RENDERER_ID,
|
||||
isTrusted: true
|
||||
}];
|
||||
}
|
||||
});
|
||||
|
||||
instantiationService.stub(IMenuService, new class extends mock<IMenuService>() {
|
||||
override createMenu(arg: any, context: any): any {
|
||||
return {
|
||||
onDidChange: () => { },
|
||||
getActions: (arg: any) => {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
instantiationService.stub(IKeybindingService, new class extends mock<IKeybindingService>() {
|
||||
override lookupKeybinding(arg: any): any {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
openerService = instantiationService.stub(IOpenerService, {});
|
||||
});
|
||||
|
||||
suiteTeardown(() => disposables.dispose());
|
||||
|
||||
test('stream outputs reuse output container', async () => {
|
||||
await withTestNotebook(
|
||||
[
|
||||
['var a = 1;', 'javascript', CellKind.Code, [
|
||||
{ outputId: 'output_id_1', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('1') }] },
|
||||
{ outputId: 'output_id_2', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('2') }] },
|
||||
{ outputId: 'output_id_err', outputs: [{ mime: 'application/vnd.code.notebook.stderr', data: valueBytesFromString('1000') }] },
|
||||
{ outputId: 'output_id_3', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('3') }] },
|
||||
], {}]
|
||||
],
|
||||
(editor, viewModel, accessor) => {
|
||||
const container = new CellOutputContainer(editor, viewModel.viewCells[0] as CodeCellViewModel, {
|
||||
outputContainer: new FastDomNode(document.createElement('div')),
|
||||
outputShowMoreContainer: new FastDomNode(document.createElement('div')),
|
||||
editor: {
|
||||
getContentHeight: () => {
|
||||
return 100;
|
||||
}
|
||||
},
|
||||
templateDisposables: new DisposableStore(),
|
||||
} as unknown as CodeCellRenderTemplate, { limit: 5 }, openerService, instantiationService);
|
||||
container.render(100);
|
||||
assert.strictEqual(container.renderedOutputEntries.length, 4);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[1].element.useDedicatedDOM, false);
|
||||
assert.strictEqual(container.renderedOutputEntries[2].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[3].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.innerContainer, container.renderedOutputEntries[1].element.innerContainer);
|
||||
assert.notStrictEqual(container.renderedOutputEntries[1].element.innerContainer, container.renderedOutputEntries[2].element.innerContainer);
|
||||
assert.notStrictEqual(container.renderedOutputEntries[2].element.innerContainer, container.renderedOutputEntries[3].element.innerContainer);
|
||||
|
||||
editor.textModel.applyEdits([{
|
||||
index: 0,
|
||||
editType: CellEditType.Output,
|
||||
outputs: [
|
||||
{
|
||||
outputId: 'output_id_4',
|
||||
outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('4') }]
|
||||
},
|
||||
{
|
||||
outputId: 'output_id_5',
|
||||
outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('5') }]
|
||||
}
|
||||
],
|
||||
append: true
|
||||
}], true, undefined, () => undefined, undefined);
|
||||
assert.strictEqual(container.renderedOutputEntries.length, 5);
|
||||
// last one is merged with previous one
|
||||
assert.strictEqual(container.renderedOutputEntries[3].element.innerContainer, container.renderedOutputEntries[4].element.innerContainer);
|
||||
|
||||
editor.textModel.applyEdits([{
|
||||
index: 0,
|
||||
editType: CellEditType.Output,
|
||||
outputs: [
|
||||
{ outputId: 'output_id_1', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('1') }] },
|
||||
{ outputId: 'output_id_2', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('2') }] },
|
||||
{ outputId: 'output_id_err', outputs: [{ mime: 'application/vnd.code.notebook.stderr', data: valueBytesFromString('1000') }] },
|
||||
{
|
||||
outputId: 'output_id_5',
|
||||
outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('5') }]
|
||||
}
|
||||
],
|
||||
}], true, undefined, () => undefined, undefined);
|
||||
assert.strictEqual(container.renderedOutputEntries.length, 4);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].model.model.outputId, 'output_id_1');
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[1].model.model.outputId, 'output_id_2');
|
||||
assert.strictEqual(container.renderedOutputEntries[1].element.useDedicatedDOM, false);
|
||||
assert.strictEqual(container.renderedOutputEntries[2].model.model.outputId, 'output_id_err');
|
||||
assert.strictEqual(container.renderedOutputEntries[2].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[3].model.model.outputId, 'output_id_5');
|
||||
assert.strictEqual(container.renderedOutputEntries[3].element.useDedicatedDOM, true);
|
||||
},
|
||||
instantiationService
|
||||
);
|
||||
});
|
||||
|
||||
test('stream outputs reuse output container 2', async () => {
|
||||
await withTestNotebook(
|
||||
[
|
||||
['var a = 1;', 'javascript', CellKind.Code, [
|
||||
{ outputId: 'output_id_1', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('1') }] },
|
||||
{ outputId: 'output_id_2', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('2') }] },
|
||||
{ outputId: 'output_id_err', outputs: [{ mime: 'application/vnd.code.notebook.stderr', data: valueBytesFromString('1000') }] },
|
||||
{ outputId: 'output_id_4', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('4') }] },
|
||||
{ outputId: 'output_id_5', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('5') }] },
|
||||
{ outputId: 'output_id_6', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('6') }] },
|
||||
], {}]
|
||||
],
|
||||
(editor, viewModel, accessor) => {
|
||||
const container = new CellOutputContainer(editor, viewModel.viewCells[0] as CodeCellViewModel, {
|
||||
outputContainer: new FastDomNode(document.createElement('div')),
|
||||
outputShowMoreContainer: new FastDomNode(document.createElement('div')),
|
||||
editor: {
|
||||
getContentHeight: () => {
|
||||
return 100;
|
||||
}
|
||||
},
|
||||
templateDisposables: new DisposableStore(),
|
||||
} as unknown as CodeCellRenderTemplate, { limit: 5 }, openerService, instantiationService);
|
||||
container.render(100);
|
||||
assert.strictEqual(container.renderedOutputEntries.length, 5);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[1].element.useDedicatedDOM, false);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.innerContainer?.innerText, '12');
|
||||
|
||||
assert.strictEqual(container.renderedOutputEntries[2].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[2].element.innerContainer?.innerText, '1000');
|
||||
|
||||
assert.strictEqual(container.renderedOutputEntries[3].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[4].element.useDedicatedDOM, false);
|
||||
assert.strictEqual(container.renderedOutputEntries[3].element.innerContainer?.innerText, '45');
|
||||
|
||||
|
||||
editor.textModel.applyEdits([{
|
||||
index: 0,
|
||||
editType: CellEditType.Output,
|
||||
outputs: [
|
||||
{ outputId: 'output_id_1', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('1') }] },
|
||||
{ outputId: 'output_id_2', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('2') }] },
|
||||
{ outputId: 'output_id_7', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('7') }] },
|
||||
{ outputId: 'output_id_5', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('5') }] },
|
||||
{ outputId: 'output_id_6', outputs: [{ mime: 'application/vnd.code.notebook.stdout', data: valueBytesFromString('6') }] },
|
||||
|
||||
]
|
||||
}], true, undefined, () => undefined, undefined);
|
||||
assert.strictEqual(container.renderedOutputEntries.length, 5);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].model.model.outputId, 'output_id_1');
|
||||
assert.strictEqual(container.renderedOutputEntries[1].model.model.outputId, 'output_id_2');
|
||||
assert.strictEqual(container.renderedOutputEntries[2].model.model.outputId, 'output_id_7');
|
||||
assert.strictEqual(container.renderedOutputEntries[3].model.model.outputId, 'output_id_5');
|
||||
assert.strictEqual(container.renderedOutputEntries[4].model.model.outputId, 'output_id_6');
|
||||
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.useDedicatedDOM, true);
|
||||
assert.strictEqual(container.renderedOutputEntries[1].element.useDedicatedDOM, false);
|
||||
assert.strictEqual(container.renderedOutputEntries[2].element.useDedicatedDOM, false);
|
||||
assert.strictEqual(container.renderedOutputEntries[3].element.useDedicatedDOM, false);
|
||||
assert.strictEqual(container.renderedOutputEntries[4].element.useDedicatedDOM, false);
|
||||
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.innerContainer, container.renderedOutputEntries[1].element.innerContainer);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.innerContainer, container.renderedOutputEntries[2].element.innerContainer);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.innerContainer, container.renderedOutputEntries[3].element.innerContainer);
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.innerContainer, container.renderedOutputEntries[4].element.innerContainer);
|
||||
|
||||
assert.strictEqual(container.renderedOutputEntries[0].element.innerContainer?.innerText, '12756');
|
||||
},
|
||||
instantiationService
|
||||
);
|
||||
});
|
||||
|
||||
});
|
|
@ -12,8 +12,6 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
|
|||
import { Mimes } from 'vs/base/common/mime';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { mock } from 'vs/base/test/common/mock';
|
||||
import { EditorFontLigatures } from 'vs/editor/common/config/editorOptions';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { LanguageService } from 'vs/editor/common/services/languageService';
|
||||
|
@ -22,7 +20,6 @@ import { ModelService } from 'vs/editor/common/services/modelService';
|
|||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/testLanguageConfigurationService';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { NullCommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
|
||||
|
@ -42,7 +39,6 @@ import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
|||
import { CellFindMatchWithIndex, IActiveNotebookEditorDelegate, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { ListViewInfoAccessor } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget';
|
||||
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
|
||||
import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer';
|
||||
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
|
||||
import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl';
|
||||
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
|
||||
|
@ -253,34 +249,6 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic
|
|||
override focusElement() { }
|
||||
override setCellEditorSelection() { }
|
||||
override async revealRangeInCenterIfOutsideViewportAsync() { }
|
||||
override getOutputRenderer() {
|
||||
return new OutputRenderer({
|
||||
creationOptions: notebookEditor.creationOptions,
|
||||
getCellOutputLayoutInfo() {
|
||||
return {
|
||||
height: 100,
|
||||
width: 100,
|
||||
fontInfo: new FontInfo({
|
||||
pixelRatio: 1,
|
||||
fontFamily: 'mockFont',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 14,
|
||||
fontFeatureSettings: EditorFontLigatures.OFF,
|
||||
lineHeight: 19,
|
||||
letterSpacing: 1.5,
|
||||
isMonospace: true,
|
||||
typicalHalfwidthCharacterWidth: 10,
|
||||
typicalFullwidthCharacterWidth: 20,
|
||||
canUseHalfwidthRightwardsArrow: true,
|
||||
spaceWidth: 10,
|
||||
middotWidth: 10,
|
||||
wsmiddotWidth: 10,
|
||||
maxDigitWidth: 10,
|
||||
}, true)
|
||||
};
|
||||
}
|
||||
}, instantiationService, NullCommandService);
|
||||
}
|
||||
override async layoutNotebookCell() { }
|
||||
override async removeInset() { }
|
||||
override async focusNotebookCell() { }
|
||||
|
|
Loading…
Reference in a new issue