main.ts 12.8 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';
B
Benjamin Pasero 已提交
8
import { Shell } 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
J
Johannes Rieken 已提交
62
	setGlobalLeakWarningThreshold(175);
63

64 65 66
	// 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)
67
	browser.setFullscreen(!!configuration.fullscreen);
68
	browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
69

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

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

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

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

M
Martin Aeschlimann 已提交
91 92 93 94 95 96 97 98
	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 已提交
99
		}
M
Martin Aeschlimann 已提交
100
	});
M
Martin Aeschlimann 已提交
101 102
}

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

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

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

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

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

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

			// Create and initialize storage service
B
wip  
Benjamin Pasero 已提交
121
			createStorageService(payload, environmentService, logService, mainProcessClient)
B
Benjamin Pasero 已提交
122 123
		]).then(services => {
			const workspaceService = services[0];
124
			const storageService = services[1];
B
Benjamin Pasero 已提交
125 126 127 128 129

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

				// Create Shell
B
Benjamin Pasero 已提交
130
				const shell = new Shell(document.body, {
B
Benjamin Pasero 已提交
131 132 133 134 135 136 137 138
					contextService: workspaceService,
					configurationService: workspaceService,
					environmentService,
					logService,
					storageService
				}, mainServices, mainProcessClient, configuration);

				// Gracefully Shutdown Storage
139
				shell.onWillShutdown(event => {
B
Benjamin Pasero 已提交
140 141 142 143 144 145 146 147 148 149 150
					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))));
151
						}
B
Benjamin Pasero 已提交
152
					}
153
				});
154 155 156 157 158
			});
		});
	});
}

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

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

	// Single-folder workspace
B
Benjamin Pasero 已提交
167
	let workspaceInitializationPayload: Promise<IWorkspaceInitializationPayload> = Promise.resolve();
168
	if (configuration.folderUri) {
169
		workspaceInitializationPayload = resolveSingleFolderWorkspaceInitializationPayload(configuration.folderUri);
170 171 172 173
	}

	return workspaceInitializationPayload.then(payload => {

174
		// Fallback to empty workspace if we have no payload yet.
175
		if (!payload) {
176 177 178
			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
179
			} else if (environmentService.isExtensionDevelopment) {
180
				id = 'ext-dev'; // extension development window never stores backups and is a singleton
181
			} else {
182
				return Promise.reject(new Error('Unexpected window configuration without backupPath'));
183 184
			}

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

188
		return payload;
189
	});
190 191
}

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

194 195 196 197
	// 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 });
	}
198

199 200 201 202 203 204 205 206 207 208 209 210
	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();
			}
211 212
		}

213 214 215
		// 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 已提交
216 217
	}

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

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

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

		return workspaceService;
	});
238 239
}

J
Johannes Rieken 已提交
240
function createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService, mainProcessClient: ElectronIPCClient): Promise<StorageService> {
241 242
	const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessClient.getChannel('storage'));
	const storageService = new StorageService(globalStorageDatabase, logService, environmentService);
243

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

248
		return storageService;
249
	});
250 251
}

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

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

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

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

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

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

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

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

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

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

	return serviceCollection;
288
}