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

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

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

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

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

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

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

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

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

	private createContents(parent: Builder): Builder {

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

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

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

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

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

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

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

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

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

		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 已提交
195 196
	}

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

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

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

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

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

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

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

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

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

240 241
		this.threadService = instantiationService.createInstance(MainThreadService);
		serviceCollection.set(IThreadService, this.threadService);
242

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

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

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

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

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

		let modelService = instantiationService.createInstance(ModelServiceImpl);
259
		serviceCollection.set(IModelService, modelService);
260 261

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

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

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

		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);
278 279

		return [instantiationService, serviceCollection];
E
Erich Gamma 已提交
280 281
	}

J
Joao Moreno 已提交
282
	private initSharedProcessChannel(instantiationService: IInstantiationService, messageService: IMessageService): IExtensionManagementChannel {
283 284 285 286 287 288 289 290 291 292 293
		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 已提交
294 295
		const extensionManagementChannelPromise = sharedProcessClientPromise
			.then(client => client.getChannel<IExtensionManagementChannel>('extensions'));
296

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

E
Erich Gamma 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
	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 已提交
326
		this.themeService.initialize(this.container).then(null, error => {
327 328
			errors.onUnexpectedError(error);
		});
E
Erich Gamma 已提交
329 330 331 332 333 334 335 336 337
	}

	private registerListeners(): void {

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

	private writeTimers(): void {
338
		let timers = (<any>window).GlobalEnvironment.timers;
E
Erich Gamma 已提交
339 340 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
		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 已提交
393
		let now = Date.now();
E
Erich Gamma 已提交
394 395 396 397 398 399 400 401 402 403 404
		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
405
		if (error && error.friendlyMessage && this.messageService) {
B
Benjamin Pasero 已提交
406
			this.messageService.show(Severity.Error, error.friendlyMessage);
E
Erich Gamma 已提交
407 408 409 410 411 412 413 414 415
		}
	}

	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 已提交
416
		this.contextViewService.layout();
E
Erich Gamma 已提交
417 418 419 420 421 422 423
		this.workbench.layout();
	}

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

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

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

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

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

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