workbench.ts 51.2 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';
16
import { Delayer, RunOnceScheduler } 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';
B
Benjamin Pasero 已提交
27
import { Position as EditorPosition, IResourceDiffInput, IUntitledResourceInput, IEditor, IResourceInput } from 'vs/platform/editor/common/editor';
J
Johannes Rieken 已提交
28
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
29
import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
J
Johannes Rieken 已提交
30 31 32 33 34 35
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 已提交
36 37
import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
import { WorkbenchLayout } from 'vs/workbench/browser/layout';
38
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions';
J
Johannes Rieken 已提交
39 40 41 42
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 已提交
43
import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService';
44
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
45 46 47 48
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';
49
import { WorkspaceService, DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configuration';
J
Johannes Rieken 已提交
50 51
import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
52 53
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
J
Johannes Rieken 已提交
54 55
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
56
import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
J
Johannes Rieken 已提交
57
import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
58
import { IActivityBarService } from 'vs/workbench/services/activity/common/activityBarService';
B
Benjamin Pasero 已提交
59
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
P
Pine Wu 已提交
60
import { ViewletService } from 'vs/workbench/services/viewlet/browser/viewletService';
61
import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService';
J
Johannes Rieken 已提交
62
import { IFileService } from 'vs/platform/files/common/files';
63
import { IListService, ListService } from 'vs/platform/list/browser/listService';
J
Johannes Rieken 已提交
64 65 66
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 已提交
67
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
J
Johannes Rieken 已提交
68
import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService';
69
import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService';
J
Johannes Rieken 已提交
70
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
71 72
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService';
J
Johannes Rieken 已提交
73 74 75 76 77 78
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 已提交
79 80
import { ISCMService } from 'vs/workbench/services/scm/common/scm';
import { SCMService } from 'vs/workbench/services/scm/common/scmService';
81 82
import { IProgressService2 } from 'vs/platform/progress/common/progress';
import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2';
83
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
84
import { ITextModelService } from 'vs/editor/common/services/resolverService';
J
Johannes Rieken 已提交
85
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
86
import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
B
Benjamin Pasero 已提交
87
import { IWindowService, IWindowConfiguration as IWindowSettings, IWindowConfiguration, IPath } from 'vs/platform/windows/common/windows';
J
Johannes Rieken 已提交
88 89
import { IMessageService } from 'vs/platform/message/common/message';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
90
import { IMenuService, SyncActionDescriptor } from 'vs/platform/actions/common/actions';
J
Johannes Rieken 已提交
91 92 93 94
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';
B
Benjamin Pasero 已提交
95
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
96
import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar } from 'vs/workbench/electron-browser/actions';
97
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
98
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
B
Benjamin Pasero 已提交
99
import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService';
B
Benjamin Pasero 已提交
100
import URI from 'vs/base/common/uri';
101 102
import { FileDecorationsService } from 'vs/workbench/services/fileDecorations/browser/fileDecorationsService';
import { IFileDecorationsService } from 'vs/workbench/services/fileDecorations/browser/fileDecorations';
103

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

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

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

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

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

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

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

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

160 161
	private static closeWhenEmptyConfigurationKey = 'window.closeWhenEmpty';

162 163
	private static fontAliasingConfigurationKey = 'workbench.fontAliasing';

164 165
	private _onTitleBarVisibilityChange: Emitter<void>;

166
	public _serviceBrand: any;
E
Erich Gamma 已提交
167

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

215
	constructor(
B
Benjamin Pasero 已提交
216
		parent: HTMLElement,
217
		container: HTMLElement,
218
		configuration: IWindowConfiguration,
219
		serviceCollection: ServiceCollection,
220
		@IInstantiationService private instantiationService: IInstantiationService,
221
		@IWorkspaceContextService private contextService: IWorkspaceContextService,
222 223 224
		@IStorageService private storageService: IStorageService,
		@ILifecycleService private lifecycleService: ILifecycleService,
		@IMessageService private messageService: IMessageService,
225
		@IConfigurationService private configurationService: WorkspaceService,
226
		@ITelemetryService private telemetryService: ITelemetryService,
I
isidor 已提交
227 228
		@IEnvironmentService private environmentService: IEnvironmentService,
		@IWindowService private windowService: IWindowService
229
	) {
B
Benjamin Pasero 已提交
230
		this.parent = parent;
231
		this.container = container;
E
Erich Gamma 已提交
232 233

		this.workbenchParams = {
234
			configuration,
235
			serviceCollection
E
Erich Gamma 已提交
236 237
		};

238
		this.hasFilesToCreateOpenOrDiff =
B
Benjamin Pasero 已提交
239 240 241
			(configuration.filesToCreate && configuration.filesToCreate.length > 0) ||
			(configuration.filesToOpen && configuration.filesToOpen.length > 0) ||
			(configuration.filesToDiff && configuration.filesToDiff.length > 0);
242

E
Erich Gamma 已提交
243 244
		this.toDispose = [];
		this.toShutdown = [];
245

E
Erich Gamma 已提交
246
		this.editorBackgroundDelayer = new Delayer<void>(50);
247
		this.closeEmptyWindowScheduler = new RunOnceScheduler(() => this.onAllEditorsClosed(), 50);
E
Erich Gamma 已提交
248

249 250
		this._onTitleBarVisibilityChange = new Emitter<void>();

251
		this.creationPromise = new TPromise<boolean>(c => {
E
Erich Gamma 已提交
252 253 254 255
			this.creationPromiseComplete = c;
		});
	}

256 257 258 259
	public get onTitleBarVisibilityChange(): Event<void> {
		return this._onTitleBarVisibilityChange.event;
	}

