This repository has been archived by the owner on May 21, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 617
/
PTY.ts
96 lines (82 loc) · 3.37 KB
/
PTY.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import * as ChildProcess from "child_process";
import * as OS from "os";
import * as _ from "lodash";
import * as pty from "node-pty";
import {loginShell} from "./utils/Shell";
import {homeDirectory, info} from "./utils/Common";
interface ITerminal {
write(data: string): void;
resize(cols: number, rows: number): void;
kill(signal?: string): void;
on(type: string, listener: (...args: any[]) => any): void;
}
export class PTY {
private terminal: ITerminal;
// TODO: write proper signatures.
// TODO: use generators.
// TODO: terminate. https://github.com/atom/atom/blob/v1.0.15/src/task.coffee#L151
constructor(words: EscapedShellWord[], env: ProcessEnvironment, dimensions: Dimensions, dataHandler: (d: string) => void, exitHandler: (c: number) => void) {
const shellArguments = [...loginShell.noConfigSwitches, ...loginShell.interactiveCommandSwitches, words.join(" ")];
info(`PTY: ${loginShell.executableName} ${JSON.stringify(shellArguments)}`);
info(`Dimensions: ${JSON.stringify(dimensions)}}`);
this.terminal = <any> pty.fork(loginShell.executableName, shellArguments, {
cols: dimensions.columns,
rows: dimensions.rows,
cwd: env.PWD,
env: env,
});
this.terminal.on("data", (data: string) => dataHandler(data));
this.terminal.on("exit", (code: number) => exitHandler(code));
}
write(data: string): void {
this.terminal.write(data);
}
resize(dimensions: Dimensions) {
this.terminal.resize(dimensions.columns, dimensions.rows);
}
kill(signal: string): void {
/**
* The if branch is necessary because pty.js doesn't handle SIGINT correctly.
* You can test whether it works by executing
* ruby -e "loop { puts 'yes'; sleep 1 }"
* and trying to kill it with SIGINT.
*
* {@link https://github.com/chjj/pty.js/issues/58}
*/
if (signal === "SIGINT") {
this.terminal.kill("SIGTERM");
} else {
this.terminal.kill(signal);
}
}
}
export function executeCommand(
command: string,
args: string[] = [],
directory: string,
execOptions?: any,
): Promise<string> {
return new Promise((resolve, reject) => {
const options = {
...execOptions,
env: _.extend({PWD: directory, SHLVL: 1}, process.env),
cwd: directory,
shell: loginShell.commandExecutorPath,
};
ChildProcess.exec(`${command} ${args.join(" ")}`, options, (error, output) => {
if (error) {
reject(error);
} else {
resolve(output.toString());
}
});
});
}
export async function linedOutputOf(command: string, args: string[], directory: string): Promise<string[]> {
let output = await executeCommand(command, args, directory);
return output.split("\\" + OS.EOL).join(" ").split(OS.EOL).filter(path => path.length > 0);
}
export async function executeCommandWithShellConfig(command: string): Promise<string[]> {
const sourceCommands = (await loginShell.existingConfigFiles()).map(fileName => `source ${fileName} &> /dev/null`);
return await linedOutputOf(loginShell.executableName, [...loginShell.executeCommandSwitches, loginShell.combineCommands([...sourceCommands, command])], homeDirectory);
}