Преглед изворни кода

Merge pull request #1714 from vidartf/simpler-cm-access

Simpler CodeMirror editor customization
Steven Silvester пре 8 година
родитељ
комит
fcadaa8196

+ 27 - 2
src/codeeditor/editor.ts

@@ -197,6 +197,15 @@ namespace CodeEditor {
    */
   export
   class Model implements IModel {
+    /**
+     * Construct a new Model.
+     */
+    constructor(options?: Model.IOptions) {
+      options = options || {};
+      this._value = new ObservableString(options.value);
+      this._mimetype = options.mimeType || 'text/plain';
+    }
+
     /**
      * A signal emitted when a mimetype changes.
      */
@@ -255,9 +264,9 @@ namespace CodeEditor {
       this._value.dispose();
     }
 
-    private _value = new ObservableString();
+    private _value: ObservableString;
     private _selections = new ObservableMap<ITextSelection[]>();
-    private _mimetype = 'text/plain';
+    private _mimetype: string;
     private _isDisposed = false;
   }
 
@@ -547,4 +556,20 @@ namespace CodeEditor {
     */
     selectionStyle?: CodeEditor.ISelectionStyle;
   }
+
+  export
+  namespace Model {
+    export
+    interface IOptions {
+      /**
+       * The initial value of the model.
+       */
+      value?: string;
+
+      /**
+       * The mimetype of the model.
+       */
+      mimeType?: string;
+    }
+  }
 }

+ 49 - 12
src/codemirror/factory.ts

@@ -1,6 +1,9 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
+import * as CodeMirror
+  from 'codemirror';
+
 import {
   CodeEditor, IEditorFactoryService
 } from '../codeeditor';
