浏览代码

Clean up the completer API

Steven Silvester 8 年之前
父节点
当前提交
48b69f17dc
共有 3 个文件被更改,包括 219 次插入198 次删除
  1. 1 1
      src/completer/handler.ts
  2. 79 182
      src/completer/model.ts
  3. 139 15
      src/completer/widget.ts

+ 1 - 1
src/completer/handler.ts

@@ -137,7 +137,7 @@ class CellCompleterHandler implements IDisposable {
     // Update the original request.
     model.original = request;
     // Update the options.
-    model.options = value.matches;
+    model.setOptions(value.matches);
     // Update the cursor.
     model.cursor = { start: value.cursor_start, end: value.cursor_end };
   }

+ 79 - 182
src/completer/model.ts

@@ -2,17 +2,17 @@
 // Distributed under the terms of the Modified BSD License.
 
 import {
-  deepEqual, JSONObject
+  IIterator, IterableOrArrayLike, iter, map, toArray
+} from 'phosphor/lib/algorithm/iteration';
+
+import {
+  deepEqual
 } from 'phosphor/lib/algorithm/json';
 
 import {
   StringSearch
 } from 'phosphor/lib/algorithm/searching';
 
-import {
-  IDisposable
-} from 'phosphor/lib/core/disposable';
-
 import {
   clearSignalData, defineSignal, ISignal
 } from 'phosphor/lib/core/signaling';
@@ -21,182 +21,20 @@ import {
   ICompletionRequest, ITextChange
 } from '../notebook/cells/editor';
 
-
-/**
- * A filtered completion menu matching result.
- */
-interface ICompletionMatch {
-  /**
-   * The raw text of a completion match.
-   */
-  raw: string;
-
-  /**
-   * A score which indicates the strength of the match.
-   *
-   * A lower score is better. Zero is the best possible score.
-   */
-  score: number;
-
-  /**
-   * The highlighted text of a completion match.
-   */
-  text: string;
-}
-
-
-/**
- * An object describing a completion option injection into text.
- */
-export
-interface ICompletionPatch {
-  /**
-   * The patch text.
-   */
-  text: string;
-
-  /**
-   * The position in the text where cursor should be after patch application.
-   */
-  position: number;
-}
-
-
-/**
- * A completer menu item.
- */
-export
-interface ICompleterItem {
-  /**
-   * The highlighted, marked up text of a visible completer item.
-   */
-  text: string;
-
-  /**
-   * The raw text of a visible completer item.
-   */
-  raw: string;
-}
-
-
-/**
- * A cursor span.
- */
-export
-interface ICursorSpan extends JSONObject {
-  /**
-   * The start position of the cursor.
-   */
-  start: number;
-
-  /**
-   * The end position of the cursor.
-   */
-  end: number;
-}
-
-
-/**
- * The data model backing a code completer widget.
- */
-export
-interface ICompleterModel extends IDisposable {
-  /**
-   * A signal emitted when state of the completer menu changes.
-   */
-  stateChanged: ISignal<ICompleterModel, void>;
-
-  /**
-   * The current text change details.
-   */
-  current: ITextChange;
-
-  /**
-   * The cursor details that the API has used to return matching options.
-   */
-  cursor: ICursorSpan;
-
-  /**
-   * A flag that is true when the model value was modified by a subset match.
-   */
-  subsetMatch: boolean;
-
-  /**
-   * The list of visible items in the completer menu.
-   */
-  items: ICompleterItem[];
-
-  /**
-   * The unfiltered list of all available options in a completer menu.
-   */
-  options: string[];
-
-  /**
-   * The original completer request details.
-   */
-  original: ICompletionRequest;
-
-  /**
-   * The query against which items are filtered.
-   */
-  query: string;
-
-  /**
-   * Handle a text change.
-   */
-  handleTextChange(change: ITextChange): void;
-
-  /**
-   * Create a resolved patch between the original state and a patch string.
-   */
-  createPatch(patch: string): ICompletionPatch;
-
-  /**
-   * Reset the state of the model.
-   */
-  reset(): void;
-}
+import {
+  CompleterWidget
+} from './widget';
 
 
 /**
  * An implementation of a completer model.
  */
 export
