workbench.ts 33.8 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  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/workbench';
9

J
Johannes Rieken 已提交
10 11
import { TPromise, ValueCallback } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
E
Erich Gamma 已提交
12 13
import strings = require('vs/base/common/strings');
import DOM = require('vs/base/browser/dom');
J
Johannes Rieken 已提交
14 15
import { Box, Builder, $ } from 'vs/base/browser/builder';
import { Delayer } from 'vs/base/common/async';
E
Erich Gamma 已提交
16 17 18
import assert = require('vs/base/common/assert');
import timer = require('vs/base/common/timer');
import errors = require('vs/base/common/errors');
J
Johannes Rieken 已提交
19 20 21 22
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Registry } from 'vs/platform/platform';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { IOptions } from 'vs/workbench/common/options';
23
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
J
Johannes Rieken 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IEditorRegistry, Extensions as EditorExtensions, TextEditorOptions, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { HistoryService } from 'vs/workbench/services/history/browser/history';
import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart';
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart';
import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart';
import { WorkbenchLayout, LayoutOptions } from 'vs/workbench/browser/layout';
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actionBarRegistry';
import { ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet';
import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import { DiffEditorInput, toDiffLabel } from 'vs/workbench/common/editor/diffEditorInput';
import { getServices } from 'vs/platform/instantiation/common/extensions';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService';
import { Position, Parts, IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ContextMenuService } from 'vs/workbench/services/contextview/electron-browser/contextmenuService';
import { WorkbenchKeybindingService } from 'vs/workbench/services/keybinding/electron-browser/keybindingService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IActivityService } from 'vs/workbench/services/activity/common/activityService';
import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService';
import { FileService } from 'vs/workbench/services/files/electron-browser/fileService';
import { IFileService } from 'vs/platform/files/common/files';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/node/configurationResolverService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { TextFileService } from 'vs/workbench/services/textfile/electron-browser/textFileService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
B
Benjamin Pasero 已提交
70
import { ITextModelResolverService, TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
J
Johannes Rieken 已提交
71 72 73 74 75 76 77 78 79
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IMessageService } from 'vs/platform/message/common/message';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IMenuService } from 'vs/platform/actions/common/actions';
import { MenuService } from 'vs/platform/actions/common/menuService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
E
Erich Gamma 已提交
80

A
Alex Dima 已提交
81 82
export const MessagesVisibleContext = new RawContextKey<boolean>('globalMessageVisible', false);
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
J
Johannes Rieken 已提交
83
export const NoEditorsVisibleContext: ContextKeyExpr = EditorsVisibleContext.toNegated();
A
Alex Dima 已提交
84

E
Erich Gamma 已提交
85 86 87
interface WorkbenchParams {
	workspace?: IWorkspace;
	options: IOptions;
88
	serviceCollection: ServiceCollection;
E
Erich Gamma 已提交
89 90 91 92
}

export interface IWorkbenchCallbacks {
	onServicesCreated?: () => void;
93
	onWorkbenchStarted?: (customKeybindingsCount: number) => void;
E
Erich Gamma 已提交
94 95
}

96 97 98 99 100 101 102 103 104
const Identifiers = {
	WORKBENCH_CONTAINER: 'workbench.main.container',
	ACTIVITYBAR_PART: 'workbench.parts.activitybar',
	SIDEBAR_PART: 'workbench.parts.sidebar',
	PANEL_PART: 'workbench.parts.panel',
	EDITOR_PART: 'workbench.parts.editor',
	STATUSBAR_PART: 'workbench.parts.statusbar'
};

E
Erich Gamma 已提交
105
/**
B
Benjamin Pasero 已提交
106
 * The workbench creates and lays out all parts that make up the workbench.
E
Erich Gamma 已提交
107 108 109 110
 */
export class Workbench implements IPartService {

	private static sidebarHiddenSettingKey = 'workbench.sidebar.hidden';
111
	private static sidebarRestoreSettingKey = 'workbench.sidebar.restore';
I
isidor 已提交
112
	private static panelHiddenSettingKey = 'workbench.panel.hidden';
E
Erich Gamma 已提交
113

114
	private static sidebarPositionConfigurationKey = 'workbench.sideBar.location';
115
	private static statusbarVisibleConfigurationKey = 'workbench.statusBar.visible';
116

117
	public _serviceBrand: any;
E
Erich Gamma 已提交
118 119 120 121 122 123 124 125