260 261 262 263 264 265
	public get onEditorLayout(): Event<void> {
		return chain(this.editorPart.onLayout)
			.map(() => void 0)
			.event;
	}

E
Erich Gamma 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
	/**
	 * 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();

281 282 283
			// Install some global actions
			this.createGlobalActions();

E
Erich Gamma 已提交
284 285 286 287 288 289
			// Services
			this.initServices();
			if (this.callbacks && this.callbacks.onServicesCreated) {
				this.callbacks.onServicesCreated();
			}

290
			// Contexts
291 292
			this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService);
			this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
I
isidor 已提交
293
			this.inZenMode = InZenModeContext.bindTo(this.contextKeyService);
294

E
Erich Gamma 已提交
295 296 297 298 299 300 301 302 303 304 305 306
			// Register Listeners
			this.registerListeners();

			// Settings
			this.initSettings();

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

			// Workbench Layout
			this.createWorkbenchLayout();

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

P
Pine Wu 已提交
310
			// Restore last opened viewlet
311
			let viewletRestoreStopWatch: StopWatch;
312
			let viewletIdToRestore: string;
313
			if (!this.sideBarHidden) {
314

315
				if (this.shouldRestoreLastOpenedViewlet()) {
316
					viewletIdToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE);
B
polish  
Benjamin Pasero 已提交
317 318 319
				}

				if (!viewletIdToRestore) {
320
					viewletIdToRestore = this.viewletService.getDefaultViewletId();
P
Pine Wu 已提交
321
				}
322

323
				viewletRestoreStopWatch = StopWatch.create();
324 325
				const viewletTimer = startTimer('restore:viewlet');
				compositeAndEditorPromises.push(viewletTimer.while(this.viewletService.openViewlet(viewletIdToRestore)).then(() => {
326 327
					viewletRestoreStopWatch.stop();
				}));
E
Erich Gamma 已提交
328 329
			}

B
Benjamin Pasero 已提交
330
			// Load Panel
331
			const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
332 333
			const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId());
			if (!this.panelHidden && !!panelId) {
I
isidor 已提交
334
				compositeAndEditorPromises.push(this.panelPart.openPanel(panelId, false));
335 336
			}

B
Benjamin Pasero 已提交
337
			// Load Editors
338
			const editorRestoreStopWatch = StopWatch.create();
339
			const restoredEditors: string[] = [];
340 341
			const editorsTimer = startTimer('restore:editors');
			compositeAndEditorPromises.push(editorsTimer.while(this.resolveEditorsToOpen().then(inputs => {
342
				let editorOpenPromise: TPromise<IEditor[]>;
343 344
				if (inputs.length) {
					editorOpenPromise = this.editorService.openEditors(inputs.map(input => { return { input, position: EditorPosition.ONE }; }));
B
Benjamin Pasero 已提交
345
				} else {
B
Benjamin Pasero 已提交
346
					editorOpenPromise = this.editorPart.restoreEditors();
347
				}
348

349
				return editorOpenPromise.then(editors => {
B
Benjamin Pasero 已提交
350
					this.handleEditorBackground(); // make sure we show the proper background in the editor area
351
					editorRestoreStopWatch.stop();
B
Benjamin Pasero 已提交
352

353
					for (const editor of editors) {
B
Benjamin Pasero 已提交
354 355 356 357 358 359
						if (editor) {
							if (editor.input) {
								restoredEditors.push(editor.input.getName());
							} else {
								restoredEditors.push(`other:${editor.getId()}`);
							}
360 361
						}
					}
E
Erich Gamma 已提交
362
				});
363
			})));
E
Erich Gamma 已提交
364

365
			if (this.storageService.getBoolean(Workbench.zenModeActiveSettingKey, StorageScope.WORKSPACE, false)) {
I
isidor 已提交
366 367 368
				this.toggleZenMode(true);
			}

E
Erich Gamma 已提交
369
			// Flag workbench as created once done
370
			const workbenchDone = (error?: Error) => {
E
Erich Gamma 已提交
371 372 373
				this.workbenchCreated = true;
				this.creationPromiseComplete(true);

B
Benjamin Pasero 已提交
374
				if (this.callbacks && this.callbacks.onWorkbenchStarted) {
375 376
					this.callbacks.onWorkbenchStarted({
						customKeybindingsCount: this.keybindingService.customKeybindingsCount(),
377 378
						restoreViewletDuration: viewletRestoreStopWatch ? Math.round(viewletRestoreStopWatch.elapsed()) : 0,
						restoreEditorsDuration: Math.round(editorRestoreStopWatch.elapsed()),
B
Benjamin Pasero 已提交
379
						pinnedViewlets: this.activitybarPart.getPinned(),
380 381
						restoredViewlet: viewletIdToRestore,
						restoredEditors
382
					});
B
Benjamin Pasero 已提交
383
				}
384 385 386 387 388 389

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

B
polish  
Benjamin Pasero 已提交
390
			// Join viewlet, panel and editor promises
391
			TPromise.join(compositeAndEditorPromises).then(() => workbenchDone(), error => workbenchDone(error));
E
Erich Gamma 已提交
392 393 394
		} catch (error) {

			// Print out error
395
			console.error(toErrorMessage(error, true));
E
Erich Gamma 已提交
396 397 398 399 400 401

			// Rethrow
			throw error;
		}
	}

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

B
Benjamin Pasero 已提交
405
		// Actions registered here to adjust for developing vs built workbench
406 407
		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 已提交
408
		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 已提交
409
		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"));
410

411 412 413 414 415 416 417 418 419
		// Actions for macOS native tabs management (only when enabled)
		const windowConfig = this.configurationService.getConfiguration<IWindowSettings>();
		if (windowConfig && windowConfig.window && windowConfig.window.nativeTabs) {
			workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousWindowTab, ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL), 'Show Previous Window Tab');
			workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextWindowTab, ShowNextWindowTab.ID, ShowNextWindowTab.LABEL), 'Show Next Window Tab');
			workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(MoveWindowTabToNewWindow, MoveWindowTabToNewWindow.ID, MoveWindowTabToNewWindow.LABEL), 'Move Window Tab to New Window');
			workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(MergeAllWindowTabs, MergeAllWindowTabs.ID, MergeAllWindowTabs.LABEL), 'Merge All Windows');
			workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleWindowTabsBar, ToggleWindowTabsBar.ID, ToggleWindowTabsBar.LABEL), 'Toggle Window Tabs Bar');
		}
420 421
	}

422
	private resolveEditorsToOpen(): TPromise<IResourceInputType[]> {
423
		const config = this.workbenchParams.configuration;
B
Benjamin Pasero 已提交
424 425

		// Files to open, diff or create
426
		if (this.hasFilesToCreateOpenOrDiff) {
B
Benjamin Pasero 已提交
427 428

			// Files to diff is exclusive
429
			const filesToDiff = this.toInputs(config.filesToDiff, false);
430
			if (filesToDiff && filesToDiff.length === 2) {
431 432 433 434 435
				return TPromise.as([<IResourceDiffInput>{
					leftResource: filesToDiff[0].resource,
					rightResource: filesToDiff[1].resource,
					options: { pinned: true }
				}]);
B
Benjamin Pasero 已提交
436 437
			}

438 439
			const filesToCreate = this.toInputs(config.filesToCreate, true);
			const filesToOpen = this.toInputs(config.filesToOpen, false);
440

441 442
			// Otherwise: Open/Create files
			return TPromise.as([...filesToOpen, ...filesToCreate]);
B
Benjamin Pasero 已提交
443 444
		}

445
		// Empty workbench
446
		else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.openUntitledFile()) {
447 448 449 450
			if (this.editorPart.hasEditorsToRestore()) {
				return TPromise.as([]); // do not open any empty untitled file if we have editors to restore
			}

451 452 453 454 455
			return this.backupFileService.hasBackups().then(hasBackups => {
				if (hasBackups) {
					return TPromise.as([]); // do not open any empty untitled file if we have backups to restore
				}

456
				return TPromise.as([<IUntitledResourceInput>{}]);
457
			});
B
Benjamin Pasero 已提交
458 459 460 461 462
		}

		return TPromise.as([]);
	}

463
	private toInputs(paths: IPath[], isNew: boolean): (IResourceInput | IUntitledResourceInput)[] {
B
Benjamin Pasero 已提交
464 465 466 467 468
		if (!paths || !paths.length) {
			return [];
		}

		return paths.map(p => {
469 470 471 472 473 474 475
			const resource = URI.file(p.filePath);
			let input: IResourceInput | IUntitledResourceInput;
			if (isNew) {
				input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput;
			} else {
				input = { resource, options: { pinned: true } } as IResourceInput;
			}
B
Benjamin Pasero 已提交
476

477
			if (!isNew && p.lineNumber) {
B
Benjamin Pasero 已提交
478 479 480 481 482 483 484 485 486 487
				input.options.selection = {
					startLineNumber: p.lineNumber,
					startColumn: p.columnNumber
				};
			}

			return input;
		});
	}

488
	private openUntitledFile() {
489
		const startupEditor = this.configurationService.lookup('workbench.startupEditor');
B
Benjamin Pasero 已提交
490 491

		// Fallback to previous workbench.welcome.enabled setting in case startupEditor is not defined
492 493
		if (!startupEditor.user && !startupEditor.workspace) {
			const welcomeEnabled = this.configurationService.lookup('workbench.welcome.enabled');
B
Benjamin Pasero 已提交
494
			if (typeof welcomeEnabled.value === 'boolean') {
495 496 497
				return !welcomeEnabled.value;
			}
		}
B
Benjamin Pasero 已提交
498

499
		return startupEditor.value === 'newUntitledFile';
500 501
	}

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

505
		this.toDispose.push(this.lifecycleService.onShutdown(this.shutdownComponents, this));
E
Erich Gamma 已提交
506 507

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

510 511 512
		// Clipboard
		serviceCollection.set(IClipboardService, new ClipboardService());

513 514 515 516 517 518
		// 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);

519 520 521
		// Progress 2
		serviceCollection.set(IProgressService2, new SyncDescriptor(ProgressService2));

522
		// Keybindings
A
Alex Dima 已提交
523
		this.contextKeyService = this.instantiationService.createInstance(ContextKeyService);
524
		serviceCollection.set(IContextKeyService, this.contextKeyService);
525

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

529 530 531
		// List
		serviceCollection.set(IListService, this.instantiationService.createInstance(ListService));

532
		// Context Menu
533
		serviceCollection.set(IContextMenuService, new SyncDescriptor(ContextMenuService));
534

535 536
		// Menus/Actions
		serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
537

P
Pine Wu 已提交
538
		// Sidebar part
539
		this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART);
E
Erich Gamma 已提交
540 541
		this.toDispose.push(this.sidebarPart);
		this.toShutdown.push(this.sidebarPart);
P
Pine Wu 已提交
542 543 544 545

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

I
isidor 已提交
547
		// Panel service (panel part)
548
		this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART);
I
isidor 已提交
549 550
		this.toDispose.push(this.panelPart);
		this.toShutdown.push(this.panelPart);
551
		serviceCollection.set(IPanelService, this.panelPart);
I
isidor 已提交
552

E
Erich Gamma 已提交
553
		// Activity service (activitybar part)
554
		this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
E
Erich Gamma 已提交
555 556
		this.toDispose.push(this.activitybarPart);
		this.toShutdown.push(this.activitybarPart);
557
		serviceCollection.set(IActivityBarService, this.activitybarPart);
E
Erich Gamma 已提交
558

559 560 561 562 563
		// File Service
		this.fileService = this.instantiationService.createInstance(RemoteFileService);
		serviceCollection.set(IFileService, this.fileService);
		this.toDispose.push(this.fileService.onFileChanges(e => this.configurationService.handleWorkspaceFileEvents(e)));

E
Erich Gamma 已提交
564
		// Editor service (editor part)
565
		this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasFilesToCreateOpenOrDiff);
E
Erich Gamma 已提交
566 567
		this.toDispose.push(this.editorPart);
		this.toShutdown.push(this.editorPart);
568
		this.editorService = this.instantiationService.createInstance(WorkbenchEditorService, this.editorPart);
569
		serviceCollection.set(IWorkbenchEditorService, this.editorService);
570
		serviceCollection.set(IEditorGroupService, this.editorPart);
E
Erich Gamma 已提交
571

572 573 574 575 576 577
		// 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);

578
		// History
579
		serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService));
580

581
		// Backup File Service
582
		this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath);
583
		serviceCollection.set(IBackupFileService, this.backupFileService);
584

585
		// Text File Service
586
		serviceCollection.set(ITextFileService, new SyncDescriptor(TextFileService));
587

588 589 590
		// File Decorations
		serviceCollection.set(IFileDecorationsService, new SyncDescriptor(FileDecorationsService));

J
Joao Moreno 已提交
591
		// SCM Service
592
		serviceCollection.set(ISCMService, new SyncDescriptor(SCMService));
593

B
Benjamin Pasero 已提交
594
		// Text Model Resolver Service
595
		serviceCollection.set(ITextModelService, new SyncDescriptor(TextModelResolverService));
B
Benjamin Pasero 已提交
596

597 598 599 600
		// JSON Editing
		const jsonEditingService = this.instantiationService.createInstance(JSONEditingService);
		serviceCollection.set(IJSONEditingService, jsonEditingService);

601
		// Configuration Editing
602 603
		this.configurationEditingService = this.instantiationService.createInstance(ConfigurationEditingService);
		serviceCollection.set(IConfigurationEditingService, this.configurationEditingService);
604

605 606 607
		// Workspace Editing
		serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService));

608 609 610
		// Keybinding Editing
		serviceCollection.set(IKeybindingEditingService, this.instantiationService.createInstance(KeybindingsEditingService));

611
		// Configuration Resolver
B
Benjamin Pasero 已提交
612
		serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, process.env));
613

E
Erich Gamma 已提交
614
		// Quick open service (quick open controller)
615
		this.quickOpen = this.instantiationService.createInstance(QuickOpenController);
E
Erich Gamma 已提交
616 617
		this.toDispose.push(this.quickOpen);
		this.toShutdown.push(this.quickOpen);
618
		serviceCollection.set(IQuickOpenService, this.quickOpen);
619

B
polish  
Benjamin Pasero 已提交
620
		// Contributed services
B
Benjamin Pasero 已提交
621
		const contributedServices = getServices();
E
Erich Gamma 已提交
622
		for (let contributedService of contributedServices) {
623
			serviceCollection.set(contributedService.id, contributedService.descriptor);
E
Erich Gamma 已提交
624 625 626
		}

		// Set the some services to registries that have been created eagerly
627 628
		Registry.as<IActionBarRegistry>(ActionBarExtensions.Actionbar).setInstantiationService(this.instantiationService);
		Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).setInstantiationService(this.instantiationService);
629
		Registry.as<IEditorInputFactoryRegistry>(EditorExtensions.EditorInputFactories).setInstantiationService(this.instantiationService);
630

631
		this.instantiationService.createInstance(DefaultConfigurationExportHelper);
E
Erich Gamma 已提交
632 633 634 635 636
	}

	private initSettings(): void {

		// Sidebar visibility
637
		this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, this.contextService.getWorkbenchState() === WorkbenchState.EMPTY);
E
Erich Gamma 已提交
638

I
isidor 已提交
639
		// Panel part visibility
640
		const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
B
Benjamin Pasero 已提交
641
		this.panelHidden = this.storageService.getBoolean(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE, true);
642 643
		if (!panelRegistry.getDefaultPanelId()) {
			this.panelHidden = true; // we hide panel part if there is no default panel
I
isidor 已提交
644
		}
I
isidor 已提交
645

E
Erich Gamma 已提交
646
		// Sidebar position
647 648
		const sideBarPosition = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
		this.sideBarPosition = (sideBarPosition === 'right') ? Position.RIGHT : Position.LEFT;
B
Benjamin Pasero 已提交
649 650

		// Statusbar visibility
651 652
		const statusBarVisible = this.configurationService.lookup<string>(Workbench.statusbarVisibleConfigurationKey).value;
		this.statusBarHidden = !statusBarVisible;
S
Sanders Lauture 已提交
653 654 655 656

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

658 659 660
		// Font aliasing
		this.fontAliasing = this.configurationService.lookup<string>(Workbench.fontAliasingConfigurationKey).value;

I
isidor 已提交
661 662
		// Zen mode
		this.zenMode = {
I
isidor 已提交
663
			active: false,
664 665 666
			transitionedToFullScreen: false,
			wasSideBarVisible: false,
			wasPanelVisible: false
I
isidor 已提交
667
		};
E
Erich Gamma 已提交
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
	}

	/**
	 * 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 已提交
689
		const activeElement = document.activeElement;
E
Erich Gamma 已提交
690 691 692 693
		if (!activeElement) {
			return false;
		}

694 695 696 697 698
		const container = this.getContainer(part);
		return DOM.isAncestor(activeElement, container);
	}

	public getContainer(part: Parts): HTMLElement {
E
Erich Gamma 已提交
699 700
		let container: Builder = null;
		switch (part) {
B
Benjamin Pasero 已提交
701 702 703
			case Parts.TITLEBAR_PART:
				container = this.titlebarPart.getContainer();
				break;
E
Erich Gamma 已提交
704 705 706 707 708 709
			case Parts.ACTIVITYBAR_PART:
				container = this.activitybarPart.getContainer();
				break;
			case Parts.SIDEBAR_PART:
				container = this.sidebarPart.getContainer();
				break;
I
isidor 已提交
710 711 712
			case Parts.PANEL_PART:
				container = this.panelPart.getContainer();
				break;
E
Erich Gamma 已提交
713 714 715 716 717 718 719
			case Parts.EDITOR_PART:
				container = this.editorPart.getContainer();
				break;
			case Parts.STATUSBAR_PART:
				container = this.statusbarPart.getContainer();
				break;
		}
720
		return container && container.getHTMLElement();
E
Erich Gamma 已提交
721 722 723
	}

	public isVisible(part: Parts): boolean {
B
Benjamin Pasero 已提交
724
		switch (part) {
B
Benjamin Pasero 已提交
725
			case Parts.TITLEBAR_PART:
726
				return this.getCustomTitleBarStyle() && !browser.isFullscreen();
B
Benjamin Pasero 已提交
727
			case Parts.SIDEBAR_PART:
728
				return !this.sideBarHidden;
B
Benjamin Pasero 已提交
729
			case Parts.PANEL_PART:
730
				return !this.panelHidden;
B
Benjamin Pasero 已提交
731
			case Parts.STATUSBAR_PART:
732
				return !this.statusBarHidden;
S
Sanders Lauture 已提交
733
			case Parts.ACTIVITYBAR_PART:
734
				return !this.activityBarHidden;
735
		}
E
Erich Gamma 已提交
736 737 738 739

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

740 741
	public getTitleBarOffset(): number {
		let offset = 0;
742
		if (this.isVisible(Parts.TITLEBAR_PART)) {
743 744 745 746 747 748
			offset = 22 / browser.getZoomFactor(); // adjust the position based on title bar size and zoom factor
		}

		return offset;
	}

749
	private getCustomTitleBarStyle(): 'custom' {
B
Benjamin Pasero 已提交
750
		if (!isMacintosh) {
751
			return null; // custom title bar is only supported on Mac currently
B
Benjamin Pasero 已提交
752 753
		}

754
		const isDev = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment;
B
Benjamin Pasero 已提交
755
		if (isDev) {
756
			return null; // not enabled when developing due to https://github.com/electron/electron/issues/3647
B
Benjamin Pasero 已提交
757 758
		}

759
		const windowConfig = this.configurationService.getConfiguration<IWindowSettings>();
760 761 762 763 764
		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 已提交
765

766 767 768 769
			const style = windowConfig.window.titleBarStyle;
			if (style === 'custom') {
				return style;
			}
770 771 772
		}

		return null;
B
Benjamin Pasero 已提交
773 774
	}

775
	private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void {
776 777
		this.statusBarHidden = hidden;

I
isidor 已提交
778

779 780
		// Layout
		if (!skipLayout) {
781
			this.workbenchLayout.layout();
782 783 784
		}
	}

S
Sanders Lauture 已提交
785 786 787
	public setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void {
		this.activityBarHidden = hidden;

I
isidor 已提交
788

S
Sanders Lauture 已提交
789 790
		// Layout
		if (!skipLayout) {
791
			this.workbenchLayout.layout();
S
Sanders Lauture 已提交
792 793 794
		}
	}

795
	public setSideBarHidden(hidden: boolean, skipLayout?: boolean): TPromise<void> {
E
Erich Gamma 已提交
796 797 798 799 800 801 802 803 804
		this.sideBarHidden = hidden;

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

B
Benjamin Pasero 已提交
805
		// If sidebar becomes hidden, also hide the current active Viewlet if any
B
Benjamin Pasero 已提交
806
		let promise = TPromise.as<any>(null);
E
Erich Gamma 已提交
807
		if (hidden && this.sidebarPart.getActiveViewlet()) {
808 809 810
			promise = this.sidebarPart.hideActiveViewlet().then(() => {
				const activeEditor = this.editorPart.getActiveEditor();
				const activePanel = this.panelPart.getActivePanel();
B
Benjamin Pasero 已提交
811

812 813 814 815 816 817 818
				// 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 已提交
819 820
		}

B
Benjamin Pasero 已提交
821
		// If sidebar becomes visible, show last active Viewlet or default viewlet
E
Erich Gamma 已提交
822
		else if (!hidden && !this.sidebarPart.getActiveViewlet()) {
823
			const viewletToOpen = this.sidebarPart.getLastActiveViewletId();
E
Erich Gamma 已提交
824
			if (viewletToOpen) {
825
				promise = this.sidebarPart.openViewlet(viewletToOpen, true);
E
Erich Gamma 已提交
826 827 828
			}
		}

829
		return promise.then(() => {
830

831
			// Remember in settings
832
			const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY;
833 834 835 836 837
			if (hidden !== defaultHidden) {
				this.storageService.store(Workbench.sidebarHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE);
			} else {
				this.storageService.remove(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE);
			}
838 839 840

			// Layout
			if (!skipLayout) {
841
				this.workbenchLayout.layout();
842 843
			}
		});
E
Erich Gamma 已提交
844 845
	}

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

849 850 851 852 853 854 855
		// Adjust CSS
		if (hidden) {
			this.workbench.addClass('nopanel');
		} else {
			this.workbench.removeClass('nopanel');
		}

I
isidor 已提交
856
		// If panel part becomes hidden, also hide the current active panel if any
857
		let promise = TPromise.as<any>(null);
I
isidor 已提交
858
		if (hidden && this.panelPart.getActivePanel()) {
859 860 861 862 863 864 865
			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 已提交
866 867 868 869
		}

		// If panel part becomes visible, show last active panel or default panel
		else if (!hidden && !this.panelPart.getActivePanel()) {
870
			const panelToOpen = this.panelPart.getLastActivePanelId();
I
isidor 已提交
871
			if (panelToOpen) {
872
				promise = this.panelPart.openPanel(panelToOpen, true);
I
isidor 已提交
873 874 875
			}
		}

876
		return promise.then(() => {
877

878
			// Remember in settings
879 880 881 882 883
			if (!hidden) {
				this.storageService.store(Workbench.panelHiddenSettingKey, 'false', StorageScope.WORKSPACE);
			} else {
				this.storageService.remove(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE);
			}
884 885 886

			// Layout
			if (!skipLayout) {
887
				this.workbenchLayout.layout();
888 889
			}
		});
I
isidor 已提交
890 891
	}

I
isidor 已提交
892
	public toggleMaximizedPanel(): void {
893
		this.workbenchLayout.layout({ toggleMaximizedPanel: true });
I
isidor 已提交
894 895
	}

I
isidor 已提交
896 897 898 899
	public isPanelMaximized(): boolean {
		return this.workbenchLayout.isPanelMaximized();
	}

E
Erich Gamma 已提交
900 901 902 903
	public getSideBarPosition(): Position {
		return this.sideBarPosition;
	}

904
	private setSideBarPosition(position: Position): void {
E
Erich Gamma 已提交
905
		if (this.sideBarHidden) {
906
			this.setSideBarHidden(false, true /* Skip Layout */).done(undefined, errors.onUnexpectedError);
