main.ts 11.4 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');
J
Johannes Rieken 已提交
9 10 11
import { TPromise } from 'vs/base/common/winjs.base';
import { WorkbenchShell } from 'vs/workbench/electron-browser/shell';
import { IOptions } from 'vs/workbench/common/options';
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');
J
Johannes Rieken 已提交
20
import { IResourceInput } from 'vs/platform/editor/common/editor';
21 22
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { EmptyWorkspaceServiceImpl, WorkspaceServiceImpl, WorkspaceService } from 'vs/workbench/services/configuration/node/configuration';
23 24
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
25
import { realpath } from 'vs/base/node/pfs';
J
Johannes Rieken 已提交
26
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
E
Erich Gamma 已提交
27 28
import path = require('path');
import gracefulFs = require('graceful-fs');
B
Benjamin Pasero 已提交
29 30
import { IInitData } from 'vs/workbench/services/timer/common/timerService';
import { TimerService } from 'vs/workbench/services/timer/node/timerService';
A
Alex Dima 已提交
31
import { KeyboardMapperFactory } from "vs/workbench/services/keybinding/electron-browser/keybindingService";
32 33
import { IWindowConfiguration, IPath, IWindowsService } from 'vs/platform/windows/common/windows';
import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc';
B
Benjamin Pasero 已提交
34 35 36
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';
37 38 39 40 41 42 43 44
import { Client as ElectronIPCClient } from 'vs/base/parts/ipc/electron-browser/ipc.electron-browser';
import { webFrame, remote } from 'electron';
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';
C
Christof Marti 已提交
45 46
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { CredentialsChannelClient } from 'vs/platform/credentials/node/credentialsIpc';
47

B
Benjamin Pasero 已提交
48
import fs = require('fs');
B
Benjamin Pasero 已提交
49
gracefulFs.gracefulify(fs); // enable gracefulFs
E
Erich Gamma 已提交
50

51 52
const currentWindowId = remote.getCurrentWindow().id;

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

55 56
	// Ensure others can listen to zoom level changes
	browser.setZoomFactor(webFrame.getZoomFactor());
57

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

62 63
	browser.setFullscreen(!!configuration.fullscreen);

A
Alex Dima 已提交
64 65
	KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(configuration.isISOKeyboard);

66
	browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
67

68 69 70
	// Setup Intl
	comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));

E
Erich Gamma 已提交
71
	// Shell Options
B
polish  
Benjamin Pasero 已提交
72 73 74
	const filesToOpen = configuration.filesToOpen && configuration.filesToOpen.length ? toInputs(configuration.filesToOpen) : null;
	const filesToCreate = configuration.filesToCreate && configuration.filesToCreate.length ? toInputs(configuration.filesToCreate) : null;
	const filesToDiff = configuration.filesToDiff && configuration.filesToDiff.length ? toInputs(configuration.filesToDiff) : null;
B
Benjamin Pasero 已提交
75
	const shellOptions: IOptions = {
76 77
		filesToOpen,
		filesToCreate,
B
Benjamin Pasero 已提交
78
		filesToDiff
E
Erich Gamma 已提交
79 80
	};

81 82
	// Open workbench
	return openWorkbench(configuration, shellOptions);
E
Erich Gamma 已提交
83 84
}

85
function toInputs(paths: IPath[], isUntitledFile?: boolean): IResourceInput[] {
E
Erich Gamma 已提交
86
	return paths.map(p => {
87 88 89 90 91 92 93
		const input = <IResourceInput>{};

		if (isUntitledFile) {
			input.resource = uri.from({ scheme: 'untitled', path: p.filePath });
		} else {
			input.resource = uri.file(p.filePath);
		}
E
Erich Gamma 已提交
94

95 96 97 98
		input.options = {
			pinned: true // opening on startup is always pinned and not preview
		};

E
Erich Gamma 已提交
99
		if (p.lineNumber) {
100 101 102
			input.options.selection = {
				startLineNumber: p.lineNumber,
				startColumn: p.columnNumber
E
Erich Gamma 已提交
103 104 105 106 107 108 109
			};
		}

		return input;
	});
}