	private container: HTMLElement;
	private workbenchParams: WorkbenchParams;
	private workbenchContainer: Builder;
	private workbench: Builder;
	private workbenchStarted: boolean;
	private workbenchCreated: boolean;
	private workbenchShutdown: boolean;
126
	private editorService: WorkbenchEditorService;
127
	private contextKeyService: IContextKeyService;
128
	private keybindingService: IKeybindingService;
129
	private configurationEditingService: IConfigurationEditingService;
E
Erich Gamma 已提交
130 131
	private activitybarPart: ActivitybarPart;
	private sidebarPart: SidebarPart;
I
isidor 已提交
132
	private panelPart: PanelPart;
E
Erich Gamma 已提交
133 134 135 136 137 138 139 140 141 142
	private editorPart: EditorPart;
	private statusbarPart: StatusbarPart;
	private quickOpen: QuickOpenController;
	private workbenchLayout: WorkbenchLayout;
	private toDispose: IDisposable[];
	private toShutdown: { shutdown: () => void; }[];
	private callbacks: IWorkbenchCallbacks;
	private creationPromise: TPromise<boolean>;
	private creationPromiseComplete: ValueCallback;
	private sideBarHidden: boolean;
143
	private statusBarHidden: boolean;
E
Erich Gamma 已提交
144
	private sideBarPosition: Position;
I
isidor 已提交
145
	private panelHidden: boolean;
E
Erich Gamma 已提交
146
	private editorBackgroundDelayer: Delayer<void>;
A
Alex Dima 已提交
147 148
	private messagesVisibleContext: IContextKey<boolean>;
	private editorsVisibleContext: IContextKey<boolean>;
149
	private hasFilesToCreateOpenOrDiff: boolean;
E
Erich Gamma 已提交
150

151 152 153 154 155
	constructor(
		container: HTMLElement,
		workspace: IWorkspace,
		options: IOptions,
		serviceCollection: ServiceCollection,
156 157
		@IInstantiationService private instantiationService: IInstantiationService,
		@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
158
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
159 160 161
		@IStorageService private storageService: IStorageService,
		@ILifecycleService private lifecycleService: ILifecycleService,
		@IMessageService private messageService: IMessageService,
162
		@IConfigurationService private configurationService: IConfigurationService,
163
		@ITelemetryService private telemetryService: ITelemetryService,
D
Daniel Imms 已提交
164
		@IEnvironmentService private environmentService: IEnvironmentService
165
	) {
166
		this.container = container;
E
Erich Gamma 已提交
167 168 169

		this.workbenchParams = {
			workspace: workspace,
170
			options,
171
			serviceCollection
E
Erich Gamma 已提交
172 173
		};

D
Daniel Imms 已提交
174
		this.hasFilesToCreateOpenOrDiff = (options.filesToCreate && options.filesToCreate.length > 0) || (options.filesToOpen && options.filesToOpen.length > 0) || (options.filesToDiff && options.filesToDiff.length > 0);
175

E
Erich Gamma 已提交
176 177 178 179
		this.toDispose = [];
		this.toShutdown = [];
		this.editorBackgroundDelayer = new Delayer<void>(50);

180
		this.creationPromise = new TPromise<boolean>(c => {
E
Erich Gamma 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
			this.creationPromiseComplete = c;
		});
	}

	/**
	 * Starts the workbench and creates the HTML elements on the container. A workbench can only be started
	 * once. Use the shutdown function to free up resources created by the workbench on startup.
	 */
	public startup(callbacks?: IWorkbenchCallbacks): void {
		assert.ok(!this.workbenchStarted, 'Can not start a workbench that was already started');
		assert.ok(!this.workbenchShutdown, 'Can not start a workbench that was shutdown');

		try {
			this.workbenchStarted = true;
			this.callbacks = callbacks;

			// Create Workbench
			this.createWorkbench();

			// Services
			this.initServices();
			if (this.callbacks && this.callbacks.onServicesCreated) {
				this.callbacks.onServicesCreated();
			}

206
			// Contexts
207 208
			this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService);
			this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
209

E
Erich Gamma 已提交
210 211 212 213 214 215 216 217 218 219 220 221
			// Register Listeners
			this.registerListeners();

			// Settings
			this.initSettings();

			// Create Workbench and Parts
			this.renderWorkbench();

			// Workbench Layout
			this.createWorkbenchLayout();

I
isidor 已提交
222
			// Load composits and editors in parallel
B
Benjamin Pasero 已提交
223
			const compositeAndEditorPromises: TPromise<any>[] = [];
E
Erich Gamma 已提交
224

B
Benjamin Pasero 已提交
225
			// Load Viewlet
226
			const viewletRegistry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets);
227
			let viewletId = viewletRegistry.getDefaultViewletId();
228
			if (this.shouldRestoreSidebar()) {
229
				viewletId = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, viewletId); // help developers and restore last view
230 231 232
			}

			if (!this.sideBarHidden && !!viewletId) {
B
Benjamin Pasero 已提交
233
				const viewletTimerEvent = timer.start(timer.Topic.STARTUP, strings.format('Opening Viewlet: {0}', viewletId));
I
isidor 已提交
234
				compositeAndEditorPromises.push(this.sidebarPart.openViewlet(viewletId, false).then(() => viewletTimerEvent.stop()));
E
Erich Gamma 已提交
235 236
			}

