workbench.ts 34.4 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');
19
import Uri from 'vs/base/common/uri';
20
import { IBackupService } from 'vs/platform/backup/common/backup';
J
Johannes Rieken 已提交
21 22 23 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 70 71 72 73 74 75 76 77 78 79
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';
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';
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,
164 165
		@IEnvironmentService private environmentService: IEnvironmentService,
		@IBackupService private backupService: IBackupService
166
	) {
167
		this.container = container;
E
Erich Gamma 已提交
168 169 170

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

175 176 177 178 179 180 181 182 183
		// Restore any backups if they exist for this workspace (empty workspaces are not supported yet)
		if (workspace) {
			options.filesToRestore = this.backupService.getWorkspaceTextFilesWithBackups(workspace.resource.fsPath).map(filePath => {
				return { resource: Uri.file(filePath), options: { pinned: true } };
			});
			options.untitledFilesToRestore = this.backupService.getWorkspaceUntitledFileBackups(workspace.resource.fsPath).map(untitledFilePath => {
				return { resource: Uri.file(untitledFilePath), options: { pinned: true } };
			});
		}
184

185 186 187 188
		this.hasFilesToCreateOpenOrDiff =
			(options.filesToCreate && options.filesToCreate.length > 0) ||
			(options.filesToOpen && options.filesToOpen.length > 0) ||
			(options.filesToDiff && options.filesToDiff.length > 0) ||
189 190
			(options.filesToRestore && options.filesToRestore.length > 0) ||
			(options.untitledFilesToRestore && options.untitledFilesToRestore.length > 0);
191

E
Erich Gamma 已提交
192 193 194 195
		this.toDispose = [];
		this.toShutdown = [];
		this.editorBackgroundDelayer = new Delayer<void>(50);

196
		this.creationPromise = new TPromise<boolean>(c => {
E
Erich Gamma 已提交
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
			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();
			}

222
			// Contexts
223 224
			this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService);
			this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
225

E
Erich Gamma 已提交
226 227 228 229 230 231 232 233 234 235 236 237
			// Register Listeners
			this.registerListeners();

			// Settings
			this.initSettings();

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

			// Workbench Layout
			this.createWorkbenchLayout();

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

B
Benjamin Pasero 已提交
241
			// Load Viewlet
B
Benjamin Pasero 已提交
242
			const viewletRegistry = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets));
243
			let viewletId = viewletRegistry.getDefaultViewletId();
244
			if (this.shouldRestoreSidebar()) {
245
				viewletId = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, viewletRegistry.getDefaultViewletId()); // help developers and restore last view
246 247 248
			}

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

B
Benjamin Pasero 已提交
253
			// Load Panel
B
Benjamin Pasero 已提交
254
			const panelRegistry = (<PanelRegistry>Registry.as(PanelExtensions.Panels));
255 256
			const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId());
			if (!this.panelHidden && !!panelId) {
I
isidor 已提交
257
				compositeAndEditorPromises.push(this.panelPart.openPanel(panelId, false));
258 259
			}

B
Benjamin Pasero 已提交
260
			// Load Editors
B
Benjamin Pasero 已提交
261
			const editorTimerEvent = timer.start(timer.Topic.STARTUP, strings.format('Restoring Editor(s)'));
262
			compositeAndEditorPromises.push(this.resolveEditorsToOpen().then(inputsWithOptions => {
B
Benjamin Pasero 已提交
263 264
				let editorOpenPromise: TPromise<BaseEditor[]>;
				if (inputsWithOptions.length) {
265
					const editors = inputsWithOptions.map(inputWithOptions => {
B
Benjamin Pasero 已提交
266 267 268
						return {
							input: inputWithOptions.input,
							options: inputWithOptions.options,
269
							position: Position.LEFT
B
Benjamin Pasero 已提交
270
						};
271 272
					});

B
Benjamin Pasero 已提交
273 274 275
					editorOpenPromise = this.editorPart.openEditors(editors);
				} else {
					editorOpenPromise = this.editorPart.restoreEditors();
276
				}
277 278

				return editorOpenPromise.then(() => {
279
					this.onEditorsChanged(); // make sure we show the proper background in the editor area
E
Erich Gamma 已提交
280 281 282 283 284
					editorTimerEvent.stop();
				});
			}));

			// Flag workbench as created once done
