Path: blob/main/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputTextHelper.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js';6import { ILogService } from '../../../../../platform/log/common/log.js';7import { NotebookTextModel } from '../../common/model/notebookTextModel.js';8import { IOutputItemDto } from '../../common/notebookCommon.js';9import { isTextStreamMime } from '../../../../../base/common/mime.js';10import { ICellOutputViewModel, ICellViewModel } from '../notebookBrowser.js';1112interface Error {13name: string;14message: string;15stack?: string;16}1718export function getAllOutputsText(notebook: NotebookTextModel, viewCell: ICellViewModel, shortErrors: boolean = false): string {19const outputText: string[] = [];20for (let i = 0; i < viewCell.outputsViewModels.length; i++) {21const outputViewModel = viewCell.outputsViewModels[i];22const outputTextModel = viewCell.model.outputs[i];23const [mimeTypes, pick] = outputViewModel.resolveMimeTypes(notebook, undefined);24const mimeType = mimeTypes[pick].mimeType;25let buffer = outputTextModel.outputs.find(output => output.mime === mimeType);2627if (!buffer || mimeType.startsWith('image')) {28buffer = outputTextModel.outputs.find(output => !output.mime.startsWith('image'));29}3031if (!buffer) {32continue;33}3435let text = '';36if (isTextStreamMime(mimeType)) {37const { text: stream, count } = getOutputStreamText(outputViewModel);38text = stream;39if (count > 1) {40i += count - 1;41}42} else {43text = getOutputText(mimeType, buffer, shortErrors);44}4546outputText.push(text);47}4849let outputContent: string;50if (outputText.length > 1) {51outputContent = outputText.map((output, i) => {52return `Cell output ${i + 1} of ${outputText.length}\n${output}`;53}).join('\n');54} else {55outputContent = outputText[0] ?? '';56}5758return outputContent;59}6061export function getOutputStreamText(output: ICellOutputViewModel): { text: string; count: number } {62let text = '';63const cellViewModel = output.cellViewModel as ICellViewModel;64let index = cellViewModel.outputsViewModels.indexOf(output);65let count = 0;66while (index < cellViewModel.model.outputs.length) {67const nextCellOutput = cellViewModel.model.outputs[index];68const nextOutput = nextCellOutput.outputs.find(output => isTextStreamMime(output.mime));69if (!nextOutput) {70break;71}7273text = text + decoder.decode(nextOutput.data.buffer);74index = index + 1;75count++;76}7778return { text: text.trim(), count };79}8081const decoder = new TextDecoder();8283export function getOutputText(mimeType: string, buffer: IOutputItemDto, shortError: boolean = false): string {84let text = `${mimeType}`; // default in case we can't get the text value for some reason.8586const charLimit = 100000;87text = decoder.decode(buffer.data.slice(0, charLimit).buffer);8889if (buffer.data.byteLength > charLimit) {90text = text + '...(truncated)';91} else if (mimeType === 'application/vnd.code.notebook.error') {92text = text.replace(/\\u001b\[[0-9;]*m/gi, '');93try {94const error = JSON.parse(text) as Error;95if (!error.stack || shortError) {96text = `${error.name}: ${error.message}`;97} else {98text = error.stack;99}100} catch {101// just use raw text102}103}104105return text.trim();106}107108export async function copyCellOutput(mimeType: string | undefined, outputViewModel: ICellOutputViewModel, clipboardService: IClipboardService, logService: ILogService) {109const cellOutput = outputViewModel.model;110const output = mimeType && TEXT_BASED_MIMETYPES.includes(mimeType) ?111cellOutput.outputs.find(output => output.mime === mimeType) :112cellOutput.outputs.find(output => TEXT_BASED_MIMETYPES.includes(output.mime));113114mimeType = output?.mime;115116if (!mimeType || !output) {117return;118}119120const text = isTextStreamMime(mimeType) ? getOutputStreamText(outputViewModel).text : getOutputText(mimeType, output);121122try {123await clipboardService.writeText(text);124125} catch (e) {126logService.error(`Failed to copy content: ${e}`);127}128}129130export const TEXT_BASED_MIMETYPES = [131'text/latex',132'text/html',133'application/vnd.code.notebook.error',134'application/vnd.code.notebook.stdout',135'application/x.notebook.stdout',136'application/x.notebook.stream',137'application/vnd.code.notebook.stderr',138'application/x.notebook.stderr',139'text/plain',140'text/markdown',141'application/json'142];143144145