B
Benjamin Pasero 已提交
237
			// Load Panel
238
			const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
239 240
			const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId());
			if (!this.panelHidden && !!panelId) {
I
isidor 已提交
241
				compositeAndEditorPromises.push(this.panelPart.openPanel(panelId, false));
242 243
			}

B
Benjamin Pasero 已提交
244
			// Load Editors
B
Benjamin Pasero 已提交
245
			const editorTimerEvent = timer.start(timer.Topic.STARTUP, strings.format('Restoring Editor(s)'));
246
			compositeAndEditorPromises.push(this.resolveEditorsToOpen().then(inputsWithOptions => {
B
Benjamin Pasero 已提交
247 248
				let editorOpenPromise: TPromise<BaseEditor[]>;
				if (inputsWithOptions.length) {
249
					const editors = inputsWithOptions.map(inputWithOptions => {
B
Benjamin Pasero 已提交
250 251 252
						return {
							input: inputWithOptions.input,
							options: inputWithOptions.options,
253
							position: EditorPosition.ONE
B
Benjamin Pasero 已提交
254
						};
255 256
					});

B
Benjamin Pasero 已提交
257 258
					editorOpenPromise = this.editorPart.openEditors(editors);
				} else {
259
					editorOpenPromise = this.editorPart.restoreEditors();
260
				}
261 262

				return editorOpenPromise.then(() => {
263
					this.onEditorsChanged(); // make sure we show the proper background in the editor area
E
Erich Gamma 已提交
264 265 266 267 268
					editorTimerEvent.stop();
				});
			}));

			// Flag workbench as created once done
269
			const workbenchDone = (error?: Error) => {
E
Erich Gamma 已提交
270 271 272
				this.workbenchCreated = true;
				this.creationPromiseComplete(true);

B
Benjamin Pasero 已提交
273
				if (this.callbacks && this.callbacks.onWorkbenchStarted) {
274
					this.callbacks.onWorkbenchStarted(this.keybindingService.customKeybindingsCount());
B
Benjamin Pasero 已提交
275
				}
276 277 278 279 280 281

				if (error) {
					errors.onUnexpectedError(error);
				}
			};

B
polish  
Benjamin Pasero 已提交
282
			// Join viewlet, panel and editor promises
283
			TPromise.join(compositeAndEditorPromises).then(() => workbenchDone(), error => workbenchDone(error));
E
Erich Gamma 已提交
284 285 286
		} catch (error) {

			// Print out error
287
			console.error(toErrorMessage(error, true));
E
Erich Gamma 已提交
288 289 290 291 292 293

			// Rethrow
			throw error;
		}
	}

B
Benjamin Pasero 已提交
294 295 296
	private resolveEditorsToOpen(): TPromise<{ input: EditorInput, options?: EditorOptions }[]> {

		// Files to open, diff or create
297 298
		if (this.hasFilesToCreateOpenOrDiff) {
			const wbopt = this.workbenchParams.options;
B
Benjamin Pasero 已提交
299 300 301
			const filesToCreate = wbopt.filesToCreate || [];
			const filesToOpen = wbopt.filesToOpen || [];
			const filesToDiff = wbopt.filesToDiff;
B
Benjamin Pasero 已提交
302 303 304

			// Files to diff is exclusive
			if (filesToDiff && filesToDiff.length) {
305
				return TPromise.join<EditorInput>(filesToDiff.map(resourceInput => this.editorService.createInput(resourceInput))).then((inputsToDiff) => {
B
Benjamin Pasero 已提交
306 307 308 309 310 311
					return [{ input: new DiffEditorInput(toDiffLabel(filesToDiff[0].resource, filesToDiff[1].resource, this.contextService), null, inputsToDiff[0], inputsToDiff[1]) }];
				});
			}

			// Otherwise: Open/Create files
			else {
B
Benjamin Pasero 已提交
312 313
				const inputs: EditorInput[] = [];
				const options: EditorOptions[] = [];
B
Benjamin Pasero 已提交
314 315

				// Files to create
316
				inputs.push(...filesToCreate.map(resourceInput => this.untitledEditorService.createOrGet(resourceInput.resource)));
B
Benjamin Pasero 已提交
317 318 319
				options.push(...filesToCreate.map(r => null)); // fill empty options for files to create because we dont have options there

				// Files to open
D
Daniel Imms 已提交
320
				return TPromise.join<EditorInput>(filesToOpen.map(resourceInput => this.editorService.createInput(resourceInput))).then((inputsToOpen) => {
B
Benjamin Pasero 已提交
321
					inputs.push(...inputsToOpen);
D
Daniel Imms 已提交
322
					options.push(...filesToOpen.map(resourceInput => TextEditorOptions.from(resourceInput)));
B
Benjamin Pasero 已提交
323 324 325 326 327 328 329

					return inputs.map((input, index) => { return { input, options: options[index] }; });
				});
			}
		}

		// Empty workbench
330 331
		else if (!this.workbenchParams.workspace && this.telemetryService.getExperiments().openUntitledFile) {
			// some first time users will not have an untiled file; returning users will always have one
B
Benjamin Pasero 已提交
332 333 334 335 336 337
			return TPromise.as([{ input: this.untitledEditorService.createOrGet() }]);
		}

		return TPromise.as([]);
	}

