Browse Source

Separate goToPath and openPath api; refactor

Trevor Slaton 5 years ago
parent
commit
4b58e6b94d

+ 1 - 1
packages/application-extension/src/index.tsx

@@ -277,7 +277,7 @@ const tree: JupyterFrontEndPlugin<void> = {
         router.navigate(url);
 
         try {
-          await commands.execute('filebrowser:navigate', { path });
+          await commands.execute('filebrowser:go-to-path', { path });
           await commands.execute('apputils:save-statedb', { immediate });
         } catch (error) {
           console.warn('Tree routing failed.', error);

+ 1 - 33
packages/docmanager-extension/src/index.ts

@@ -23,7 +23,6 @@ import { IChangedArgs, ISettingRegistry, Time } from '@jupyterlab/coreutils';
 
 import {
   renameDialog,
-  getOpenPath,
   DocumentManager,
   IDocumentManager,
   PathStatus,
@@ -54,8 +53,6 @@ namespace CommandIDs {
 
   export const openBrowserTab = 'docmanager:open-browser-tab';
 
-  export const openDirect = 'docmanager:open-direct';
-
   export const reload = 'docmanager:reload';
 
   export const rename = 'docmanager:rename';
@@ -368,34 +365,6 @@ function addCommands(
     label: () => 'Open in New Browser Tab'
   });
 
-  commands.addCommand(CommandIDs.openDirect, {
-    label: () => 'Open From Path...',
-    caption: 'Open from path',
-    isEnabled: () => true,
-    execute: () => {
-      return getOpenPath(docManager.services.contents).then(path => {
-        if (!path) {
-          return;
-        }
-        docManager.services.contents.get(path, { content: false }).then(
-          item => {
-            // exists
-            return commands.execute('filebrowser:navigate', { path: path });
-          },
-          () => {
-            // does not exist
-            return showDialog({
-              title: 'Cannot open',
-              body: 'No such file or directory found',
-              buttons: [Dialog.okButton()]
-            });
-          }
-        );
-        return;
-      });
-    }
-  });
-
   commands.addCommand(CommandIDs.reload, {
     label: () =>
       `Reload ${fileType(shell.currentWidget, docManager)} from Disk`,
@@ -549,7 +518,6 @@ function addCommands(
 
   if (palette) {
     [
-      CommandIDs.openDirect,
       CommandIDs.reload,
       CommandIDs.restoreCheckpoint,
       CommandIDs.save,
@@ -636,7 +604,7 @@ function addLabCommands(
 
       // 'activate' is needed if this command is selected in the "open tabs" sidebar
       await commands.execute('filebrowser:activate', { path: context.path });
-      await commands.execute('filebrowser:navigate', { path: context.path });
+      await commands.execute('filebrowser:go-to-path', { path: context.path });
     }
   });
 

+ 0 - 60
packages/docmanager/src/dialogs.ts

@@ -145,49 +145,6 @@ class RenameHandler extends Widget {
   }
 }
 
-/*
- * A widget used to open a file directly.
- */
-class OpenDirectWidget extends Widget {
-  /**
-   * Construct a new open file widget.
-   */
-  constructor() {
-    super({ node: Private.createOpenNode() });
-  }
-
-  /**
-   * Get the value of the widget.
-   */
-  getValue(): string {
-    return this.inputNode.value;
-  }
-
-  /**
-   * Get the input text node.
-   */
-  get inputNode(): HTMLInputElement {
-    return this.node.getElementsByTagName('input')[0] as HTMLInputElement;
-  }
-}
-
-/**
- * Create the node for the open handler.
- */
-export function getOpenPath(contentsManager: any): Promise<string | undefined> {
-  return showDialog({
-    title: 'Open Path',
-    body: new OpenDirectWidget(),
-    buttons: [Dialog.cancelButton(), Dialog.okButton({ label: 'OPEN' })],
-    focusNodeSelector: 'input'
-  }).then((result: any) => {
-    if (result.button.label === 'OPEN') {
-      return result.value;
-    }
-    return;
-  });
-}
-
 /**
  * A namespace for private data.
  */
@@ -213,21 +170,4 @@ namespace Private {
     body.appendChild(name);
     return body;
   }
-
-  /**
-   * Create the node for a open widget.
-   */
-  export function createOpenNode(): HTMLElement {
-    let body = document.createElement('div');
-    let existingLabel = document.createElement('label');
-    existingLabel.textContent = 'Path:';
-
-    let input = document.createElement('input');
-    input.value = '';
-    input.placeholder = '/path/relative/to/jlab/root';
-
-    body.appendChild(existingLabel);
-    body.appendChild(input);
-    return body;
-  }
 }

+ 1 - 0
packages/filebrowser-extension/package.json

@@ -42,6 +42,7 @@
     "@jupyterlab/docmanager": "^1.0.0-alpha.10",
     "@jupyterlab/filebrowser": "^1.0.0-alpha.10",
     "@jupyterlab/launcher": "^1.0.0-alpha.10",
+    "@jupyterlab/mainmenu": "^1.0.0-alpha.10",
     "@jupyterlab/services": "^4.0.0-alpha.10",
     "@jupyterlab/statusbar": "^1.0.0-alpha.10",
     "@phosphor/algorithm": "^1.1.2",

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

@@ -12,7 +12,10 @@ import {
   Clipboard,
   InstanceTracker,
   MainAreaWidget,
-  ToolbarButton
+  ToolbarButton,
+  ICommandPalette,
+  Dialog,
+  showDialog
 } from '@jupyterlab/apputils';
 
 import {
@@ -25,11 +28,14 @@ import {
 
 import { IDocumentManager } from '@jupyterlab/docmanager';
 
+import { IMainMenu } from '@jupyterlab/mainmenu';
+
 import {
   FileBrowserModel,
   FileBrowser,
   FileUploadStatus,
-  IFileBrowserFactory
+  IFileBrowserFactory,
+  askUserForPath
 } from '@jupyterlab/filebrowser';
 
 import { Launcher } from '@jupyterlab/launcher';
@@ -68,7 +74,9 @@ namespace CommandIDs {
   // For main browser only.
   export const hideBrowser = 'filebrowser:hide-main';
 
-  export const navigate = 'filebrowser:navigate';
+  export const goToPath = 'filebrowser:go-to-path';
+
+  export const openPath = 'filebrowser:open-path';
 
   export const open = 'filebrowser:open';
 
@@ -107,6 +115,7 @@ const browser: JupyterFrontEndPlugin<void> = {
     ILayoutRestorer,
     ISettingRegistry
   ],
+  optional: [ICommandPalette, IMainMenu],
   autoStart: true
 };
 
@@ -238,7 +247,9 @@ function activateBrowser(
   docManager: IDocumentManager,
   labShell: ILabShell,
   restorer: ILayoutRestorer,
-  settingRegistry: ISettingRegistry
+  settingRegistry: ISettingRegistry,
+  palette: ICommandPalette,
+  mainMenu: IMainMenu
 ): void {
   const browser = factory.defaultBrowser;
   const { commands } = app;
@@ -251,7 +262,7 @@ function activateBrowser(
   // responsible for their own restoration behavior, if any.
   restorer.add(browser, namespace);
 
-  addCommands(app, factory, labShell, docManager);
+  addCommands(app, factory, labShell, docManager, palette, mainMenu);
 
   browser.title.iconClass = 'jp-FolderIcon jp-SideBar-tabIcon';
   browser.title.caption = 'File Browser';
@@ -292,22 +303,21 @@ function activateBrowser(
       });
 
     // Whether to automatically navigate to a document's current directory
-    labShell.currentChanged.connect((_, change) => {
+    labShell.currentChanged.connect(async (_, change) => {
       if (navigateToCurrentDirectory && change.newValue) {
         const { newValue } = change;
         const context = docManager.contextForWidget(newValue);
         if (context) {
           const { path } = context;
-          Private.navigateToPath(path, factory)
-            .then(() => {
-              labShell.currentWidget.activate();
-            })
-            .catch((reason: any) => {
-              console.warn(
-                `${CommandIDs.navigate} failed to open: ${path}`,
-                reason
-              );
-            });
+          try {
+            await Private.navigateToPath(path, factory);
+            labShell.currentWidget.activate();
+          } catch (reason) {
+            console.warn(
+              `${CommandIDs.goToPath} failed to open: ${path}`,
+              reason
+            );
+          }
         }
       }
     });
@@ -347,7 +357,9 @@ function addCommands(
   app: JupyterFrontEnd,
   factory: IFileBrowserFactory,
   labShell: ILabShell,
-  docManager: IDocumentManager
+  docManager: IDocumentManager,
+  palette: ICommandPalette | null,
+  mainMenu: IMainMenu | null
 ): void {
   const registry = app.docRegistry;
   const { commands } = app;
@@ -425,24 +437,57 @@ function addCommands(
     }
   });
 
-  commands.addCommand(CommandIDs.navigate, {
-    execute: args => {
+  commands.addCommand(CommandIDs.goToPath, {
+    execute: async args => {
       const path = (args.path as string) || '';
-      return Private.navigateToPath(path, factory)
-        .then(model => {
-          if (model.type === 'directory') {
-            return commands.execute(CommandIDs.showBrowser, { path });
-          }
-          return commands.execute('docmanager:open', { path });
-        })
-        .catch((reason: any) => {
-          console.warn(
-            `${CommandIDs.navigate} failed to open: ${path}`,
-            reason
-          );
+      try {
+        await Private.navigateToPath(path, factory);
+      } catch (reason) {
+        console.warn(`${CommandIDs.goToPath} failed to go to: ${path}`, reason);
+      }
+
+      return commands.execute(CommandIDs.showBrowser, { path });
+    }
+  });
+
+  commands.addCommand(CommandIDs.openPath, {
+    label: () => 'Open From Path...',
+    caption: 'Open from path',
+    isEnabled: () => true,
+    execute: async () => {
+      const path = await askUserForPath(docManager.services.contents);
+      if (!path) {
+        return;
+      }
+      try {
+        const item = await docManager.services.contents.get(path, {
+          content: false
+        });
+        await commands.execute(CommandIDs.goToPath, { path });
+        if (item.type === 'directory') {
+          return;
+        }
+        return commands.execute('docmanager:open', { path });
+      } catch {
+        return showDialog({
+          title: 'Cannot open',
+          body: 'No such file or directory found',
+          buttons: [Dialog.okButton()]
         });
+      }
     }
   });
+  // Add the openPath command to the command palette
+  if (palette) {
+    palette.addItem({
+      command: CommandIDs.openPath,
+      category: 'File Operations'
+    });
+  }
+  // Add the openPath command to the File menu
+  if (mainMenu) {
+    mainMenu.fileMenu.addGroup([{ command: CommandIDs.openPath }], 0);
+  }
 
   commands.addCommand(CommandIDs.open, {
     execute: args => {
@@ -850,7 +895,7 @@ namespace Private {
       if (!browserForPath) {
         // warn that no filebrowser could be found for this driveName
         console.warn(
-          `${CommandIDs.navigate} failed to find filebrowser for path: ${path}`
+          `${CommandIDs.goToPath} failed to find filebrowser for path: ${path}`
         );
         return;
       }
@@ -863,9 +908,9 @@ namespace Private {
   }
 
   /**
-   * Navigate to a path.
+   * Navigate to a path or the path containing a file.
    */
-  export function navigateToPath(
+  export async function navigateToPath(
     path: string,
     factory: IFileBrowserFactory
   ): Promise<Contents.IModel> {
@@ -873,23 +918,15 @@ namespace Private {
     const { services } = browserForPath.model.manager;
     const localPath = services.contents.localPath(path);
 
-    return services.ready
-      .then(() => services.contents.get(path, { content: false }))
-      .then(value => {
-        const { model } = browserForPath;
-        const { restored } = model;
-
-        if (value.type === 'directory') {
-          return restored.then(() => {
-            model.cd(`/${localPath}`);
-            return value;
-          });
-        }
-
-        return restored.then(() => {
-          model.cd(`/${PathExt.dirname(localPath)}`);
-          return value;
-        });
-      });
+    await services.ready;
+    let item = await services.contents.get(path, { content: false });
+    const { model } = browserForPath;
+    await model.restored;
+    if (item.type === 'directory') {
+      await model.cd(`/${localPath}`);
+    } else {
+      await model.cd(`/${PathExt.dirname(localPath)}`);
+    }
+    return item;
   }
 }

+ 3 - 0
packages/filebrowser-extension/tsconfig.json

@@ -24,6 +24,9 @@
     {
       "path": "../launcher"
     },
+    {
+      "path": "../mainmenu"
+    },
     {
       "path": "../services"
     },

+ 70 - 0
packages/filebrowser/src/dialog.ts

@@ -0,0 +1,70 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { Dialog, showDialog } from '@jupyterlab/apputils';
+
+import { Widget } from '@phosphor/widgets';
+/*
+ * A widget used to open a path based on user input.
+ */
+class OpenPathWidget extends Widget {
+  /**
+   * Construct a new open path widget.
+   */
+  constructor() {
+    super({ node: Private.createOpenPathNode() });
+  }
+
+  /**
+   * Get the value of the widget.
+   */
+  getValue(): string {
+    return this.inputNode.value;
+  }
+
+  /**
+   * Get the input text node.
+   */
+  get inputNode(): HTMLInputElement {
+    return this.node.getElementsByTagName('input')[0] as HTMLInputElement;
+  }
+}
+
+/**
+ * Create the node for the open path handler.
+ */
+export async function askUserForPath(
+  contentsManager: any
+): Promise<string | undefined> {
+  const result = await showDialog({
+    title: 'Open Path',
+    body: new OpenPathWidget(),
+    buttons: [Dialog.cancelButton(), Dialog.okButton({ label: 'OPEN' })],
+    focusNodeSelector: 'input'
+  });
+  if (result.button.label === 'OPEN') {
+    return result.value;
+  }
+}
+
+/**
+ * A namespace for private data.
+ */
+namespace Private {
+  /**
+   * Create the node for an open path widget.
+   */
+  export function createOpenPathNode(): HTMLElement {
+    let body = document.createElement('div');
+    let existingLabel = document.createElement('label');
+    existingLabel.textContent = 'Path:';
+
+    let input = document.createElement('input');
+    input.value = '';
+    input.placeholder = '/path/relative/to/jlab/root';
+
+    body.appendChild(existingLabel);
+    body.appendChild(input);
+    return body;
+  }
+}

+ 1 - 0
packages/filebrowser/src/index.ts

@@ -3,6 +3,7 @@
 
 export * from './browser';
 export * from './crumbs';
+export * from './dialog';
 export * from './tokens';
 export * from './listing';
 export * from './model';