Skip to content

Commit

Permalink
Use new EventEmitter2
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyriar committed Apr 28, 2019
1 parent b1201e3 commit f277da3
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 1 deletion.
30 changes: 30 additions & 0 deletions src/eventEmitter2.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2019, Microsoft Corporation (MIT License).
*/

import * as assert from 'assert';
import { EventEmitter2 } from './eventEmitter2';

describe('EventEmitter2', () => {
it('should fire listeners multiple times', () => {
const order: string[] = [];
const emitter = new EventEmitter2<number>();
emitter.event(data => order.push(data + 'a'));
emitter.event(data => order.push(data + 'b'));
emitter.fire(1);
emitter.fire(2);
assert.deepEqual(order, [ '1a', '1b', '2a', '2b' ]);
});

it('should not fire listeners once disposed', () => {
const order: string[] = [];
const emitter = new EventEmitter2<number>();
emitter.event(data => order.push(data + 'a'));
const disposeB = emitter.event(data => order.push(data + 'b'));
emitter.event(data => order.push(data + 'c'));
emitter.fire(1);
disposeB.dispose();
emitter.fire(2);
assert.deepEqual(order, [ '1a', '1b', '1c', '2a', '2c' ]);
});
});
48 changes: 48 additions & 0 deletions src/eventEmitter2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2019, Microsoft Corporation (MIT License).
*/

import { IDisposable } from './types';

interface IListener<T> {
(e: T): void;
}

export interface IEvent<T> {
(listener: (e: T) => any): IDisposable;
}

export class EventEmitter2<T> {
private _listeners: IListener<T>[] = [];
private _event?: IEvent<T>;

public get event(): IEvent<T> {
if (!this._event) {
this._event = (listener: (e: T) => any) => {
this._listeners.push(listener);
const disposable = {
dispose: () => {
for (let i = 0; i < this._listeners.length; i++) {
if (this._listeners[i] === listener) {
this._listeners.splice(i, 1);
return;
}
}
}
};
return disposable;
};
}
return this._event;
}

public fire(data: T): void {
const queue: IListener<T>[] = [];
for (let i = 0; i < this._listeners.length; i++) {
queue.push(this._listeners[i]);
}
for (let i = 0; i < queue.length; i++) {
queue[i].call(undefined, data);
}
}
}
13 changes: 12 additions & 1 deletion src/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
* Copyright (c) 2018, Microsoft Corporation (MIT License).
*/

import * as path from 'path';
import { Socket } from 'net';
import { EventEmitter } from 'events';
import { ITerminal, IPtyForkOptions } from './interfaces';
import { EventEmitter2, IEvent } from './eventEmitter2';
import { IExitEvent } from './types';

export const DEFAULT_COLS: number = 80;
export const DEFAULT_ROWS: number = 24;
Expand All @@ -28,6 +29,11 @@ export abstract class Terminal implements ITerminal {

protected _internalee: EventEmitter;

private _onData = new EventEmitter2<string>();
public get onData(): IEvent<string> { return this._onData.event; }
private _onExit = new EventEmitter2<IExitEvent>();
public get onExit(): IEvent<IExitEvent> { return this._onExit.event; }

public get pid(): number { return this._pid; }

constructor(opt?: IPtyForkOptions) {
Expand All @@ -50,6 +56,11 @@ export abstract class Terminal implements ITerminal {
this._checkType('encoding', opt.encoding ? opt.encoding : null, 'string');
}

protected _forwardEvents(): void {
this.on('data', e => this._onData.fire(e));
this.on('exit', (exitCode, signal) => this._onExit.fire({ exitCode, signal }));
}

private _checkType(name: string, value: any, type: string): void {
if (value && typeof value !== type) {
throw new Error(`${name} must be a ${type} (not a ${typeof value})`);
Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,12 @@
*/

export type ArgvOrCommandLine = string[] | string;

export interface IExitEvent {
exitCode: number;
signal: number | undefined;
}

export interface IDisposable {
dispose(): void;
}
2 changes: 2 additions & 0 deletions src/unixTerminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ export class UnixTerminal extends Terminal {
this._close();
this.emit('close');
});

this._forwardEvents();
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/windowsTerminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ export class WindowsTerminal extends Terminal {

this._readable = true;
this._writable = true;

this._forwardEvents();
}

/**
Expand Down
30 changes: 30 additions & 0 deletions typings/node-pty.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,24 @@ declare module 'node-pty' {
*/
process: string;

/**
* Adds an event listener for when a data event fires. This happens when data is returned from
* the pty.
* @returns an `IDisposable` to stop listening.
*/
onData: IEvent<string>;

/**
* Adds an event listener for when an exit event fires. This happens when the pty exits.
* @returns an `IDisposable` to stop listening.
*/
onExit: IEvent<{ exitCode: number, signal?: number }>;

/**
* Adds a listener to the data event, fired when data is returned from the pty.
* @param event The name of the event.
* @param listener The callback function.
* @deprecated Use IPty.onData
*/
on(event: 'data', listener: (data: string) => void): void;

Expand All @@ -62,6 +76,7 @@ declare module 'node-pty' {
* @param event The name of the event.
* @param listener The callback function, exitCode is the exit code of the process and signal is
* the signal that triggered the exit. signal is not supported on Windows.
* @deprecated Use IPty.onExit
*/
on(event: 'exit', listener: (exitCode: number, signal?: number) => void): void;

Expand All @@ -86,4 +101,19 @@ declare module 'node-pty' {
*/
kill(signal?: string): void;
}

/**
* An object that can be disposed via a dispose function.
*/
export interface IDisposable {
dispose(): void;
}

/**
* An event that can be listened to.
* @returns an `IDisposable` to stop listening.
*/
export interface IEvent<T> {
(listener: (e: T) => any): IDisposable;
}
}

0 comments on commit f277da3

Please sign in to comment.