110
function openWorkbench(configuration: IWindowConfiguration, options: IOptions): TPromise<void> {
111 112
	const mainProcessClient = new ElectronIPCClient(String(`window${currentWindowId}`));
	const mainServices = createMainProcessServices(mainProcessClient);
113

114 115 116 117
	const environmentService = new EnvironmentService(configuration, configuration.execPath);

	// 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
118
	return createAndInitializeWorkspaceService(configuration, environmentService, <IWorkspacesService>mainServices.get(IWorkspacesService)).then(workspaceService => {
119 120
		const timerService = new TimerService((<any>window).MonacoEnvironment.timers as IInitData, !workspaceService.hasWorkspace());
		const storageService = createStorageService(configuration, workspaceService, environmentService);
121 122 123 124 125 126 127 128 129 130 131 132 133 134

		timerService.beforeDOMContentLoaded = Date.now();

		return domContentLoaded().then(() => {
			timerService.afterDOMContentLoaded = Date.now();

			// Open Shell
			timerService.beforeWorkbenchOpen = Date.now();
			const shell = new WorkbenchShell(document.body, {
				contextService: workspaceService,
				configurationService: workspaceService,
				environmentService,
				timerService,
				storageService
135
			}, mainServices, configuration, options);
136 137 138 139 140 141 142
			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));
143
					}
144
				}
145 146 147 148 149
			});
		});
	});
}

150
function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService, workspacesService: IWorkspacesService): TPromise<WorkspaceService> {
151
	return validateWorkspacePath(configuration).then(() => {
152 153 154
		const workspaceConfigPath = configuration.workspace ? uri.file(configuration.workspace.configPath) : null;
		const folderPath = configuration.folderPath ? uri.file(configuration.folderPath) : null;
		const workspaceService = (workspaceConfigPath || configuration.folderPath) ? new WorkspaceServiceImpl(workspaceConfigPath, folderPath, environmentService, workspacesService) : new EmptyWorkspaceServiceImpl(environmentService);
B
Benjamin Pasero 已提交
155

156 157
		return workspaceService.initialize().then(() => workspaceService, error => new EmptyWorkspaceServiceImpl(environmentService));
	});
158 159
}

160 161 162
function validateWorkspacePath(configuration: IWindowConfiguration): TPromise<void> {
	if (!configuration.folderPath) {
		return TPromise.as(null);
E
Erich Gamma 已提交
163 164
	}

165
	return realpath(configuration.folderPath).then(realFolderPath => {
166

E
Erich Gamma 已提交
167 168 169 170
		// for some weird reason, node adds a trailing slash to UNC paths
		// we never ever want trailing slashes as our workspace path unless
		// someone opens root ("/").
		// See also https://github.com/nodejs/io.js/issues/1765
171 172
		if (paths.isUNC(realFolderPath) && strings.endsWith(realFolderPath, paths.nativeSep)) {
			realFolderPath = strings.rtrim(realFolderPath, paths.nativeSep);
173
		}
E
Erich Gamma 已提交
174

175
		// update config
176
		configuration.folderPath = realFolderPath;
177
	}, error => {
178 179
		errors.onUnexpectedError(error);

180
		return null; // treat invalid paths as empty workspace
181
	});
E
Erich Gamma 已提交
182 183
}

184 185
function createStorageService(configuration: IWindowConfiguration, workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageService {
	const workspace = workspaceService.getWorkspace();
186 187 188 189

	let workspaceId: string;
	let secondaryWorkspaceId: number;

190 191 192 193
	// in multi root workspace mode we use the provided ID as key for workspace storage
	if (workspaceService.hasMultiFolderWorkspace()) {
		workspaceId = uri.from({ path: workspace.id, scheme: 'root' }).toString();
	}
194

195 196 197 198 199 200
	// 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
	else if (workspaceService.hasFolderWorkspace()) {
		const legacyWorkspace = workspaceService.getLegacyWorkspace();
		workspaceId = legacyWorkspace.resource.toString();
		secondaryWorkspaceId = legacyWorkspace.ctime;
201 202 203 204 205 206 207 208 209 210
	}

	// 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.
	else if (configuration.backupPath) {
		workspaceId = uri.from({ path: path.basename(configuration.backupPath), scheme: 'empty' }).toString();
211 212
	}

213 214
	const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests!
	const storage = disableStorage ? inMemoryLocalStorageInstance : window.localStorage;
215

216
	return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
217 218
}

219
function createMainProcessServices(mainProcessClient: ElectronIPCClient): ServiceCollection {
220 221 222 223 224 225 226 227 228
	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');
229
	serviceCollection.set(IURLService, new SyncDescriptor(URLChannelClient, urlChannel, currentWindowId));
230 231

	const workspacesChannel = mainProcessClient.getChannel('workspaces');
232
	serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel));
233

C
Christof Marti 已提交
234 235 236
	const credentialsChannel = mainProcessClient.getChannel('credentials');
	serviceCollection.set(ICredentialsService, new CredentialsChannelClient(credentialsChannel));

237 238 239
	return serviceCollection;
}

240 241 242 243 244 245
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)));
246
}