main.js 8.6 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
5 6

//@ts-check
7 8
'use strict';

J
Joao Moreno 已提交
9
const perf = require('./vs/base/common/performance');
10 11
const lp = require('./vs/base/node/languagePacks');

12 13
perf.mark('main:started');

J
Joao Moreno 已提交
14
const path = require('path');
15 16
const bootstrap = require('./bootstrap');
const paths = require('./paths');
17 18 19
// @ts-ignore
const product = require('../product.json');
// @ts-ignore
20
const app = require('electron').app;
J
Joao Moreno 已提交
21

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
// Enable portable support
const portable = bootstrap.configurePortable();

// Enable ASAR support
bootstrap.enableASARSupport();

// Set userData path before app 'ready' event and call to process.chdir
const args = parseCLIArgs();
const userDataPath = getUserDataPath(args);
app.setPath('userData', userDataPath);

// Update cwd based on environment and platform
setCurrentWorkingDirectory();

// Global app listeners
registerListeners();

39 40 41 42 43
/**
 * Support user defined locale
 *
 * @type {Promise}
 */
44
let nlsConfiguration = undefined;
45
const userDefinedLocale = getUserDefinedLocale();
46 47
const metaDataFile = path.join(__dirname, 'nls.metadata.json');

48
userDefinedLocale.then(locale => {
49
	if (locale && !nlsConfiguration) {
50
		nlsConfiguration = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
51 52 53
	}
});

54
// Cached data
55
const nodeCachedDataDir = getNodeCachedDir();
56 57 58

// Configure command line switches
configureCommandlineSwitches(args);
59 60 61

// Load our code once ready
app.once('ready', function () {
B
Benjamin Pasero 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
	if (args['trace']) {
		// @ts-ignore
		const contentTracing = require('electron').contentTracing;

		const traceOptions = {
			categoryFilter: args['trace-category-filter'] || '*',
			traceOptions: args['trace-options'] || 'record-until-full,enable-sampling'
		};

		contentTracing.startRecording(traceOptions, () => onReady());
	} else {
		onReady();
	}
});

function onReady() {
78 79 80 81
	perf.mark('main:appReady');

	Promise.all([nodeCachedDataDir.ensureExists(), userDefinedLocale]).then(([cachedDataDir, locale]) => {
		if (locale && !nlsConfiguration) {
82
			nlsConfiguration = lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale);
83 84 85 86 87 88
		}

		if (!nlsConfiguration) {
			nlsConfiguration = Promise.resolve(undefined);
		}

H
HYEWON HWANG 已提交
89
		// First, we need to test a user defined locale. If it fails we try the app locale.
90
		// If that fails we fall back to English.
91
		nlsConfiguration.then(nlsConfig => {
92 93 94 95

			const startup = nlsConfig => {
				nlsConfig._languagePackSupport = true;
				process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig);
96
				process.env['VSCODE_NODE_CACHED_DATA_DIR'] = cachedDataDir || '';
97

98
				// Load main in AMD
99 100 101 102
				perf.mark('willLoadMainBundle');
				require('./bootstrap-amd').load('vs/code/electron-main/main', () => {
					perf.mark('didLoadMainBundle');
				});
103 104
			};

105
			// We received a valid nlsConfig from a user defined locale
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
			if (nlsConfig) {
				startup(nlsConfig);
			}

			// Try to use the app locale. Please note that the app locale is only
			// valid after we have received the app ready event. This is why the
			// code is here.
			else {
				let appLocale = app.getLocale();
				if (!appLocale) {
					startup({ locale: 'en', availableLanguages: {} });
				} else {

					// See above the comment about the loader and case sensitiviness
					appLocale = appLocale.toLowerCase();

122
					lp.getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale).then(nlsConfig => {
123 124 125 126 127 128 129 130 131 132
						if (!nlsConfig) {
							nlsConfig = { locale: appLocale, availableLanguages: {} };
						}

						startup(nlsConfig);
					});
				}
			}
		});
	}, console.error);
B
Benjamin Pasero 已提交
133
}
134

135
/**
136
 * @typedef	 {{ [arg: string]: any; '--'?: string[]; _: string[]; }} ParsedArgs
137 138 139
 *
 * @param {ParsedArgs} cliArgs
 */
140
function configureCommandlineSwitches(cliArgs) {
141 142

	// Force pre-Chrome-60 color profile handling (for https://github.com/Microsoft/vscode/issues/51791)
143
	app.commandLine.appendSwitch('disable-color-correct-rendering');
144 145

	// Support JS Flags
146
	const jsFlags = getJSFlags(cliArgs);
147 148
	if (jsFlags) {
		app.commandLine.appendSwitch('--js-flags', jsFlags);
J
Joao Moreno 已提交
149 150 151
	}
}

152
/**
153
 * @param {ParsedArgs} cliArgs
154 155
 * @returns {string}
 */
156 157
function getJSFlags(cliArgs) {
	const jsFlags = [];
158 159

	// Add any existing JS flags we already got from the command line
160 161
	if (cliArgs['js-flags']) {
		jsFlags.push(cliArgs['js-flags']);
162 163
	}

164
	// Support max-memory flag
165 166
	if (cliArgs['max-memory'] && !/max_old_space_size=(\d+)/g.exec(cliArgs['js-flags'])) {
		jsFlags.push(`--max_old_space_size=${cliArgs['max-memory']}`);
167
	}
168 169

	return jsFlags.length > 0 ? jsFlags.join(' ') : null;
170 171
}

172
/**
173 174
 * @param {ParsedArgs} cliArgs
 *
175 176
 * @returns {string}
 */
