installed pty
[VSoRC/.git] / node_modules / node-pty / src / terminal.ts
1 /**
2  * Copyright (c) 2012-2015, Christopher Jeffrey (MIT License)
3  * Copyright (c) 2016, Daniel Imms (MIT License).
4  * Copyright (c) 2018, Microsoft Corporation (MIT License).
5  */
6
7 import { Socket } from 'net';
8 import { EventEmitter } from 'events';
9 import { ITerminal, IPtyForkOptions } from './interfaces';
10 import { EventEmitter2, IEvent } from './eventEmitter2';
11 import { IExitEvent } from './types';
12
13 export const DEFAULT_COLS: number = 80;
14 export const DEFAULT_ROWS: number = 24;
15
16 /**
17  * Default messages to indicate PAUSE/RESUME for automatic flow control.
18  * To avoid conflicts with rebound XON/XOFF control codes (such as on-my-zsh),
19  * the sequences can be customized in `IPtyForkOptions`.
20  */
21 const FLOW_CONTROL_PAUSE =  '\x13';   // defaults to XOFF
22 const FLOW_CONTROL_RESUME = '\x11';   // defaults to XON
23
24 export abstract class Terminal implements ITerminal {
25   protected _socket: Socket;
26   protected _pid: number;
27   protected _fd: number;
28   protected _pty: any;
29
30   protected _file: string;
31   protected _name: string;
32   protected _cols: number;
33   protected _rows: number;
34
35   protected _readable: boolean;
36   protected _writable: boolean;
37
38   protected _internalee: EventEmitter;
39   private _flowControlPause: string;
40   private _flowControlResume: string;
41   public handleFlowControl: boolean;
42
43   private _onData = new EventEmitter2<string>();
44   public get onData(): IEvent<string> { return this._onData.event; }
45   private _onExit = new EventEmitter2<IExitEvent>();
46   public get onExit(): IEvent<IExitEvent> { return this._onExit.event; }
47
48   public get pid(): number { return this._pid; }
49   public get cols(): number { return this._cols; }
50   public get rows(): number { return this._rows; }
51
52   constructor(opt?: IPtyForkOptions) {
53     // for 'close'
54     this._internalee = new EventEmitter();
55
56     if (!opt) {
57       return;
58     }
59
60     // Do basic type checks here in case node-pty is being used within JavaScript. If the wrong
61     // types go through to the C++ side it can lead to hard to diagnose exceptions.
62     this._checkType('name', opt.name ? opt.name : null, 'string');
63     this._checkType('cols', opt.cols ? opt.cols : null, 'number');
64     this._checkType('rows', opt.rows ? opt.rows : null, 'number');
65     this._checkType('cwd', opt.cwd ? opt.cwd : null, 'string');
66     this._checkType('env', opt.env ? opt.env : null, 'object');
67     this._checkType('uid', opt.uid ? opt.uid : null, 'number');
68     this._checkType('gid', opt.gid ? opt.gid : null, 'number');
69     this._checkType('encoding', opt.encoding ? opt.encoding : null, 'string');
70
71     // setup flow control handling
72     this.handleFlowControl = !!(opt.handleFlowControl);
73     this._flowControlPause = opt.flowControlPause || FLOW_CONTROL_PAUSE;
74     this._flowControlResume = opt.flowControlResume || FLOW_CONTROL_RESUME;
75   }
76
77   protected abstract _write(data: string): void;
78
79   public write(data: string): void {
80     if (this.handleFlowControl) {
81       // PAUSE/RESUME messages are not forwarded to the pty
82       if (data === this._flowControlPause) {
83         this.pause();
84         return;
85       }
86       if (data === this._flowControlResume) {
87         this.resume();
88         return;
89       }
90     }
91     // everything else goes to the real pty
92     this._write(data);
93   }
94
95   protected _forwardEvents(): void {
96     this.on('data', e => this._onData.fire(e));
97     this.on('exit', (exitCode, signal) => this._onExit.fire({ exitCode, signal }));
98   }
99
100   private _checkType(name: string, value: any, type: string): void {
101     if (value && typeof value !== type) {
102       throw new Error(`${name} must be a ${type} (not a ${typeof value})`);
103     }
104   }
105
106   /** See net.Socket.end */
107   public end(data: string): void {
108     this._socket.end(data);
109   }
110
111   /** See stream.Readable.pipe */
112   public pipe(dest: any, options: any): any {
113     return this._socket.pipe(dest, options);
114   }
115
116   /** See net.Socket.pause */
117   public pause(): Socket {
118     return this._socket.pause();
119   }
120
121   /** See net.Socket.resume */
122   public resume(): Socket {
123     return this._socket.resume();
124   }
125
126   /** See net.Socket.setEncoding */
127   public setEncoding(encoding: string | null): void {
128     if ((<any>this._socket)._decoder) {
129       delete (<any>this._socket)._decoder;
130     }
131     if (encoding) {
132       this._socket.setEncoding(encoding);
133     }
134   }
135
136   public addListener(eventName: string, listener: (...args: any[]) => any): void { this.on(eventName, listener); }
137   public on(eventName: string, listener: (...args: any[]) => any): void {
138     if (eventName === 'close') {
139       this._internalee.on('close', listener);
140       return;
141     }
142     this._socket.on(eventName, listener);
143   }
144
145   public emit(eventName: string, ...args: any[]): any {
146     if (eventName === 'close') {
147       return this._internalee.emit.apply(this._internalee, arguments);
148     }
149     return this._socket.emit.apply(this._socket, arguments);
150   }
151
152   public listeners(eventName: string): Function[] {
153     return this._socket.listeners(eventName);
154   }
155
156   public removeListener(eventName: string, listener: (...args: any[]) => any): void {
157     this._socket.removeListener(eventName, listener);
158   }
159
160   public removeAllListeners(eventName: string): void {
161     this._socket.removeAllListeners(eventName);
162   }
163
164   public once(eventName: string, listener: (...args: any[]) => any): void {
165     this._socket.once(eventName, listener);
166   }
167
168   public abstract resize(cols: number, rows: number): void;
169   public abstract destroy(): void;
170   public abstract kill(signal?: string): void;
171
172   public abstract get process(): string;
173   public abstract get master(): Socket;
174   public abstract get slave(): Socket;
175
176   protected _close(): void {
177     this._socket.writable = false;
178     this._socket.readable = false;
179     this.write = () => {};
180     this.end = () => {};
181     this._writable = false;
182     this._readable = false;
183   }
184
185   protected _parseEnv(env: {[key: string]: string}): string[] {
186     const keys = Object.keys(env || {});
187     const pairs = [];
188
189     for (let i = 0; i < keys.length; i++) {
190       pairs.push(keys[i] + '=' + env[keys[i]]);
191     }
192
193     return pairs;
194   }
195 }