shell.ts 18.3 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, ITelemetryServiceConfig} from  'vs/platform/telemetry/common/telemetryService';
24
import {IdleMonitor, UserStatus} from  'vs/platform/telemetry/browser/idleMonitor';
J
Joao Moreno 已提交
25
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
26
import {createAppender} from 'vs/platform/telemetry/node/appInsightsAppender';
27
import {resolveCommonProperties} from 'vs/platform/telemetry/node/commonProperties';
E
Erich Gamma 已提交
28 29
import {ElectronIntegration} from 'vs/workbench/electron-browser/integration';
import {Update} from 'vs/workbench/electron-browser/update';
B
Benjamin Pasero 已提交
30
import {WorkspaceStats} from 'vs/platform/telemetry/common/workspaceStats';
E
Erich Gamma 已提交
31 32 33 34 35 36 37 38
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';
39
import {MainProcessMarkerService} from 'vs/platform/markers/common/markerService';
E
Erich Gamma 已提交
40 41 42 43
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';
44 45
import {EditorWorkerServiceImpl} from 'vs/editor/common/services/editorWorkerServiceImpl';
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
46
import {MainProcessExtensionService} from 'vs/platform/extensions/common/nativeExtensionService';
E
Erich Gamma 已提交
47
import {IOptions} from 'vs/workbench/common/options';
M
Martin Aeschlimann 已提交
48
import {IStorageService} from 'vs/platform/storage/common/storage';
49
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
50
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
51
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
52
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
E
Erich Gamma 已提交
53 54 55
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 已提交
56
import {IMarkerService} from 'vs/platform/markers/common/markers';
E
Erich Gamma 已提交
57 58 59 60 61
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 已提交
62
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
E
Erich Gamma 已提交
63 64
import {MainThreadModeServiceImpl} from 'vs/editor/common/services/modeServiceImpl';
import {IModeService} from 'vs/editor/common/services/modeService';
65
import {IUntitledEditorService, UntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
E
Erich Gamma 已提交
66
import {CrashReporter} from 'vs/workbench/electron-browser/crashReporter';
M
Martin Aeschlimann 已提交
67 68
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {ThemeService} from 'vs/workbench/services/themes/electron-browser/themeService';
J
Joao Moreno 已提交
69
import {getDelayedChannel} from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
70
import {connect} from 'vs/base/parts/ipc/node/ipc.net';
J
Joao Moreno 已提交
71 72
import {IExtensionManagementChannel, ExtensionManagementChannelClient} from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import {IExtensionManagementService} from 'vs/platform/extensionManagement/common/extensionManagement';
73
import {ReloadWindowAction} from 'vs/workbench/electron-browser/actions';
74
import 'vs/platform/opener/electron-browser/opener.contribution'; // self registering service
75

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

E
Erich Gamma 已提交
85
/**
B
Benjamin Pasero 已提交
86
 * The workbench shell contains the workbench with a rich header containing navigation and the activity bar.
E
Erich Gamma 已提交
87 88 89
 * 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 已提交
90
	private storageService: IStorageService;
91
	private messageService: MessageService;
92
	private eventService: IEventService;
B
Benjamin Pasero 已提交
93 94 95
	private contextViewService: ContextViewService;
	private windowService: IWindowService;
	private threadService: MainThreadService;
96
	private configurationService: IConfigurationService;
M
Martin Aeschlimann 已提交
97
	private themeService: ThemeService;
98
	private contextService: IWorkspaceContextService;
99
	private telemetryService: ITelemetryService;
E
Erich Gamma 已提交
100 101

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

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

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

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

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

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

	private createContents(parent: Builder): Builder {

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

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

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

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

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

		// 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;
	}

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

		// 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(),
186
				customKeybindingsCount,
187
				theme: this.themeService.getTheme(),
188
				language: platform.language
B
Benjamin Pasero 已提交
189 190 191 192
			});

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

		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 已提交
197 198
	}

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

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

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

		// Storage
A
Alex Dima 已提交
212
		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
213 214
		this.storageService = instantiationService.createInstance(Storage, window.localStorage, disableWorkspaceStorage ? inMemoryLocalStorageInstance : window.localStorage);
		serviceCollection.set(IStorageService, this.storageService);
E
Erich Gamma 已提交
215

216
		// Telemetry
217
		if (this.configuration.env.isBuilt && !this.configuration.env.extensionDevelopmentPath && !!this.configuration.env.enableTelemetry) {
218 219 220
			const appender = createAppender(this.configuration.env);
			const config: ITelemetryServiceConfig = {
				appender,
221
				commonProperties: resolveCommonProperties(this.storageService, this.contextService),
222
				piiPaths: [this.configuration.env.appRoot, this.configuration.env.userExtensionsHome]
223
			};
224

J
Joao Moreno 已提交
225
			const telemetryService = instantiationService.createInstance(TelemetryService, config);
226 227
			this.telemetryService = telemetryService;

J
Joao Moreno 已提交
228
			const errorTelemetry = new ErrorTelemetry(telemetryService);
229
			const idleMonitor = new IdleMonitor(2 * 60 * 1000); // 2 minutes
230

231 232 233 234 235 236
			const listener = idleMonitor.onStatusChange(status =>
				this.telemetryService.publicLog(status === UserStatus.Active
					? TelemetryService.IDLE_STOP_EVENT_NAME
					: TelemetryService.IDLE_START_EVENT_NAME
			));

237
			disposables.add(telemetryService, errorTelemetry, listener, idleMonitor, appender);
238
		} else {
239
			this.telemetryService = NullTelemetryService;
240
		}
E
Erich Gamma 已提交
241

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

244 245
		this.messageService = instantiationService.createInstance(MessageService);
		serviceCollection.set(IMessageService, this.messageService);
E
Erich Gamma 已提交
246

247 248
		let fileService = disposables.add(instantiationService.createInstance(FileService));
		serviceCollection.set(IFileService, fileService);
E
Erich Gamma 已提交
249

250 251 252
		let lifecycleService = instantiationService.createInstance(LifecycleService);
		this.toUnbind.push(lifecycleService.onShutdown(() => disposables.dispose()));
		serviceCollection.set(ILifecycleService, lifecycleService);
253

254 255
		this.threadService = instantiationService.createInstance(MainThreadService);
		serviceCollection.set(IThreadService, this.threadService);
256

257 258
		let extensionService = instantiationService.createInstance(MainProcessExtensionService);
		serviceCollection.set(IExtensionService, extensionService);
E
Erich Gamma 已提交
259

260 261
		this.contextViewService = instantiationService.createInstance(ContextViewService, this.container);
		serviceCollection.set(IContextViewService, this.contextViewService);
E
Erich Gamma 已提交
262

263 264
		let requestService = disposables.add(instantiationService.createInstance(RequestService));
		serviceCollection.set(IRequestService, requestService);
E
Erich Gamma 已提交
265

266 267
		let markerService = instantiationService.createInstance(MainProcessMarkerService);
		serviceCollection.set(IMarkerService, markerService);
E
Erich Gamma 已提交
268

269
		let modeService = instantiationService.createInstance(MainThreadModeServiceImpl);
270
		serviceCollection.set(IModeService, modeService);
271 272

		let modelService = instantiationService.createInstance(ModelServiceImpl);
273
		serviceCollection.set(IModelService, modelService);
274 275

		let editorWorkerService = instantiationService.createInstance(EditorWorkerServiceImpl);
276
		serviceCollection.set(IEditorWorkerService, editorWorkerService);
277 278 279 280 281

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

		this.themeService = instantiationService.createInstance(ThemeService);
282
		serviceCollection.set(IThemeService, this.themeService);
283 284 285 286 287 288 289 290 291

		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);
292 293

		return [instantiationService, serviceCollection];
E
Erich Gamma 已提交
294 295
	}

J
Joao Moreno 已提交
296
	private initSharedProcessChannel(instantiationService: IInstantiationService, messageService: IMessageService): IExtensionManagementChannel {
297 298 299 300 301 302 303 304 305 306 307
		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 已提交
308 309
		const extensionManagementChannelPromise = sharedProcessClientPromise
			.then(client => client.getChannel<IExtensionManagementChannel>('extensions'));
310

J
Joao Moreno 已提交
311
		return getDelayedChannel<IExtensionManagementChannel>(extensionManagementChannelPromise);
312 313
	}

E
Erich Gamma 已提交
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	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 已提交
340
		this.themeService.initialize(this.container).then(null, error => {
341 342
			errors.onUnexpectedError(error);
		});
E
Erich Gamma 已提交
343 344 345 346 347 348 349 350 351
	}

	private registerListeners(): void {

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

	private writeTimers(): void {
352
		let timers = (<any>window).GlobalEnvironment.timers;
E
Erich Gamma 已提交
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 395 396 397 398 399 400 401 402 403 404 405 406
		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 已提交
407
		let now = Date.now();
E
Erich Gamma 已提交
408 409 410 411 412 413 414 415 416 417 418
		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
419
		if (error && error.friendlyMessage && this.messageService) {
B
Benjamin Pasero 已提交
420
			this.messageService.show(Severity.Error, error.friendlyMessage);
E
Erich Gamma 已提交
421 422 423 424 425 426 427 428 429
		}
	}

	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 已提交
430
		this.contextViewService.layout();
E
Erich Gamma 已提交
431 432 433 434 435 436 437
		this.workbench.layout();
	}

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

B
Benjamin Pasero 已提交
438
	public dispose(): void {
E
Erich Gamma 已提交
439 440 441

		// Workbench
		if (this.workbench) {
B
Benjamin Pasero 已提交
442
			this.workbench.dispose();
E
Erich Gamma 已提交
443 444
		}

B
Benjamin Pasero 已提交
445
		this.contextViewService.dispose();
E
Erich Gamma 已提交
446 447

		// Listeners
J
Joao Moreno 已提交
448
		this.toUnbind = dispose(this.toUnbind);
E
Erich Gamma 已提交
449 450 451 452 453

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