瀏覽代碼

Merge pull request #12 from blink1073/file-opener

File opener plugin
Dave Willmer 9 年之前
父節點
當前提交
ce9b5135b0

+ 2 - 1
examples/lab/src/index.ts

@@ -12,7 +12,8 @@ function main() {
     require('phosphide/lib/commandregistry/plugin'),
     require('phosphide/lib/commandpalette/plugin'),
     require('../../lib/terminal/plugin'),
-    require('../../lib/editor/plugin'),
+    require('../../lib/fileopener/plugin'),
+    require('../../lib/filehandler/plugin'),
     require('../../lib/filebrowser/plugin'),
     require('../../lib/services/plugin'),
     require('./plugin')

+ 12 - 7
examples/lab/src/plugin.ts

@@ -3,7 +3,7 @@
 'use strict';
 
 import {
-  FileBrowser
+  FileBrowserWidget
 } from 'jupyter-js-filebrowser';
 
 import {
@@ -27,7 +27,7 @@ import {
 } from 'phosphor-widget';
 
 import {
-  ITerminalProvider, IFileBrowserProvider, IServicesProvider
+  ITerminalProvider, IFileBrowserProvider, IServicesProvider, IFileOpener
 } from '../../lib';
 
 
@@ -53,27 +53,29 @@ class DefaultHandler {
   /**
    * The dependencies required by the default plugin.
    */
-  static requires: Token<any>[] = [IAppShell, ITerminalProvider, ICommandPalette, ICommandRegistry, IFileBrowserProvider, IServicesProvider];
+  static requires: Token<any>[] = [IAppShell, ITerminalProvider, ICommandPalette, ICommandRegistry, IFileBrowserProvider, IServicesProvider, IFileOpener];
 
   /**
    * Create a default plugin instance..
    */
   static create(shell: IAppShell, term: ITerminalProvider, palette: ICommandPalette, registry: ICommandRegistry, browser: IFileBrowserProvider,
-    services: IServicesProvider): DefaultHandler {
-    return new DefaultHandler(shell, term, palette, registry, browser, services);
+    services: IServicesProvider, opener: IFileOpener): DefaultHandler {
+    return new DefaultHandler(shell, term, palette, registry, browser,
+      services, opener);
   }
 
   /**
    * Construct a new default plugin.
    */
   constructor(shell: IAppShell, term: ITerminalProvider, palette: ICommandPalette, registry: ICommandRegistry, browser: IFileBrowserProvider,
-    services: IServicesProvider) {
+    services: IServicesProvider, opener: IFileOpener) {
     this._shell = shell;
     this._term = term;
     this._palette = palette;
     this._registry = registry;
     this._browser = browser.fileBrowser;
     this._services = services;
+    this._opener = opener;
   }
 
   /**
@@ -128,10 +130,12 @@ class DefaultHandler {
     }
     this._palette.add([section]);
 
+    /*
     let term = this._term.createTerminal();
     term.color = 'black';
     term.background = 'white';
     this._shell.addToMainArea(term);
+    */
 
     // Start a default session.
     let contents = this._services.contentsManager;
@@ -147,6 +151,7 @@ class DefaultHandler {
   private _shell: IAppShell = null;
   private _palette: ICommandPalette = null;
   private _registry: ICommandRegistry = null;
-  private _browser: FileBrowser = null;
+  private _browser: FileBrowserWidget = null;
   private _services: IServicesProvider = null;
+  private _opener: IFileOpener = null;
 }

+ 6 - 5
package.json

@@ -6,14 +6,15 @@
   "typings": "lib/index.d.ts",
   "dependencies": {
     "codemirror": "^5.10.0",
-    "jupyter-js-filebrowser": "^0.2.8",
-    "jupyter-js-services": "^0.3.0-alpha",
-    "jupyter-js-terminal": "^0.1.3",
-    "jupyter-js-utils": "^0.1.0",
+    "jupyter-js-filebrowser": "^0.3.0",
+    "jupyter-js-services": "^0.3.0",
+    "jupyter-js-terminal": "^0.1.9",
+    "jupyter-js-utils": "^0.2.4",
     "phosphide": "^0.0.8",
     "phosphor-codemirror": "^0.0.1",
     "phosphor-command": "^0.5.0",
-    "phosphor-di": "^0.9.0"
+    "phosphor-di": "^0.9.0",
+    "phosphor-properties": "^2.0.0"
   },
   "devDependencies": {
     "css-loader": "^0.23.1",

+ 0 - 31
src/editor/codemirror-ipython.ts

@@ -1,31 +0,0 @@
-// IPython mode is just a slightly altered Python Mode with `?` beeing a extra
-// single operator. Here we define `ipython` mode in the require `python`
-// callback to auto-load python mode, which is more likely not the best things
-// to do, but at least the simple one for now.
-"use strict";
-
-import * as CodeMirror
-  from 'codemirror';
-
-import "codemirror/mode/python/python";
-
-
-CodeMirror.defineMode("ipython", (config: CodeMirror.EditorConfiguration, modeOptions: any) => {
-    var pythonConf: any = {};
-    for (var prop in modeOptions) {
-      if (modeOptions.hasOwnProperty(prop)) {
-        (pythonConf as any)[prop] = (modeOptions as any)[prop];
-      }
-    }
-    pythonConf.name = 'python';
-    pythonConf.singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\\?]");
-    if (pythonConf.version === 3) {
-      pythonConf.identifiers = new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*");
-    } else if (pythonConf.version === 2) {
-      pythonConf.identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
-    }
-    return CodeMirror.getMode(config, pythonConf);
-}, 'python');
-
-CodeMirror.defineMIME("text/x-ipython", "ipython");
-

+ 0 - 62
src/editor/index.ts

@@ -1,62 +0,0 @@
-// Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
-'use strict';
-
-import * as CodeMirror
-  from 'codemirror';
-
-import {
-  CodeMirrorWidget
-} from 'phosphor-codemirror';
-
-import {
-  Token
-} from 'phosphor-di';
-
-
-/**
- * A handler for creating and manipulating Jupyter editors.
- */
-export
-interface IEditorHandler {
-
-  /**
-   * Create a new IEditor instance.
-   */
-  createEditor(options?: CodeMirror.EditorConfiguration): CodeMirrorWidget;
-
-  /**
-   * Set the editor mode by name.  This will lode the mode file
-   * as needed.
-   */
-  setModeByName(widget: CodeMirrorWidget, mode: string): void;
-
-  /**
-   * Set the editor mode by file name.  This will lode the mode file
-   * as needed.
-   */
-  setModeByFileName(widget: CodeMirrorWidget, filename: string): void;
-
-  /**
-   * Set the editor mode by mime type.  This will lode the mode file
-   * as needed.
-   */
-  setModeByMIMEType(widget: CodeMirrorWidget, mime: string): void;
-
-  /**
-   * A convenience method to get the text from the editor.
-   */
-  getText(widget: CodeMirrorWidget, text: string): string;
-
-  /**
-   * A convenience method to set the text on the editor.
-   */
-  setText(widget: CodeMirrorWidget, text: string): void;
-}
-
-
-/**
- * The dependency token for the `IEditorHandler` interface.
- */
-export
-const IEditorHandler = new Token<IEditorHandler>('jupyter-js-plugins.IEditorHandler');

+ 0 - 10
src/editor/plugin.css

@@ -1,10 +0,0 @@
-/*-----------------------------------------------------------------------------
-| Copyright (c) Jupyter Development Team.
-| Distributed under the terms of the Modified BSD License.
-|----------------------------------------------------------------------------*/
-.p-CodeMirrorWidget {
-  min-width: 50px;
-  min-height: 50px;
-  border: 1px solid #C0C0C0;
-  z-index: 0;
-}

+ 0 - 140
src/editor/plugin.ts

@@ -1,140 +0,0 @@
-// Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
-'use strict';
-
-import * as CodeMirror
-  from 'codemirror';
-
-import {
-  getConfigOption
-} from 'jupyter-js-utils';
-
-import {
-  CodeMirrorWidget
-} from 'phosphor-codemirror';
-
-import {
-  Container, Token
-} from 'phosphor-di';
-
-import {
-  IEditorHandler
-} from './index';
-
-import './plugin.css';
-
-
-/**
- * Bundle common modes.
- */
-import 'codemirror/mode/css/css';
-import 'codemirror/mode/javascript/javascript';
-import 'codemirror/mode/julia/julia';
-import 'codemirror/mode/python/python';
-import 'codemirror/mode/r/r';
-import 'codemirror/mode/markdown/markdown';
-import './codemirror-ipython';
-
-
-/**
- * Register the plugin contributions.
- *
- * @param container - The di container for type registration.
- *
- * #### Notes
- * This is called automatically when the plugin is loaded.
- */
-export
-function register(container: Container): void {
-  container.register(IEditorHandler, EditorHandler);
-}
-
-
-/**
- * An implemenation of an IEditorHandler.
- */
-class EditorHandler implements IEditorHandler {
-
-  /**
-   * The dependencies required by the editor handler.
-   */
-  static requires: Token<any>[] = [];
-
-  /**
-   * Create a new editor handler instance.
-   */
-  static create(): IEditorHandler {
-    return new EditorHandler();
-  }
-
-  /**
-   * Create a new IEditor instance.
-   */
-  createEditor(options?: CodeMirror.EditorConfiguration): CodeMirrorWidget {
-    return new CodeMirrorWidget(options);
-  }
-
-  /**
-   * A convenience method to get the text from the editor.
-   */
-  getText(widget: CodeMirrorWidget, text: string): string {
-    return widget.editor.getDoc().getValue();
-  }
-
-  /**
-   * A convenience method to set the text on the editor.
-   */
-  setText(widget: CodeMirrorWidget, text: string): void {
-    widget.editor.getDoc().setValue(text);
-  }
-
-  /**
-   * Set the editor mode by name.  This will lode the mode file
-   * as needed.
-   */
-  setModeByName(widget: CodeMirrorWidget, mode: string): void {
-    let info = CodeMirror.findModeByName(mode);
-    if (info) {
-      loadCodeMirrorMode(widget.editor, info.mode, info.mime);
-    }
-  }
-
-  /**
-   * Set the editor mode by file name.  This will lode the mode file
-   * as needed.
-   */
-  setModeByFileName(widget: CodeMirrorWidget, filename: string): void {
-    let info = CodeMirror.findModeByFileName(filename);
-    if (info) {
-      loadCodeMirrorMode(widget.editor, info.mode, info.mime);
-    }
-  }
-
-  /**
-   * Set the editor mode by mime type.  This will lode the mode file
-   * as needed.
-   */
-  setModeByMIMEType(widget: CodeMirrorWidget, mime: string): void {
-    let info = CodeMirror.findModeByMIME(mime);
-    if (info) {
-      loadCodeMirrorMode(widget.editor, info.mode, info.mime);
-    }
-  }
-}
-
-/**
- * Load and set a CodeMirror mode.
- *
- * #### Notes
- * This assumes WebPack as the module loader.
- */
-function loadCodeMirrorMode(editor: CodeMirror.Editor, mode: string, mime: string): void {
-  if (CodeMirror.modes.hasOwnProperty(mode)) {
-    editor.setOption('mode', mime);
-  } else {
-    // Load the full codemirror mode bundle.
-    require([`codemirror/mode/${mode}/${mode}.js`], () => {
-      editor.setOption('mode', mime);
-    });
-  }
-}

+ 0 - 0
src/editor/test.ts


+ 2 - 2
src/filebrowser/index.ts

@@ -7,7 +7,7 @@ import {
 } from 'phosphor-di';
 
 import {
-  FileBrowser
+  FileBrowserWidget
 } from 'jupyter-js-filebrowser';
 
 
@@ -16,7 +16,7 @@ import {
  */
 export
 interface IFileBrowserProvider {
-  fileBrowser: FileBrowser;
+  fileBrowser: FileBrowserWidget;
 }
 
 

+ 13 - 30
src/filebrowser/plugin.ts

@@ -3,7 +3,7 @@
 'use strict';
 
 import {
-  FileBrowser, FileBrowserModel
+  FileBrowserWidget, FileBrowserModel
 } from 'jupyter-js-filebrowser';
 
 import {
@@ -19,7 +19,7 @@ import {
 } from './index';
 
 import {
-  IEditorHandler, IServicesProvider
+  IServicesProvider
 } from '../index';
 
 import './plugin.css';
@@ -47,22 +47,24 @@ class FileBrowserProvider implements IFileBrowserProvider {
   /**
    * The dependencies required by the application shell.
    */
-  static requires: Token<any>[] = [IAppShell, IServicesProvider, IEditorHandler];
+  static requires: Token<any>[] = [IServicesProvider];
 
   /**
    * Create a new application shell instance.
    */
-  static create(shell: IAppShell, services: IServicesProvider, editor: IEditorHandler): IFileBrowserProvider {
-    return new FileBrowserProvider(shell, services, editor);
+  static create(services: IServicesProvider): IFileBrowserProvider {
+    return new FileBrowserProvider(services);
   }
 
   /**
    * Construct a new filebrowser provider instance.
    */
-  constructor(shell: IAppShell, services: IServicesProvider, editor: IEditorHandler) {
-    this._shell = shell;
-    this._editor = editor;
-    this._services = services;
+  constructor(services: IServicesProvider) {
+    let contents = services.contentsManager;
+    let sessions = services.notebookSessionManager;
+    let model = new FileBrowserModel(contents, sessions);
+    this._browser = new FileBrowserWidget(model);
+    this._browser.title.text = 'Files';
   }
 
   /**
@@ -71,28 +73,9 @@ class FileBrowserProvider implements IFileBrowserProvider {
    * #### Notes
    * This is a read-only property.
    */
-  get fileBrowser(): FileBrowser {
-    if (this._browser === null) {
-      let contents = this._services.contentsManager;
-      let sessions = this._services.notebookSessionManager;
-      let model = new FileBrowserModel('', contents, sessions);
-      this._browser = new FileBrowser(model);
-      this._browser.title.text = 'Files';
-      model.changed.connect((instance, change) => {
-      if (change.name === 'open' && change.newValue.type === 'file') {
-        let newEditor = this._editor.createEditor();
-        this._editor.setModeByFileName(newEditor, change.newValue.name);
-        this._editor.setText(newEditor, change.newValue.content);
-        newEditor.title.text = change.newValue.name;
-        this._shell.addToMainArea(newEditor);
-      }
-    });
-    }
+  get fileBrowser(): FileBrowserWidget {
     return this._browser;
   }
 
-  private _shell: IAppShell = null;
-  private _editor: IEditorHandler = null;
-  private _browser: FileBrowser = null;
-  private _services: IServicesProvider;
+  private _browser: FileBrowserWidget = null;
 }

+ 69 - 0
src/filehandler/plugin.ts

@@ -0,0 +1,69 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+'use strict';
+
+import {
+  FileBrowserWidget, FileHandler
+} from 'jupyter-js-filebrowser';
+
+import {
+  Container, Token
+} from 'phosphor-di';
+
+import {
+  Widget
+} from 'phosphor-widget';
+
+import {
+  IServicesProvider, IFileBrowserProvider, IFileOpener, IFileHandler
+} from '../index';
+
+
+/**
+ * Register the plugin contributions.
+ *
+ * @param container - The di container for type registration.
+ *
+ * #### Notes
+ * This is called automatically when the plugin is loaded.
+ */
+export
+function resolve(container: Container): Promise<void> {
+  return container.resolve(DefaultFileHandler).then(handler => handler.run());
+}
+
+
+/**
+ * An implementation of an IFileHandler.
+ */
+class DefaultFileHandler {
+
+  /**
+   * The dependencies required by the file handler.
+   */
+  static requires: Token<any>[] = [IServicesProvider, IFileOpener];
+
+  /**
+   * Create a new file handler instance.
+   */
+  static create(services: IServicesProvider, opener: IFileOpener): DefaultFileHandler {
+    return new DefaultFileHandler(services, opener);
+  }
+
+  /**
+   * Construct a new DefaultFileHandler.
+   */
+  constructor(services: IServicesProvider, opener: IFileOpener) {
+    this._services = services;
+    this._opener = opener;
+  }
+
+  run(): void {
+    this._handler = new FileHandler(this._services.contentsManager);
+    this._opener.register(this._handler, true);
+  }
+
+  private _handler: IFileHandler = null;
+  private _services: IServicesProvider = null;
+  private _opener: IFileOpener = null;
+}

+ 52 - 0
src/fileopener/index.ts

@@ -0,0 +1,52 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+'use strict';
+
+import {
+  Widget
+} from 'phosphor-widget';
+
+import {
+  Token
+} from 'phosphor-di';
+
+
+/**
+ * An interface for a file handler
+ */
+export
+interface IFileHandler {
+  /**
+   * he list of file extensions supported by the handler.
+   */
+  fileExtensions: string[];
+
+  /**
+   * Open the file and return a populated widget.
+   */
+  open(path: string): Promise<Widget>;
+
+  /**
+   * Close the file widget.
+   */
+  close(widget: Widget): boolean;
+}
+
+
+/**
+ * An interface for a file opener.
+ */
+export
+interface IFileOpener {
+  /**
+   * Register a file opener.
+   */
+  register(handler: IFileHandler, isDefault: boolean): void;
+}
+
+
+/**
+ * The dependency token for the `IFileOpener` interface.
+ */
+export
+const IFileOpener = new Token<IFileOpener>('jupyter-js-plugins.IFileOpener');

+ 134 - 0
src/fileopener/plugin.ts

@@ -0,0 +1,134 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+'use strict';
+
+import {
+  FileBrowserWidget, FileHandler
+} from 'jupyter-js-filebrowser';
+
+import {
+  IAppShell
+} from 'phosphide';
+
+import {
+  Container, Token
+} from 'phosphor-di';
+
+import {
+  Property
+} from 'phosphor-properties';
+
+import {
+  Widget
+} from 'phosphor-widget';
+
+import {
+  IFileBrowserProvider
+} from '../index';
+
+import {
+  IFileOpener, IFileHandler
+} from './index';
+
+
+/**
+ * Register the plugin contributions.
+ */
+export
+function register(container: Container): void {
+  container.register(IFileOpener, FileOpener);
+}
+
+
+/**
+ * An implementation on an IFileOpener.
+ */
+class FileOpener implements IFileOpener {
+
+  /**
+   * The dependencies required by the file opener.
+   */
+  static requires: Token<any>[] = [IAppShell, IFileBrowserProvider];
+
+  /**
+   * Create a new file opener instance.
+   */
+  static create(appShell: IAppShell, browserProvider: IFileBrowserProvider): IFileOpener {
+    return new FileOpener(appShell, browserProvider);
+  }
+
+  /**
+   * Construct a new file opener.
+   */
+  constructor(appShell: IAppShell, browserProvider: IFileBrowserProvider) {
+    browserProvider.fileBrowser.openRequested.connect(this._openRequested,
+      this);
+    this._appShell = appShell;
+  }
+
+  /**
+   * Register a file handler.
+   */
+  register(handler: IFileHandler, isDefault: boolean) {
+    this._handlers.push(handler);
+    isDefaultProperty.set(handler, isDefault);
+  }
+
+  /**
+   * Handle an `openRequested` signal by invoking the appropriate handler.
+   */
+  private _openRequested(browser: FileBrowserWidget, path: string): void {
+    if (this._handlers.length === 0) {
+      return;
+    }
+    let ext = '.' + path.split('.').pop();
+    let handlers: IFileHandler[] = [];
+    // Look for matching file extensions.
+    for (let h of this._handlers) {
+      if (h.fileExtensions.indexOf(ext) !== -1) handlers.push(h);
+    }
+    // If there was only one match, use it.
+    if (handlers.length === 1) {
+      this._open(handlers[0], path);
+      return;
+
+    // If there were no matches, look for default handler(s).
+    } else if (handlers.length === 0) {
+      for (let h of this._handlers) {
+        if (isDefaultProperty.get(h)) handlers.push(h);
+      }
+    }
+
+    // If there we no matches, do nothing.
+    if (handlers.length == 0) {
+      console.warn('Could not open file ')
+
+    // If there was one handler, use it.
+    } else if (handlers.length === 1) {
+      this._open(handlers[0], path);
+    } else {
+      // There are more than one possible handlers.
+      // TODO: Ask the user to choose one.
+      this._open(handlers[0], path);
+    }
+  }
+
+  private _open(handler: IFileHandler, path: string): void {
+    handler.open(path).then(widget => {
+      this._appShell.addToMainArea(widget);
+    });
+  }
+
+  private _handlers: IFileHandler[] = [];
+  private _appShell: IAppShell = null;
+}
+
+
+/**
+ * An attached property for whether a file handler is a default.
+ */
+const
+isDefaultProperty = new Property<IFileHandler, boolean>({
+  name: 'isDefault',
+  value: false,
+});

+ 1 - 1
src/index.ts

@@ -2,7 +2,7 @@
 // Distributed under the terms of the Modified BSD License.
 'use strict';
 
-export * from './editor/index';
 export * from './filebrowser/index';
+export * from './fileopener/index';
 export * from './services/index';
 export * from './terminal/index';

+ 3 - 3
src/tsconfig.json

@@ -13,11 +13,11 @@
     "../typings/requirejs/requirejs.d.ts",
     "../typings/codemirror/codemirror.d.ts",
     "index.ts",
-    "editor/index.ts",
-    "editor/codemirror-ipython.ts",
-    "editor/plugin.ts",
     "filebrowser/index.ts",
     "filebrowser/plugin.ts",
+    "filehandler/plugin.ts",
+    "fileopener/index.ts",
+    "fileopener/plugin.ts",
     "services/index.ts",
     "services/plugin.ts",
     "terminal/index.ts",