main.ts 12.9 KB
Newer Older
E
Erich Gamma 已提交
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 nls from 'vs/nls';
7
import * as perf from 'vs/base/common/performance';
J
Johannes Rieken 已提交
8
import { WorkbenchShell } from 'vs/workbench/electron-browser/shell';
9
import * as browser from 'vs/base/browser/browser';
J
Johannes Rieken 已提交
10
import { domContentLoaded } from 'vs/base/browser/dom';
11
import { onUnexpectedError } from 'vs/base/common/errors';
12 13
import * as comparer from 'vs/base/common/comparers';
import * as platform from 'vs/base/common/platform';
14
import { URI as uri } from 'vs/base/common/uri';
15
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
16 17
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
18
import { stat } from 'vs/base/node/pfs';
J
Johannes Rieken 已提交
19
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
20
import * as gracefulFs from 'graceful-fs';
21
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
B
Benjamin Pasero 已提交
22
import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows';
J
Joao Moreno 已提交
23
import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc';
B
Benjamin Pasero 已提交
24
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
25
import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser';
26
import { webFrame } from 'electron';
J
Joao Moreno 已提交
27
import { UpdateChannelClient } from 'vs/platform/update/node/updateIpc';
28
import { IUpdateService } from 'vs/platform/update/common/update';
J
Joao Moreno 已提交
29
import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/node/urlIpc';
30
import { IURLService } from 'vs/platform/url/common/url';
J
Joao Moreno 已提交
31
import { WorkspacesChannelClient } from 'vs/platform/workspaces/node/workspacesIpc';
32
import { IWorkspacesService, ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, IMultiFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
33
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
34
import * as fs from 'fs';
S
Sandeep Somavarapu 已提交
35
import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
36
import { StorageService } from 'vs/platform/storage/node/storageService';
J
Joao Moreno 已提交
37
import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc';
38
import { IIssueService } from 'vs/platform/issue/common/issue';
J
Joao Moreno 已提交
39
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc';
J
Joao Moreno 已提交
40
import { RelayURLService } from 'vs/platform/url/common/urlService';
J
Joao Moreno 已提交
41
import { MenubarChannelClient } from 'vs/platform/menubar/node/menubarIpc';
42
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
43
import { Schemas } from 'vs/base/common/network';
44
import { sanitizeFilePath } from 'vs/base/node/extfs';
45
import { basename } from 'path';
46
import { createHash } from 'crypto';
47
import { IdleValue } from 'vs/base/common/async';
48
import { setGlobalLeakWarningThreshold } from 'vs/base/common/event';
B
wip  
Benjamin Pasero 已提交
49
import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc';
J
Joao Moreno 已提交
50

B
Benjamin Pasero 已提交
51
gracefulFs.gracefulify(fs); // enable gracefulFs
E
Erich Gamma 已提交
52

53
export function startup(configuration: IWindowConfiguration): Promise<void> {
54

55
	// Massage configuration file URIs
M
Martin Aeschlimann 已提交
56 57
	revive(configuration);

58
	// Setup perf
59 60
	perf.importEntries(configuration.perfEntries);

61
	// Configure emitter leak warning threshold
62 63
	setGlobalLeakWarningThreshold(-1);

64

65 66 67
	// Browser config
	browser.setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes
	browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151)
68
	browser.setFullscreen(!!configuration.fullscreen);
69
	browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
70

71
	// Keyboard support
72
	KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
A
Alex Dima 已提交
73

74
	// Setup Intl for comparers
75 76 77 78 79 80 81
	comparer.setFileNameComparer(new IdleValue(() => {
		const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
		return {
			collator: collator,
			collatorIsNumeric: collator.resolvedOptions().numeric
		};
	}));
82

83
	// Open workbench
B
Benjamin Pasero 已提交
84
	return openWorkbench(configuration);
E
Erich Gamma 已提交
85 86
}

M
Martin Aeschlimann 已提交
87 88 89 90
function revive(workbench: IWindowConfiguration) {
	if (workbench.folderUri) {
		workbench.folderUri = uri.revive(workbench.folderUri);
	}
91

M
Martin Aeschlimann 已提交
92 93 94 95 96 97 98 99
	const filesToWaitPaths = workbench.filesToWait && workbench.filesToWait.paths;
	[filesToWaitPaths, workbench.filesToOpen, workbench.filesToCreate, workbench.filesToDiff].forEach(paths => {
		if (Array.isArray(paths)) {
			paths.forEach(path => {
				if (path.fileUri) {
					path.fileUri = uri.revive(path.fileUri);
				}
			});
M
Martin Aeschlimann 已提交
100
		}
M
Martin Aeschlimann 已提交
101
	});
M
Martin Aeschlimann 已提交
102 103
}

