event.ts 21.0 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import { onUnexpectedError } from 'vs/base/common/errors';
J
Johannes Rieken 已提交
7
import { once as onceFn } from 'vs/base/common/functional';
J
Joao Moreno 已提交
8
import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
9
import { LinkedList } from 'vs/base/common/linkedList';
E
Erich Gamma 已提交
10 11 12 13 14

/**
 * To an event a function with one or zero parameters
 * can be subscribed. The event is the subscriber function itself.
 */
M
Matt Bierner 已提交
15
export interface Event<T> {
E
Erich Gamma 已提交
16 17 18
	(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
}

M
Matt Bierner 已提交
19
export namespace Event {
E
Erich Gamma 已提交
20
	const _disposable = { dispose() { } };
J
Johannes Rieken 已提交
21
	export const None: Event<any> = function () { return _disposable; };
J
Joao Moreno 已提交
22 23 24 25 26 27 28 29

	/**
	 * 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;
J
Johannes Rieken 已提交
30 31
			let result: IDisposable;
			result = event(e => {
J
Joao Moreno 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
				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.
	 */
J
Joao Moreno 已提交
96 97
	export function reduce<I, O>(event: Event<I>, merge: (last: O | undefined, event: I) => O, initial?: O): Event<O> {
		let output: O | undefined = initial;
J
Joao Moreno 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

		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) {
140
							emitter.fire(_output!);
J
Joao Moreno 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
						}

						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>;
J
Joao Moreno 已提交
281
		reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R>;
J
Joao Moreno 已提交
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
		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));
		}

J
Joao Moreno 已提交
305 306 307 308
		reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R> {
			return new ChainableEvent(reduce(this._event, merge, initial));
		}

J
Joao Moreno 已提交
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
		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;
	}

340 341
	export function fromPromise<T = any>(promise: Promise<T>): Event<undefined> {
		const emitter = new Emitter<undefined>();
J
Joao Moreno 已提交
342 343 344 345 346 347
		let shouldEmit = false;

		promise
			.then(undefined, () => null)
			.then(() => {
				if (!shouldEmit) {
348
					setTimeout(() => emitter.fire(undefined), 0);
J
Joao Moreno 已提交
349
				} else {
350
					emitter.fire(undefined);
J
Joao Moreno 已提交
351 352 353 354 355 356 357
				}
			});

		shouldEmit = true;
		return emitter.event;
	}

J
Johannes Rieken 已提交
358
	export function toPromise<T>(event: Event<T>): Promise<T> {
J
Joao Moreno 已提交
359 360
		return new Promise(c => once(event)(c));
	}
E
Erich Gamma 已提交
361 362
}

M
Matt Bierner 已提交
363
type Listener<T> = [(e: T) => void, any] | ((e: T) => void);
364

E
Erich Gamma 已提交
365 366
export interface EmitterOptions {
	onFirstListenerAdd?: Function;
J
Joao Moreno 已提交
367
	onFirstListenerDidAdd?: Function;
368
	onListenerDidAdd?: Function;
E
Erich Gamma 已提交
369
	onLastListenerRemove?: Function;
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
	leakWarningThreshold?: number;
}

let _globalLeakWarningThreshold = -1;
export function setGlobalLeakWarningThreshold(n: number): IDisposable {
	let oldValue = _globalLeakWarningThreshold;
	_globalLeakWarningThreshold = n;
	return {
		dispose() {
			_globalLeakWarningThreshold = oldValue;
		}
	};
}

class LeakageMonitor {

	private _stacks: Map<string, number> | undefined;
	private _warnCountdown: number = 0;

	constructor(
		readonly customThreshold?: number,
		readonly name: string = Math.random().toString(18).slice(2, 5),
	) { }

