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
	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 => {

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

322 323 324
			// update lifecycle *after* triggering the editor restore
			this.lifecycleService.phase = LifecyclePhase.Restoring;

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

				editorRestoreClock.stop();
				editorRestoreStopWatch.stop();
330

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

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

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

353 354 355
			if (!viewletIdToRestore) {
				viewletIdToRestore = this.viewletService.getDefaultViewletId();
			}
356

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

365 366 367 368 369 370
		// 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 已提交
371

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

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

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

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

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

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

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

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

431 432
			const filesToCreate = this.toInputs(config.filesToCreate, true);
			const filesToOpen = this.toInputs(config.filesToOpen, false);
433

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

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

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

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

		return TPromise.as([]);
	}

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

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

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

			return input;
		});
	}

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

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

492
		return startupEditor.value === 'newUntitledFile';
493 494
	}

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

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

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

503 504 505
		// Clipboard
		serviceCollection.set(IClipboardService, new ClipboardService());

506 507 508 509 510 511
		// 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);

512 513 514
		// Progress 2
		serviceCollection.set(IProgressService2, new SyncDescriptor(ProgressService2));

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

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

522 523 524
		// List
		serviceCollection.set(IListService, this.instantiationService.createInstance(ListService));

525
		// Context Menu
526
		serviceCollection.set(IContextMenuService, new SyncDescriptor(ContextMenuService));
527

528 529
		// Menus/Actions
		serviceCollection.set(IMenuService, new SyncDescriptor(MenuService));
530

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

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

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

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

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

566 567 568 569 570 571
		// 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);

572
		// History
573
		serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService));
574

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

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

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

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

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

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

595 596 597
		// Workspace Editing
		serviceCollection.set(IWorkspaceEditingService, new SyncDescriptor(WorkspaceEditingService));

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

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

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

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

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

621
		this.instantiationService.createInstance(DefaultConfigurationExportHelper);
622 623

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

	private initSettings(): void {

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

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

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

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

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

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

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

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

	/**
	 * 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 已提交
681
		const activeElement = document.activeElement;
E
Erich Gamma 已提交
682 683 684 685
		if (!activeElement) {
			return false;
		}

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

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

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

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

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

		return offset;
	}

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

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

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

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

		return null;
B
Benjamin Pasero 已提交
765 766
	}

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

I
isidor 已提交
770

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

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

I
isidor 已提交
780

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

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

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

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

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

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

822
		return promise.then(() => {
823

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

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

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

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

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

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

869
		return promise.then(() => {
870

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

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

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

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

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

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

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

912 913 914 915
		// Update Styles
		this.activitybarPart.updateStyles();
		this.sidebarPart.updateStyles();

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

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

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

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

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

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

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

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

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

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

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

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

	private registerListeners(): void {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	private createWorkbench(): void {

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

	private renderWorkbench(): void {

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

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

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

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

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

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

		this.titlebarPart.create(titlebarContainer);
	}

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

		this.activitybarPart.create(activitybarPartContainer);
	}

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

		this.sidebarPart.create(sidebarPartContainer);
	}

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

		this.panelPart.create(panelPartContainer);
	}

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

		this.editorPart.create(editorContainer);
	}

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

		this.statusbarPart.create(statusbarContainer);
	}

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

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

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

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

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

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

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

1294 1295 1296
			if (config.hideActivityBar) {
				this.setActivityBarHidden(true, true);
			}
1297

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

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

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

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

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

1325
		this.inZenMode.set(this.zenMode.active);
I
isidor 已提交
1326

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

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

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


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