@@ -313,83 +313,91 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
313
313
return VSBuffer . wrap ( bytes ) ;
314
314
}
315
315
316
- async $saveNotebook ( handle : number , uriComponents : UriComponents , versionId : number , options : files . IWriteFileOptions , token : CancellationToken ) : Promise < INotebookPartialFileStatsWithMetadata > {
316
+ async $saveNotebook ( handle : number , uriComponents : UriComponents , versionId : number , options : files . IWriteFileOptions , token : CancellationToken ) : Promise < INotebookPartialFileStatsWithMetadata | files . FileOperationError > {
317
317
const uri = URI . revive ( uriComponents ) ;
318
318
const serializer = this . _notebookSerializer . get ( handle ) ;
319
319
this . trace ( `enter saveNotebook(versionId: ${ versionId } , ${ uri . toString ( ) } )` ) ;
320
320
321
- if ( ! serializer ) {
322
- throw new NotebookSaveError ( 'NO serializer found' ) ;
323
- }
321
+ try {
322
+ if ( ! serializer ) {
323
+ throw new NotebookSaveError ( 'NO serializer found' ) ;
324
+ }
324
325
325
- const document = this . _documents . get ( uri ) ;
326
- if ( ! document ) {
327
- throw new NotebookSaveError ( 'Document NOT found' ) ;
328
- }
326
+ const document = this . _documents . get ( uri ) ;
327
+ if ( ! document ) {
328
+ throw new NotebookSaveError ( 'Document NOT found' ) ;
329
+ }
329
330
330
- if ( document . versionId !== versionId ) {
331
- throw new NotebookSaveError ( 'Document version mismatch, expected: ' + versionId + ', actual: ' + document . versionId ) ;
332
- }
331
+ if ( document . versionId !== versionId ) {
332
+ throw new NotebookSaveError ( 'Document version mismatch, expected: ' + versionId + ', actual: ' + document . versionId ) ;
333
+ }
333
334
334
- if ( ! this . _extHostFileSystem . value . isWritableFileSystem ( uri . scheme ) ) {
335
- throw new files . FileOperationError ( localize ( 'err.readonly' , "Unable to modify read-only file '{0}'" , this . _resourceForError ( uri ) ) , files . FileOperationResult . FILE_PERMISSION_DENIED ) ;
336
- }
335
+ if ( ! this . _extHostFileSystem . value . isWritableFileSystem ( uri . scheme ) ) {
336
+ throw new files . FileOperationError ( localize ( 'err.readonly' , "Unable to modify read-only file '{0}'" , this . _resourceForError ( uri ) ) , files . FileOperationResult . FILE_PERMISSION_DENIED ) ;
337
+ }
337
338
338
- const data : vscode . NotebookData = {
339
- metadata : filter ( document . apiNotebook . metadata , key => ! ( serializer . options ?. transientDocumentMetadata ?? { } ) [ key ] ) ,
340
- cells : [ ] ,
341
- } ;
339
+ const data : vscode . NotebookData = {
340
+ metadata : filter ( document . apiNotebook . metadata , key => ! ( serializer . options ?. transientDocumentMetadata ?? { } ) [ key ] ) ,
341
+ cells : [ ] ,
342
+ } ;
342
343
343
- // this data must be retrieved before any async calls to ensure the data is for the correct version
344
- for ( const cell of document . apiNotebook . getCells ( ) ) {
345
- const cellData = new extHostTypes . NotebookCellData (
346
- cell . kind ,
347
- cell . document . getText ( ) ,
348
- cell . document . languageId ,
349
- cell . mime ,
350
- ! ( serializer . options ?. transientOutputs ) ? [ ...cell . outputs ] : [ ] ,
351
- cell . metadata ,
352
- cell . executionSummary
353
- ) ;
354
-
355
- cellData . metadata = filter ( cell . metadata , key => ! ( serializer . options ?. transientCellMetadata ?? { } ) [ key ] ) ;
356
- data . cells . push ( cellData ) ;
357
- }
344
+ // this data must be retrieved before any async calls to ensure the data is for the correct version
345
+ for ( const cell of document . apiNotebook . getCells ( ) ) {
346
+ const cellData = new extHostTypes . NotebookCellData (
347
+ cell . kind ,
348
+ cell . document . getText ( ) ,
349
+ cell . document . languageId ,
350
+ cell . mime ,
351
+ ! ( serializer . options ?. transientOutputs ) ? [ ...cell . outputs ] : [ ] ,
352
+ cell . metadata ,
353
+ cell . executionSummary
354
+ ) ;
358
355
359
- // validate write
360
- await this . _validateWriteFile ( uri , options ) ;
356
+ cellData . metadata = filter ( cell . metadata , key => ! ( serializer . options ?. transientCellMetadata ?? { } ) [ key ] ) ;
357
+ data . cells . push ( cellData ) ;
358
+ }
361
359
362
- if ( token . isCancellationRequested ) {
363
- throw new CancellationError ( ) ;
364
- }
365
- const bytes = await serializer . serializer . serializeNotebook ( data , token ) ;
366
- if ( token . isCancellationRequested ) {
367
- throw new CancellationError ( ) ;
368
- }
360
+ // validate write
361
+ await this . _validateWriteFile ( uri , options ) ;
369
362
370
- // Don't accept any cancellation beyond this point, we need to report the result of the file write
371
- this . trace ( `serialized versionId: ${ versionId } ${ uri . toString ( ) } ` ) ;
372
- await this . _extHostFileSystem . value . writeFile ( uri , bytes ) ;
373
- this . trace ( `Finished write versionId: ${ versionId } ${ uri . toString ( ) } ` ) ;
374
- const providerExtUri = this . _extHostFileSystem . getFileSystemProviderExtUri ( uri . scheme ) ;
375
- const stat = await this . _extHostFileSystem . value . stat ( uri ) ;
363
+ if ( token . isCancellationRequested ) {
364
+ throw new CancellationError ( ) ;
365
+ }
366
+ const bytes = await serializer . serializer . serializeNotebook ( data , token ) ;
367
+ if ( token . isCancellationRequested ) {
368
+ throw new CancellationError ( ) ;
369
+ }
376
370
377
- const fileStats = {
378
- name : providerExtUri . basename ( uri ) ,
379
- isFile : ( stat . type & files . FileType . File ) !== 0 ,
380
- isDirectory : ( stat . type & files . FileType . Directory ) !== 0 ,
381
- isSymbolicLink : ( stat . type & files . FileType . SymbolicLink ) !== 0 ,
382
- mtime : stat . mtime ,
383
- ctime : stat . ctime ,
384
- size : stat . size ,
385
- readonly : Boolean ( ( stat . permissions ?? 0 ) & files . FilePermission . Readonly ) || ! this . _extHostFileSystem . value . isWritableFileSystem ( uri . scheme ) ,
386
- locked : Boolean ( ( stat . permissions ?? 0 ) & files . FilePermission . Locked ) ,
387
- etag : files . etag ( { mtime : stat . mtime , size : stat . size } ) ,
388
- children : undefined
389
- } ;
371
+ // Don't accept any cancellation beyond this point, we need to report the result of the file write
372
+ this . trace ( `serialized versionId: ${ versionId } ${ uri . toString ( ) } ` ) ;
373
+ await this . _extHostFileSystem . value . writeFile ( uri , bytes ) ;
374
+ this . trace ( `Finished write versionId: ${ versionId } ${ uri . toString ( ) } ` ) ;
375
+ const providerExtUri = this . _extHostFileSystem . getFileSystemProviderExtUri ( uri . scheme ) ;
376
+ const stat = await this . _extHostFileSystem . value . stat ( uri ) ;
377
+
378
+ const fileStats = {
379
+ name : providerExtUri . basename ( uri ) ,
380
+ isFile : ( stat . type & files . FileType . File ) !== 0 ,
381
+ isDirectory : ( stat . type & files . FileType . Directory ) !== 0 ,
382
+ isSymbolicLink : ( stat . type & files . FileType . SymbolicLink ) !== 0 ,
383
+ mtime : stat . mtime ,
384
+ ctime : stat . ctime ,
385
+ size : stat . size ,
386
+ readonly : Boolean ( ( stat . permissions ?? 0 ) & files . FilePermission . Readonly ) || ! this . _extHostFileSystem . value . isWritableFileSystem ( uri . scheme ) ,
387
+ locked : Boolean ( ( stat . permissions ?? 0 ) & files . FilePermission . Locked ) ,
388
+ etag : files . etag ( { mtime : stat . mtime , size : stat . size } ) ,
389
+ children : undefined
390
+ } ;
390
391
391
- this . trace ( `exit saveNotebook(versionId: ${ versionId } , ${ uri . toString ( ) } )` ) ;
392
- return fileStats ;
392
+ this . trace ( `exit saveNotebook(versionId: ${ versionId } , ${ uri . toString ( ) } )` ) ;
393
+ return fileStats ;
394
+ } catch ( error ) {
395
+ // return fileOperationsErrors to keep the whole object across serialization, these errors are handled specially by the WCS
396
+ if ( error instanceof files . FileOperationError ) {
397
+ return { ...error , message : error . message } ;
398
+ }
399
+ throw error ;
400
+ }
393
401
}
394
402
395
403
/**
0 commit comments