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

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

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

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

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

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

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

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

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

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

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

163 164
	private _onTitleBarVisibilityChange: Emitter<void>;

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

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

			// Create Workbench
			this.createWorkbench();

278 279 280
			// Install some global actions
			this.createGlobalActions();

E
Erich Gamma 已提交
281 282 283 284 285 286
			// Services
			this.initServices();
			if (this.callbacks && this.callbacks.onServicesCreated) {
				this.callbacks.onServicesCreated();
			}

287
			// Contexts
288 289
			this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService);
			this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
I
isidor 已提交
290
			this.inZenMode = InZenModeContext.bindTo(this.contextKeyService);
291
			this.sideBarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService);
292

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

			// Settings
			this.initSettings();

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

			// Workbench Layout
			this.createWorkbenchLayout();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

			// Rethrow
			throw error;
		}
	}

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

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

410
		// Actions for macOS native tabs management (only when enabled)
411
		const windowConfig = this.configurationService.getConfiguration<IWindowConfiguration>();
412 413 414 415 416 417 418
		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');
		}
419 420
	}

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

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

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

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

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

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

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

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

		return TPromise.as([]);
	}

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

		return paths.map(p => {
468 469 470 471 472 473 474
			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 已提交
475

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

			return input;
		});
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

E
Erich Gamma 已提交
552
		// Activity service (activitybar part)
553
		this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
E
Erich Gamma 已提交
554 555
		this.toDispose.push(this.activitybarPart);
		this.toShutdown.push(this.activitybarPart);
I
isidor 已提交
556 557
		const activityService = this.instantiationService.createInstance(ActivityService, this.activitybarPart, this.panelPart);
		serviceCollection.set(IActivityService, activityService);
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
		// File Decorations
J
Johannes Rieken 已提交
589
		serviceCollection.set(IDecorationsService, new SyncDescriptor(FileDecorationsService));
590

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 602 603
		// Workspace Editing
		serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService));

604 605 606
		// Keybinding Editing
		serviceCollection.set(IKeybindingEditingService, this.instantiationService.createInstance(KeybindingsEditingService));

607
		// Configuration Resolver
B
Benjamin Pasero 已提交
608
		serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, process.env));
609

E
Erich Gamma 已提交
610
		// Quick open service (quick open controller)
611
		this.quickOpen = this.instantiationService.createInstance(QuickOpenController);
E
Erich Gamma 已提交
612 613
		this.toDispose.push(this.quickOpen);
		this.toShutdown.push(this.quickOpen);
614
		serviceCollection.set(IQuickOpenService, this.quickOpen);
615

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

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

627
		this.instantiationService.createInstance(DefaultConfigurationExportHelper);
628 629

		this.configurationService.setInstantiationService(this.getInstantiationService());
E
Erich Gamma 已提交
630 631 632 633 634
	}

	private initSettings(): void {

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

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

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

I
isidor 已提交
648 649 650 651
		// Panel position
		const panelPosition = this.configurationService.getValue<string>(Workbench.panelPositionConfigurationKey);
		this.panelPosition = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM;

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

		// Activity bar visibility
657
		const activityBarVisible = this.configurationService.getValue<string>(Workbench.activityBarVisibleConfigurationKey);
S
Sanders Lauture 已提交
658
		this.activityBarHidden = !activityBarVisible;
I
isidor 已提交
659

660
		// Font aliasing
661
		this.fontAliasing = this.configurationService.getValue<string>(Workbench.fontAliasingConfigurationKey);
662

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

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

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

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

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

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

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

		return offset;
	}

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

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

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

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

		return null;
B
Benjamin Pasero 已提交
775 776
	}

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

I
isidor 已提交
780

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

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

I
isidor 已提交
790

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

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

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

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

815 816 817 818 819 820 821
				// 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 已提交
822 823
		}

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

832
		return promise.then(() => {
833

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

			// Layout
			if (!skipLayout) {
844
				this.workbenchLayout.layout();
845 846
			}
		});
E
Erich Gamma 已提交
847 848
	}

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

852 853 854 855 856 857 858
		// Adjust CSS
		if (hidden) {
			this.workbench.addClass('nopanel');
		} else {
			this.workbench.removeClass('nopanel');
		}

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

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

879
		return promise.then(() => {
880

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

			// Layout
			if (!skipLayout) {
890
				this.workbenchLayout.layout();
891 892
			}
		});
I
isidor 已提交
893 894
	}

I
isidor 已提交
895
	public toggleMaximizedPanel(): void {
896
		this.workbenchLayout.layout({ toggleMaximizedPanel: true });
I
isidor 已提交
897 898
	}

I
isidor 已提交
899 900 901 902
	public isPanelMaximized(): boolean {
		return this.workbenchLayout.isPanelMaximized();
	}

