diff --git a/src/Terminal.ts b/src/Terminal.ts index db696bd6..8c69a551 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -52,6 +52,7 @@ import { IKeyboardEvent } from './common/Types'; import { evaluateKeyboardEvent } from './core/input/Keyboard'; import { KeyboardResultType, ICharset } from './core/Types'; import { clone } from './common/Clone'; +import { EventEmitter2, IEvent } from './common/EventEmitter2'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -217,6 +218,23 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II public cols: number; public rows: number; + private _onLineFeed = new EventEmitter2(); + public get onLineFeed(): IEvent { return this._onLineFeed.event; } + private _onSelectionChange = new EventEmitter2(); + public get onSelectionChange(): IEvent { return this._onSelectionChange.event; } + private _onInput = new EventEmitter2(); + public get onInput(): IEvent { return this._onInput.event; } + private _onTitleChange = new EventEmitter2(); + public get onTitleChange(): IEvent { return this._onTitleChange.event; } + private _onScroll = new EventEmitter2(); + public get onScroll(): IEvent { return this._onScroll.event; } + private _onKey = new EventEmitter2<{ key: string, domEvent: KeyboardEvent }>(); + public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._onKey.event; } + private _onRender = new EventEmitter2<{ start: number, end: number }>(); + public get onRender(): IEvent<{ start: number, end: number }> { return this._onRender.event; } + private _onResize = new EventEmitter2<{ cols: number, rows: number }>(); + public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; } + /** * Creates a new `Terminal` object. * @@ -235,6 +253,16 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II super(); this.options = clone(options); this._setup(); + + // TODO: Replace EventEmitter with EventEmitter2 internally + this.on('linefeed', () => this._onLineFeed.fire()); + this.on('selection', () => this._onSelectionChange.fire()); + this.on('data', e => this._onInput.fire(e)); + this.on('title', e => this._onTitleChange.fire(e)); + this.on('scroll', e => this._onScroll.fire(e)); + this.on('key', e => this._onKey.fire(e)); + this.on('refresh', e => this._onRender.fire(e)); + this.on('resize', e => this._onResize.fire(e)); } public dispose(): void { diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts index 87fcfaef..181beeeb 100644 --- a/src/public/Terminal.ts +++ b/src/public/Terminal.ts @@ -7,6 +7,7 @@ import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILink import { ITerminal } from '../Types'; import { Terminal as TerminalCore } from '../Terminal'; import * as Strings from '../Strings'; +import { IEvent } from '../../lib/common/EventEmitter2'; export class Terminal implements ITerminalApi { private _core: ITerminal; @@ -15,6 +16,15 @@ export class Terminal implements ITerminalApi { this._core = new TerminalCore(options); } + public get onLineFeed(): IEvent { return this._core.onLineFeed; } + public get onSelectionChange(): IEvent { return this._core.onSelectionChange; } + public get onInput(): IEvent { return this._core.onInput; } + public get onTitleChange(): IEvent { return this._core.onTitleChange; } + public get onScroll(): IEvent { return this._core.onScroll; } + public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; } + public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; } + public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; } + public get element(): HTMLElement { return this._core.element; } public get textarea(): HTMLTextAreaElement { return this._core.textarea; } public get rows(): number { return this._core.rows; } diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index d813bc5f..0a29cfc8 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -295,6 +295,14 @@ declare module 'xterm' { dispose(): void; } + /** + * An event that can be listened to. + * @returns an `IDisposable` to stop listening. + */ + export interface IEvent { + (listener: (e: T) => any): IDisposable; + } + export interface IMarker extends IDisposable { readonly id: number; readonly isDisposed: boolean; @@ -353,6 +361,64 @@ declare module 'xterm' { */ constructor(options?: ITerminalOptions); + /** + * Adds an event listener for when a line feed is added. + * @returns an `IDisposable` to stop listening. + */ + onLineFeed: IEvent; + + /** + * Adds an event listener for when a selection change occurs. + * @returns an `IDisposable` to stop listening. + */ + onSelectionChange: IEvent; + + /** + * Adds an event listener for when an input event fires. This happens for + * example when the user types or pastes into the terminal. The event value + * is whatever `string` results, in a typical setup, this should be passed + * on to the backing pty. + * @returns an `IDisposable` to stop listening. + */ + onInput: IEvent; + + /** + * Adds an event listener for when an OSC 0 or OSC 2 title change occurs. + * The event value is the new title. + * @returns an `IDisposable` to stop listening. + */ + onTitleChange: IEvent; + + /** + * Adds an event listener for when a scroll occurs. The event value is the + * new position of the viewport. + * @returns an `IDisposable` to stop listening. + */ + onScroll: IEvent; + + /** + * Adds an event listener for a key is pressed. The event value contains the + * string that will be sent in the data event as well as the DOM event that + * triggered it. + * @returns an `IDisposable` to stop listening. + */ + onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>; + + /** + * Adds an event listener for when rows are rendered. The event value + * contains the start row and end rows of the rendered area (ranges from `0` + * to `Terminal.rows - 1`). + * @returns an `IDisposable` to stop listening. + */ + onRender: IEvent<{ start: number, end: number }>; + + /** + * Adds an event listener for when the terminal is resized. The event value + * contains the new size. + * @returns an `IDisposable` to stop listening. + */ + onResize: IEvent<{ cols: number, rows: number }>; + /** * Unfocus the terminal. */ @@ -367,54 +433,63 @@ declare module 'xterm' { * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'blur' | 'focus' | 'linefeed' | 'selection', listener: () => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'data', listener: (...args: any[]) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'key', listener: (key: string, event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'keypress' | 'keydown', listener: (event: KeyboardEvent) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'refresh', listener: (data: {start: number, end: number}) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'resize', listener: (data: {cols: number, rows: number}) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'scroll', listener: (ydisp: number) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: 'title', listener: (title: string) => void): void; /** * Registers an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener)` instead. */ on(type: string, listener: (...args: any[]) => void): void; @@ -422,6 +497,7 @@ declare module 'xterm' { * Deregisters an event listener. * @param type The type of the event. * @param listener The listener. + * @deprecated use `Terminal.onEvent(listener).dispose()` instead. */ off(type: 'blur' | 'focus' | 'linefeed' | 'selection' | 'data' | 'key' | 'keypress' | 'keydown' | 'refresh' | 'resize' | 'scroll' | 'title' | string, listener: (...args: any[]) => void): void; @@ -439,6 +515,7 @@ declare module 'xterm' { * be used to conveniently remove the event listener. * @param type The type of event. * @param handler The event handler. + * @deprecated use `Terminal.onEvent(listener)` instead. */ addDisposableListener(type: string, handler: (...args: any[]) => void): IDisposable;