E
Erich Gamma 已提交
907 908
		}

B
Benjamin Pasero 已提交
909 910
		const newPositionValue = (position === Position.LEFT) ? 'left' : 'right';
		const oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right';
E
Erich Gamma 已提交
911 912 913 914 915 916 917 918
		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);

919 920 921 922
		// Update Styles
		this.activitybarPart.updateStyles();
		this.sidebarPart.updateStyles();

E
Erich Gamma 已提交
923
		// Layout
924
		this.workbenchLayout.layout();
E
Erich Gamma 已提交
925 926
	}

927 928
	private setFontAliasing(aliasing: string) {
		this.fontAliasing = aliasing;
B
Benjamin Pasero 已提交
929

930 931 932
		document.body.style['-webkit-font-smoothing'] = (aliasing === 'default' ? '' : aliasing);
	}

B
Benjamin Pasero 已提交
933
	public dispose(): void {
E
Erich Gamma 已提交
934
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
935
			this.shutdownComponents();
E
Erich Gamma 已提交
936 937 938
			this.workbenchShutdown = true;
		}

J
Joao Moreno 已提交
939
		this.toDispose = dispose(this.toDispose);
E
Erich Gamma 已提交
940 941 942 943 944 945
	}

	/**
	 * 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 已提交
946
	public layout(options?: ILayoutOptions): void {
E
Erich Gamma 已提交
947
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
948
			this.workbenchLayout.layout(options);
E
Erich Gamma 已提交
949 950 951
		}
	}

952 953 954 955 956 957
	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 已提交
958

I
isidor 已提交
959
		// Preserve zen mode only on reload. Real quit gets out of zen mode so novice users do not get stuck in zen mode.
960 961 962 963 964 965 966
		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 已提交
967 968 969 970 971 972 973 974

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

	private registerListeners(): void {

		// Listen to editor changes
975
		this.toDispose.push(this.editorPart.onEditorsChanged(() => this.onEditorsChanged()));
976

977 978 979 980 981 982 983 984 985 986
		// Listen to editor closing (if we run with --wait)
		const filesToWait = this.workbenchParams.configuration.filesToWait;
		if (filesToWait) {
			const resourcesToWaitFor = filesToWait.paths.map(p => URI.file(p.filePath));
			const waitMarkerFile = URI.file(filesToWait.waitMarkerFilePath);
			const listenerDispose = this.editorPart.getStacksModel().onEditorClosed(() => this.onEditorClosed(listenerDispose, resourcesToWaitFor, waitMarkerFile));

			this.toDispose.push(listenerDispose);
		}

987
		// Handle message service and quick open events
988 989
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesShowing(() => this.messagesVisibleContext.set(true)));
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesCleared(() => this.messagesVisibleContext.reset()));
990

991 992 993 994 995
		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()));
996 997 998 999 1000 1001 1002 1003 1004 1005

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

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

1006
		// Apply as CSS class
1007 1008 1009 1010 1011
		const isFullscreen = browser.isFullscreen();
		if (isFullscreen) {
			this.addClass('fullscreen');
		} else {
			this.removeClass('fullscreen');
I
isidor 已提交
1012 1013
			if (this.zenMode.transitionedToFullScreen && this.zenMode.active) {
				this.toggleZenMode();
I
isidor 已提交
1014
			}
1015
		}
1016

1017 1018 1019 1020 1021
		// 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
1022
		}
E
Erich Gamma 已提交
1023 1024
	}

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
	private onEditorClosed(listenerDispose: IDisposable, resourcesToWaitFor: URI[], waitMarkerFile: URI): void {

		// In wait mode, listen to changes to the editors and wait until the files
		// are closed that the user wants to wait for. When this happens we delete
		// the wait marker file to signal to the outside that editing is done.
		const stacks = this.editorPart.getStacksModel();
		if (resourcesToWaitFor.every(r => !stacks.isOpen(r))) {
			listenerDispose.dispose();
			this.fileService.del(waitMarkerFile).done(null, errors.onUnexpectedError);
		}
	}

1037
	private onEditorsChanged(): void {
B
Benjamin Pasero 已提交
1038
		const visibleEditors = this.editorService.getVisibleEditors().length;
E
Erich Gamma 已提交
1039

1040 1041
		// Close when empty: check if we should close the window based on the setting
		// Overruled by: window has a workspace opened or this window is for extension development
1042
		// or setting is disabled. Also enabled when running with --wait from the command line.
1043
		if (visibleEditors === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) {
1044
			const closeWhenEmpty = this.configurationService.lookup<boolean>(Workbench.closeWhenEmptyConfigurationKey).value;
1045
			if (closeWhenEmpty || this.environmentService.args.wait) {
1046 1047 1048 1049
				this.closeEmptyWindowScheduler.schedule();
			}
		}

E
Erich Gamma 已提交
1050 1051
		// We update the editorpart class to indicate if an editor is opened or not
		// through a delay to accomodate for fast editor switching
B
Benjamin Pasero 已提交
1052 1053 1054 1055 1056 1057
		this.handleEditorBackground();
	}

	private handleEditorBackground(): void {
		const visibleEditors = this.editorService.getVisibleEditors().length;

1058
		const editorContainer = this.editorPart.getContainer();
E
Erich Gamma 已提交
1059
		if (visibleEditors === 0) {
1060
			this.editorsVisibleContext.reset();
1061
			this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty'));
E
Erich Gamma 已提交
1062
		} else {
1063
			this.editorsVisibleContext.set(true);
1064
			this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty'));
E
Erich Gamma 已提交
1065 1066 1067
		}
	}

1068 1069 1070 1071 1072 1073 1074
	private onAllEditorsClosed(): void {
		const visibleEditors = this.editorService.getVisibleEditors().length;
		if (visibleEditors === 0) {
			this.windowService.closeWindow();
		}
	}

1075
	private onDidUpdateConfiguration(skipLayout?: boolean): void {
1076
		const newSidebarPositionValue = this.configurationService.lookup<string>(Workbench.sidebarPositionConfigurationKey).value;
1077
		const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT;
1078 1079 1080
		if (newSidebarPosition !== this.getSideBarPosition()) {
			this.setSideBarPosition(newSidebarPosition);
		}
1081

1082 1083 1084 1085 1086
		const fontAliasing = this.configurationService.lookup<string>(Workbench.fontAliasingConfigurationKey).value;
		if (fontAliasing !== this.fontAliasing) {
			this.setFontAliasing(fontAliasing);
		}

1087 1088 1089 1090 1091
		if (!this.zenMode.active) {
			const newStatusbarHiddenValue = !this.configurationService.lookup<boolean>(Workbench.statusbarVisibleConfigurationKey).value;
			if (newStatusbarHiddenValue !== this.statusBarHidden) {
				this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1092

1093 1094 1095 1096
			const newActivityBarHiddenValue = !this.configurationService.lookup<boolean>(Workbench.activityBarVisibleConfigurationKey).value;
			if (newActivityBarHiddenValue !== this.activityBarHidden) {
				this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1097
		}
1098 1099
	}

E
Erich Gamma 已提交
1100 1101 1102 1103
	private createWorkbenchLayout(): void {
		this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout,
			$(this.container),							// Parent
			this.workbench,								// Workbench Container
I
isidor 已提交
1104
			{
B
Benjamin Pasero 已提交
1105
				titlebar: this.titlebarPart,			// Title Bar
I
isidor 已提交
1106 1107 1108 1109 1110 1111
				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 已提交
1112
			this.quickOpen								// Quickopen
E
Erich Gamma 已提交
1113 1114 1115 1116 1117 1118 1119 1120 1121
		);

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

	private createWorkbench(): void {

		// Create Workbench DIV Off-DOM
		this.workbenchContainer = $('.monaco-workbench-container');
1122
		this.workbench = $().div({ 'class': 'monaco-workbench ' + (isWindows ? 'windows' : isLinux ? 'linux' : 'mac'), id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer);
E
Erich Gamma 已提交
1123 1124 1125 1126 1127 1128 1129 1130
	}

	private renderWorkbench(): void {

		// Apply sidebar state as CSS class
		if (this.sideBarHidden) {
			this.workbench.addClass('nosidebar');
		}
1131 1132 1133
		if (this.panelHidden) {
			this.workbench.addClass('nopanel');
		}
E
Erich Gamma 已提交
1134

B
Benjamin Pasero 已提交
1135
		// Apply font aliasing
1136 1137
		this.setFontAliasing(this.fontAliasing);

B
Benjamin Pasero 已提交
1138
		// Apply title style if shown
1139 1140 1141 1142 1143 1144 1145 1146
		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 已提交
1147 1148
		}

E
Erich Gamma 已提交
1149
		// Create Parts
B
Benjamin Pasero 已提交
1150
		this.createTitlebarPart();
E
Erich Gamma 已提交
1151 1152 1153
		this.createActivityBarPart();
		this.createSidebarPart();
		this.createEditorPart();
1154
		this.createPanelPart();
E
Erich Gamma 已提交
1155 1156 1157 1158 1159 1160
		this.createStatusbarPart();

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

B
Benjamin Pasero 已提交
1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
	private createTitlebarPart(): void {
		const titlebarContainer = $(this.workbench).div({
			'class': ['part', 'titlebar'],
			id: Identifiers.TITLEBAR_PART,
			role: 'contentinfo'
		});

		this.titlebarPart.create(titlebarContainer);
	}

E
Erich Gamma 已提交
1171
	private createActivityBarPart(): void {
B
Benjamin Pasero 已提交
1172
		const activitybarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
1173 1174
			.div({
				'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
1175 1176
				id: Identifiers.ACTIVITYBAR_PART,
				role: 'navigation'
E
Erich Gamma 已提交
1177 1178 1179 1180 1181 1182
			});

		this.activitybarPart.create(activitybarPartContainer);
	}

	private createSidebarPart(): void {
B
Benjamin Pasero 已提交
1183
		const sidebarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
1184 1185
			.div({
				'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
1186 1187
				id: Identifiers.SIDEBAR_PART,
				role: 'complementary'
E
Erich Gamma 已提交
1188 1189 1190 1191 1192
			});

		this.sidebarPart.create(sidebarPartContainer);
	}

I
isidor 已提交
1193
	private createPanelPart(): void {
B
Benjamin Pasero 已提交
1194
		const panelPartContainer = $(this.workbench)
I
isidor 已提交
1195
			.div({
1196
				'class': ['part', 'panel'],
1197 1198
				id: Identifiers.PANEL_PART,
				role: 'complementary'
I
isidor 已提交
1199 1200 1201 1202 1203
			});

		this.panelPart.create(panelPartContainer);
	}

E
Erich Gamma 已提交
1204
	private createEditorPart(): void {
B
Benjamin Pasero 已提交
1205
		const editorContainer = $(this.workbench)
E
Erich Gamma 已提交
1206
			.div({
1207
				'class': ['part', 'editor', 'empty'],
1208 1209
				id: Identifiers.EDITOR_PART,
				role: 'main'
E
Erich Gamma 已提交
1210 1211 1212 1213 1214 1215
			});

		this.editorPart.create(editorContainer);
	}

	private createStatusbarPart(): void {
B
Benjamin Pasero 已提交
1216
		const statusbarContainer = $(this.workbench).div({
E
Erich Gamma 已提交
1217
			'class': ['part', 'statusbar'],
1218 1219
			id: Identifiers.STATUSBAR_PART,
			role: 'contentinfo'
E
Erich Gamma 已提交
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
		});

		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 已提交
1237 1238 1239 1240 1241 1242
	public getPanelPart(): PanelPart {
		assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.');

		return this.panelPart;
	}

E
Erich Gamma 已提交
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
	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);
		}
	}
1260 1261 1262 1263

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

I
isidor 已提交
1265
	public toggleZenMode(skipLayout?: boolean): void {
I
isidor 已提交
1266 1267
		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
1268
		let toggleFullScreen = false;
I
isidor 已提交
1269
		if (this.zenMode.active) {
I
isidor 已提交
1270 1271
			const config = this.configurationService.getConfiguration<IZenModeSettings>('zenMode');
			toggleFullScreen = !browser.isFullscreen() && config.fullScreen;
I
isidor 已提交
1272
			this.zenMode.transitionedToFullScreen = toggleFullScreen;
1273 1274
			this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART);
			this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART);
1275
			this.setPanelHidden(true, true).done(undefined, errors.onUnexpectedError);
1276
			this.setSideBarHidden(true, true).done(undefined, errors.onUnexpectedError);
I
isidor 已提交
1277

1278 1279 1280
			if (config.hideActivityBar) {
				this.setActivityBarHidden(true, true);
			}
I
isidor 已提交
1281
			if (config.hideStatusBar) {
I
isidor 已提交
1282 1283
				this.setStatusBarHidden(true, true);
			}
I
isidor 已提交
1284
			if (config.hideTabs) {
I
isidor 已提交
1285 1286
				this.editorPart.hideTabs(true);
			}
1287
		} else {
1288
			if (this.zenMode.wasPanelVisible) {
1289
				this.setPanelHidden(false, true).done(undefined, errors.onUnexpectedError);
1290
			}
1291
			if (this.zenMode.wasSideBarVisible) {
1292
				this.setSideBarHidden(false, true).done(undefined, errors.onUnexpectedError);
1293
			}
1294 1295
			// Status bar and activity bar visibility come from settings -> update their visibility.
			this.onDidUpdateConfiguration(true);
I
isidor 已提交
1296
			this.editorPart.hideTabs(false);
I
isidor 已提交
1297 1298 1299 1300
			const activeEditor = this.editorPart.getActiveEditor();
			if (activeEditor) {
				activeEditor.focus();
			}
1301
			toggleFullScreen = this.zenMode.transitionedToFullScreen && browser.isFullscreen();
I
isidor 已提交
1302
		}
1303
		this.inZenMode.set(this.zenMode.active);
I
isidor 已提交
1304

I
isidor 已提交
1305
		if (!skipLayout) {
1306
			this.layout();
I
isidor 已提交
1307
		}
1308 1309 1310
		if (toggleFullScreen) {
			this.windowService.toggleFullScreen().done(undefined, errors.onUnexpectedError);
		}
I
isidor 已提交
1311 1312
	}

1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
	// 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 已提交
1323
				return; // Cannot resize other parts
1324 1325 1326 1327
		}
	}


1328
	private shouldRestoreLastOpenedViewlet(): boolean {
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339
		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 已提交
1340
}