shell.ts 19.0 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
import errors = require('vs/base/common/errors');
18
import {toErrorMessage} from 'vs/base/common/errorMessage';
19 20
import product from 'vs/platform/product';
import pkg from 'vs/platform/package';
E
Erich Gamma 已提交
21 22
import {ContextViewService} from 'vs/platform/contextview/browser/contextViewService';
import timer = require('vs/base/common/timer');
23
import {Workbench} from 'vs/workbench/electron-browser/workbench';
24
import {Storage, inMemoryLocalStorageInstance} from 'vs/workbench/common/storage';
25
import {ITelemetryService, NullTelemetryService} from 'vs/platform/telemetry/common/telemetry';
26
import {ITelemetryAppenderChannel, TelemetryAppenderClient} from 'vs/platform/telemetry/common/telemetryIpc';
27
import {TelemetryService, ITelemetryServiceConfig} from  'vs/platform/telemetry/common/telemetryService';
28
import {IdleMonitor, UserStatus} from  'vs/platform/telemetry/browser/idleMonitor';
J
Joao Moreno 已提交
29
import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry';
30
import {resolveWorkbenchCommonProperties} from 'vs/platform/telemetry/node/workbenchCommonProperties';
E
Erich Gamma 已提交
31 32
import {ElectronIntegration} from 'vs/workbench/electron-browser/integration';
import {Update} from 'vs/workbench/electron-browser/update';
A
Alex Dima 已提交
33
import {WorkspaceStats} from 'vs/workbench/services/telemetry/common/workspaceStats';
E
Erich Gamma 已提交
34 35
import {IWindowService, WindowService} from 'vs/workbench/services/window/electron-browser/windowService';
import {MessageService} from 'vs/workbench/services/message/electron-browser/messageService';
J
Joao Moreno 已提交
36 37
import {IRequestService} from 'vs/platform/request/common/request';
import {RequestService} from 'vs/platform/request/node/requestService';
E
Erich Gamma 已提交
38 39 40 41
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
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';
42
import {MarkerService} from 'vs/platform/markers/common/markerService';
E
Erich Gamma 已提交
43 44
import {IModelService} from 'vs/editor/common/services/modelService';
import {ModelServiceImpl} from 'vs/editor/common/services/modelServiceImpl';
45 46
import {ICompatWorkerService} from 'vs/editor/common/services/compatWorkerService';
import {MainThreadCompatWorkerService} from 'vs/editor/common/services/compatWorkerServiceMain';
E
Erich Gamma 已提交
47 48
import {CodeEditorServiceImpl} from 'vs/editor/browser/services/codeEditorServiceImpl';
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
49 50
import {EditorWorkerServiceImpl} from 'vs/editor/common/services/editorWorkerServiceImpl';
import {IEditorWorkerService} from 'vs/editor/common/services/editorWorkerService';
51
import {MainProcessExtensionService} from 'vs/workbench/api/node/mainThreadExtensionService';
E
Erich Gamma 已提交
52
import {IOptions} from 'vs/workbench/common/options';
M
Martin Aeschlimann 已提交
53
import {IStorageService} from 'vs/platform/storage/common/storage';
54
import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection';
55
import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService';
56
import {IContextViewService} from 'vs/platform/contextview/browser/contextView';
E
Erich Gamma 已提交
57 58
import {IEventService} from 'vs/platform/event/common/event';
import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle';
B
Benjamin Pasero 已提交
59
import {IMarkerService} from 'vs/platform/markers/common/markers';
60
import {IEnvironmentService} from 'vs/platform/environment/common/environment';
J
Joao Moreno 已提交
61
import {IMessageService, IChoiceService, Severity} from 'vs/platform/message/common/message';
E
Erich Gamma 已提交
62
import {ISearchService} from 'vs/platform/search/common/search';
63
import {IThreadService} from 'vs/workbench/services/thread/common/threadService';
64 65
import {ICommandService} from 'vs/platform/commands/common/commands';
import {CommandService} from 'vs/platform/commands/common/commandService';
66
import {IWorkspaceContextService, IWorkspace} from 'vs/platform/workspace/common/workspace';
A
Alex Dima 已提交
67
import {IExtensionService} from 'vs/platform/extensions/common/extensions';
E
Erich Gamma 已提交
68 69
import {MainThreadModeServiceImpl} from 'vs/editor/common/services/modeServiceImpl';
import {IModeService} from 'vs/editor/common/services/modeService';
70
import {IUntitledEditorService, UntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
E
Erich Gamma 已提交
71
import {CrashReporter} from 'vs/workbench/electron-browser/crashReporter';
M
Martin Aeschlimann 已提交
72 73
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {ThemeService} from 'vs/workbench/services/themes/electron-browser/themeService';
J
Joao Moreno 已提交
74
import {getDelayedChannel} from 'vs/base/parts/ipc/common/ipc';
J
Joao Moreno 已提交
75 76 77
import {connect as connectNet} from 'vs/base/parts/ipc/node/ipc.net';
import {Client as ElectronIPCClient} from 'vs/base/parts/ipc/common/ipc.electron';
import {ipcRenderer} from 'electron';
J
Joao Moreno 已提交
78 79
import {IExtensionManagementChannel, ExtensionManagementChannelClient} from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import {IExtensionManagementService} from 'vs/platform/extensionManagement/common/extensionManagement';
J
Joao Moreno 已提交
80 81
import {URLChannelClient} from 'vs/platform/url/common/urlIpc';
import {IURLService} from 'vs/platform/url/common/url';
82
import {ReloadWindowAction} from 'vs/workbench/electron-browser/actions';
83 84

// self registering services
85
import 'vs/platform/opener/browser/opener.contribution';
86

87 88 89 90 91 92 93
/**
 * Services that we require for the Shell
 */
export interface ICoreServices {
	contextService: IWorkspaceContextService;
	eventService: IEventService;
	configurationService: IConfigurationService;
94
	environmentService: IEnvironmentService;
95 96
}

E
Erich Gamma 已提交
97
/**
B
Benjamin Pasero 已提交
98
 * The workbench shell contains the workbench with a rich header containing navigation and the activity bar.
E
Erich Gamma 已提交
99 100 101
 * 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 已提交
102
	private storageService: IStorageService;
103
	private messageService: MessageService;
104
	private eventService: IEventService;
105
	private environmentService:IEnvironmentService;
B
Benjamin Pasero 已提交
106 107 108
	private contextViewService: ContextViewService;
	private windowService: IWindowService;
	private threadService: MainThreadService;
109
	private configurationService: IConfigurationService;
M
Martin Aeschlimann 已提交
110
	private themeService: ThemeService;
111
	private contextService: IWorkspaceContextService;
112
	private telemetryService: ITelemetryService;
E
Erich Gamma 已提交
113 114

	private container: HTMLElement;
115
	private toUnbind: IDisposable[];
E
Erich Gamma 已提交
116 117 118 119 120 121 122 123 124
	private previousErrorValue: string;
	private previousErrorTime: number;
	private content: HTMLElement;
	private contentsContainer: Builder;

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

125
	constructor(container: HTMLElement, workspace: IWorkspace, services: ICoreServices, options: IOptions) {
E
Erich Gamma 已提交
126 127 128
		this.container = container;

		this.workspace = workspace;
129 130 131 132 133
		this.options = options;

		this.contextService = services.contextService;
		this.eventService = services.eventService;
		this.configurationService = services.configurationService;
134
		this.environmentService = services.environmentService;
E
Erich Gamma 已提交
135 136 137 138 139 140 141

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

	private createContents(parent: Builder): Builder {

142
		// ARIA
B
Benjamin Pasero 已提交
143
		aria.setARIAContainer(document.body);
144

E
Erich Gamma 已提交
145
		// Workbench Container
146
		const workbenchContainer = $(parent).div();
E
Erich Gamma 已提交
147 148

		// Instantiation service with services
149
		const [instantiationService, serviceCollection] = this.initServiceCollection(parent.getHTMLElement());
E
Erich Gamma 已提交
150 151

		//crash reporting
152 153 154
		if (!!product.crashReporter) {
			const crashReporter = instantiationService.createInstance(CrashReporter, pkg.version, product.commit);
			crashReporter.start(product.crashReporter);
E
Erich Gamma 已提交
155 156 157
		}

		// Workbench
158
		this.workbench = instantiationService.createInstance(Workbench, workbenchContainer.getHTMLElement(), this.workspace, this.options, serviceCollection);
E
Erich Gamma 已提交
159
		this.workbench.startup({
160 161
			onWorkbenchStarted: (customKeybindingsCount) => {
				this.onWorkbenchStarted(customKeybindingsCount);
E
Erich Gamma 已提交
162 163 164 165 166 167 168 169 170 171
			}
		});

		// 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
172
		const timeoutHandle = setTimeout(() => {
E
Erich Gamma 已提交
173 174 175 176 177 178 179 180 181 182
			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;
	}

183
	private onWorkbenchStarted(customKeybindingsCount: number): void {
B
Benjamin Pasero 已提交
184 185

		// Log to telemetry service
186
		const windowSize = {
B
Benjamin Pasero 已提交
187 188 189 190 191 192 193 194 195 196 197
			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(),
198
				customKeybindingsCount,
M
Martin Aeschlimann 已提交
199
				theme: this.themeService.getColorTheme(),
200
				language: platform.language
B
Benjamin Pasero 已提交
201 202
			});

203
		const workspaceStats: WorkspaceStats = <WorkspaceStats>this.workbench.getInstantiationService().createInstance(WorkspaceStats);
B
Benjamin Pasero 已提交
204
		workspaceStats.reportWorkspaceTags();
J
Joao Moreno 已提交
205 206 207 208

		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 已提交
209 210
	}

211
	private initServiceCollection(container: HTMLElement): [InstantiationService, ServiceCollection] {
J
Joao Moreno 已提交
212 213
		const disposables = new Disposables();

214
		const sharedProcess = connectNet(this.environmentService.sharedIPCHandle);
J
Joao Moreno 已提交
215 216
		sharedProcess.done(client => {
			client.onClose(() => {
217 218 219 220 221 222 223
				this.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 已提交
224
		const mainProcessClient = new ElectronIPCClient(ipcRenderer);
J
Joao Moreno 已提交
225
		disposables.add(mainProcessClient);
J
Joao Moreno 已提交
226

227 228 229 230
		const serviceCollection = new ServiceCollection();
		serviceCollection.set(IEventService, this.eventService);
		serviceCollection.set(IWorkspaceContextService, this.contextService);
		serviceCollection.set(IConfigurationService, this.configurationService);
231
		serviceCollection.set(IEnvironmentService, this.environmentService);
232

233
		const instantiationService = new InstantiationService(serviceCollection, true);
234

235 236 237 238
		this.windowService = instantiationService.createInstance(WindowService);
		serviceCollection.set(IWindowService, this.windowService);

		// Storage
239
		const disableWorkspaceStorage = this.environmentService.extensionTestsPath || (!this.workspace && !this.environmentService.extensionDevelopmentPath); // without workspace or in any extension test, we use inMemory storage unless we develop an extension where we want to preserve state
240 241
		this.storageService = instantiationService.createInstance(Storage, window.localStorage, disableWorkspaceStorage ? inMemoryLocalStorageInstance : window.localStorage);
		serviceCollection.set(IStorageService, this.storageService);
E
Erich Gamma 已提交
242

243
		// Telemetry
244
		if (this.environmentService.isBuilt && !this.environmentService.extensionDevelopmentPath && !!product.enableTelemetry) {
245
			const channel = getDelayedChannel<ITelemetryAppenderChannel>(sharedProcess.then(c => c.getChannel('telemetryAppender')));
246 247
			const commit = product.commit;
			const version = pkg.version;
248

249
			const config: ITelemetryServiceConfig = {
250
				appender: new TelemetryAppenderClient(channel),
251
				commonProperties: resolveWorkbenchCommonProperties(this.storageService, commit, version),
B
Benjamin Pasero 已提交
252
				piiPaths: [this.environmentService.appRoot, this.environmentService.extensionsPath]
253
			};
254

J
Joao Moreno 已提交
255
			const telemetryService = instantiationService.createInstance(TelemetryService, config);
256 257
			this.telemetryService = telemetryService;

J
Joao Moreno 已提交
258
			const errorTelemetry = new ErrorTelemetry(telemetryService);
259
			const idleMonitor = new IdleMonitor(2 * 60 * 1000); // 2 minutes
260

261 262 263 264
			const listener = idleMonitor.onStatusChange(status =>
				this.telemetryService.publicLog(status === UserStatus.Active
					? TelemetryService.IDLE_STOP_EVENT_NAME
					: TelemetryService.IDLE_START_EVENT_NAME
265
				));
266

267
			disposables.add(telemetryService, errorTelemetry, listener, idleMonitor);
268
		} else {
269
			this.telemetryService = NullTelemetryService;
270
		}
E
Erich Gamma 已提交
271

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

274
		this.messageService = instantiationService.createInstance(MessageService, container);
275
		serviceCollection.set(IMessageService, this.messageService);
J
Joao Moreno 已提交
276
		serviceCollection.set(IChoiceService, this.messageService);
E
Erich Gamma 已提交
277

278
		const lifecycleService = instantiationService.createInstance(LifecycleService);
279 280
		this.toUnbind.push(lifecycleService.onShutdown(() => disposables.dispose()));
		serviceCollection.set(ILifecycleService, lifecycleService);
281

282 283
		this.threadService = instantiationService.createInstance(MainThreadService);
		serviceCollection.set(IThreadService, this.threadService);
284

285
		const extensionService = instantiationService.createInstance(MainProcessExtensionService);
286
		serviceCollection.set(IExtensionService, extensionService);
E
Erich Gamma 已提交
287

288 289
		serviceCollection.set(ICommandService, new CommandService(instantiationService, extensionService));

290 291
		this.contextViewService = instantiationService.createInstance(ContextViewService, this.container);
		serviceCollection.set(IContextViewService, this.contextViewService);
E
Erich Gamma 已提交
292

J
Joao Moreno 已提交
293
		const requestService = instantiationService.createInstance(RequestService);
294
		serviceCollection.set(IRequestService, requestService);
E
Erich Gamma 已提交
295

296
		const markerService = instantiationService.createInstance(MarkerService);
297
		serviceCollection.set(IMarkerService, markerService);
E
Erich Gamma 已提交
298

299
		const modeService = instantiationService.createInstance(MainThreadModeServiceImpl);
300
		serviceCollection.set(IModeService, modeService);
301

302
		const modelService = instantiationService.createInstance(ModelServiceImpl);
303
		serviceCollection.set(IModelService, modelService);
304

305
		const compatWorkerService = instantiationService.createInstance(MainThreadCompatWorkerService);
306 307
		serviceCollection.set(ICompatWorkerService, compatWorkerService);

308
		const editorWorkerService = instantiationService.createInstance(EditorWorkerServiceImpl);
309
		serviceCollection.set(IEditorWorkerService, editorWorkerService);
310

311
		const untitledEditorService = instantiationService.createInstance(UntitledEditorService);
312 313 314
		serviceCollection.set(IUntitledEditorService, untitledEditorService);

		this.themeService = instantiationService.createInstance(ThemeService);
315
		serviceCollection.set(IThemeService, this.themeService);
316

317
		const searchService = instantiationService.createInstance(SearchService);
318 319
		serviceCollection.set(ISearchService, searchService);

320
		const codeEditorService = instantiationService.createInstance(CodeEditorServiceImpl);
321 322
		serviceCollection.set(ICodeEditorService, codeEditorService);

323
		const extensionManagementChannel = getDelayedChannel<IExtensionManagementChannel>(sharedProcess.then(c => c.getChannel('extensions')));
J
Joao Moreno 已提交
324
		const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel);
325
		serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient);
326

J
Joao Moreno 已提交
327
		const urlChannel = mainProcessClient.getChannel('url');
J
Joao Moreno 已提交
328
		const urlChannelClient = new URLChannelClient(urlChannel, this.windowService.getWindowId());
J
Joao Moreno 已提交
329 330
		serviceCollection.set(IURLService, urlChannelClient);

331
		return [instantiationService, serviceCollection];
E
Erich Gamma 已提交
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
	}

	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 已提交
360
		this.themeService.initialize(this.container).then(null, error => {
361 362
			errors.onUnexpectedError(error);
		});
E
Erich Gamma 已提交
363 364 365 366 367 368 369 370 371
	}

	private registerListeners(): void {

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

	private writeTimers(): void {
372
		const timers = (<any>window).MonacoEnvironment.timers;
E
Erich Gamma 已提交
373
		if (timers) {
374
			const events: timer.IExistingTimerEvent[] = [];
E
Erich Gamma 已提交
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 407 408 409 410

			// 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 {
411
		const errorMsg = toErrorMessage(error, true);
E
Erich Gamma 已提交
412 413 414 415
		if (!errorMsg) {
			return;
		}

416
		const now = Date.now();
E
Erich Gamma 已提交
417 418 419 420 421 422 423 424 425 426 427
		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
428
		if (error && error.friendlyMessage && this.messageService) {
B
Benjamin Pasero 已提交
429
			this.messageService.show(Severity.Error, error.friendlyMessage);
E
Erich Gamma 已提交
430 431 432
		}
	}

B
Benjamin Pasero 已提交
433
	private layout(): void {
434
		const clArea = $(this.container).getClientArea();
E
Erich Gamma 已提交
435

436
		const contentsSize = new Dimension(clArea.width, clArea.height);
E
Erich Gamma 已提交
437 438
		this.contentsContainer.size(contentsSize.width, contentsSize.height);

B
Benjamin Pasero 已提交
439
		this.contextViewService.layout();
E
Erich Gamma 已提交
440 441 442 443 444 445 446
		this.workbench.layout();
	}

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

B
Benjamin Pasero 已提交
447
	public dispose(): void {
E
Erich Gamma 已提交
448 449 450

		// Workbench
		if (this.workbench) {
B
Benjamin Pasero 已提交
451
			this.workbench.dispose();
E
Erich Gamma 已提交
452 453
		}

B
Benjamin Pasero 已提交
454
		this.contextViewService.dispose();
E
Erich Gamma 已提交
455 456

		// Listeners
J
Joao Moreno 已提交
457
		this.toUnbind = dispose(this.toUnbind);
E
Erich Gamma 已提交
458 459 460 461 462

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