mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge VS Code 1.31.1 (#4283)
This commit is contained in:
@@ -19,9 +19,348 @@ export interface Event<T> {
|
||||
export namespace Event {
|
||||
const _disposable = { dispose() { } };
|
||||
export const None: Event<any> = function () { return _disposable; };
|
||||
|
||||
/**
|
||||
* Given an event, returns another event which only fires once.
|
||||
*/
|
||||
export function once<T>(event: Event<T>): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => {
|
||||
// we need this, in case the event fires during the listener call
|
||||
let didFire = false;
|
||||
|
||||
const result = event(e => {
|
||||
if (didFire) {
|
||||
return;
|
||||
} else if (result) {
|
||||
result.dispose();
|
||||
} else {
|
||||
didFire = true;
|
||||
}
|
||||
|
||||
return listener.call(thisArgs, e);
|
||||
}, null, disposables);
|
||||
|
||||
if (didFire) {
|
||||
result.dispose();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event and a `map` function, returns another event which maps each element
|
||||
* throught the mapping function.
|
||||
*/
|
||||
export function map<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
|
||||
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event and an `each` function, returns another identical event and calls
|
||||
* the `each` function per each element.
|
||||
*/
|
||||
export function forEach<I>(event: Event<I>, each: (i: I) => void): Event<I> {
|
||||
return (listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event and a `filter` function, returns another event which emits those
|
||||
* elements for which the `filter` function returns `true`.
|
||||
*/
|
||||
export function filter<T>(event: Event<T>, filter: (e: T) => boolean): Event<T>;
|
||||
export function filter<T, R>(event: Event<T | R>, filter: (e: T | R) => e is R): Event<R>;
|
||||
export function filter<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event, returns the same event but typed as `Event<void>`.
|
||||
*/
|
||||
export function signal<T>(event: Event<T>): Event<void> {
|
||||
return event as Event<any> as Event<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a collection of events, returns a single event which emits
|
||||
* whenever any of the provided events emit.
|
||||
*/
|
||||
export function any<T>(...events: Event<T>[]): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event and a `merge` function, returns another event which maps each element
|
||||
* and the cummulative result throught the `merge` function. Similar to `map`, but with memory.
|
||||
*/
|
||||
export function reduce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, initial?: O): Event<O> {
|
||||
let output: O | undefined = initial;
|
||||
|
||||
return map<I, O>(event, e => {
|
||||
output = merge(output, e);
|
||||
return output;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Debounces the provided event, given a `merge` function.
|
||||
*
|
||||
* @param event The input event.
|
||||
* @param merge The reducing function.
|
||||
* @param delay The debouncing delay in millis.
|
||||
* @param leading Whether the event should fire in the leading phase of the timeout.
|
||||
* @param leakWarningThreshold The leak warning threshold override.
|
||||
*/
|
||||
export function debounce<T>(event: Event<T>, merge: (last: T, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<T>;
|
||||
export function debounce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<O>;
|
||||
export function debounce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, delay: number = 100, leading = false, leakWarningThreshold?: number): Event<O> {
|
||||
|
||||
let subscription: IDisposable;
|
||||
let output: O | undefined = undefined;
|
||||
let handle: any = undefined;
|
||||
let numDebouncedCalls = 0;
|
||||
|
||||
const emitter = new Emitter<O>({
|
||||
leakWarningThreshold,
|
||||
onFirstListenerAdd() {
|
||||
subscription = event(cur => {
|
||||
numDebouncedCalls++;
|
||||
output = merge(output, cur);
|
||||
|
||||
if (leading && !handle) {
|
||||
emitter.fire(output);
|
||||
}
|
||||
|
||||
clearTimeout(handle);
|
||||
handle = setTimeout(() => {
|
||||
let _output = output;
|
||||
output = undefined;
|
||||
handle = undefined;
|
||||
if (!leading || numDebouncedCalls > 1) {
|
||||
emitter.fire(_output!);
|
||||
}
|
||||
|
||||
numDebouncedCalls = 0;
|
||||
}, delay);
|
||||
});
|
||||
},
|
||||
onLastListenerRemove() {
|
||||
subscription.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event, it returns another event which fires only once and as soon as
|
||||
* the input event emits. The event data is the number of millis it took for the
|
||||
* event to fire.
|
||||
*/
|
||||
export function stopwatch<T>(event: Event<T>): Event<number> {
|
||||
const start = new Date().getTime();
|
||||
return map(once(event), _ => new Date().getTime() - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an event, it returns another event which fires only when the event
|
||||
* element changes.
|
||||
*/
|
||||
export function latch<T>(event: Event<T>): Event<T> {
|
||||
let firstCall = true;
|
||||
let cache: T;
|
||||
|
||||
return filter(event, value => {
|
||||
let shouldEmit = firstCall || value !== cache;
|
||||
firstCall = false;
|
||||
cache = value;
|
||||
return shouldEmit;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffers the provided event until a first listener comes
|
||||
* along, at which point fire all the events at once and
|
||||
* pipe the event from then on.
|
||||
*
|
||||
* ```typescript
|
||||
* const emitter = new Emitter<number>();
|
||||
* const event = emitter.event;
|
||||
* const bufferedEvent = buffer(event);
|
||||
*
|
||||
* emitter.fire(1);
|
||||
* emitter.fire(2);
|
||||
* emitter.fire(3);
|
||||
* // nothing...
|
||||
*
|
||||
* const listener = bufferedEvent(num => console.log(num));
|
||||
* // 1, 2, 3
|
||||
*
|
||||
* emitter.fire(4);
|
||||
* // 4
|
||||
* ```
|
||||
*/
|
||||
export function buffer<T>(event: Event<T>, nextTick = false, _buffer: T[] = []): Event<T> {
|
||||
let buffer: T[] | null = _buffer.slice();
|
||||
|
||||
let listener: IDisposable | null = event(e => {
|
||||
if (buffer) {
|
||||
buffer.push(e);
|
||||
} else {
|
||||
emitter.fire(e);
|
||||
}
|
||||
});
|
||||
|
||||
const flush = () => {
|
||||
if (buffer) {
|
||||
buffer.forEach(e => emitter.fire(e));
|
||||
}
|
||||
buffer = null;
|
||||
};
|
||||
|
||||
const emitter = new Emitter<T>({
|
||||
onFirstListenerAdd() {
|
||||
if (!listener) {
|
||||
listener = event(e => emitter.fire(e));
|
||||
}
|
||||
},
|
||||
|
||||
onFirstListenerDidAdd() {
|
||||
if (buffer) {
|
||||
if (nextTick) {
|
||||
setTimeout(flush);
|
||||
} else {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLastListenerRemove() {
|
||||
if (listener) {
|
||||
listener.dispose();
|
||||
}
|
||||
listener = null;
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to `buffer` but it buffers indefinitely and repeats
|
||||
* the buffered events to every new listener.
|
||||
*/
|
||||
export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): Event<T> {
|
||||
buffer = buffer.slice();
|
||||
|
||||
event(e => {
|
||||
buffer.push(e);
|
||||
emitter.fire(e);
|
||||
});
|
||||
|
||||
const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e));
|
||||
|
||||
const emitter = new Emitter<T>({
|
||||
onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) {
|
||||
if (nextTick) {
|
||||
setTimeout(() => flush(listener, thisArgs));
|
||||
} else {
|
||||
flush(listener, thisArgs);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
export interface IChainableEvent<T> {
|
||||
event: Event<T>;
|
||||
map<O>(fn: (i: T) => O): IChainableEvent<O>;
|
||||
forEach(fn: (i: T) => void): IChainableEvent<T>;
|
||||
filter(fn: (e: T) => boolean): IChainableEvent<T>;
|
||||
reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R>;
|
||||
latch(): IChainableEvent<T>;
|
||||
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
}
|
||||
|
||||
class ChainableEvent<T> implements IChainableEvent<T> {
|
||||
|
||||
get event(): Event<T> { return this._event; }
|
||||
|
||||
constructor(private _event: Event<T>) { }
|
||||
|
||||
map<O>(fn: (i: T) => O): IChainableEvent<O> {
|
||||
return new ChainableEvent(map(this._event, fn));
|
||||
}
|
||||
|
||||
forEach(fn: (i: T) => void): IChainableEvent<T> {
|
||||
return new ChainableEvent(forEach(this._event, fn));
|
||||
}
|
||||
|
||||
filter(fn: (e: T) => boolean): IChainableEvent<T> {
|
||||
return new ChainableEvent(filter(this._event, fn));
|
||||
}
|
||||
|
||||
reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R> {
|
||||
return new ChainableEvent(reduce(this._event, merge, initial));
|
||||
}
|
||||
|
||||
latch(): IChainableEvent<T> {
|
||||
return new ChainableEvent(latch(this._event));
|
||||
}
|
||||
|
||||
on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return this._event(listener, thisArgs, disposables);
|
||||
}
|
||||
|
||||
once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return once(this._event)(listener, thisArgs, disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export function chain<T>(event: Event<T>): IChainableEvent<T> {
|
||||
return new ChainableEvent(event);
|
||||
}
|
||||
|
||||
export interface NodeEventEmitter {
|
||||
on(event: string | symbol, listener: Function): this;
|
||||
removeListener(event: string | symbol, listener: Function): this;
|
||||
}
|
||||
|
||||
export function fromNodeEventEmitter<T>(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
|
||||
const fn = (...args: any[]) => result.fire(map(...args));
|
||||
const onFirstListenerAdd = () => emitter.on(eventName, fn);
|
||||
const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
|
||||
const result = new Emitter<T>({ onFirstListenerAdd, onLastListenerRemove });
|
||||
|
||||
return result.event;
|
||||
}
|
||||
|
||||
export function fromPromise<T = any>(promise: Promise<T>): Event<undefined> {
|
||||
const emitter = new Emitter<undefined>();
|
||||
let shouldEmit = false;
|
||||
|
||||
promise
|
||||
.then(undefined, () => null)
|
||||
.then(() => {
|
||||
if (!shouldEmit) {
|
||||
setTimeout(() => emitter.fire(undefined), 0);
|
||||
} else {
|
||||
emitter.fire(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
shouldEmit = true;
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
export function toPromise<T>(event: Event<T>): Promise<T> {
|
||||
return new Promise(c => once(event)(c));
|
||||
}
|
||||
}
|
||||
|
||||
type Listener = [Function, any] | Function;
|
||||
type Listener<T> = [(e: T) => void, any] | ((e: T) => void);
|
||||
|
||||
export interface EmitterOptions {
|
||||
onFirstListenerAdd?: Function;
|
||||
@@ -80,7 +419,7 @@ class LeakageMonitor {
|
||||
if (this._warnCountdown <= 0) {
|
||||
// only warn on first exceed and then every time the limit
|
||||
// is exceeded by 50% again
|
||||
this._warnCountdown = threshold * .5;
|
||||
this._warnCountdown = threshold * 0.5;
|
||||
|
||||
// find most frequent listener and print warning
|
||||
let topStack: string;
|
||||
@@ -128,12 +467,12 @@ export class Emitter<T> {
|
||||
|
||||
private static readonly _noop = function () { };
|
||||
|
||||
private readonly _options: EmitterOptions | undefined;
|
||||
private readonly _leakageMon: LeakageMonitor | undefined;
|
||||
private readonly _options?: EmitterOptions;
|
||||
private readonly _leakageMon?: LeakageMonitor;
|
||||
private _disposed: boolean = false;
|
||||
private _event: Event<T> | undefined;
|
||||
private _deliveryQueue: [Listener, (T | undefined)][] | undefined;
|
||||
protected _listeners: LinkedList<Listener> | undefined;
|
||||
private _event?: Event<T>;
|
||||
private _deliveryQueue: [Listener<T>, T][];
|
||||
protected _listeners?: LinkedList<Listener<T>>;
|
||||
|
||||
constructor(options?: EmitterOptions) {
|
||||
this._options = options;
|
||||
@@ -207,7 +546,7 @@ export class Emitter<T> {
|
||||
* To be kept private to fire an event to
|
||||
* subscribers
|
||||
*/
|
||||
fire(event?: T): any {
|
||||
fire(event: T): void {
|
||||
if (this._listeners) {
|
||||
// put all [listener,event]-pairs into delivery queue
|
||||
// then emit all event. an inner/nested event might be
|
||||
@@ -251,14 +590,14 @@ export class Emitter<T> {
|
||||
}
|
||||
|
||||
export interface IWaitUntil {
|
||||
waitUntil(thenable: Thenable<any>): void;
|
||||
waitUntil(thenable: Promise<any>): void;
|
||||
}
|
||||
|
||||
export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
||||
|
||||
private _asyncDeliveryQueue: [Listener, T, Thenable<any>[]][];
|
||||
private _asyncDeliveryQueue: [Listener<T>, T, Promise<any>[]][];
|
||||
|
||||
async fireAsync(eventFn: (thenables: Thenable<any>[], listener: Function) => T): Promise<void> {
|
||||
async fireAsync(eventFn: (thenables: Promise<any>[], listener: Function) => T): Promise<void> {
|
||||
if (!this._listeners) {
|
||||
return;
|
||||
}
|
||||
@@ -271,7 +610,7 @@ export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
|
||||
}
|
||||
|
||||
for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
|
||||
let thenables: Thenable<void>[] = [];
|
||||
let thenables: Promise<void>[] = [];
|
||||
this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]);
|
||||
}
|
||||
|
||||
@@ -359,98 +698,6 @@ export class EventMultiplexer<T> implements IDisposable {
|
||||
}
|
||||
}
|
||||
|
||||
export function fromPromise<T =any>(promise: Thenable<T>): Event<T> {
|
||||
const emitter = new Emitter<T>();
|
||||
let shouldEmit = false;
|
||||
|
||||
promise
|
||||
.then(undefined, () => null)
|
||||
.then(() => {
|
||||
if (!shouldEmit) {
|
||||
setTimeout(() => emitter.fire(), 0);
|
||||
} else {
|
||||
emitter.fire();
|
||||
}
|
||||
});
|
||||
|
||||
shouldEmit = true;
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
export function toPromise<T>(event: Event<T>): Thenable<T> {
|
||||
return new Promise(c => once(event)(c));
|
||||
}
|
||||
|
||||
export function once<T>(event: Event<T>): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => {
|
||||
// we need this, in case the event fires during the listener call
|
||||
let didFire = false;
|
||||
|
||||
const result = event(e => {
|
||||
if (didFire) {
|
||||
return;
|
||||
} else if (result) {
|
||||
result.dispose();
|
||||
} else {
|
||||
didFire = true;
|
||||
}
|
||||
|
||||
return listener.call(thisArgs, e);
|
||||
}, null, disposables);
|
||||
|
||||
if (didFire) {
|
||||
result.dispose();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function anyEvent<T>(...events: Event<T>[]): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => combinedDisposable(events.map(event => event(e => listener.call(thisArgs, e), null, disposables)));
|
||||
}
|
||||
|
||||
export function debounceEvent<T>(event: Event<T>, merger: (last: T, event: T) => T, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<T>;
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O | undefined, event: I) => O, delay?: number, leading?: boolean, leakWarningThreshold?: number): Event<O>;
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O | undefined, event: I) => O, delay: number = 100, leading = false, leakWarningThreshold?: number): Event<O> {
|
||||
|
||||
let subscription: IDisposable;
|
||||
let output: O | undefined = undefined;
|
||||
let handle: any = undefined;
|
||||
let numDebouncedCalls = 0;
|
||||
|
||||
const emitter = new Emitter<O>({
|
||||
leakWarningThreshold,
|
||||
onFirstListenerAdd() {
|
||||
subscription = event(cur => {
|
||||
numDebouncedCalls++;
|
||||
output = merger(output, cur);
|
||||
|
||||
if (leading && !handle) {
|
||||
emitter.fire(output);
|
||||
}
|
||||
|
||||
clearTimeout(handle);
|
||||
handle = setTimeout(() => {
|
||||
let _output = output;
|
||||
output = undefined;
|
||||
handle = undefined;
|
||||
if (!leading || numDebouncedCalls > 1) {
|
||||
emitter.fire(_output);
|
||||
}
|
||||
|
||||
numDebouncedCalls = 0;
|
||||
}, delay);
|
||||
});
|
||||
},
|
||||
onLastListenerRemove() {
|
||||
subscription.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* The EventBufferer is useful in situations in which you want
|
||||
* to delay firing your events during some code.
|
||||
@@ -485,12 +732,12 @@ export class EventBufferer {
|
||||
} else {
|
||||
listener.call(thisArgs, i);
|
||||
}
|
||||
}, void 0, disposables);
|
||||
}, undefined, disposables);
|
||||
};
|
||||
}
|
||||
|
||||
bufferEvents<R = void>(fn: () => R): R {
|
||||
const buffer: Function[] = [];
|
||||
const buffer: Array<() => R> = [];
|
||||
this.buffers.push(buffer);
|
||||
const r = fn();
|
||||
this.buffers.pop();
|
||||
@@ -499,169 +746,12 @@ export class EventBufferer {
|
||||
}
|
||||
}
|
||||
|
||||
export interface IChainableEvent<T> {
|
||||
event: Event<T>;
|
||||
map<O>(fn: (i: T) => O): IChainableEvent<O>;
|
||||
forEach(fn: (i: T) => void): IChainableEvent<T>;
|
||||
filter(fn: (e: T) => boolean): IChainableEvent<T>;
|
||||
latch(): IChainableEvent<T>;
|
||||
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
|
||||
}
|
||||
|
||||
export function mapEvent<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
|
||||
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
|
||||
}
|
||||
|
||||
export function forEach<I>(event: Event<I>, each: (i: I) => void): Event<I> {
|
||||
return (listener, thisArgs = null, disposables?) => event(i => { each(i); listener.call(thisArgs, i); }, null, disposables);
|
||||
}
|
||||
|
||||
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T>;
|
||||
export function filterEvent<T, R>(event: Event<T | R>, filter: (e: T | R) => e is R): Event<R>;
|
||||
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
|
||||
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
|
||||
}
|
||||
|
||||
export function signalEvent<T>(event: Event<T>): Event<void> {
|
||||
return event as Event<any> as Event<void>;
|
||||
}
|
||||
|
||||
class ChainableEvent<T> implements IChainableEvent<T> {
|
||||
|
||||
get event(): Event<T> { return this._event; }
|
||||
|
||||
constructor(private _event: Event<T>) { }
|
||||
|
||||
map<O>(fn: (i: T) => O): IChainableEvent<O> {
|
||||
return new ChainableEvent(mapEvent(this._event, fn));
|
||||
}
|
||||
|
||||
forEach(fn: (i: T) => void): IChainableEvent<T> {
|
||||
return new ChainableEvent(forEach(this._event, fn));
|
||||
}
|
||||
|
||||
filter(fn: (e: T) => boolean): IChainableEvent<T> {
|
||||
return new ChainableEvent(filterEvent(this._event, fn));
|
||||
}
|
||||
|
||||
latch(): IChainableEvent<T> {
|
||||
return new ChainableEvent(latch(this._event));
|
||||
}
|
||||
|
||||
on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return this._event(listener, thisArgs, disposables);
|
||||
}
|
||||
|
||||
once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) {
|
||||
return once(this._event)(listener, thisArgs, disposables);
|
||||
}
|
||||
}
|
||||
|
||||
export function chain<T>(event: Event<T>): IChainableEvent<T> {
|
||||
return new ChainableEvent(event);
|
||||
}
|
||||
|
||||
export function stopwatch<T>(event: Event<T>): Event<number> {
|
||||
const start = new Date().getTime();
|
||||
return mapEvent(once(event), _ => new Date().getTime() - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffers the provided event until a first listener comes
|
||||
* along, at which point fire all the events at once and
|
||||
* pipe the event from then on.
|
||||
*
|
||||
* ```typescript
|
||||
* const emitter = new Emitter<number>();
|
||||
* const event = emitter.event;
|
||||
* const bufferedEvent = buffer(event);
|
||||
*
|
||||
* emitter.fire(1);
|
||||
* emitter.fire(2);
|
||||
* emitter.fire(3);
|
||||
* // nothing...
|
||||
*
|
||||
* const listener = bufferedEvent(num => console.log(num));
|
||||
* // 1, 2, 3
|
||||
*
|
||||
* emitter.fire(4);
|
||||
* // 4
|
||||
* ```
|
||||
* A Relay is an event forwarder which functions as a replugabble event pipe.
|
||||
* Once created, you can connect an input event to it and it will simply forward
|
||||
* events from that input event through its own `event` property. The `input`
|
||||
* can be changed at any point in time.
|
||||
*/
|
||||
export function buffer<T>(event: Event<T>, nextTick = false, _buffer: T[] = []): Event<T> {
|
||||
let buffer: T[] | null = _buffer.slice();
|
||||
|
||||
let listener: IDisposable | null = event(e => {
|
||||
if (buffer) {
|
||||
buffer.push(e);
|
||||
} else {
|
||||
emitter.fire(e);
|
||||
}
|
||||
});
|
||||
|
||||
const flush = () => {
|
||||
if (buffer) {
|
||||
buffer.forEach(e => emitter.fire(e));
|
||||
}
|
||||
buffer = null;
|
||||
};
|
||||
|
||||
const emitter = new Emitter<T>({
|
||||
onFirstListenerAdd() {
|
||||
if (!listener) {
|
||||
listener = event(e => emitter.fire(e));
|
||||
}
|
||||
},
|
||||
|
||||
onFirstListenerDidAdd() {
|
||||
if (buffer) {
|
||||
if (nextTick) {
|
||||
setTimeout(flush);
|
||||
} else {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLastListenerRemove() {
|
||||
if (listener) {
|
||||
listener.dispose();
|
||||
}
|
||||
listener = null;
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to `buffer` but it buffers indefinitely and repeats
|
||||
* the buffered events to every new listener.
|
||||
*/
|
||||
export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): Event<T> {
|
||||
buffer = buffer.slice();
|
||||
|
||||
event(e => {
|
||||
buffer.push(e);
|
||||
emitter.fire(e);
|
||||
});
|
||||
|
||||
const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e));
|
||||
|
||||
const emitter = new Emitter<T>({
|
||||
onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) {
|
||||
if (nextTick) {
|
||||
setTimeout(() => flush(listener, thisArgs));
|
||||
} else {
|
||||
flush(listener, thisArgs);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return emitter.event;
|
||||
}
|
||||
|
||||
export class Relay<T> implements IDisposable {
|
||||
|
||||
private listening = false;
|
||||
@@ -695,29 +785,3 @@ export class Relay<T> implements IDisposable {
|
||||
this.emitter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export interface NodeEventEmitter {
|
||||
on(event: string | symbol, listener: Function): this;
|
||||
removeListener(event: string | symbol, listener: Function): this;
|
||||
}
|
||||
|
||||
export function fromNodeEventEmitter<T>(emitter: NodeEventEmitter, eventName: string, map: (...args: any[]) => T = id => id): Event<T> {
|
||||
const fn = (...args: any[]) => result.fire(map(...args));
|
||||
const onFirstListenerAdd = () => emitter.on(eventName, fn);
|
||||
const onLastListenerRemove = () => emitter.removeListener(eventName, fn);
|
||||
const result = new Emitter<T>({ onFirstListenerAdd, onLastListenerRemove });
|
||||
|
||||
return result.event;
|
||||
}
|
||||
|
||||
export function latch<T>(event: Event<T>): Event<T> {
|
||||
let firstCall = true;
|
||||
let cache: T;
|
||||
|
||||
return filterEvent(event, value => {
|
||||
let shouldEmit = firstCall || value !== cache;
|
||||
firstCall = false;
|
||||
cache = value;
|
||||
return shouldEmit;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user