E
Erich Gamma 已提交
338
	private initServices(): void {
339 340
		const {serviceCollection} = this.workbenchParams;

341
		this.toDispose.push(this.lifecycleService.onShutdown(this.shutdownComponents, this));
E
Erich Gamma 已提交
342 343

		// Services we contribute
344
		serviceCollection.set(IPartService, this);
E
Erich Gamma 已提交
345

346 347 348 349 350 351 352
		// Status bar
		this.statusbarPart = this.instantiationService.createInstance(StatusbarPart, Identifiers.STATUSBAR_PART);
		this.toDispose.push(this.statusbarPart);
		this.toShutdown.push(this.statusbarPart);
		serviceCollection.set(IStatusbarService, this.statusbarPart);

		// Keybindings
A
Alex Dima 已提交
353
		this.contextKeyService = this.instantiationService.createInstance(ContextKeyService);
354
		serviceCollection.set(IContextKeyService, this.contextKeyService);
355

A
Alex Dima 已提交
356
		this.keybindingService = this.instantiationService.createInstance(WorkbenchKeybindingService, <any>window);
357
		serviceCollection.set(IKeybindingService, this.keybindingService);
A
Alex Dima 已提交
358

359
		// Context Menu
360
		serviceCollection.set(IContextMenuService, this.instantiationService.createInstance(ContextMenuService));
361

362 363
		// Menus/Actions
		serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
364

E
Erich Gamma 已提交
365
		// Viewlet service (sidebar part)
366
		this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART);
E
Erich Gamma 已提交
367 368
		this.toDispose.push(this.sidebarPart);
		this.toShutdown.push(this.sidebarPart);
369
		serviceCollection.set(IViewletService, this.sidebarPart);
E
Erich Gamma 已提交
370

I
isidor 已提交
371
		// Panel service (panel part)
372
		this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART);
I
isidor 已提交
373 374
		this.toDispose.push(this.panelPart);
		this.toShutdown.push(this.panelPart);
375
		serviceCollection.set(IPanelService, this.panelPart);
I
isidor 已提交
376

E
Erich Gamma 已提交
377
		// Activity service (activitybar part)
378
		this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
E
Erich Gamma 已提交
379 380
		this.toDispose.push(this.activitybarPart);
		this.toShutdown.push(this.activitybarPart);
381
		serviceCollection.set(IActivityService, this.activitybarPart);
E
Erich Gamma 已提交
382 383

		// Editor service (editor part)
384
		this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasFilesToCreateOpenOrDiff);
E
Erich Gamma 已提交
385 386
		this.toDispose.push(this.editorPart);
		this.toShutdown.push(this.editorPart);
387
		this.editorService = this.instantiationService.createInstance(WorkbenchEditorService, this.editorPart);
388
		serviceCollection.set(IWorkbenchEditorService, this.editorService);
389
		serviceCollection.set(IEditorGroupService, this.editorPart);
E
Erich Gamma 已提交
390

391
		// File Service
392 393 394
		const fileService = this.instantiationService.createInstance(FileService);
		serviceCollection.set(IFileService, fileService);

395
		// History
396
		serviceCollection.set(IHistoryService, this.instantiationService.createInstance(HistoryService));
397

398 399 400
		// Text File Service
		serviceCollection.set(ITextFileService, this.instantiationService.createInstance(TextFileService));

B
Benjamin Pasero 已提交
401 402 403
		// Text Model Resolver Service
		serviceCollection.set(ITextModelResolverService, this.instantiationService.createInstance(TextModelResolverService));

404
		// Configuration Editing
405 406
		this.configurationEditingService = this.instantiationService.createInstance(ConfigurationEditingService);
		serviceCollection.set(IConfigurationEditingService, this.configurationEditingService);
407

408
		// Configuration Resolver
409 410
		const workspace = this.contextService.getWorkspace();
		const configurationResolverService = this.instantiationService.createInstance(ConfigurationResolverService, workspace ? workspace.resource : null, process.env);
411 412
		serviceCollection.set(IConfigurationResolverService, configurationResolverService);

E
Erich Gamma 已提交
413
		// Quick open service (quick open controller)
414
		this.quickOpen = this.instantiationService.createInstance(QuickOpenController);
