terminalEnvironment.ts 5.2 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  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';
D
Daniel Imms 已提交
7
import * as paths from 'vs/base/common/paths';
8 9
import * as platform from 'vs/base/common/platform';
import pkg from 'vs/platform/node/package';
10
import { URI as Uri } from 'vs/base/common/uri';
11
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
12
import { IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
13 14 15 16 17 18
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';

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

19
export function mergeEnvironments(parent: platform.IProcessEnvironment, other: platform.IProcessEnvironment): void {
20 21 22 23 24 25 26
	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 已提交
27
		for (const configKey in other) {
28
			let actualKey = configKey;
D
Daniel Imms 已提交
29
			for (const envKey in parent) {
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
				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);
		});
	}
}

46
function _mergeEnvironmentValue(env: platform.IProcessEnvironment, key: string, value: string | null): void {
47 48 49 50 51 52 53
	if (typeof value === 'string') {
		env[key] = value;
	} else {
		delete env[key];
	}
}

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
export function sanitizeEnvironment(env: platform.IProcessEnvironment): void {
	// Remove keys based on strings
	const keysToRemove = [
		'ELECTRON_ENABLE_STACK_DUMPING',
		'ELECTRON_ENABLE_LOGGING',
		'ELECTRON_NO_ASAR',
		'ELECTRON_NO_ATTACH_CONSOLE',
		'ELECTRON_RUN_AS_NODE',
		'GOOGLE_API_KEY',
		'VSCODE_CLI',
		'VSCODE_DEV',
		'VSCODE_IPC_HOOK',
		'VSCODE_LOGS',
		'VSCODE_NLS_CONFIG',
		'VSCODE_PORTABLE',
		'VSCODE_PID',
70
		'VSCODE_NODE_CACHED_DATA_DIR'
71 72 73 74 75 76 77 78
	];
	keysToRemove.forEach((key) => {
		if (env[key]) {
			delete env[key];
		}
	});
}

79
export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, locale: string | undefined): void {
80 81 82 83 84
	env['TERM_PROGRAM'] = 'vscode';
	env['TERM_PROGRAM_VERSION'] = pkg.version;
	env['LANG'] = _getLangEnvVariable(locale);
}

85
export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: platform.IProcessEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder): platform.IProcessEnvironment {
86 87 88 89 90 91 92 93
	Object.keys(env).forEach((key) => {
		if (typeof env[key] === 'string') {
			env[key] = configurationResolverService.resolve(lastActiveWorkspaceRoot, env[key]);
		}
	});
	return env;
}

94
function _getLangEnvVariable(locale?: string) {
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
	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';
}

127
export function getCwd(shell: IShellLaunchConfig, root: Uri, customCwd: string): string {
128 129 130 131
	if (shell.cwd) {
		return shell.cwd;
	}

132
	let cwd: string | undefined;
133 134

	// TODO: Handle non-existent customCwd
135 136 137 138 139
	if (!shell.ignoreConfigurationCwd && customCwd) {
		if (paths.isAbsolute(customCwd)) {
			cwd = customCwd;
		} else if (root) {
			cwd = paths.normalize(paths.join(root.fsPath, customCwd));
140 141 142 143 144
		}
	}

	// If there was no custom cwd or it was relative with no workspace
	if (!cwd) {
145
		cwd = root ? root.fsPath : os.homedir();
146 147 148 149 150 151 152 153 154 155 156 157
	}

	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;
}
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

/**
 * Adds quotes to a path if it contains whitespaces
 */
export function preparePathForTerminal(path: string): string {
	if (platform.isWindows) {
		if (/\s+/.test(path)) {
			return `"${path}"`;
		}
		return path;
	}
	path = path.replace(/(%5C|\\)/g, '\\\\');
	const charsToEscape = [
		' ', '\'', '"', '?', ':', ';', '!', '*', '(', ')', '{', '}', '[', ']'
	];
	for (let i = 0; i < path.length; i++) {
		const indexOfChar = charsToEscape.indexOf(path.charAt(i));
		if (indexOfChar >= 0) {
			path = `${path.substring(0, i)}\\${path.charAt(i)}${path.substring(i + 1)}`;
			i++; // Skip char due to escape char being added
		}
	}
	return path;
}