Steven Silvester hace 8 años
padre
commit
ad9e102a07

+ 55 - 113
src/common/observablelist.ts

@@ -2,13 +2,9 @@
 // Distributed under the terms of the Modified BSD License.
 
 import {
-  IIterator
+  EmptyIterator, IIterator, IIterable, IterableOrArrayLike, iter, each
 } from 'phosphor/lib/algorithm/iteration';
 
-import {
-  ISequence
-} from 'phosphor/lib/algorithm/sequence';
-
 import {
   move
 } from 'phosphor/lib/algorithm/mutation';
@@ -40,6 +36,11 @@ type ListChangeType =
    */
   'add' |
 
+  /**
+   * Items were assigned or cleared in the list.
+   */
+  'assign' |
+
   /**
    * An item was moved within the list.
    */
@@ -50,11 +51,6 @@ type ListChangeType =
    */
   'remove' |
 
-  /**
-   * Items were replaced in the list.
-   */
-  'replace' |
-
   /**
    * An item was set in the list.
    */
@@ -76,9 +72,10 @@ interface IListChangedArgs<T> {
    *
    * The semantics of this value depend upon the change type:
    *   - `Add`: The index of the added item.
+   *   - `Assign`: Always `-1`.
    *   - `Move`: The new index of the item.
    *   - `Remove`: Always `-1`.
-   *   - `Replace`: The index of the replacement.
+   *   - `Assign`: Always `-1`.
    *   - `Set`: The index of the set item.
    */
   newIndex: number;
@@ -88,21 +85,21 @@ interface IListChangedArgs<T> {
    *
    * The semantics of this value depend upon the change type:
    *   - `Add`: The item which was added.
+   *   - `Assign`: The new items.
    *   - `Move`: The item which was moved.
    *   - `Remove`: Always `undefined`.
-   *   - `Replace`: The `items[]` which were added.
    *   - `Set`: The new item at the index.
    */
-  newValue: T | T[];
+  newValue: T | IIterator<T>;
 
   /**
    * The old index associated with the change.
    *
    * The semantics of this value depend upon the change type:
    *   - `Add`: Always `-1`.
+   *   - `Assign`: Always `-1`.
    *   - `Move`: The old index of the item.
    *   - `Remove`: The index of the removed item.
-   *   - `Replace`: The index of the replacement.
    *   - `Set`: The index of the set item.
    */
   oldIndex: number;
@@ -112,12 +109,12 @@ interface IListChangedArgs<T> {
    *
    * The semantics of this value depend upon the change type:
    *   - `Add`: Always `undefined`.
+   *   - `Assign`: The old items.
    *   - `Move`: The item which was moved.
    *   - `Remove`: The item which was removed.
-   *   - `Replace`: The `items[]` which were removed.
    *   - `Set`: The old item at the index.
    */
-  oldValue: T | T[];
+  oldValue: T | IIterator<T>;
 }
 
 
@@ -125,7 +122,7 @@ interface IListChangedArgs<T> {
  * A sequence container which can be observed for changes.
  */
 export
-interface IObservableList<T> extends IDisposable {
+interface IObservableList<T> extends IDisposable, IIterable<T> {
   /**
    * A signal emitted when the list has changed.
    */
@@ -136,18 +133,6 @@ interface IObservableList<T> extends IDisposable {
    */
   readonly length: number;
 
-  /**
-   * The read-only sequence of items in the list.
-   */
-  readonly items: ISequence<T>;
-
-  /**
-   * Create an iterator over the values in the list.
-   *
-   * @returns A new iterator starting at the front of the list.
-   */
-  iter(): IIterator<T>;
-
   /**
    * Get the item at a specific index in the list.
    *
@@ -177,7 +162,7 @@ interface IObservableList<T> extends IDisposable {
    *
    * @param item - The item to add to the list.
    *
-   * @returns The index at which the item was added.
+   * @returns The new length of the vector.
    */
   pushBack(item: T): number;
 
@@ -190,7 +175,7 @@ interface IObservableList<T> extends IDisposable {
    *
    * @param item - The item to insert into the list.
    *
-   * @returns The index at which the item was inserted.
+   * @returns The the new length of the vector.
    */
   insert(index: number, item: T): number;
 
@@ -229,30 +214,23 @@ interface IObservableList<T> extends IDisposable {
   removeAt(index: number): T;
 
   /**
-   * Replace items at a specific location in the list.
-   *
-   * @param index - The index at which to modify the list. If this is
-   *   negative, it is offset from the end of the list. In all cases,
-   *   it is clamped to the bounds of the list.
+   * Assign the items in the list.
    *
-   * @param count - The number of items to remove at the given index.
-   *   This is clamped to the length of the list.
+   * @param items - The items to assign.
    *
-   * @param items - The items to insert at the specified index.
-   *
-   * @returns An array of the items removed from the list.
+   * @returns An iterator for of the items in the existing list.
    */
-  replace(index: number, count: number, items: T[]): T[];
+  assign(items: IterableOrArrayLike<T>): IIterator<T>;
 
   /**
    * Remove all items from the list.
    *
-   * @returns An array of the items removed from the list.
+   * @returns An iterator for the items removed from the list.
    *
    * #### Notes
-   * This is equivalent to `list.replace(0, list.length, [])`.
+   * This is equivalent to `list.assign([])`.
    */
-  clear(): T[];
+  clear(): IIterator<T>;
 }
 
 
@@ -266,7 +244,7 @@ class ObservableList<T> implements IObservableList<T> {
    *
    * @param items - The initial items for the list.
    */
-  constructor(items?: T[]) {
+  constructor(items?: IterableOrArrayLike<T>) {
     this.internal = new Vector<T>(items);
   }
 
@@ -285,18 +263,11 @@ class ObservableList<T> implements IObservableList<T> {
     return this.internal.length;
   }
 
-  /**
-   * The read-only sequence of items in the list.
-   */
-  get items(): ISequence<T> {
-    return this.internal;
-  }
-
   /**
    * Test whether the list has been disposed.
    */
   get isDisposed(): boolean {
-    return this._isDisposed;
+    return this.internal === null;
   }
 
   /**
@@ -306,9 +277,9 @@ class ObservableList<T> implements IObservableList<T> {
     if (this.isDisposed) {
       return;
     }
-    this._isDisposed = true;
     clearSignalData(this);
     this.internal.clear();
+    this.internal = null;
   }
 
   /**
@@ -447,33 +418,44 @@ class ObservableList<T> implements IObservableList<T> {
   }
 
   /**
-   * Replace items at a specific location in the list.
-   *
-   * @param index - The index at which to modify the list. If this is
-   *   negative, it is offset from the end of the list. In all cases,
-   *   it is clamped to the bounds of the list.
-   *
-   * @param count - The number of items to remove at the given index.
-   *   This is clamped to the length of the list.
+   * Assign the items in the list.
    *
-   * @param items - The items to insert at the specified index.
+   * @param items - The items to assign.
    *
-   * @returns An array of the items removed from the list.
+   * @returns An iterator for of the items in the existing list.
    */
-  replace(index: number, count: number, items: T[]): T[] {
-    return this.replaceItems(this._norm(index), this._limit(count), items);
+  assign(items: IterableOrArrayLike<T>): IIterator<T> {
+    let old: T[] = [];
+    while (!this.internal.isEmpty) {
+      old.push(this.internal.removeAt(0));
+    }
+    let newValue = iter(items);
+    let oldValue = iter(old);
+
+    each(newValue, item => {
+      this.internal.pushBack(item);
+    });
+
+    this.changed.emit({
+      type: 'assign',
+      newIndex: -1,
+      newValue,
+      oldIndex: -1,
+      oldValue
+    });
+    return oldValue;
   }
 
   /**
    * Remove all items from the list.
    *
-   * @returns An array of the items removed from the list.
+   * @returns An iterator for the items removed from the list.
    *
    * #### Notes
-   * This is equivalent to `list.replace(0, list.length, [])`.
+   * This is equivalent to `list.assign([])`.
    */
-  clear(): T[] {
-    return this.replaceItems(0, this.internal.length, []);
+  clear(): IIterator<T> {
+    return this.assign(EmptyIterator.instance);
   }
 
   /**
@@ -498,7 +480,7 @@ class ObservableList<T> implements IObservableList<T> {
    * This may be reimplemented by subclasses to customize the behavior.
    */
   protected addItem(index: number, item: T): number {
-    this.internal.insert(index, item);
+    let value = this.internal.insert(index, item);
     this.changed.emit({
       type: 'add',
       newIndex: index,
@@ -506,7 +488,7 @@ class ObservableList<T> implements IObservableList<T> {
       oldIndex: -1,
       oldValue: void 0,
     });
-    return index;
+    return value;
   }
 
   /**
@@ -563,44 +545,6 @@ class ObservableList<T> implements IObservableList<T> {
     return item;
   }
 
-  /**
-   * Replace items at a specific location in the list.
-   *
-   * @param index - The index at which to modify the list. This must
-   *   be an integer in the range `[0, internal.length]`.
-   *
-   * @param count - The number of items to remove from the list. This
-   *   must be an integer in the range `[0, internal.length]`.
-   *
-   * @param items - The items to insert at the specified index.
-   *
-   * @returns An array of the items removed from the list.
-   *
-   * #### Notes
-   * This may be reimplemented by subclasses to customize the behavior.
-   */
-  protected replaceItems(index: number, count: number, items: T[]): T[] {
-    let old: T[] = [];
-    while (count-- > 0) {
-      old.push(this.internal.removeAt(index));
-    }
-
-    let i = index;
-    let j = 0;
-    let len = items.length;
-    while (j < len) {
-      this.internal.insert(i++, items[j++]);
-    }
-    this.changed.emit({
-      type: 'replace',
-      newIndex: index,
-      newValue: items,
-      oldIndex: index,
-      oldValue: old,
-    });
-    return old;
-  }
-
   /**
    * Set the item at a specific index in the list.
    *
@@ -654,8 +598,6 @@ class ObservableList<T> implements IObservableList<T> {
   private _limit(c: number): number {
     return Math.max(0, Math.min(Math.floor(c), this.internal.length));
   }
-
-  private _isDisposed = false;
 }
 
 

+ 26 - 24
src/notebook/common/undo.ts

@@ -1,6 +1,10 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
+import {
+  IIterator, IIterable, iter, each, toArray
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   IDisposable
 } from 'phosphor/lib/core/disposable';
@@ -190,10 +194,9 @@ class ObservableUndoableList<T extends ISerializable> extends ObservableList<T>
     case 'move':
       this.move(change.newIndex, change.oldIndex);
       break;
-    case 'replace':
-      let len = (change.newValue as any[]).length;
-      let values = this._createValues(change.oldValue as any[]);
-      this.replace(change.oldIndex, len, values);
+    case 'assign':
+      let values = this._createValues(change.oldValue as IIterable<T>);
+      this.assign(values);
       break;
     default:
       return;
@@ -220,10 +223,9 @@ class ObservableUndoableList<T extends ISerializable> extends ObservableList<T>
     case 'move':
       this.move(change.oldIndex, change.newIndex);
       break;
-    case 'replace':
-      let len = (change.oldValue as any[]).length;
-      let cells = this._createValues(change.newValue as any[]);
-      this.replace(change.oldIndex, len, cells);
+    case 'assign':
+      let cells = this._createValues(change.newValue as IIterable<T>);
+      this.assign(cells);
       break;
     default:
       return;
@@ -241,11 +243,11 @@ class ObservableUndoableList<T extends ISerializable> extends ObservableList<T>
   /**
    * Create a list of cell models from JSON.
    */
-  private _createValues(bundles: any[]): T[] {
+  private _createValues(bundles: IIterable<T>): T[] {
     let values: T[] = [];
-    for (let bundle of bundles) {
+    each(bundles, bundle => {
       values.push(this._createValue(bundle));
-    }
+    });
     return values;
   }
 
@@ -253,8 +255,8 @@ class ObservableUndoableList<T extends ISerializable> extends ObservableList<T>
    * Copy a change as JSON.
    */
   private _copyChange(change: IListChangedArgs<T>): IListChangedArgs<any> {
-    if (change.type === 'replace') {
-      return this._copyReplace(change);
+    if (change.type === 'assign') {
+      return this._copyAssign(change);
     }
     let oldValue: any = null;
     let newValue: any = null;
@@ -285,23 +287,23 @@ class ObservableUndoableList<T extends ISerializable> extends ObservableList<T>
   }
 
   /**
-   * Copy a replace change as JSON.
+   * Copy an assign change as JSON.
    */
-  private _copyReplace(change: IListChangedArgs<T>): IListChangedArgs<any> {
+  private _copyAssign(change: IListChangedArgs<T>): IListChangedArgs<any> {
     let oldValue: any[] = [];
-    for (let value of (change.oldValue as T[])) {
+    each(change.oldValue as IIterator<T>, value => {
       oldValue.push(value.toJSON());
-    }
+    });
     let newValue: any[] = [];
-    for (let value of (change.newValue as T[])) {
+    each(change.newValue as IIterator<T>, value => {
       newValue.push(value.toJSON());
-    }
+    });
     return {
-      type: 'replace',
-      oldIndex: change.oldIndex,
-      newIndex: change.newIndex,
-      oldValue,
-      newValue
+      type: 'assign',
+      oldIndex: -1,
+      newIndex: -1,
+      oldValue: iter(oldValue),
+      newValue: iter(newValue)
     };
   }
 

+ 18 - 2
src/notebook/notebook/actions.ts

@@ -5,6 +5,10 @@ import {
   IKernel, KernelMessage
 } from '@jupyterlab/services';
 
+import {
+  each
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   MimeData as IClipboard
 } from 'phosphor/lib/core/mimedata';
@@ -80,7 +84,12 @@ namespace NotebookActions {
     clone1.source = orig.slice(position).replace(/^\s+/g, '');
 
     // Make the changes while preserving history.
-    nbModel.cells.replace(index, 1, [clone0, clone1]);
+    let cells = nbModel.cells;
+    cells.beginCompoundOperation();
+    cells.set(index, clone0);
+    cells.insert(index + 1, clone1);
+    cells.endCompoundOperation();
+
     widget.activeCellIndex++;
     widget.scrollToActiveCell();
   }
@@ -717,7 +726,14 @@ namespace NotebookActions {
       }
     }
     let index = widget.activeCellIndex;
-    widget.model.cells.replace(index + 1, 0, newCells);
+
+    let cells = widget.model.cells;
+    cells.beginCompoundOperation();
+    each(newCells, cell => {
+      cells.insert(++index, cell);
+    });
+    cells.endCompoundOperation();
+
     widget.activeCellIndex += newCells.length;
     Private.deselectCells(widget);
   }

+ 14 - 10
src/notebook/notebook/model.ts

@@ -5,6 +5,10 @@ import {
   utils
 } from '@jupyterlab/services';
 
+import {
+  IIterable, each
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   deepEqual
 } from 'phosphor/lib/algorithm/json';
@@ -317,7 +321,7 @@ class NotebookModel extends DocumentModel implements INotebookModel {
         continue;
       }
     }
-    this.cells.replace(0, cells.length, cells);
+    this.cells.assign(cells);
     let oldValue = 0;
     let newValue = 0;
     if (value.nbformat !== this._nbformat) {
@@ -411,15 +415,15 @@ class NotebookModel extends DocumentModel implements INotebookModel {
     case 'remove':
       (change.oldValue as ICellModel).dispose();
       break;
-    case 'replace':
-      let newValues = change.newValue as ICellModel[];
-      for (cell of newValues) {
-        cell.contentChanged.connect(this._onCellChanged, this);
-      }
-      let oldValues = change.oldValue as ICellModel[];
-      for (cell of oldValues) {
-        cell.dispose();
-      }
+    case 'assign':
+      let newValues = change.newValue as IIterable<ICellModel>;
+      each(newValues, value => {
+        value.contentChanged.connect(this._onCellChanged, this);
+      });
+      let oldValues = change.oldValue as IIterable<ICellModel>;
+      each(oldValues, value => {
+        value.dispose();
+      });
       break;
     case 'set':
       cell = change.newValue as ICellModel;

+ 13 - 8
src/notebook/notebook/widget.ts

@@ -5,6 +5,10 @@ import {
   KernelMessage
 } from '@jupyterlab/services';
 
+import {
+  IIterable, each
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   find
 } from 'phosphor/lib/algorithm/searching';
@@ -344,16 +348,17 @@ class StaticNotebook extends Widget {
     case 'remove':
       this._removeCell(args.oldIndex);
       break;
-    case 'replace':
+    case 'assign':
       // TODO: reuse existing cell widgets if possible.
-      let oldValues = args.oldValue as ICellModel[];
-      for (let i = 0; i < oldValues.length; i++) {
+      let oldValues = args.oldValue as IIterable<ICellModel>;
+      each(oldValues, value => {
         this._removeCell(args.oldIndex);
-      }
-      let newValues = args.newValue as ICellModel[];
-      for (let i = newValues.length; i > 0; i--) {
-        this._insertCell(args.newIndex, newValues[i - 1]);
-      }
+      });
+      let newValues = args.newValue as IIterable<ICellModel>;
+      let index = 0;
+      each(newValues, value => {
+        this._insertCell(index++, value);
+      });
       break;
     case 'set':
       // TODO: reuse existing widget if possible.

+ 7 - 3
src/notebook/output-area/model.ts

@@ -5,6 +5,10 @@ import {
   IKernel, KernelMessage
 } from '@jupyterlab/services';
 
+import {
+  EmptyIterator, IIterator
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   JSONObject
 } from 'phosphor/lib/algorithm/json';
@@ -152,10 +156,10 @@ class OutputAreaModel implements IDisposable {
    *
    * @param wait Delay clearing the output until the next message is added.
    */
-  clear(wait: boolean = false): OutputAreaModel.Output[] {
+  clear(wait: boolean = false): IIterator<OutputAreaModel.Output> {
     if (wait) {
       this.clearNext = true;
-      return [];
+      return EmptyIterator.instance;
     }
     return this.list.clear();
   }
@@ -175,7 +179,7 @@ class OutputAreaModel implements IDisposable {
    * Types are validated before being added.
    */
   addMimeData(output: nbformat.IDisplayData | nbformat.IExecuteResult, mimetype: string, value: string | JSONObject): void {
-    let index = indexOf(this.list.items, output);
+    let index = indexOf(this.list, output);
     if (index === -1) {
       throw new Error(`Cannot add data to non-tracked bundle`);
     }

+ 8 - 4
src/notebook/output-area/widget.ts

@@ -5,6 +5,10 @@ import {
   IKernel
 } from '@jupyterlab/services';
 
+import {
+  IIterable, each
+} from 'phosphor/lib/algorithm/iteration';
+
 import {
   JSONObject
 } from 'phosphor/lib/algorithm/json';
@@ -367,7 +371,7 @@ class OutputAreaWidget extends Widget {
       // Children are always added at the end.
       this.addChild();
       break;
-    case 'replace':
+    case 'assign':
       // Only "clear" is supported by the model.
       // When an output area is cleared and then quickly replaced with new
       // content (as happens with @interact in widgets, for example), the
@@ -386,10 +390,10 @@ class OutputAreaWidget extends Widget {
         this.node.style.minHeight = '';
       }, 50);
 
-      let oldValues = args.oldValue as nbformat.IOutput[];
-      for (let i = args.oldIndex; i < oldValues.length; i++) {
+      let oldValues = args.oldValue as IIterable<nbformat.IOutput>;
+      each(oldValues, value => {
         this.removeChild(args.oldIndex);
-      }
+      });
       break;
     case 'set':
       if (!this._injecting) {

+ 19 - 19
test/src/common/observablelist.spec.ts

@@ -60,7 +60,7 @@ describe('common/observablelist', () => {
 
       it('should initialize the list items', () => {
         let list = new ObservableList<number>([1, 2, 3]);
-        expect(toArray(list.items)).to.eql([1, 2, 3]);
+        expect(toArray(list)).to.eql([1, 2, 3]);
       });
 
     });
@@ -164,13 +164,13 @@ describe('common/observablelist', () => {
       it('should set the item at a specific index', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.set(1, 4);
-        expect(toArray(list.items)).to.eql([1, 4, 3]);
+        expect(toArray(list)).to.eql([1, 4, 3]);
       });
 
       it('should index from the end if negative', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.set(-1, 4);
-        expect(toArray(list.items)).to.eql([1, 2, 4]);
+        expect(toArray(list)).to.eql([1, 2, 4]);
       });
 
       it('should return the item which occupied the index', () => {
@@ -208,7 +208,7 @@ describe('common/observablelist', () => {
       it('should add an item to the end of the list', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.pushBack(4);
-        expect(toArray(list.items)).to.eql([1, 2, 3, 4]);
+        expect(toArray(list)).to.eql([1, 2, 3, 4]);
       });
 
       it('should return the new index of the item in the list', () => {
@@ -241,19 +241,19 @@ describe('common/observablelist', () => {
       it('should insert an item into the list at a specific index', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.insert(1, 4);
-        expect(toArray(list.items)).to.eql([1, 4, 2, 3]);
+        expect(toArray(list)).to.eql([1, 4, 2, 3]);
       });
 
       it('should index from the end of list if the index is negative', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.insert(-1, 4);
-        expect(toArray(list.items)).to.eql([1, 2, 4, 3]);
+        expect(toArray(list)).to.eql([1, 2, 4, 3]);
       });
 
       it('should clamp to the bounds of the list', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.insert(10, 4);
-        expect(toArray(list.items)).to.eql([1, 2, 3, 4]);
+        expect(toArray(list)).to.eql([1, 2, 3, 4]);
       });
 
       it('should return the new index of the item in the list', () => {
@@ -288,19 +288,19 @@ describe('common/observablelist', () => {
       it('should move an item from one index to another', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.move(1, 2);
-        expect(toArray(list.items)).to.eql([1, 3, 2]);
+        expect(toArray(list)).to.eql([1, 3, 2]);
       });
 
       it('should index fromIndex from the end of list if negative', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.move(-1, 1);
-        expect(toArray(list.items)).to.eql([1, 3, 2]);
+        expect(toArray(list)).to.eql([1, 3, 2]);
       });
 
       it('should index toIndex from the end of list if negative', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.move(0, -1);
-        expect(toArray(list.items)).to.eql([2, 3, 1]);
+        expect(toArray(list)).to.eql([2, 3, 1]);
       });
 
       it('should return `true` if the item was moved', () => {
@@ -339,7 +339,7 @@ describe('common/observablelist', () => {
       it('should remove the first occurrence of a specific item from the list', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.remove(1);
-        expect(toArray(list.items)).to.eql([2, 3]);
+        expect(toArray(list)).to.eql([2, 3]);
       });
 
       it('should return the index occupied by the item', () => {
@@ -377,13 +377,13 @@ describe('common/observablelist', () => {
       it('should remove the item at a specific index', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.removeAt(1);
-        expect(toArray(list.items)).to.eql([1, 3]);
+        expect(toArray(list)).to.eql([1, 3]);
       });
 
       it('should index from the end of list if negative', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.removeAt(-1);
-        expect(toArray(list.items)).to.eql([1, 2]);
+        expect(toArray(list)).to.eql([1, 2]);
       });
 
       it('should return the item at the specified index', () => {
@@ -421,37 +421,37 @@ describe('common/observablelist', () => {
       it('should replace items at a specific location in the list', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.replace(1, 2, [4, 5, 6]);
-        expect(toArray(list.items)).to.eql([1, 4, 5, 6]);
+        expect(toArray(list)).to.eql([1, 4, 5, 6]);
       });
 
       it('should index from the end of the list if negative', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.replace(-2, 2, [4, 5, 6]);
-        expect(toArray(list.items)).to.eql([1, 4, 5, 6]);
+        expect(toArray(list)).to.eql([1, 4, 5, 6]);
       });
 
       it('should clamp the index to the bounds of the list', () => {
         let list = new ObservableList<number>([1, 2, 3]);
         list.replace(10, 2, [4, 5, 6]);
-        expect(toArray(list.items)).to.eql([1, 2, 3, 4, 5, 6]);
+        expect(toArray(list)).to.eql([1, 2, 3, 4, 5, 6]);
       });
 
       it('should remove the given count of items', () => {
         let list = new ObservableList<number>([1, 2, 3, 4, 5, 6]);
         list.replace(0, 3, [1, 2]);
-        expect(toArray(list.items)).to.eql([1, 2, 4, 5, 6]);
+        expect(toArray(list)).to.eql([1, 2, 4, 5, 6]);
       });
 
       it('should clamp the count to the length of the list', () => {
         let list = new ObservableList<number>([1, 2, 3, 4, 5, 6]);
         list.replace(0, 10, [1, 2]);
-        expect(toArray(list.items)).to.eql([1, 2]);
+        expect(toArray(list)).to.eql([1, 2]);
       });
 
       it('should handle an empty items array', () => {
         let list = new ObservableList<number>([1, 2, 3, 4, 5, 6]);
         list.replace(1, 10, []);
-        expect(toArray(list.items)).to.eql([1]);
+        expect(toArray(list)).to.eql([1]);
       });
 
       it('should return an array of items removed from the list', () => {