E
Erich Gamma 已提交
415 416
		this.toDispose.push(this.quickOpen);
		this.toShutdown.push(this.quickOpen);
417
		serviceCollection.set(IQuickOpenService, this.quickOpen);
E
Erich Gamma 已提交
418

B
polish  
Benjamin Pasero 已提交
419
		// Contributed services
B
Benjamin Pasero 已提交
420
		const contributedServices = getServices();
E
Erich Gamma 已提交
421
		for (let contributedService of contributedServices) {
422
			serviceCollection.set(contributedService.id, contributedService.descriptor);
E
Erich Gamma 已提交
423 424 425
		}

		// Set the some services to registries that have been created eagerly
426 427 428
		Registry.as<IActionBarRegistry>(ActionBarExtensions.Actionbar).setInstantiationService(this.instantiationService);
		Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).setInstantiationService(this.instantiationService);
		Registry.as<IEditorRegistry>(EditorExtensions.Editors).setInstantiationService(this.instantiationService);
E
Erich Gamma 已提交
429 430 431 432 433 434
	}

	private initSettings(): void {

		// Sidebar visibility
		this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, false);
B
Benjamin Pasero 已提交
435
		if (!this.contextService.getWorkspace()) {
436 437
			// some first time users will see a sidebar; returning users will not see the sidebar
			this.sideBarHidden = !this.telemetryService.getExperiments().showDefaultViewlet;
E
Erich Gamma 已提交
438 439
		}

440
		const viewletRegistry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets);
I
isidor 已提交
441
		if (!viewletRegistry.getDefaultViewletId()) {
B
Benjamin Pasero 已提交
442
			this.sideBarHidden = true; // can only hide sidebar if we dont have a default Viewlet id
E
Erich Gamma 已提交
443 444
		}

I
isidor 已提交
445
		// Panel part visibility
446
		const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
I
isidor 已提交
447
		this.panelHidden = this.storageService.getBoolean(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE, true);
B
Benjamin Pasero 已提交
448
		if (!this.contextService.getWorkspace() || !panelRegistry.getDefaultPanelId()) {
449
			this.panelHidden = true; // we hide panel part in single-file-mode or if there is no default panel
I
isidor 已提交
450
		}
I
isidor 已提交
451

E
Erich Gamma 已提交
452
		// Sidebar position
453 454
		const sideBarPosition = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
		this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT;
B
Benjamin Pasero 已提交
455 456

		// Statusbar visibility
457 458
		const statusBarVisible = this.configurationService.lookup<string>(Workbench.statusbarVisibleConfigurationKey).value;
		this.statusBarHidden = !statusBarVisible;
E
Erich Gamma 已提交
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
	}

	/**
	 * Returns whether the workbench has been started.
	 */
	public isStarted(): boolean {
		return this.workbenchStarted && !this.workbenchShutdown;
	}

	/**
	 * Returns whether the workbench has been fully created.
	 */
	public isCreated(): boolean {
		return this.workbenchCreated && this.workbenchStarted;
	}

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

	public hasFocus(part: Parts): boolean {
B
Benjamin Pasero 已提交
480
		const activeElement = document.activeElement;
E
Erich Gamma 已提交
481 482 483 484
		if (!activeElement) {
			return false;
		}

485 486 487 488 489
		const container = this.getContainer(part);
		return DOM.isAncestor(activeElement, container);
	}

	public getContainer(part: Parts): HTMLElement {
E
Erich Gamma 已提交
490 491 492 493 494 495 496 497
		let container: Builder = null;
		switch (part) {
			case Parts.ACTIVITYBAR_PART:
				container = this.activitybarPart.getContainer();
				break;
			case Parts.SIDEBAR_PART:
				container = this.sidebarPart.getContainer();
				break;
I
isidor 已提交
498 499 500
			case Parts.PANEL_PART:
				container = this.panelPart.getContainer();
				break;
E
Erich Gamma 已提交
501 502 503 504 505 506 507
			case Parts.EDITOR_PART:
				container = this.editorPart.getContainer();
				break;
			case Parts.STATUSBAR_PART:
				container = this.statusbarPart.getContainer();
				break;
		}
508
		return container && container.getHTMLElement();
E
Erich Gamma 已提交
509 510 511
	}

	public isVisible(part: Parts): boolean {
B
Benjamin Pasero 已提交
512 513 514 515 516 517 518
		switch (part) {
			case Parts.SIDEBAR_PART:
				return !this.sideBarHidden;
			case Parts.PANEL_PART:
				return !this.panelHidden;
			case Parts.STATUSBAR_PART:
				return !this.statusBarHidden;
519
		}
E
Erich Gamma 已提交
520 521 522 523

		return true; // any other part cannot be hidden
	}

524 525 526 527
	public isStatusBarHidden(): boolean {
		return this.statusBarHidden;
	}

