Path: blob/main/extensions/ipynb/src/notebookSerializer.ts
3292 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 type * as nbformat from '@jupyterlab/nbformat';6import detectIndent from 'detect-indent';7import * as vscode from 'vscode';8import { getPreferredLanguage, jupyterNotebookModelToNotebookData } from './deserializers';9import * as fnv from '@enonic/fnv-plus';10import { serializeNotebookToString } from './serializers';1112export abstract class NotebookSerializerBase extends vscode.Disposable implements vscode.NotebookSerializer {13protected disposed: boolean = false;14constructor(protected readonly context: vscode.ExtensionContext) {15super(() => { });16}1718override dispose() {19this.disposed = true;20super.dispose();21}2223public async deserializeNotebook(content: Uint8Array, _token: vscode.CancellationToken): Promise<vscode.NotebookData> {24let contents = '';25try {26contents = new TextDecoder().decode(content);27} catch {28}2930let json = contents && /\S/.test(contents) ? (JSON.parse(contents) as Partial<nbformat.INotebookContent>) : {};3132if (json.__webview_backup) {33const backupId = json.__webview_backup;34const uri = this.context.globalStorageUri;35const folder = uri.with({ path: this.context.globalStorageUri.path.replace('vscode.ipynb', 'ms-toolsai.jupyter') });36const fileHash = fnv.fast1a32hex(backupId) as string;37const fileName = `${fileHash}.ipynb`;38const file = vscode.Uri.joinPath(folder, fileName);39const data = await vscode.workspace.fs.readFile(file);40json = data ? JSON.parse(data.toString()) : {};4142if (json.contents && typeof json.contents === 'string') {43contents = json.contents;44json = JSON.parse(contents) as Partial<nbformat.INotebookContent>;45}46}4748if (json.nbformat && json.nbformat < 4) {49throw new Error('Only Jupyter notebooks version 4+ are supported');50}5152// Then compute indent from the contents (only use first 1K characters as a perf optimization)53const indentAmount = contents ? detectIndent(contents.substring(0, 1_000)).indent : ' ';5455const preferredCellLanguage = getPreferredLanguage(json.metadata);56// Ensure we always have a blank cell.57if ((json.cells || []).length === 0) {58json.cells = [59];60}6162// For notebooks without metadata default the language in metadata to the preferred language.63if (!json.metadata || (!json.metadata.kernelspec && !json.metadata.language_info)) {64json.metadata = json.metadata || {};65json.metadata.language_info = json.metadata.language_info || { name: preferredCellLanguage };66}6768const data = jupyterNotebookModelToNotebookData(69json,70preferredCellLanguage71);72data.metadata = data.metadata || {};73data.metadata.indentAmount = indentAmount;7475return data;76}7778public async serializeNotebook(data: vscode.NotebookData, _token: vscode.CancellationToken): Promise<Uint8Array> {79if (this.disposed) {80return new Uint8Array(0);81}8283const serialized = serializeNotebookToString(data);84return new TextEncoder().encode(serialized);85}8687}888990