Merge pull request #606 from vector-im/bwindels/typescript-observable-2

Typescript conversion of yet more observables
This commit is contained in:
Bruno Windels 2021-11-30 17:09:16 +01:00 committed by GitHub
commit 08314bd4b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 181 additions and 168 deletions

View file

@ -188,7 +188,7 @@ import {NullLogItem, NullLogger} from "../../../../logging/NullLogger";
import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js";
// other imports
import {BaseMessageTile} from "./tiles/BaseMessageTile.js";
import {MappedList} from "../../../../observable/list/MappedList.js";
import {MappedList} from "../../../../observable/list/MappedList";
import {ObservableValue} from "../../../../observable/ObservableValue";
import {PowerLevels} from "../../../../matrix/room/PowerLevels.js";

View file

@ -253,7 +253,7 @@ export class TilesCollection extends BaseObservableList {
}
}
import {ObservableArray} from "../../../../observable/list/ObservableArray.js";
import {ObservableArray} from "../../../../observable/list/ObservableArray";
import {UpdateAction} from "./UpdateAction.js";
export function tests() {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {SortedArray} from "../../../observable/list/SortedArray.js";
import {SortedArray} from "../../../observable/list/SortedArray";
import {ConnectionError} from "../../error.js";
import {PendingEvent, SendStatus} from "./PendingEvent.js";
import {makeTxnId, isTxnId} from "../../common.js";

View file

@ -20,11 +20,11 @@ import {MappedMap} from "./map/MappedMap.js";
import {JoinedMap} from "./map/JoinedMap.js";
import {BaseObservableMap} from "./map/BaseObservableMap.js";
// re-export "root" (of chain) collections
export { ObservableArray } from "./list/ObservableArray.js";
export { SortedArray } from "./list/SortedArray.js";
export { MappedList } from "./list/MappedList.js";
export { AsyncMappedList } from "./list/AsyncMappedList.js";
export { ConcatList } from "./list/ConcatList.js";
export { ObservableArray } from "./list/ObservableArray";
export { SortedArray } from "./list/SortedArray";
export { MappedList } from "./list/MappedList";
export { AsyncMappedList } from "./list/AsyncMappedList";
export { ConcatList } from "./list/ConcatList";
export { ObservableMap } from "./map/ObservableMap.js";
// avoid circular dependency between these classes

View file

@ -15,15 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
import {IListObserver} from "./BaseObservableList";
import {BaseMappedList, Mapper, Updater, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
export class AsyncMappedList extends BaseMappedList {
constructor(sourceList, mapper, updater, removeCallback) {
super(sourceList, mapper, updater, removeCallback);
this._eventQueue = null;
}
export class AsyncMappedList<F,T> extends BaseMappedList<F,T,Promise<T>> implements IListObserver<F> {
private _eventQueue: AsyncEvent<F>[] | null = null;
private _flushing: boolean = false;
onSubscribeFirst() {
onSubscribeFirst(): void {
this._sourceUnsubscribe = this._sourceList.subscribe(this);
this._eventQueue = [];
this._mappedValues = [];
@ -35,122 +34,112 @@ export class AsyncMappedList extends BaseMappedList {
this._flush();
}
async _flush() {
async _flush(): Promise<void> {
if (this._flushing) {
return;
}
this._flushing = true;
try {
while (this._eventQueue.length) {
const event = this._eventQueue.shift();
await event.run(this);
while (this._eventQueue!.length) {
const event = this._eventQueue!.shift();
await event!.run(this);
}
} finally {
this._flushing = false;
}
}
onReset() {
onReset(): void {
if (this._eventQueue) {
this._eventQueue.push(new ResetEvent());
this._flush();
}
}
onAdd(index, value) {
onAdd(index: number, value: F): void {
if (this._eventQueue) {
this._eventQueue.push(new AddEvent(index, value));
this._flush();
}
}
onUpdate(index, value, params) {
onUpdate(index: number, value: F, params: any): void {
if (this._eventQueue) {
this._eventQueue.push(new UpdateEvent(index, value, params));
this._flush();
}
}
onRemove(index) {
onRemove(index: number): void {
if (this._eventQueue) {
this._eventQueue.push(new RemoveEvent(index));
this._flush();
}
}
onMove(fromIdx, toIdx) {
onMove(fromIdx: number, toIdx: number): void {
if (this._eventQueue) {
this._eventQueue.push(new MoveEvent(fromIdx, toIdx));
this._flush();
}
}
onUnsubscribeLast() {
this._sourceUnsubscribe();
onUnsubscribeLast(): void {
this._sourceUnsubscribe!();
this._eventQueue = null;
this._mappedValues = null;
}
}
class AddEvent {
constructor(index, value) {
this.index = index;
this.value = value;
}
type AsyncEvent<F> = AddEvent<F> | UpdateEvent<F> | RemoveEvent<F> | MoveEvent<F> | ResetEvent<F>
async run(list) {
class AddEvent<F> {
constructor(public index: number, public value: F) {}
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
const mappedValue = await list._mapper(this.value);
runAdd(list, this.index, mappedValue);
}
}
class UpdateEvent {
constructor(index, value, params) {
this.index = index;
this.value = value;
this.params = params;
}
class UpdateEvent<F> {
constructor(public index: number, public value: F, public params: any) {}
async run(list) {
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
runUpdate(list, this.index, this.value, this.params);
}
}
class RemoveEvent {
constructor(index) {
this.index = index;
}
class RemoveEvent<F> {
constructor(public index: number) {}
async run(list) {
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
runRemove(list, this.index);
}
}
class MoveEvent {
constructor(fromIdx, toIdx) {
this.fromIdx = fromIdx;
this.toIdx = toIdx;
}
class MoveEvent<F> {
constructor(public fromIdx: number, public toIdx: number) {}
async run(list) {
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
runMove(list, this.fromIdx, this.toIdx);
}
}
class ResetEvent {
async run(list) {
class ResetEvent<F> {
async run<T>(list: AsyncMappedList<F,T>): Promise<void> {
runReset(list);
}
}
import {ObservableArray} from "./ObservableArray.js";
import {ObservableArray} from "./ObservableArray";
import {ListObserver} from "../../mocks/ListObserver.js";
export function tests() {
return {
"events are emitted in order": async assert => {
const double = n => n * n;
const source = new ObservableArray();
const source = new ObservableArray<number>();
const mapper = new AsyncMappedList(source, async n => {
await new Promise(r => setTimeout(r, n));
return {n: double(n)};

View file

@ -21,15 +21,15 @@ import {findAndUpdateInArray} from "./common";
export type Mapper<F,T> = (value: F) => T
export type Updater<F,T> = (mappedValue: T, params: any, value: F) => void;
export class BaseMappedList<F,T> extends BaseObservableList<T> {
export class BaseMappedList<F,T,R = T> extends BaseObservableList<T> {
protected _sourceList: BaseObservableList<F>;
protected _sourceUnsubscribe: (() => void) | null = null;
_mapper: Mapper<F,T>;
_updater: Updater<F,T>;
_mapper: Mapper<F,R>;
_updater?: Updater<F,T>;
_removeCallback?: (value: T) => void;
_mappedValues: T[] | null = null;
constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,T>, updater: Updater<F,T>, removeCallback?: (value: T) => void) {
constructor(sourceList: BaseObservableList<F>, mapper: Mapper<F,R>, updater?: Updater<F,T>, removeCallback?: (value: T) => void) {
super();
this._sourceList = sourceList;
this._mapper = mapper;
@ -50,12 +50,12 @@ export class BaseMappedList<F,T> extends BaseObservableList<T> {
}
}
export function runAdd<F,T>(list: BaseMappedList<F,T>, index: number, mappedValue: T): void {
export function runAdd<F,T,R>(list: BaseMappedList<F,T,R>, index: number, mappedValue: T): void {
list._mappedValues!.splice(index, 0, mappedValue);
list.emitAdd(index, mappedValue);
}
export function runUpdate<F,T>(list: BaseMappedList<F,T>, index: number, value: F, params: any): void {
export function runUpdate<F,T,R>(list: BaseMappedList<F,T,R>, index: number, value: F, params: any): void {
const mappedValue = list._mappedValues![index];
if (list._updater) {
list._updater(mappedValue, params, value);
@ -63,7 +63,7 @@ export function runUpdate<F,T>(list: BaseMappedList<F,T>, index: number, value:
list.emitUpdate(index, mappedValue, params);
}
export function runRemove<F,T>(list: BaseMappedList<F,T>, index: number): void {
export function runRemove<F,T,R>(list: BaseMappedList<F,T,R>, index: number): void {
const mappedValue = list._mappedValues![index];
list._mappedValues!.splice(index, 1);
if (list._removeCallback) {
@ -72,14 +72,14 @@ export function runRemove<F,T>(list: BaseMappedList<F,T>, index: number): void {
list.emitRemove(index, mappedValue);
}
export function runMove<F,T>(list: BaseMappedList<F,T>, fromIdx: number, toIdx: number): void {
export function runMove<F,T,R>(list: BaseMappedList<F,T,R>, fromIdx: number, toIdx: number): void {
const mappedValue = list._mappedValues![fromIdx];
list._mappedValues!.splice(fromIdx, 1);
list._mappedValues!.splice(toIdx, 0, mappedValue);
list.emitMove(fromIdx, toIdx, mappedValue);
}
export function runReset<F,T>(list: BaseMappedList<F,T>): void {
export function runReset<F,T,R>(list: BaseMappedList<F,T,R>): void {
list._mappedValues = [];
list.emitReset();
}

View file

@ -24,7 +24,18 @@ export interface IListObserver<T> {
onMove(from: number, to: number, value: T, list: BaseObservableList<T>): void
}
export abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> {
export function defaultObserverWith<T>(overrides: { [key in keyof IListObserver<T>]?: IListObserver<T>[key] }): IListObserver<T> {
const defaults = {
onReset(){},
onAdd(){},
onUpdate(){},
onRemove(){},
onMove(){},
}
return Object.assign(defaults, overrides);
}
export abstract class BaseObservableList<T> extends BaseObservable<IListObserver<T>> implements Iterable<T> {
emitReset() {
for(let h of this._handlers) {
h.onReset(this);
@ -38,7 +49,7 @@ export abstract class BaseObservableList<T> extends BaseObservable<IListObserver
}
}
emitUpdate(index: number, value: T, params: any): void {
emitUpdate(index: number, value: T, params?: any): void {
for(let h of this._handlers) {
h.onUpdate(index, value, params, this);
}
@ -58,6 +69,6 @@ export abstract class BaseObservableList<T> extends BaseObservable<IListObserver
}
}
abstract [Symbol.iterator](): IterableIterator<T>;
abstract [Symbol.iterator](): Iterator<T>;
abstract get length(): number;
}

View file

@ -14,16 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {BaseObservableList} from "./BaseObservableList";
import {BaseObservableList, IListObserver} from "./BaseObservableList";
export class ConcatList extends BaseObservableList {
constructor(...sourceLists) {
export class ConcatList<T> extends BaseObservableList<T> implements IListObserver<T> {
protected _sourceLists: BaseObservableList<T>[];
protected _sourceUnsubscribes: (() => void)[] | null = null;
constructor(...sourceLists: BaseObservableList<T>[]) {
super();
this._sourceLists = sourceLists;
this._sourceUnsubscribes = null;
}
_offsetForSource(sourceList) {
_offsetForSource(sourceList: BaseObservableList<T>): number {
const listIdx = this._sourceLists.indexOf(sourceList);
let offset = 0;
for (let i = 0; i < listIdx; ++i) {
@ -32,17 +34,17 @@ export class ConcatList extends BaseObservableList {
return offset;
}
onSubscribeFirst() {
onSubscribeFirst(): void {
this._sourceUnsubscribes = this._sourceLists.map(sourceList => sourceList.subscribe(this));
}
onUnsubscribeLast() {
for (const sourceUnsubscribe of this._sourceUnsubscribes) {
onUnsubscribeLast(): void {
for (const sourceUnsubscribe of this._sourceUnsubscribes!) {
sourceUnsubscribe();
}
}
onReset() {
onReset(): void {
// TODO: not ideal if other source lists are large
// but working impl for now
// reset, and
@ -54,11 +56,11 @@ export class ConcatList extends BaseObservableList {
}
}
onAdd(index, value, sourceList) {
onAdd(index: number, value: T, sourceList: BaseObservableList<T>): void {
this.emitAdd(this._offsetForSource(sourceList) + index, value);
}
onUpdate(index, value, params, sourceList) {
onUpdate(index: number, value: T, params: any, sourceList: BaseObservableList<T>): void {
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
// as we are not supposed to call `length` on any uninitialized list
if (!this._sourceUnsubscribes) {
@ -67,16 +69,16 @@ export class ConcatList extends BaseObservableList {
this.emitUpdate(this._offsetForSource(sourceList) + index, value, params);
}
onRemove(index, value, sourceList) {
onRemove(index: number, value: T, sourceList: BaseObservableList<T>): void {
this.emitRemove(this._offsetForSource(sourceList) + index, value);
}
onMove(fromIdx, toIdx, value, sourceList) {
onMove(fromIdx: number, toIdx: number, value: T, sourceList: BaseObservableList<T>): void {
const offset = this._offsetForSource(sourceList);
this.emitMove(offset + fromIdx, offset + toIdx, value);
}
get length() {
get length(): number {
let len = 0;
for (let i = 0; i < this._sourceLists.length; ++i) {
len += this._sourceLists[i].length;
@ -104,7 +106,8 @@ export class ConcatList extends BaseObservableList {
}
}
import {ObservableArray} from "./ObservableArray.js";
import {ObservableArray} from "./ObservableArray";
import {defaultObserverWith} from "./BaseObservableList";
export async function tests() {
return {
test_length(assert) {
@ -133,13 +136,13 @@ export async function tests() {
const list2 = new ObservableArray([11, 12, 13]);
const all = new ConcatList(list1, list2);
let fired = false;
all.subscribe({
all.subscribe(defaultObserverWith({
onAdd(index, value) {
fired = true;
assert.equal(index, 4);
assert.equal(value, 11.5);
}
});
}));
list2.insert(1, 11.5);
assert(fired);
},
@ -148,13 +151,13 @@ export async function tests() {
const list2 = new ObservableArray([11, 12, 13]);
const all = new ConcatList(list1, list2);
let fired = false;
all.subscribe({
all.subscribe(defaultObserverWith({
onUpdate(index, value) {
fired = true;
assert.equal(index, 4);
assert.equal(value, 10);
}
});
}));
list2.emitUpdate(1, 10);
assert(fired);
},

View file

@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {IListObserver} from "./BaseObservableList";
import {BaseMappedList, runAdd, runUpdate, runRemove, runMove, runReset} from "./BaseMappedList";
export class MappedList extends BaseMappedList {
export class MappedList<F,T> extends BaseMappedList<F,T> implements IListObserver<F> {
onSubscribeFirst() {
this._sourceUnsubscribe = this._sourceList.subscribe(this);
this._mappedValues = [];
@ -26,16 +27,16 @@ export class MappedList extends BaseMappedList {
}
}
onReset() {
onReset(): void {
runReset(this);
}
onAdd(index, value) {
onAdd(index: number, value: F): void {
const mappedValue = this._mapper(value);
runAdd(this, index, mappedValue);
}
onUpdate(index, value, params) {
onUpdate(index: number, value: F, params: any): void {
// if an update is emitted while calling source.subscribe() from onSubscribeFirst, ignore it
if (!this._mappedValues) {
return;
@ -43,24 +44,25 @@ export class MappedList extends BaseMappedList {
runUpdate(this, index, value, params);
}
onRemove(index) {
onRemove(index: number): void {
runRemove(this, index);
}
onMove(fromIdx, toIdx) {
onMove(fromIdx: number, toIdx: number): void {
runMove(this, fromIdx, toIdx);
}
onUnsubscribeLast() {
this._sourceUnsubscribe();
onUnsubscribeLast(): void {
this._sourceUnsubscribe!();
}
}
import {ObservableArray} from "./ObservableArray.js";
import {ObservableArray} from "./ObservableArray";
import {BaseObservableList} from "./BaseObservableList";
import {defaultObserverWith} from "./BaseObservableList";
export async function tests() {
class MockList extends BaseObservableList {
class MockList extends BaseObservableList<number> {
get length() {
return 0;
}
@ -74,26 +76,26 @@ export async function tests() {
const source = new MockList();
const mapped = new MappedList(source, n => {return {n: n*n};});
let fired = false;
const unsubscribe = mapped.subscribe({
const unsubscribe = mapped.subscribe(defaultObserverWith({
onAdd(idx, value) {
fired = true;
assert.equal(idx, 0);
assert.equal(value.n, 36);
}
});
}));
source.emitAdd(0, 6);
assert(fired);
unsubscribe();
},
test_update(assert) {
const source = new MockList();
const mapped = new MappedList(
const mapped = new MappedList<number, { n: number, m?: number }>(
source,
n => {return {n: n*n};},
(o, p, n) => o.m = n*n
);
let fired = false;
const unsubscribe = mapped.subscribe({
const unsubscribe = mapped.subscribe(defaultObserverWith({
onAdd() {},
onUpdate(idx, value) {
fired = true;
@ -101,7 +103,7 @@ export async function tests() {
assert.equal(value.n, 36);
assert.equal(value.m, 49);
}
});
}));
source.emitAdd(0, 6);
source.emitUpdate(0, 7);
assert(fired);
@ -113,9 +115,9 @@ export async function tests() {
source,
n => {return n*n;}
);
mapped.subscribe({
mapped.subscribe(defaultObserverWith({
onUpdate() { assert.fail(); }
});
}));
assert.equal(mapped.findAndUpdate(
n => n === 100,
() => assert.fail()
@ -127,9 +129,9 @@ export async function tests() {
source,
n => {return n*n;}
);
mapped.subscribe({
mapped.subscribe(defaultObserverWith({
onUpdate() { assert.fail(); }
});
}));
let fired = false;
assert.equal(mapped.findAndUpdate(
n => n === 9,
@ -148,14 +150,14 @@ export async function tests() {
n => {return n*n;}
);
let fired = false;
mapped.subscribe({
mapped.subscribe(defaultObserverWith({
onUpdate(idx, n, params) {
assert.equal(idx, 1);
assert.equal(n, 9);
assert.equal(params, "param");
fired = true;
}
});
}));
assert.equal(mapped.findAndUpdate(n => n === 9, () => "param"), true);
assert.equal(fired, true);
},

View file

@ -16,35 +16,37 @@ limitations under the License.
import {BaseObservableList} from "./BaseObservableList";
export class ObservableArray extends BaseObservableList {
constructor(initialValues = []) {
export class ObservableArray<T> extends BaseObservableList<T> {
private _items: T[];
constructor(initialValues: T[] = []) {
super();
this._items = initialValues;
}
append(item) {
append(item: T): void {
this._items.push(item);
this.emitAdd(this._items.length - 1, item);
}
remove(idx) {
remove(idx: number): void {
const [item] = this._items.splice(idx, 1);
this.emitRemove(idx, item);
}
insertMany(idx, items) {
insertMany(idx: number, items: T[]): void {
for(let item of items) {
this.insert(idx, item);
idx += 1;
}
}
insert(idx, item) {
insert(idx: number, item: T): void {
this._items.splice(idx, 0, item);
this.emitAdd(idx, item);
}
move(fromIdx, toIdx) {
move(fromIdx: number, toIdx: number): void {
if (fromIdx < this._items.length && toIdx < this._items.length) {
const [item] = this._items.splice(fromIdx, 1);
this._items.splice(toIdx, 0, item);
@ -52,24 +54,24 @@ export class ObservableArray extends BaseObservableList {
}
}
update(idx, item, params = null) {
update(idx: number, item: T, params: any = null): void {
if (idx < this._items.length) {
this._items[idx] = item;
this.emitUpdate(idx, item, params);
}
}
get array() {
get array(): Readonly<T[]> {
return this._items;
}
at(idx) {
at(idx: number): T | undefined {
if (this._items && idx >= 0 && idx < this._items.length) {
return this._items[idx];
}
}
get length() {
get length(): number {
return this._items.length;
}

View file

@ -18,18 +18,20 @@ import {BaseObservableList} from "./BaseObservableList";
import {sortedIndex} from "../../utils/sortedIndex";
import {findAndUpdateInArray} from "./common";
export class SortedArray extends BaseObservableList {
constructor(comparator) {
export class SortedArray<T> extends BaseObservableList<T> {
private _comparator: (left: T, right: T) => number;
private _items: T[] = [];
constructor(comparator: (left: T, right: T) => number) {
super();
this._comparator = comparator;
this._items = [];
}
setManyUnsorted(items) {
setManyUnsorted(items: T[]): void {
this.setManySorted(items);
}
setManySorted(items) {
setManySorted(items: T[]): void {
// TODO: we can make this way faster by only looking up the first and last key,
// and merging whatever is inbetween with items
// if items is not sorted, 💩🌀 will follow!
@ -42,11 +44,11 @@ export class SortedArray extends BaseObservableList {
}
}
findAndUpdate(predicate, updater) {
findAndUpdate(predicate: (value: T) => boolean, updater: (value: T) => any | false): boolean {
return findAndUpdateInArray(predicate, this._items, this, updater);
}
getAndUpdate(item, updater, updateParams = null) {
getAndUpdate(item: T, updater: (existing: T, item: T) => any, updateParams: any = null): void {
const idx = this.indexOf(item);
if (idx !== -1) {
const existingItem = this._items[idx];
@ -56,7 +58,7 @@ export class SortedArray extends BaseObservableList {
}
}
update(item, updateParams = null) {
update(item: T, updateParams: any = null): void {
const idx = this.indexOf(item);
if (idx !== -1) {
this._items[idx] = item;
@ -64,7 +66,7 @@ export class SortedArray extends BaseObservableList {
}
}
indexOf(item) {
indexOf(item: T): number {
const idx = sortedIndex(this._items, item, this._comparator);
if (idx < this._items.length && this._comparator(this._items[idx], item) === 0) {
return idx;
@ -73,7 +75,7 @@ export class SortedArray extends BaseObservableList {
}
}
_getNext(item) {
_getNext(item: T): T | undefined {
let idx = sortedIndex(this._items, item, this._comparator);
while(idx < this._items.length && this._comparator(this._items[idx], item) <= 0) {
idx += 1;
@ -81,7 +83,7 @@ export class SortedArray extends BaseObservableList {
return this.get(idx);
}
set(item, updateParams = null) {
set(item: T, updateParams: any = null): void {
const idx = sortedIndex(this._items, item, this._comparator);
if (idx >= this._items.length || this._comparator(this._items[idx], item) !== 0) {
this._items.splice(idx, 0, item);
@ -92,21 +94,21 @@ export class SortedArray extends BaseObservableList {
}
}
get(idx) {
get(idx: number): T | undefined {
return this._items[idx];
}
remove(idx) {
remove(idx: number): void {
const item = this._items[idx];
this._items.splice(idx, 1);
this.emitRemove(idx, item);
}
get array() {
get array(): T[] {
return this._items;
}
get length() {
get length(): number {
return this._items.length;
}
@ -116,8 +118,11 @@ export class SortedArray extends BaseObservableList {
}
// iterator that works even if the current value is removed while iterating
class Iterator {
constructor(sortedArray) {
class Iterator<T> {
private _sortedArray: SortedArray<T> | null
private _current: T | null | undefined
constructor(sortedArray: SortedArray<T>) {
this._sortedArray = sortedArray;
this._current = null;
}
@ -145,7 +150,7 @@ class Iterator {
export function tests() {
return {
"setManyUnsorted": assert => {
const sa = new SortedArray((a, b) => a.localeCompare(b));
const sa = new SortedArray<string>((a, b) => a.localeCompare(b));
sa.setManyUnsorted(["b", "a", "c"]);
assert.equal(sa.length, 3);
assert.equal(sa.get(0), "a");
@ -153,7 +158,7 @@ export function tests() {
assert.equal(sa.get(2), "c");
},
"_getNext": assert => {
const sa = new SortedArray((a, b) => a.localeCompare(b));
const sa = new SortedArray<string>((a, b) => a.localeCompare(b));
sa.setManyUnsorted(["b", "a", "f"]);
assert.equal(sa._getNext("a"), "b");
assert.equal(sa._getNext("b"), "f");
@ -162,7 +167,7 @@ export function tests() {
assert.equal(sa._getNext("f"), undefined);
},
"iterator with removals": assert => {
const queue = new SortedArray((a, b) => a.idx - b.idx);
const queue = new SortedArray<{idx: number}>((a, b) => a.idx - b.idx);
queue.setManyUnsorted([{idx: 5}, {idx: 3}, {idx: 1}, {idx: 4}, {idx: 2}]);
const it = queue[Symbol.iterator]();
assert.equal(it.next().value.idx, 1);

View file

@ -15,6 +15,7 @@ limitations under the License.
*/
import {Range, RangeZone} from "./Range";
import {defaultObserverWith} from "../../../../observable/list/BaseObservableList";
function skipOnIterator<T>(it: Iterator<T>, pos: number): boolean {
let i = 0;
@ -268,7 +269,7 @@ export function tests() {
const list = new ObservableArray(["b", "c", "d", "e"]);
const range = new ListRange(1, 3, list.length);
let added = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onAdd(idx, value) {
added = true;
const result = range.queryAdd(idx, value, list);
@ -280,7 +281,7 @@ export function tests() {
newRange: new ListRange(1, 3, 5)
});
}
});
}));
list.insert(0, "a");
assert(added);
},
@ -288,7 +289,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "d", "e"]);
const range = new ListRange(1, 3, list.length);
let added = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onAdd(idx, value) {
added = true;
const result = range.queryAdd(idx, value, list);
@ -300,7 +301,7 @@ export function tests() {
newRange: new ListRange(1, 3, 5)
});
}
});
}));
list.insert(2, "c");
assert(added);
},
@ -308,7 +309,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d"]);
const range = new ListRange(1, 3, list.length);
let added = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onAdd(idx, value) {
added = true;
const result = range.queryAdd(idx, value, list);
@ -317,7 +318,7 @@ export function tests() {
newRange: new ListRange(1, 3, 5)
});
}
});
}));
list.insert(4, "e");
assert(added);
},
@ -326,7 +327,7 @@ export function tests() {
const viewportItemCount = 4;
const range = new ListRange(0, 3, list.length, viewportItemCount);
let added = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onAdd(idx, value) {
added = true;
const result = range.queryAdd(idx, value, list);
@ -337,7 +338,7 @@ export function tests() {
value: "c"
});
}
});
}));
list.insert(2, "c");
assert(added);
},
@ -345,7 +346,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(1, 3, list.length);
let removed = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onRemove(idx) {
removed = true;
const result = range.queryRemove(idx, list);
@ -357,7 +358,7 @@ export function tests() {
newRange: new ListRange(1, 3, 4)
});
}
});
}));
list.remove(0);
assert(removed);
},
@ -365,7 +366,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(1, 3, list.length);
let removed = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onRemove(idx) {
removed = true;
const result = range.queryRemove(idx, list);
@ -378,7 +379,7 @@ export function tests() {
});
assert.equal(list.length, 4);
}
});
}));
list.remove(2);
assert(removed);
},
@ -386,7 +387,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(1, 3, list.length);
let removed = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onRemove(idx) {
removed = true;
const result = range.queryRemove(idx, list);
@ -395,7 +396,7 @@ export function tests() {
newRange: new ListRange(1, 3, 4)
});
}
});
}));
list.remove(3);
assert(removed);
},
@ -403,7 +404,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c"]);
const range = new ListRange(1, 3, list.length);
let removed = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onRemove(idx) {
removed = true;
const result = range.queryRemove(idx, list);
@ -415,7 +416,7 @@ export function tests() {
value: "a"
});
}
});
}));
list.remove(2);
assert(removed);
},
@ -423,7 +424,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c"]);
const range = new ListRange(0, 3, list.length);
let removed = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onRemove(idx) {
removed = true;
const result = range.queryRemove(idx, list);
@ -433,7 +434,7 @@ export function tests() {
removeIdx: 2,
});
}
});
}));
list.remove(2);
assert(removed);
},
@ -441,7 +442,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(1, 4, list.length);
let moved = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onMove(fromIdx, toIdx, value) {
moved = true;
const result = range.queryMove(fromIdx, toIdx, value, list);
@ -451,7 +452,7 @@ export function tests() {
toIdx: 3
});
}
});
}));
list.move(2, 3);
assert(moved);
},
@ -459,7 +460,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(2, 5, list.length);
let moved = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onMove(fromIdx, toIdx, value) {
moved = true;
const result = range.queryMove(fromIdx, toIdx, value, list);
@ -470,7 +471,7 @@ export function tests() {
value: "a"
});
}
});
}));
list.move(0, 3); // move "a" to after "d"
assert(moved);
},
@ -478,7 +479,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(0, 3, list.length);
let moved = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onMove(fromIdx, toIdx, value) {
moved = true;
const result = range.queryMove(fromIdx, toIdx, value, list);
@ -489,7 +490,7 @@ export function tests() {
value: "e"
});
}
});
}));
list.move(4, 1); // move "e" to before "b"
assert(moved);
},
@ -497,7 +498,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(0, 3, list.length);
let moved = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onMove(fromIdx, toIdx, value) {
moved = true;
const result = range.queryMove(fromIdx, toIdx, value, list);
@ -508,7 +509,7 @@ export function tests() {
value: "d"
});
}
});
}));
list.move(1, 3); // move "b" to after "d"
assert(moved);
},
@ -516,7 +517,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(2, 5, list.length);
let moved = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onMove(fromIdx, toIdx, value) {
moved = true;
const result = range.queryMove(fromIdx, toIdx, value, list);
@ -527,7 +528,7 @@ export function tests() {
value: "b"
});
}
});
}));
list.move(3, 0); // move "d" to before "a"
assert(moved);
},
@ -535,7 +536,7 @@ export function tests() {
const list = new ObservableArray(["a", "b", "c", "d", "e"]);
const range = new ListRange(1, 4, list.length);
let moved = false;
list.subscribe({
list.subscribe(defaultObserverWith({
onMove(fromIdx, toIdx, value) {
moved = true;
const result = range.queryMove(fromIdx, toIdx, value, list);
@ -546,7 +547,7 @@ export function tests() {
value: "e"
});
}
});
}));
list.move(0, 4); // move "a" to after "e"
assert(moved);
},

View file

@ -43,7 +43,7 @@ export class Range {
return range.start < this.end && this.start < range.end;
}
forEachInIterator<T>(it: IterableIterator<T>, callback: ((T, i: number) => void)) {
forEachInIterator<T>(it: Iterator<T>, callback: ((T, i: number) => void)) {
let i = 0;
for (i = 0; i < this.start; i += 1) {
it.next();