workbench.ts 47.0 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

10
import { localize } from 'vs/nls';
J
Johannes Rieken 已提交
11 12
import { TPromise, ValueCallback } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
13
import Event, { Emitter, chain } from 'vs/base/common/event';
E
Erich Gamma 已提交
14
import DOM = require('vs/base/browser/dom');
B
Benjamin Pasero 已提交
15
import { Builder, $ } from 'vs/base/browser/builder';
J
Johannes Rieken 已提交
16
import { Delayer } from 'vs/base/common/async';
17
import * as browser from 'vs/base/browser/browser';
E
Erich Gamma 已提交
18
import assert = require('vs/base/common/assert');
19
import { StopWatch } from 'vs/base/common/stopwatch';
20
import { startTimer } from 'vs/base/node/startupTimers';
E
Erich Gamma 已提交
21
import errors = require('vs/base/common/errors');
22
import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
23
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
J
Johannes Rieken 已提交
24
import { toErrorMessage } from 'vs/base/common/errorMessage';
25
import { Registry } from 'vs/platform/registry/common/platform';
B
Benjamin Pasero 已提交
26
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
J
Johannes Rieken 已提交
27
import { IOptions } from 'vs/workbench/common/options';
28
import { Position as EditorPosition, IResourceDiffInput, IUntitledResourceInput, IEditor } from 'vs/platform/editor/common/editor';
J
Johannes Rieken 已提交
29
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
30
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
J
Johannes Rieken 已提交
31 32 33 34 35 36
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';
B
Benjamin Pasero 已提交
37 38
import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
import { WorkbenchLayout } from 'vs/workbench/browser/layout';
39
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions';
J
Johannes Rieken 已提交
40 41 42 43
import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import { getServices } from 'vs/platform/instantiation/common/extensions';
import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService';
I
isidor 已提交
44
import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService';
45
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
46 47 48 49
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';
50
import { WorkspaceConfigurationService } from 'vs/workbench/services/configuration/node/configuration';
J
Johannes Rieken 已提交
51 52 53 54
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 { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
55
import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
J
Johannes Rieken 已提交
56
import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
57
import { IActivityBarService } from 'vs/workbench/services/activity/common/activityBarService';
B
Benjamin Pasero 已提交
58
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
P
Pine Wu 已提交
59
import { ViewletService } from 'vs/workbench/services/viewlet/browser/viewletService';
J
Johannes Rieken 已提交
60 61
import { FileService } from 'vs/workbench/services/files/electron-browser/fileService';
import { IFileService } from 'vs/platform/files/common/files';
62
import { IListService, ListService } from 'vs/platform/list/browser/listService';
J
Johannes Rieken 已提交
63 64 65
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';
B
Benjamin Pasero 已提交
66
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
J
Johannes Rieken 已提交
67
import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService';
68
import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService';
J
Johannes Rieken 已提交
69
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
70 71
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService';
J
Johannes Rieken 已提交
72 73 74 75 76 77
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';
J
Joao Moreno 已提交
78 79
import { ISCMService } from 'vs/workbench/services/scm/common/scm';
import { SCMService } from 'vs/workbench/services/scm/common/scmService';
80 81
import { IProgressService2 } from 'vs/platform/progress/common/progress';
import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2';
82
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
83
import { ITextModelService } from 'vs/editor/common/services/resolverService';
J
Johannes Rieken 已提交
84
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
85
import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
86
import { IWindowService, IWindowConfiguration as IWindowSettings, IWindowConfiguration } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
87 88
import { IMessageService } from 'vs/platform/message/common/message';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
89
import { IMenuService, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
J
Johannes Rieken 已提交
90 91 92 93
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';
94
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
95
import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, inRecentFilesPickerContextKey } from "vs/workbench/electron-browser/actions";
96
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
97 98
import { KeybindingsRegistry } from "vs/platform/keybinding/common/keybindingsRegistry";
import { getQuickNavigateHandler, inQuickOpenContext } from "vs/workbench/browser/parts/quickopen/quickopen";
99 100
import { IWorkspaceEditingService } from "vs/workbench/services/workspace/common/workspaceEditing";
import { WorkspaceEditingService } from "vs/workbench/services/workspace/node/workspaceEditingService";
E
Erich Gamma 已提交
101

A
Alex Dima 已提交
102 103
export const MessagesVisibleContext = new RawContextKey<boolean>('globalMessageVisible', false);
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
I
isidor 已提交
104
export const InZenModeContext = new RawContextKey<boolean>('inZenMode', false);
J
Johannes Rieken 已提交
105
export const NoEditorsVisibleContext: ContextKeyExpr = EditorsVisibleContext.toNegated();
A
Alex Dima 已提交
106

E
Erich Gamma 已提交
107 108
interface WorkbenchParams {
	options: IOptions;
109
	configuration: IWindowConfiguration;
110
	serviceCollection: ServiceCollection;
E
Erich Gamma 已提交
111 112
}

I
isidor 已提交
113 114 115
interface IZenModeSettings {
	fullScreen: boolean;
	hideTabs: boolean;
116
	hideActivityBar: boolean;
I
isidor 已提交
117
	hideStatusBar: boolean;
I
isidor 已提交
118
	restore: boolean;
I
isidor 已提交
119 120
}

121 122 123 124 125
export interface IWorkbenchStartedInfo {
	customKeybindingsCount: number;
	restoreViewletDuration: number;
	restoreEditorsDuration: number;
	pinnedViewlets: string[];
126 127
	restoredViewlet: string;
	restoredEditors: string[];
128 129
}

E
Erich Gamma 已提交
130 131
export interface IWorkbenchCallbacks {
	onServicesCreated?: () => void;
132
	onWorkbenchStarted?: (info: IWorkbenchStartedInfo) => void;
E
Erich Gamma 已提交
133 134
}

135 136
const Identifiers = {
	WORKBENCH_CONTAINER: 'workbench.main.container',
B
Benjamin Pasero 已提交
137
	TITLEBAR_PART: 'workbench.parts.titlebar',
138 139 140 141 142 143 144
	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 已提交
145
/**
B
Benjamin Pasero 已提交
146
 * The workbench creates and lays out all parts that make up the workbench.
E
Erich Gamma 已提交
147 148 149 150
 */
export class Workbench implements IPartService {

	private static sidebarHiddenSettingKey = 'workbench.sidebar.hidden';
151
	private static sidebarRestoreSettingKey = 'workbench.sidebar.restore';
I
isidor 已提交
152
	private static panelHiddenSettingKey = 'workbench.panel.hidden';
I
isidor 已提交
153
	private static zenModeActiveSettingKey = 'workbench.zenmode.active';
E
Erich Gamma 已提交
154

155
	private static sidebarPositionConfigurationKey = 'workbench.sideBar.location';
156
	private static statusbarVisibleConfigurationKey = 'workbench.statusBar.visible';
S
Sanders Lauture 已提交
157
	private static activityBarVisibleConfigurationKey = 'workbench.activityBar.visible';
158

159 160
	private _onTitleBarVisibilityChange: Emitter<void>;

161
	public _serviceBrand: any;
E
Erich Gamma 已提交
162

B
Benjamin Pasero 已提交
163
	private parent: HTMLElement;
E
Erich Gamma 已提交
164 165 166 167 168 169 170
	private container: HTMLElement;
	private workbenchParams: WorkbenchParams;
	private workbenchContainer: Builder;
	private workbench: Builder;
	private workbenchStarted: boolean;
	private workbenchCreated: boolean;
	private workbenchShutdown: boolean;
171
	private editorService: WorkbenchEditorService;
P
Pine Wu 已提交
172
	private viewletService: IViewletService;
173
	private contextKeyService: IContextKeyService;
174
	private keybindingService: IKeybindingService;
175
	private backupFileService: IBackupFileService;
176
	private configurationEditingService: IConfigurationEditingService;
B
Benjamin Pasero 已提交
177
	private titlebarPart: TitlebarPart;
E
Erich Gamma 已提交
178 179
	private activitybarPart: ActivitybarPart;
	private sidebarPart: SidebarPart;
I
isidor 已提交
180
	private panelPart: PanelPart;
E
Erich Gamma 已提交
181 182 183 184 185 186 187 188 189 190
	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;
191
	private statusBarHidden: boolean;
S
Sanders Lauture 已提交
192
	private activityBarHidden: boolean;
E
Erich Gamma 已提交
193
	private sideBarPosition: Position;
I
isidor 已提交
194
	private panelHidden: boolean;
E
Erich Gamma 已提交
195
	private editorBackgroundDelayer: Delayer<void>;
A
Alex Dima 已提交
196 197
	private messagesVisibleContext: IContextKey<boolean>;
	private editorsVisibleContext: IContextKey<boolean>;
I
isidor 已提交
198
	private inZenMode: IContextKey<boolean>;
199
	private hasFilesToCreateOpenOrDiff: boolean;
I
isidor 已提交
200
	private zenMode: {
I
isidor 已提交
201 202
		active: boolean;
		transitionedToFullScreen: boolean;
203 204
		wasSideBarVisible: boolean;
		wasPanelVisible: boolean;
I
isidor 已提交
205
	};
E
Erich Gamma 已提交
206

207
	constructor(
B
Benjamin Pasero 已提交
208
		parent: HTMLElement,
209
		container: HTMLElement,
210
		configuration: IWindowConfiguration,
211 212
		options: IOptions,
		serviceCollection: ServiceCollection,
213
		@IInstantiationService private instantiationService: IInstantiationService,
214
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
215 216 217
		@IStorageService private storageService: IStorageService,
		@ILifecycleService private lifecycleService: ILifecycleService,
		@IMessageService private messageService: IMessageService,
218
		@IConfigurationService private configurationService: WorkspaceConfigurationService,
219
		@ITelemetryService private telemetryService: ITelemetryService,
I
isidor 已提交
220 221
		@IEnvironmentService private environmentService: IEnvironmentService,
		@IWindowService private windowService: IWindowService
222
	) {
B
Benjamin Pasero 已提交
223
		this.parent = parent;
224
		this.container = container;
E
Erich Gamma 已提交
225 226

		this.workbenchParams = {
227
			options,
228
			configuration,
229
			serviceCollection
E
Erich Gamma 已提交
230 231
		};

232 233 234
		this.hasFilesToCreateOpenOrDiff =
			(options.filesToCreate && options.filesToCreate.length > 0) ||
			(options.filesToOpen && options.filesToOpen.length > 0) ||
235
			(options.filesToDiff && options.filesToDiff.length > 0);
236

E
Erich Gamma 已提交
237 238 239 240
		this.toDispose = [];
		this.toShutdown = [];
		this.editorBackgroundDelayer = new Delayer<void>(50);

241 242
		this._onTitleBarVisibilityChange = new Emitter<void>();

243
		this.creationPromise = new TPromise<boolean>(c => {
E
Erich Gamma 已提交
244 245 246 247
			this.creationPromiseComplete = c;
		});
	}

248 249 250 251
	public get onTitleBarVisibilityChange(): Event<void> {
		return this._onTitleBarVisibilityChange.event;
	}

252 253 254 255 256 257
	public get onEditorLayout(): Event<void> {
		return chain(this.editorPart.onLayout)
			.map(() => void 0)
			.event;
	}

E
Erich Gamma 已提交
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
	/**
	 * 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();

273 274 275
			// Install some global actions
			this.createGlobalActions();

E
Erich Gamma 已提交
276 277 278 279 280 281
			// Services
			this.initServices();
			if (this.callbacks && this.callbacks.onServicesCreated) {
				this.callbacks.onServicesCreated();
			}

282
			// Contexts
283 284
			this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService);
			this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
I
isidor 已提交
285
			this.inZenMode = InZenModeContext.bindTo(this.contextKeyService);
286

E
Erich Gamma 已提交
287 288 289 290 291 292 293 294 295 296 297 298
			// Register Listeners
			this.registerListeners();

			// Settings
			this.initSettings();

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

			// Workbench Layout
			this.createWorkbenchLayout();

B
Benjamin Pasero 已提交
299
			// Load composites and editors in parallel
B
Benjamin Pasero 已提交
300
			const compositeAndEditorPromises: TPromise<any>[] = [];
E
Erich Gamma 已提交
301

P
Pine Wu 已提交
302
			// Restore last opened viewlet
303
			let viewletRestoreStopWatch: StopWatch;
304
			let viewletIdToRestore: string;
305
			if (!this.sideBarHidden) {
306

307
				if (this.shouldRestoreLastOpenedViewlet()) {
308
					viewletIdToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE);
B
polish  
Benjamin Pasero 已提交
309 310 311
				}

				if (!viewletIdToRestore) {
312
					viewletIdToRestore = this.viewletService.getDefaultViewletId();
P
Pine Wu 已提交
313
				}
314

315
				viewletRestoreStopWatch = StopWatch.create();
316 317
				const viewletTimer = startTimer('restore:viewlet');
				compositeAndEditorPromises.push(viewletTimer.while(this.viewletService.openViewlet(viewletIdToRestore)).then(() => {
318 319
					viewletRestoreStopWatch.stop();
				}));
E
Erich Gamma 已提交
320 321
			}

B
Benjamin Pasero 已提交
322
			// Load Panel
323
			const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
324 325
			const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId());
			if (!this.panelHidden && !!panelId) {
I
isidor 已提交
326
				compositeAndEditorPromises.push(this.panelPart.openPanel(panelId, false));
327 328
			}

B
Benjamin Pasero 已提交
329
			// Load Editors
330
			const editorRestoreStopWatch = StopWatch.create();
331
			const restoredEditors: string[] = [];
332 333
			const editorsTimer = startTimer('restore:editors');
			compositeAndEditorPromises.push(editorsTimer.while(this.resolveEditorsToOpen().then(inputs => {
334
				let editorOpenPromise: TPromise<IEditor[]>;
335 336
				if (inputs.length) {
					editorOpenPromise = this.editorService.openEditors(inputs.map(input => { return { input, position: EditorPosition.ONE }; }));
B
Benjamin Pasero 已提交
337
				} else {
B
Benjamin Pasero 已提交
338
					editorOpenPromise = this.editorPart.restoreEditors();
339
				}
340

341
				return editorOpenPromise.then(editors => {
342
					this.onEditorsChanged(); // make sure we show the proper background in the editor area
343
					editorRestoreStopWatch.stop();
344 345 346 347 348 349 350
					for (const editor of editors) {
						if (editor.input) {
							restoredEditors.push(editor.input.getName());
						} else {
							restoredEditors.push(`other:${editor.getId()}`);
						}
					}
E
Erich Gamma 已提交
351
				});
352
			})));
E
Erich Gamma 已提交
353

354
			if (this.storageService.getBoolean(Workbench.zenModeActiveSettingKey, StorageScope.WORKSPACE, false)) {
I
isidor 已提交
355 356 357
				this.toggleZenMode(true);
			}

E
Erich Gamma 已提交
358
			// Flag workbench as created once done
359
			const workbenchDone = (error?: Error) => {
E
Erich Gamma 已提交
360 361 362
				this.workbenchCreated = true;
				this.creationPromiseComplete(true);

B
Benjamin Pasero 已提交
363
				if (this.callbacks && this.callbacks.onWorkbenchStarted) {
364 365
					this.callbacks.onWorkbenchStarted({
						customKeybindingsCount: this.keybindingService.customKeybindingsCount(),
366 367
						restoreViewletDuration: viewletRestoreStopWatch ? Math.round(viewletRestoreStopWatch.elapsed()) : 0,
						restoreEditorsDuration: Math.round(editorRestoreStopWatch.elapsed()),
B
Benjamin Pasero 已提交
368
						pinnedViewlets: this.activitybarPart.getPinned(),
369 370
						restoredViewlet: viewletIdToRestore,
						restoredEditors
371
					});
B
Benjamin Pasero 已提交
372
				}
373 374 375 376 377 378

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

B
polish  
Benjamin Pasero 已提交
379
			// Join viewlet, panel and editor promises
380
			TPromise.join(compositeAndEditorPromises).then(() => workbenchDone(), error => workbenchDone(error));
E
Erich Gamma 已提交
381 382 383
		} catch (error) {

			// Print out error
384
			console.error(toErrorMessage(error, true));
E
Erich Gamma 已提交
385 386 387 388 389 390

			// Rethrow
			throw error;
		}
	}

391
	private createGlobalActions(): void {
B
Benjamin Pasero 已提交
392
		const isDeveloping = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment;
393

B
Benjamin Pasero 已提交
394
		// Actions registered here to adjust for developing vs built workbench
395 396
		const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
		workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyCode.KEY_R } : void 0), 'Reload Window');
B
Benjamin Pasero 已提交
397
		workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL, isDeveloping ? { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_I } } : void 0), 'Developer: Toggle Developer Tools', localize('developer', "Developer"));
B
Benjamin Pasero 已提交
398
		workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: isDeveloping ? null : KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', localize('file', "File"));
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420

		const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey));

		const quickOpenNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker';
		KeybindingsRegistry.registerCommandAndKeybindingRule({
			id: quickOpenNavigateNextInRecentFilesPickerId,
			weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
			handler: getQuickNavigateHandler(quickOpenNavigateNextInRecentFilesPickerId, true),
			when: recentFilesPickerContext,
			primary: KeyMod.CtrlCmd | KeyCode.KEY_R,
			mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R }
		});

		const quickOpenNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker';
		KeybindingsRegistry.registerCommandAndKeybindingRule({
			id: quickOpenNavigatePreviousInRecentFilesPicker,
			weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
			handler: getQuickNavigateHandler(quickOpenNavigatePreviousInRecentFilesPicker, false),
			when: recentFilesPickerContext,
			primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R,
			mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R }
		});
421 422
	}

423
	private resolveEditorsToOpen(): TPromise<IResourceInputType[]> {
B
Benjamin Pasero 已提交
424 425

		// Files to open, diff or create
426 427
		if (this.hasFilesToCreateOpenOrDiff) {
			const wbopt = this.workbenchParams.options;
B
Benjamin Pasero 已提交
428 429 430
			const filesToCreate = wbopt.filesToCreate || [];
			const filesToOpen = wbopt.filesToOpen || [];
			const filesToDiff = wbopt.filesToDiff;
B
Benjamin Pasero 已提交
431 432

			// Files to diff is exclusive
433
			if (filesToDiff && filesToDiff.length === 2) {
434 435 436 437 438
				return TPromise.as([<IResourceDiffInput>{
					leftResource: filesToDiff[0].resource,
					rightResource: filesToDiff[1].resource,
					options: { pinned: true }
				}]);
B
Benjamin Pasero 已提交
439 440 441 442
			}

			// Otherwise: Open/Create files
			else {
443 444 445
				const filesToCreateInputs: IUntitledResourceInput[] = filesToCreate.map(resourceInput => {
					return <IUntitledResourceInput>{
						filePath: resourceInput.resource.fsPath,
446 447
						options: { pinned: true }
					};
B
Benjamin Pasero 已提交
448
				});
449

450
				return TPromise.as([].concat(filesToOpen).concat(filesToCreateInputs));
B
Benjamin Pasero 已提交
451 452 453
			}
		}

454 455
		// Empty workbench
		else if (!this.contextService.hasWorkspace() && this.openUntitledFile()) {
456 457 458 459
			if (this.editorPart.hasEditorsToRestore()) {
				return TPromise.as([]); // do not open any empty untitled file if we have editors to restore
			}

460 461 462 463 464
			return this.backupFileService.hasBackups().then(hasBackups => {
				if (hasBackups) {
					return TPromise.as([]); // do not open any empty untitled file if we have backups to restore
				}

465
				return TPromise.as([<IUntitledResourceInput>{}]);
466
			});
B
Benjamin Pasero 已提交
467 468 469 470 471
		}

		return TPromise.as([]);
	}

472
	private openUntitledFile() {
473 474 475 476 477 478 479 480
		const startupEditor = this.configurationService.lookup('workbench.startupEditor');
		if (!startupEditor.user && !startupEditor.workspace) {
			const welcomeEnabled = this.configurationService.lookup('workbench.welcome.enabled');
			if (welcomeEnabled.value !== undefined && welcomeEnabled.value !== null) {
				return !welcomeEnabled.value;
			}
		}
		return startupEditor.value === 'newUntitledFile';
481 482
	}

E
Erich Gamma 已提交
483
	private initServices(): void {
B
Benjamin Pasero 已提交
484
		const { serviceCollection } = this.workbenchParams;
485

486
		this.toDispose.push(this.lifecycleService.onShutdown(this.shutdownComponents, this));
E
Erich Gamma 已提交
487 488

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

491 492 493
		// Clipboard
		serviceCollection.set(IClipboardService, new ClipboardService());

494 495 496 497 498 499
		// 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);

500 501 502
		// Progress 2
		serviceCollection.set(IProgressService2, new SyncDescriptor(ProgressService2));

503
		// Keybindings
A
Alex Dima 已提交
504
		this.contextKeyService = this.instantiationService.createInstance(ContextKeyService);
505
		serviceCollection.set(IContextKeyService, this.contextKeyService);
506

B
Benjamin Pasero 已提交
507
		this.keybindingService = this.instantiationService.createInstance(WorkbenchKeybindingService, window);
508
		serviceCollection.set(IKeybindingService, this.keybindingService);
A
Alex Dima 已提交
509

510 511 512
		// List
		serviceCollection.set(IListService, this.instantiationService.createInstance(ListService));

513
		// Context Menu
514
		serviceCollection.set(IContextMenuService, new SyncDescriptor(ContextMenuService));
515

516 517
		// Menus/Actions
		serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
518

P
Pine Wu 已提交
519
		// Sidebar part
520
		this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART);
E
Erich Gamma 已提交
521 522
		this.toDispose.push(this.sidebarPart);
		this.toShutdown.push(this.sidebarPart);
P
Pine Wu 已提交
523 524 525 526

		// Viewlet service
		this.viewletService = this.instantiationService.createInstance(ViewletService, this.sidebarPart);
		serviceCollection.set(IViewletService, this.viewletService);
E
Erich Gamma 已提交
527

I
isidor 已提交
528
		// Panel service (panel part)
529
		this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART);
I
isidor 已提交
530 531
		this.toDispose.push(this.panelPart);
		this.toShutdown.push(this.panelPart);
532
		serviceCollection.set(IPanelService, this.panelPart);
I
isidor 已提交
533

E
Erich Gamma 已提交
534
		// Activity service (activitybar part)
535
		this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
E
Erich Gamma 已提交
536 537
		this.toDispose.push(this.activitybarPart);
		this.toShutdown.push(this.activitybarPart);
538
		serviceCollection.set(IActivityBarService, this.activitybarPart);
E
Erich Gamma 已提交
539 540

		// Editor service (editor part)
541
		this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasFilesToCreateOpenOrDiff);
E
Erich Gamma 已提交
542 543
		this.toDispose.push(this.editorPart);
		this.toShutdown.push(this.editorPart);
544
		this.editorService = this.instantiationService.createInstance(WorkbenchEditorService, this.editorPart);
545
		serviceCollection.set(IWorkbenchEditorService, this.editorService);
546
		serviceCollection.set(IEditorGroupService, this.editorPart);
E
Erich Gamma 已提交
547

548 549 550 551 552 553
		// Title bar
		this.titlebarPart = this.instantiationService.createInstance(TitlebarPart, Identifiers.TITLEBAR_PART);
		this.toDispose.push(this.titlebarPart);
		this.toShutdown.push(this.titlebarPart);
		serviceCollection.set(ITitleService, this.titlebarPart);

554
		// File Service
555 556 557
		const fileService = this.instantiationService.createInstance(FileService);
		serviceCollection.set(IFileService, fileService);
		this.toDispose.push(fileService.onFileChanges(e => this.configurationService.handleWorkspaceFileEvents(e)));
558

559
		// History
560
		serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService));
561

562
		// Backup File Service
563
		this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath);
564
		serviceCollection.set(IBackupFileService, this.backupFileService);
565

566
		// Text File Service
567
		serviceCollection.set(ITextFileService, new SyncDescriptor(TextFileService));
568

J
Joao Moreno 已提交
569
		// SCM Service
570
		serviceCollection.set(ISCMService, new SyncDescriptor(SCMService));
571

B
Benjamin Pasero 已提交
572
		// Text Model Resolver Service
573
		serviceCollection.set(ITextModelService, new SyncDescriptor(TextModelResolverService));
B
Benjamin Pasero 已提交
574

575
		// Configuration Editing
576 577
		this.configurationEditingService = this.instantiationService.createInstance(ConfigurationEditingService);
		serviceCollection.set(IConfigurationEditingService, this.configurationEditingService);
578

579 580 581
		// Workspace Editing
		serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService));

582 583 584
		// Keybinding Editing
		serviceCollection.set(IKeybindingEditingService, this.instantiationService.createInstance(KeybindingsEditingService));

585
		// Configuration Resolver
B
Benjamin Pasero 已提交
586
		serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, process.env));
587

E
Erich Gamma 已提交
588
		// Quick open service (quick open controller)
589
		this.quickOpen = this.instantiationService.createInstance(QuickOpenController);
E
Erich Gamma 已提交
590 591
		this.toDispose.push(this.quickOpen);
		this.toShutdown.push(this.quickOpen);
592
		serviceCollection.set(IQuickOpenService, this.quickOpen);
E
Erich Gamma 已提交
593

B
polish  
Benjamin Pasero 已提交
594
		// Contributed services
B
Benjamin Pasero 已提交
595
		const contributedServices = getServices();
E
Erich Gamma 已提交
596
		for (let contributedService of contributedServices) {
597
			serviceCollection.set(contributedService.id, contributedService.descriptor);
E
Erich Gamma 已提交
598 599 600
		}

		// Set the some services to registries that have been created eagerly
601 602 603
		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 已提交
604 605 606 607 608
	}

	private initSettings(): void {

		// Sidebar visibility
609
		this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, !this.contextService.hasWorkspace());
E
Erich Gamma 已提交
610

I
isidor 已提交
611
		// Panel part visibility
612
		const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
B
Benjamin Pasero 已提交
613
		this.panelHidden = this.storageService.getBoolean(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE, true);
614 615
		if (!panelRegistry.getDefaultPanelId()) {
			this.panelHidden = true; // we hide panel part if there is no default panel
I
isidor 已提交
616
		}
I
isidor 已提交
617

E
Erich Gamma 已提交
618
		// Sidebar position
619 620
		const sideBarPosition = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
		this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT;
B
Benjamin Pasero 已提交
621 622

		// Statusbar visibility
623 624
		const statusBarVisible = this.configurationService.lookup<string>(Workbench.statusbarVisibleConfigurationKey).value;
		this.statusBarHidden = !statusBarVisible;
S
Sanders Lauture 已提交
625 626 627 628

		// Activity bar visibility
		const activityBarVisible = this.configurationService.lookup<string>(Workbench.activityBarVisibleConfigurationKey).value;
		this.activityBarHidden = !activityBarVisible;
I
isidor 已提交
629

I
isidor 已提交
630 631
		// Zen mode
		this.zenMode = {
I
isidor 已提交
632
			active: false,
633 634 635
			transitionedToFullScreen: false,
			wasSideBarVisible: false,
			wasPanelVisible: false
I
isidor 已提交
636
		};
E
Erich Gamma 已提交
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
	}

	/**
	 * 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 已提交
658
		const activeElement = document.activeElement;
E
Erich Gamma 已提交
659 660 661 662
		if (!activeElement) {
			return false;
		}

663 664 665 666 667
		const container = this.getContainer(part);
		return DOM.isAncestor(activeElement, container);
	}

	public getContainer(part: Parts): HTMLElement {
E
Erich Gamma 已提交
668 669
		let container: Builder = null;
		switch (part) {
B
Benjamin Pasero 已提交
670 671 672
			case Parts.TITLEBAR_PART:
				container = this.titlebarPart.getContainer();
				break;
E
Erich Gamma 已提交
673 674 675 676 677 678
			case Parts.ACTIVITYBAR_PART:
				container = this.activitybarPart.getContainer();
				break;
			case Parts.SIDEBAR_PART:
				container = this.sidebarPart.getContainer();
				break;
I
isidor 已提交
679 680 681
			case Parts.PANEL_PART:
				container = this.panelPart.getContainer();
				break;
E
Erich Gamma 已提交
682 683 684 685 686 687 688
			case Parts.EDITOR_PART:
				container = this.editorPart.getContainer();
				break;
			case Parts.STATUSBAR_PART:
				container = this.statusbarPart.getContainer();
				break;
		}
689
		return container && container.getHTMLElement();
E
Erich Gamma 已提交
690 691 692
	}

	public isVisible(part: Parts): boolean {
B
Benjamin Pasero 已提交
693
		switch (part) {
B
Benjamin Pasero 已提交
694
			case Parts.TITLEBAR_PART:
695
				return this.getCustomTitleBarStyle() && !browser.isFullscreen();
B
Benjamin Pasero 已提交
696
			case Parts.SIDEBAR_PART:
697
				return !this.sideBarHidden;
B
Benjamin Pasero 已提交
698
			case Parts.PANEL_PART:
699
				return !this.panelHidden;
B
Benjamin Pasero 已提交
700
			case Parts.STATUSBAR_PART:
701
				return !this.statusBarHidden;
S
Sanders Lauture 已提交
702
			case Parts.ACTIVITYBAR_PART:
703
				return !this.activityBarHidden;
704
		}
E
Erich Gamma 已提交
705 706 707 708

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

709 710
	public getTitleBarOffset(): number {
		let offset = 0;
711
		if (this.isVisible(Parts.TITLEBAR_PART)) {
712 713 714 715 716 717
			offset = 22 / browser.getZoomFactor(); // adjust the position based on title bar size and zoom factor
		}

		return offset;
	}

718
	private getCustomTitleBarStyle(): 'custom' {
B
Benjamin Pasero 已提交
719
		if (!isMacintosh) {
720
			return null; // custom title bar is only supported on Mac currently
B
Benjamin Pasero 已提交
721 722
		}

723
		const isDev = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment;
B
Benjamin Pasero 已提交
724
		if (isDev) {
725
			return null; // not enabled when developing due to https://github.com/electron/electron/issues/3647
B
Benjamin Pasero 已提交
726 727
		}

728
		const windowConfig = this.configurationService.getConfiguration<IWindowSettings>();
729 730 731 732 733
		if (windowConfig && windowConfig.window) {
			const useNativeTabs = windowConfig.window.nativeTabs;
			if (useNativeTabs) {
				return null; // native tabs on sierra do not work with custom title style
			}
B
Benjamin Pasero 已提交
734

735 736 737 738
			const style = windowConfig.window.titleBarStyle;
			if (style === 'custom') {
				return style;
			}
739 740 741
		}

		return null;
B
Benjamin Pasero 已提交
742 743
	}

744
	private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void {
745 746
		this.statusBarHidden = hidden;

I
isidor 已提交
747

748 749
		// Layout
		if (!skipLayout) {
750
			this.workbenchLayout.layout();
751 752 753
		}
	}

S
Sanders Lauture 已提交
754 755 756
	public setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void {
		this.activityBarHidden = hidden;

I
isidor 已提交
757

S
Sanders Lauture 已提交
758 759
		// Layout
		if (!skipLayout) {
760
			this.workbenchLayout.layout();
S
Sanders Lauture 已提交
761 762 763
		}
	}

764
	public setSideBarHidden(hidden: boolean, skipLayout?: boolean): TPromise<void> {
E
Erich Gamma 已提交
765 766 767 768 769 770 771 772 773
		this.sideBarHidden = hidden;

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

B
Benjamin Pasero 已提交
774
		// If sidebar becomes hidden, also hide the current active Viewlet if any
B
Benjamin Pasero 已提交
775
		let promise = TPromise.as<any>(null);
E
Erich Gamma 已提交
776
		if (hidden && this.sidebarPart.getActiveViewlet()) {
777 778 779
			promise = this.sidebarPart.hideActiveViewlet().then(() => {
				const activeEditor = this.editorPart.getActiveEditor();
				const activePanel = this.panelPart.getActivePanel();
B
Benjamin Pasero 已提交
780

781 782 783 784 785 786 787
				// 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 已提交
788 789
		}

B
Benjamin Pasero 已提交
790
		// If sidebar becomes visible, show last active Viewlet or default viewlet
E
Erich Gamma 已提交
791
		else if (!hidden && !this.sidebarPart.getActiveViewlet()) {
792
			const viewletToOpen = this.sidebarPart.getLastActiveViewletId();
E
Erich Gamma 已提交
793
			if (viewletToOpen) {
794
				promise = this.sidebarPart.openViewlet(viewletToOpen, true);
E
Erich Gamma 已提交
795 796 797
			}
		}

798
		return promise.then(() => {
799

800
			// Remember in settings
801 802 803 804 805 806
			const defaultHidden = !this.contextService.hasWorkspace();
			if (hidden !== defaultHidden) {
				this.storageService.store(Workbench.sidebarHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE);
			} else {
				this.storageService.remove(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE);
			}
807 808 809

			// Layout
			if (!skipLayout) {
810
				this.workbenchLayout.layout();
811 812
			}
		});
E
Erich Gamma 已提交
813 814
	}

815
	public setPanelHidden(hidden: boolean, skipLayout?: boolean): TPromise<void> {
I
isidor 已提交
816
		this.panelHidden = hidden;
I
isidor 已提交
817

818 819 820 821 822 823 824
		// Adjust CSS
		if (hidden) {
			this.workbench.addClass('nopanel');
		} else {
			this.workbench.removeClass('nopanel');
		}

I
isidor 已提交
825
		// If panel part becomes hidden, also hide the current active panel if any
826
		let promise = TPromise.as<any>(null);
I
isidor 已提交
827
		if (hidden && this.panelPart.getActivePanel()) {
828 829 830 831 832 833 834
			promise = this.panelPart.hideActivePanel().then(() => {
				// Pass Focus to Editor if Panel part is now hidden
				const editor = this.editorPart.getActiveEditor();
				if (editor) {
					editor.focus();
				}
			});
I
isidor 已提交
835 836 837 838
		}

		// If panel part becomes visible, show last active panel or default panel
		else if (!hidden && !this.panelPart.getActivePanel()) {
839
			const panelToOpen = this.panelPart.getLastActivePanelId();
I
isidor 已提交
840
			if (panelToOpen) {
841
				promise = this.panelPart.openPanel(panelToOpen, true);
I
isidor 已提交
842 843 844
			}
		}

845
		return promise.then(() => {
846

847
			// Remember in settings
848 849 850 851 852
			if (!hidden) {
				this.storageService.store(Workbench.panelHiddenSettingKey, 'false', StorageScope.WORKSPACE);
			} else {
				this.storageService.remove(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE);
			}
853 854 855

			// Layout
			if (!skipLayout) {
856
				this.workbenchLayout.layout();
857 858
			}
		});
I
isidor 已提交
859 860
	}

I
isidor 已提交
861
	public toggleMaximizedPanel(): void {
862
		this.workbenchLayout.layout({ toggleMaximizedPanel: true });
I
isidor 已提交
863 864
	}

I
isidor 已提交
865 866 867 868
	public isPanelMaximized(): boolean {
		return this.workbenchLayout.isPanelMaximized();
	}

E
Erich Gamma 已提交
869 870 871 872
	public getSideBarPosition(): Position {
		return this.sideBarPosition;
	}

873
	private setSideBarPosition(position: Position): void {
E
Erich Gamma 已提交
874
		if (this.sideBarHidden) {
875
			this.setSideBarHidden(false, true /* Skip Layout */).done(undefined, errors.onUnexpectedError);
E
Erich Gamma 已提交
876 877
		}

B
Benjamin Pasero 已提交
878 879
		const newPositionValue = (position === Position.LEFT) ? 'left' : 'right';
		const oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right';
E
Erich Gamma 已提交
880 881 882 883 884 885 886 887
		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);

