mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-31 09:10:30 -04:00
Vscode merge (#4582)
* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd * fix issues with merges * bump node version in azpipe * replace license headers * remove duplicate launch task * fix build errors * fix build errors * fix tslint issues * working through package and linux build issues * more work * wip * fix packaged builds * working through linux build errors * wip * wip * wip * fix mac and linux file limits * iterate linux pipeline * disable editor typing * revert series to parallel * remove optimize vscode from linux * fix linting issues * revert testing change * add work round for new node * readd packaging for extensions * fix issue with angular not resolving decorator dependencies
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { TerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminalProcessExtHostProxy';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { sanitizeProcessEnvironment } from 'vs/base/common/processes';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IRemoteEnvironmentService } from 'vs/workbench/services/remote/common/remoteEnvironmentService';
|
||||
|
||||
/** The amount of time to consider terminal errors to be related to the launch */
|
||||
const LAUNCHING_DURATION = 500;
|
||||
|
||||
/**
|
||||
* Holds all state related to the creation and management of terminal processes.
|
||||
*
|
||||
* Internal definitions:
|
||||
* - Process: The process launched with the terminalProcess.ts file, or the pty as a whole
|
||||
* - Pty Process: The pseudoterminal master process (or the winpty agent process)
|
||||
* - Shell Process: The pseudoterminal slave process (ie. the shell)
|
||||
*/
|
||||
export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
public processState: ProcessState = ProcessState.UNINITIALIZED;
|
||||
public ptyProcessReady: Promise<void>;
|
||||
public shellProcessId: number;
|
||||
public remoteAuthority: string | undefined;
|
||||
public os: platform.OperatingSystem | undefined;
|
||||
public userHome: string | undefined;
|
||||
|
||||
private _process: ITerminalChildProcess | null = null;
|
||||
private _preLaunchInputQueue: string[] = [];
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private readonly _onProcessReady = new Emitter<void>();
|
||||
public get onProcessReady(): Event<void> { return this._onProcessReady.event; }
|
||||
private readonly _onProcessData = new Emitter<string>();
|
||||
public get onProcessData(): Event<string> { return this._onProcessData.event; }
|
||||
private readonly _onProcessTitle = new Emitter<string>();
|
||||
public get onProcessTitle(): Event<string> { return this._onProcessTitle.event; }
|
||||
private readonly _onProcessExit = new Emitter<number>();
|
||||
public get onProcessExit(): Event<number> { return this._onProcessExit.event; }
|
||||
|
||||
constructor(
|
||||
private readonly _terminalId: number,
|
||||
private readonly _configHelper: ITerminalConfigHelper,
|
||||
@IHistoryService private readonly _historyService: IHistoryService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
|
||||
@IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService,
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IConfigurationService private readonly _workspaceConfigurationService: IConfigurationService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
@ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService,
|
||||
@IRemoteEnvironmentService private readonly _remoteEnvironmentService: IRemoteEnvironmentService
|
||||
) {
|
||||
this.ptyProcessReady = new Promise<void>(c => {
|
||||
this.onProcessReady(() => {
|
||||
this._logService.debug(`Terminal process ready (shellProcessId: ${this.shellProcessId})`);
|
||||
c(undefined);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(immediate: boolean = false): void {
|
||||
if (this._process) {
|
||||
// If the process was still connected this dispose came from
|
||||
// within VS Code, not the process, so mark the process as
|
||||
// killed by the user.
|
||||
this.processState = ProcessState.KILLED_BY_USER;
|
||||
this._process.shutdown(immediate);
|
||||
this._process = null;
|
||||
}
|
||||
this._disposables.forEach(d => d.dispose());
|
||||
this._disposables.length = 0;
|
||||
}
|
||||
|
||||
public addDisposable(disposable: IDisposable) {
|
||||
this._disposables.push(disposable);
|
||||
}
|
||||
|
||||
public createProcess(
|
||||
shellLaunchConfig: IShellLaunchConfig,
|
||||
cols: number,
|
||||
rows: number
|
||||
): void {
|
||||
const forceExtHostProcess = (this._configHelper.config as any).extHostProcess;
|
||||
if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') {
|
||||
this.remoteAuthority = getRemoteAuthority(shellLaunchConfig.cwd);
|
||||
} else {
|
||||
this.remoteAuthority = this._windowService.getConfiguration().remoteAuthority;
|
||||
}
|
||||
const hasRemoteAuthority = !!this.remoteAuthority;
|
||||
let launchRemotely = hasRemoteAuthority || forceExtHostProcess;
|
||||
|
||||
this.userHome = this._environmentService.userHome;
|
||||
this.os = platform.OS;
|
||||
if (launchRemotely) {
|
||||
if (hasRemoteAuthority) {
|
||||
this._remoteEnvironmentService.remoteEnvironment.then(env => {
|
||||
if (!env) {
|
||||
return;
|
||||
}
|
||||
this.userHome = env.userHome.path;
|
||||
this.os = env.os;
|
||||
});
|
||||
}
|
||||
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(hasRemoteAuthority ? REMOTE_HOST_SCHEME : undefined);
|
||||
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows);
|
||||
} else {
|
||||
if (!shellLaunchConfig.executable) {
|
||||
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
|
||||
}
|
||||
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd);
|
||||
|
||||
// Compel type system as process.env should not have any undefined entries
|
||||
let env: platform.IProcessEnvironment = {};
|
||||
|
||||
if (shellLaunchConfig.strictEnv) {
|
||||
// Only base the terminal process environment on this environment and add the
|
||||
// various mixins when strictEnv is false
|
||||
env = { ...shellLaunchConfig.env } as any;
|
||||
} else {
|
||||
// Merge process env with the env from config and from shellLaunchConfig
|
||||
env = { ...process.env } as any;
|
||||
|
||||
// Resolve env vars from config and shell
|
||||
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
|
||||
const envFromConfigValue = this._workspaceConfigurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`);
|
||||
const allowedEnvFromConfig = (isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user);
|
||||
const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...allowedEnvFromConfig }, lastActiveWorkspaceRoot);
|
||||
const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
|
||||
shellLaunchConfig.env = envFromShell;
|
||||
|
||||
terminalEnvironment.mergeEnvironments(env, envFromConfig);
|
||||
terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
|
||||
|
||||
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
||||
// variables
|
||||
sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
|
||||
|
||||
// Adding other env keys necessary to create the process
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables);
|
||||
}
|
||||
|
||||
this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
|
||||
this._process = this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);
|
||||
}
|
||||
this.processState = ProcessState.LAUNCHING;
|
||||
|
||||
// The process is non-null, but TS isn't clever enough to know
|
||||
const p = this._process!;
|
||||
|
||||
p.onProcessData(data => {
|
||||
this._onProcessData.fire(data);
|
||||
});
|
||||
|
||||
p.onProcessIdReady(pid => {
|
||||
this.shellProcessId = pid;
|
||||
this._onProcessReady.fire();
|
||||
|
||||
// Send any queued data that's waiting
|
||||
if (this._preLaunchInputQueue.length > 0) {
|
||||
p.input(this._preLaunchInputQueue.join(''));
|
||||
this._preLaunchInputQueue.length = 0;
|
||||
}
|
||||
});
|
||||
|
||||
p.onProcessTitleChanged(title => this._onProcessTitle.fire(title));
|
||||
p.onProcessExit(exitCode => this._onExit(exitCode));
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.processState === ProcessState.LAUNCHING) {
|
||||
this.processState = ProcessState.RUNNING;
|
||||
}
|
||||
}, LAUNCHING_DURATION);
|
||||
}
|
||||
|
||||
public setDimensions(cols: number, rows: number): void {
|
||||
if (!this._process) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The child process could already be terminated
|
||||
try {
|
||||
this._process.resize(cols, rows);
|
||||
} catch (error) {
|
||||
// We tried to write to a closed pipe / channel.
|
||||
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
|
||||
throw (error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public write(data: string): void {
|
||||
if (this.shellProcessId) {
|
||||
if (this._process) {
|
||||
// Send data if the pty is ready
|
||||
this._process.input(data);
|
||||
}
|
||||
} else {
|
||||
// If the pty is not ready, queue the data received to send later
|
||||
this._preLaunchInputQueue.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
public getInitialCwd(): Promise<string> {
|
||||
if (!this._process) {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
return this._process.getInitialCwd();
|
||||
}
|
||||
|
||||
public getCwd(): Promise<string> {
|
||||
if (!this._process) {
|
||||
return Promise.resolve('');
|
||||
}
|
||||
return this._process.getCwd();
|
||||
}
|
||||
|
||||
private _onExit(exitCode: number): void {
|
||||
this._process = null;
|
||||
|
||||
// If the process is marked as launching then mark the process as killed
|
||||
// during launch. This typically means that there is a problem with the
|
||||
// shell and args.
|
||||
if (this.processState === ProcessState.LAUNCHING) {
|
||||
this.processState = ProcessState.KILLED_DURING_LAUNCH;
|
||||
}
|
||||
|
||||
// If TerminalInstance did not know about the process exit then it was
|
||||
// triggered by the process, not on VS Code's side.
|
||||
if (this.processState === ProcessState.RUNNING) {
|
||||
this.processState = ProcessState.KILLED_BY_PROCESS;
|
||||
}
|
||||
|
||||
this._onProcessExit.fire(exitCode);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user