main.ts 12.2 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

8
import * as nls from 'vs/nls';
9
import * as perf from 'vs/base/common/performance';
J
Johannes Rieken 已提交
10 11
import { TPromise } from 'vs/base/common/winjs.base';
import { WorkbenchShell } from 'vs/workbench/electron-browser/shell';
12
import * as browser from 'vs/base/browser/browser';
J
Johannes Rieken 已提交
13
import { domContentLoaded } from 'vs/base/browser/dom';
14 15 16 17
import * as errors from 'vs/base/common/errors';
import * as comparer from 'vs/base/common/comparers';
import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
M
Martin Aeschlimann 已提交
18
import uri from 'vs/base/common/uri';
19
import * as strings from 'vs/base/common/strings';
20
import { IWorkspaceContextService, Workspace, WorkbenchState } from 'vs/platform/workspace/common/workspace';
21
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
22 23
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
24
import { realpath } from 'vs/base/node/pfs';
J
Johannes Rieken 已提交
25
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
26
import * as gracefulFs from 'graceful-fs';
27
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
B
Benjamin Pasero 已提交
28
import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows';
J
Joao Moreno 已提交
29
import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc';
B
Benjamin Pasero 已提交
30 31
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
32
import { StorageService, inMemoryLocalStorageInstance, IStorage } from 'vs/platform/storage/common/storageService';
33
import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser';
34
import { webFrame } from 'electron';
J
Joao Moreno 已提交
35
import { UpdateChannelClient } from 'vs/platform/update/node/updateIpc';
36
import { IUpdateService } from 'vs/platform/update/common/update';
J
Joao Moreno 已提交
37
import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/node/urlIpc';
38
import { IURLService } from 'vs/platform/url/common/url';
J
Joao Moreno 已提交
39
import { WorkspacesChannelClient } from 'vs/platform/workspaces/node/workspacesIpc';
40
import { IWorkspacesService, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
41
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
42
import * as fs from 'fs';
S
Sandeep Somavarapu 已提交
43
import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
J
Joao Moreno 已提交
44
import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc';
45
import { IIssueService } from 'vs/platform/issue/common/issue';
J
Joao Moreno 已提交
46
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc';
J
Joao Moreno 已提交
47
import { RelayURLService } from 'vs/platform/url/common/urlService';
J
Joao Moreno 已提交
48
import { MenubarChannelClient } from 'vs/platform/menubar/node/menubarIpc';
49
import { IMenubarService } from 'vs/platform/menubar/common/menubar';
50
import { Schemas } from 'vs/base/common/network';
51
import { IUriLabelService, UriLabelService } from 'vs/platform/uriLabel/common/uriLabel';
J
Joao Moreno 已提交
52

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

55
export function startup(configuration: IWindowConfiguration): TPromise<void> {
56

M
Martin Aeschlimann 已提交
57 58
	revive(configuration);

59 60
	perf.importEntries(configuration.perfEntries);

61 62
	// Ensure others can listen to zoom level changes
	browser.setZoomFactor(webFrame.getZoomFactor());
63

64 65
	// See https://github.com/Microsoft/vscode/issues/26151
	// Can be trusted because we are not setting it ourselves.
66
	browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */);
67

68 69
	browser.setFullscreen(!!configuration.fullscreen);

70
	KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
A
Alex Dima 已提交
71

72
	browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
73

74 75 76
	// Setup Intl
	comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));

77
	// Open workbench
B
Benjamin Pasero 已提交
78
	return openWorkbench(configuration);
E
Erich Gamma 已提交
79 80
}

M
Martin Aeschlimann 已提交
81 82 83 84
function revive(workbench: IWindowConfiguration) {
	if (workbench.folderUri) {
		workbench.folderUri = uri.revive(workbench.folderUri);
	}
M
Martin Aeschlimann 已提交
85 86 87 88 89 90 91 92
	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 已提交
93
		}
M
Martin Aeschlimann 已提交
94
	});
M
Martin Aeschlimann 已提交
95 96
}

B
Benjamin Pasero 已提交
97
function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
J
Joao Moreno 已提交
98
	const mainProcessClient = new ElectronIPCClient(`window:${configuration.windowId}`);
99
	const mainServices = createMainProcessServices(mainProcessClient, configuration);
100

101
	const environmentService = new EnvironmentService(configuration, configuration.execPath);
102
	const uriLabelService = new UriLabelService(environmentService);
S
Sandeep Somavarapu 已提交
103
	const logService = createLogService(mainProcessClient, configuration, environmentService);
J
Joao Moreno 已提交
104
	logService.trace('openWorkbench configuration', JSON.stringify(configuration));
105 106 107

	// Since the configuration service is one of the core services that is used in so many places, we initialize it
	// right before startup of the workbench shell to have its data ready for consumers
108
	return createAndInitializeWorkspaceService(configuration, environmentService, uriLabelService).then(workspaceService => {
109
		const storageService = createStorageService(workspaceService, environmentService);
110
		uriLabelService.acquireContextService(workspaceService);
111 112 113 114

		return domContentLoaded().then(() => {

			// Open Shell
115
			perf.mark('willStartWorkbench');
116 117 118 119
			const shell = new WorkbenchShell(document.body, {
				contextService: workspaceService,
				configurationService: workspaceService,
				environmentService,
J
Joao Moreno 已提交
120
				logService,
121 122
				storageService,
				uriLabelService
J
Joao Moreno 已提交
123
			}, mainServices, mainProcessClient, configuration);
124 125 126 127 128 129 130
			shell.open();

			// Inform user about loading issues from the loader
			(<any>self).require.config({
				onError: (err: any) => {
					if (err.errorCode === 'load') {
						shell.onUnexpectedError(loaderError(err));
131
					}
132
				}
133 134 135 136 137
			});
		});
	});
}