@@ -17,10 +20,10 @@ export
 class CodeMirrorEditorFactory implements IEditorFactoryService {
 
   /**
-   * Create a new editor for inline code.
+   * Construct an IEditorFactoryService for CodeMirrorEditors.
    */
-  newInlineEditor(options: CodeEditor.IOptions): CodeEditor.IEditor {
-    return new CodeMirrorEditor(options, {
+  constructor(codeMirrorOptions?: CodeMirror.EditorConfiguration) {
+    this.inlineCodeMirrorOptions = {
       extraKeys: {
         'Cmd-Right': 'goLineRight',
         'End': 'goLineRight',
@@ -32,21 +35,55 @@ class CodeMirrorEditorFactory implements IEditorFactoryService {
         'Cmd-/': 'toggleComment',
         'Ctrl-/': 'toggleComment',
       }
-    });
-  }
-
-  /**
-   * Create a new editor for a full document.
-   */
-  newDocumentEditor(options: CodeEditor.IOptions): CodeEditor.IEditor {
-    return new CodeMirrorEditor(options, {
+    };
+    this.documentCodeMirrorOptions = {
       extraKeys: {
         'Tab': 'indentMore',
         'Shift-Enter': () => { /* no-op */ }
       },
       lineNumbers: true,
       lineWrapping: true
-    });
+    };
+    if (codeMirrorOptions !== undefined) {
+      // Note: If codeMirrorOptions include `extraKeys`,
+      // existing option will be overwritten.
+      Private.assign(this.inlineCodeMirrorOptions, codeMirrorOptions);
+      Private.assign(this.documentCodeMirrorOptions, codeMirrorOptions);
+    }
+  }
+
+  /**
+   * Create a new editor for inline code.
+   */
+  newInlineEditor(options: CodeEditor.IOptions): CodeEditor.IEditor {
+    return new CodeMirrorEditor(options, this.inlineCodeMirrorOptions);
+  }
+
+  /**
+   * Create a new editor for a full document.
+   */
+  newDocumentEditor(options: CodeEditor.IOptions): CodeEditor.IEditor {
+    return new CodeMirrorEditor(options, this.documentCodeMirrorOptions);
   }
 
+  protected inlineCodeMirrorOptions: CodeMirror.EditorConfiguration;
+  protected documentCodeMirrorOptions: CodeMirror.EditorConfiguration;
+
+}
+
+
+namespace Private {
+  // Replace with Object.assign when available.
+  export
+  function assign<T>(target: T, ...configs: any[]): T {
+    for (const source of configs) {
+      if (source) {
+        Object.keys(source).forEach(key => {
+          (target as any)[key] = (source as any)[key];
+        });
+      }
+    }
+
+    return target;
+  }
 }

+ 2 - 5
src/editorwidget/widget.ts

@@ -25,10 +25,6 @@ import {
   CodeEditorWidget
 } from '../codeeditor/widget';
 
-import {
-  Widget
-} from 'phosphor/lib/ui/widget';
-
 
 /**
  * The class name added to a dirty widget.
@@ -200,7 +196,8 @@ class EditorWidgetFactory extends ABCWidgetFactory<EditorWidget, DocumentRegistr
    * Create a new widget given a context.
    */
   protected createNewWidget(context: DocumentRegistry.CodeContext): EditorWidget {
-    let func = this._services.factoryService.newDocumentEditor
+    let func = this._services.factoryService.newDocumentEditor.bind(
+      this._services.factoryService);
     let factory: CodeEditor.Factory = options => {
       options.lineNumbers = true;
       options.readOnly = false;

+ 16 - 0
test/src/codeeditor/editor.spec.ts

@@ -23,6 +23,22 @@ describe('CodeEditor.Model', () => {
 
     it('should create a CodeEditor Model', () => {
       expect(model).to.be.a(CodeEditor.Model);
+      expect(model.value.text).to.equal('');
+    });
+
+    it('should create a CodeEditor Model with an initial value', () => {
+      let other = new CodeEditor.Model({value: 'Initial text here'});
+      expect(other).to.be.a(CodeEditor.Model);
+      expect(other.value.text).to.equal('Initial text here');
+      other.dispose();
+    });
+
+    it('should create a CodeEditor Model with an initial mimetype', () => {
+      let other = new CodeEditor.Model({value: 'import this', mimeType: 'text/x-python'});
+      expect(other).to.be.a(CodeEditor.Model);
+      expect(other.mimeType).to.equal('text/x-python');
+      expect(other.value.text).to.equal('import this');
+      other.dispose();
     });
 
   });

+ 105 - 0
test/src/codemirror/factory.spec.ts

@@ -0,0 +1,105 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import expect = require('expect.js');
+
+import {
+  CodeEditor
+} from '../../../lib/codeeditor';
+
+import {
+  CodeMirrorEditorFactory, CodeMirrorEditor
+} from '../../../lib/codemirror';
+
+
+class ExposeCodeMirrorEditorFactory extends CodeMirrorEditorFactory {
+  public inlineCodeMirrorOptions: CodeMirror.EditorConfiguration;
+  public documentCodeMirrorOptions: CodeMirror.EditorConfiguration;
+}
+
+
+describe('CodeMirrorEditorFactory', () => {
+  let host: HTMLElement;
+  let model: CodeEditor.IModel;
+
+  const options: CodeMirror.EditorConfiguration = {
+    lineNumbers: false,
+    lineWrapping: true,
+    extraKeys: {
+      'Ctrl-Tab': 'indentAuto',
+    },
+    undoDepth: 5,
+  };
+
+  beforeEach(() => {
+    host = document.createElement('div');
+    document.body.appendChild(host);
+    model = new CodeEditor.Model();
+  });
+
+  afterEach(() => {
+    document.body.removeChild(host);
+  });
+
+  describe('#constructor()', () => {
+
+    it('should create a CodeMirrorEditorFactory', () => {
+      let factory = new CodeMirrorEditorFactory();
+      expect(factory).to.be.a(CodeMirrorEditorFactory);
+    });
+
+    it('should create a CodeMirrorEditorFactory', () => {
+
+      let factory = new ExposeCodeMirrorEditorFactory(options);
+      expect(factory).to.be.a(CodeMirrorEditorFactory);
+      expect(factory.inlineCodeMirrorOptions).to.eql(options);
+      expect(factory.documentCodeMirrorOptions).to.eql(options);
+    });
+
+  });
+
+  describe('#newInlineEditor', () => {
+
+    it('should create a new editor', () => {
+      let factory = new CodeMirrorEditorFactory();
+      let editor = factory.newInlineEditor({host, model});
+      expect(editor).to.be.a(CodeMirrorEditor);
+      editor.dispose();
+    });
+
+    it('should create a new editor with given options', () => {
+      let factory = new CodeMirrorEditorFactory(options);
+      let editor = factory.newInlineEditor({host, model});
+      expect(editor).to.be.a(CodeMirrorEditor);
+      let inner = (editor as CodeMirrorEditor).editor;
+      for (let key of Object.keys(options)) {
+        expect(inner.getOption(key)).to.equal((options as any)[key]);
+      }
+      editor.dispose();
+    });
+
+  });
+
+  describe('#newDocumentEditor', () => {
+
+    it('should create a new editor', () => {
+      let factory = new CodeMirrorEditorFactory();
+      let editor = factory.newDocumentEditor({host, model});
+      expect(editor).to.be.a(CodeMirrorEditor);
+      editor.dispose();
+    });
+
+    it('should create a new editor with given options', () => {
+      let factory = new CodeMirrorEditorFactory(options);
+      let editor = factory.newDocumentEditor({host, model});
+      expect(editor).to.be.a(CodeMirrorEditor);
+      let inner = (editor as CodeMirrorEditor).editor;
+      for (let key of Object.keys(options)) {
+        expect(inner.getOption(key)).to.equal((options as any)[key]);
+      }
+      editor.dispose();
+    });
+
+  });
+
+});

+ 0 - 4
test/src/editorwidget/widget.spec.ts

@@ -7,10 +7,6 @@ import {
   ServiceManager, utils
 } from '@jupyterlab/services';
 
-import {
-  Widget
-} from 'phosphor/lib/ui/widget';
-
 import {
   CodeMirrorEditorFactory, CodeMirrorMimeTypeService
 } from '../../../lib/codemirror';

+ 1 - 0
test/src/index.ts

@@ -11,6 +11,7 @@ import './codeeditor/editor.spec';
 import './codeeditor/widget.spec';
 
 import './codemirror/editor.spec';
+import './codemirror/factory.spec';
 
 import './commandlinker/commandlinker.spec';