@@ -28,7 +28,8 @@ import { IServiceContainer } from '../../ioc/types';
2828import { captureTelemetry , sendTelemetryEvent } from '../../telemetry' ;
2929import { Telemetry } from '../constants' ;
3030import { NotebookModelChange } from '../interactive-common/interactiveWindowTypes' ;
31- import { INotebookEditor , INotebookEditorProvider , INotebookModel , INotebookStorage } from '../types' ;
31+ import { INotebookEditor , INotebookEditorProvider , INotebookModel } from '../types' ;
32+ import { INotebookStorageProvider } from './notebookStorageProvider' ;
3233
3334// Class that is registered as the custom editor provider for notebooks. VS code will call into this class when
3435// opening an ipynb file. This class then creates a backing storage, model, and opens a view for the file.
@@ -69,9 +70,8 @@ export class NativeEditorProvider
6970 protected customDocuments = new Map < string , CustomDocument > ( ) ;
7071 private readonly _onDidCloseNotebookEditor = new EventEmitter < INotebookEditor > ( ) ;
7172 private openedEditors : Set < INotebookEditor > = new Set < INotebookEditor > ( ) ;
72- private storageAndModels = new Map < string , Promise < { model : INotebookModel ; storage : INotebookStorage } > > ( ) ;
7373 private executedEditors : Set < string > = new Set < string > ( ) ;
74- private models = new WeakSet < INotebookModel > ( ) ;
74+ private models = new Set < INotebookModel > ( ) ;
7575 private notebookCount : number = 0 ;
7676 private openedNotebookCount : number = 0 ;
7777 private _id = uuid ( ) ;
@@ -81,7 +81,8 @@ export class NativeEditorProvider
8181 @inject ( IDisposableRegistry ) protected readonly disposables : IDisposableRegistry ,
8282 @inject ( IWorkspaceService ) protected readonly workspace : IWorkspaceService ,
8383 @inject ( IConfigurationService ) protected readonly configuration : IConfigurationService ,
84- @inject ( ICustomEditorService ) private customEditorService : ICustomEditorService
84+ @inject ( ICustomEditorService ) private customEditorService : ICustomEditorService ,
85+ @inject ( INotebookStorageProvider ) private readonly storage : INotebookStorageProvider
8586 ) {
8687 traceInfo ( `id is ${ this . _id } ` ) ;
8788 asyncRegistry . push ( this ) ;
@@ -100,19 +101,13 @@ export class NativeEditorProvider
100101 } ) ;
101102 }
102103
103- public save ( document : CustomDocument , cancellation : CancellationToken ) : Promise < void > {
104- return this . loadStorage ( document . uri ) . then ( async ( s ) => {
105- if ( s ) {
106- await s . save ( cancellation ) ;
107- }
108- } ) ;
104+ public async save ( document : CustomDocument , cancellation : CancellationToken ) : Promise < void > {
105+ const model = await this . loadModel ( document . uri ) ;
106+ await this . storage . save ( model , cancellation ) ;
109107 }
110- public saveAs ( document : CustomDocument , targetResource : Uri ) : Promise < void > {
111- return this . loadStorage ( document . uri ) . then ( async ( s ) => {
112- if ( s ) {
113- await s . saveAs ( targetResource ) ;
114- }
115- } ) ;
108+ public async saveAs ( document : CustomDocument , targetResource : Uri ) : Promise < void > {
109+ const model = await this . loadModel ( document . uri ) ;
110+ await this . storage . saveAs ( model , targetResource ) ;
116111 }
117112 public applyEdits ( document : CustomDocument , edits : readonly NotebookModelChange [ ] ) : Promise < void > {
118113 return this . loadModel ( document . uri ) . then ( ( s ) => {
@@ -132,11 +127,8 @@ export class NativeEditorProvider
132127 noop ( ) ;
133128 }
134129 public async backup ( document : CustomDocument , cancellation : CancellationToken ) : Promise < void > {
135- return this . loadStorage ( document . uri ) . then ( async ( s ) => {
136- if ( s ) {
137- await s . backup ( cancellation ) ;
138- }
139- } ) ;
130+ const model = await this . loadModel ( document . uri ) ;
131+ await this . storage . backup ( model , cancellation ) ;
140132 }
141133
142134 public async resolveCustomEditor ( document : CustomDocument , panel : WebviewPanel ) {
@@ -146,12 +138,7 @@ export class NativeEditorProvider
146138
147139 public async resolveCustomDocument ( document : CustomDocument ) : Promise < void > {
148140 this . customDocuments . set ( document . uri . fsPath , document ) ;
149- await this . loadStorage ( document . uri ) ;
150- }
151-
152- public async resolveNativeEditorStorage ( document : CustomDocument ) : Promise < INotebookStorage > {
153- this . customDocuments . set ( document . uri . fsPath , document ) ;
154- return this . loadStorage ( document . uri ) ;
141+ await this . loadModel ( document . uri ) ;
155142 }
156143
157144 public async dispose ( ) : Promise < void > {
@@ -204,11 +191,18 @@ export class NativeEditorProvider
204191 this . notebookCount += 1 ;
205192
206193 // Set these contents into the storage before the file opens
207- await this . loadStorage ( uri , contents ) ;
194+ await this . loadModel ( uri , contents ) ;
208195
209196 return this . open ( uri ) ;
210197 }
211198
199+ public loadModel ( file : Uri , contents ?: string ) {
200+ return this . storage . load ( file , contents ) . then ( ( m ) => {
201+ this . trackModel ( m ) ;
202+ return m ;
203+ } ) ;
204+ }
205+
212206 protected async createNotebookEditor ( resource : Uri , panel ?: WebviewPanel ) {
213207 try {
214208 // Get the model
@@ -242,13 +236,15 @@ export class NativeEditorProvider
242236
243237 private closedEditor ( editor : INotebookEditor ) : void {
244238 this . openedEditors . delete ( editor ) ;
245- // If last editor, dispose of the storage
246- const key = editor . file . toString ( ) ;
247- if ( ! [ ...this . openedEditors ] . find ( ( e ) => e . file . toString ( ) === key ) ) {
248- this . storageAndModels . delete ( key ) ;
249- }
250239 this . _onDidCloseNotebookEditor . fire ( editor ) ;
251240 }
241+ private trackModel ( model : INotebookModel ) {
242+ if ( ! this . models . has ( model ) ) {
243+ this . models . add ( model ) ;
244+ this . disposables . push ( model . onDidDispose ( ( ) => this . models . delete ( model ) ) ) ;
245+ this . disposables . push ( model . onDidEdit ( this . modelEdited . bind ( this , model ) ) ) ;
246+ }
247+ }
252248
253249 private onChangedViewState ( ) : void {
254250 this . _onDidChangeActiveNotebookEditor . fire ( this . activeEditor ) ;
@@ -260,15 +256,6 @@ export class NativeEditorProvider
260256 }
261257 }
262258
263- private async modelChanged ( change : NotebookModelChange ) {
264- if ( change . source === 'user' && change . kind === 'file' ) {
265- // Update our storage
266- const promise = this . storageAndModels . get ( change . oldFile . toString ( ) ) ;
267- this . storageAndModels . delete ( change . oldFile . toString ( ) ) ;
268- this . storageAndModels . set ( change . newFile . toString ( ) , promise ! ) ;
269- }
270- }
271-
272259 private async modelEdited ( model : INotebookModel , change : NotebookModelChange ) {
273260 // Find the document associated with this edit.
274261 const document = this . customDocuments . get ( model . file . fsPath ) ;
@@ -277,43 +264,15 @@ export class NativeEditorProvider
277264 }
278265 }
279266
280- private async loadModel ( file : Uri , contents ?: string ) : Promise < INotebookModel > {
281- const modelAndStorage = await this . loadModelAndStorage ( file , contents ) ;
282- return modelAndStorage . model ;
283- }
284-
285- private async loadStorage ( file : Uri , contents ?: string ) : Promise < INotebookStorage > {
286- const modelAndStorage = await this . loadModelAndStorage ( file , contents ) ;
287- return modelAndStorage . storage ;
288- }
289-
290- private loadModelAndStorage ( file : Uri , contents ?: string ) {
291- const key = file . toString ( ) ;
292- let modelPromise = this . storageAndModels . get ( key ) ;
293- if ( ! modelPromise ) {
294- const storage = this . serviceContainer . get < INotebookStorage > ( INotebookStorage ) ;
295- modelPromise = storage . load ( file , contents ) . then ( ( m ) => {
296- if ( ! this . models . has ( m ) ) {
297- this . models . add ( m ) ;
298- this . disposables . push ( m . changed ( this . modelChanged . bind ( this ) ) ) ;
299- this . disposables . push ( storage . onDidEdit ( this . modelEdited . bind ( this , m ) ) ) ;
300- }
301- return { model : m , storage } ;
302- } ) ;
303- this . storageAndModels . set ( key , modelPromise ) ;
304- }
305- return modelPromise ;
306- }
307-
308267 private async getNextNewNotebookUri ( ) : Promise < Uri > {
268+ // tslint:disable-next-line: no-suspicious-comment
269+ // TODO: This will not work, if we close an untitled document.
309270 // See if we have any untitled storage already
310- const untitledStorage = [ ...this . storageAndModels . keys ( ) ] . filter ( ( k ) => Uri . parse ( k ) . scheme === 'untitled' ) ;
311-
271+ const untitledStorage = Array . from ( this . models . values ( ) ) . filter ( ( model ) => model ?. file ?. scheme === 'untitled' ) ;
312272 // Just use the length (don't bother trying to fill in holes). We never remove storage objects from
313273 // our map, so we'll keep creating new untitled notebooks.
314274 const fileName = `${ localize . DataScience . untitledNotebookFileName ( ) } -${ untitledStorage . length + 1 } .ipynb` ;
315275 const fileUri = Uri . file ( fileName ) ;
316-
317276 // Turn this back into an untitled
318277 return fileUri . with ( { scheme : 'untitled' , path : fileName } ) ;
319278 }
0 commit comments