workbench.ts 51.4 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import 'vs/css!./media/workbench';
9

10
import { localize } from 'vs/nls';
11
import { TPromise } from 'vs/base/common/winjs.base';
J
Johannes Rieken 已提交
12
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 { time } from 'vs/base/common/performance';
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';
23
import { Registry } from 'vs/platform/registry/common/platform';
B
Benjamin Pasero 已提交
24
import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
B
Benjamin Pasero 已提交
25
import { Position as EditorPosition, IResourceDiffInput, IUntitledResourceInput, IEditor, IResourceInput } from 'vs/platform/editor/common/editor';
J
Johannes Rieken 已提交
26
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
27
import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
J
Johannes Rieken 已提交
28 29 30 31 32 33
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 已提交
34 35
import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart';
import { WorkbenchLayout } from 'vs/workbench/browser/layout';
36
import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions';
J
Johannes Rieken 已提交
37 38 39
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';
40
import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService';
41
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
J
Johannes Rieken 已提交
42 43 44 45
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';
46
import { WorkspaceService, DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationService';
47 48
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService';
J
Johannes Rieken 已提交
49 50
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
51
import { IKeybindingEditingService, KeybindingsEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
J
Johannes Rieken 已提交
52
import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
I
isidor 已提交
53
import { IActivityService } from 'vs/workbench/services/activity/common/activity';
B
Benjamin Pasero 已提交
54
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
P
Pine Wu 已提交
55
import { ViewletService } from 'vs/workbench/services/viewlet/browser/viewletService';
56
import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService';
J
Johannes Rieken 已提交
57
import { IFileService } from 'vs/platform/files/common/files';
58
import { IListService, ListService } from 'vs/platform/list/browser/listService';
J
Johannes Rieken 已提交
59 60 61
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 已提交
62
import { ITitleService } from 'vs/workbench/services/title/common/titleService';
J
Johannes Rieken 已提交
63
import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService';
B
Benjamin Pasero 已提交
64
import { IWorkbenchEditorService, IResourceInputType, WorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
J
Johannes Rieken 已提交
65
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
66 67
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService';
J
Johannes Rieken 已提交
68 69 70 71 72 73
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 已提交
74 75
import { ISCMService } from 'vs/workbench/services/scm/common/scm';
import { SCMService } from 'vs/workbench/services/scm/common/scmService';
76 77
import { IProgressService2 } from 'vs/platform/progress/common/progress';
import { ProgressService2 } from 'vs/workbench/services/progress/browser/progressService2';
78
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
79
import { ITextModelService } from 'vs/editor/common/services/resolverService';
J
Johannes Rieken 已提交
80
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
81 82
import { ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { LifecycleService } from 'vs/workbench/services/lifecycle/electron-browser/lifecycleService';
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
import { MenuService } from 'vs/platform/actions/common/menuService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
B
Benjamin Pasero 已提交
90
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
91
import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar } from 'vs/workbench/electron-browser/actions';
92
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
93
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
B
Benjamin Pasero 已提交
94
import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService';
95
import { FileDecorationsService } from 'vs/workbench/services/decorations/browser/decorationsService';
J
Johannes Rieken 已提交
96
import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations';
I
isidor 已提交
97
import { ActivityService } from 'vs/workbench/services/activity/browser/activityService';
B
Benjamin Pasero 已提交
98
import URI from 'vs/base/common/uri';
99

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

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

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

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

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

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

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

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

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

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

162 163
	private _onTitleBarVisibilityChange: Emitter<void>;

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

B
Benjamin Pasero 已提交
166
	private parent: HTMLElement;
E
Erich Gamma 已提交
167 168 169 170 171 172 173
	private container: HTMLElement;
	private workbenchParams: WorkbenchParams;
	private workbenchContainer: Builder;
	private workbench: Builder;
	private workbenchStarted: boolean;
	private workbenchCreated: boolean;
	private workbenchShutdown: boolean;
174
	private editorService: WorkbenchEditorService;
P
Pine Wu 已提交
175
	private viewletService: IViewletService;
176
	private contextKeyService: IContextKeyService;
177
	private keybindingService: IKeybindingService;
178
	private backupFileService: IBackupFileService;
179
	private fileService: IFileService;
B
Benjamin Pasero 已提交
180
	private titlebarPart: TitlebarPart;
E
Erich Gamma 已提交
181 182
	private activitybarPart: ActivitybarPart;
	private sidebarPart: SidebarPart;
I
isidor 已提交
183
	private panelPart: PanelPart;
E
Erich Gamma 已提交
184 185 186 187 188 189 190 191
	private editorPart: EditorPart;
	private statusbarPart: StatusbarPart;
	private quickOpen: QuickOpenController;
	private workbenchLayout: WorkbenchLayout;
	private toDispose: IDisposable[];
	private toShutdown: { shutdown: () => void; }[];
	private callbacks: IWorkbenchCallbacks;
	private sideBarHidden: boolean;
192
	private statusBarHidden: boolean;
S
Sanders Lauture 已提交
193
	private activityBarHidden: boolean;
E
Erich Gamma 已提交
194
	private sideBarPosition: Position;
I
isidor 已提交
195
	private panelPosition: Position;
I
isidor 已提交
196
	private panelHidden: boolean;
E
Erich Gamma 已提交
197
	private editorBackgroundDelayer: Delayer<void>;
198
	private closeEmptyWindowScheduler: RunOnceScheduler;
A
Alex Dima 已提交
199 200
	private messagesVisibleContext: IContextKey<boolean>;
	private editorsVisibleContext: IContextKey<boolean>;
I
isidor 已提交
201
	private inZenMode: IContextKey<boolean>;
202
	private sideBarVisibleContext: IContextKey<boolean>;
203
	private hasFilesToCreateOpenOrDiff: boolean;
204
	private fontAliasing: string;
I
isidor 已提交
205
	private zenMode: {
I
isidor 已提交
206 207
		active: boolean;
		transitionedToFullScreen: boolean;
208 209
		wasSideBarVisible: boolean;
		wasPanelVisible: boolean;
I
isidor 已提交
210
	};
E
Erich Gamma 已提交
211

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

		this.workbenchParams = {
230
			configuration,
231
			serviceCollection
E
Erich Gamma 已提交
232 233
		};

234
		this.hasFilesToCreateOpenOrDiff =
B
Benjamin Pasero 已提交
235 236 237
			(configuration.filesToCreate && configuration.filesToCreate.length > 0) ||
			(configuration.filesToOpen && configuration.filesToOpen.length > 0) ||
			(configuration.filesToDiff && configuration.filesToDiff.length > 0);
238

E
Erich Gamma 已提交
239 240
		this.toDispose = [];
		this.toShutdown = [];
241

E
Erich Gamma 已提交
242
		this.editorBackgroundDelayer = new Delayer<void>(50);
243
		this.closeEmptyWindowScheduler = new RunOnceScheduler(() => this.onAllEditorsClosed(), 50);
E
Erich Gamma 已提交
244

245
		this._onTitleBarVisibilityChange = new Emitter<void>();
E
Erich Gamma 已提交
246 247
	}

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

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

E
Erich Gamma 已提交
258 259 260 261 262
	/**
	 * 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 {
263 264
		this.workbenchStarted = true;
		this.callbacks = callbacks;
E
Erich Gamma 已提交
265

266 267
		// Create Workbench
		this.createWorkbench();
E
Erich Gamma 已提交
268

269 270
		// Install some global actions
		this.createGlobalActions();
271

272 273 274 275 276
		// Services
		this.initServices();
		if (this.callbacks && this.callbacks.onServicesCreated) {
			this.callbacks.onServicesCreated();
		}
E
Erich Gamma 已提交
277

278 279 280 281 282
		// Contexts
		this.messagesVisibleContext = MessagesVisibleContext.bindTo(this.contextKeyService);
		this.editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService);
		this.inZenMode = InZenModeContext.bindTo(this.contextKeyService);
		this.sideBarVisibleContext = SidebarVisibleContext.bindTo(this.contextKeyService);
E
Erich Gamma 已提交
283

284 285
		// Register Listeners
		this.registerListeners();
E
Erich Gamma 已提交
286

287 288
		// Settings
		this.initSettings();
E
Erich Gamma 已提交
289

290 291
		// Create Workbench and Parts
		this.renderWorkbench();
292

293 294
		// Workbench Layout
		this.createWorkbenchLayout();
B
polish  
Benjamin Pasero 已提交
295

296 297 298
		// Restore Parts
		this.restoreParts().done(startedInfo => {
			this.workbenchCreated = true;
299

300 301
			if (this.callbacks && this.callbacks.onWorkbenchStarted) {
				this.callbacks.onWorkbenchStarted(startedInfo);
E
Erich Gamma 已提交
302
			}
303 304
		});
	}
E
Erich Gamma 已提交
305

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
	private restoreParts(): TPromise<IWorkbenchStartedInfo> {
		const restorePromises: TPromise<any>[] = [];

		// Restore Editors
		const editorRestoreStopWatch = StopWatch.create();
		const editorRestoreClock = time('restore:editors');
		const restoredEditors: string[] = [];
		restorePromises.push(this.resolveEditorsToOpen().then(inputs => {
			this.lifecycleService.phase = LifecyclePhase.Restoring;

			let editorOpenPromise: TPromise<IEditor[]>;
			if (inputs.length) {
				editorOpenPromise = this.editorService.openEditors(inputs.map(input => { return { input, position: EditorPosition.ONE }; }));
			} else {
				editorOpenPromise = this.editorPart.restoreEditors();
321 322
			}

323 324 325 326 327
			return editorOpenPromise.then(editors => {
				this.handleEditorBackground(); // make sure we show the proper background in the editor area

				editorRestoreClock.stop();
				editorRestoreStopWatch.stop();
328

329 330 331 332 333 334
				for (const editor of editors) {
					if (editor) {
						if (editor.input) {
							restoredEditors.push(editor.input.getName());
						} else {
							restoredEditors.push(`other:${editor.getId()}`);
335 336
						}
					}
337 338 339
				}
			});
		}));
E
Erich Gamma 已提交
340

341 342 343 344 345
		// Restore Sidebar
		let viewletRestoreStopWatch: StopWatch;
		let viewletIdToRestore: string;
		if (!this.sideBarHidden) {
			this.sideBarVisibleContext.set(true);
I
isidor 已提交
346

347 348 349
			if (this.shouldRestoreLastOpenedViewlet()) {
				viewletIdToRestore = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE);
			}
350

351 352 353
			if (!viewletIdToRestore) {
				viewletIdToRestore = this.viewletService.getDefaultViewletId();
			}
354

355 356 357 358 359 360 361
			viewletRestoreStopWatch = StopWatch.create();
			const viewletRestoreClock = time('restore:viewlet');
			restorePromises.push(this.viewletService.openViewlet(viewletIdToRestore).then(() => {
				viewletRestoreStopWatch.stop();
				viewletRestoreClock.stop();
			}));
		}
E
Erich Gamma 已提交
362

363 364 365 366 367 368
		// Restore Panel
		const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
		const panelId = this.storageService.get(PanelPart.activePanelSettingsKey, StorageScope.WORKSPACE, panelRegistry.getDefaultPanelId());
		if (!this.panelHidden && !!panelId) {
			restorePromises.push(this.panelPart.openPanel(panelId, false));
		}
E
Erich Gamma 已提交
369

370 371 372
		// Restore Zen Mode if active
		if (this.storageService.getBoolean(Workbench.zenModeActiveSettingKey, StorageScope.WORKSPACE, false)) {
			this.toggleZenMode(true);
E
Erich Gamma 已提交
373
		}
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390

		const onRestored = (error?: Error): IWorkbenchStartedInfo => {
			if (error) {
				errors.onUnexpectedError(error);
			}

			return {
				customKeybindingsCount: this.keybindingService.customKeybindingsCount(),
				restoreViewletDuration: viewletRestoreStopWatch ? Math.round(viewletRestoreStopWatch.elapsed()) : 0,
				restoreEditorsDuration: Math.round(editorRestoreStopWatch.elapsed()),
				pinnedViewlets: this.activitybarPart.getPinned(),
				restoredViewlet: viewletIdToRestore,
				restoredEditors
			};
		};

		return TPromise.join(restorePromises).then(() => onRestored(), error => onRestored(error));
E
Erich Gamma 已提交
391 392
	}

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

B
Benjamin Pasero 已提交
396
		// Actions registered here to adjust for developing vs built workbench
397 398
		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 已提交
399
		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 已提交
400
		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"));
401

402
		// Actions for macOS native tabs management (only when enabled)
403
		const windowConfig = this.configurationService.getConfiguration<IWindowConfiguration>();
404 405 406 407 408 409 410
		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');
		}
411 412
	}

413
	private resolveEditorsToOpen(): TPromise<IResourceInputType[]> {
414
		const config = this.workbenchParams.configuration;
B
Benjamin Pasero 已提交
415 416

		// Files to open, diff or create
417
		if (this.hasFilesToCreateOpenOrDiff) {
B
Benjamin Pasero 已提交
418 419

			// Files to diff is exclusive
420
			const filesToDiff = this.toInputs(config.filesToDiff, false);
421
			if (filesToDiff && filesToDiff.length === 2) {
422 423 424 425 426
				return TPromise.as([<IResourceDiffInput>{
					leftResource: filesToDiff[0].resource,
					rightResource: filesToDiff[1].resource,
					options: { pinned: true }
				}]);
B
Benjamin Pasero 已提交
427 428
			}

429 430
			const filesToCreate = this.toInputs(config.filesToCreate, true);
			const filesToOpen = this.toInputs(config.filesToOpen, false);
431

432 433
			// Otherwise: Open/Create files
			return TPromise.as([...filesToOpen, ...filesToCreate]);
B
Benjamin Pasero 已提交
434 435
		}

436
		// Empty workbench
437
		else if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && this.openUntitledFile()) {
438 439 440 441
			if (this.editorPart.hasEditorsToRestore()) {
				return TPromise.as([]); // do not open any empty untitled file if we have editors to restore
			}

442 443 444 445 446
			return this.backupFileService.hasBackups().then(hasBackups => {
				if (hasBackups) {
					return TPromise.as([]); // do not open any empty untitled file if we have backups to restore
				}

447
				return TPromise.as([<IUntitledResourceInput>{}]);
448
			});
B
Benjamin Pasero 已提交
449 450 451 452 453
		}

		return TPromise.as([]);
	}

454
	private toInputs(paths: IPath[], isNew: boolean): (IResourceInput | IUntitledResourceInput)[] {
B
Benjamin Pasero 已提交
455 456 457 458 459
		if (!paths || !paths.length) {
			return [];
		}

		return paths.map(p => {
460 461 462 463 464 465 466
			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 已提交
467

468
			if (!isNew && p.lineNumber) {
B
Benjamin Pasero 已提交
469 470 471 472 473 474 475 476 477 478
				input.options.selection = {
					startLineNumber: p.lineNumber,
					startColumn: p.columnNumber
				};
			}

			return input;
		});
	}

479
	private openUntitledFile() {
480
		const startupEditor = this.configurationService.inspect('workbench.startupEditor');
B
Benjamin Pasero 已提交
481 482

		// Fallback to previous workbench.welcome.enabled setting in case startupEditor is not defined
483
		if (!startupEditor.user && !startupEditor.workspace) {
484 485 486
			const welcomeEnabledValue = this.configurationService.getValue('workbench.welcome.enabled');
			if (typeof welcomeEnabledValue === 'boolean') {
				return !welcomeEnabledValue;
487 488
			}
		}
B
Benjamin Pasero 已提交
489

490
		return startupEditor.value === 'newUntitledFile';
491 492
	}

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

496
		this.toDispose.push(this.lifecycleService.onShutdown(this.shutdownComponents, this));
E
Erich Gamma 已提交
497 498

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

501 502 503
		// Clipboard
		serviceCollection.set(IClipboardService, new ClipboardService());

504 505 506 507 508 509
		// 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);

510 511 512
		// Progress 2
		serviceCollection.set(IProgressService2, new SyncDescriptor(ProgressService2));

513
		// Keybindings
A
Alex Dima 已提交
514
		this.contextKeyService = this.instantiationService.createInstance(ContextKeyService);
515
		serviceCollection.set(IContextKeyService, this.contextKeyService);
516

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

520 521 522
		// List
		serviceCollection.set(IListService, this.instantiationService.createInstance(ListService));

523
		// Context Menu
524
		serviceCollection.set(IContextMenuService, new SyncDescriptor(ContextMenuService));
525

526 527
		// Menus/Actions
		serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
528

P
Pine Wu 已提交
529
		// Sidebar part
530
		this.sidebarPart = this.instantiationService.createInstance(SidebarPart, Identifiers.SIDEBAR_PART);
E
Erich Gamma 已提交
531 532
		this.toDispose.push(this.sidebarPart);
		this.toShutdown.push(this.sidebarPart);
P
Pine Wu 已提交
533 534 535 536

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

I
isidor 已提交
538
		// Panel service (panel part)
539
		this.panelPart = this.instantiationService.createInstance(PanelPart, Identifiers.PANEL_PART);
I
isidor 已提交
540 541
		this.toDispose.push(this.panelPart);
		this.toShutdown.push(this.panelPart);
542
		serviceCollection.set(IPanelService, this.panelPart);
I
isidor 已提交
543

E
Erich Gamma 已提交
544
		// Activity service (activitybar part)
545
		this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
E
Erich Gamma 已提交
546 547
		this.toDispose.push(this.activitybarPart);
		this.toShutdown.push(this.activitybarPart);
I
isidor 已提交
548 549
		const activityService = this.instantiationService.createInstance(ActivityService, this.activitybarPart, this.panelPart);
		serviceCollection.set(IActivityService, activityService);
E
Erich Gamma 已提交
550

551 552 553 554 555
		// 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 已提交
556
		// Editor service (editor part)
557
		this.editorPart = this.instantiationService.createInstance(EditorPart, Identifiers.EDITOR_PART, !this.hasFilesToCreateOpenOrDiff);
E
Erich Gamma 已提交
558 559
		this.toDispose.push(this.editorPart);
		this.toShutdown.push(this.editorPart);
560
		this.editorService = this.instantiationService.createInstance(WorkbenchEditorService, this.editorPart);
561
		serviceCollection.set(IWorkbenchEditorService, this.editorService);
562
		serviceCollection.set(IEditorGroupService, this.editorPart);
E
Erich Gamma 已提交
563

564 565 566 567 568 569
		// 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);

570
		// History
571
		serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService));
572

573
		// Backup File Service
574
		this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath);
575
		serviceCollection.set(IBackupFileService, this.backupFileService);
576

577
		// Text File Service
578
		serviceCollection.set(ITextFileService, new SyncDescriptor(TextFileService));
579

580
		// File Decorations
J
Johannes Rieken 已提交
581
		serviceCollection.set(IDecorationsService, new SyncDescriptor(FileDecorationsService));
582

J
Joao Moreno 已提交
583
		// SCM Service
584
		serviceCollection.set(ISCMService, new SyncDescriptor(SCMService));
585

B
Benjamin Pasero 已提交
586
		// Text Model Resolver Service
587
		serviceCollection.set(ITextModelService, new SyncDescriptor(TextModelResolverService));
B
Benjamin Pasero 已提交
588

589 590 591 592
		// JSON Editing
		const jsonEditingService = this.instantiationService.createInstance(JSONEditingService);
		serviceCollection.set(IJSONEditingService, jsonEditingService);

593 594 595
		// Workspace Editing
		serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService));

596 597 598
		// Keybinding Editing
		serviceCollection.set(IKeybindingEditingService, this.instantiationService.createInstance(KeybindingsEditingService));

599
		// Configuration Resolver
B
Benjamin Pasero 已提交
600
		serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, process.env));
601

E
Erich Gamma 已提交
602
		// Quick open service (quick open controller)
603
		this.quickOpen = this.instantiationService.createInstance(QuickOpenController);
E
Erich Gamma 已提交
604 605
		this.toDispose.push(this.quickOpen);
		this.toShutdown.push(this.quickOpen);
606
		serviceCollection.set(IQuickOpenService, this.quickOpen);
607

B
polish  
Benjamin Pasero 已提交
608
		// Contributed services
B
Benjamin Pasero 已提交
609
		const contributedServices = getServices();
E
Erich Gamma 已提交
610
		for (let contributedService of contributedServices) {
611
			serviceCollection.set(contributedService.id, contributedService.descriptor);
E
Erich Gamma 已提交
612 613 614
		}

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

619
		this.instantiationService.createInstance(DefaultConfigurationExportHelper);
620 621

		this.configurationService.setInstantiationService(this.getInstantiationService());
E
Erich Gamma 已提交
622 623 624 625 626
	}

	private initSettings(): void {

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

I
isidor 已提交
629
		// Panel part visibility
630
		const panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
B
Benjamin Pasero 已提交
631
		this.panelHidden = this.storageService.getBoolean(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE, true);
632 633
		if (!panelRegistry.getDefaultPanelId()) {
			this.panelHidden = true; // we hide panel part if there is no default panel
I
isidor 已提交
634
		}
I
isidor 已提交
635

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

I
isidor 已提交
640 641 642 643
		// Panel position
		const panelPosition = this.configurationService.getValue<string>(Workbench.panelPositionConfigurationKey);
		this.panelPosition = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM;

B
Benjamin Pasero 已提交
644
		// Statusbar visibility
645
		const statusBarVisible = this.configurationService.getValue<string>(Workbench.statusbarVisibleConfigurationKey);
646
		this.statusBarHidden = !statusBarVisible;
S
Sanders Lauture 已提交
647 648

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

652
		// Font aliasing
653
		this.fontAliasing = this.configurationService.getValue<string>(Workbench.fontAliasingConfigurationKey);
654

I
isidor 已提交
655 656
		// Zen mode
		this.zenMode = {
I
isidor 已提交
657
			active: false,
658 659 660
			transitionedToFullScreen: false,
			wasSideBarVisible: false,
			wasPanelVisible: false
I
isidor 已提交
661
		};
E
Erich Gamma 已提交
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
	}

	/**
	 * 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 hasFocus(part: Parts): boolean {
B
Benjamin Pasero 已提交
679
		const activeElement = document.activeElement;
E
Erich Gamma 已提交
680 681 682 683
		if (!activeElement) {
			return false;
		}

684 685 686 687 688
		const container = this.getContainer(part);
		return DOM.isAncestor(activeElement, container);
	}

	public getContainer(part: Parts): HTMLElement {
E
Erich Gamma 已提交
689 690
		let container: Builder = null;
		switch (part) {
B
Benjamin Pasero 已提交
691 692 693
			case Parts.TITLEBAR_PART:
				container = this.titlebarPart.getContainer();
				break;
E
Erich Gamma 已提交
694 695 696 697 698 699
			case Parts.ACTIVITYBAR_PART:
				container = this.activitybarPart.getContainer();
				break;
			case Parts.SIDEBAR_PART:
				container = this.sidebarPart.getContainer();
				break;
I
isidor 已提交
700 701 702
			case Parts.PANEL_PART:
				container = this.panelPart.getContainer();
				break;
E
Erich Gamma 已提交
703 704 705 706 707 708 709
			case Parts.EDITOR_PART:
				container = this.editorPart.getContainer();
				break;
			case Parts.STATUSBAR_PART:
				container = this.statusbarPart.getContainer();
				break;
		}
710
		return container && container.getHTMLElement();
E
Erich Gamma 已提交
711 712 713
	}

	public isVisible(part: Parts): boolean {
B
Benjamin Pasero 已提交
714
		switch (part) {
B
Benjamin Pasero 已提交
715
			case Parts.TITLEBAR_PART:
716
				return this.getCustomTitleBarStyle() && !browser.isFullscreen();
B
Benjamin Pasero 已提交
717
			case Parts.SIDEBAR_PART:
718
				return !this.sideBarHidden;
B
Benjamin Pasero 已提交
719
			case Parts.PANEL_PART:
720
				return !this.panelHidden;
B
Benjamin Pasero 已提交
721
			case Parts.STATUSBAR_PART:
722
				return !this.statusBarHidden;
S
Sanders Lauture 已提交
723
			case Parts.ACTIVITYBAR_PART:
724
				return !this.activityBarHidden;
725
		}
E
Erich Gamma 已提交
726 727 728 729

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

730 731
	public getTitleBarOffset(): number {
		let offset = 0;
732
		if (this.isVisible(Parts.TITLEBAR_PART)) {
733 734 735 736 737 738
			offset = 22 / browser.getZoomFactor(); // adjust the position based on title bar size and zoom factor
		}

		return offset;
	}

739
	private getCustomTitleBarStyle(): 'custom' {
B
Benjamin Pasero 已提交
740
		if (!isMacintosh) {
741
			return null; // custom title bar is only supported on Mac currently
B
Benjamin Pasero 已提交
742 743
		}

744
		const isDev = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment;
B
Benjamin Pasero 已提交
745
		if (isDev) {
746
			return null; // not enabled when developing due to https://github.com/electron/electron/issues/3647
B
Benjamin Pasero 已提交
747 748
		}

749
		const windowConfig = this.configurationService.getConfiguration<IWindowSettings>();
750 751 752 753 754
		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 已提交
755

756 757 758 759
			const style = windowConfig.window.titleBarStyle;
			if (style === 'custom') {
				return style;
			}
760 761 762
		}

		return null;
B
Benjamin Pasero 已提交
763 764
	}

765
	private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void {
766 767
		this.statusBarHidden = hidden;

I
isidor 已提交
768

769 770
		// Layout
		if (!skipLayout) {
771
			this.workbenchLayout.layout();
772 773 774
		}
	}

S
Sanders Lauture 已提交
775 776 777
	public setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void {
		this.activityBarHidden = hidden;

I
isidor 已提交
778

S
Sanders Lauture 已提交
779 780
		// Layout
		if (!skipLayout) {
781
			this.workbenchLayout.layout();
S
Sanders Lauture 已提交
782 783 784
		}
	}

785
	public setSideBarHidden(hidden: boolean, skipLayout?: boolean): TPromise<void> {
E
Erich Gamma 已提交
786
		this.sideBarHidden = hidden;
787
		this.sideBarVisibleContext.set(!hidden);
E
Erich Gamma 已提交
788 789 790 791 792 793 794 795

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

B
Benjamin Pasero 已提交
796
		// If sidebar becomes hidden, also hide the current active Viewlet if any
797
		let promise = TPromise.wrap<any>(null);
E
Erich Gamma 已提交
798
		if (hidden && this.sidebarPart.getActiveViewlet()) {
799 800 801
			promise = this.sidebarPart.hideActiveViewlet().then(() => {
				const activeEditor = this.editorPart.getActiveEditor();
				const activePanel = this.panelPart.getActivePanel();
B
Benjamin Pasero 已提交
802

803 804 805 806 807 808 809
				// 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 已提交
810 811
		}

B
Benjamin Pasero 已提交
812
		// If sidebar becomes visible, show last active Viewlet or default viewlet
E
Erich Gamma 已提交
813
		else if (!hidden && !this.sidebarPart.getActiveViewlet()) {
814
			const viewletToOpen = this.sidebarPart.getLastActiveViewletId();
E
Erich Gamma 已提交
815
			if (viewletToOpen) {
816
				promise = this.sidebarPart.openViewlet(viewletToOpen, true);
E
Erich Gamma 已提交
817 818 819
			}
		}

820
		return promise.then(() => {
821

822
			// Remember in settings
823
			const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY;
824 825 826 827 828
			if (hidden !== defaultHidden) {
				this.storageService.store(Workbench.sidebarHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE);
			} else {
				this.storageService.remove(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE);
			}
829 830 831

			// Layout
			if (!skipLayout) {
832
				this.workbenchLayout.layout();
833 834
			}
		});
E
Erich Gamma 已提交
835 836
	}

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

840 841 842 843 844 845 846
		// Adjust CSS
		if (hidden) {
			this.workbench.addClass('nopanel');
		} else {
			this.workbench.removeClass('nopanel');
		}

I
isidor 已提交
847
		// If panel part becomes hidden, also hide the current active panel if any
848
		let promise = TPromise.wrap<any>(null);
I
isidor 已提交
849
		if (hidden && this.panelPart.getActivePanel()) {
850 851 852 853 854 855 856
			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 已提交
857 858 859 860
		}

		// If panel part becomes visible, show last active panel or default panel
		else if (!hidden && !this.panelPart.getActivePanel()) {
861
			const panelToOpen = this.panelPart.getLastActivePanelId();
I
isidor 已提交
862
			if (panelToOpen) {
863
				promise = this.panelPart.openPanel(panelToOpen, true);
I
isidor 已提交
864 865 866
			}
		}

867
		return promise.then(() => {
868

869
			// Remember in settings
870 871 872 873 874
			if (!hidden) {
				this.storageService.store(Workbench.panelHiddenSettingKey, 'false', StorageScope.WORKSPACE);
			} else {
				this.storageService.remove(Workbench.panelHiddenSettingKey, StorageScope.WORKSPACE);
			}
875 876 877

			// Layout
			if (!skipLayout) {
878
				this.workbenchLayout.layout();
879 880
			}
		});
I
isidor 已提交
881 882
	}

883 884 885 886 887 888 889 890
	public toggleMaximizedPanel(): void {
		this.workbenchLayout.layout({ toggleMaximizedPanel: true });
	}

	public isPanelMaximized(): boolean {
		return this.workbenchLayout.isPanelMaximized();
	}

E
Erich Gamma 已提交
891 892 893 894
	public getSideBarPosition(): Position {
		return this.sideBarPosition;
	}

895
	private setSideBarPosition(position: Position): void {
E
Erich Gamma 已提交
896
		if (this.sideBarHidden) {
897
			this.setSideBarHidden(false, true /* Skip Layout */).done(void 0, errors.onUnexpectedError);
E
Erich Gamma 已提交
898 899
		}

