@sussudio/base
Version:
Internal APIs for VS Code's utilities and user interface building blocks.
420 lines (414 loc) • 16.2 kB
text/typescript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from './cancellation.mjs';
import { DisposableStore, IDisposable, SafeDisposable } from './lifecycle.mjs';
import { LinkedList } from './linkedList.mjs';
import { IObservable } from './observable.mjs';
/**
* An event with zero or one parameters that can be subscribed to. The event is a function itself.
*/
export interface Event<T> {
(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
}
export declare namespace Event {
const None: Event<any>;
/**
* Given an event, returns another event which debounces calls and defers the listeners to a later task via a shared
* `setTimeout`. The event is converted into a signal (`Event<void>`) to avoid additional object creation as a
* result of merging events and to try prevent race conditions that could arise when using related deferred and
* non-deferred events.
*
* This is useful for deferring non-critical work (eg. general UI updates) to ensure it does not block critical work
* (eg. latency of keypress to text rendered).
*
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*
* @param event The event source for the new event.
* @param disposable A disposable store to add the new EventEmitter to.
*/
function defer(event: Event<unknown>, disposable?: DisposableStore): Event<void>;
/**
* Given an event, returns another event which only fires once.
*
* @param event The event source for the new event.
*/
function once<T>(event: Event<T>): Event<T>;
/**
* Maps an event of one type into an event of another type using a mapping function, similar to how
* `Array.prototype.map` works.
*
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*
* @param event The event source for the new event.
* @param map The mapping function.
* @param disposable A disposable store to add the new EventEmitter to.
*/
function map<I, O>(event: Event<I>, map: (i: I) => O, disposable?: DisposableStore): Event<O>;
/**
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function forEach<I>(event: Event<I>, each: (i: I) => void, disposable?: DisposableStore): Event<I>;
/**
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function filter<T, U>(event: Event<T | U>, filter: (e: T | U) => e is T, disposable?: DisposableStore): Event<T>;
function filter<T>(event: Event<T>, filter: (e: T) => boolean, disposable?: DisposableStore): Event<T>;
function filter<T, R>(event: Event<T | R>, filter: (e: T | R) => e is R, disposable?: DisposableStore): Event<R>;
/**
* Given an event, returns the same event but typed as `Event<void>`.
*/
function signal<T>(event: Event<T>): Event<void>;
/**
* Given a collection of events, returns a single event which emits
* whenever any of the provided events emit.
*/
function any<T>(...events: Event<T>[]): Event<T>;
function any(...events: Event<any>[]): Event<void>;
/**
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function reduce<I, O>(
event: Event<I>,
merge: (last: O | undefined, event: I) => O,
initial?: O,
disposable?: DisposableStore,
): Event<O>;
/**
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function debounce<T>(
event: Event<T>,
merge: (last: T | undefined, event: T) => T,
delay?: number,
leading?: boolean,
leakWarningThreshold?: number,
disposable?: DisposableStore,
): Event<T>;
/**
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function debounce<I, O>(
event: Event<I>,
merge: (last: O | undefined, event: I) => O,
delay?: number,
leading?: boolean,
leakWarningThreshold?: number,
disposable?: DisposableStore,
): Event<O>;
/**
* Debounces an event, firing after some delay (default=0) with an array of all event original objects.
*
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function accumulate<T>(event: Event<T>, delay?: number, disposable?: DisposableStore): Event<T[]>;
/**
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function latch<T>(event: Event<T>, equals?: (a: T, b: T) => boolean, disposable?: DisposableStore): Event<T>;
/**
* Splits an event whose parameter is a union type into 2 separate events for each type in the union.
*
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*
* @example
* ```
* const event = new EventEmitter<number | undefined>().event;
* const [numberEvent, undefinedEvent] = Event.split(event, isUndefined);
* ```
*
* @param event The event source for the new event.
* @param isT A function that determines what event is of the first type.
* @param disposable A disposable store to add the new EventEmitter to.
*/
function split<T, U>(
event: Event<T | U>,
isT: (e: T | U) => e is T,
disposable?: DisposableStore,
): [Event<T>, Event<U>];
/**
* *NOTE* that this function returns an `Event` and it MUST be called with a `DisposableStore` whenever the returned
* event is accessible to "third parties", e.g the event is a public property. Otherwise a leaked listener on the
* returned event causes this utility to leak a listener on the original event.
*/
function buffer<T>(event: Event<T>, flushAfterTimeout?: boolean, _buffer?: T[]): Event<T>;
interface IChainableEvent<T> extends IDisposable {
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>;
filter<R>(fn: (e: T | R) => e is R): IChainableEvent<R>;
reduce<R>(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent<R>;
latch(): IChainableEvent<T>;
debounce(
merge: (last: T | undefined, event: T) => T,
delay?: number,
leading?: boolean,
leakWarningThreshold?: number,
): IChainableEvent<T>;
debounce<R>(
merge: (last: R | undefined, event: T) => R,
delay?: number,
leading?: boolean,
leakWarningThreshold?: number,
): IChainableEvent<R>;
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable;
once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
}
function chain<T>(event: Event<T>): IChainableEvent<T>;
interface NodeEventEmitter {
on(event: string | symbol, listener: Function): unknown;
removeListener(event: string | symbol, listener: Function): unknown;
}
function fromNodeEventEmitter<T>(emitter: NodeEventEmitter, eventName: string, map?: (...args: any[]) => T): Event<T>;
interface DOMEventEmitter {
addEventListener(event: string | symbol, listener: Function): void;
removeEventListener(event: string | symbol, listener: Function): void;
}
function fromDOMEventEmitter<T>(emitter: DOMEventEmitter, eventName: string, map?: (...args: any[]) => T): Event<T>;
function toPromise<T>(event: Event<T>): Promise<T>;
function runAndSubscribe<T>(event: Event<T>, handler: (e: T | undefined) => any): IDisposable;
function runAndSubscribeWithStore<T>(
event: Event<T>,
handler: (e: T | undefined, disposableStore: DisposableStore) => any,
): IDisposable;
function fromObservable<T>(obs: IObservable<T, any>, store?: DisposableStore): Event<T>;
}
export interface EmitterOptions {
/**
* Optional function that's called *before* the very first listener is added
*/
onWillAddFirstListener?: Function;
/**
* Optional function that's called *after* the very first listener is added
*/
onDidAddFirstListener?: Function;
/**
* Optional function that's called after a listener is added
*/
onDidAddListener?: Function;
/**
* Optional function that's called *after* remove the very last listener
*/
onDidRemoveLastListener?: Function;
/**
* Number of listeners that are allowed before assuming a leak. Default to
* a globally configured value
*
* @see setGlobalLeakWarningThreshold
*/
leakWarningThreshold?: number;
/**
* Pass in a delivery queue, which is useful for ensuring
* in order event delivery across multiple emitters.
*/
deliveryQueue?: EventDeliveryQueue;
/** ONLY enable this during development */
_profName?: string;
}
export declare class EventProfiling {
static readonly all: Set<EventProfiling>;
private static _idPool;
readonly name: string;
listenerCount: number;
invocationCount: number;
elapsedOverall: number;
durations: number[];
private _stopWatch?;
constructor(name: string);
start(listenerCount: number): void;
stop(): void;
}
export declare function setGlobalLeakWarningThreshold(n: number): IDisposable;
declare class Stacktrace {
readonly value: string;
static create(): Stacktrace;
private constructor();
print(): void;
}
declare class Listener<T> {
readonly callback: (e: T) => void;
readonly callbackThis: any | undefined;
readonly stack: Stacktrace | undefined;
readonly subscription: SafeDisposable;
constructor(callback: (e: T) => void, callbackThis: any | undefined, stack: Stacktrace | undefined);
invoke(e: T): void;
}
/**
* The Emitter can be used to expose an Event to the public
* to fire it from the insides.
* Sample:
class Document {
private readonly _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 declare class Emitter<T> {
private readonly _options?;
private readonly _leakageMon?;
private readonly _perfMon?;
private _disposed;
private _event?;
private _deliveryQueue?;
protected _listeners?: LinkedList<Listener<T>>;
constructor(options?: EmitterOptions);
dispose(): void;
/**
* For the public to allow to subscribe
* to events from this Emitter
*/
get event(): Event<T>;
/**
* To be kept private to fire an event to
* subscribers
*/
fire(event: T): void;
hasListeners(): boolean;
}
export declare class EventDeliveryQueue {
protected _queue: LinkedList<EventDeliveryQueueElement<any>>;
get size(): number;
push<T>(emitter: Emitter<T>, listener: Listener<T>, event: T): void;
clear<T>(emitter: Emitter<T>): void;
deliver(): void;
}
declare class EventDeliveryQueueElement<T = any> {
readonly emitter: Emitter<T>;
readonly listener: Listener<T>;
readonly event: T;
constructor(emitter: Emitter<T>, listener: Listener<T>, event: T);
}
export interface IWaitUntil {
token: CancellationToken;
waitUntil(thenable: Promise<unknown>): void;
}
export type IWaitUntilData<T> = Omit<Omit<T, 'waitUntil'>, 'token'>;
export declare class AsyncEmitter<T extends IWaitUntil> extends Emitter<T> {
private _asyncDeliveryQueue?;
fireAsync(
data: IWaitUntilData<T>,
token: CancellationToken,
promiseJoin?: (p: Promise<unknown>, listener: Function) => Promise<unknown>,
): Promise<void>;
}
export declare class PauseableEmitter<T> extends Emitter<T> {
private _isPaused;
protected _eventQueue: LinkedList<T>;
private _mergeFn?;
constructor(
options?: EmitterOptions & {
merge?: (input: T[]) => T;
},
);
pause(): void;
resume(): void;
fire(event: T): void;
}
export declare class DebounceEmitter<T> extends PauseableEmitter<T> {
private readonly _delay;
private _handle;
constructor(
options: EmitterOptions & {
merge: (input: T[]) => T;
delay?: number;
},
);
fire(event: T): void;
}
/**
* An emitter which queue all events and then process them at the
* end of the event loop.
*/
export declare class MicrotaskEmitter<T> extends Emitter<T> {
private _queuedEvents;
private _mergeFn?;
constructor(
options?: EmitterOptions & {
merge?: (input: T[]) => T;
},
);
fire(event: T): void;
}
export declare class EventMultiplexer<T> implements IDisposable {
private readonly emitter;
private hasListeners;
private events;
constructor();
get event(): Event<T>;
add(event: Event<T>): IDisposable;
private onFirstListenerAdd;
private onLastListenerRemove;
private hook;
private unhook;
dispose(): void;
}
/**
* The EventBufferer is useful in situations in which you want
* 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();
* const delayedEvent = delayer.wrapEvent(emitter.event);
*
* delayedEvent(console.log);
*
* delayer.bufferEvents(() => {
* emitter.fire(); // event will not be fired yet
* });
*
* // event will only be fired at this point
* ```
*/
export declare class EventBufferer {
private buffers;
wrapEvent<T>(event: Event<T>): Event<T>;
bufferEvents<R = void>(fn: () => R): R;
}
/**
* 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 declare class Relay<T> implements IDisposable {
private listening;
private inputEvent;
private inputEventListener;
private readonly emitter;
readonly event: Event<T>;
set input(event: Event<T>);
dispose(): void;
}
export {};