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

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

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

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

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

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

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

		this.workspace = workspace;
		this.configuration = configuration;
130 131 132 133 134
		this.options = options;

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

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

	private createContents(parent: Builder): Builder {

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

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

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

		//crash reporting
		if (!!this.configuration.env.crashReporter) {
154
			const crashReporter = instantiationService.createInstance(CrashReporter, this.configuration.env.version, this.configuration.env.commitHash);
E
Erich Gamma 已提交
155 156 157 158
			crashReporter.start(this.configuration.env.crashReporter);
		}

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

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

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

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

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

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

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

J
Joao Moreno 已提交
215
		const sharedProcess = connectNet(process.env['VSCODE_SHARED_IPC_HOOK']);
216 217 218 219 220 221 222 223 224
		sharedProcess.done(service => {
			service.onClose(() => {
				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 已提交
225
		const mainProcessClient = new ElectronIPCClient(ipcRenderer);
J
Joao Moreno 已提交
226
		disposables.add(mainProcessClient);
J
Joao Moreno 已提交
227

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

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

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

		// Storage
240
		const 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
241 242
		this.storageService = instantiationService.createInstance(Storage, window.localStorage, disableWorkspaceStorage ? inMemoryLocalStorageInstance : window.localStorage);
		serviceCollection.set(IStorageService, this.storageService);
E
Erich Gamma 已提交
243

244
		// Telemetry
245
		if (this.configuration.env.isBuilt && !this.configuration.env.extensionDevelopmentPath && !!this.configuration.env.enableTelemetry) {
246
			const channel = getDelayedChannel<ITelemetryAppenderChannel>(sharedProcess.then(c => c.getChannel('telemetryAppender')));
247 248 249
			const commit = this.contextService.getConfiguration().env.commitHash;
			const version = this.contextService.getConfiguration().env.version;

250
			const config: ITelemetryServiceConfig = {
251
				appender: new TelemetryAppenderClient(channel),
252
				commonProperties: resolveWorkbenchCommonProperties(this.storageService, commit, version),
253
				piiPaths: [this.configuration.env.appRoot, this.configuration.env.userExtensionsHome]
254
			};
255

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

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

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

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

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

275 276
		this.messageService = instantiationService.createInstance(MessageService);
		serviceCollection.set(IMessageService, this.messageService);
E
Erich Gamma 已提交
277

278
		const fileService = disposables.add(instantiationService.createInstance(FileService));
279
		serviceCollection.set(IFileService, fileService);
E
Erich Gamma 已提交
280

281
		const lifecycleService = instantiationService.createInstance(LifecycleService);
282 283
		this.toUnbind.push(lifecycleService.onShutdown(() => disposables.dispose()));
		serviceCollection.set(ILifecycleService, lifecycleService);
284

285 286
		this.threadService = instantiationService.createInstance(MainThreadService);
		serviceCollection.set(IThreadService, this.threadService);
287

288
		const extensionService = instantiationService.createInstance(MainProcessExtensionService);
289
		serviceCollection.set(IExtensionService, extensionService);
E
Erich Gamma 已提交
290

291 292
		serviceCollection.set(ICommandService, new CommandService(instantiationService, extensionService));

293 294
		this.contextViewService = instantiationService.createInstance(ContextViewService, this.container);
		serviceCollection.set(IContextViewService, this.contextViewService);
E
Erich Gamma 已提交
295

J
Joao Moreno 已提交
296
		const requestService = instantiationService.createInstance(RequestService);
297
		serviceCollection.set(IRequestService, requestService);
E
Erich Gamma 已提交
298

299
		const markerService = instantiationService.createInstance(MarkerService);
300
		serviceCollection.set(IMarkerService, markerService);
E
Erich Gamma 已提交
301

302
		const modeService = instantiationService.createInstance(MainThreadModeServiceImpl);
303
		serviceCollection.set(IModeService, modeService);
304

305
		const modelService = instantiationService.createInstance(ModelServiceImpl);
306
		serviceCollection.set(IModelService, modelService);
307

308
		const compatWorkerService = instantiationService.createInstance(MainThreadCompatWorkerService);
309 310
		serviceCollection.set(ICompatWorkerService, compatWorkerService);

311
		const editorWorkerService = instantiationService.createInstance(EditorWorkerServiceImpl);
312
		serviceCollection.set(IEditorWorkerService, editorWorkerService);
313

314
		const untitledEditorService = instantiationService.createInstance(UntitledEditorService);
315 316 317
		serviceCollection.set(IUntitledEditorService, untitledEditorService);

		this.themeService = instantiationService.createInstance(ThemeService);
318
		serviceCollection.set(IThemeService, this.themeService);
319

320
		const searchService = instantiationService.createInstance(SearchService);
321 322
		serviceCollection.set(ISearchService, searchService);

323
		const codeEditorService = instantiationService.createInstance(CodeEditorServiceImpl);
324 325
		serviceCollection.set(ICodeEditorService, codeEditorService);

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

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

334
		return [instantiationService, serviceCollection];
E
Erich Gamma 已提交
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 360 361 362
	}

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

	private registerListeners(): void {

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

	private writeTimers(): void {
375
		const timers = (<any>window).MonacoEnvironment.timers;
E
Erich Gamma 已提交
376
		if (timers) {
377
			const events: timer.IExistingTimerEvent[] = [];
E
Erich Gamma 已提交
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 411 412 413 414 415 416 417 418 419 420 421 422 423 424

			// 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 {
425
		const errorMsg = errors.toErrorMessage(error, true);
E
Erich Gamma 已提交
426 427 428 429
		if (!errorMsg) {
			return;
		}

430
		const now = Date.now();
E
Erich Gamma 已提交
431 432 433 434 435 436 437 438 439 440 441
		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
442
		if (error && error.friendlyMessage && this.messageService) {
B
Benjamin Pasero 已提交
443
			this.messageService.show(Severity.Error, error.friendlyMessage);
E
Erich Gamma 已提交
444 445 446 447
		}
	}

	public layout(): void {
448
		const clArea = $(this.container).getClientArea();
E
Erich Gamma 已提交
449

450
		const contentsSize = new Dimension(clArea.width, clArea.height);
E
Erich Gamma 已提交
451 452
		this.contentsContainer.size(contentsSize.width, contentsSize.height);

B
Benjamin Pasero 已提交
453
		this.contextViewService.layout();
E
Erich Gamma 已提交
454 455 456 457 458 459 460
		this.workbench.layout();
	}

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

B
Benjamin Pasero 已提交
461
	public dispose(): void {
E
Erich Gamma 已提交
462 463 464

		// Workbench
		if (this.workbench) {
B
Benjamin Pasero 已提交
465
			this.workbench.dispose();
E
Erich Gamma 已提交
466 467
		}

B
Benjamin Pasero 已提交
468
		this.contextViewService.dispose();
E
Erich Gamma 已提交
469 470

		// Listeners
J
Joao Moreno 已提交
471
		this.toUnbind = dispose(this.toUnbind);
E
Erich Gamma 已提交
472 473 474 475 476

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