B
Benjamin Pasero 已提交
900 901
		const newPositionValue = (position === Position.LEFT) ? 'left' : 'right';
		const oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right';
E
Erich Gamma 已提交
902 903 904 905 906 907 908 909
		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);

910 911 912 913
		// Update Styles
		this.activitybarPart.updateStyles();
		this.sidebarPart.updateStyles();

E
Erich Gamma 已提交
914
		// Layout
915
		this.workbenchLayout.layout();
E
Erich Gamma 已提交
916 917
	}

I
isidor 已提交
918 919 920 921 922 923
	public getPanelPosition(): Position {
		return this.panelPosition;
	}

	private setPanelPosition(position: Position): void {
		if (this.panelHidden) {
924
			this.setPanelHidden(false, true /* Skip Layout */).done(void 0, errors.onUnexpectedError);
I
isidor 已提交
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
		}

		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();
	}

942 943
	private setFontAliasing(aliasing: string) {
		this.fontAliasing = aliasing;
B
Benjamin Pasero 已提交
944

945 946 947
		document.body.style['-webkit-font-smoothing'] = (aliasing === 'default' ? '' : aliasing);
	}

B
Benjamin Pasero 已提交
948
	public dispose(): void {
E
Erich Gamma 已提交
949
		if (this.isStarted()) {
B
Benjamin Pasero 已提交
950
			this.shutdownComponents();
E
Erich Gamma 已提交
951 952 953
			this.workbenchShutdown = true;
		}

J
Joao Moreno 已提交
954
		this.toDispose = dispose(this.toDispose);
E
Erich Gamma 已提交
955 956 957 958 959 960
	}

	/**
	 * Asks the workbench and all its UI components inside to lay out according to
	 * the containers dimension the workbench is living in.
	 */