888 889 890 891
		// Update Styles
		this.activitybarPart.updateStyles();
		this.sidebarPart.updateStyles();

E
Erich Gamma 已提交
892
		// Layout
893
		this.workbenchLayout.layout();
E
Erich Gamma 已提交
894 895
	}

B
Benjamin Pasero 已提交
896
	public dispose(): void {
E
Erich Gamma 已提交
897
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
898
			this.shutdownComponents();
E
Erich Gamma 已提交
899 900 901
			this.workbenchShutdown = true;
		}

J
Joao Moreno 已提交
902
		this.toDispose = dispose(this.toDispose);
E
Erich Gamma 已提交
903 904 905 906 907 908
	}

	/**
	 * Asks the workbench and all its UI components inside to lay out according to
	 * the containers dimension the workbench is living in.
	 */
B
Benjamin Pasero 已提交
909
	public layout(options?: ILayoutOptions): void {
E
Erich Gamma 已提交
910
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
911
			this.workbenchLayout.layout(options);
E
Erich Gamma 已提交
912 913 914
		}
	}

915 916 917 918 919 920
	private shutdownComponents(reason = ShutdownReason.QUIT): void {

		// Restore sidebar if we are being shutdown as a matter of a reload
		if (reason === ShutdownReason.RELOAD) {
			this.storageService.store(Workbench.sidebarRestoreSettingKey, 'true', StorageScope.WORKSPACE);
		}
I
isidor 已提交
921

I
isidor 已提交
922
		// Preserve zen mode only on reload. Real quit gets out of zen mode so novice users do not get stuck in zen mode.
923 924 925 926 927 928 929
		const zenConfig = this.configurationService.getConfiguration<IZenModeSettings>('zenMode');
		const zenModeActive = (zenConfig.restore || reason === ShutdownReason.RELOAD) && this.zenMode.active;
		if (zenModeActive) {
			this.storageService.store(Workbench.zenModeActiveSettingKey, true, StorageScope.WORKSPACE);
		} else {
			this.storageService.remove(Workbench.zenModeActiveSettingKey, StorageScope.WORKSPACE);
		}
E
Erich Gamma 已提交
930 931 932 933 934 935 936 937

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

	private registerListeners(): void {

		// Listen to editor changes
938
		this.toDispose.push(this.editorPart.onEditorsChanged(() => this.onEditorsChanged()));
939 940

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

944 945 946 947 948
		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()));
