Core framework for building cloud and desktop IDE applications using modern web technologies with TypeScript and dependency injection.
—
Theia's event system provides type-safe event handling with emitters, async event support, cancellation tokens, and disposable subscriptions for building reactive applications.
Core event subscription interface for type-safe event handling.
/**
* Represents an event that can be subscribed to
*/
interface Event<T> {
/**
* Subscribe to the event
* @param listener - Function to call when event fires
* @param thisArgs - Optional 'this' context for listener
* @param disposables - Optional array to add subscription disposable to
* @returns Disposable to unsubscribe
*/
(listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable;
}Creates and manages events with type safety and subscription management.
/**
* Event emitter for firing events to subscribers
*/
class Emitter<T> {
/**
* The event that subscribers can listen to
*/
readonly event: Event<T>;
/**
* Create emitter with optional configuration
* @param options - Emitter configuration options
*/
constructor(options?: EmitterOptions);
/**
* Fire the event to all subscribers
* @param event - Event data to send
*/
fire(event: T): void;
/**
* Process each listener one by one
* @param processor - Function that processes each listener, returns false to stop
* @returns Promise that resolves when processing is complete
*/
sequence(processor: (listener: (e: T) => any) => MaybePromise<boolean>): Promise<void>;
/**
* Dispose the emitter and all subscriptions
*/
dispose(): void;
}
/**
* Configuration options for event emitters
*/
interface EmitterOptions {
/** Called when first listener is added */
onFirstListenerAdd?: Function;
/** Called when last listener is removed */
onLastListenerRemove?: Function;
/** Threshold for memory leak warnings */
leakWarningThreshold?: number;
}Usage Example:
import { Emitter, Event } from "@theia/core";
class FileWatcher {
private readonly onDidChangeEmitter = new Emitter<string>();
/** Event fired when file changes */
readonly onDidChange: Event<string> = this.onDidChangeEmitter.event;
private watchFile(path: string): void {
// File watching logic
// When file changes:
this.onDidChangeEmitter.fire(path);
}
dispose(): void {
this.onDidChangeEmitter.dispose();
}
}
// Usage
const watcher = new FileWatcher();
const subscription = watcher.onDidChange(path => {
console.log(`File changed: ${path}`);
});
// Later: subscription.dispose();Handles asynchronous event processing with Promise support.
/**
* Emitter for async events that return promises
*/
class AsyncEmitter<T> {
readonly event: Event<T>;
constructor(options?: EmitterOptions);
/**
* Fire async event and wait for all handlers
* @param event - Event data
* @returns Promise that resolves when all handlers complete
*/
fireAndAwait(event: T): Promise<void>;
dispose(): void;
}Emitter that queues events and fires them in batches.
/**
* Emitter that can queue events and fire them as arrays
*/
class QueueableEmitter<T> extends Emitter<T[]> {
/** Current queue of events to fire */
currentQueue?: T[];
constructor(options?: EmitterOptions);
/**
* Queue events to be fired together
* @param arg - Events to queue
*/
queue(...arg: T[]): void;
/**
* Fire all queued events as an array
*/
fire(): void;
dispose(): void;
}Special event type for handling async operations with wait semantics.
/**
* Event that allows listeners to delay completion
*/
interface WaitUntilEvent {
/**
* Register a promise that must complete before event finishes
* @param thenable - Promise to wait for
*/
waitUntil(thenable: Promise<any>): void;
}
namespace WaitUntilEvent {
/**
* Fire a wait-until event
* @param emitter - Event emitter
* @param event - Event data
* @returns Promise that waits for all waitUntil promises
*/
function fire<T>(emitter: Emitter<T & WaitUntilEvent>, event: T): Promise<void>;
}Usage Example:
import { Emitter, WaitUntilEvent } from "@theia/core";
interface BeforeSaveEvent extends WaitUntilEvent {
readonly uri: string;
}
class DocumentManager {
private readonly onWillSaveEmitter = new Emitter<BeforeSaveEvent>();
async save(uri: string): Promise<void> {
// Fire event and wait for all handlers
await WaitUntilEvent.fire(this.onWillSaveEmitter, { uri });
// Now perform actual save
await this.doSave(uri);
}
private async doSave(uri: string): Promise<void> {
// Actual save implementation
}
}
// Extensions can delay save
documentManager.onWillSave(event => {
if (event.uri.endsWith('.ts')) {
// Format before save
event.waitUntil(this.formatTypeScript(event.uri));
}
});Provides cancellation tokens for long-running operations.
/**
* Token that signals when operation should be cancelled
*/
interface CancellationToken {
/** True if cancellation has been requested */
readonly isCancellationRequested: boolean;
/** Event fired when cancellation is requested */
readonly onCancellationRequested: Event<any>;
}
/**
* Creates cancellation tokens
*/
class CancellationTokenSource {
/** The cancellation token */
readonly token: CancellationToken;
/** Request cancellation */
cancel(): void;
/** Dispose the token source */
dispose(): void;
}
/**
* Error thrown when operation is cancelled
*/
class CancellationError extends Error {
constructor();
}
/**
* Pre-defined tokens
*/
namespace CancellationToken {
/** Token that is never cancelled */
const None: CancellationToken;
/** Token that is already cancelled */
const Cancelled: CancellationToken;
}Usage Example:
import { CancellationToken, CancellationTokenSource, CancellationError } from "@theia/core";
class SearchService {
async search(query: string, token: CancellationToken): Promise<string[]> {
const results: string[] = [];
for (let i = 0; i < 1000; i++) {
// Check for cancellation
if (token.isCancellationRequested) {
throw new CancellationError();
}
// Simulate work
await this.searchChunk(query, i);
results.push(`result-${i}`);
}
return results;
}
private async searchChunk(query: string, chunk: number): Promise<void> {
// Simulate async work
await new Promise(resolve => setTimeout(resolve, 10));
}
}
// Usage with timeout
async function searchWithTimeout(service: SearchService, query: string): Promise<string[]> {
const source = new CancellationTokenSource();
// Cancel after 5 seconds
setTimeout(() => source.cancel(), 5000);
try {
return await service.search(query, source.token);
} finally {
source.dispose();
}
}Utility functions for working with events.
namespace Event {
/** Event that never fires */
const None: Event<any>;
/**
* Get the maximum number of listeners for an event
* @param event - Event to check
* @returns Maximum listeners count
*/
function getMaxListeners(event: Event<unknown>): number;
/**
* Set the maximum number of listeners for an event
* @param event - Event to configure
* @param maxListeners - Maximum listeners count
* @returns The set maximum listeners count
*/
function setMaxListeners<N extends number>(event: Event<unknown>, maxListeners: N): N;
/**
* Add to the maximum number of listeners for an event
* @param event - Event to configure
* @param add - Number to add to current max
* @returns New maximum listeners count
*/
function addMaxListeners(event: Event<unknown>, add: number): number;
/**
* Create event that fires only once
* @param event - Source event
* @returns Event that fires once then disposes
*/
function once<T>(event: Event<T>): Event<T>;
/**
* Convert event to promise that resolves on first emission
* @param event - Source event
* @returns Promise that resolves with event data
*/
function toPromise<T>(event: Event<T>): Promise<T>;
/**
* Transform event data
* @param event - Source event
* @param map - Transformation function
* @returns Event with transformed data
*/
function map<I, O>(event: Event<I>, map: (i: I) => O): Event<O>;
/**
* Filter event data
* @param event - Source event
* @param filter - Filter predicate
* @returns Event that only fires when filter returns true
*/
function filter<T>(event: Event<T>, filter: (e: T) => boolean): Event<T>;
/**
* Combine multiple events into single event
* @param events - Array of events to combine
* @returns Event that fires when any source event fires
*/
function any<T>(...events: Event<T>[]): Event<T>;
function any(...events: Event<any>[]): Event<void>;
/**
* Debounce event firing
* @param event - Source event
* @param delay - Debounce delay in milliseconds
* @returns Debounced event
*/
function debounce<T>(event: Event<T>, delay: number): Event<T>;
}Usage Example:
import { Event } from "@theia/core";
class ConfigurationWatcher {
private readonly onDidChangeEmitter = new Emitter<string>();
// Transform and filter events
readonly onImportantConfigChange = Event.filter(
Event.map(this.onDidChangeEmitter.event, path => path.toLowerCase()),
path => path.includes('important')
);
// Debounced events for high-frequency changes
readonly onConfigChangeDebounced = Event.debounce(
this.onDidChangeEmitter.event,
500 // 500ms debounce
);
}High-level service for displaying user messages and dialogs.
/**
* Service for showing messages to users
*/
interface MessageService {
/**
* Show info message
* @param message - Message text
* @param actions - Optional actions
* @returns Promise resolving to selected action
*/
info(message: string, ...actions: string[]): Promise<string | undefined>;
/**
* Show warning message
* @param message - Message text
* @param actions - Optional actions
* @returns Promise resolving to selected action
*/
warn(message: string, ...actions: string[]): Promise<string | undefined>;
/**
* Show error message
* @param message - Message text
* @param actions - Optional actions
* @returns Promise resolving to selected action
*/
error(message: string, ...actions: string[]): Promise<string | undefined>;
}
/**
* Service token for MessageService
*/
const MessageService: symbol;type EventListener<T> = (e: T) => any;
type MaybePromise<T> = T | Promise<T>;
interface Disposable {
dispose(): void;
}
interface DisposableCollection extends Disposable {
push(disposable: Disposable): Disposable;
}Install with Tessl CLI
npx tessl i tessl/npm-theia--core