961
	public layout(options?: ILayoutOptions): void {
E
Erich Gamma 已提交
962
		if (this.isStarted()) {
963
			this.workbenchLayout.layout(options);
E
Erich Gamma 已提交
964 965 966
		}
	}

967 968 969 970 971 972
	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 已提交
973

I
isidor 已提交
974
		// Preserve zen mode only on reload. Real quit gets out of zen mode so novice users do not get stuck in zen mode.
975 976 977 978 979 980 981
		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 已提交
982 983 984 985 986 987 988 989

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

	private registerListeners(): void {

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

992 993 994 995 996 997 998 999 1000 1001
		// 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);
		}

1002
		// Handle message service and quick open events
1003 1004
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesShowing(() => this.messagesVisibleContext.set(true)));
		this.toDispose.push((<WorkbenchMessageService>this.messageService).onMessagesCleared(() => this.messagesVisibleContext.reset()));
1005

1006 1007 1008 1009
		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
1010
		this.toDispose.push(this.configurationService.onDidChangeConfiguration(() => this.onDidUpdateConfiguration()));
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020

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

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

1021
		// Apply as CSS class
1022 1023 1024 1025 1026
		const isFullscreen = browser.isFullscreen();
		if (isFullscreen) {
			this.addClass('fullscreen');
		} else {
			this.removeClass('fullscreen');
I
isidor 已提交
1027 1028
			if (this.zenMode.transitionedToFullScreen && this.zenMode.active) {
				this.toggleZenMode();
I
isidor 已提交
1029
			}
1030
		}