285
			const workbenchDone = (error?: Error) => {
E
Erich Gamma 已提交
286 287 288
				this.workbenchCreated = true;
				this.creationPromiseComplete(true);

B
Benjamin Pasero 已提交
289
				if (this.callbacks && this.callbacks.onWorkbenchStarted) {
290
					this.callbacks.onWorkbenchStarted(this.keybindingService.customKeybindingsCount());
B
Benjamin Pasero 已提交
291
				}
292 293 294 295 296 297

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

B
polish  
Benjamin Pasero 已提交
298
			// Join viewlet, panel and editor promises
299
			TPromise.join(compositeAndEditorPromises).then(() => workbenchDone(), error => workbenchDone(error));
E
Erich Gamma 已提交
300 301 302
		} catch (error) {

			// Print out error
303
			console.error(toErrorMessage(error, true));
E
Erich Gamma 已提交
304 305 306 307 308 309

			// Rethrow
			throw error;
		}
	}

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

		// Files to open, diff or create
313 314
		if (this.hasFilesToCreateOpenOrDiff) {
			const wbopt = this.workbenchParams.options;
B
Benjamin Pasero 已提交
315 316
			const filesToCreate = wbopt.filesToCreate || [];
			const filesToOpen = wbopt.filesToOpen || [];
317
			const filesToRestore = wbopt.filesToRestore || [];
318
			const untitledFilesToRestore = wbopt.untitledFilesToRestore || [];
B
Benjamin Pasero 已提交
319
			const filesToDiff = wbopt.filesToDiff;
B
Benjamin Pasero 已提交
320 321 322

			// Files to diff is exclusive
			if (filesToDiff && filesToDiff.length) {
323
				return TPromise.join<EditorInput>(filesToDiff.map(resourceInput => this.editorService.createInput(resourceInput))).then((inputsToDiff) => {
B
Benjamin Pasero 已提交
324 325 326 327 328 329
					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 已提交
330 331
				const inputs: EditorInput[] = [];
				const options: EditorOptions[] = [];
B
Benjamin Pasero 已提交
332 333

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

337 338 339 340
				// Files to restore
				inputs.push(...untitledFilesToRestore.map(resourceInput => this.untitledEditorService.createOrGet(null, null, resourceInput.resource)));
				options.push(...untitledFilesToRestore.map(r => null)); // fill empty options for files to create because we dont have options there

B
Benjamin Pasero 已提交
341
				// Files to open
342 343
				let filesToOpenInputPromise = filesToOpen.map(resourceInput => this.editorService.createInput(resourceInput));
				let filesToRestoreInputPromise = filesToRestore.map(resourceInput => this.editorService.createInput(resourceInput, true));
344

345
				return TPromise.join<EditorInput>(filesToOpenInputPromise.concat(filesToRestoreInputPromise)).then((inputsToOpen) => {
B
Benjamin Pasero 已提交
346
					inputs.push(...inputsToOpen);
D
Daniel Imms 已提交
347
					options.push(...filesToOpen.concat(filesToRestore).map(resourceInput => TextEditorOptions.from(resourceInput)));
B
Benjamin Pasero 已提交
348 349 350 351 352 353 354

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

		// Empty workbench
355
		else if (!this.workbenchParams.workspace && this.telemetryService.getExperiments().openUntitledFile) {
B
Benjamin Pasero 已提交
356 357 358 359 360 361
			return TPromise.as([{ input: this.untitledEditorService.createOrGet() }]);
		}

		return TPromise.as([]);
	}

E
Erich Gamma 已提交
362
	private initServices(): void {
363 364
		const {serviceCollection} = this.workbenchParams;

365
		this.toDispose.push(this.lifecycleService.onShutdown(this.shutdownComponents, this));
E
Erich Gamma 已提交
366 367

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

370 371 372 373 374 375 376
		// 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 已提交
377
		this.contextKeyService = this.instantiationService.createInstance(ContextKeyService);
378
		serviceCollection.set(IContextKeyService, this.contextKeyService);
379

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

383
		// Context Menu
384
		serviceCollection.set(IContextMenuService, this.instantiationService.createInstance(ContextMenuService));
385

386 387
		// Menus/Actions
		serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
388

E
Erich Gamma 已提交
389
		// Viewlet service (sidebar part)
390
		this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART);
E
Erich Gamma 已提交
391 392
		this.toDispose.push(this.sidebarPart);
		this.toShutdown.push(this.sidebarPart);
393
		serviceCollection.set(IViewletService, this.sidebarPart);
E
Erich Gamma 已提交
394

I
isidor 已提交
395
		// Panel service (panel part)
396
		this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART);
I
isidor 已提交
397 398
		this.toDispose.push(this.panelPart);
		this.toShutdown.push(this.panelPart);
399
		serviceCollection.set(IPanelService, this.panelPart);
I
isidor 已提交
400

E
Erich Gamma 已提交
401
		// Activity service (activitybar part)
402
		this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
E
Erich Gamma 已提交
403 404
		this.toDispose.push(this.activitybarPart);
		this.toShutdown.push(this.activitybarPart);
405
		serviceCollection.set(IActivityService, this.activitybarPart);
E
Erich Gamma 已提交
406 407

		// Editor service (editor part)
408
		this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasFilesToCreateOpenOrDiff);
E
Erich Gamma 已提交
409 410
		this.toDispose.push(this.editorPart);
		this.toShutdown.push(this.editorPart);
411
		this.editorService = this.instantiationService.createInstance(WorkbenchEditorService, this.editorPart);
412
		serviceCollection.set(IWorkbenchEditorService, this.editorService);
413
		serviceCollection.set(IEditorGroupService, this.editorPart);
E
Erich Gamma 已提交
414

415
		// File Service
416 417 418
		const fileService = this.instantiationService.createInstance(FileService);
		serviceCollection.set(IFileService, fileService);

419
		// History
420
		serviceCollection.set(IHistoryService, this.instantiationService.createInstance(HistoryService));
421

422 423 424
		// Text File Service
		serviceCollection.set(ITextFileService, this.instantiationService.createInstance(TextFileService));

425
		// Configuration Editing
426 427
		this.configurationEditingService = this.instantiationService.createInstance(ConfigurationEditingService);
		serviceCollection.set(IConfigurationEditingService, this.configurationEditingService);
428

429
		// Configuration Resolver
430 431
		const workspace = this.contextService.getWorkspace();
		const configurationResolverService = this.instantiationService.createInstance(ConfigurationResolverService, workspace ? workspace.resource : null, process.env);
432 433
		serviceCollection.set(IConfigurationResolverService, configurationResolverService);

E
Erich Gamma 已提交
434
		// Quick open service (quick open controller)
435
		this.quickOpen = this.instantiationService.createInstance(QuickOpenController);
E
Erich Gamma 已提交
436 437
		this.toDispose.push(this.quickOpen);
		this.toShutdown.push(this.quickOpen);
438
		serviceCollection.set(IQuickOpenService, this.quickOpen);
E
Erich Gamma 已提交
439

B
polish  
Benjamin Pasero 已提交
440
		// Contributed services
B
Benjamin Pasero 已提交
441
		const contributedServices = getServices();
E
Erich Gamma 已提交
442
		for (let contributedService of contributedServices) {
443
			serviceCollection.set(contributedService.id, contributedService.descriptor);
E
Erich Gamma 已提交
444 445 446 447 448 449 450 451 452 453 454 455
		}

		// Set the some services to registries that have been created eagerly
		<IActionBarRegistry>Registry.as(ActionBarExtensions.Actionbar).setInstantiationService(this.instantiationService);
		<IWorkbenchContributionsRegistry>Registry.as(WorkbenchExtensions.Workbench).setInstantiationService(this.instantiationService);
		<IEditorRegistry>Registry.as(EditorExtensions.Editors).setInstantiationService(this.instantiationService);
	}

	private initSettings(): void {

		// Sidebar visibility
		this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, false);
B
Benjamin Pasero 已提交
456
		if (!this.contextService.getWorkspace()) {
457
			this.sideBarHidden = !this.telemetryService.getExperiments().showDefaultViewlet;
E
Erich Gamma 已提交
458 459
		}

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

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

E
Erich Gamma 已提交
472
		// Sidebar position
473 474
		const sideBarPosition = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
		this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT;
B
Benjamin Pasero 已提交
475 476

		// Statusbar visibility
477 478
		const statusBarVisible = this.configurationService.lookup<string>(Workbench.statusbarVisibleConfigurationKey).value;
		this.statusBarHidden = !statusBarVisible;
E
Erich Gamma 已提交
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
	}

	/**
	 * 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 已提交
500
		const activeElement = document.activeElement;
E
Erich Gamma 已提交
501 502 503 504
		if (!activeElement) {
			return false;
		}

505 506 507 508 509
		const container = this.getContainer(part);
		return DOM.isAncestor(activeElement, container);
	}

	public getContainer(part: Parts): HTMLElement {
E
Erich Gamma 已提交
510 511 512 513 514 515 516 517
		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 已提交
518 519 520
			case Parts.PANEL_PART:
				container = this.panelPart.getContainer();
				break;
E
Erich Gamma 已提交
521 522 523 524 525 526 527
			case Parts.EDITOR_PART:
				container = this.editorPart.getContainer();
				break;
			case Parts.STATUSBAR_PART:
				container = this.statusbarPart.getContainer();
				break;
		}
528
		return container && container.getHTMLElement();
E
Erich Gamma 已提交
529 530 531
	}

	public isVisible(part: Parts): boolean {
B
Benjamin Pasero 已提交
532 533 534 535 536 537 538
		switch (part) {
			case Parts.SIDEBAR_PART:
				return !this.sideBarHidden;
			case Parts.PANEL_PART:
				return !this.panelHidden;
			case Parts.STATUSBAR_PART:
				return !this.statusBarHidden;
539
		}
E
Erich Gamma 已提交
540 541 542 543

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

544 545 546 547
	public isStatusBarHidden(): boolean {
		return this.statusBarHidden;
	}

548
	private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void {
549 550 551 552 553 554 555 556
		this.statusBarHidden = hidden;

		// Layout
		if (!skipLayout) {
			this.workbenchLayout.layout(true);
		}
	}

E
Erich Gamma 已提交
557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
	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) {
			this.workbenchLayout.layout(true);
		}

B
Benjamin Pasero 已提交
576
		// If sidebar becomes hidden, also hide the current active Viewlet if any
E
Erich Gamma 已提交
577 578 579 580
		if (hidden && this.sidebarPart.getActiveViewlet()) {
			this.sidebarPart.hideActiveViewlet();

			// Pass Focus to Editor if Sidebar is now hidden
B
Benjamin Pasero 已提交
581
			const editor = this.editorPart.getActiveEditor();
E
Erich Gamma 已提交
582 583 584 585 586
			if (editor) {
				editor.focus();
			}
		}

B
Benjamin Pasero 已提交
587
		// If sidebar becomes visible, show last active Viewlet or default viewlet
E
Erich Gamma 已提交
588
		else if (!hidden && !this.sidebarPart.getActiveViewlet()) {
B
Benjamin Pasero 已提交
589 590
			const registry = (<ViewletRegistry>Registry.as(ViewletExtensions.Viewlets));
			const viewletToOpen = this.sidebarPart.getLastActiveViewletId() || registry.getDefaultViewletId();
E
Erich Gamma 已提交
591 592 593 594 595 596 597 598 599
			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 已提交
600 601
	public isPanelHidden(): boolean {
		return this.panelHidden;
I
isidor 已提交
602 603
	}

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

607 608 609 610 611 612 613
		// Adjust CSS
		if (hidden) {
			this.workbench.addClass('nopanel');
		} else {
			this.workbench.removeClass('nopanel');
		}

I
isidor 已提交
614 615 616 617 618 619 620 621 622 623
		// Layout
		if (!skipLayout) {
			this.workbenchLayout.layout(true);
		}

		// 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 已提交
624
			const editor = this.editorPart.getActiveEditor();
I
isidor 已提交
625 626 627 628 629 630 631
			if (editor) {
				editor.focus();
			}
		}

		// If panel part becomes visible, show last active panel or default panel
		else if (!hidden && !this.panelPart.getActivePanel()) {
B
Benjamin Pasero 已提交
632 633
			const registry = (<PanelRegistry>Registry.as(PanelExtensions.Panels));
			const panelToOpen = this.panelPart.getLastActivePanelId() || registry.getDefaultPanelId();
I
isidor 已提交
634 635 636 637 638 639
			if (panelToOpen) {
				this.panelPart.openPanel(panelToOpen, true).done(null, errors.onUnexpectedError);
			}
		}

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

E
Erich Gamma 已提交
643 644 645 646
	public getSideBarPosition(): Position {
		return this.sideBarPosition;
	}

647
	private setSideBarPosition(position: Position): void {
E
Erich Gamma 已提交
648 649 650 651
		if (this.sideBarHidden) {
			this.setSideBarHidden(false, true /* Skip Layout */);
		}

