main.ts 9.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');
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 23
import { LegacyWorkspace, Workspace } from 'vs/platform/workspace/common/workspace';
import { WorkspaceService, EmptyWorkspaceServiceImpl, WorkspaceServiceImpl } from 'vs/workbench/services/configuration/node/configuration';
import { realpath } from 'vs/base/node/pfs';
J
Johannes Rieken 已提交
24
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
E
Erich Gamma 已提交
25 26
import path = require('path');
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';
A
Alex Dima 已提交
29
import { KeyboardMapperFactory } from "vs/workbench/services/keybinding/electron-browser/keybindingService";
B
Benjamin Pasero 已提交
30 31 32 33
import { IWindowConfiguration, IPath } from 'vs/platform/windows/common/windows';
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';
B
Benjamin Pasero 已提交
34

35 36
import { webFrame } from 'electron';

B
Benjamin Pasero 已提交
37
import fs = require('fs');
B
Benjamin Pasero 已提交
38
gracefulFs.gracefulify(fs); // enable gracefulFs
E
Erich Gamma 已提交
39

40
export function startup(configuration: IWindowConfiguration): TPromise<void> {
41

42 43
	// Ensure others can listen to zoom level changes
	browser.setZoomFactor(webFrame.getZoomFactor());
44

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

49 50
	browser.setFullscreen(!!configuration.fullscreen);

A
Alex Dima 已提交
51 52
	KeyboardMapperFactory.INSTANCE._onKeyboardLayoutChanged(configuration.isISOKeyboard);

53
	browser.setAccessibilitySupport(configuration.accessibilitySupport ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
54

55 56 57
	// Setup Intl
	comparer.setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }));

E
Erich Gamma 已提交
58
	// Shell Options
B
polish  
Benjamin Pasero 已提交
59 60 61
	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 已提交
62
	const shellOptions: IOptions = {
63 64
		filesToOpen,
		filesToCreate,
B
Benjamin Pasero 已提交
65
		filesToDiff
E
Erich Gamma 已提交
66 67
	};

68 69
	// Open workbench
	return openWorkbench(configuration, shellOptions);
E
Erich Gamma 已提交
70 71
}

72
function toInputs(paths: IPath[], isUntitledFile?: boolean): IResourceInput[] {
E
Erich Gamma 已提交
73
	return paths.map(p => {
74 75 76 77 78 79 80
		const input = <IResourceInput>{};

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

82 83 84 85
		input.options = {
			pinned: true // opening on startup is always pinned and not preview
		};

E
Erich Gamma 已提交
86
		if (p.lineNumber) {
87 88 89
			input.options.selection = {
				startLineNumber: p.lineNumber,
				startColumn: p.columnNumber
E
Erich Gamma 已提交
90 91 92 93 94 95 96
			};
		}

		return input;
	});
}

97
function openWorkbench(configuration: IWindowConfiguration, options: IOptions): TPromise<void> {
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	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
	return createAndInitializeWorkspaceService(configuration, environmentService).then(workspaceService => {
		const workspace = <Workspace>workspaceService.getWorkspace();
		const legacyWorkspace = <LegacyWorkspace>workspaceService.getLegacyWorkspace();
		const timerService = new TimerService((<any>window).MonacoEnvironment.timers as IInitData, !!workspace);
		const storageService = createStorageService(legacyWorkspace, workspace, configuration, environmentService);

		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
			}, configuration, options);
			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));
129
					}
130
				}
131 132 133 134 135
			});
		});
	});
}

136 137 138 139 140 141
function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): TPromise<WorkspaceService> {
	return validateWorkspacePath(configuration).then(() => {
		const workspaceConfigPath = configuration.workspace ? configuration.workspace.configPath : null;
		const workspaceService = (workspaceConfigPath || configuration.folderPath) ? new WorkspaceServiceImpl(workspaceConfigPath, configuration.folderPath, environmentService) : new EmptyWorkspaceServiceImpl(environmentService);
		return workspaceService.initialize().then(() => workspaceService, error => new EmptyWorkspaceServiceImpl(environmentService));
	});
142 143
}

144 145 146
function validateWorkspacePath(configuration: IWindowConfiguration): TPromise<void> {
	if (!configuration.folderPath) {
		return TPromise.as(null);
E
Erich Gamma 已提交
147 148
	}

149
	return realpath(configuration.folderPath).then(realFolderPath => {
150

E
Erich Gamma 已提交
151 152 153 154
		// 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
155 156
		if (paths.isUNC(realFolderPath) && strings.endsWith(realFolderPath, paths.nativeSep)) {
			realFolderPath = strings.rtrim(realFolderPath, paths.nativeSep);
157
		}
E
Erich Gamma 已提交
158

159
		// update config
160
		configuration.folderPath = realFolderPath;
161
	}, error => {
162 163
		errors.onUnexpectedError(error);

164
		return null; // treat invalid paths as empty workspace
165
	});
E
Erich Gamma 已提交
166 167
}

168
function createStorageService(legacyWorkspace: LegacyWorkspace, workspace: Workspace, configuration: IWindowConfiguration, environmentService: IEnvironmentService): IStorageService {
169 170 171 172

	let workspaceId: string;
	let secondaryWorkspaceId: number;

173
	if (workspace) {
174

175 176 177 178
		// in multi root workspace mode we use the provided ID as key for workspace storage
		if (workspace.configuration) {
			workspaceId = uri.from({ path: workspace.id, scheme: 'root' }).toString();
		}
179

180 181 182 183 184 185
		// 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 {
			workspaceId = legacyWorkspace.resource.toString();
			secondaryWorkspaceId = legacyWorkspace.ctime;
		}
186 187 188 189 190 191 192 193 194 195
	}

	// 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();
196 197
	}

198 199
	const disableStorage = !!environmentService.extensionTestsPath; // never keep any state when running extension tests!
	const storage = disableStorage ? inMemoryLocalStorageInstance : window.localStorage;
200

201
	return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId);
202 203 204 205 206 207 208 209
}

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)));
210
}