@@ -14,8 +14,9 @@ import { isFalsyOrWhitespace } from 'vs/base/common/strings';
1414import { assertIsDefined } from 'vs/base/common/types' ;
1515import { URI , UriComponents } from 'vs/base/common/uri' ;
1616import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' ;
17+ import * as files from 'vs/platform/files/common/files' ;
1718import { Cache } from 'vs/workbench/api/common/cache' ;
18- import { ExtHostNotebookShape , IMainContext , IModelAddedData , INotebookCellStatusBarListDto , INotebookDocumentsAndEditorsDelta , INotebookDocumentShowOptions , INotebookEditorAddData , MainContext , MainThreadNotebookDocumentsShape , MainThreadNotebookEditorsShape , MainThreadNotebookShape , NotebookDataDto } from 'vs/workbench/api/common/extHost.protocol' ;
19+ import { ExtHostNotebookShape , IMainContext , IModelAddedData , INotebookCellStatusBarListDto , INotebookDocumentsAndEditorsDelta , INotebookDocumentShowOptions , INotebookEditorAddData , INotebookPartialFileStatsWithMetadata , MainContext , MainThreadNotebookDocumentsShape , MainThreadNotebookEditorsShape , MainThreadNotebookShape , NotebookDataDto } from 'vs/workbench/api/common/extHost.protocol' ;
1920import { ApiCommand , ApiCommandArgument , ApiCommandResult , CommandsConverter , ExtHostCommands } from 'vs/workbench/api/common/extHostCommands' ;
2021import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments' ;
2122import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors' ;
@@ -27,6 +28,9 @@ import type * as vscode from 'vscode';
2728import { ExtHostCell , ExtHostNotebookDocument } from './extHostNotebookDocument' ;
2829import { ExtHostNotebookEditor } from './extHostNotebookEditor' ;
2930import { onUnexpectedExternalError } from 'vs/base/common/errors' ;
31+ import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer' ;
32+ import { basename } from 'vs/base/common/resources' ;
33+ import { filter } from 'vs/base/common/objects' ;
3034
3135
3236
@@ -69,6 +73,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
6973 commands : ExtHostCommands ,
7074 private _textDocumentsAndEditors : ExtHostDocumentsAndEditors ,
7175 private _textDocuments : ExtHostDocuments ,
76+ private _extHostFileSystem : IExtHostConsumerFileSystem
7277 ) {
7378 this . _notebookProxy = mainContext . getProxy ( MainContext . MainThreadNotebook ) ;
7479 this . _notebookDocumentsProxy = mainContext . getProxy ( MainContext . MainThreadNotebookDocuments ) ;
@@ -263,14 +268,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
263268 // --- serialize/deserialize
264269
265270 private _handlePool = 0 ;
266- private readonly _notebookSerializer = new Map < number , vscode . NotebookSerializer > ( ) ;
271+ private readonly _notebookSerializer = new Map < number , { viewType : string ; serializer : vscode . NotebookSerializer ; options : vscode . NotebookDocumentContentOptions | undefined } > ( ) ;
267272
268273 registerNotebookSerializer ( extension : IExtensionDescription , viewType : string , serializer : vscode . NotebookSerializer , options ?: vscode . NotebookDocumentContentOptions , registration ?: vscode . NotebookRegistrationData ) : vscode . Disposable {
269274 if ( isFalsyOrWhitespace ( viewType ) ) {
270275 throw new Error ( `viewType cannot be empty or just whitespace` ) ;
271276 }
272277 const handle = this . _handlePool ++ ;
273- this . _notebookSerializer . set ( handle , serializer ) ;
278+ this . _notebookSerializer . set ( handle , { viewType , serializer, options } ) ;
274279 this . _notebookProxy . $registerNotebookSerializer (
275280 handle ,
276281 { id : extension . identifier , location : extension . extensionLocation } ,
@@ -288,7 +293,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
288293 if ( ! serializer ) {
289294 throw new Error ( 'NO serializer found' ) ;
290295 }
291- const data = await serializer . deserializeNotebook ( bytes . buffer , token ) ;
296+ const data = await serializer . serializer . deserializeNotebook ( bytes . buffer , token ) ;
292297 return new SerializableObjectWithBuffers ( typeConverters . NotebookData . from ( data ) ) ;
293298 }
294299
@@ -297,10 +302,67 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
297302 if ( ! serializer ) {
298303 throw new Error ( 'NO serializer found' ) ;
299304 }
300- const bytes = await serializer . serializeNotebook ( typeConverters . NotebookData . to ( data . value ) , token ) ;
305+ const bytes = await serializer . serializer . serializeNotebook ( typeConverters . NotebookData . to ( data . value ) , token ) ;
301306 return VSBuffer . wrap ( bytes ) ;
302307 }
303308
309+ async $saveNotebook ( handle : number , uriComponents : UriComponents , versionId : number , options : files . IWriteFileOptions , token : CancellationToken ) : Promise < INotebookPartialFileStatsWithMetadata > {
310+ const uri = URI . revive ( uriComponents ) ;
311+ const serializer = this . _notebookSerializer . get ( handle ) ;
312+ if ( ! serializer ) {
313+ throw new Error ( 'NO serializer found' ) ;
314+ }
315+
316+ const document = this . _documents . get ( uri ) ;
317+ if ( ! document ) {
318+ throw new Error ( 'Document NOT found' ) ;
319+ }
320+
321+ if ( document . versionId !== versionId ) {
322+ throw new Error ( 'Document version mismatch' ) ;
323+ }
324+
325+ const data : vscode . NotebookData = {
326+ metadata : filter ( document . apiNotebook . metadata , key => ! ( serializer . options ?. transientDocumentMetadata ?? { } ) [ key ] ) ,
327+ cells : [ ] ,
328+ } ;
329+
330+ for ( const cell of document . apiNotebook . getCells ( ) ) {
331+ const cellData = new extHostTypes . NotebookCellData (
332+ cell . kind ,
333+ cell . document . getText ( ) ,
334+ cell . document . languageId ,
335+ cell . mime ,
336+ ! ( serializer . options ?. transientOutputs ) ? [ ...cell . outputs ] : [ ] ,
337+ cell . metadata ,
338+ cell . executionSummary
339+ ) ;
340+
341+ cellData . metadata = filter ( cell . metadata , key => ! ( serializer . options ?. transientCellMetadata ?? { } ) [ key ] ) ;
342+ data . cells . push ( cellData ) ;
343+ }
344+
345+ const bytes = await serializer . serializer . serializeNotebook ( data , token ) ;
346+ await this . _extHostFileSystem . value . writeFile ( uri , bytes ) ;
347+ const stat = await this . _extHostFileSystem . value . stat ( uri ) ;
348+
349+ const fileStats = {
350+ name : basename ( uri ) , // providerExtUri.basename(resource)
351+ isFile : ( stat . type & files . FileType . File ) !== 0 ,
352+ isDirectory : ( stat . type & files . FileType . Directory ) !== 0 ,
353+ isSymbolicLink : ( stat . type & files . FileType . SymbolicLink ) !== 0 ,
354+ mtime : stat . mtime ,
355+ ctime : stat . ctime ,
356+ size : stat . size ,
357+ readonly : Boolean ( ( stat . permissions ?? 0 ) & files . FilePermission . Readonly ) || ! this . _extHostFileSystem . value . isWritableFileSystem ( uri . scheme ) ,
358+ locked : Boolean ( ( stat . permissions ?? 0 ) & files . FilePermission . Locked ) ,
359+ etag : files . etag ( { mtime : stat . mtime , size : stat . size } ) ,
360+ children : undefined
361+ } ;
362+
363+ return fileStats ;
364+ }
365+
304366 // --- open, save, saveAs, backup
305367
306368
0 commit comments