main.ts 10.1 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 nls = require('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';
E
Erich Gamma 已提交
14
import errors = require('vs/base/common/errors');
15
import comparer = require('vs/base/common/comparers');
E
Erich Gamma 已提交
16 17 18 19
import platform = require('vs/base/common/platform');
import paths = require('vs/base/common/paths');
import uri from 'vs/base/common/uri';
import strings = require('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';
E
Erich Gamma 已提交
26
import gracefulFs = require('graceful-fs');
B
Benjamin Pasero 已提交
27 28
import { IInitData } from 'vs/workbench/services/timer/common/timerService';
import { TimerService } from 'vs/workbench/services/timer/node/timerService';
29
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
B
Benjamin Pasero 已提交
30
import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/common/windows';
31
import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc';
B
Benjamin Pasero 已提交
32 33 34
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { StorageService, inMemoryLocalStorageInstance } from 'vs/platform/storage/common/storageService';
35
import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser';
36
import { webFrame } from 'electron';
37 38 39 40 41 42
import { UpdateChannelClient } from 'vs/platform/update/common/updateIpc';
import { IUpdateService } from 'vs/platform/update/common/update';
import { URLChannelClient } from 'vs/platform/url/common/urlIpc';
import { IURLService } from 'vs/platform/url/common/url';
import { WorkspacesChannelClient } from 'vs/platform/workspaces/common/workspacesIpc';
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
43
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
44

B
Benjamin Pasero 已提交
45
import fs = require('fs');
J
Joao Moreno 已提交
46
import { ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
B
Benjamin Pasero 已提交
47
gracefulFs.gracefulify(fs); // enable gracefulFs
E
Erich Gamma 已提交
48

49
export function startup(configuration: IWindowConfiguration): TPromise<void> {
50

51 52
	// Ensure others can listen to zoom level changes
	browser.setZoomFactor(webFrame.getZoomFactor());
53

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

58 59
	browser.setFullscreen(!!configuration.fullscreen);

60
	KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged();
A
Alex Dima 已提交
61

62
	browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
63

64 65 66
	// Setup Intl
	comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));

67
	// Open workbench
B
Benjamin Pasero 已提交
68
	return openWorkbench(configuration);
E
Erich Gamma 已提交
69 70
}

B
Benjamin Pasero 已提交
71
function openWorkbench(configuration: IWindowConfiguration): TPromise<void> {
72 73
	const mainProcessClient = new ElectronIPCClient(String(`window${configuration.windowId}`));
	const mainServices = createMainProcessServices(mainProcessClient, configuration);
74

75
	const environmentService = new EnvironmentService(configuration, configuration.execPath);
76
	const spdlogService = createSpdLogService(`renderer${configuration.windowId}`, environmentService);
J
Joao Moreno 已提交
77 78
	const consoleLogService = new ConsoleLogService(environmentService);
	const logService = new MultiplexLogService([consoleLogService, spdlogService]);
J
Joao Moreno 已提交
79

J
Joao Moreno 已提交
80
	logService.trace('openWorkbench configuration', JSON.stringify(configuration));
81 82 83

	// 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
S
Sandeep Somavarapu 已提交
84
	return createAndInitializeWorkspaceService(configuration, environmentService).then(workspaceService => {
85

86
		const timerService = new TimerService((<any>window).MonacoEnvironment.timers as IInitData, workspaceService.getWorkbenchState() === WorkbenchState.EMPTY);
87
		const storageService = createStorageService(workspaceService, environmentService);
88 89 90 91

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

			// Open Shell
92
			perf.mark('willStartWorkbench');
93 94 95 96
			const shell = new WorkbenchShell(document.body, {
				contextService: workspaceService,
				configurationService: workspaceService,
				environmentService,
J
Joao Moreno 已提交
97
				logService,
98 99
				timerService,
				storageService
B
Benjamin Pasero 已提交
100
			}, mainServices, configuration);
101 102 103 104 105 106 107
			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));
108
					}
109
				}
110 111 112 113 114
			});
		});
	});
}

S
Sandeep Somavarapu 已提交
115
function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): TPromise<WorkspaceService> {
116
	return validateSingleFolderPath(configuration).then(() => {
S
Sandeep Somavarapu 已提交
117
		const workspaceService = new WorkspaceService(environmentService);
118

119
		return workspaceService.initialize(configuration.workspace || configuration.folderPath || configuration).then(() => workspaceService, error => workspaceService);
120
	});
121 122
}

123 124 125
function validateSingleFolderPath(configuration: IWindowConfiguration): TPromise<void> {

	// Return early if we do not have a single folder path
126
	if (!configuration.folderPath) {
127
		return TPromise.as(void 0);
E
Erich Gamma 已提交
128 129
	}

130
	// Otherwise: use realpath to resolve symbolic links to the truth
131
	return realpath(configuration.folderPath).then(realFolderPath => {
132

133
		// For some weird reason, node adds a trailing slash to UNC paths
E
Erich Gamma 已提交
134 135 136
		// we never ever want trailing slashes as our workspace path unless
		// someone opens root ("/").
		// See also https://github.com/nodejs/io.js/issues/1765
137 138
		if (paths.isUNC(realFolderPath) && strings.endsWith(realFolderPath, paths.nativeSep)) {
			realFolderPath = strings.rtrim(realFolderPath, paths.nativeSep);
139
		}
E
Erich Gamma 已提交
140

141
		return realFolderPath;
142
	}, error => {
143 144 145 146 147 148 149 150
		if (configuration.verbose) {
			errors.onUnexpectedError(error);
		}

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

	}).then(realFolderPathOrNull => {
151

152 153
		// Update config with real path if we have one
		configuration.folderPath = realFolderPathOrNull;
154
	});
E
Erich Gamma 已提交
155 156
}

157
function createStorageService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService {
158 159 160
	let workspaceId: string;
	let secondaryWorkspaceId: number;

161
	switch (workspaceService.getWorkbenchState()) {
162 163

		// in multi root workspace mode we use the provided ID as key for workspace storage
164
		case WorkbenchState.WORKSPACE:
165
			workspaceId = uri.from({ path: workspaceService.getWorkspace().id, scheme: 'root' }).toString();
166 167 168 169
			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
170
		case WorkbenchState.FOLDER:
171
			const workspace: Workspace = <Workspace>workspaceService.getWorkspace();
172
			workspaceId = workspace.folders[0].uri.toString();
173 174 175 176 177 178 179 180 181
			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.
182
		case WorkbenchState.EMPTY:
183
			workspaceId = workspaceService.getWorkspace().id;
184
			break;
185 186
	}

187 188
	const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests!
	const storage = disableStorage ? inMemoryLocalStorageInstance : window.localStorage;
189

190
	return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
191 192
}

193
function createMainProcessServices(mainProcessClient: ElectronIPCClient, configuration: IWindowConfiguration): ServiceCollection {
194 195 196 197 198 199 200 201 202
	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));

	const urlChannel = mainProcessClient.getChannel('url');
203
	serviceCollection.set(IURLService, new SyncDescriptor(URLChannelClient, urlChannel, configuration.windowId));
204 205

	const workspacesChannel = mainProcessClient.getChannel('workspaces');
206
	serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel));
207 208 209 210

	return serviceCollection;
}

211 212 213 214 215 216
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)));
217
}