@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
89 lines (79 loc) • 3.35 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2025 ST Microelectronics and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { Disposable } from './disposable';
/**
* Ths code in this file implements a list of listeners that can be invoked sequentially with a callback to handle
* the results of the listener invocation.
*/
type Listener<T, U> = (e: T) => U;
interface ListenerRegistration<T, U> {
id: number,
listener: Listener<T, U>;
}
export namespace Listener {
export type Registration<T, U = void> = (listener: (e: T) => U) => Disposable;
export const None: Registration<void, void> = () => Disposable.NULL;
/**
* Convenience function to await all listener invocations
* @param value The value to invoke the listeners with
* @param list the listener list to invoke
* @returns the return values from the listener invocation
*/
export async function awaitAll<T, U>(value: T, list: ListenerList<T, Promise<U>>): Promise<U[]> {
const promises: Promise<U>[] = [];
list.invoke(value, promise => {
promises.push(promise);
});
return await Promise.all(promises);
}
}
export class ListenerList<T, U> {
private listeners: ListenerRegistration<T, U> | (ListenerRegistration<T, U>)[] | undefined;
private registeredCount = 1; // start at 1 to prevent falsy madness
registration: Listener.Registration<T, U> = this.register.bind(this);
private register(listener: Listener<T, U>): Disposable {
const reg: ListenerRegistration<T, U> = { id: this.registeredCount++, listener };
if (!this.listeners) {
this.listeners = reg;
} else if (Array.isArray(this.listeners)) {
this.listeners.push(reg as ListenerRegistration<T, U>);
} else {
this.listeners = [this.listeners, reg];
}
return Disposable.create(() => {
this.remove(reg.id);
});
}
private remove(id: number): void {
if (Array.isArray(this.listeners)) {
const index = this.listeners.findIndex(v => v.id === id);
if (index >= 0) {
this.listeners.splice(index, 1);
}
} else if (this.listeners && this.listeners.id === id) {
this.listeners = undefined;
}
}
invoke(e: T, callback: (value: U) => void): void {
if (Array.isArray(this.listeners)) {
for (const l of this.listeners) {
callback(l.listener(e));
}
} else if (this.listeners) {
callback(this.listeners.listener(e));
}
}
}