mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 01:00:29 -04:00
Merge from vscode ada4bddb8edc69eea6ebaaa0e88c5f903cbd43d8 (#5529)
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user