104
function openWorkbench(configuration: IWindowConfiguration): Promise<void> {
J
Joao Moreno 已提交
105
	const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`);
B
wip  
Benjamin Pasero 已提交
106
	const mainServices = createMainProcessServices(mainProcessClient);
107

108
	const environmentService = new EnvironmentService(configuration, configuration.execPath);
109

S
Sandeep Somavarapu 已提交
110
	const logService = createLogService(mainProcessClient, configuration, environmentService);
J
Joao Moreno 已提交
111
	logService.trace('openWorkbench configuration', JSON.stringify(configuration));
112

113 114 115
	// Resolve a workspace payload that we can get the workspace ID from
	return createWorkspaceInitializationPayload(configuration, environmentService).then(payload => {

B
Benjamin Pasero 已提交
116 117 118 119 120 121
		return Promise.all([

			// Create and initialize workspace/configuration service
			createWorkspaceService(payload, environmentService, logService),

			// Create and initialize storage service
B
wip  
Benjamin Pasero 已提交
122
			createStorageService(payload, environmentService, logService, mainProcessClient)
B
Benjamin Pasero 已提交
123 124
		]).then(services => {
			const workspaceService = services[0];
125
			const storageService = services[1];
B
Benjamin Pasero 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139

			return domContentLoaded().then(() => {
				perf.mark('willStartWorkbench');

				// Create Shell
				const shell = new WorkbenchShell(document.body, {
					contextService: workspaceService,
					configurationService: workspaceService,
					environmentService,
					logService,
					storageService
				}, mainServices, mainProcessClient, configuration);

				// Gracefully Shutdown Storage
140
				shell.onWillShutdown(event => {
B
Benjamin Pasero 已提交
141 142 143 144 145 146 147 148 149 150 151
					event.join(storageService.close());
				});

				// Open Shell
				shell.open();

				// Inform user about loading issues from the loader
				(<any>self).require.config({
					onError: err => {
						if (err.errorCode === 'load') {
							shell.onUnexpectedError(new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err))));
152
						}
B
Benjamin Pasero 已提交
153
					}
154
				});
155 156 157 158 159
			});
		});
	});
}

160
function createWorkspaceInitializationPayload(configuration: IWindowConfiguration, environmentService: EnvironmentService): Promise<IWorkspaceInitializationPayload> {
161 162 163

	// Multi-root workspace
	if (configuration.workspace) {
164
		return Promise.resolve(configuration.workspace as IMultiFolderWorkspaceInitializationPayload);
165 166 167
	}

	// Single-folder workspace
168 169
	let workspaceInitializationPayload: Promise<IWorkspaceInitializationPayload> = Promise.resolve(void 0);
	if (configuration.folderUri) {
170
		workspaceInitializationPayload = resolveSingleFolderWorkspaceInitializationPayload(configuration.folderUri);
171 172 173 174
	}

	return workspaceInitializationPayload.then(payload => {

175
		// Fallback to empty workspace if we have no payload yet.
176
		if (!payload) {
177 178 179
			let id: string;
			if (configuration.backupPath) {
				id = basename(configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID
180
			} else if (environmentService.isExtensionDevelopment) {
181
				id = 'ext-dev'; // extension development window never stores backups and is a singleton
182
			} else {
183
				return Promise.reject(new Error('Unexpected window configuration without backupPath'));
184 185
			}

186
			payload = { id } as IEmptyWorkspaceInitializationPayload;
187 188
		}

189
		return payload;
190
	});
191 192
}

193
function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise<ISingleFolderWorkspaceInitializationPayload> {
194

195 196 197 198
	// Return early the folder is not local
	if (folderUri.scheme !== Schemas.file) {
		return Promise.resolve({ id: createHash('md5').update(folderUri.toString()).digest('hex'), folder: folderUri });
	}
199

200 201 202 203 204 205 206 207 208 209 210 211
	function computeLocalDiskFolderId(folder: uri, stat: fs.Stats): string {
		let ctime: number;
		if (platform.isLinux) {
			ctime = stat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead!
		} else if (platform.isMacintosh) {
			ctime = stat.birthtime.getTime(); // macOS: birthtime is fine to use as is
		} else if (platform.isWindows) {
			if (typeof stat.birthtimeMs === 'number') {
				ctime = Math.floor(stat.birthtimeMs); // Windows: fix precision issue in node.js 8.x to get 7.x results (see https://github.com/nodejs/node/issues/19897)
			} else {
				ctime = stat.birthtime.getTime();
			}
212 213
		}

214 215 216
		// we use the ctime as extra salt to the ID so that we catch the case of a folder getting
		// deleted and recreated. in that case we do not want to carry over previous state
		return createHash('md5').update(folder.fsPath).update(ctime ? String(ctime) : '').digest('hex');
E
Erich Gamma 已提交
217 218
	}

219
	// For local: ensure path is absolute and exists
220
	const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd());
221
	return stat(sanitizedFolderPath).then(stat => {
222
		const sanitizedFolderUri = uri.file(sanitizedFolderPath);
223
		return {
224
			id: computeLocalDiskFolderId(sanitizedFolderUri, stat),
225
			folder: sanitizedFolderUri
226
		} as ISingleFolderWorkspaceInitializationPayload;
227
	}, error => onUnexpectedError(error));
E
Erich Gamma 已提交
228 229
}

230
function createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Promise<WorkspaceService> {
231 232
	const workspaceService = new WorkspaceService(environmentService);

233
	return workspaceService.initialize(payload).then(() => workspaceService, error => {
234 235
		onUnexpectedError(error);
		logService.error(error);
236 237 238

		return workspaceService;
	});
239 240
}

B
wip  
Benjamin Pasero 已提交
241
function createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService, mainProcessClient: ElectronIPCClient): Thenable<StorageService> {
242 243
	const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessClient.getChannel('storage'));
	const storageService = new StorageService(globalStorageDatabase, logService, environmentService);
244

245 246 247
	return storageService.initialize(payload).then(() => storageService, error => {
		onUnexpectedError(error);
		logService.error(error);
248

249
		return storageService;
250
	});
251 252
}

S
Sandeep Somavarapu 已提交
253
function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService {
S
Sandeep Somavarapu 已提交
254 255
	const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, configuration.logLevel, environmentService.logsPath);
	const consoleLogService = new ConsoleLogService(configuration.logLevel);
S
Sandeep Somavarapu 已提交
256
	const logService = new MultiplexLogService([consoleLogService, spdlogService]);
257
	const logLevelClient = new LogLevelSetterChannelClient(mainProcessClient.getChannel('loglevel'));
258

S
Sandeep Somavarapu 已提交
259 260 261
	return new FollowerLogService(logLevelClient, logService);
}

B
wip  
Benjamin Pasero 已提交
262
function createMainProcessServices(mainProcessClient: ElectronIPCClient): ServiceCollection {
263 264 265 266 267 268
	const serviceCollection = new ServiceCollection();

	const windowsChannel = mainProcessClient.getChannel('windows');
	serviceCollection.set(IWindowsService, new WindowsChannelClient(windowsChannel));

	const updateChannel = mainProcessClient.getChannel('update');
269
	serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, [updateChannel]));
270

J
Joao Moreno 已提交
271 272 273 274
	const urlChannel = mainProcessClient.getChannel('url');
	const mainUrlService = new URLServiceChannelClient(urlChannel);
	const urlService = new RelayURLService(mainUrlService);
	serviceCollection.set(IURLService, urlService);
J
Joao Moreno 已提交
275

J
Joao Moreno 已提交
276
	const urlHandlerChannel = new URLHandlerChannel(urlService);
J
Joao Moreno 已提交
277
	mainProcessClient.registerChannel('urlHandler', urlHandlerChannel);
278

279
	const issueChannel = mainProcessClient.getChannel('issue');
280
	serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, [issueChannel]));
281

282
	const menubarChannel = mainProcessClient.getChannel('menubar');
283
	serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, [menubarChannel]));
284

285
	const workspacesChannel = mainProcessClient.getChannel('workspaces');
286
	serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel));
287 288

	return serviceCollection;
289
}