E
Erich Gamma 已提交
903 904 905 906
	public getSideBarPosition(): Position {
		return this.sideBarPosition;
	}

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

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

922 923 924 925
		// Update Styles
		this.activitybarPart.updateStyles();
		this.sidebarPart.updateStyles();

E
Erich Gamma 已提交
926
		// Layout
927
		this.workbenchLayout.layout();
E
Erich Gamma 已提交
928 929
	}

I
isidor 已提交
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
	public getPanelPosition(): Position {
		return this.panelPosition;
	}

	private setPanelPosition(position: Position): void {
		if (this.panelHidden) {
			this.setPanelHidden(false, true /* Skip Layout */).done(undefined, errors.onUnexpectedError);
		}

		const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right';
		const oldPositionValue = (this.panelPosition === Position.BOTTOM) ? 'bottom' : 'right';
		this.panelPosition = position;

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

		// Update Styles
		this.panelPart.updateStyles();

		// Layout
		this.workbenchLayout.layout();
	}

954 955
	private setFontAliasing(aliasing: string) {
		this.fontAliasing = aliasing;
B
Benjamin Pasero 已提交
956

957 958 959
		document.body.style['-webkit-font-smoothing'] = (aliasing === 'default' ? '' : aliasing);
	}

B
Benjamin Pasero 已提交
960
	public dispose(): void {
E
Erich Gamma 已提交
961
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
962
			this.shutdownComponents();
E
Erich Gamma 已提交
963 964 965
			this.workbenchShutdown = true;
		}

J
Joao Moreno 已提交
966
		this.toDispose = dispose(this.toDispose);