949 950 951 952 953 954 955 956 957 958

		// Fullscreen changes
		this.toDispose.push(browser.onDidChangeFullscreen(() => this.onFullscreenChanged()));
	}

	private onFullscreenChanged(): void {
		if (!this.isCreated) {
			return; // we need to be ready
		}

959
		// Apply as CSS class
960 961 962 963 964
		const isFullscreen = browser.isFullscreen();
		if (isFullscreen) {
			this.addClass('fullscreen');
		} else {
			this.removeClass('fullscreen');
I
isidor 已提交
965 966
			if (this.zenMode.transitionedToFullScreen && this.zenMode.active) {
				this.toggleZenMode();
I
isidor 已提交
967
			}
968
		}
969

970 971 972 973 974
		// Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update
		const hasCustomTitle = this.getCustomTitleBarStyle() === 'custom';
		if (hasCustomTitle) {
			this._onTitleBarVisibilityChange.fire();
			this.layout(); // handle title bar when fullscreen changes
975
		}
E
Erich Gamma 已提交
976 977
	}

978
	private onEditorsChanged(): void {
B
Benjamin Pasero 已提交
979
		const visibleEditors = this.editorService.getVisibleEditors().length;
E
Erich Gamma 已提交
980 981 982 983

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

984
		const editorContainer = this.editorPart.getContainer();
E
Erich Gamma 已提交
985
		if (visibleEditors === 0) {
986
			this.editorsVisibleContext.reset();
987
			this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty'));
E
Erich Gamma 已提交
988
		} else {
989
			this.editorsVisibleContext.set(true);
990
			this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty'));
E
Erich Gamma 已提交
991 992 993
		}
	}

