提交 036a087a 编写于 作者: D Daniel Imms

Move environment helper functions into own module

上级 0b1b44d3
......@@ -459,7 +459,6 @@ export interface ITerminalCommandTracker {
export interface ITerminalProcessMessage {
type: 'pid' | 'data' | 'title';
// TODO: Use conditional type so type is narrowed
content: number | string;
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as os from 'os';
import * as path from 'path';
import * as platform from 'vs/base/common/platform';
import pkg from 'vs/platform/node/package';
import Uri from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/parts/terminal/common/terminal';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
/**
* This module contains utility functions related to the environment, cwd and paths.
*/
export function mergeEnvironments(parent: IStringDictionary<string>, other: IStringDictionary<string>) {
if (!other) {
return;
}
// On Windows apply the new values ignoring case, while still retaining
// the case of the original key.
if (platform.isWindows) {
for (let configKey in other) {
let actualKey = configKey;
for (let envKey in parent) {
if (configKey.toLowerCase() === envKey.toLowerCase()) {
actualKey = envKey;
break;
}
}
const value = other[configKey];
_mergeEnvironmentValue(parent, actualKey, value);
}
} else {
Object.keys(other).forEach((key) => {
const value = other[key];
_mergeEnvironmentValue(parent, key, value);
});
}
}
function _mergeEnvironmentValue(env: IStringDictionary<string>, key: string, value: string | null) {
if (typeof value === 'string') {
env[key] = value;
} else {
delete env[key];
}
}
export function createTerminalEnv(parentEnv: IStringDictionary<string>, shell: IShellLaunchConfig, cwd: string, locale: string, cols?: number, rows?: number): IStringDictionary<string> {
const env = { ...parentEnv };
if (shell.env) {
mergeEnvironments(env, shell.env);
}
env['PTYPID'] = process.pid.toString();
env['PTYSHELL'] = shell.executable;
env['TERM_PROGRAM'] = 'vscode';
env['TERM_PROGRAM_VERSION'] = pkg.version;
if (shell.args) {
if (typeof shell.args === 'string') {
env[`PTYSHELLCMDLINE`] = shell.args;
} else {
shell.args.forEach((arg, i) => env[`PTYSHELLARG${i}`] = arg);
}
}
env['PTYCWD'] = cwd;
env['LANG'] = _getLangEnvVariable(locale);
if (cols && rows) {
env['PTYCOLS'] = cols.toString();
env['PTYROWS'] = rows.toString();
}
env['AMD_ENTRYPOINT'] = 'vs/workbench/parts/terminal/node/terminalProcess';
return env;
}
// TODO:should be protected/non-static
export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: IStringDictionary<string>, lastActiveWorkspaceRoot: IWorkspaceFolder): IStringDictionary<string> {
Object.keys(env).forEach((key) => {
if (typeof env[key] === 'string') {
env[key] = configurationResolverService.resolve(lastActiveWorkspaceRoot, env[key]);
}
});
return env;
}
function _getLangEnvVariable(locale?: string) {
const parts = locale ? locale.split('-') : [];
const n = parts.length;
if (n === 0) {
// Fallback to en_US to prevent possible encoding issues.
return 'en_US.UTF-8';
}
if (n === 1) {
// app.getLocale can return just a language without a variant, fill in the variant for
// supported languages as many shells expect a 2-part locale.
const languageVariants = {
de: 'DE',
en: 'US',
es: 'ES',
fi: 'FI',
fr: 'FR',
it: 'IT',
ja: 'JP',
ko: 'KR',
pl: 'PL',
ru: 'RU',
zh: 'CN'
};
if (parts[0] in languageVariants) {
parts.push(languageVariants[parts[0]]);
}
} else {
// Ensure the variant is uppercase
parts[1] = parts[1].toUpperCase();
}
return parts.join('_') + '.UTF-8';
}
export function getCwd(shell: IShellLaunchConfig, root: Uri, configHelper: ITerminalConfigHelper): string {
if (shell.cwd) {
return shell.cwd;
}
let cwd: string;
// TODO: Handle non-existent customCwd
if (!shell.ignoreConfigurationCwd) {
// Evaluate custom cwd first
const customCwd = configHelper.config.cwd;
if (customCwd) {
if (path.isAbsolute(customCwd)) {
cwd = customCwd;
} else if (root) {
cwd = path.normalize(path.join(root.fsPath, customCwd));
}
}
}
// If there was no custom cwd or it was relative with no workspace
if (!cwd) {
cwd = root ? root.fsPath : os.homedir();
}
return _sanitizeCwd(cwd);
}
function _sanitizeCwd(cwd: string): string {
// Make the drive letter uppercase on Windows (see #9448)
if (platform.platform === platform.Platform.Windows && cwd && cwd[1] === ':') {
return cwd[0].toUpperCase() + cwd.substr(1);
}
return cwd;
}
......@@ -4,19 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import * as os from 'os';
import * as path from 'path';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/parts/terminal/electron-browser/terminalEnvironment';
import Uri from 'vs/base/common/uri';
import pkg from 'vs/platform/node/package';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ProcessState, ITerminalProcessManager, ITerminalProcessMessage, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/parts/terminal/common/terminal';
import { TPromise } from 'vs/base/common/winjs.base';
import { ILogService } from 'vs/platform/log/common/log';
import { Emitter, Event } from 'vs/base/common/event';
import { IStringDictionary } from 'vs/base/common/collections';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
/** The amount of time to consider terminal errors to be related to the launch */
......@@ -95,22 +93,22 @@ export class TerminalProcessManager implements ITerminalProcessManager {
}
const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot('file');
this.initialCwd = this._getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri);
this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri, this._configHelper);
// Resolve env vars from config and shell
const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri);
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
const envFromConfig = TerminalProcessManager.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
const envFromShell = TerminalProcessManager.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
shellLaunchConfig.env = envFromShell;
// Merge process env with the env from config
const parentEnv = { ...process.env };
TerminalProcessManager.mergeEnvironments(parentEnv, envFromConfig);
terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const env = TerminalProcessManager.createTerminalEnv(parentEnv, shellLaunchConfig, this.initialCwd, locale, cols, rows);
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, this.initialCwd, locale, cols, rows);
const cwd = Uri.parse(path.dirname(require.toUrl('../node/terminalProcess'))).fsPath;
const options = { env, cwd };
this._logService.debug(`Terminal process launching`, options);
......@@ -141,34 +139,6 @@ export class TerminalProcessManager implements ITerminalProcessManager {
}
}
protected _getCwd(shell: IShellLaunchConfig, root: Uri): string {
if (shell.cwd) {
return shell.cwd;
}
let cwd: string;
// TODO: Handle non-existent customCwd
if (!shell.ignoreConfigurationCwd) {
// Evaluate custom cwd first
const customCwd = this._configHelper.config.cwd;
if (customCwd) {
if (path.isAbsolute(customCwd)) {
cwd = customCwd;
} else if (root) {
cwd = path.normalize(path.join(root.fsPath, customCwd));
}
}
}
// If there was no custom cwd or it was relative with no workspace
if (!cwd) {
cwd = root ? root.fsPath : os.homedir();
}
return TerminalProcessManager._sanitizeCwd(cwd);
}
public write(data: string): void {
if (this.shellProcessId) {
// Send data if the pty is ready
......@@ -225,118 +195,4 @@ export class TerminalProcessManager implements ITerminalProcessManager {
this._onProcessExit.fire(exitCode);
}
public static mergeEnvironments(parent: IStringDictionary<string>, other: IStringDictionary<string>) {
if (!other) {
return;
}
// On Windows apply the new values ignoring case, while still retaining
// the case of the original key.
if (platform.isWindows) {
for (let configKey in other) {
let actualKey = configKey;
for (let envKey in parent) {
if (configKey.toLowerCase() === envKey.toLowerCase()) {
actualKey = envKey;
break;
}
}
const value = other[configKey];
TerminalProcessManager._mergeEnvironmentValue(parent, actualKey, value);
}
} else {
Object.keys(other).forEach((key) => {
const value = other[key];
TerminalProcessManager._mergeEnvironmentValue(parent, key, value);
});
}
}
private static _mergeEnvironmentValue(env: IStringDictionary<string>, key: string, value: string | null) {
if (typeof value === 'string') {
env[key] = value;
} else {
delete env[key];
}
}
// TODO: This should be private/protected
public static createTerminalEnv(parentEnv: IStringDictionary<string>, shell: IShellLaunchConfig, cwd: string, locale: string, cols?: number, rows?: number): IStringDictionary<string> {
const env = { ...parentEnv };
if (shell.env) {
TerminalProcessManager.mergeEnvironments(env, shell.env);
}
env['PTYPID'] = process.pid.toString();
env['PTYSHELL'] = shell.executable;
env['TERM_PROGRAM'] = 'vscode';
env['TERM_PROGRAM_VERSION'] = pkg.version;
if (shell.args) {
if (typeof shell.args === 'string') {
env[`PTYSHELLCMDLINE`] = shell.args;
} else {
shell.args.forEach((arg, i) => env[`PTYSHELLARG${i}`] = arg);
}
}
env['PTYCWD'] = cwd;
env['LANG'] = TerminalProcessManager._getLangEnvVariable(locale);
if (cols && rows) {
env['PTYCOLS'] = cols.toString();
env['PTYROWS'] = rows.toString();
}
env['AMD_ENTRYPOINT'] = 'vs/workbench/parts/terminal/node/terminalProcess';
return env;
}
// TODO:should be protected/non-static
private static resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: IStringDictionary<string>, lastActiveWorkspaceRoot: IWorkspaceFolder): IStringDictionary<string> {
Object.keys(env).forEach((key) => {
if (typeof env[key] === 'string') {
env[key] = configurationResolverService.resolve(lastActiveWorkspaceRoot, env[key]);
}
});
return env;
}
private static _sanitizeCwd(cwd: string) {
// Make the drive letter uppercase on Windows (see #9448)
if (platform.platform === platform.Platform.Windows && cwd && cwd[1] === ':') {
return cwd[0].toUpperCase() + cwd.substr(1);
}
return cwd;
}
private static _getLangEnvVariable(locale?: string) {
const parts = locale ? locale.split('-') : [];
const n = parts.length;
if (n === 0) {
// Fallback to en_US to prevent possible encoding issues.
return 'en_US.UTF-8';
}
if (n === 1) {
// app.getLocale can return just a language without a variant, fill in the variant for
// supported languages as many shells expect a 2-part locale.
const languageVariants = {
de: 'DE',
en: 'US',
es: 'ES',
fi: 'FI',
fr: 'FR',
it: 'IT',
ja: 'JP',
ko: 'KR',
pl: 'PL',
ru: 'RU',
zh: 'CN'
};
if (parts[0] in languageVariants) {
parts.push(languageVariants[parts[0]]);
}
} else {
// Ensure the variant is uppercase
parts[1] = parts[1].toUpperCase();
}
return parts.join('_') + '.UTF-8';
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册