138
function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService, uriLabelService: IUriLabelService): TPromise<WorkspaceService> {
M
Martin Aeschlimann 已提交
139
	return validateFolderUri(configuration.folderUri, configuration.verbose).then(validatedFolderUri => {
140

141
		const workspaceService = new WorkspaceService(environmentService, uriLabelService);
142

143
		return workspaceService.initialize(configuration.workspace || validatedFolderUri || configuration).then(() => workspaceService, error => workspaceService);
144
	});
145 146
}

147
function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): TPromise<uri> {
148

149 150 151
	// Return early if we do not have a single folder uri or if it is a non file uri
	if (!folderUri || folderUri.scheme !== Schemas.file) {
		return TPromise.as(folderUri);
E
Erich Gamma 已提交
152 153
	}

154
	// Otherwise: use realpath to resolve symbolic links to the truth
155
	return realpath(folderUri.fsPath).then(realFolderPath => {
156

157
		// For some weird reason, node adds a trailing slash to UNC paths
E
Erich Gamma 已提交
158 159 160
		// we never ever want trailing slashes as our workspace path unless
		// someone opens root ("/").
		// See also https://github.com/nodejs/io.js/issues/1765
161 162
		if (paths.isUNC(realFolderPath) && strings.endsWith(realFolderPath, paths.nativeSep)) {
			realFolderPath = strings.rtrim(realFolderPath, paths.nativeSep);
163
		}
E
Erich Gamma 已提交
164

165
		return uri.file(realFolderPath);
166
	}, error => {
167
		if (verbose) {
168 169 170 171 172 173
			errors.onUnexpectedError(error);
		}

		// Treat any error case as empty workbench case (no folder path)
		return null;

174
	}).then(realFolderUriOrNull => {
175

176
		// Update config with real path if we have one
177
		return realFolderUriOrNull;
178
	});
E
Erich Gamma 已提交
179 180
}

181
function createStorageService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService {
182 183 184
	let workspaceId: string;
	let secondaryWorkspaceId: number;

185
	switch (workspaceService.getWorkbenchState()) {
186 187

		// in multi root workspace mode we use the provided ID as key for workspace storage
188
		case WorkbenchState.WORKSPACE:
189
			workspaceId = uri.from({ path: workspaceService.getWorkspace().id, scheme: 'root' }).toString();
190 191 192 193
			break;

		// in single folder mode we use the path of the opened folder as key for workspace storage
		// the ctime is used as secondary workspace id to clean up stale UI state if necessary
194
		case WorkbenchState.FOLDER:
195
			const workspace: Workspace = <Workspace>workspaceService.getWorkspace();
196
			workspaceId = workspace.folders[0].uri.toString();
197 198 199 200 201 202 203 204 205
			secondaryWorkspaceId = workspace.ctime;
			break;

		// finaly, if we do not have a workspace open, we need to find another identifier for the window to store
		// workspace UI state. if we have a backup path in the configuration we can use that because this
		// will be a unique identifier per window that is stable between restarts as long as there are
		// dirty files in the workspace.
		// We use basename() to produce a short identifier, we do not need the full path. We use a custom
		// scheme so that we can later distinguish these identifiers from the workspace one.
206
		case WorkbenchState.EMPTY:
207
			workspaceId = workspaceService.getWorkspace().id;
208
			break;
209 210
	}

211
	const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests!
212 213 214 215 216 217 218

	let storage: IStorage;
	if (disableStorage) {
		storage = inMemoryLocalStorageInstance;
	} else {
		storage = window.localStorage;
	}
219

220
	return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
221 222
}

S
Sandeep Somavarapu 已提交
223
function createLogService(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration, environmentService: IEnvironmentService): ILogService {
S
Sandeep Somavarapu 已提交
224 225
	const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, configuration.logLevel, environmentService.logsPath);
	const consoleLogService = new ConsoleLogService(configuration.logLevel);
S
Sandeep Somavarapu 已提交
226
	const logService = new MultiplexLogService([consoleLogService, spdlogService]);
227
	const logLevelClient = new LogLevelSetterChannelClient(mainProcessClient.getChannel('loglevel'));
S
Sandeep Somavarapu 已提交
228 229 230
	return new FollowerLogService(logLevelClient, logService);
}

231
function createMainProcessServices(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration): ServiceCollection {
232 233 234 235 236 237 238 239
	const serviceCollection = new ServiceCollection();

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

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

J
Joao Moreno 已提交
240 241 242 243
	const urlChannel = mainProcessClient.getChannel('url');
	const mainUrlService = new URLServiceChannelClient(urlChannel);
	const urlService = new RelayURLService(mainUrlService);
	serviceCollection.set(IURLService, urlService);
J
Joao Moreno 已提交
244

J
Joao Moreno 已提交
245
	const urlHandlerChannel = new URLHandlerChannel(urlService);
J
Joao Moreno 已提交
246
	mainProcessClient.registerChannel('urlHandler', urlHandlerChannel);
247

248 249 250
	const issueChannel = mainProcessClient.getChannel('issue');
	serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, issueChannel));

251 252 253
	const menubarChannel = mainProcessClient.getChannel('menubar');
	serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, menubarChannel));

254
	const workspacesChannel = mainProcessClient.getChannel('workspaces');
255
	serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel));
256 257 258 259

	return serviceCollection;
}

260 261 262 263 264 265
function loaderError(err: Error): Error {
	if (platform.isWeb) {
		return new Error(nls.localize('loaderError', "Failed to load a required file. Either you are no longer connected to the internet or the server you are connected to is offline. Please refresh the browser to try again."));
	}

	return new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)));
266
}