123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 |
- // Copyright (c) Jupyter Development Team.
- // Distributed under the terms of the Modified BSD License.
- import {
- ArrayExt,
- ArrayIterator,
- IIterator,
- IterableOrArrayLike,
- each,
- toArray
- } from '@lumino/algorithm';
- import { IDisposable } from '@lumino/disposable';
- import { ISignal, Signal } from '@lumino/signaling';
- /**
- * A list which can be observed for changes.
- */
- export interface IObservableList<T> extends IDisposable {
- /**
- * A signal emitted when the list has changed.
- */
- readonly changed: ISignal<this, IObservableList.IChangedArgs<T>>;
- /**
- * The type of this object.
- */
- readonly type: 'List';
- /**
- * The length of the list.
- *
- * #### Notes
- * This is a read-only property.
- */
- length: number;
- /**
- * Create an iterator over the values in the list.
- *
- * @returns A new iterator starting at the front of the list.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * No changes.
- */
- iter(): IIterator<T>;
- /**
- * Remove all values from the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * All current iterators are invalidated.
- */
- clear(): void;
- /**
- * Get the value at the specified index.
- *
- * @param index - The positive integer index of interest.
- *
- * @returns The value at the specified index.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral or out of range.
- */
- get(index: number): T;
- /**
- * Insert a value into the list at a specific index.
- *
- * @param index - The index at which to insert the value.
- *
- * @param value - The value to set at the specified index.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * No changes.
- *
- * #### Notes
- * The `index` will be clamped to the bounds of the list.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral.
- */
- insert(index: number, value: T): void;
- /**
- * Insert a set of items into the list at the specified index.
- *
- * @param index - The index at which to insert the values.
- *
- * @param values - The values to insert at the specified index.
- *
- * #### Complexity.
- * Linear.
- *
- * #### Iterator Validity
- * No changes.
- *
- * #### Notes
- * The `index` will be clamped to the bounds of the list.
- *
- * #### Undefined Behavior.
- * An `index` which is non-integral.
- */
- insertAll(index: number, values: IterableOrArrayLike<T>): void;
- /**
- * Move a value from one index to another.
- *
- * @parm fromIndex - The index of the element to move.
- *
- * @param toIndex - The index to move the element to.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * Iterators pointing at the lesser of the `fromIndex` and the `toIndex`
- * and beyond are invalidated.
- *
- * #### Undefined Behavior
- * A `fromIndex` or a `toIndex` which is non-integral.
- */
- move(fromIndex: number, toIndex: number): void;
- /**
- * Add a value to the back of the list.
- *
- * @param value - The value to add to the back of the list.
- *
- * @returns The new length of the list.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * No changes.
- */
- push(value: T): number;
- /**
- * Push a set of values to the back of the list.
- *
- * @param values - An iterable or array-like set of values to add.
- *
- * @returns The new length of the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * No changes.
- */
- pushAll(values: IterableOrArrayLike<T>): number;
- /**
- * Remove and return the value at a specific index.
- *
- * @param index - The index of the value of interest.
- *
- * @returns The value at the specified index, or `undefined` if the
- * index is out of range.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * Iterators pointing at the removed value and beyond are invalidated.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral.
- */
- remove(index: number): T | undefined;
- /**
- * Remove a range of items from the list.
- *
- * @param startIndex - The start index of the range to remove (inclusive).
- *
- * @param endIndex - The end index of the range to remove (exclusive).
- *
- * @returns The new length of the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * Iterators pointing to the first removed value and beyond are invalid.
- *
- * #### Undefined Behavior
- * A `startIndex` or `endIndex` which is non-integral.
- */
- removeRange(startIndex: number, endIndex: number): number;
- /**
- * Remove the first occurrence of a value from the list.
- *
- * @param value - The value of interest.
- *
- * @returns The index of the removed value, or `-1` if the value
- * is not contained in the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * Iterators pointing at the removed value and beyond are invalidated.
- */
- removeValue(value: T): number;
- /**
- * Set the value at the specified index.
- *
- * @param index - The positive integer index of interest.
- *
- * @param value - The value to set at the specified index.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * No changes.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral or out of range.
- */
- set(index: number, value: T): void;
- }
- /**
- * The namespace for IObservableList related interfaces.
- */
- export namespace IObservableList {
- /**
- * The change types which occur on an observable list.
- */
- export type ChangeType =
- /**
- * Item(s) were added to the list.
- */
- | 'add'
- /**
- * An item was moved within the list.
- */
- | 'move'
- /**
- * Item(s) were removed from the list.
- */
- | 'remove'
- /**
- * An item was set in the list.
- */
- | 'set';
- /**
- * The changed args object which is emitted by an observable list.
- */
- export interface IChangedArgs<T> {
- /**
- * The type of change undergone by the vector.
- */
- type: ChangeType;
- /**
- * The new index associated with the change.
- */
- newIndex: number;
- /**
- * The new values associated with the change.
- *
- * #### Notes
- * The values will be contiguous starting at the `newIndex`.
- */
- newValues: T[];
- /**
- * The old index associated with the change.
- */
- oldIndex: number;
- /**
- * The old values associated with the change.
- *
- * #### Notes
- * The values will be contiguous starting at the `oldIndex`.
- */
- oldValues: T[];
- }
- }
- /**
- * A concrete implementation of [[IObservableList]].
- */
- export class ObservableList<T> implements IObservableList<T> {
- /**
- * Construct a new observable map.
- */
- constructor(options: ObservableList.IOptions<T> = {}) {
- if (options.values !== void 0) {
- each(options.values, value => {
- this._array.push(value);
- });
- }
- this._itemCmp = options.itemCmp || Private.itemCmp;
- }
- /**
- * The type of this object.
- */
- get type(): 'List' {
- return 'List';
- }
- /**
- * A signal emitted when the list has changed.
- */
- get changed(): ISignal<this, IObservableList.IChangedArgs<T>> {
- return this._changed;
- }
- /**
- * The length of the list.
- */
- get length(): number {
- return this._array.length;
- }
- /**
- * Test whether the list has been disposed.
- */
- get isDisposed(): boolean {
- return this._isDisposed;
- }
- /**
- * Dispose of the resources held by the list.
- */
- dispose(): void {
- if (this._isDisposed) {
- return;
- }
- this._isDisposed = true;
- Signal.clearData(this);
- this.clear();
- }
- /**
- * Create an iterator over the values in the list.
- *
- * @returns A new iterator starting at the front of the list.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * No changes.
- */
- iter(): IIterator<T> {
- return new ArrayIterator(this._array);
- }
- /**
- * Get the value at the specified index.
- *
- * @param index - The positive integer index of interest.
- *
- * @returns The value at the specified index.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral or out of range.
- */
- get(index: number): T {
- return this._array[index];
- }
- /**
- * Set the value at the specified index.
- *
- * @param index - The positive integer index of interest.
- *
- * @param value - The value to set at the specified index.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * No changes.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral or out of range.
- */
- set(index: number, value: T): void {
- let oldValue = this._array[index];
- if (value === undefined) {
- throw new Error('Cannot set an undefined item');
- }
- // Bail if the value does not change.
- let itemCmp = this._itemCmp;
- if (itemCmp(oldValue, value)) {
- return;
- }
- this._array[index] = value;
- this._changed.emit({
- type: 'set',
- oldIndex: index,
- newIndex: index,
- oldValues: [oldValue],
- newValues: [value]
- });
- }
- /**
- * Add a value to the end of the list.
- *
- * @param value - The value to add to the end of the list.
- *
- * @returns The new length of the list.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * No changes.
- */
- push(value: T): number {
- let num = this._array.push(value);
- this._changed.emit({
- type: 'add',
- oldIndex: -1,
- newIndex: this.length - 1,
- oldValues: [],
- newValues: [value]
- });
- return num;
- }
- /**
- * Insert a value into the list at a specific index.
- *
- * @param index - The index at which to insert the value.
- *
- * @param value - The value to set at the specified index.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * No changes.
- *
- * #### Notes
- * The `index` will be clamped to the bounds of the list.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral.
- */
- insert(index: number, value: T): void {
- ArrayExt.insert(this._array, index, value);
- this._changed.emit({
- type: 'add',
- oldIndex: -1,
- newIndex: index,
- oldValues: [],
- newValues: [value]
- });
- }
- /**
- * Remove the first occurrence of a value from the list.
- *
- * @param value - The value of interest.
- *
- * @returns The index of the removed value, or `-1` if the value
- * is not contained in the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * Iterators pointing at the removed value and beyond are invalidated.
- */
- removeValue(value: T): number {
- let itemCmp = this._itemCmp;
- let index = ArrayExt.findFirstIndex(this._array, item => {
- return itemCmp(item, value);
- });
- this.remove(index);
- return index;
- }
- /**
- * Remove and return the value at a specific index.
- *
- * @param index - The index of the value of interest.
- *
- * @returns The value at the specified index, or `undefined` if the
- * index is out of range.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * Iterators pointing at the removed value and beyond are invalidated.
- *
- * #### Undefined Behavior
- * An `index` which is non-integral.
- */
- remove(index: number): T | undefined {
- let value = ArrayExt.removeAt(this._array, index);
- if (value === undefined) {
- return;
- }
- this._changed.emit({
- type: 'remove',
- oldIndex: index,
- newIndex: -1,
- newValues: [],
- oldValues: [value]
- });
- return value;
- }
- /**
- * Remove all values from the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * All current iterators are invalidated.
- */
- clear(): void {
- let copy = this._array.slice();
- this._array.length = 0;
- this._changed.emit({
- type: 'remove',
- oldIndex: 0,
- newIndex: 0,
- newValues: [],
- oldValues: copy
- });
- }
- /**
- * Move a value from one index to another.
- *
- * @parm fromIndex - The index of the element to move.
- *
- * @param toIndex - The index to move the element to.
- *
- * #### Complexity
- * Constant.
- *
- * #### Iterator Validity
- * Iterators pointing at the lesser of the `fromIndex` and the `toIndex`
- * and beyond are invalidated.
- *
- * #### Undefined Behavior
- * A `fromIndex` or a `toIndex` which is non-integral.
- */
- move(fromIndex: number, toIndex: number): void {
- if (this.length <= 1 || fromIndex === toIndex) {
- return;
- }
- let values = [this._array[fromIndex]];
- ArrayExt.move(this._array, fromIndex, toIndex);
- this._changed.emit({
- type: 'move',
- oldIndex: fromIndex,
- newIndex: toIndex,
- oldValues: values,
- newValues: values
- });
- }
- /**
- * Push a set of values to the back of the list.
- *
- * @param values - An iterable or array-like set of values to add.
- *
- * @returns The new length of the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * No changes.
- */
- pushAll(values: IterableOrArrayLike<T>): number {
- let newIndex = this.length;
- each(values, value => {
- this._array.push(value);
- });
- this._changed.emit({
- type: 'add',
- oldIndex: -1,
- newIndex,
- oldValues: [],
- newValues: toArray(values)
- });
- return this.length;
- }
- /**
- * Insert a set of items into the list at the specified index.
- *
- * @param index - The index at which to insert the values.
- *
- * @param values - The values to insert at the specified index.
- *
- * #### Complexity.
- * Linear.
- *
- * #### Iterator Validity
- * No changes.
- *
- * #### Notes
- * The `index` will be clamped to the bounds of the list.
- *
- * #### Undefined Behavior.
- * An `index` which is non-integral.
- */
- insertAll(index: number, values: IterableOrArrayLike<T>): void {
- let newIndex = index;
- each(values, value => {
- ArrayExt.insert(this._array, index++, value);
- });
- this._changed.emit({
- type: 'add',
- oldIndex: -1,
- newIndex,
- oldValues: [],
- newValues: toArray(values)
- });
- }
- /**
- * Remove a range of items from the list.
- *
- * @param startIndex - The start index of the range to remove (inclusive).
- *
- * @param endIndex - The end index of the range to remove (exclusive).
- *
- * @returns The new length of the list.
- *
- * #### Complexity
- * Linear.
- *
- * #### Iterator Validity
- * Iterators pointing to the first removed value and beyond are invalid.
- *
- * #### Undefined Behavior
- * A `startIndex` or `endIndex` which is non-integral.
- */
- removeRange(startIndex: number, endIndex: number): number {
- let oldValues = this._array.slice(startIndex, endIndex);
- for (let i = startIndex; i < endIndex; i++) {
- ArrayExt.removeAt(this._array, startIndex);
- }
- this._changed.emit({
- type: 'remove',
- oldIndex: startIndex,
- newIndex: -1,
- oldValues,
- newValues: []
- });
- return this.length;
- }
- private _array: Array<T> = [];
- private _isDisposed = false;
- private _itemCmp: (first: T, second: T) => boolean;
- private _changed = new Signal<this, IObservableList.IChangedArgs<T>>(this);
- }
- /**
- * The namespace for `ObservableList` class statics.
- */
- export namespace ObservableList {
- /**
- * The options used to initialize an observable map.
- */
- export interface IOptions<T> {
- /**
- * An optional initial set of values.
- */
- values?: IterableOrArrayLike<T>;
- /**
- * The item comparison function for change detection on `set`.
- *
- * If not given, strict `===` equality will be used.
- */
- itemCmp?: (first: T, second: T) => boolean;
- }
- }
- /**
- * The namespace for module private data.
- */
- namespace Private {
- /**
- * The default strict equality item cmp.
- */
- export function itemCmp(first: any, second: any): boolean {
- return first === second;
- }
- }
|