B
Benjamin Pasero 已提交
652 653
		const newPositionValue = (position === Position.LEFT) ? 'left' : 'right';
		const oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right';
E
Erich Gamma 已提交
654 655 656 657 658 659 660 661 662 663 664 665
		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
		this.workbenchLayout.layout(true);
	}

B
Benjamin Pasero 已提交
666
	public dispose(): void {
E
Erich Gamma 已提交
667
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
668
			this.shutdownComponents();
E
Erich Gamma 已提交
669 670 671
			this.workbenchShutdown = true;
		}

J
Joao Moreno 已提交
672
		this.toDispose = dispose(this.toDispose);
E
Erich Gamma 已提交
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
	}

	/**
	 * 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
694
		this.toDispose.push(this.editorPart.onEditorsChanged(() => this.onEditorsChanged()));
695 696

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

700 701 702 703 704
		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 已提交
705 706
	}

707
	private onEditorsChanged(): void {
B
Benjamin Pasero 已提交
708
		const visibleEditors = this.editorService.getVisibleEditors().length;
E
Erich Gamma 已提交
709 710 711 712

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

713
		const editorContainer = this.editorPart.getContainer();
E
Erich Gamma 已提交
714
		if (visibleEditors === 0) {
715
			this.editorsVisibleContext.reset();
716
			this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty'));
E
Erich Gamma 已提交
717
		} else {
718
			this.editorsVisibleContext.set(true);
719
			this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty'));
E
Erich Gamma 已提交
720 721 722
		}
	}

723 724
	private onDidUpdateConfiguration(): void {
		const newSidebarPositionValue = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
725
		const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT;
726 727 728
		if (newSidebarPosition !== this.getSideBarPosition()) {
			this.setSideBarPosition(newSidebarPosition);
		}
729 730 731 732 733

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

E
Erich Gamma 已提交
736
	private createWorkbenchLayout(): void {
B
Benjamin Pasero 已提交
737
		const options = new LayoutOptions();
E
Erich Gamma 已提交
738 739 740 741 742
		options.setMargin(new Box(0, 0, 0, 0));

		this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout,
			$(this.container),							// Parent
			this.workbench,								// Workbench Container
I
isidor 已提交
743 744 745 746 747 748 749
			{
				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 已提交
750 751 752 753 754 755 756 757 758 759 760
			this.quickOpen,								// Quickopen
			options										// Layout Options
		);

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

	private createWorkbench(): void {

		// Create Workbench DIV Off-DOM
		this.workbenchContainer = $('.monaco-workbench-container');
761
		this.workbench = $().div({ 'class': 'monaco-workbench ' + (isWindows ? 'windows' : isLinux ? 'linux' : 'mac'), id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer);
E
Erich Gamma 已提交
762 763 764 765 766 767 768 769
	}

	private renderWorkbench(): void {

		// Apply sidebar state as CSS class
		if (this.sideBarHidden) {
			this.workbench.addClass('nosidebar');
		}
770 771 772
		if (this.panelHidden) {
			this.workbench.addClass('nopanel');
		}
E
Erich Gamma 已提交
773 774 775 776 777 778 779 780 781 782

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

		// Create Parts
		this.createActivityBarPart();
		this.createSidebarPart();
		this.createEditorPart();
783
		this.createPanelPart();
E
Erich Gamma 已提交
784 785 786 787 788 789 790
		this.createStatusbarPart();

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

	private createActivityBarPart(): void {
B
Benjamin Pasero 已提交
791
		const activitybarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
792 793
			.div({
				'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
794 795
				id: Identifiers.ACTIVITYBAR_PART,
				role: 'navigation'
E
Erich Gamma 已提交
796 797 798 799 800 801
			});

		this.activitybarPart.create(activitybarPartContainer);
	}

	private createSidebarPart(): void {
B
Benjamin Pasero 已提交
802
		const sidebarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
803 804
			.div({
				'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
805 806
				id: Identifiers.SIDEBAR_PART,
				role: 'complementary'
E
Erich Gamma 已提交
807 808 809 810 811
			});

		this.sidebarPart.create(sidebarPartContainer);
	}

I
isidor 已提交
812
	private createPanelPart(): void {
B
Benjamin Pasero 已提交
813
		const panelPartContainer = $(this.workbench)
I
isidor 已提交
814
			.div({
815
				'class': ['part', 'panel', 'monaco-editor-background'],
816 817
				id: Identifiers.PANEL_PART,
				role: 'complementary'
I
isidor 已提交
818 819 820 821 822
			});

		this.panelPart.create(panelPartContainer);
	}

E
Erich Gamma 已提交
823
	private createEditorPart(): void {
B
Benjamin Pasero 已提交
824
		const editorContainer = $(this.workbench)
E
Erich Gamma 已提交
825
			.div({
826
				'class': ['part', 'editor', 'monaco-editor-background'],
827 828
				id: Identifiers.EDITOR_PART,
				role: 'main'
E
Erich Gamma 已提交
829 830 831 832 833 834
			});

		this.editorPart.create(editorContainer);
	}

	private createStatusbarPart(): void {
B
Benjamin Pasero 已提交
835
		const statusbarContainer = $(this.workbench).div({
E
Erich Gamma 已提交
836
			'class': ['part', 'statusbar'],
837 838
			id: Identifiers.STATUSBAR_PART,
			role: 'contentinfo'
E
Erich Gamma 已提交
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855
		});

		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 已提交
856 857 858 859 860 861
	public getPanelPart(): PanelPart {
		assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.');

		return this.panelPart;
	}

E
Erich Gamma 已提交
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
	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);
		}
	}
879 880 881 882

	public getWorkbenchElementId(): string {
		return Identifiers.WORKBENCH_CONTAINER;
	}
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899

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