994
	private onDidUpdateConfiguration(skipLayout?: boolean): void {
995
		const newSidebarPositionValue = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
996
		const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT;
997 998 999
		if (newSidebarPosition !== this.getSideBarPosition()) {
			this.setSideBarPosition(newSidebarPosition);
		}
1000

1001 1002 1003 1004 1005
		if (!this.zenMode.active) {
			const newStatusbarHiddenValue = !this.configurationService.lookup<boolean>(Workbench.statusbarVisibleConfigurationKey).value;
			if (newStatusbarHiddenValue !== this.statusBarHidden) {
				this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1006

1007 1008 1009 1010
			const newActivityBarHiddenValue = !this.configurationService.lookup<boolean>(Workbench.activityBarVisibleConfigurationKey).value;
			if (newActivityBarHiddenValue !== this.activityBarHidden) {
				this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1011
		}
1012 1013
	}

E
Erich Gamma 已提交
1014 1015 1016 1017
	private createWorkbenchLayout(): void {
		this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout,
			$(this.container),							// Parent
			this.workbench,								// Workbench Container
I
isidor 已提交
1018
			{
B
Benjamin Pasero 已提交
1019
				titlebar: this.titlebarPart,			// Title Bar
I
isidor 已提交
1020 1021 1022 1023 1024 1025
				activitybar: this.activitybarPart,		// Activity Bar
				editor: this.editorPart,				// Editor
				sidebar: this.sidebarPart,				// Sidebar
				panel: this.panelPart,					// Panel Part
				statusbar: this.statusbarPart,			// Statusbar
			},
B
Benjamin Pasero 已提交
1026
			this.quickOpen								// Quickopen
E
Erich Gamma 已提交
1027 1028 1029 1030 1031 1032 1033 1034 1035
		);

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

	private createWorkbench(): void {

		// Create Workbench DIV Off-DOM
		this.workbenchContainer = $('.monaco-workbench-container');
1036
		this.workbench = $().div({ 'class': 'monaco-workbench ' + (isWindows ? 'windows' : isLinux ? 'linux' : 'mac'), id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer);
E
Erich Gamma 已提交
1037 1038 1039 1040 1041 1042 1043 1044
	}

	private renderWorkbench(): void {

		// Apply sidebar state as CSS class
		if (this.sideBarHidden) {
			this.workbench.addClass('nosidebar');
		}
1045 1046 1047
		if (this.panelHidden) {
			this.workbench.addClass('nopanel');
		}
E
Erich Gamma 已提交
1048

B
Benjamin Pasero 已提交
1049
		// Apply title style if shown
1050 1051 1052 1053 1054 1055 1056 1057
		const titleStyle = this.getCustomTitleBarStyle();
		if (titleStyle) {
			DOM.addClass(this.parent, `titlebar-style-${titleStyle}`);
		}

		// Apply fullscreen state
		if (browser.isFullscreen()) {
			this.workbench.addClass('fullscreen');
B
Benjamin Pasero 已提交
1058 1059
		}

E
Erich Gamma 已提交
1060
		// Create Parts
B
Benjamin Pasero 已提交
1061
		this.createTitlebarPart();
E
Erich Gamma 已提交
1062 1063 1064
		this.createActivityBarPart();
		this.createSidebarPart();
		this.createEditorPart();
1065
		this.createPanelPart();
E
Erich Gamma 已提交
1066 1067 1068 1069 1070 1071
		this.createStatusbarPart();

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

B
Benjamin Pasero 已提交
1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
	private createTitlebarPart(): void {
		const titlebarContainer = $(this.workbench).div({
			'class': ['part', 'titlebar'],
			id: Identifiers.TITLEBAR_PART,
			role: 'contentinfo'
		});

		this.titlebarPart.create(titlebarContainer);
	}

E
Erich Gamma 已提交
1082
	private createActivityBarPart(): void {
B
Benjamin Pasero 已提交
1083
		const activitybarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
1084 1085
			.div({
				'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
1086 1087
				id: Identifiers.ACTIVITYBAR_PART,
				role: 'navigation'
E
Erich Gamma 已提交
1088 1089 1090 1091 1092 1093
			});

		this.activitybarPart.create(activitybarPartContainer);
	}

	private createSidebarPart(): void {
B
Benjamin Pasero 已提交
1094
		const sidebarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
1095 1096
			.div({
				'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
1097 1098
				id: Identifiers.SIDEBAR_PART,
				role: 'complementary'
E
Erich Gamma 已提交
1099 1100 1101 1102 1103
			});

		this.sidebarPart.create(sidebarPartContainer);
	}

I
isidor 已提交
1104
	private createPanelPart(): void {
B
Benjamin Pasero 已提交
1105
		const panelPartContainer = $(this.workbench)
I
isidor 已提交
1106
			.div({
1107
				'class': ['part', 'panel'],
1108 1109
				id: Identifiers.PANEL_PART,
				role: 'complementary'
I
isidor 已提交
1110 1111 1112 1113 1114
			});

		this.panelPart.create(panelPartContainer);
	}

E
Erich Gamma 已提交
1115
	private createEditorPart(): void {
B
Benjamin Pasero 已提交
1116
		const editorContainer = $(this.workbench)
E
Erich Gamma 已提交
1117
			.div({
1118
				'class': ['part', 'editor', 'empty'],
1119 1120
				id: Identifiers.EDITOR_PART,
				role: 'main'
E
Erich Gamma 已提交
1121 1122 1123 1124 1125 1126
			});

		this.editorPart.create(editorContainer);
	}

	private createStatusbarPart(): void {
B
Benjamin Pasero 已提交
1127
		const statusbarContainer = $(this.workbench).div({
E
Erich Gamma 已提交
1128
			'class': ['part', 'statusbar'],
1129 1130
			id: Identifiers.STATUSBAR_PART,
			role: 'contentinfo'
E
Erich Gamma 已提交
1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
		});

		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 已提交
1148 1149 1150 1151 1152 1153
	public getPanelPart(): PanelPart {
		assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.');

		return this.panelPart;
	}

E
Erich Gamma 已提交
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
	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);
		}
	}
1171 1172 1173 1174

	public getWorkbenchElementId(): string {
		return Identifiers.WORKBENCH_CONTAINER;
	}
1175

I
isidor 已提交
1176
	public toggleZenMode(skipLayout?: boolean): void {
I
isidor 已提交
1177 1178
		this.zenMode.active = !this.zenMode.active;
		// Check if zen mode transitioned to full screen and if now we are out of zen mode -> we need to go out of full screen
1179
		let toggleFullScreen = false;
I
isidor 已提交
1180
		if (this.zenMode.active) {
I
isidor 已提交
1181 1182
			const config = this.configurationService.getConfiguration<IZenModeSettings>('zenMode');
			toggleFullScreen = !browser.isFullscreen() && config.fullScreen;
I
isidor 已提交
1183
			this.zenMode.transitionedToFullScreen = toggleFullScreen;
1184 1185
			this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART);
			this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART);
1186
			this.setPanelHidden(true, true).done(undefined, errors.onUnexpectedError);
1187
			this.setSideBarHidden(true, true).done(undefined, errors.onUnexpectedError);
I
isidor 已提交
1188

1189 1190 1191
			if (config.hideActivityBar) {
				this.setActivityBarHidden(true, true);
			}
I
isidor 已提交
1192
			if (config.hideStatusBar) {
I
isidor 已提交
1193 1194
				this.setStatusBarHidden(true, true);
			}
I
isidor 已提交
1195
			if (config.hideTabs) {
I
isidor 已提交
1196 1197
				this.editorPart.hideTabs(true);
			}
1198
		} else {
1199
			if (this.zenMode.wasPanelVisible) {
1200
				this.setPanelHidden(false, true).done(undefined, errors.onUnexpectedError);
1201
			}
1202
			if (this.zenMode.wasSideBarVisible) {
1203
				this.setSideBarHidden(false, true).done(undefined, errors.onUnexpectedError);
1204
			}
1205 1206
			// Status bar and activity bar visibility come from settings -> update their visibility.
			this.onDidUpdateConfiguration(true);
I
isidor 已提交
1207
			this.editorPart.hideTabs(false);
I
isidor 已提交
1208 1209 1210 1211
			const activeEditor = this.editorPart.getActiveEditor();
			if (activeEditor) {
				activeEditor.focus();
			}
1212
			toggleFullScreen = this.zenMode.transitionedToFullScreen && browser.isFullscreen();
I
isidor 已提交
1213
		}
1214
		this.inZenMode.set(this.zenMode.active);
I
isidor 已提交
1215

I
isidor 已提交
1216
		if (!skipLayout) {
1217
			this.layout();
I
isidor 已提交
1218
		}
1219 1220 1221
		if (toggleFullScreen) {
			this.windowService.toggleFullScreen().done(undefined, errors.onUnexpectedError);
		}
I
isidor 已提交
1222 1223
	}

1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
	// Resize requested part along the main axis
	// layout will do all the math for us and adjusts the other Parts
	public resizePart(part: Parts, sizeChange: number): void {
		switch (part) {
			case Parts.SIDEBAR_PART:
			case Parts.PANEL_PART:
			case Parts.EDITOR_PART:
				this.workbenchLayout.resizePart(part, sizeChange);
				break;
			default:
B
Benjamin Pasero 已提交
1234
				return; // Cannot resize other parts
1235 1236 1237 1238
		}
	}


1239
	private shouldRestoreLastOpenedViewlet(): boolean {
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
		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;
	}
J
Johannes Rieken 已提交
1251
}