528
	private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void {
529 530 531 532
		this.statusBarHidden = hidden;

		// Layout
		if (!skipLayout) {
B
Benjamin Pasero 已提交
533
			this.workbenchLayout.layout({ forceStyleReCompute: true });
534 535 536
		}
	}

E
Erich Gamma 已提交
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
	public isSideBarHidden(): boolean {
		return this.sideBarHidden;
	}

	public setSideBarHidden(hidden: boolean, skipLayout?: boolean): void {
		this.sideBarHidden = hidden;

		// Adjust CSS
		if (hidden) {
			this.workbench.addClass('nosidebar');
		} else {
			this.workbench.removeClass('nosidebar');
		}

		// Layout
		if (!skipLayout) {
B
Benjamin Pasero 已提交
553
			this.workbenchLayout.layout({ forceStyleReCompute: true });
E
Erich Gamma 已提交
554 555
		}

B
Benjamin Pasero 已提交
556
		// If sidebar becomes hidden, also hide the current active Viewlet if any
E
Erich Gamma 已提交
557 558 559
		if (hidden && this.sidebarPart.getActiveViewlet()) {
			this.sidebarPart.hideActiveViewlet();

B
Benjamin Pasero 已提交
560 561 562 563 564 565 566 567
			const activeEditor = this.editorPart.getActiveEditor();
			const activePanel = this.panelPart.getActivePanel();

			// Pass Focus to Editor or Panel if Sidebar is now hidden
			if (this.hasFocus(Parts.PANEL_PART) && activePanel) {
				activePanel.focus();
			} else if (activeEditor) {
				activeEditor.focus();
E
Erich Gamma 已提交
568 569 570
			}
		}

B
Benjamin Pasero 已提交
571
		// If sidebar becomes visible, show last active Viewlet or default viewlet
E
Erich Gamma 已提交
572
		else if (!hidden && !this.sidebarPart.getActiveViewlet()) {
573
			const registry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets);
B
Benjamin Pasero 已提交
574
			const viewletToOpen = this.sidebarPart.getLastActiveViewletId() || registry.getDefaultViewletId();
E
Erich Gamma 已提交
575 576 577 578 579 580 581 582 583
			if (viewletToOpen) {
				this.sidebarPart.openViewlet(viewletToOpen, true).done(null, errors.onUnexpectedError);
			}
		}

		// Remember in settings
		this.storageService.store(Workbench.sidebarHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE);
	}

I
isidor 已提交
584 585
	public isPanelHidden(): boolean {
		return this.panelHidden;
I
isidor 已提交
586 587
	}

B
Benjamin Pasero 已提交
588
	public setPanelHidden(hidden: boolean, skipLayout?: boolean): void {
I
isidor 已提交
589
		this.panelHidden = hidden;
I
isidor 已提交
590

591 592 593 594 595 596 597
		// Adjust CSS
		if (hidden) {
			this.workbench.addClass('nopanel');
		} else {
			this.workbench.removeClass('nopanel');
		}

I
isidor 已提交
598 599
		// Layout
		if (!skipLayout) {
B
Benjamin Pasero 已提交
600
			this.workbenchLayout.layout({ forceStyleReCompute: true });
I
isidor 已提交
601 602 603 604 605 606 607
		}

		// If panel part becomes hidden, also hide the current active panel if any
		if (hidden && this.panelPart.getActivePanel()) {
			this.panelPart.hideActivePanel();

			// Pass Focus to Editor if Panel part is now hidden
B
Benjamin Pasero 已提交
608
			const editor = this.editorPart.getActiveEditor();
I
isidor 已提交
609 610 611 612 613 614 615
			if (editor) {
				editor.focus();
			}
		}

		// If panel part becomes visible, show last active panel or default panel
		else if (!hidden && !this.panelPart.getActivePanel()) {
616
			const registry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
B
Benjamin Pasero 已提交
617
			const panelToOpen = this.panelPart.getLastActivePanelId() || registry.getDefaultPanelId();
I
isidor 已提交
618 619 620 621 622 623
			if (panelToOpen) {
				this.panelPart.openPanel(panelToOpen, true).done(null, errors.onUnexpectedError);
			}
		}

		// Remember in settings
I
isidor 已提交
624
		this.storageService.store(Workbench.panelHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE);
I
isidor 已提交
625 626
	}

I
isidor 已提交
627
	public toggleMaximizedPanel(): void {
B
Benjamin Pasero 已提交
628
		this.workbenchLayout.layout({ forceStyleReCompute: true, toggleMaximizedPanel: true });
I
isidor 已提交
629 630
	}

E
Erich Gamma 已提交
631 632 633 634
	public getSideBarPosition(): Position {
		return this.sideBarPosition;
	}