-class CompleterModel implements ICompleterModel {
+class CompleterModel implements CompleterWidget.IModel {
   /**
    * A signal emitted when state of the completer menu changes.
    */
-  stateChanged: ISignal<ICompleterModel, void>;
-
-  /**
-   * The list of visible items in the completer menu.
-   *
-   * #### Notes
-   * This is a read-only property.
-   */
-  get items(): ICompleterItem[] {
-    return this._filter();
-  }
-
-  /**
-   * The unfiltered list of all available options in a completer menu.
-   */
-  get options(): string[] {
-    return this._options;
-  }
-  set options(newValue: string[]) {
-    if (deepEqual(newValue, this._options)) {
-      return;
-    }
-    if (newValue && newValue.length) {
-      this._options = [];
-      this._options.push(...newValue);
-      this._subsetMatch = true;
-    } else {
-      this._options = null;
-    }
-    this.stateChanged.emit(void 0);
-  }
+  readonly stateChanged: ISignal<this, void>;
 
   /**
    * The original completion request details.
@@ -266,10 +104,10 @@ class CompleterModel implements ICompleterModel {
   /**
    * The cursor details that the API has used to return matching options.
    */
-  get cursor(): ICursorSpan {
+  get cursor(): CompleterWidget.ICursorSpan {
     return this._cursor;
   }
-  set cursor(newValue: ICursorSpan) {
+  set cursor(newValue: CompleterWidget.ICursorSpan) {
     // Original request must always be set before a cursor change. If it isn't
     // the model fails silently.
     if (!this.original) {
@@ -318,6 +156,41 @@ class CompleterModel implements ICompleterModel {
     this._reset();
   }
 
+  /**
+   * The list of visible items in the completer menu.
+   *
+   * #### Notes
+   * This is a read-only property.
+   */
+  items(): IIterator<CompleterWidget.IItem> {
+    return this._filter();
+  }
+
+  /**
+   * The unfiltered list of all available options in a completer menu.
+   */
+  options(): IIterator<string> {
+    return iter(this._options);
+  }
+
+  /**
+   * Set the avilable options in the completer menu.
+   */
+  setOptions(newValue: IterableOrArrayLike<string>) {
+    let values = toArray(newValue);
+    if (deepEqual(values, this._options)) {
+      return;
+    }
+    if (values && values.length) {
+      this._options = [];
+      this._options.push(...values);
+      this._subsetMatch = true;
+    } else {
+      this._options = null;
+    }
+    this.stateChanged.emit(void 0);
+  }
+
   /**
    * Handle a text change.
    */
@@ -348,7 +221,7 @@ class CompleterModel implements ICompleterModel {
    *
    * @returns A patched text change or null if original value did not exist.
    */
-  createPatch(patch: string): ICompletionPatch {
+  createPatch(patch: string): CompleterWidget.IPatch {
     let original = this._original;
     let cursor = this._cursor;
 
@@ -377,13 +250,13 @@ class CompleterModel implements ICompleterModel {
   /**
    * Apply the query to the complete options list to return the matching subset.
    */
-  private _filter(): ICompleterItem[] {
+  private _filter(): IIterator<CompleterWidget.IItem> {
     let options = this._options || [];
     let query = this._query;
     if (!query) {
-      return options.map(option => ({ raw: option, text: option }));
+      return map(options, option => ({ raw: option, text: option }));
     }
-    let results: ICompletionMatch[] = [];
+    let results: Private.IMatch[] = [];
     for (let option of options) {
       let match = StringSearch.sumOfSquares(option, query);
       if (match) {
@@ -394,8 +267,9 @@ class CompleterModel implements ICompleterModel {
         });
       }
     }
-    return results.sort(Private.scoreCmp)
-      .map(result => ({ text: result.text, raw: result.raw }));
+    return map(results.sort(Private.scoreCmp), result =>
+      ({ text: result.text, raw: result.raw })
+    );
   }
 
   /**
@@ -411,7 +285,7 @@ class CompleterModel implements ICompleterModel {
   }
 
   private _current: ITextChange = null;
-  private _cursor: ICursorSpan = null;
+  private _cursor: CompleterWidget.ICursorSpan = null;
   private _isDisposed = false;
   private _options: string[] = null;
   private _original: ICompletionRequest = null;
@@ -428,6 +302,29 @@ defineSignal(CompleterModel.prototype, 'stateChanged');
  * A namespace for completer model private data.
  */
 namespace Private {
+  /**
+   * A filtered completion menu matching result.
+   */
+  export
+  interface IMatch {
+    /**
+     * The raw text of a completion match.
+     */
+    raw: string;
+
+    /**
+     * A score which indicates the strength of the match.
+     *
+     * A lower score is better. Zero is the best possible score.
+     */
+    score: number;
+
+    /**
+     * The highlighted text of a completion match.
+     */
+    text: string;
+  }
+
   /**
    * A sort comparison function for item match scores.
    *
@@ -436,7 +333,7 @@ namespace Private {
    * by locale order of the item text.
    */
   export
-  function scoreCmp(a: ICompletionMatch, b: ICompletionMatch): number {
+  function scoreCmp(a: IMatch, b: IMatch): number {
     let delta = a.score - b.score;
     if (delta !== 0) {
       return delta;

+ 139 - 15
src/completer/widget.ts

@@ -1,6 +1,18 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
+import {
+  IIterator, IterableOrArrayLike, toArray
+} from 'phosphor/lib/algorithm/iteration';
+
+import {
+  JSONObject
+} from 'phosphor/lib/algorithm/json';
+
+import {
+  IDisposable
+} from 'phosphor/lib/core/disposable';
+
 import {
   Message
 } from 'phosphor/lib/core/messaging';
@@ -18,8 +30,8 @@ import {
 } from 'phosphor/lib/ui/widget';
 
 import {
-  ICompleterModel, ICompleterItem
-} from './model';
+  ICompletionRequest, ITextChange
+} from '../notebook/cells/editor';
 
 
 /**
@@ -84,7 +96,7 @@ class CompleterWidget extends Widget {
   /**
    * A signal emitted when a selection is made from the completer menu.
    */
-  selected: ISignal<CompleterWidget, string>;
+  readonly selected: ISignal<this, string>;
 
   /**
    * A signal emitted when the completer widget's visibility changes.
@@ -93,18 +105,15 @@ class CompleterWidget extends Widget {
    * This signal is useful when there are multiple floating widgets that may
    * contend with the same space and ought to be mutually exclusive.
    */
-  visibilityChanged: ISignal<CompleterWidget, void>;
+  readonly visibilityChanged: ISignal<this, void>;
 
   /**
    * The model used by the completer widget.
-   *
-   * #### Notes
-   * This is a read-only property.
    */
-  get model(): ICompleterModel {
+  get model(): CompleterWidget.IModel {
     return this._model;
   }
-  set model(model: ICompleterModel) {
+  set model(model: CompleterWidget.IModel) {
     if (!model && !this._model || model === this._model) {
       return;
     }
@@ -233,7 +242,7 @@ class CompleterWidget extends Widget {
       return;
     }
 
-    let items = model.items;
+    let items = toArray(model.items());
 
     // If there are no items, reset and bail.
     if (!items || !items.length) {
@@ -517,7 +526,7 @@ class CompleterWidget extends Widget {
   private _anchor: HTMLElement = null;
   private _anchorPoint = 0;
   private _activeIndex = 0;
-  private _model: ICompleterModel = null;
+  private _model: CompleterWidget.IModel = null;
   private _renderer: CompleterWidget.IRenderer = null;
 }
 
@@ -546,7 +555,7 @@ namespace CompleterWidget {
     /**
      * The model for the completer widget.
      */
-    model?: ICompleterModel;
+    model?: IModel;
 
     /**
      * The renderer for the completer widget nodes.
@@ -554,6 +563,122 @@ namespace CompleterWidget {
     renderer?: IRenderer;
   }
 
+  /**
+   * The data model backing a code completer widget.
+   */
+  export
+  interface IModel extends IDisposable {
+    /**
+     * A signal emitted when state of the completer menu changes.
+     */
+    readonly stateChanged: ISignal<IModel, void>;
+
+    /**
+     * The current text change details.
+     */
+    current: ITextChange;
+
+    /**
+     * The cursor details that the API has used to return matching options.
+     */
+    cursor: ICursorSpan;
+
+    /**
+     * A flag that is true when the model value was modified by a subset match.
+     */
+    subsetMatch: boolean;
+
+    /**
+     * The original completer request details.
+     */
+    original: ICompletionRequest;
+
+    /**
+     * The query against which items are filtered.
+     */
+    query: string;
+
+    /**
+     * Get the of visible items in the completer menu.
+     */
+    items(): IIterator<IItem>;
+
+    /**
+     * Get the unfiltered options in a completer menu.
+     */
+    options(): IIterator<string>;
+
+    /**
+     * Set the avilable options in the completer menu.
+     */
+    setOptions(options: IterableOrArrayLike<string>): void;
+
+    /**
+     * Handle a text change.
+     */
+    handleTextChange(change: ITextChange): void;
+
+    /**
+     * Create a resolved patch between the original state and a patch string.
+     */
+    createPatch(patch: string): IPatch;
+
+    /**
+     * Reset the state of the model.
+     */
+    reset(): void;
+  }
+
+  /**
+   * An object describing a completion option injection into text.
+   */
+  export
+  interface IPatch {
+    /**
+     * The patch text.
+     */
+    text: string;
+
+    /**
+     * The position in the text where cursor should be after patch application.
+     */
+    position: number;
+  }
+
+
+  /**
+   * A completer menu item.
+   */
+  export
+  interface IItem {
+    /**
+     * The highlighted, marked up text of a visible completer item.
+     */
+    text: string;
+
+    /**
+     * The raw text of a visible completer item.
+     */
+    raw: string;
+  }
+
+
+  /**
+   * A cursor span.
+   */
+  export
+  interface ICursorSpan extends JSONObject {
+    /**
+     * The start position of the cursor.
+     */
+    start: number;
+
+    /**
+     * The end position of the cursor.
+     */
+    end: number;
+  }
+
   /**
    * A renderer for completer widget nodes.
    */
@@ -562,7 +687,7 @@ namespace CompleterWidget {
     /**
      * Create an item node (an `li` element) for a text completer menu.
      */
-    createItemNode(item: ICompleterItem): HTMLLIElement;
+    createItemNode(item: IItem): HTMLLIElement;
   }
 
   /**
@@ -573,7 +698,7 @@ namespace CompleterWidget {
     /**
      * Create an item node for a text completer menu.
      */
-    createItemNode(item: ICompleterItem): HTMLLIElement {
+    createItemNode(item: IItem): HTMLLIElement {
       let li = document.createElement('li');
       let code = document.createElement('code');
 
@@ -586,7 +711,6 @@ namespace CompleterWidget {
     }
   }
 
-
   /**
    * The default `IRenderer` instance.
    */