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

'use strict';

import 'vs/css!./media/shell';

10
import * as nls from 'vs/nls';
11
import {TPromise} from 'vs/base/common/winjs.base';
J
Joao Moreno 已提交
12
import * as platform from 'vs/base/common/platform';
E
Erich Gamma 已提交
13 14
import {Dimension, Builder, $} from 'vs/base/browser/builder';
import dom = require('vs/base/browser/dom');
15
import aria = require('vs/base/browser/ui/aria/aria');
16
import {dispose, IDisposable, Disposables} from 'vs/base/common/lifecycle';
E
Erich Gamma 已提交
17 18 19 20
import errors = require('vs/base/common/errors');
import {ContextViewService} from 'vs/platform/contextview/browser/contextViewService';
import timer = require('vs/base/common/timer');
import {Workbench} from 'vs/workbench/browser/workbench';
21
import {Storage, inMemoryLocalStorageInstance} from 'vs/workbench/common/storage';
22
import {ITelemetryService, NullTelemetryService} from 'vs/platform/telemetry/common/telemetry';
23
import {TelemetryService} from  'vs/platform/telemetry/browser/telemetryService';
J
Joao Moreno 已提交
24
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
25
import {createAppender} from 'vs/platform/telemetry/node/appInsightsAppender';
26
import {resolveCommonProperties} from 'vs/platform/telemetry/node/commonProperties';
E
Erich Gamma 已提交
27 28
import {ElectronIntegration} from 'vs/workbench/electron-browser/integration';
import {Update} from 'vs/workbench/electron-browser/update';
B
Benjamin Pasero 已提交
29
import {WorkspaceStats} from 'vs/platform/telemetry/common/workspaceStats';
E
Erich Gamma 已提交
30 31 32 33 34 35 36 37
import {IWindowService, WindowService} from 'vs/workbench/services/window/electron-browser/windowService';
import {MessageService} from 'vs/workbench/services/message/electron-browser/messageService';
import {RequestService} from 'vs/workbench/services/request/node/requestService';
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
import {FileService} from 'vs/workbench/services/files/electron-browser/fileService';
import {SearchService} from 'vs/workbench/services/search/node/searchService';
import {LifecycleService} from 'vs/workbench/services/lifecycle/electron-browser/lifecycleService';
import {MainThreadService} from 'vs/workbench/services/thread/electron-browser/threadService';
38
import {MainProcessMarkerService} from 'vs/platform/markers/common/markerService';
E
Erich Gamma 已提交
39 40 41 42
import {IModelService} from 'vs/editor/common/services/modelService';
import {ModelServiceImpl} from 'vs/editor/common/services/modelServiceImpl';
import {CodeEditorServiceImpl} from 'vs/editor/browser/services/codeEditorServiceImpl';
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
43 44
import {EditorWorkerServiceImpl} from 'vs/editor/common/services/editorWorkerServiceImpl';
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
45
import {MainProcessExtensionService} from 'vs/platform/extensions/common/nativeExtensionService';
E
Erich Gamma 已提交
46
import {IOptions} from 'vs/workbench/common/options';
M
Martin Aeschlimann 已提交
47
import {IStorageService} from 'vs/platform/storage/common/storage';
48
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
49
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
50
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
51
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
E
Erich Gamma 已提交
52 53 54
import {IEventService} from 'vs/platform/event/common/event';
import {IFileService} from 'vs/platform/files/common/files';
import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle';
B
Benjamin Pasero 已提交
55
import {IMarkerService} from 'vs/platform/markers/common/markers';
E
Erich Gamma 已提交
56 57 58 59 60
import {IMessageService, Severity} from 'vs/platform/message/common/message';
import {IRequestService} from 'vs/platform/request/common/request';
import {ISearchService} from 'vs/platform/search/common/search';
import {IThreadService} from 'vs/platform/thread/common/thread';
import {IWorkspaceContextService, IConfiguration, IWorkspace} from 'vs/platform/workspace/common/workspace';
A
Alex Dima 已提交
61
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
E
Erich Gamma 已提交
62 63
import {MainThreadModeServiceImpl} from 'vs/editor/common/services/modeServiceImpl';
import {IModeService} from 'vs/editor/common/services/modeService';
64
import {IUntitledEditorService, UntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
E
Erich Gamma 已提交
65
import {CrashReporter} from 'vs/workbench/electron-browser/crashReporter';
M
Martin Aeschlimann 已提交
66 67
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {ThemeService} from 'vs/workbench/services/themes/electron-browser/themeService';
J
Joao Moreno 已提交
68
import {getDelayedChannel} from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
69
import {connect} from 'vs/base/parts/ipc/node/ipc.net';
J
Joao Moreno 已提交
70 71
import {IExtensionManagementChannel, ExtensionManagementChannelClient} from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import {IExtensionManagementService} from 'vs/platform/extensionManagement/common/extensionManagement';
72
import {ReloadWindowAction} from 'vs/workbench/electron-browser/actions';
73
import 'vs/platform/opener/electron-browser/opener.contribution'; // self registering service
74

75 76 77 78 79 80 81 82 83
/**
 * Services that we require for the Shell
 */
export interface ICoreServices {
	contextService: IWorkspaceContextService;
	eventService: IEventService;
	configurationService: IConfigurationService;
}

E
Erich Gamma 已提交
84
/**
B
Benjamin Pasero 已提交
85
 * The workbench shell contains the workbench with a rich header containing navigation and the activity bar.
E
Erich Gamma 已提交
86 87 88
 * With the Shell being the top level element in the page, it is also responsible for driving the layouting.
 */
export class WorkbenchShell {
B
Benjamin Pasero 已提交
89
	private storageService: IStorageService;
90
	private messageService: MessageService;
91
	private eventService: IEventService;
B
Benjamin Pasero 已提交
92 93 94
	private contextViewService: ContextViewService;
	private windowService: IWindowService;
	private threadService: MainThreadService;
95
	private configurationService: IConfigurationService;
M
Martin Aeschlimann 已提交
96
	private themeService: ThemeService;
97
	private contextService: IWorkspaceContextService;
98
	private telemetryService: ITelemetryService;
E
Erich Gamma 已提交
99 100

	private container: HTMLElement;
101
	private toUnbind: IDisposable[];
E
Erich Gamma 已提交
102 103 104 105 106 107 108 109 110 111
	private previousErrorValue: string;
	private previousErrorTime: number;
	private content: HTMLElement;
	private contentsContainer: Builder;

	private configuration: IConfiguration;
	private workspace: IWorkspace;
	private options: IOptions;
	private workbench: Workbench;

112
	constructor(container: HTMLElement, workspace: IWorkspace, services: ICoreServices, configuration: IConfiguration, options: IOptions) {
E
Erich Gamma 已提交
113 114 115 116
		this.container = container;

		this.workspace = workspace;
		this.configuration = configuration;
117 118 119 120 121
		this.options = options;

		this.contextService = services.contextService;
		this.eventService = services.eventService;
		this.configurationService = services.configurationService;
E
Erich Gamma 已提交
122 123 124 125 126 127 128

		this.toUnbind = [];
		this.previousErrorTime = 0;
	}

	private createContents(parent: Builder): Builder {

129
		// ARIA
B
Benjamin Pasero 已提交
130
		aria.setARIAContainer(document.body);
131

E
Erich Gamma 已提交
132 133 134 135
		// Workbench Container
		let workbenchContainer = $(parent).div();

		// Instantiation service with services
136
		let [instantiationService, serviceCollection] = this.initServiceCollection();
E
Erich Gamma 已提交
137 138 139

		//crash reporting
		if (!!this.configuration.env.crashReporter) {
140
			let crashReporter = instantiationService.createInstance(CrashReporter, this.configuration.env.version, this.configuration.env.commitHash);
E
Erich Gamma 已提交
141 142 143 144
			crashReporter.start(this.configuration.env.crashReporter);
		}

		// Workbench
145
		this.workbench = instantiationService.createInstance(Workbench, workbenchContainer.getHTMLElement(), this.workspace, this.configuration, this.options, serviceCollection);
E
Erich Gamma 已提交
146
		this.workbench.startup({
147 148
			onWorkbenchStarted: (customKeybindingsCount) => {
				this.onWorkbenchStarted(customKeybindingsCount);
E
Erich Gamma 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
			}
		});

		// Electron integration
		this.workbench.getInstantiationService().createInstance(ElectronIntegration).integrate(this.container);

		// Update
		this.workbench.getInstantiationService().createInstance(Update);

		// Handle case where workbench is not starting up properly
		let timeoutHandle = setTimeout(() => {
			console.warn('Workbench did not finish loading in 10 seconds, that might be a problem that should be reported.');
		}, 10000);

		this.workbench.joinCreation().then(() => {
			clearTimeout(timeoutHandle);
		});

		return workbenchContainer;
	}

170
	private onWorkbenchStarted(customKeybindingsCount: number): void {
B
Benjamin Pasero 已提交
171 172 173 174 175 176 177 178 179 180 181 182 183 184

		// Log to telemetry service
		let windowSize = {
			innerHeight: window.innerHeight,
			innerWidth: window.innerWidth,
			outerHeight: window.outerHeight,
			outerWidth: window.outerWidth
		};

		this.telemetryService.publicLog('workspaceLoad',
			{
				userAgent: navigator.userAgent,
				windowSize: windowSize,
				emptyWorkbench: !this.contextService.getWorkspace(),
185
				customKeybindingsCount,
186
				theme: this.themeService.getTheme(),
187
				language: platform.language
B
Benjamin Pasero 已提交
188 189 190 191
			});

		let workspaceStats: WorkspaceStats = <WorkspaceStats>this.workbench.getInstantiationService().createInstance(WorkspaceStats);
		workspaceStats.reportWorkspaceTags();
J
Joao Moreno 已提交
192 193 194 195

		if ((platform.isLinux || platform.isMacintosh) && process.getuid() === 0) {
			this.messageService.show(Severity.Warning, nls.localize('runningAsRoot', "It is recommended not to run Code as 'root'."));
		}
B
Benjamin Pasero 已提交
196 197
	}

198
	private initServiceCollection(): [InstantiationService, ServiceCollection] {
199 200 201 202
		const serviceCollection = new ServiceCollection();
		serviceCollection.set(IEventService, this.eventService);
		serviceCollection.set(IWorkspaceContextService, this.contextService);
		serviceCollection.set(IConfigurationService, this.configurationService);
203

204 205
		const instantiationService = new InstantiationService(serviceCollection, true);
		const disposables = new Disposables();
206

207 208 209 210
		this.windowService = instantiationService.createInstance(WindowService);
		serviceCollection.set(IWindowService, this.windowService);

		// Storage
A
Alex Dima 已提交
211
		let disableWorkspaceStorage = this.configuration.env.extensionTestsPath || (!this.workspace && !this.configuration.env.extensionDevelopmentPath); // without workspace or in any extension test, we use inMemory storage unless we develop an extension where we want to preserve state
212 213
		this.storageService = instantiationService.createInstance(Storage, window.localStorage, disableWorkspaceStorage ? inMemoryLocalStorageInstance : window.localStorage);
		serviceCollection.set(IStorageService, this.storageService);
E
Erich Gamma 已提交
214

215
		// Telemetry
216
		if (this.configuration.env.isBuilt && !this.configuration.env.extensionDevelopmentPath && !!this.configuration.env.enableTelemetry) {
217
			const config = {
218
				appender: createAppender(this.configuration.env),
219
				commonProperties: resolveCommonProperties(this.storageService, this.contextService),
220
				piiPaths: [this.configuration.env.appRoot, this.configuration.env.userExtensionsHome]
221
			};
J
Joao Moreno 已提交
222 223
			const telemetryService = instantiationService.createInstance(TelemetryService, config);
			const errorTelemetry = new ErrorTelemetry(telemetryService);
224
			this.telemetryService = telemetryService;
J
Joao Moreno 已提交
225
			disposables.add(telemetryService, errorTelemetry, ...config.appender);
226
		} else {
227
			this.telemetryService = NullTelemetryService;
228
		}
E
Erich Gamma 已提交
229

230
		serviceCollection.set(ITelemetryService, this.telemetryService);
E
Erich Gamma 已提交
231

232 233
		this.messageService = instantiationService.createInstance(MessageService);
		serviceCollection.set(IMessageService, this.messageService);
E
Erich Gamma 已提交
234

235 236
		let fileService = disposables.add(instantiationService.createInstance(FileService));
		serviceCollection.set(IFileService, fileService);
E
Erich Gamma 已提交
237

238 239 240
		let lifecycleService = instantiationService.createInstance(LifecycleService);
		this.toUnbind.push(lifecycleService.onShutdown(() => disposables.dispose()));
		serviceCollection.set(ILifecycleService, lifecycleService);
241

242 243
		this.threadService = instantiationService.createInstance(MainThreadService);
		serviceCollection.set(IThreadService, this.threadService);
244

245 246
		let extensionService = instantiationService.createInstance(MainProcessExtensionService);
		serviceCollection.set(IExtensionService, extensionService);
E
Erich Gamma 已提交
247

248 249
		this.contextViewService = instantiationService.createInstance(ContextViewService, this.container);
		serviceCollection.set(IContextViewService, this.contextViewService);
E
Erich Gamma 已提交
250

251 252
		let requestService = disposables.add(instantiationService.createInstance(RequestService));
		serviceCollection.set(IRequestService, requestService);
E
Erich Gamma 已提交
253

254 255
		let markerService = instantiationService.createInstance(MainProcessMarkerService);
		serviceCollection.set(IMarkerService, markerService);
E
Erich Gamma 已提交
256

257
		let modeService = instantiationService.createInstance(MainThreadModeServiceImpl);
258
		serviceCollection.set(IModeService, modeService);
259 260

		let modelService = instantiationService.createInstance(ModelServiceImpl);
261
		serviceCollection.set(IModelService, modelService);
262 263

		let editorWorkerService = instantiationService.createInstance(EditorWorkerServiceImpl);
264
		serviceCollection.set(IEditorWorkerService, editorWorkerService);
265 266 267 268 269

		let untitledEditorService = instantiationService.createInstance(UntitledEditorService);
		serviceCollection.set(IUntitledEditorService, untitledEditorService);

		this.themeService = instantiationService.createInstance(ThemeService);
270
		serviceCollection.set(IThemeService, this.themeService);
271 272 273 274 275 276 277 278 279

		let searchService = instantiationService.createInstance(SearchService);
		serviceCollection.set(ISearchService, searchService);

		let codeEditorService = instantiationService.createInstance(CodeEditorServiceImpl);
		serviceCollection.set(ICodeEditorService, codeEditorService);

		let extensionManagementChannelClient = instantiationService.createInstance(ExtensionManagementChannelClient, this.initSharedProcessChannel(instantiationService, this.messageService));
		serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient);
280 281

		return [instantiationService, serviceCollection];
E
Erich Gamma 已提交
282 283
	}

J
Joao Moreno 已提交
284
	private initSharedProcessChannel(instantiationService: IInstantiationService, messageService: IMessageService): IExtensionManagementChannel {
285 286 287 288 289 290 291 292 293 294 295
		const sharedProcessClientPromise = connect(process.env['VSCODE_SHARED_IPC_HOOK']);

		sharedProcessClientPromise.done(service => {
			service.onClose(() => {
				messageService.show(Severity.Error, {
					message: nls.localize('sharedProcessCrashed', "The shared process terminated unexpectedly. Please reload the window to recover."),
					actions: [instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL)]
				});
			});
		}, errors.onUnexpectedError);

J
Joao Moreno 已提交
296 297
		const extensionManagementChannelPromise = sharedProcessClientPromise
			.then(client => client.getChannel<IExtensionManagementChannel>('extensions'));
298

J
Joao Moreno 已提交
299
		return getDelayedChannel<IExtensionManagementChannel>(extensionManagementChannelPromise);
300 301
	}

