/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import type * as nbformat from '@jupyterlab/nbformat'; import detectIndent from 'detect-indent'; import * as vscode from 'vscode'; import { getPreferredLanguage, jupyterNotebookModelToNotebookData } from './deserializers'; import * as fnv from '@enonic/fnv-plus'; import { serializeNotebookToString } from './serializers'; export abstract class NotebookSerializerBase extends vscode.Disposable implements vscode.NotebookSerializer { protected disposed: boolean = false; constructor(protected readonly context: vscode.ExtensionContext) { super(() => { }); } override dispose() { this.disposed = true; super.dispose(); } public async deserializeNotebook(content: Uint8Array, _token: vscode.CancellationToken): Promise { let contents = ''; try { contents = new TextDecoder().decode(content); } catch { } let json = contents && /\S/.test(contents) ? (JSON.parse(contents) as Partial) : {}; if (json.__webview_backup) { const backupId = json.__webview_backup; const uri = this.context.globalStorageUri; const folder = uri.with({ path: this.context.globalStorageUri.path.replace('vscode.ipynb', 'ms-toolsai.jupyter') }); const fileHash = fnv.fast1a32hex(backupId) as string; const fileName = `${fileHash}.ipynb`; const file = vscode.Uri.joinPath(folder, fileName); const data = await vscode.workspace.fs.readFile(file); json = data ? JSON.parse(data.toString()) : {}; if (json.contents && typeof json.contents === 'string') { contents = json.contents; json = JSON.parse(contents) as Partial; } } if (json.nbformat && json.nbformat < 4) { throw new Error('Only Jupyter notebooks version 4+ are supported'); } // Then compute indent from the contents (only use first 1K characters as a perf optimization) const indentAmount = contents ? detectIndent(contents.substring(0, 1_000)).indent : ' '; const preferredCellLanguage = getPreferredLanguage(json.metadata); // Ensure we always have a blank cell. if ((json.cells || []).length === 0) { json.cells = [ ]; } // For notebooks without metadata default the language in metadata to the preferred language. if (!json.metadata || (!json.metadata.kernelspec && !json.metadata.language_info)) { json.metadata = json.metadata || {}; json.metadata.language_info = json.metadata.language_info || { name: preferredCellLanguage }; } const data = jupyterNotebookModelToNotebookData( json, preferredCellLanguage ); data.metadata = data.metadata || {}; data.metadata.indentAmount = indentAmount; return data; } public async serializeNotebook(data: vscode.NotebookData, _token: vscode.CancellationToken): Promise { if (this.disposed) { return new Uint8Array(0); } const serialized = serializeNotebookToString(data); return new TextEncoder().encode(serialized); } }