635
	private setSideBarPosition(position: Position): void {
E
Erich Gamma 已提交
636 637 638 639
		if (this.sideBarHidden) {
			this.setSideBarHidden(false, true /* Skip Layout */);
		}

B
Benjamin Pasero 已提交
640 641
		const newPositionValue = (position === Position.LEFT) ? 'left' : 'right';
		const oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right';
E
Erich Gamma 已提交
642 643 644 645 646 647 648 649 650
		this.sideBarPosition = position;

		// Adjust CSS
		this.activitybarPart.getContainer().removeClass(oldPositionValue);
		this.sidebarPart.getContainer().removeClass(oldPositionValue);
		this.activitybarPart.getContainer().addClass(newPositionValue);
		this.sidebarPart.getContainer().addClass(newPositionValue);

		// Layout
B
Benjamin Pasero 已提交
651
		this.workbenchLayout.layout({ forceStyleReCompute: true });
E
Erich Gamma 已提交
652 653
	}

B
Benjamin Pasero 已提交
654
	public dispose(): void {
E
Erich Gamma 已提交
655
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
656
			this.shutdownComponents();
E
Erich Gamma 已提交
657 658 659
			this.workbenchShutdown = true;
		}

J
Joao Moreno 已提交
660
		this.toDispose = dispose(this.toDispose);
E
Erich Gamma 已提交
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
	}

	/**
	 * Asks the workbench and all its UI components inside to lay out according to
	 * the containers dimension the workbench is living in.
	 */
	public layout(): void {
		if (this.isStarted()) {
			this.workbenchLayout.layout();
		}
	}

	private shutdownComponents(): void {

		// Pass shutdown on to each participant
		this.toShutdown.forEach(s => s.shutdown());
	}

	private registerListeners(): void {

		// Listen to editor changes
682
		this.toDispose.push(this.editorPart.onEditorsChanged(() => this.onEditorsChanged()));
683 684

		// Handle message service and quick open events
685 686
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesShowing(() => this.messagesVisibleContext.set(true)));
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesCleared(() => this.messagesVisibleContext.reset()));
687

688 689 690 691 692
		this.toDispose.push(this.quickOpen.onShow(() => (<WorkbenchMessageService>this.messageService).suspend())); // when quick open is open, don't show messages behind
		this.toDispose.push(this.quickOpen.onHide(() => (<WorkbenchMessageService>this.messageService).resume()));  // resume messages once quick open is closed again

		// Configuration changes
		this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.onDidUpdateConfiguration()));
E
Erich Gamma 已提交
693 694
	}

695
	private onEditorsChanged(): void {
B
Benjamin Pasero 已提交
696
		const visibleEditors = this.editorService.getVisibleEditors().length;
E
Erich Gamma 已提交
697 698 699 700

		// We update the editorpart class to indicate if an editor is opened or not
		// through a delay to accomodate for fast editor switching

701
		const editorContainer = this.editorPart.getContainer();
E
Erich Gamma 已提交
702
		if (visibleEditors === 0) {
703
			this.editorsVisibleContext.reset();
704
			this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty'));
E
Erich Gamma 已提交
705
		} else {
706
			this.editorsVisibleContext.set(true);
707
			this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty'));
E
Erich Gamma 已提交
708 709 710
		}
	}

711 712
	private onDidUpdateConfiguration(): void {
		const newSidebarPositionValue = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
713
		const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT;
714 715 716
		if (newSidebarPosition !== this.getSideBarPosition()) {
			this.setSideBarPosition(newSidebarPosition);
		}
717 718 719 720 721

		const newStatusbarHiddenValue = !this.configurationService.lookup<boolean>(Workbench.statusbarVisibleConfigurationKey).value;
		if (newStatusbarHiddenValue !== this.isStatusBarHidden()) {
			this.setStatusBarHidden(newStatusbarHiddenValue);
		}
722 723
	}