E
Erich Gamma 已提交
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
	public open(): void {

		// Listen on unexpected errors
		errors.setUnexpectedErrorHandler((error: any) => {
			this.onUnexpectedError(error);
		});

		// Shell Class for CSS Scoping
		$(this.container).addClass('monaco-shell');

		// Controls
		this.content = $('.monaco-shell-content').appendTo(this.container).getHTMLElement();

		// Handle Load Performance Timers
		this.writeTimers();

		// Create Contents
		this.contentsContainer = this.createContents($(this.content));

		// Layout
		this.layout();

		// Listeners
		this.registerListeners();

		// Enable theme support
M
Martin Aeschlimann 已提交
328
		this.themeService.initialize(this.container).then(null, error => {
329 330
			errors.onUnexpectedError(error);
		});
E
Erich Gamma 已提交
331 332 333 334 335 336 337 338 339
	}

	private registerListeners(): void {

		// Resize
		$(window).on(dom.EventType.RESIZE, () => this.layout(), this.toUnbind);
	}

	private writeTimers(): void {
340
		let timers = (<any>window).GlobalEnvironment.timers;
E
Erich Gamma 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
		if (timers) {
			let events: timer.IExistingTimerEvent[] = [];

			// Program
			if (timers.beforeProgram) {
				events.push({
					startTime: timers.beforeProgram,
					stopTime: timers.afterProgram,
					topic: 'Startup',
					name: 'Program Start',
					description: 'Time it takes to pass control to VSCodes main method'
				});
			}

			// Window
			if (timers.vscodeStart) {
				events.push({
					startTime: timers.vscodeStart,
					stopTime: timers.beforeLoad,
					topic: 'Startup',
					name: 'VSCode Startup',
					description: 'Time it takes to create a window and startup VSCode'
				});
			}

			// Load
			events.push({
				startTime: timers.beforeLoad,
				stopTime: timers.afterLoad,
				topic: 'Startup',
				name: 'Load Modules',
				description: 'Time it takes to load VSCodes main modules'
			});

			// Ready
			events.push({
				startTime: timers.beforeReady,
				stopTime: timers.afterReady,
				topic: 'Startup',
				name: 'Event DOMContentLoaded',
				description: 'Time it takes for the DOM to emit DOMContentLoaded event'
			});

			// Write to Timer
			timer.getTimeKeeper().setInitialCollectedEvents(events, timers.start);
		}
	}

	public onUnexpectedError(error: any): void {
		let errorMsg = errors.toErrorMessage(error, true);
		if (!errorMsg) {
			return;
		}

B
Benjamin Pasero 已提交
395
		let now = Date.now();
E
Erich Gamma 已提交
396 397 398 399 400 401 402 403 404 405 406
		if (errorMsg === this.previousErrorValue && now - this.previousErrorTime <= 1000) {
			return; // Return if error message identical to previous and shorter than 1 second
		}

		this.previousErrorTime = now;
		this.previousErrorValue = errorMsg;

		// Log to console
		console.error(errorMsg);

		// Show to user if friendly message provided
407
		if (error && error.friendlyMessage && this.messageService) {
B
Benjamin Pasero 已提交
408
			this.messageService.show(Severity.Error, error.friendlyMessage);
E
Erich Gamma 已提交
409 410 411 412 413 414 415 416 417
		}
	}

	public layout(): void {
		let clArea = $(this.container).getClientArea();

		let contentsSize = new Dimension(clArea.width, clArea.height);
		this.contentsContainer.size(contentsSize.width, contentsSize.height);

B
Benjamin Pasero 已提交
418
		this.contextViewService.layout();
E
Erich Gamma 已提交
419 420 421 422 423 424 425
		this.workbench.layout();
	}

	public joinCreation(): TPromise<boolean> {
		return this.workbench.joinCreation();
	}

B
Benjamin Pasero 已提交
426
	public dispose(): void {
E
Erich Gamma 已提交
427 428 429

		// Workbench
		if (this.workbench) {
B
Benjamin Pasero 已提交
430
			this.workbench.dispose();
E
Erich Gamma 已提交
431 432
		}

B
Benjamin Pasero 已提交
433
		this.contextViewService.dispose();
E
Erich Gamma 已提交
434 435

		// Listeners
J
Joao Moreno 已提交
436
		this.toUnbind = dispose(this.toUnbind);
E
Erich Gamma 已提交
437 438 439 440 441

		// Container
		$(this.container).empty();
	}
}