1031

1032 1033 1034 1035 1036
		// 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
1037
		}
E
Erich Gamma 已提交
1038 1039
	}

1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
	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);
		}
	}

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

1055 1056
		// 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
1057
		// or setting is disabled. Also enabled when running with --wait from the command line.
1058
		if (visibleEditors === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) {
1059
			const closeWhenEmpty = this.configurationService.getValue<boolean>(Workbench.closeWhenEmptyConfigurationKey);
1060
			if (closeWhenEmpty || this.environmentService.args.wait) {
1061 1062 1063 1064
				this.closeEmptyWindowScheduler.schedule();
			}
		}

E
Erich Gamma 已提交
1065 1066
		// 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 已提交
1067 1068 1069 1070 1071 1072
		this.handleEditorBackground();
	}

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

1073
		const editorContainer = this.editorPart.getContainer();
E
Erich Gamma 已提交
1074
		if (visibleEditors === 0) {
1075
			this.editorsVisibleContext.reset();
1076
			this.editorBackgroundDelayer.trigger(() => editorContainer.addClass('empty'));
E
Erich Gamma 已提交
1077
		} else {
1078
			this.editorsVisibleContext.set(true);
1079
			this.editorBackgroundDelayer.trigger(() => editorContainer.removeClass('empty'));
E
Erich Gamma 已提交
1080 1081 1082
		}
	}