E
Erich Gamma 已提交
724
	private createWorkbenchLayout(): void {
B
Benjamin Pasero 已提交
725
		const options = new LayoutOptions();
E
Erich Gamma 已提交
726 727 728 729 730
		options.setMargin(new Box(0, 0, 0, 0));

		this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout,
			$(this.container),							// Parent
			this.workbench,								// Workbench Container
I
isidor 已提交
731 732 733 734 735 736 737
			{
				activitybar: this.activitybarPart,		// Activity Bar
				editor: this.editorPart,				// Editor
				sidebar: this.sidebarPart,				// Sidebar
				panel: this.panelPart,					// Panel Part
				statusbar: this.statusbarPart,			// Statusbar
			},
E
Erich Gamma 已提交
738 739 740 741 742 743 744 745 746 747 748
			this.quickOpen,								// Quickopen
			options										// Layout Options
		);

		this.toDispose.push(this.workbenchLayout);
	}

	private createWorkbench(): void {

		// Create Workbench DIV Off-DOM
		this.workbenchContainer = $('.monaco-workbench-container');
749
		this.workbench = $().div({ 'class': 'monaco-workbench ' + (isWindows ? 'windows' : isLinux ? 'linux' : 'mac'), id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer);
E
Erich Gamma 已提交
750 751 752 753 754 755 756 757
	}

	private renderWorkbench(): void {

		// Apply sidebar state as CSS class
		if (this.sideBarHidden) {
			this.workbench.addClass('nosidebar');
		}
758 759 760
		if (this.panelHidden) {
			this.workbench.addClass('nopanel');
		}
E
Erich Gamma 已提交
761 762 763 764 765 766 767 768 769 770

		// Apply no-workspace state as CSS class
		if (!this.workbenchParams.workspace) {
			this.workbench.addClass('no-workspace');
		}

		// Create Parts
		this.createActivityBarPart();
		this.createSidebarPart();
		this.createEditorPart();
771
		this.createPanelPart();
E
Erich Gamma 已提交
772 773 774 775 776 777 778
		this.createStatusbarPart();

		// Add Workbench to DOM
		this.workbenchContainer.build(this.container);
	}

	private createActivityBarPart(): void {
B
Benjamin Pasero 已提交
779
		const activitybarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
780 781
			.div({
				'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
782 783
				id: Identifiers.ACTIVITYBAR_PART,
				role: 'navigation'
E
Erich Gamma 已提交
784 785 786 787 788 789
			});

		this.activitybarPart.create(activitybarPartContainer);
	}

	private createSidebarPart(): void {
B
Benjamin Pasero 已提交
790
		const sidebarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
791 792
			.div({
				'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
793 794
				id: Identifiers.SIDEBAR_PART,
				role: 'complementary'
E
Erich Gamma 已提交
795 796 797 798 799
			});

		this.sidebarPart.create(sidebarPartContainer);
	}

I
isidor 已提交
800
	private createPanelPart(): void {
B
Benjamin Pasero 已提交
801
		const panelPartContainer = $(this.workbench)
I
isidor 已提交
802
			.div({
803
				'class': ['part', 'panel', 'monaco-editor-background'],
804 805
				id: Identifiers.PANEL_PART,
				role: 'complementary'
I
isidor 已提交
806 807 808 809 810
			});

		this.panelPart.create(panelPartContainer);
	}

E
Erich Gamma 已提交
811
	private createEditorPart(): void {
B
Benjamin Pasero 已提交
812
		const editorContainer = $(this.workbench)
E
Erich Gamma 已提交
813
			.div({
814
				'class': ['part', 'editor', 'monaco-editor-background', 'empty'],
815 816
				id: Identifiers.EDITOR_PART,
				role: 'main'
E
Erich Gamma 已提交
817 818 819 820 821 822
			});

		this.editorPart.create(editorContainer);
	}

	private createStatusbarPart(): void {
B
Benjamin Pasero 已提交
823
		const statusbarContainer = $(this.workbench).div({
E
Erich Gamma 已提交
824
			'class': ['part', 'statusbar'],
825 826
			id: Identifiers.STATUSBAR_PART,
			role: 'contentinfo'
E
Erich Gamma 已提交
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
		});

		this.statusbarPart.create(statusbarContainer);
	}

	public getEditorPart(): EditorPart {
		assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.');

		return this.editorPart;
	}

	public getSidebarPart(): SidebarPart {
		assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.');

		return this.sidebarPart;
	}

I
isidor 已提交
844 845 846 847 848 849
	public getPanelPart(): PanelPart {
		assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.');

		return this.panelPart;
	}

E
Erich Gamma 已提交
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
	public getInstantiationService(): IInstantiationService {
		assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.');

		return this.instantiationService;
	}

	public addClass(clazz: string): void {
		if (this.workbench) {
			this.workbench.addClass(clazz);
		}
	}

	public removeClass(clazz: string): void {
		if (this.workbench) {
			this.workbench.removeClass(clazz);
		}
	}
867 868 869 870

	public getWorkbenchElementId(): string {
		return Identifiers.WORKBENCH_CONTAINER;
	}
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887

	public setRestoreSidebar(): void {
		this.storageService.store(Workbench.sidebarRestoreSettingKey, 'true', StorageScope.WORKSPACE);
	}

	private shouldRestoreSidebar(): boolean {
		if (!this.environmentService.isBuilt) {
			return true; // always restore sidebar when we are in development mode
		}

		const restore = this.storageService.getBoolean(Workbench.sidebarRestoreSettingKey, StorageScope.WORKSPACE);
		if (restore) {
			this.storageService.remove(Workbench.sidebarRestoreSettingKey, StorageScope.WORKSPACE); // only support once
		}

		return restore;
	}
E
Erich Gamma 已提交
888
}