Ver código fonte

Initialize a model after the context's first save/revert.

We do this at the context level rather than the widget level since multiple widgets may share the same context. This enables, for example, the cell undo/redo to work even after creating a new widget view of a notebook.
Jason Grout 7 anos atrás
pai
commit
64212a6f10

+ 14 - 3
packages/docregistry/src/context.ts

@@ -216,19 +216,21 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
    */
   initialize(isNew: boolean): Promise<void> {
     if (isNew) {
+      this._model.initialize();
       return this._save();
     }
     if (this._modelDB) {
       return this._modelDB.connected.then(() => {
         if (this._modelDB.isPrepopulated) {
+          this._model.initialize();
           this._save();
           return void 0;
         } else {
-          return this._revert();
+          return this._revert(true);
         }
       });
     } else {
-      return this._revert();
+      return this._revert(true);
     }
   }
 
@@ -483,8 +485,11 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
 
   /**
    * Revert the document contents to disk contents.
+   *
+   * @param initializeModel - call the model's initialization function after
+   * deserializing the content.
    */
-  private _revert(): Promise<void> {
+  private _revert(initializeModel: boolean = false): Promise<void> {
     let opts: Contents.IFetchOptions = {
       format: this._factory.fileFormat,
       type: this._factory.contentType,
@@ -501,6 +506,9 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
       let dirty = false;
       if (contents.format === 'json') {
         model.fromJSON(contents.content);
+        if (initializeModel) {
+          model.initialize();
+        }
       } else {
         let content = contents.content;
         // Convert line endings if necessary, marking the file
@@ -510,6 +518,9 @@ class Context<T extends DocumentRegistry.IModel> implements DocumentRegistry.ICo
           content = content.replace(/\r\n|\r/g, '\n');
         }
         model.fromString(content);
+        if (initializeModel) {
+          model.initialize();
+        }
       }
       this._updateContentsModel(contents);
       model.dirty = dirty;

+ 7 - 0
packages/docregistry/src/default.ts

@@ -154,6 +154,13 @@ class DocumentModel extends CodeEditor.Model implements DocumentRegistry.ICodeMo
     this.fromString(JSON.stringify(value));
   }
 
+  /**
+   * Initialize the model with its current state.
+   */
+  initialize(): void {
+    return;
+  }
+
   /**
    * Trigger a state change signal.
    */

+ 9 - 0
packages/docregistry/src/registry.ts

@@ -646,6 +646,15 @@ namespace DocumentRegistry {
      * Should emit a [contentChanged] signal.
      */
     fromJSON(value: any): void;
+
+    /**
+     * Initialize model state after initial data load.
+     *
+     * #### Notes
+     * This function must be called after the initial data is loaded to set up
+     * initial model state, such as an initial undo stack, etc.
+     */
+    initialize(): void;
   }
 
   /**

+ 8 - 0
packages/notebook/src/model.ts

@@ -251,6 +251,14 @@ class NotebookModel extends DocumentModel implements INotebookModel {
     this.dirty = true;
   }
 
+  /**
+   * Initialize the model with its current state.
+   */
+  initialize(): void {
+    super.initialize();
+    this.cells.clearUndo();
+  }
+
   /**
    * Handle a change in the cells list.
    */

+ 0 - 5
packages/notebook/src/widget.ts

@@ -1315,11 +1315,6 @@ class Notebook extends StaticNotebook {
     // Try to set the active cell index to 0.
     // It will be set to `-1` if there is no new model or the model is empty.
     this.activeCellIndex = 0;
-
-    // Clear the undo history of each cell.
-    if (this.model) {
-      this.model.cells.clearUndo();
-    }
   }
 
   /**

+ 46 - 21
tests/test-docregistry/src/context.spec.ts

@@ -24,9 +24,14 @@ import {
 } from '@jupyterlab/rendermime';
 
 import {
-  waitForDialog, acceptDialog, dismissDialog
+  waitForDialog, acceptDialog, dismissDialog, createNotebookContext
 } from '../../utils';
 
+import {
+  DEFAULT_CONTENT
+} from '../../notebook-utils';
+
+
 
 describe('docregistry/context', () => {
 
@@ -106,19 +111,42 @@ describe('docregistry/context', () => {
 
     describe('#ready()', () => {
 
-      it('should resolve when the file is saved for the first time', (done) => {
-        context.ready.then(done, done);
-        context.initialize(true).catch(done);
+      it('should resolve when the file is saved for the first time', async () => {
+        await context.initialize(true);
+        await context.ready;
       });
 
-      it('should resolve when the file is reverted for the first time', (done) => {
+      it('should resolve when the file is reverted for the first time', async () => {
         manager.contents.save(context.path, {
           type: factory.contentType,
           format: factory.fileFormat,
           content: 'foo'
         });
-        context.ready.then(done, done);
-        context.initialize(false).catch(done);
+        await context.initialize(false);
+        await context.ready;
+      });
+
+      it('should initialize the model when the file is saved for the first time', async () => {
+        const context = await createNotebookContext();
+        context.model.fromJSON(DEFAULT_CONTENT);
+        expect(context.model.cells.canUndo).to.be(true);
+        await context.initialize(true);
+        await context.ready;
+        expect(context.model.cells.canUndo).to.be(false);
+      });
+
+      it('should initialize the model when the file is reverted for the first time', async () => {
+        const context = await createNotebookContext();
+        manager.contents.save(context.path, {
+          type: 'notebook',
+          format: 'json',
+          content: DEFAULT_CONTENT
+        });
+        context.model.fromJSON(DEFAULT_CONTENT);
+        expect(context.model.cells.canUndo).to.be(true);
+        await context.initialize(false);
+        await context.ready;
+        expect(context.model.cells.canUndo).to.be(false);
       });
 
     });
@@ -208,20 +236,17 @@ describe('docregistry/context', () => {
 
     describe('#save()', () => {
 
-      it('should save the contents of the file to disk', () => {
-        return context.initialize(true).then(() => {
-          context.model.fromString('foo');
-          return context.save();
-        }).then(() => {
-          let opts: Contents.IFetchOptions = {
-            format: factory.fileFormat,
-            type: factory.contentType,
-            content: true
-          };
-          return manager.contents.get(context.path, opts);
-        }).then(model => {
-          expect(model.content).to.be('foo');
-        });
+      it('should save the contents of the file to disk', async () => {
+        await context.initialize(true);
+        context.model.fromString('foo');
+        await context.save();
+        let opts: Contents.IFetchOptions = {
+          format: factory.fileFormat,
+          type: factory.contentType,
+          content: true
+        };
+        const model = await manager.contents.get(context.path, opts);
+        expect(model.content).to.be('foo');
       });
 
     });

+ 1 - 1
tests/test-docregistry/src/default.spec.ts

@@ -555,7 +555,7 @@ describe('docregistry/default', () => {
 
   });
 
-  describe.only('DocumentWidget', () => {
+  describe('DocumentWidget', () => {
 
     let manager: ServiceManager.IManager;
 

+ 0 - 11
tests/test-notebook/src/panel.spec.ts

@@ -53,17 +53,6 @@ describe('@jupyterlab/notebook', () => {
         expect(panel).to.be.a(NotebookPanel);
       });
 
-      it('should initialize the model state', async () => {
-        context.model.fromJSON(DEFAULT_CONTENT);
-        await context.initialize(true);
-        await context.ready;
-        expect(context.model.cells.canUndo).to.be(true);
-        const panel = createNotebookPanel(context);
-        expect(panel.content.model).to.be(context.model);
-        // Model state is reset, including the undo history.
-        expect(panel.content.model.cells.canUndo).to.be(false);
-      });
-
       it('should change notebook to edit mode if we have a single empty code cell', async () => {
         const panel = createNotebookPanel(context);
         const model = panel.content.model;