	dispose(): void {
		if (this._stacks) {
			this._stacks.clear();
		}
	}

400
	check(listenerCount: number): undefined | (() => void) {
401 402 403 404 405

		let threshold = _globalLeakWarningThreshold;
		if (typeof this.customThreshold === 'number') {
			threshold = this.customThreshold;
		}
406 407 408 409 410 411 412

		if (threshold <= 0 || listenerCount < threshold) {
			return undefined;
		}

		if (!this._stacks) {
			this._stacks = new Map();
413
		}
414 415 416 417 418 419 420 421
		let stack = new Error().stack!.split('\n').slice(3).join('\n');
		let count = (this._stacks.get(stack) || 0);
		this._stacks.set(stack, count + 1);
		this._warnCountdown -= 1;

		if (this._warnCountdown <= 0) {
			// only warn on first exceed and then every time the limit
			// is exceeded by 50% again
422
			this._warnCountdown = threshold * 0.5;
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438

			// find most frequent listener and print warning
			let topStack: string;
			let topCount: number = 0;
			this._stacks.forEach((count, stack) => {
				if (!topStack || topCount < count) {
					topStack = stack;
					topCount = count;
				}
			});

			console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`);
			console.warn(topStack!);
		}

		return () => {
J
Johannes Rieken 已提交
439 440
			let count = (this._stacks!.get(stack) || 0);
			this._stacks!.set(stack, count - 1);
441
		};
442
	}
E
Erich Gamma 已提交
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
}

/**
 * The Emitter can be used to expose an Event to the public
 * to fire it from the insides.
 * Sample:
	class Document {

		private _onDidChange = new Emitter<(value:string)=>any>();

		public onDidChange = this._onDidChange.event;

		// getter-style
		// get onDidChange(): Event<(value:string)=>any> {
		// 	return this._onDidChange.event;
		// }

		private _doIt() {
			//...
			this._onDidChange.fire(value);
		}
	}
 */
export class Emitter<T> {

468
	private static readonly _noop = function () { };
E
Erich Gamma 已提交
469

470 471
	private readonly _options?: EmitterOptions;
	private readonly _leakageMon?: LeakageMonitor;
472
	private _disposed: boolean = false;
473 474 475
	private _event?: Event<T>;
	private _deliveryQueue: [Listener<T>, T][];
	protected _listeners?: LinkedList<Listener<T>>;
476

477 478
	constructor(options?: EmitterOptions) {
		this._options = options;
479 480 481
		this._leakageMon = _globalLeakWarningThreshold > 0
			? new LeakageMonitor(this._options && this._options.leakWarningThreshold)
			: undefined;
E
Erich Gamma 已提交
482 483 484 485 486 487 488 489
	}

	/**
	 * For the public to allow to subscribe
	 * to events from this Emitter
	 */
	get event(): Event<T> {
		if (!this._event) {
J
Johannes Rieken 已提交
490
			this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]) => {
491 492
				if (!this._listeners) {
					this._listeners = new LinkedList();
E
Erich Gamma 已提交
493
				}
J
Joao Moreno 已提交
494

495
				const firstListener = this._listeners.isEmpty();
J
Joao Moreno 已提交
496 497

				if (firstListener && this._options && this._options.onFirstListenerAdd) {
E
Erich Gamma 已提交
498 499
					this._options.onFirstListenerAdd(this);
				}
J
Joao Moreno 已提交
500

501
				const remove = this._listeners.push(!thisArgs ? listener : [listener, thisArgs]);
E
Erich Gamma 已提交
502

J
Joao Moreno 已提交
503 504 505 506
				if (firstListener && this._options && this._options.onFirstListenerDidAdd) {
					this._options.onFirstListenerDidAdd(this);
				}

507 508 509 510
				if (this._options && this._options.onListenerDidAdd) {
					this._options.onListenerDidAdd(this, listener, thisArgs);
				}

511
				// check and record this emitter for potential leakage
512 513 514 515
				let removeMonitor: (() => void) | undefined;
				if (this._leakageMon) {
					removeMonitor = this._leakageMon.check(this._listeners.size);
				}
516

E
Erich Gamma 已提交
517 518 519
				let result: IDisposable;
				result = {
					dispose: () => {
520 521
						if (removeMonitor) {
							removeMonitor();
522
						}
E
Erich Gamma 已提交
523 524
						result.dispose = Emitter._noop;
						if (!this._disposed) {
525
							remove();
526 527 528 529 530
							if (this._options && this._options.onLastListenerRemove) {
								const hasListeners = (this._listeners && !this._listeners.isEmpty());
								if (!hasListeners) {
									this._options.onLastListenerRemove(this);
								}
E
Erich Gamma 已提交
531 532 533 534
							}
						}
					}
				};
J
Johannes Rieken 已提交
535
				if (Array.isArray(disposables)) {
E
Erich Gamma 已提交
536 537 538 539 540 541 542 543 544 545 546 547 548
					disposables.push(result);
				}

				return result;
			};
		}
		return this._event;
	}

	/**
	 * To be kept private to fire an event to
	 * subscribers
	 */
M
Matt Bierner 已提交
549
	fire(event: T): void {
550 551 552 553 554 555 556
		if (this._listeners) {
			// put all [listener,event]-pairs into delivery queue
			// then emit all event. an inner/nested event might be
			// the driver of this

			if (!this._deliveryQueue) {
				this._deliveryQueue = [];
557
			}
558

559 560 561 562 563
			for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
				this._deliveryQueue.push([e.value, event]);
			}

			while (this._deliveryQueue.length > 0) {
564
				const [listener, event] = this._deliveryQueue.shift()!;
565 566 567 568 569 570 571 572 573 574
				try {
					if (typeof listener === 'function') {
						listener.call(undefined, event);
					} else {
						listener[0].call(listener[1], event);
					}
				} catch (e) {
					onUnexpectedError(e);
				}
			}
E
Erich Gamma 已提交
575 576 577 578
		}
	}

	dispose() {
579
		if (this._listeners) {
580
			this._listeners = undefined;
581 582
		}
		if (this._deliveryQueue) {
583
			this._deliveryQueue.length = 0;
E
Erich Gamma 已提交
584
		}
585 586 587
		if (this._leakageMon) {
			this._leakageMon.dispose();
		}
588
		this._disposed = true;
E
Erich Gamma 已提交
589 590 591
	}
}

J
Johannes Rieken 已提交
592
export interface IWaitUntil {
J
Johannes Rieken 已提交
593
	waitUntil(thenable: Promise<any>): void;
J
Johannes Rieken 已提交
594 595 596 597
}

export class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {

M
Matt Bierner 已提交
598
	private _asyncDeliveryQueue: [Listener<T>, T, Promise<any>[]][];
J
Johannes Rieken 已提交
599

J
Johannes Rieken 已提交
600
	async fireAsync(eventFn: (thenables: Promise<any>[], listener: Function) => T): Promise<void> {
J
Johannes Rieken 已提交
601 602 603 604 605 606 607 608 609 610 611 612
		if (!this._listeners) {
			return;
		}

		// put all [listener,event]-pairs into delivery queue
		// then emit all event. an inner/nested event might be
		// the driver of this
		if (!this._asyncDeliveryQueue) {
			this._asyncDeliveryQueue = [];
		}

		for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) {
J
Johannes Rieken 已提交
613
			let thenables: Promise<void>[] = [];
J
Johannes Rieken 已提交
614 615 616 617
			this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]);
		}

		while (this._asyncDeliveryQueue.length > 0) {
618
			const [listener, event, thenables] = this._asyncDeliveryQueue.shift()!;
J
Johannes Rieken 已提交
619 620 621 622 623 624 625 626 627 628 629 630 631 632
			try {
				if (typeof listener === 'function') {
					listener.call(undefined, event);
				} else {
					listener[0].call(listener[1], event);
				}
			} catch (e) {
				onUnexpectedError(e);
				continue;
			}

			// freeze thenables-collection to enforce sync-calls to
			// wait until and then wait for all thenables to resolve
			Object.freeze(thenables);
633
			await Promise.all(thenables);
J
Johannes Rieken 已提交
634 635 636 637
		}
	}
}

J
Joao Moreno 已提交
638 639
export class EventMultiplexer<T> implements IDisposable {

M
Matt Bierner 已提交
640
	private readonly emitter: Emitter<T>;
J
Joao Moreno 已提交
641
	private hasListeners = false;
642
	private events: { event: Event<T>; listener: IDisposable | null; }[] = [];
J
Joao Moreno 已提交
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684

	constructor() {
		this.emitter = new Emitter<T>({
			onFirstListenerAdd: () => this.onFirstListenerAdd(),
			onLastListenerRemove: () => this.onLastListenerRemove()
		});
	}

	get event(): Event<T> {
		return this.emitter.event;
	}

	add(event: Event<T>): IDisposable {
		const e = { event: event, listener: null };
		this.events.push(e);

		if (this.hasListeners) {
			this.hook(e);
		}

		const dispose = () => {
			if (this.hasListeners) {
				this.unhook(e);
			}

			const idx = this.events.indexOf(e);
			this.events.splice(idx, 1);
		};

		return toDisposable(onceFn(dispose));
	}

	private onFirstListenerAdd(): void {
		this.hasListeners = true;
		this.events.forEach(e => this.hook(e));
	}

	private onLastListenerRemove(): void {
		this.hasListeners = false;
		this.events.forEach(e => this.unhook(e));
	}

685
	private hook(e: { event: Event<T>; listener: IDisposable | null; }): void {
J
Joao Moreno 已提交
686 687 688
		e.listener = e.event(r => this.emitter.fire(r));
	}

689 690 691 692
	private unhook(e: { event: Event<T>; listener: IDisposable | null; }): void {
		if (e.listener) {
			e.listener.dispose();
		}
J
Joao Moreno 已提交
693 694 695 696 697 698 699 700
		e.listener = null;
	}

	dispose(): void {
		this.emitter.dispose();
	}
}

701
/**
702
 * The EventBufferer is useful in situations in which you want
703 704 705 706 707 708 709
 * to delay firing your events during some code.
 * You can wrap that code and be sure that the event will not
 * be fired during that wrap.
 *
 * ```
 * const emitter: Emitter;
 * const delayer = new EventDelayer();
J
Joao Moreno 已提交
710
 * const delayedEvent = delayer.wrapEvent(emitter.event);
711 712 713
 *
 * delayedEvent(console.log);
 *
J
Joao Moreno 已提交
714
 * delayer.bufferEvents(() => {
715 716 717 718 719 720
 *   emitter.fire(); // event will not be fired yet
 * });
 *
 * // event will only be fired at this point
 * ```
 */
J
Joao Moreno 已提交
721
export class EventBufferer {
722

J
Joao Moreno 已提交
723
	private buffers: Function[][] = [];
724

J
Joao Moreno 已提交
725
	wrapEvent<T>(event: Event<T>): Event<T> {
726 727
		return (listener, thisArgs?, disposables?) => {
			return event(i => {
J
Joao Moreno 已提交
728 729 730
				const buffer = this.buffers[this.buffers.length - 1];

				if (buffer) {
J
Joao Moreno 已提交
731
					buffer.push(() => listener.call(thisArgs, i));
732
				} else {
J
Joao Moreno 已提交
733
					listener.call(thisArgs, i);
734
				}
R
Rob Lourens 已提交
735
			}, undefined, disposables);
736 737 738
		};
	}

J
Joao Moreno 已提交
739
	bufferEvents<R = void>(fn: () => R): R {
740
		const buffer: Array<() => R> = [];
J
Joao Moreno 已提交
741
		this.buffers.push(buffer);
J
Joao Moreno 已提交
742
		const r = fn();
J
Joao Moreno 已提交
743 744
		this.buffers.pop();
		buffer.forEach(flush => flush());
J
Joao Moreno 已提交
745
		return r;
746
	}
X
xzper 已提交
747
}
J
Joao Moreno 已提交
748

749
/**
J
Joao Moreno 已提交
750 751 752 753
 * 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.
754
 */
J
Joao Moreno 已提交
755 756
export class Relay<T> implements IDisposable {

J
Joao Moreno 已提交
757 758 759 760 761 762 763 764 765 766 767 768 769 770
	private listening = false;
	private inputEvent: Event<T> = Event.None;
	private inputEventListener: IDisposable = Disposable.None;

	private emitter = new Emitter<T>({
		onFirstListenerDidAdd: () => {
			this.listening = true;
			this.inputEventListener = this.inputEvent(this.emitter.fire, this.emitter);
		},
		onLastListenerRemove: () => {
			this.listening = false;
			this.inputEventListener.dispose();
		}
	});
J
Joao Moreno 已提交
771

J
Joao Moreno 已提交
772
	readonly event: Event<T> = this.emitter.event;
J
Joao Moreno 已提交
773 774

	set input(event: Event<T>) {
J
Joao Moreno 已提交
775 776 777 778 779 780
		this.inputEvent = event;

		if (this.listening) {
			this.inputEventListener.dispose();
			this.inputEventListener = event(this.emitter.fire, this.emitter);
		}
J
Joao Moreno 已提交
781 782 783
	}

	dispose() {
J
Joao Moreno 已提交
784
		this.inputEventListener.dispose();
J
Joao Moreno 已提交
785 786
		this.emitter.dispose();
	}
787
}