1083 1084 1085 1086 1087 1088 1089
	private onAllEditorsClosed(): void {
		const visibleEditors = this.editorService.getVisibleEditors().length;
		if (visibleEditors === 0) {
			this.windowService.closeWindow();
		}
	}

1090
	private onDidUpdateConfiguration(skipLayout?: boolean): void {
1091
		const newSidebarPositionValue = this.configurationService.getValue<string>(Workbench.sidebarPositionConfigurationKey);
1092
		const newSidebarPosition = (newSidebarPositionValue === 'right') ? Position.RIGHT : Position.LEFT;
1093 1094 1095
		if (newSidebarPosition !== this.getSideBarPosition()) {
			this.setSideBarPosition(newSidebarPosition);
		}
1096

I
isidor 已提交
1097 1098
		const newPanelPositionValue = this.configurationService.getValue<string>(Workbench.panelPositionConfigurationKey);
		const newPanelPosition = (newPanelPositionValue === 'right') ? Position.RIGHT : Position.BOTTOM;
I
isidor 已提交
1099
		if (newPanelPosition !== this.getPanelPosition()) {
I
isidor 已提交
1100 1101 1102
			this.setPanelPosition(newPanelPosition);
		}

1103
		const fontAliasing = this.configurationService.getValue<string>(Workbench.fontAliasingConfigurationKey);
1104 1105 1106 1107
		if (fontAliasing !== this.fontAliasing) {
			this.setFontAliasing(fontAliasing);
		}

1108
		if (!this.zenMode.active) {
1109
			const newStatusbarHiddenValue = !this.configurationService.getValue<boolean>(Workbench.statusbarVisibleConfigurationKey);
1110 1111 1112
			if (newStatusbarHiddenValue !== this.statusBarHidden) {
				this.setStatusBarHidden(newStatusbarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1113

1114
			const newActivityBarHiddenValue = !this.configurationService.getValue<boolean>(Workbench.activityBarVisibleConfigurationKey);
1115 1116 1117
			if (newActivityBarHiddenValue !== this.activityBarHidden) {
				this.setActivityBarHidden(newActivityBarHiddenValue, skipLayout);
			}
S
Sanders Lauture 已提交
1118
		}
1119 1120
	}

E
Erich Gamma 已提交
1121 1122 1123 1124
	private createWorkbenchLayout(): void {
		this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout,
			$(this.container),							// Parent
			this.workbench,								// Workbench Container
I
isidor 已提交
1125
			{
B
Benjamin Pasero 已提交
1126
				titlebar: this.titlebarPart,			// Title Bar
I
isidor 已提交
1127 1128 1129 1130 1131 1132
				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 已提交
1133
			this.quickOpen								// Quickopen
E
Erich Gamma 已提交
1134 1135 1136 1137 1138 1139 1140 1141 1142
		);

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

	private createWorkbench(): void {

		// Create Workbench DIV Off-DOM
		this.workbenchContainer = $('.monaco-workbench-container');
1143
		this.workbench = $().div({ 'class': 'monaco-workbench ' + (isWindows ? 'windows' : isLinux ? 'linux' : 'mac'), id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer);
E
Erich Gamma 已提交
1144 1145 1146 1147 1148 1149 1150 1151
	}

	private renderWorkbench(): void {

		// Apply sidebar state as CSS class
		if (this.sideBarHidden) {
			this.workbench.addClass('nosidebar');
		}
1152 1153 1154
		if (this.panelHidden) {
			this.workbench.addClass('nopanel');
		}
E
Erich Gamma 已提交
1155

B
Benjamin Pasero 已提交
1156
		// Apply font aliasing
1157 1158
		this.setFontAliasing(this.fontAliasing);

B
Benjamin Pasero 已提交
1159
		// Apply title style if shown
1160 1161 1162 1163 1164 1165 1166 1167
		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 已提交
1168 1169
		}

E
Erich Gamma 已提交
1170
		// Create Parts
B
Benjamin Pasero 已提交
1171
		this.createTitlebarPart();
E
Erich Gamma 已提交
1172 1173 1174
		this.createActivityBarPart();
		this.createSidebarPart();
		this.createEditorPart();
1175
		this.createPanelPart();
E
Erich Gamma 已提交
1176 1177 1178 1179 1180 1181
		this.createStatusbarPart();

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

B
Benjamin Pasero 已提交
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
	private createTitlebarPart(): void {
		const titlebarContainer = $(this.workbench).div({
			'class': ['part', 'titlebar'],
			id: Identifiers.TITLEBAR_PART,
			role: 'contentinfo'
		});

		this.titlebarPart.create(titlebarContainer);
	}

E
Erich Gamma 已提交
1192
	private createActivityBarPart(): void {
B
Benjamin Pasero 已提交
1193
		const activitybarPartContainer = $(this.workbench)
E
Erich Gamma 已提交
1194 1195
			.div({
				'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'],
1196 1197
				id: Identifiers.ACTIVITYBAR_PART,
				role: 'navigation'
E
Erich Gamma 已提交
1198 1199 1200 1201 1202 1203
			});

		this.activitybarPart.create(activitybarPartContainer);
	}

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

		this.sidebarPart.create(sidebarPartContainer);
	}

I
isidor 已提交
1214
	private createPanelPart(): void {
B
Benjamin Pasero 已提交
1215
		const panelPartContainer = $(this.workbench)
I
isidor 已提交
1216
			.div({
1217
				'class': ['part', 'panel', this.panelPosition === Position.BOTTOM ? 'bottom' : 'right'],
1218 1219
				id: Identifiers.PANEL_PART,
				role: 'complementary'
I
isidor 已提交
1220 1221 1222 1223 1224
			});

		this.panelPart.create(panelPartContainer);
	}

E
Erich Gamma 已提交
1225
	private createEditorPart(): void {
B
Benjamin Pasero 已提交
1226
		const editorContainer = $(this.workbench)
E
Erich Gamma 已提交
1227
			.div({
1228
				'class': ['part', 'editor', 'empty'],
1229 1230
				id: Identifiers.EDITOR_PART,
				role: 'main'
E
Erich Gamma 已提交
1231 1232 1233 1234 1235 1236
			});

		this.editorPart.create(editorContainer);
	}

	private createStatusbarPart(): void {
B
Benjamin Pasero 已提交
1237
		const statusbarContainer = $(this.workbench).div({
E
Erich Gamma 已提交
1238
			'class': ['part', 'statusbar'],
1239 1240
			id: Identifiers.STATUSBAR_PART,
			role: 'contentinfo'
E
Erich Gamma 已提交
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
		});

		this.statusbarPart.create(statusbarContainer);
	}

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

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

I
isidor 已提交
1254 1255 1256 1257
	public getPanelPart(): PanelPart {
		return this.panelPart;
	}

E
Erich Gamma 已提交
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
	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);
		}
	}
