Ver Fonte

Clean up the filebrowser model

Steven Silvester há 8 anos atrás
pai
commit
66be81d541
1 ficheiros alterados com 55 adições e 83 exclusões
  1. 55 83
      src/filebrowser/model.ts

+ 55 - 83
src/filebrowser/model.ts

@@ -2,12 +2,16 @@
 // Distributed under the terms of the Modified BSD License.
 
 import {
-  Contents, Kernel, IServiceManager, Session
+  Contents, ContentsManager, Kernel, IServiceManager, Session
 } from 'jupyter-js-services';
 
 import {
-  deepEqual
-} from 'phosphor/lib/algorithm/json';
+  ISequence
+} from 'phosphor/lib/algorithm/sequence';
+
+import {
+  Vector
+} from 'phosphor/lib/collections/vector';
 
 import {
   IDisposable
@@ -37,7 +41,7 @@ class FileBrowserModel implements IDisposable {
    */
   constructor(options: FileBrowserModel.IOptions) {
     this._manager = options.manager;
-    this._model = { path: '', name: '/', type: 'directory', content: [] };
+    this._model = { path: '', name: '/', type: 'directory' };
     this.cd();
     this._manager.sessions.runningChanged.connect(this._onRunningChanged, this);
   }
@@ -59,36 +63,30 @@ class FileBrowserModel implements IDisposable {
 
   /**
    * Get the current path.
-   *
-   * #### Notes
-   * This is a read-only property.
    */
   get path(): string {
-    return this._model.path;
+    return this._model ? this._model.path : '';
   }
 
   /**
    * Get a read-only list of the items in the current path.
    */
-  get items(): Contents.IModel[] {
-    return this._model.content ? this._model.content.slice() : [];
+  get items(): ISequence<Contents.IModel> {
+    return this._items;
   }
 
   /**
-   * Get whether the view model is disposed.
+   * Get whether the model is disposed.
    */
   get isDisposed(): boolean {
     return this._model === null;
   }
 
   /**
-   * Get the session models for active notebooks.
-   *
-   * #### Notes
-   * This is a read-only property.
+   * Get the session models for active notebooks in the current directory.
    */
-  get sessions(): Session.IModel[] {
-    return this._sessions.slice();
+  get sessions(): ISequence<Session.IModel> {
+    return this._sessions;
   }
 
   /**
@@ -99,10 +97,15 @@ class FileBrowserModel implements IDisposable {
   }
 
   /**
-   * Dispose of the resources held by the view model.
+   * Dispose of the resources held by the model.
    */
   dispose(): void {
+    if (this.isDisposed) {
+      return;
+    }
     this._model = null;
+    this._sessions.clear();
+    this._items.clear();
     this._manager = null;
     clearSignalData(this);
   }
@@ -120,7 +123,7 @@ class FileBrowserModel implements IDisposable {
     }
     // Collapse requests to the same directory.
     if (newValue === this._pendingPath) {
-      return Promise.resolve(void 0);
+      return this._pending;
     }
     let oldValue = this.path;
     let options: Contents.IFetchOptions = { content: true };
@@ -129,13 +132,18 @@ class FileBrowserModel implements IDisposable {
       newValue = this.path;
     }
     if (oldValue !== newValue) {
-      this._sessions = [];
+      this._sessions.clear();
     }
-    this._pending = this._manager.contents.get(newValue, options).then(contents => {
-      this._model = contents;
-      return this._manager.sessions.listRunning();
+    let manager = this._manager;
+    this._pending = manager.contents.get(newValue, options).then(contents => {
+      this._handleContents(contents);
+      this._pendingPath = null;
+      return manager.sessions.listRunning();
     }).then(models => {
-      this._onRunningChanged(this._manager.sessions, models);
+      if (this.isDisposed) {
+        return;
+      }
+      this._onRunningChanged(manager.sessions, models);
       if (oldValue !== newValue) {
         this.pathChanged.emit({
           name: 'path',
@@ -144,7 +152,6 @@ class FileBrowserModel implements IDisposable {
         });
       }
       this.refreshed.emit(void 0);
-      this._pendingPath = null;
     });
     return this._pending;
   }
@@ -198,7 +205,7 @@ class FileBrowserModel implements IDisposable {
     return this._manager.contents.delete(path).then(() => {
       this.fileChanged.emit({
         name: 'file',
-        oldValue: {path: path},
+        oldValue: { path: path },
         newValue: void 0
       });
     });
@@ -299,7 +306,7 @@ class FileBrowserModel implements IDisposable {
     path = path ? path + '/' + file.name : file.name;
     return this._manager.contents.get(path, {}).then(() => {
       let msg = `"${file.name}" already exists`;
-      return Private.typedThrow<Contents.IModel>(msg);
+      throw new Error(msg);
     }, () => {
       return this._upload(file);
     });
@@ -359,25 +366,29 @@ class FileBrowserModel implements IDisposable {
 
   }
 
+  /**
+   * Handle an updated contents model.
+   */
+  private _handleContents(contents: Contents.IModel): void {
+    // Update our internal data.
+    this._model = contents;
+    this._items.clear();
+    this._paths = contents.content.map((model: Contents.IModel) => {
+      return model.path;
+    });
+    this._items = new Vector<Contents.IModel>(contents.content);
+    this._model.content = null;
+  }
+
   /**
    * Handle a change to the running sessions.
    */
   private _onRunningChanged(sender: Session.IManager, models: Session.IModel[]): void {
-    if (deepEqual(models, this._sessions)) {
-      return;
-    }
-    this._sessions = [];
-    if (!models.length) {
-      this.refreshed.emit(void 0);
-      return;
-    }
-    let paths = this._model.content.map((contents: Contents.IModel) => {
-      return contents.path;
-    });
+    this._sessions.clear();
     for (let model of models) {
-      let index = paths.indexOf(model.notebook.path);
+      let index = this._paths.indexOf(model.notebook.path);
       if (index !== -1) {
-        this._sessions.push(model);
+        this._sessions.pushBack(model);
       }
     }
     this.refreshed.emit(void 0);
@@ -385,7 +396,9 @@ class FileBrowserModel implements IDisposable {
 
   private _maxUploadSizeMb = 15;
   private _manager: IServiceManager = null;
-  private _sessions: Session.IModel[] = [];
+  private _sessions = new Vector<Session.IModel>();
+  private _items = new Vector<Contents.IModel>();
+  private _paths: string[] = [];
   private _model: Contents.IModel;
   private _pendingPath: string = null;
   private _pending: Promise<void> = null;
@@ -446,47 +459,6 @@ namespace Private {
    */
   export
   function normalizePath(root: string, path: string): string {
-    // Current directory
-    if (path === '.') {
-      return root;
-    }
-    // Root path.
-    if (path.indexOf('/') === 0) {
-      path = path.slice(1, path.length);
-      root = '';
-    // Current directory.
-    } else if (path.indexOf('./') === 0) {
-      path = path.slice(2, path.length);
-    // Grandparent directory.
-    } else if (path.indexOf('../../') === 0) {
-      let parts = root.split('/');
-      root = parts.splice(0, parts.length - 2).join('/');
-      path = path.slice(6, path.length);
-    // Parent directory.
-    } else if (path.indexOf('../') === 0) {
-      let parts = root.split('/');
-      root = parts.splice(0, parts.length - 1).join('/');
-      path = path.slice(3, path.length);
-    } else {
-      // Current directory.
-    }
-    if (path[path.length - 1] === '/') {
-      path = path.slice(0, path.length - 1);
-    }
-    // Combine the root and the path if necessary.
-    if (root && path) {
-      path = root + '/' + path;
-    } else if (root) {
-      path = root;
-    }
-    return path;
-  }
-
-  /**
-   * Work around TS 1.8 type inferencing in promises which only throw.
-   */
-  export
-  function typedThrow<T>(msg: string): T {
-    throw new Error(msg);
+    return ContentsManager.getAbsolutePath(path, root);
   }
 }