Path: blob/main/components/gitpod-protocol/src/util/event.ts
2500 views
/*1* Copyright (C) 2017 TypeFox and others.2*3* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.4* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.05*/67import { Disposable } from "./disposable";8import { log } from "./logging";910/**11* Represents a typed event.12*/13export interface Event<T> {14/**15*16* @param listener The listener function will be call when the event happens.17* @param thisArgs The 'this' which will be used when calling the event listener.18* @param disposables An array to which a {{IDisposable}} will be added.19* @return a disposable to remove the listener again.20*/21(listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable;22}2324export namespace Event {25const _disposable = { dispose() {} };26export const None: Event<any> = function () {27return _disposable;28};29}3031class CallbackList {32private _callbacks: Function[] | undefined;33private _contexts: any[] | undefined;3435public add(callback: Function, context: any = null, bucket?: Disposable[]): void {36if (!this._callbacks) {37this._callbacks = [];38this._contexts = [];39}40this._callbacks.push(callback);41this._contexts!.push(context);4243if (Array.isArray(bucket)) {44bucket.push({ dispose: () => this.remove(callback, context) });45}46}4748public remove(callback: Function, context: any = null): void {49if (!this._callbacks) {50return;51}5253let foundCallbackWithDifferentContext = false;54for (let i = 0, len = this._callbacks.length; i < len; i++) {55if (this._callbacks[i] === callback) {56if (this._contexts![i] === context) {57// callback & context match => remove it58this._callbacks.splice(i, 1);59this._contexts!.splice(i, 1);60return;61} else {62foundCallbackWithDifferentContext = true;63}64}65}6667if (foundCallbackWithDifferentContext) {68throw new Error("When adding a listener with a context, you should remove it with the same context");69}70}7172public invoke(...args: any[]): any[] {73if (!this._callbacks) {74return [];75}7677const ret: any[] = [];78const callbacks = this._callbacks.slice(0);79const contexts = this._contexts!.slice(0);8081for (let i = 0, len = callbacks.length; i < len; i++) {82try {83ret.push(callbacks[i].apply(contexts[i], args));84} catch (e) {85log.error(e);86}87}88return ret;89}9091public isEmpty(): boolean {92return !this._callbacks || this._callbacks.length === 0;93}9495public dispose(): void {96this._callbacks = undefined;97this._contexts = undefined;98}99}100101export interface EmitterOptions {102onFirstListenerAdd?: Function;103onLastListenerRemove?: Function;104}105106export class Emitter<T> {107private static _noop = function () {};108109private _event: Event<T>;110private _callbacks: CallbackList | undefined;111112constructor(private _options?: EmitterOptions) {}113114/**115* For the public to allow to subscribe116* to events from this Emitter117*/118get event(): Event<T> {119if (!this._event) {120this._event = (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {121if (!this._callbacks) {122this._callbacks = new CallbackList();123}124if (this._options && this._options.onFirstListenerAdd && this._callbacks.isEmpty()) {125this._options.onFirstListenerAdd(this);126}127this._callbacks.add(listener, thisArgs);128129const result = {130dispose: () => {131this._callbacks!.remove(listener, thisArgs);132result.dispose = Emitter._noop;133if (this._options && this._options.onLastListenerRemove && this._callbacks!.isEmpty()) {134this._options.onLastListenerRemove(this);135}136},137};138if (Array.isArray(disposables)) {139disposables.push(result);140}141142return result;143};144}145return this._event;146}147148/**149* To be kept private to fire an event to150* subscribers151*/152fire(event: T): any {153if (this._callbacks) {154this._callbacks.invoke.call(this._callbacks, event);155}156}157158dispose() {159if (this._callbacks) {160this._callbacks.dispose();161this._callbacks = undefined;162}163}164}165166167