terminalEnvironment.ts 6.5 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import * as path from 'vs/base/common/path';
7
import * as platform from 'vs/base/common/platform';
8
import { URI as Uri } from 'vs/base/common/uri';
9
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
10
import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
11 12 13 14 15 16
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';

/**
 * This module contains utility functions related to the environment, cwd and paths.
 */

17
export function mergeEnvironments(parent: platform.IProcessEnvironment, other?: ITerminalEnvironment): void {
18 19 20 21 22 23 24
	if (!other) {
		return;
	}

	// On Windows apply the new values ignoring case, while still retaining
	// the case of the original key.
	if (platform.isWindows) {
D
Daniel Imms 已提交
25
		for (const configKey in other) {
26
			let actualKey = configKey;
D
Daniel Imms 已提交
27
			for (const envKey in parent) {
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
				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);
		});
	}
}

44
function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: string | null): void {
45 46 47 48 49 50 51
	if (typeof value === 'string') {
		env[key] = value;
	} else {
		delete env[key];
	}
}

52
export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void {
53
	env['TERM_PROGRAM'] = 'vscode';
54
	env['TERM_PROGRAM_VERSION'] = version ? version : null;
D
Daniel Imms 已提交
55 56 57
	if (setLocaleVariables) {
		env['LANG'] = _getLangEnvVariable(locale);
	}
58 59
}

60
export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | null): ITerminalEnvironment {
61
	Object.keys(env).forEach((key) => {
62 63 64
		const value = env[key];
		if (typeof value === 'string' && lastActiveWorkspaceRoot !== null) {
			env[key] = configurationResolverService.resolve(lastActiveWorkspaceRoot, value);
65 66 67 68 69
		}
	});
	return env;
}

70
function _getLangEnvVariable(locale?: string) {
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 97 98 99 100 101 102
	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';
}

103
export function getCwd(shell: IShellLaunchConfig, userHome: string, root?: Uri, customCwd?: string): string {
104
	if (shell.cwd) {
A
Alex Ross 已提交
105
		return (typeof shell.cwd === 'object') ? shell.cwd.fsPath : shell.cwd;
106 107
	}

108
	let cwd: string | undefined;
109 110

	// TODO: Handle non-existent customCwd
111
	if (!shell.ignoreConfigurationCwd && customCwd) {
112
		if (path.isAbsolute(customCwd)) {
113 114
			cwd = customCwd;
		} else if (root) {
115
			cwd = path.join(root.fsPath, customCwd);
116 117 118 119 120
		}
	}

	// If there was no custom cwd or it was relative with no workspace
	if (!cwd) {
121
		cwd = root ? root.fsPath : userHome;
122 123 124 125 126 127 128 129 130 131 132 133
	}

	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;
}
134

135 136 137 138
export function escapeNonWindowsPath(path: string): string {
	let newPath = path;
	if (newPath.indexOf('\\') !== 0) {
		newPath = newPath.replace(/\\/g, '\\\\');
139
	}
140 141 142 143
	if (!newPath && (newPath.indexOf('"') !== -1)) {
		newPath = '\'' + newPath + '\'';
	} else if (newPath.indexOf(' ') !== -1) {
		newPath = newPath.replace(/ /g, '\\ ');
144
	}
145
	return newPath;
146
}
D
Daniel Imms 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

export function mergeDefaultShellPathAndArgs(
	shell: IShellLaunchConfig,
	fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined },
	isWorkspaceShellAllowed: boolean,
	platformOverride: platform.Platform = platform.platform
): void {
	const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux';
	const shellConfigValue = fetchSetting(`terminal.integrated.shell.${platformKey}`);
	// const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
	const shellArgsConfigValue = fetchSetting(`terminal.integrated.shellArgs.${platformKey}`);
	// const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);

	shell.executable = (isWorkspaceShellAllowed ? <string>shellConfigValue.value : <string>shellConfigValue.user) || <string>shellConfigValue.default;
	shell.args = (isWorkspaceShellAllowed ? <string[]>shellArgsConfigValue.value : <string[]>shellArgsConfigValue.user) || <string[]>shellArgsConfigValue.default;

	// Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's
	// safe to assume that this was used by accident as Sysnative does not
	// exist and will break the terminal in non-WoW64 environments.
	if ((platformOverride === platform.Platform.Windows) && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && process.env.windir) {
		const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase();
		if (shell.executable && shell.executable.toLowerCase().indexOf(sysnativePath) === 0) {
			shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length));
		}
	}

	// Convert / to \ on Windows for convenience
	if (shell.executable && platformOverride === platform.Platform.Windows) {
		shell.executable = shell.executable.replace(/\//g, '\\');
	}
}