Revert "Revert "Merge from vscode ada4bddb8edc69eea6ebaaa0e88c5f903cbd43d8 (#5529)" (#5553)" (#5562)

This reverts commit c9a4f8f664.
This commit is contained in:
Anthony Dresser
2019-05-21 14:19:32 -07:00
committed by GitHub
parent 7670104e4d
commit 81ae86ff79
325 changed files with 4497 additions and 3328 deletions

View File

@@ -13,11 +13,12 @@ import { getWindowsBuildNumber } from 'vs/workbench/contrib/terminal/node/termin
import { IDisposable } from 'vs/base/common/lifecycle';
import { IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
import { exec } from 'child_process';
import { ILogService } from 'vs/platform/log/common/log';
export class TerminalProcess implements ITerminalChildProcess, IDisposable {
private _exitCode: number;
private _closeTimeout: any;
private _ptyProcess: pty.IPty;
private _ptyProcess: pty.IPty | undefined;
private _currentTitle: string = '';
private _processStartupComplete: Promise<void>;
private _isDisposed: boolean = false;
@@ -39,7 +40,8 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
cols: number,
rows: number,
env: platform.IProcessEnvironment,
windowsEnableConpty: boolean
windowsEnableConpty: boolean,
@ILogService private readonly _logService: ILogService
) {
let shellName: string;
if (os.platform() === 'win32') {
@@ -69,37 +71,42 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
experimentalUseConpty: useConpty
};
try {
this._ptyProcess = pty.spawn(shellLaunchConfig.executable!, shellLaunchConfig.args || [], options);
this._processStartupComplete = new Promise<void>(c => {
this.onProcessIdReady((pid) => {
c();
});
});
} catch (error) {
// The only time this is expected to happen is when the file specified to launch with does not exist.
this._exitCode = 2;
this._queueProcessExit();
this._processStartupComplete = Promise.resolve(undefined);
return;
}
this._ptyProcess.on('data', (data) => {
// TODO: Need to verify whether executable is on $PATH, otherwise things like cmd.exe will break
// fs.stat(shellLaunchConfig.executable!, (err) => {
// if (err && err.code === 'ENOENT') {
// this._exitCode = SHELL_PATH_INVALID_EXIT_CODE;
// this._queueProcessExit();
// this._processStartupComplete = Promise.resolve(undefined);
// return;
// }
this.setupPtyProcess(shellLaunchConfig, options);
// });
}
private setupPtyProcess(shellLaunchConfig: IShellLaunchConfig, options: pty.IPtyForkOptions): void {
const args = shellLaunchConfig.args || [];
this._logService.trace('IPty#spawn', shellLaunchConfig.executable, args, options);
const ptyProcess = pty.spawn(shellLaunchConfig.executable!, args, options);
this._ptyProcess = ptyProcess;
this._processStartupComplete = new Promise<void>(c => {
this.onProcessIdReady(() => c());
});
ptyProcess.on('data', (data) => {
this._onProcessData.fire(data);
if (this._closeTimeout) {
clearTimeout(this._closeTimeout);
this._queueProcessExit();
}
});
this._ptyProcess.on('exit', (code) => {
ptyProcess.on('exit', (code) => {
this._exitCode = code;
this._queueProcessExit();
});
this._setupTitlePolling(ptyProcess);
// TODO: We should no longer need to delay this since pty.spawn is sync
setTimeout(() => {
this._sendProcessId();
this._sendProcessId(ptyProcess);
}, 500);
this._setupTitlePolling();
}
public dispose(): void {
@@ -114,15 +121,15 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
this._onProcessTitleChanged.dispose();
}
private _setupTitlePolling() {
private _setupTitlePolling(ptyProcess: pty.IPty) {
// Send initial timeout async to give event listeners a chance to init
setTimeout(() => {
this._sendProcessTitle();
this._sendProcessTitle(ptyProcess);
}, 0);
// Setup polling
this._titleInterval = setInterval(() => {
if (this._currentTitle !== this._ptyProcess.process) {
this._sendProcessTitle();
if (this._currentTitle !== ptyProcess.process) {
this._sendProcessTitle(ptyProcess);
}
}, 200);
}
@@ -146,7 +153,10 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
// Attempt to kill the pty, it may have already been killed at this
// point but we want to make sure
try {
this._ptyProcess.kill();
if (this._ptyProcess) {
this._logService.trace('IPty#kill');
this._ptyProcess.kill();
}
} catch (ex) {
// Swallow, the pty has already been killed
}
@@ -155,15 +165,15 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
});
}
private _sendProcessId() {
this._onProcessIdReady.fire(this._ptyProcess.pid);
private _sendProcessId(ptyProcess: pty.IPty) {
this._onProcessIdReady.fire(ptyProcess.pid);
}
private _sendProcessTitle(): void {
private _sendProcessTitle(ptyProcess: pty.IPty): void {
if (this._isDisposed) {
return;
}
this._currentTitle = this._ptyProcess.process;
this._currentTitle = ptyProcess.process;
this._onProcessTitleChanged.fire(this._currentTitle);
}
@@ -176,9 +186,10 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
}
public input(data: string): void {
if (this._isDisposed) {
if (this._isDisposed || !this._ptyProcess) {
return;
}
this._logService.trace('IPty#write', data);
this._ptyProcess.write(data);
}
@@ -188,7 +199,12 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
}
// Ensure that cols and rows are always >= 1, this prevents a native
// exception in winpty.
this._ptyProcess.resize(Math.max(cols, 1), Math.max(rows, 1));
if (this._ptyProcess) {
cols = Math.max(cols, 1);
rows = Math.max(rows, 1);
this._logService.trace('IPty#resize', cols, rows);
this._ptyProcess.resize(cols, rows);
}
}
public getInitialCwd(): Promise<string> {
@@ -198,6 +214,11 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
public getCwd(): Promise<string> {
if (platform.isMacintosh) {
return new Promise<string>(resolve => {
if (!this._ptyProcess) {
resolve(this._initialCwd);
return;
}
this._logService.trace('IPty#pid');
exec('lsof -p ' + this._ptyProcess.pid + ' | grep cwd', (error, stdout, stderr) => {
if (stdout !== '') {
resolve(stdout.substring(stdout.indexOf('/'), stdout.length - 1));
@@ -208,6 +229,11 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable {
if (platform.isLinux) {
return new Promise<string>(resolve => {
if (!this._ptyProcess) {
resolve(this._initialCwd);
return;
}
this._logService.trace('IPty#pid');
fs.readlink('/proc/' + this._ptyProcess.pid + '/cwd', (err, linkedstr) => {
if (err) {
resolve(this._initialCwd);