177 178 179 180
function getUserDataPath(cliArgs) {
	if (portable.isPortable) {
		return path.join(portable.portableDataPath, 'user-data');
	}
J
Joao Moreno 已提交
181

182
	return path.resolve(cliArgs['user-data-dir'] || paths.getDefaultUserDataPath(process.platform));
J
Joao Moreno 已提交
183 184
}

185 186 187
/**
 * @returns {ParsedArgs}
 */
188
function parseCLIArgs() {
189
	const minimist = require('vscode-minimist');
190

191 192 193 194 195 196 197 198
	return minimist(process.argv, {
		string: [
			'user-data-dir',
			'locale',
			'js-flags',
			'max-memory'
		]
	});
J
Joao Moreno 已提交
199 200
}

201 202 203
function setCurrentWorkingDirectory() {
	try {
		if (process.platform === 'win32') {
204
			process.env['VSCODE_CWD'] = process.cwd(); // remember as environment variable
205 206 207
			process.chdir(path.dirname(app.getPath('exe'))); // always set application folder as cwd
		} else if (process.env['VSCODE_CWD']) {
			process.chdir(process.env['VSCODE_CWD']);
A
Alex Dima 已提交
208
		}
209 210 211 212
	} catch (err) {
		console.error(err);
	}
}
A
Alex Dima 已提交
213

214
function registerListeners() {
A
Alex Dima 已提交
215

216 217 218 219 220 221
	/**
	 * Mac: when someone drops a file to the not-yet running VSCode, the open-file event fires even before
	 * the app-ready event. We listen very early for open-file and remember this upon startup as path to open.
	 *
	 * @type {string[]}
	 */
222 223
	const macOpenFiles = [];
	global['macOpenFiles'] = macOpenFiles;
224
	app.on('open-file', function (event, path) {
225
		macOpenFiles.push(path);
226
	});
227

228 229 230 231 232
	/**
	 * React to open-url requests.
	 *
	 * @type {string[]}
	 */
233 234 235
	const openUrls = [];
	const onOpenUrl = function (event, url) {
		event.preventDefault();
236

237 238
		openUrls.push(url);
	};
J
Joao Moreno 已提交
239

240 241 242
	app.on('will-finish-launching', function () {
		app.on('open-url', onOpenUrl);
	});
J
Joao Moreno 已提交
243

244
	global['getOpenUrls'] = function () {
245
		app.removeListener('open-url', onOpenUrl);
246

247 248
		return openUrls;
	};
249 250
}

251
/**
252
 * @returns {{ ensureExists: () => Promise<string | void> }}
253
 */
254
function getNodeCachedDir() {
255
	return new class {
256

257 258 259 260 261
		constructor() {
			this.value = this._compute();
		}

		ensureExists() {
262
			return bootstrap.mkdirp(this.value).then(() => this.value, () => { /*ignore*/ });
263 264 265 266 267 268
		}

		_compute() {
			if (process.argv.indexOf('--no-cached-data') > 0) {
				return undefined;
			}
269

270 271 272 273
			// IEnvironmentService.isBuilt
			if (process.env['VSCODE_DEV']) {
				return undefined;
			}
274

275
			// find commit id
276
			const commit = product.commit;
277 278 279
			if (!commit) {
				return undefined;
			}
280

281 282 283 284
			return path.join(userDataPath, 'CachedData', commit);
		}
	};
}
285

286
//#region NLS Support
287 288 289 290
/**
 * @param {string} content
 * @returns {string}
 */
291
function stripComments(content) {
A
Alex Dima 已提交
292
	const regexp = /("(?:[^\\"]*(?:\\.)?)*")|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
293 294

	return content.replace(regexp, function (match, m1, m2, m3, m4) {
295 296 297 298
		// Only one of m1, m2, m3, m4 matches
		if (m3) {
			// A block comment. Replace with nothing
			return '';
D
Dirk Baeumer 已提交
299
		} else if (m4) {
300
			// A line comment. If it ends in \r?\n then keep it.
301
			const length_1 = m4.length;
302 303 304 305 306 307
			if (length_1 > 2 && m4[length_1 - 1] === '\n') {
				return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
			}
			else {
				return '';
			}
D
Dirk Baeumer 已提交
308
		} else {
309 310 311 312
			// We match a string
			return match;
		}
	});
J
lint  
Joao Moreno 已提交
313
}
314

J
Joshua 已提交
315
// Language tags are case insensitive however an amd loader is case sensitive
D
Dirk Baeumer 已提交
316 317 318
// To make this work on case preserving & insensitive FS we do the following:
// the language bundles have lower case language tags and we always lower case
// the locale we receive from the user or OS.
319 320 321
/**
 * @returns {Promise<string>}
 */
D
Dirk Baeumer 已提交
322
function getUserDefinedLocale() {
323
	const locale = args['locale'];
D
Dirk Baeumer 已提交
324 325 326 327
	if (locale) {
		return Promise.resolve(locale.toLowerCase());
	}

328
	const localeConfig = path.join(userDataPath, 'User', 'locale.json');
329
	return bootstrap.readFile(localeConfig).then(content => {
330 331 332 333 334
		content = stripComments(content);
		try {
			const value = JSON.parse(content).locale;
			return value && typeof value === 'string' ? value.toLowerCase() : undefined;
		} catch (e) {
D
Dirk Baeumer 已提交
335 336
			return undefined;
		}
337 338
	}, () => {
		return undefined;
D
Dirk Baeumer 已提交
339 340
	});
}
341
//#endregion