E
Erich Gamma 已提交
967 968 969 970 971 972
	}

	/**
	 * 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 已提交
973
	public layout(options?: ILayoutOptions): void {
E
Erich Gamma 已提交
974
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
975
			this.workbenchLayout.layout(options);
E
Erich Gamma 已提交
976 977 978
		}
	}

979 980 981 982 983 984
	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 已提交
985

I
isidor 已提交
986
		// Preserve zen mode only on reload. Real quit gets out of zen mode so novice users do not get stuck in zen mode.
987 988 989 990 991 992 993
		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 已提交
994 995 996 997 998 999 1000 1001

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

	private registerListeners(): void {

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

1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
		// 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);
		}

1014
		// Handle message service and quick open events
1015 1016
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesShowing(() => this.messagesVisibleContext.set(true)));
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesCleared(() => this.messagesVisibleContext.reset()));
1017

1018 1019 1020 1021
		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
1022
		this.toDispose.push(this.configurationService.onDidChangeConfiguration(() => this.onDidUpdateConfiguration()));
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032

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

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

1033
		// Apply as CSS class
1034 1035 1036 1037 1038
		const isFullscreen = browser.isFullscreen();
		if (isFullscreen) {
			this.addClass('fullscreen');
		} else {
			this.removeClass('fullscreen');
I
isidor 已提交
1039 1040
			if (this.zenMode.transitionedToFullScreen && this.zenMode.active) {
				this.toggleZenMode();
I
isidor 已提交
1041
			}
1042
		}
1043

1044 1045 1046 1047 1048
		// 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
1049
		}
E
Erich Gamma 已提交
1050 1051
	}

1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
	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);
		}
	}

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

1067 1068
		// 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
1069
		// or setting is disabled. Also enabled when running with --wait from the command line.
1070
		if (visibleEditors === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) {
1071
			const closeWhenEmpty = this.configurationService.getValue<boolean>(Workbench.closeWhenEmptyConfigurationKey);
1072
			if (closeWhenEmpty || this.environmentService.args.wait) {
1073 1074 1075 1076
				this.closeEmptyWindowScheduler.schedule();
			}
		}

E
Erich Gamma 已提交
1077 1078
		// 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 已提交
1079 1080 1081 1082 1083 1084
		this.handleEditorBackground();
	}

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

1085
		const editorContainer = this.editorPart.getContainer();
E
Erich Gamma 已提交
1086
		if (visibleEditors === 0) {
1087
			this.editorsVisibleContext.reset();
1088
			this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty'));
E
Erich Gamma 已提交
1089
		} else {
1090
			this.editorsVisibleContext.set(true);
1091
			this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty'));
E
Erich Gamma 已提交
1092 1093 1094
		}
	}

1095 1096 1097 1098 1099 1100 1101
	private onAllEditorsClosed(): void {
		const visibleEditors = this.editorService.getVisibleEditors().length;
		if (visibleEditors === 0) {
			this.windowService.closeWindow();
		}
	}

1102
	private onDidUpdateConfiguration(skipLayout?: boolean): void {
1103
		const newSidebarPositionValue = this.configurationService.getValue<string>(Workbench.sidebarPositionConfigurationKey);
1104
		const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT;
1105 1106 1107
		if (newSidebarPosition !== this.getSideBarPosition()) {
			this.setSideBarPosition(newSidebarPosition);
		}
1108

I
isidor 已提交
1109 1110 1111 1112 1113 1114
		const newPanelPositionValue = this.configurationService.getValue<string>(Workbench.panelPositionConfigurationKey);
		const newPanelPosition = (newPanelPositionValue === 'right') ? Position.RIGHT : Position.BOTTOM;
		if (newPanelPosition !== this.getSideBarPosition()) {
			this.setPanelPosition(newPanelPosition);
		}

1115
		const fontAliasing = this.configurationService.getValue<string>(Workbench.fontAliasingConfigurationKey);
1116 1117 1118 1119
		if (fontAliasing !== this.fontAliasing) {
			this.setFontAliasing(fontAliasing);
		}

1120
		if (!this.zenMode.active) {
1121
			const newStatusbarHiddenValue = !this.configurationService.getValue<boolean>(Workbench.statusbarVisibleConfigurationKey);
1122 1123 1124
			if (newStatusbarHiddenValue !== this.statusBarHidden) {
				this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1125

1126
			const newActivityBarHiddenValue = !this.configurationService.getValue<boolean>(Workbench.activityBarVisibleConfigurationKey);
1127 1128 1129
			if (newActivityBarHiddenValue !== this.activityBarHidden) {
				this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1130
		}
1131 1132
	}

E
Erich Gamma 已提交
1133 1134 1135 1136
	private createWorkbenchLayout(): void {
		this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout,
			$(this.container),							// Parent
			this.workbench,								// Workbench Container
I
isidor 已提交
1137
			{
B
Benjamin Pasero 已提交
1138
				titlebar: this.titlebarPart,			// Title Bar
I
isidor 已提交
1139 1140 1141 1142 1143 1144
				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 已提交
1145
			this.quickOpen								// Quickopen
E
Erich Gamma 已提交
1146 1147 1148 1149 1150 1151 1152 1153 1154
		);

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

	private createWorkbench(): void {

		// Create Workbench DIV Off-DOM
		this.workbenchContainer = $('.monaco-workbench-container');
1155
		this.workbench = $().div({ 'class': 'monaco-workbench ' + (isWindows ? 'windows' : isLinux ? 'linux' : 'mac'), id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer);
E
Erich Gamma 已提交
1156 1157 1158 1159 1160 1161 1162 1163
	}

	private renderWorkbench(): void {

		// Apply sidebar state as CSS class
		if (this.sideBarHidden) {
			this.workbench.addClass('nosidebar');
		}
1164 1165 1166
		if (this.panelHidden) {
			this.workbench.addClass('nopanel');
		}
E
Erich Gamma 已提交
1167

B
Benjamin Pasero 已提交
1168
		// Apply font aliasing
1169 1170
		this.setFontAliasing(this.fontAliasing);

B
Benjamin Pasero 已提交
1171
		// Apply title style if shown
1172 1173 1174 1175 1176 1177 1178 1179
		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 已提交
1180 1181
		}

E
Erich Gamma 已提交
1182
		// Create Parts
B
Benjamin Pasero 已提交
1183
		this.createTitlebarPart();
E
Erich Gamma 已提交
1184 1185 1186
		this.createActivityBarPart();
		this.createSidebarPart();
		this.createEditorPart();
1187
		this.createPanelPart();
E
Erich Gamma 已提交
1188 1189 1190 1191 1192 1193
		this.createStatusbarPart();

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

B
Benjamin Pasero 已提交
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
	private createTitlebarPart(): void {
		const titlebarContainer = $(this.workbench).div({
			'class': ['part', 'titlebar'],
			id: Identifiers.TITLEBAR_PART,
			role: 'contentinfo'
		});

		this.titlebarPart.create(titlebarContainer);
	}

E
Erich Gamma 已提交
1204
	private createActivityBarPart(): void {
B
Benjamin Pasero 已提交
1205
		const activitybarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
1206 1207
			.div({
				'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
1208 1209
				id: Identifiers.ACTIVITYBAR_PART,
				role: 'navigation'
E
Erich Gamma 已提交
1210 1211 1212 1213 1214 1215
			});

		this.activitybarPart.create(activitybarPartContainer);
	}

	private createSidebarPart(): void {
B
Benjamin Pasero 已提交
1216
		const sidebarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
1217 1218
			.div({
				'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
1219 1220
				id: Identifiers.SIDEBAR_PART,
				role: 'complementary'
E
Erich Gamma 已提交
1221 1222 1223 1224 1225
			});

		this.sidebarPart.create(sidebarPartContainer);
	}

I
isidor 已提交
1226
	private createPanelPart(): void {
B
Benjamin Pasero 已提交
1227
		const panelPartContainer = $(this.workbench)
I
isidor 已提交
1228
			.div({
1229
				'class': ['part', 'panel', this.panelPosition === Position.BOTTOM ? 'bottom' : 'right'],
1230 1231
				id: Identifiers.PANEL_PART,
				role: 'complementary'
I
isidor 已提交
1232 1233 1234 1235 1236
			});

		this.panelPart.create(panelPartContainer);
	}

E
Erich Gamma 已提交
1237
	private createEditorPart(): void {
B
Benjamin Pasero 已提交
1238
		const editorContainer = $(this.workbench)
E
Erich Gamma 已提交
1239
			.div({
1240
				'class': ['part', 'editor', 'empty'],
1241 1242
				id: Identifiers.EDITOR_PART,
				role: 'main'
E
Erich Gamma 已提交
1243 1244 1245 1246 1247 1248
			});

		this.editorPart.create(editorContainer);
	}

	private createStatusbarPart(): void {
B
Benjamin Pasero 已提交
1249
		const statusbarContainer = $(this.workbench).div({
E
Erich Gamma 已提交
1250
			'class': ['part', 'statusbar'],
1251 1252
			id: Identifiers.STATUSBAR_PART,
			role: 'contentinfo'
E
Erich Gamma 已提交
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
		});

		this.statusbarPart.create(statusbarContainer);
	}

	public getEditorPart(): EditorPart {
		return this.editorPart;
	}

	public getSidebarPart(): SidebarPart {
		return this.sidebarPart;
	}

I
isidor 已提交
1266 1267 1268 1269
	public getPanelPart(): PanelPart {
		return this.panelPart;
	}

E
Erich Gamma 已提交
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
	public getInstantiationService(): IInstantiationService {
		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);
		}
	}
1285 1286 1287 1288

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

I
isidor 已提交
1290
	public toggleZenMode(skipLayout?: boolean): void {
I
isidor 已提交
1291
		this.zenMode.active = !this.zenMode.active;
1292

I
isidor 已提交
1293
		// 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
1294
		let toggleFullScreen = false;
I
isidor 已提交
1295
		if (this.zenMode.active) {
I
isidor 已提交
1296 1297
			const config = this.configurationService.getConfiguration<IZenModeSettings>('zenMode');
			toggleFullScreen = !browser.isFullscreen() && config.fullScreen;
I
isidor 已提交
1298
			this.zenMode.transitionedToFullScreen = toggleFullScreen;
1299 1300
			this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART);
			this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART);
1301
			this.setPanelHidden(true, true).done(undefined, errors.onUnexpectedError);
1302
			this.setSideBarHidden(true, true).done(undefined, errors.onUnexpectedError);
I
isidor 已提交
1303

1304 1305 1306
			if (config.hideActivityBar) {
				this.setActivityBarHidden(true, true);
			}
1307

I
isidor 已提交
1308
			if (config.hideStatusBar) {
I
isidor 已提交
1309 1310
				this.setStatusBarHidden(true, true);
			}
1311

I
isidor 已提交
1312
			if (config.hideTabs) {
I
isidor 已提交
1313 1314
				this.editorPart.hideTabs(true);
			}
1315
		} else {
1316
			if (this.zenMode.wasPanelVisible) {
1317
				this.setPanelHidden(false, true).done(undefined, errors.onUnexpectedError);
1318
			}
1319

1320
			if (this.zenMode.wasSideBarVisible) {
1321
				this.setSideBarHidden(false, true).done(undefined, errors.onUnexpectedError);
1322
			}
1323

1324 1325
			// Status bar and activity bar visibility come from settings -> update their visibility.
			this.onDidUpdateConfiguration(true);
I
isidor 已提交
1326
			this.editorPart.hideTabs(false);
I
isidor 已提交
1327 1328 1329 1330
			const activeEditor = this.editorPart.getActiveEditor();
			if (activeEditor) {
				activeEditor.focus();
			}
1331

1332
			toggleFullScreen = this.zenMode.transitionedToFullScreen && browser.isFullscreen();
I
isidor 已提交
1333
		}
1334

1335
		this.inZenMode.set(this.zenMode.active);
I
isidor 已提交
1336

I
isidor 已提交
1337
		if (!skipLayout) {
1338
			this.layout();
I
isidor 已提交
1339
		}
1340

1341 1342 1343
		if (toggleFullScreen) {
			this.windowService.toggleFullScreen().done(undefined, errors.onUnexpectedError);
		}
I
isidor 已提交
1344 1345
	}

1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
	// 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 已提交
1356
				return; // Cannot resize other parts
1357 1358 1359 1360
		}
	}


1361
	private shouldRestoreLastOpenedViewlet(): boolean {
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
		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 已提交
1373
}