瀏覽代碼

Finish docmanager refactor

Steven Silvester 7 年之前
父節點
當前提交
c96816206f

+ 5 - 89
packages/docmanager-extension/src/index.ts

@@ -10,8 +10,7 @@ import {
 } from '@jupyterlab/apputils';
 
 import {
-  createFromDialog, renameDialog, DocumentManager, IDocumentManager,
-  IFileContainer, showErrorMessage
+  renameDialog, DocumentManager, IDocumentManager, showErrorMessage
 } from '@jupyterlab/docmanager';
 
 import {
@@ -22,10 +21,6 @@ import {
   Contents, Kernel, IServiceManager
 } from '@jupyterlab/services';
 
-import {
-  Menu
-} from '@phosphor/widgets';
-
 
 /**
  * The command IDs used by the document manager plugin.
@@ -60,9 +55,6 @@ namespace CommandIDs {
 
   export
   const rename = 'file-operations:rename';
-
-  export
-  const createLauncher = 'file-operations:create-launcher';
 };
 
 
@@ -90,9 +82,6 @@ const plugin: JupyterLabPlugin<IDocumentManager> = {
       }
     };
     const docManager = new DocumentManager({ registry, manager, opener });
-    let menu = createMenu(app, docManager, registry);
-
-    mainMenu.addMenu(menu, { rank: 1 });
 
     // Register the file operations commands.
     addCommands(app, docManager, registry, palette);
@@ -133,53 +122,23 @@ function addCommands(app: JupyterLab, docManager: IDocumentManager, registry: ID
     execute: () => { app.shell.closeAll(); }
   });
 
-  commands.addCommand(CommandIDs.createFrom, {
-    label: args => (args['label'] || args['creatorName']) as string,
-    execute: args => {
-      const path = typeof args['path'] === 'undefined' ? docManager.cwd
-        : args['path'] as string;
-      const creatorName = args['creatorName'] as string;
-      if (!creatorName) {
-        const command = CommandIDs.createFrom;
-        throw new Error(`${command} requires creatorName.`);
-      }
-
-      const items = args['items'] as string[];
-      if (items) {
-        const container: IFileContainer = { items, path };
-        return createFromDialog(container, docManager, creatorName);
-      }
-
-      const { services } = docManager;
-      return services.contents.get(path, { content: true }).then(contents => {
-        const items = contents.content.map((item: Contents.IModel) => {
-          return item.name;
-        });
-        const container: IFileContainer = { items, path };
-
-        return createFromDialog(container, docManager, creatorName);
-      });
-    }
-  });
-
   commands.addCommand(CommandIDs.deleteFile, {
     execute: args => {
-      const path = typeof args['path'] === 'undefined' ? docManager.cwd
+      const path = typeof args['path'] === 'undefined' ? ''
         : args['path'] as string;
-      const basePath = (args['basePath'] as string) || '';
 
       if (!path) {
         const command = CommandIDs.deleteFile;
         throw new Error(`A non-empty path is required for ${command}.`);
       }
-      return docManager.deleteFile(path, basePath);
+      return docManager.deleteFile(path);
     }
   });
 
   commands.addCommand(CommandIDs.newUntitled, {
     execute: args => {
       const errorTitle = args['error'] as string || 'Error';
-      const path = typeof args['path'] === 'undefined' ? docManager.cwd
+      const path = typeof args['path'] === 'undefined' ? ''
         : args['path'] as string;
       let options: Partial<Contents.ICreateOptions> = {
         type: args['type'] as Contents.ContentType,
@@ -198,7 +157,7 @@ function addCommands(app: JupyterLab, docManager: IDocumentManager, registry: ID
 
   commands.addCommand(CommandIDs.open, {
     execute: args => {
-      const path = typeof args['path'] === 'undefined' ? docManager.cwd
+      const path = typeof args['path'] === 'undefined' ? ''
         : args['path'] as string;
       const factory = args['factory'] as string || void 0;
       const kernel = args['kernel'] as Kernel.IModel || void 0;
@@ -246,25 +205,6 @@ function addCommands(app: JupyterLab, docManager: IDocumentManager, registry: ID
     }
   });
 
-  commands.addCommand(CommandIDs.createLauncher, {
-    label: 'New...',
-    execute: () => {
-      return commands.execute('launcher-jupyterlab:create', {
-        cwd: docManager.cwd
-      });
-    }
-  });
-
-  // Create a launcher with a banner if ther are no open items.
-  app.restored.then(() => {
-    if (app.shell.isEmpty('main')) {
-      commands.execute('launcher-jupyterlab:create', {
-        cwd: docManager.cwd,
-        banner: true
-      });
-    }
-  });
-
   commands.addCommand(CommandIDs.rename, {
     isVisible: () => {
       const widget = app.shell.currentWidget;
@@ -305,30 +245,6 @@ function addCommands(app: JupyterLab, docManager: IDocumentManager, registry: ID
 }
 
 
-/**
- * Create a top level menu for the file browser.
- */
-function createMenu(app: JupyterLab, docManager: IDocumentManager, registry: IDocumentRegistry): Menu {
-  const { commands } = app;
-  const menu = new Menu({ commands });
-
-  menu.title.label = 'File';
-  [
-    CommandIDs.createLauncher,
-    CommandIDs.save,
-    CommandIDs.saveAs,
-    CommandIDs.rename,
-    CommandIDs.restoreCheckpoint,
-    CommandIDs.close,
-    CommandIDs.closeAllFiles
-  ].forEach(command => { menu.addItem({ command }); });
-  menu.addItem({ type: 'separator' });
-  menu.addItem({ command: 'setting-editor:open' });
-
-  return menu;
-}
-
-
 /**
  * A namespace for private module data.
  */

+ 5 - 7
packages/docmanager/src/dialogs.ts

@@ -79,8 +79,8 @@ function renameDialog(manager: IDocumentManager, oldPath: string): Promise<Conte
  * Rename a file with optional dialog.
  */
 export
-function renameFile(manager: IDocumentManager, oldPath: string, newPath: string, basePath = ''): Promise<Contents.IModel> {
-  return manager.rename(oldPath, newPath, basePath).catch(error => {
+function renameFile(manager: IDocumentManager, oldPath: string, newPath: string): Promise<Contents.IModel> {
+  return manager.rename(oldPath, newPath).catch(error => {
     if (error.xhr) {
       error.message = `${error.xhr.statusText} ${error.xhr.status}`;
     }
@@ -93,7 +93,7 @@ function renameFile(manager: IDocumentManager, oldPath: string, newPath: string,
       };
       return showDialog(options).then(button => {
         if (button.accept) {
-          return manager.overwrite(oldPath, newPath, basePath);
+          return manager.overwrite(oldPath, newPath);
         }
       });
     } else {
@@ -249,8 +249,7 @@ class CreateFromHandler extends Widget {
         });
       }
 
-      const basePath = this._container.path;
-      this._manager.deleteFile('/' + this._orig.path, basePath);
+      this._manager.deleteFile('/' + this._orig.path);
       return null;
     });
   }
@@ -316,8 +315,7 @@ class CreateFromHandler extends Widget {
       kernelId = JSON.parse(kernelValue) as Kernel.IModel;
     }
     if (file !== oldPath) {
-      let basePath = this._container.path;
-      let promise = renameFile(this._manager, oldPath, file, basePath);
+      let promise = renameFile(this._manager, oldPath, file);
       return promise.then((contents: Contents.IModel) => {
         if (!contents) {
           return null;

+ 15 - 45
packages/docmanager/src/manager.ts

@@ -7,7 +7,7 @@ import {
 } from '@jupyterlab/apputils';
 
 import {
-  ModelDB, PathExt, uuid
+  ModelDB, uuid
 } from '@jupyterlab/coreutils';
 
 import {
@@ -111,22 +111,6 @@ class DocumentManager implements IDisposable {
     return this._activateRequested;
   }
 
-  /**
-   * The current working directory of the document manager.
-   *
-   * #### Notes
-   * This attribute is DEPRECATED. It is intended for use as a stopgap measure
-   * to create some notion of an application-level working directory for
-   * launching activities that need a sensible starting directory. It will be
-   * replaced with another concept in later releases.
-   */
-  get cwd(): string {
-    return this._cwd;
-  }
-  set cwd(cwd: string) {
-    this._cwd = cwd;
-  }
-
   /**
    * Get whether the document manager has been disposed.
    */
@@ -205,17 +189,13 @@ class DocumentManager implements IDisposable {
   /**
    * Copy a file.
    *
-   * @param fromFile - The path of the original file.
-   *
-   * @param toDir - The path to the target directory.
+   * @param fromFile - The full path of the original file.
    *
-   * @param basePath - The base path to resolve against, defaults to ''.
+   * @param toDir - The full path to the target directory.
    *
    * @returns A promise which resolves to the contents of the file.
    */
-  copy(fromFile: string, toDir: string, basePath = ''): Promise<Contents.IModel> {
-    fromFile = PathExt.resolve(basePath, fromFile);
-    toDir = PathExt.resolve(basePath, toDir);
+  copy(fromFile: string, toDir: string): Promise<Contents.IModel> {
     return this.services.contents.copy(fromFile, toDir);
   }
 
@@ -241,9 +221,7 @@ class DocumentManager implements IDisposable {
   /**
    * Delete a file.
    *
-   * @param path - The path to the file to be deleted.
-   *
-   * @param basePath - The base path to resolve against, defaults to ''.
+   * @param path - The full path to the file to be deleted.
    *
    * @returns A promise which resolves when the file is deleted.
    *
@@ -251,8 +229,7 @@ class DocumentManager implements IDisposable {
    * If there is a running session associated with the file and no other
    * sessions are using the kernel, the session will be shut down.
    */
-  deleteFile(path: string, basePath = ''): Promise<void> {
-    path = PathExt.resolve(basePath, path);
+  deleteFile(path: string): Promise<void> {
     return this.services.sessions.stopIfNeeded(path).then(() => {
       return this.services.contents.delete(path);
     });
@@ -345,40 +322,34 @@ class DocumentManager implements IDisposable {
   /**
    * Overwrite a file.
    *
-   * @param oldPath - The path to the original file.
+   * @param oldPath - The full path to the original file.
    *
-   * @param newPath - The path to the new file.
-   *
-   * @param basePath - The base path to resolve against, defaults to ''.
+   * @param newPath - The full path to the new file.
    *
    * @returns A promise containing the new file contents model.
    */
-  overwrite(oldPath: string, newPath: string, basePath = ''): Promise<Contents.IModel> {
+  overwrite(oldPath: string, newPath: string): Promise<Contents.IModel> {
     // Cleanly overwrite the file by moving it, making sure the original does
     // not exist, and then renaming to the new path.
     const tempPath = `${newPath}.${uuid()}`;
-    const cb = () => this.rename(tempPath, newPath, basePath);
-    return this.rename(oldPath, tempPath, basePath).then(() => {
-      return this.deleteFile(newPath, basePath);
+    const cb = () => this.rename(tempPath, newPath);
+    return this.rename(oldPath, tempPath).then(() => {
+      return this.deleteFile(newPath);
     }).then(cb, cb);
   }
 
   /**
    * Rename a file or directory.
    *
-   * @param oldPath - The path to the original file.
-   *
-   * @param newPath - The path to the new file.
+   * @param oldPath - The full path to the original file.
    *
-   * @param basePath - The base path to resolve against, defaults to ''.
+   * @param newPath - The full path to the new file.
    *
    * @returns A promise containing the new file contents model.  The promise
    * will reject if the newPath already exists.  Use [[overwrite]] to overwrite
    * a file.
    */
-  rename(oldPath: string, newPath: string, basePath = ''): Promise<Contents.IModel> {
-    oldPath = PathExt.resolve(basePath, oldPath);
-    newPath = PathExt.resolve(basePath, newPath);
+  rename(oldPath: string, newPath: string): Promise<Contents.IModel> {
     return this.services.contents.rename(oldPath, newPath);
   }
 
@@ -507,7 +478,6 @@ class DocumentManager implements IDisposable {
 
   private _activateRequested = new Signal<this, string>(this);
   private _contexts: Private.IContext[] = [];
-  private _cwd: string = '';
   private _modelDBFactory: ModelDB.IFactory = null;
   private _opener: DocumentManager.IWidgetOpener = null;
   private _widgetManager: DocumentWidgetManager = null;

+ 52 - 5
packages/filebrowser-extension/src/index.ts

@@ -11,7 +11,7 @@ import {
 } from '@jupyterlab/apputils';
 
 import {
-  IChangedArgs, IStateDB
+  IStateDB
 } from '@jupyterlab/coreutils';
 
 import {
@@ -78,8 +78,12 @@ namespace CommandIDs {
 
   export
   const toggleBrowser = 'filebrowser-main:toggle'; // For main browser only.
+
+  export
+  const createLauncher = 'filebrowser-main:create-launcher'; // For main browser only.
 };
 
+
 /**
  * The default file browser extension.
  */
@@ -188,10 +192,6 @@ function activateFileBrowser(app: JupyterLab, factory: IFileBrowserFactory, docM
 
   addCommands(app, factory.tracker, fbWidget);
 
-  fbWidget.model.pathChanged.connect((sender: any, args: IChangedArgs<string>) => {
-    docManager.cwd = args.newValue;
-  });
-
   fbWidget.title.label = 'Files';
   app.shell.addToLeftArea(fbWidget, { rank: 100 });
 
@@ -201,6 +201,10 @@ function activateFileBrowser(app: JupyterLab, factory: IFileBrowserFactory, docM
       app.commands.execute(CommandIDs.showBrowser, void 0);
     }
   });
+
+  let menu = createMenu(app);
+
+  mainMenu.addMenu(menu, { rank: 1 });
 }
 
 
@@ -355,6 +359,49 @@ function addCommands(app: JupyterLab, tracker: InstanceTracker<FileBrowser>, mai
       }
     }
   });
+
+  commands.addCommand(CommandIDs.createLauncher, {
+    label: 'New...',
+    execute: () => {
+      return commands.execute('launcher-jupyterlab:create', {
+        cwd: mainBrowser.model.path,
+      });
+    }
+  });
+
+  // Create a launcher with a banner if ther are no open items.
+  app.restored.then(() => {
+    if (app.shell.isEmpty('main')) {
+      commands.execute('launcher-jupyterlab:create', {
+        cwd: mainBrowser.model.path,
+        banner: true
+      });
+    }
+  });
+}
+
+
+/**
+ * Create a top level menu for the file browser.
+ */
+function createMenu(app: JupyterLab): Menu {
+  const { commands } = app;
+  const menu = new Menu({ commands });
+
+  menu.title.label = 'File';
+  [
+    CommandIDs.createLauncher,
+    'file-operations:save',
+    'file-operations:save-as',
+    'file-operations:rename',
+    'file-operations:restore-checkpoint',
+    'file-operations:close',
+    'file-operations:close-all-files'
+  ].forEach(command => { menu.addItem({ command }); });
+  menu.addItem({ type: 'separator' });
+  menu.addItem({ command: 'setting-editor:open' });
+
+  return menu;
 }
 
 

+ 29 - 13
packages/filebrowser/src/listing.ts

@@ -358,9 +358,10 @@ class DirListing extends Widget {
       if (this._isCut) {
         const parts = path.split('/');
         const name = parts[parts.length - 1];
-        promises.push(this._model.manager.rename(path, name, basePath));
+        const newPath = PathExt.join(basePath, name);
+        promises.push(this._model.manager.rename(path, newPath));
       } else {
-        promises.push(this._model.manager.copy(path, '.', basePath));
+        promises.push(this._model.manager.copy(path, basePath));
       }
     });
 
@@ -418,7 +419,8 @@ class DirListing extends Widget {
 
     each(this.selectedItems(), item => {
       if (item.type !== 'directory') {
-        promises.push(this._model.manager.copy(item.name, '.', basePath));
+        let oldPath = PathExt.join(basePath, item.name);
+        promises.push(this._model.manager.copy(oldPath, basePath));
       }
     });
     return Promise.all(promises).catch(error => {
@@ -1062,15 +1064,23 @@ class DirListing extends Widget {
     // Get the path based on the target node.
     const index = ArrayExt.firstIndexOf(this._items, target);
     const items = this._sortedItems;
-    const path = items[index].name + '/';
+    let basePath = this._model.path;
+    if (items[index].type === 'directory') {
+      basePath = PathExt.join(basePath, items[index].name);
+    }
     const manager = this._manager;
 
-    // Move all of the items.
+    // Handle the items.
     const promises: Promise<Contents.IModel>[] = [];
-    const names = event.mimeData.getData(CONTENTS_MIME) as string[];
-    for (let name of names) {
-      let newPath = path + name;
-      promises.push(renameFile(manager, name, newPath, this._model.path));
+    const paths = event.mimeData.getData(CONTENTS_MIME) as string[];
+    for (let path of paths) {
+      let name = PathExt.basename(path);
+      let newPath = PathExt.join(basePath, name);
+      // Skip files that are not moving.
+      if (newPath === path) {
+        continue;
+      }
+      promises.push(renameFile(manager, path, newPath));
     }
     Promise.all(promises).catch(error => {
       utils.showErrorMessage('Move Error', error);
@@ -1105,7 +1115,11 @@ class DirListing extends Widget {
       supportedActions: 'move',
       proposedAction: 'move'
     });
-    this._drag.mimeData.setData(CONTENTS_MIME, selectedNames);
+    let basePath = this._model.path;
+    let paths = toArray(map(selectedNames, name => {
+      return PathExt.join(basePath, name);
+    }));
+    this._drag.mimeData.setData(CONTENTS_MIME, paths);
     if (item && item.type !== 'directory') {
       this._drag.mimeData.setData(FACTORY_MIME, () => {
         let path = item.path;
@@ -1241,7 +1255,8 @@ class DirListing extends Widget {
     const promises: Promise<void>[] = [];
     const basePath = this._model.path;
     for (let name of names) {
-      let promise = this._model.manager.deleteFile(name, basePath).catch(err => {
+      let newPath = PathExt.join(basePath, name);
+      let promise = this._model.manager.deleteFile(newPath).catch(err => {
         utils.showErrorMessage('Delete Failed', err);
       });
       promises.push(promise);
@@ -1275,8 +1290,9 @@ class DirListing extends Widget {
       }
 
       const manager = this._manager;
-      const basePath = this._model.path;
-      const promise = renameFile(manager, original, newName, basePath);
+      const oldPath = PathExt.join(this._model.path, original);
+      const newPath = PathExt.join(this._model.path, newName);
+      const promise = renameFile(manager, oldPath, newPath);
       return promise.catch(error => {
         utils.showErrorMessage('Rename Error', error);
         this._inRename = false;

+ 0 - 12
packages/shortcuts-extension/src/index.ts

@@ -100,18 +100,6 @@ const SHORTCUTS = [
     selector: 'body',
     keys: ['Accel Shift L']
   },
-  {
-    command: 'file-operations:create-from',
-    args: { creatorName: 'Text File' },
-    selector: 'body',
-    keys: ['Ctrl O']
-  },
-  {
-    command: 'file-operations:create-from',
-    args: { creatorName: 'Notebook' },
-    selector: 'body',
-    keys: ['Ctrl Shift N']
-  },
   {
     command: 'file-operations:save',
     selector: 'body',