1273 1274 1275 1276

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

I
isidor 已提交
1278
	public toggleZenMode(skipLayout?: boolean): void {
I
isidor 已提交
1279
		this.zenMode.active = !this.zenMode.active;
1280

I
isidor 已提交
1281
		// 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
1282
		let toggleFullScreen = false;
I
isidor 已提交
1283
		if (this.zenMode.active) {
I
isidor 已提交
1284 1285
			const config = this.configurationService.getConfiguration<IZenModeSettings>('zenMode');
			toggleFullScreen = !browser.isFullscreen() && config.fullScreen;
I
isidor 已提交
1286
			this.zenMode.transitionedToFullScreen = toggleFullScreen;
1287 1288
			this.zenMode.wasSideBarVisible = this.isVisible(Parts.SIDEBAR_PART);
			this.zenMode.wasPanelVisible = this.isVisible(Parts.PANEL_PART);
1289 1290
			this.setPanelHidden(true, true).done(void 0, errors.onUnexpectedError);
			this.setSideBarHidden(true, true).done(void 0, errors.onUnexpectedError);
I
isidor 已提交
1291

1292 1293 1294
			if (config.hideActivityBar) {
				this.setActivityBarHidden(true, true);
			}
1295

I
isidor 已提交
1296
			if (config.hideStatusBar) {
I
isidor 已提交
1297 1298
				this.setStatusBarHidden(true, true);
			}
1299

I
isidor 已提交
1300
			if (config.hideTabs) {
I
isidor 已提交
1301 1302
				this.editorPart.hideTabs(true);
			}
1303
		} else {
1304
			if (this.zenMode.wasPanelVisible) {
1305
				this.setPanelHidden(false, true).done(void 0, errors.onUnexpectedError);
1306
			}
1307

1308
			if (this.zenMode.wasSideBarVisible) {
1309
				this.setSideBarHidden(false, true).done(void 0, errors.onUnexpectedError);
1310
			}
1311

1312 1313
			// Status bar and activity bar visibility come from settings -> update their visibility.
			this.onDidUpdateConfiguration(true);
I
isidor 已提交
1314
			this.editorPart.hideTabs(false);
I
isidor 已提交
1315 1316 1317 1318
			const activeEditor = this.editorPart.getActiveEditor();
			if (activeEditor) {
				activeEditor.focus();
			}
1319

1320
			toggleFullScreen = this.zenMode.transitionedToFullScreen && browser.isFullscreen();
I
isidor 已提交
1321
		}
1322

1323
		this.inZenMode.set(this.zenMode.active);
I
isidor 已提交
1324

I
isidor 已提交
1325
		if (!skipLayout) {
1326
			this.layout();
I
isidor 已提交
1327
		}
1328

1329
		if (toggleFullScreen) {
1330
			this.windowService.toggleFullScreen().done(void 0, errors.onUnexpectedError);
1331
		}
I
isidor 已提交
1332 1333
	}

1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
	// 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 已提交
1344
				return; // Cannot resize other parts
1345 1346 1347 1348
		}
	}


1349
	private shouldRestoreLastOpenedViewlet(): boolean {
1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
		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 已提交
1361
}