terminalEnvironment.ts 4.9 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 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
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) {
D
Daniel Imms 已提交
28
		for (const configKey in other) {
29
			let actualKey = configKey;
D
Daniel Imms 已提交
30
			for (const envKey in parent) {
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
				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];
	}
}

D
wip  
Daniel Imms 已提交
55 56
export function createTerminalEnv(shell: IShellLaunchConfig, locale: string): IStringDictionary<string> {
	const env = { ...process.env };
57 58 59 60 61 62 63
	if (shell.env) {
		mergeEnvironments(env, shell.env);
	}

	env['TERM_PROGRAM'] = 'vscode';
	env['TERM_PROGRAM_VERSION'] = pkg.version;
	env['LANG'] = _getLangEnvVariable(locale);
D
wip  
Daniel Imms 已提交
64

65 66 67 68 69 70 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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	return env;
}

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) {
D
Daniel Imms 已提交
122
			if (paths.isAbsolute(customCwd)) {
123 124
				cwd = customCwd;
			} else if (root) {
125
				cwd = paths.normalize(paths.join(root.fsPath, customCwd));
126 127 128 129 130 131
			}
		}
	}

	// If there was no custom cwd or it was relative with no workspace
	if (!cwd) {
132
		cwd = root ? root.fsPath : os.homedir();
133 134 135 136 137 138 139 140 141 142 143 144
	}

	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;
}
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

/**
 * 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;
}