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

'use strict';

J
Joao Moreno 已提交
8
import * as nls from 'vs/nls';
9
import { isMacintosh, isLinux, isWindows, language } from 'vs/base/common/platform';
J
Joao Moreno 已提交
10
import * as arrays from 'vs/base/common/arrays';
11
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
12
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem, BrowserWindow } from 'electron';
C
Christof Marti 已提交
13 14
import { OpenContext } from 'vs/code/common/windows';
import { IWindowsMainService } from 'vs/code/electron-main/windows';
15
import { VSCodeWindow } from 'vs/code/electron-main/window';
B
Benjamin Pasero 已提交
16
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
17
import { IStorageService } from 'vs/code/electron-main/storage';
B
Benjamin Pasero 已提交
18
import { IFilesConfiguration, AutoSaveConfiguration } from 'vs/platform/files/common/files';
J
Joao Moreno 已提交
19
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
J
Joao Moreno 已提交
20
import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update';
21
import product from 'vs/platform/node/product';
22
import { RunOnceScheduler } from 'vs/base/common/async';
23 24 25 26
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import Event, { Emitter, once } from 'vs/base/common/event';
import { ConfigWatcher } from 'vs/base/node/config';
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
E
Erich Gamma 已提交
27

28
interface IKeybinding {
B
Benjamin Pasero 已提交
29
	id: string;
30 31
	label: string;
	isNative: boolean;
E
Erich Gamma 已提交
32 33
}

34 35 36 37 38
interface IExtensionViewlet {
	id: string;
	label: string;
}

B
Benjamin Pasero 已提交
39
interface IConfiguration extends IFilesConfiguration {
40 41 42
	window: {
		enableMenuBarMnemonics: boolean;
	};
B
Benjamin Pasero 已提交
43 44 45 46 47 48
	workbench: {
		sideBar: {
			location: 'left' | 'right';
		},
		statusBar: {
			visible: boolean;
S
Sanders Lauture 已提交
49 50 51
		},
		activityBar: {
			visible: boolean;
B
Benjamin Pasero 已提交
52 53
		}
	};
54 55 56
	editor: {
		multiCursorModifier: 'ctrlCmd' | 'alt'
	};
B
Benjamin Pasero 已提交
57 58
}

59
class KeybindingsResolver {
E
Erich Gamma 已提交
60 61 62

	private static lastKnownKeybindingsMapStorageKey = 'lastKnownKeybindings';

63
	private commandIds: Set<string>;
64
	private keybindings: { [commandId: string]: IKeybinding };
65 66 67 68 69 70 71 72 73 74 75 76
	private keybindingsWatcher: ConfigWatcher<IUserFriendlyKeybinding[]>;

	private _onKeybindingsChanged = new Emitter<void>();
	onKeybindingsChanged: Event<void> = this._onKeybindingsChanged.event;

	constructor(
		@IStorageService private storageService: IStorageService,
		@IEnvironmentService environmentService: IEnvironmentService,
		@IWindowsMainService private windowsService: IWindowsMainService
	) {
		this.commandIds = new Set<string>();
		this.keybindings = this.storageService.getItem<{ [id: string]: string; }>(KeybindingsResolver.lastKnownKeybindingsMapStorageKey) || Object.create(null);
77
		this.keybindingsWatcher = new ConfigWatcher<IUserFriendlyKeybinding[]>(environmentService.appKeybindingsPath, { changeBufferDelay: 100 });
78 79 80 81 82 83 84 85 86 87 88 89

		this.registerListeners();
	}

	private registerListeners(): void {

		// Resolve keybindings when any first window is loaded
		const onceOnWindowReady = once(this.windowsService.onWindowReady);
		onceOnWindowReady(win => this.resolveKeybindings(win));

		// Listen to resolved keybindings from window
		ipc.on('vscode:keybindingsResolved', (event, rawKeybindings: string) => {
90
			let keybindings: IKeybinding[] = [];
91 92 93 94 95 96 97 98 99
			try {
				keybindings = JSON.parse(rawKeybindings);
			} catch (error) {
				// Should not happen
			}

			// Fill hash map of resolved keybindings and check for changes
			let keybindingsChanged = false;
			let keybindingsCount = 0;
100
			const resolvedKeybindings: { [commandId: string]: IKeybinding } = Object.create(null);
101
			keybindings.forEach(keybinding => {
102
				keybindingsCount++;
103

104
				resolvedKeybindings[keybinding.id] = keybinding;
B
Benjamin Pasero 已提交
105

106 107
				if (!this.keybindings[keybinding.id] || keybinding.label !== this.keybindings[keybinding.id].label) {
					keybindingsChanged = true;
108 109 110 111 112 113 114 115 116
				}
			});

			// A keybinding might have been unassigned, so we have to account for that too
			if (Object.keys(this.keybindings).length !== keybindingsCount) {
				keybindingsChanged = true;
			}

			if (keybindingsChanged) {
B
Benjamin Pasero 已提交
117
				this.keybindings = resolvedKeybindings;
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
				this.storageService.setItem(KeybindingsResolver.lastKnownKeybindingsMapStorageKey, this.keybindings); // keep to restore instantly after restart

				this._onKeybindingsChanged.fire();
			}
		});

		// Resolve keybindings again when keybindings.json changes
		this.keybindingsWatcher.onDidUpdateConfiguration(() => this.resolveKeybindings());

		// Resolve keybindings when window reloads because an installed extension could have an impact
		this.windowsService.onWindowReload(() => this.resolveKeybindings());
	}

	private resolveKeybindings(win: VSCodeWindow = this.windowsService.getLastActiveWindow()): void {
		if (this.commandIds.size && win) {
			const commandIds = [];
			this.commandIds.forEach(id => commandIds.push(id));
			win.sendWhenReady('vscode:resolveKeybindings', JSON.stringify(commandIds));
		}
	}

139 140 141 142 143
	public getKeybinding(commandId: string): IKeybinding {
		if (!commandId) {
			return void 0;
		}

144 145 146 147 148 149 150 151
		if (!this.commandIds.has(commandId)) {
			this.commandIds.add(commandId);
		}

		return this.keybindings[commandId];
	}
}

152 153
const telemetryFrom = 'menu';

154 155
export class VSCodeMenu {

156
	private static MAX_MENU_RECENT_ENTRIES = 10;
157

B
Benjamin Pasero 已提交
158
	private currentAutoSaveSetting: string;
159
	private currentMultiCursorModifierSetting: string;
B
Benjamin Pasero 已提交
160 161
	private currentSidebarLocation: 'left' | 'right';
	private currentStatusbarVisible: boolean;
S
Sanders Lauture 已提交
162
	private currentActivityBarVisible: boolean;
163
	private currentEnableMenuBarMnemonics: boolean;
B
Benjamin Pasero 已提交
164

B
Benjamin Pasero 已提交
165
	private isQuitting: boolean;
E
Erich Gamma 已提交
166 167
	private appMenuInstalled: boolean;

168 169
	private menuUpdater: RunOnceScheduler;

170
	private keybindingsResolver: KeybindingsResolver;
E
Erich Gamma 已提交
171

172 173
	private extensionViewlets: IExtensionViewlet[];

J
Joao Moreno 已提交
174
	constructor(
B
Benjamin Pasero 已提交
175
		@IUpdateService private updateService: IUpdateService,
176
		@IInstantiationService instantiationService: IInstantiationService,
B
Benjamin Pasero 已提交
177
		@IConfigurationService private configurationService: IConfigurationService,
J
Joao Moreno 已提交
178
		@IWindowsMainService private windowsService: IWindowsMainService,
J
Joao Moreno 已提交
179 180
		@IEnvironmentService private environmentService: IEnvironmentService,
		@ITelemetryService private telemetryService: ITelemetryService
J
Joao Moreno 已提交
181
	) {
182
		this.extensionViewlets = [];
E
Erich Gamma 已提交
183

184
		this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
185
		this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver);
186

B
Benjamin Pasero 已提交
187
		this.onConfigurationUpdated(this.configurationService.getConfiguration<IConfiguration>());
E
Erich Gamma 已提交
188 189

		this.install();
190 191

		this.registerListeners();
E
Erich Gamma 已提交
192 193 194 195 196 197
	}

	private registerListeners(): void {

		// Keep flag when app quits
		app.on('will-quit', () => {
B
Benjamin Pasero 已提交
198
			this.isQuitting = true;
E
Erich Gamma 已提交
199 200
		});

201
		// Listen to some events from window service
B
Benjamin Pasero 已提交
202
		this.windowsService.onPathsOpen(paths => this.updateMenu());
203 204
		this.windowsService.onRecentPathsChange(paths => this.updateMenu());
		this.windowsService.onWindowClose(_ => this.onClose(this.windowsService.getWindowCount()));
E
Erich Gamma 已提交
205

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
		// Listen to extension viewlets
		ipc.on('vscode:extensionViewlets', (event, rawExtensionViewlets) => {
			let extensionViewlets: IExtensionViewlet[] = [];
			try {
				extensionViewlets = JSON.parse(rawExtensionViewlets);
			} catch (error) {
				// Should not happen
			}

			if (extensionViewlets.length) {
				this.extensionViewlets = extensionViewlets;
				this.updateMenu();
			}
		});

B
Benjamin Pasero 已提交
221
		// Update when auto save config changes
B
Benjamin Pasero 已提交
222
		this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(e.config, true /* update menu if changed */));
B
Benjamin Pasero 已提交
223

B
Benjamin Pasero 已提交
224
		// Listen to update service
225
		this.updateService.onStateChange(() => this.updateMenu());
226 227 228

		// Listen to keybindings change
		this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu());
E
Erich Gamma 已提交
229 230
	}

B
Benjamin Pasero 已提交
231
	private onConfigurationUpdated(config: IConfiguration, handleMenu?: boolean): void {
B
Benjamin Pasero 已提交
232
		let updateMenu = false;
B
Benjamin Pasero 已提交
233 234 235
		const newAutoSaveSetting = config && config.files && config.files.autoSave;
		if (newAutoSaveSetting !== this.currentAutoSaveSetting) {
			this.currentAutoSaveSetting = newAutoSaveSetting;
B
Benjamin Pasero 已提交
236 237 238
			updateMenu = true;
		}

239 240 241 242 243 244
		const newMultiCursorModifierSetting = config && config.editor && config.editor.multiCursorModifier;
		if (newMultiCursorModifierSetting !== this.currentMultiCursorModifierSetting) {
			this.currentMultiCursorModifierSetting = newMultiCursorModifierSetting;
			updateMenu = true;
		}

245 246 247 248 249
		const newSidebarLocation = config && config.workbench && config.workbench.sideBar && config.workbench.sideBar.location || 'left';
		if (newSidebarLocation !== this.currentSidebarLocation) {
			this.currentSidebarLocation = newSidebarLocation;
			updateMenu = true;
		}
B
Benjamin Pasero 已提交
250

251 252 253 254 255 256 257 258
		let newStatusbarVisible = config && config.workbench && config.workbench.statusBar && config.workbench.statusBar.visible;
		if (typeof newStatusbarVisible !== 'boolean') {
			newStatusbarVisible = true;
		}
		if (newStatusbarVisible !== this.currentStatusbarVisible) {
			this.currentStatusbarVisible = newStatusbarVisible;
			updateMenu = true;
		}
S
Sanders Lauture 已提交
259

260 261 262 263 264 265 266
		let newActivityBarVisible = config && config.workbench && config.workbench.activityBar && config.workbench.activityBar.visible;
		if (typeof newActivityBarVisible !== 'boolean') {
			newActivityBarVisible = true;
		}
		if (newActivityBarVisible !== this.currentActivityBarVisible) {
			this.currentActivityBarVisible = newActivityBarVisible;
			updateMenu = true;
B
Benjamin Pasero 已提交
267 268
		}

269 270 271 272 273 274 275 276 277
		let newEnableMenuBarMnemonics = config && config.window && config.window.enableMenuBarMnemonics;
		if (typeof newEnableMenuBarMnemonics !== 'boolean') {
			newEnableMenuBarMnemonics = true;
		}
		if (newEnableMenuBarMnemonics !== this.currentEnableMenuBarMnemonics) {
			this.currentEnableMenuBarMnemonics = newEnableMenuBarMnemonics;
			updateMenu = true;
		}

B
Benjamin Pasero 已提交
278
		if (handleMenu && updateMenu) {
B
Benjamin Pasero 已提交
279 280 281 282
			this.updateMenu();
		}
	}

E
Erich Gamma 已提交
283
	private updateMenu(): void {
284 285 286 287
		this.menuUpdater.schedule(); // buffer multiple attempts to update the menu
	}

	private doUpdateMenu(): void {
E
Erich Gamma 已提交
288 289 290

		// Due to limitations in Electron, it is not possible to update menu items dynamically. The suggested
		// workaround from Electron is to set the application menu again.
M
Martin Aeschlimann 已提交
291
		// See also https://github.com/electron/electron/issues/846
E
Erich Gamma 已提交
292 293 294 295 296 297 298 299 300 301 302
		//
		// Run delayed to prevent updating menu while it is open
		if (!this.isQuitting) {
			setTimeout(() => {
				if (!this.isQuitting) {
					this.install();
				}
			}, 10 /* delay this because there is an issue with updating a menu when it is open */);
		}
	}

B
Benjamin Pasero 已提交
303
	private onClose(remainingWindowCount: number): void {
304
		if (remainingWindowCount === 0 && isMacintosh) {
E
Erich Gamma 已提交
305 306 307 308 309 310 311
			this.updateMenu();
		}
	}

	private install(): void {

		// Menus
B
Benjamin Pasero 已提交
312
		const menubar = new Menu();
E
Erich Gamma 已提交
313 314

		// Mac: Application
B
Benjamin Pasero 已提交
315
		let macApplicationMenuItem: Electron.MenuItem;
316
		if (isMacintosh) {
B
Benjamin Pasero 已提交
317
			const applicationMenu = new Menu();
B
Benjamin Pasero 已提交
318
			macApplicationMenuItem = new MenuItem({ label: product.nameShort, submenu: applicationMenu });
E
Erich Gamma 已提交
319 320 321 322
			this.setMacApplicationMenu(applicationMenu);
		}

		// File
B
Benjamin Pasero 已提交
323
		const fileMenu = new Menu();
324
		const fileMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mFile', comment: ['&& denotes a mnemonic'] }, "&&File")), submenu: fileMenu });
E
Erich Gamma 已提交
325 326 327
		this.setFileMenu(fileMenu);

		// Edit
B
Benjamin Pasero 已提交
328
		const editMenu = new Menu();
329
		const editMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mEdit', comment: ['&& denotes a mnemonic'] }, "&&Edit")), submenu: editMenu });
E
Erich Gamma 已提交
330 331
		this.setEditMenu(editMenu);

C
Christof Marti 已提交
332 333
		// Selection
		const selectionMenu = new Menu();
334
		const selectionMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu });
C
Christof Marti 已提交
335 336
		this.setSelectionMenu(selectionMenu);

E
Erich Gamma 已提交
337
		// View
B
Benjamin Pasero 已提交
338
		const viewMenu = new Menu();
339
		const viewMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View")), submenu: viewMenu });
E
Erich Gamma 已提交
340 341 342
		this.setViewMenu(viewMenu);

		// Goto
B
Benjamin Pasero 已提交
343
		const gotoMenu = new Menu();
344
		const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu });
E
Erich Gamma 已提交
345 346
		this.setGotoMenu(gotoMenu);

I
isidor 已提交
347 348
		// Debug
		const debugMenu = new Menu();
349
		const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu });
I
isidor 已提交
350 351 352
		this.setDebugMenu(debugMenu);


E
Erich Gamma 已提交
353
		// Mac: Window
B
Benjamin Pasero 已提交
354
		let macWindowMenuItem: Electron.MenuItem;
355
		if (isMacintosh) {
B
Benjamin Pasero 已提交
356
			const windowMenu = new Menu();
357
			macWindowMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize('mWindow', "Window")), submenu: windowMenu, role: 'window' });
E
Erich Gamma 已提交
358 359 360 361
			this.setMacWindowMenu(windowMenu);
		}

		// Help
B
Benjamin Pasero 已提交
362
		const helpMenu = new Menu();
363
		const helpMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help")), submenu: helpMenu, role: 'help' });
E
Erich Gamma 已提交
364 365 366 367 368 369 370 371 372
		this.setHelpMenu(helpMenu);

		// Menu Structure
		if (macApplicationMenuItem) {
			menubar.append(macApplicationMenuItem);
		}

		menubar.append(fileMenuItem);
		menubar.append(editMenuItem);
C
Christof Marti 已提交
373
		menubar.append(selectionMenuItem);
E
Erich Gamma 已提交
374 375
		menubar.append(viewMenuItem);
		menubar.append(gotoMenuItem);
I
isidor 已提交
376
		menubar.append(debugMenuItem);
E
Erich Gamma 已提交
377 378 379 380 381 382 383 384 385 386

		if (macWindowMenuItem) {
			menubar.append(macWindowMenuItem);
		}

		menubar.append(helpMenuItem);

		Menu.setApplicationMenu(menubar);

		// Dock Menu
387
		if (isMacintosh && !this.appMenuInstalled) {
E
Erich Gamma 已提交
388 389
			this.appMenuInstalled = true;

B
Benjamin Pasero 已提交
390
			const dockMenu = new Menu();
391
			dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.DOCK) }));
E
Erich Gamma 已提交
392

393
			app.dock.setMenu(dockMenu);
E
Erich Gamma 已提交
394 395 396
		}
	}

B
Benjamin Pasero 已提交
397
	private setMacApplicationMenu(macApplicationMenu: Electron.Menu): void {
B
Benjamin Pasero 已提交
398
		const about = new MenuItem({ label: nls.localize('mAbout', "About {0}", product.nameLong), role: 'about' });
B
Benjamin Pasero 已提交
399 400
		const checkForUpdates = this.getUpdateMenuItems();
		const preferences = this.getPreferencesMenu();
401 402
		const servicesMenu = new Menu();
		const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu });
B
Benjamin Pasero 已提交
403
		const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' });
B
Benjamin Pasero 已提交
404 405
		const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' });
		const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' });
406
		const quit = new MenuItem(this.likeAction('workbench.action.quit', { label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => this.windowsService.quit() }));
B
Benjamin Pasero 已提交
407 408

		const actions = [about];
E
Erich Gamma 已提交
409 410 411 412 413
		actions.push(...checkForUpdates);
		actions.push(...[
			__separator__(),
			preferences,
			__separator__(),
414 415
			services,
			__separator__(),
E
Erich Gamma 已提交
416 417 418 419 420 421 422 423 424 425
			hide,
			hideOthers,
			showAll,
			__separator__(),
			quit
		]);

		actions.forEach(i => macApplicationMenu.append(i));
	}

B
Benjamin Pasero 已提交
426
	private setFileMenu(fileMenu: Electron.Menu): void {
B
Benjamin Pasero 已提交
427
		const hasNoWindows = (this.windowsService.getWindowCount() === 0);
E
Erich Gamma 已提交
428

B
Benjamin Pasero 已提交
429
		let newFile: Electron.MenuItem;
E
Erich Gamma 已提交
430
		if (hasNoWindows) {
431
			newFile = new MenuItem(this.likeAction('workbench.action.files.newUntitledFile', { label: this.mnemonicLabel(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
E
Erich Gamma 已提交
432
		} else {
B
Benjamin Pasero 已提交
433
			newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile');
E
Erich Gamma 已提交
434 435
		}

436 437
		const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.openFileFolderPicker(this.isOptionClick(event), { from: telemetryFrom }) }));
		const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.openFolderPicker(this.isOptionClick(event), undefined, { from: telemetryFrom }) }));
E
Erich Gamma 已提交
438

439 440
		let openFile: Electron.MenuItem;
		if (hasNoWindows) {
441
			openFile = new MenuItem(this.likeAction('workbench.action.files.openFile', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...")), click: (menuItem, win, event) => this.windowsService.openFilePicker(this.isOptionClick(event), undefined, undefined, { from: telemetryFrom }) }));
442
		} else {
443
			openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']);
444 445
		}

B
Benjamin Pasero 已提交
446
		const openRecentMenu = new Menu();
E
Erich Gamma 已提交
447
		this.setOpenRecentMenu(openRecentMenu);
448
		const openRecent = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent")), submenu: openRecentMenu, enabled: openRecentMenu.items.length > 0 });
E
Erich Gamma 已提交
449

B
Benjamin Pasero 已提交
450 451 452
		const saveFile = this.createMenuItem(nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), 'workbench.action.files.save', this.windowsService.getWindowCount() > 0);
		const saveFileAs = this.createMenuItem(nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As..."), 'workbench.action.files.saveAs', this.windowsService.getWindowCount() > 0);
		const saveAllFiles = this.createMenuItem(nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll"), 'workbench.action.files.saveAll', this.windowsService.getWindowCount() > 0);
E
Erich Gamma 已提交
453

B
Benjamin Pasero 已提交
454
		const autoSaveEnabled = [AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE].some(s => this.currentAutoSaveSetting === s);
455
		const autoSave = new MenuItem(this.likeAction('vscode.toggleAutoSave', { label: this.mnemonicLabel(nls.localize('miAutoSave', "Auto Save")), type: 'checkbox', checked: autoSaveEnabled, enabled: this.windowsService.getWindowCount() > 0, click: () => this.windowsService.sendToFocused('vscode.toggleAutoSave') }, false));
B
Benjamin Pasero 已提交
456

B
Benjamin Pasero 已提交
457
		const preferences = this.getPreferencesMenu();
E
Erich Gamma 已提交
458

459
		const newWindow = new MenuItem(this.likeAction('workbench.action.newWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.MENU) }));
B
Benjamin Pasero 已提交
460
		const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert', this.windowsService.getWindowCount() > 0);
461
		const closeWindow = new MenuItem(this.likeAction('workbench.action.closeWindow', { label: this.mnemonicLabel(nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window")), click: () => this.windowsService.getLastActiveWindow().win.close(), enabled: this.windowsService.getWindowCount() > 0 }));
E
Erich Gamma 已提交
462

B
Benjamin Pasero 已提交
463
		const closeFolder = this.createMenuItem(nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), 'workbench.action.closeFolder');
464
		const closeEditor = this.createMenuItem(nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor"), 'workbench.action.closeActiveEditor');
E
Erich Gamma 已提交
465

466
		const exit = new MenuItem(this.likeAction('workbench.action.quit', { label: this.mnemonicLabel(nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit")), click: () => this.windowsService.quit() }));
E
Erich Gamma 已提交
467 468 469 470 471

		arrays.coalesce([
			newFile,
			newWindow,
			__separator__(),
472 473 474
			isMacintosh ? open : null,
			!isMacintosh ? openFile : null,
			!isMacintosh ? openFolder : null,
E
Erich Gamma 已提交
475 476 477 478 479 480
			openRecent,
			__separator__(),
			saveFile,
			saveFileAs,
			saveAllFiles,
			__separator__(),
B
Benjamin Pasero 已提交
481 482
			autoSave,
			__separator__(),
483 484
			!isMacintosh ? preferences : null,
			!isMacintosh ? __separator__() : null,
E
Erich Gamma 已提交
485 486 487
			revertFile,
			closeEditor,
			closeFolder,
B
Benjamin Pasero 已提交
488
			closeWindow,
489 490
			!isMacintosh ? __separator__() : null,
			!isMacintosh ? exit : null
B
Benjamin Pasero 已提交
491
		]).forEach(item => fileMenu.append(item));
E
Erich Gamma 已提交
492 493
	}

B
Benjamin Pasero 已提交
494
	private getPreferencesMenu(): Electron.MenuItem {
S
Sandeep Somavarapu 已提交
495
		const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openGlobalSettings');
B
Benjamin Pasero 已提交
496
		const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings');
497
		const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions');
B
Benjamin Pasero 已提交
498 499 500 501 502
		const snippetsSettings = this.createMenuItem(nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), 'workbench.action.openSnippets');
		const colorThemeSelection = this.createMenuItem(nls.localize({ key: 'miSelectColorTheme', comment: ['&& denotes a mnemonic'] }, "&&Color Theme"), 'workbench.action.selectTheme');
		const iconThemeSelection = this.createMenuItem(nls.localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme"), 'workbench.action.selectIconTheme');

		const preferencesMenu = new Menu();
503
		preferencesMenu.append(settings);
E
Erich Gamma 已提交
504 505
		preferencesMenu.append(__separator__());
		preferencesMenu.append(kebindingSettings);
506
		preferencesMenu.append(keymapExtensions);
E
Erich Gamma 已提交
507 508 509
		preferencesMenu.append(__separator__());
		preferencesMenu.append(snippetsSettings);
		preferencesMenu.append(__separator__());
510 511
		preferencesMenu.append(colorThemeSelection);
		preferencesMenu.append(iconThemeSelection);
E
Erich Gamma 已提交
512

513
		return new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu });
E
Erich Gamma 已提交
514 515
	}

B
Benjamin Pasero 已提交
516
	private setOpenRecentMenu(openRecentMenu: Electron.Menu): void {
517
		openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor'));
D
Daniel Imms 已提交
518

A
Alex Dima 已提交
519
		const { folders, files } = this.windowsService.getRecentPathsList();
E
Erich Gamma 已提交
520 521

		// Folders
522
		if (folders.length > 0) {
523
			openRecentMenu.append(__separator__());
524 525

			for (let i = 0; i < VSCodeMenu.MAX_MENU_RECENT_ENTRIES && i < folders.length; i++) {
526
				openRecentMenu.append(this.createOpenRecentMenuItem(folders[i], 'openRecentFolder'));
527
			}
528
		}
E
Erich Gamma 已提交
529 530

		// Files
531
		if (files.length > 0) {
532
			openRecentMenu.append(__separator__());
E
Erich Gamma 已提交
533

534
			for (let i = 0; i < VSCodeMenu.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) {
535
				openRecentMenu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile'));
536
			}
E
Erich Gamma 已提交
537 538
		}

539
		if (folders.length || files.length) {
E
Erich Gamma 已提交
540
			openRecentMenu.append(__separator__());
541
			openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recent Files"), 'workbench.action.clearRecentFiles'));
E
Erich Gamma 已提交
542 543 544
		}
	}

545
	private createOpenRecentMenuItem(path: string, commandId: string): Electron.MenuItem {
546
		let label = path;
547
		if ((isMacintosh || isLinux) && path.indexOf(this.environmentService.userHome) === 0) {
548
			label = `~${path.substr(this.environmentService.userHome.length)}`;
549 550
		}

551
		return new MenuItem(this.likeAction(commandId, {
552
			label: this.unmnemonicLabel(label), click: (menuItem, win, event) => {
553
				const openInNewWindow = this.isOptionClick(event);
554
				const success = !!this.windowsService.open({ context: OpenContext.MENU, cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow });
B
Benjamin Pasero 已提交
555
				if (!success) {
556
					this.windowsService.removeFromRecentPathsList(path);
B
Benjamin Pasero 已提交
557
				}
E
Erich Gamma 已提交
558
			}
559
		}, false));
E
Erich Gamma 已提交
560 561
	}

J
Joao Moreno 已提交
562
	private isOptionClick(event: Electron.Event & Electron.Modifiers): boolean {
563
		return event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)));
564 565
	}

566
	private createRoleMenuItem(label: string, commandId: string, role: Electron.MenuItemRole): Electron.MenuItem {
B
Benjamin Pasero 已提交
567
		const options: Electron.MenuItemOptions = {
568
			label: this.mnemonicLabel(label),
B
Benjamin Pasero 已提交
569
			role,
570 571 572
			enabled: true
		};

573
		return new MenuItem(this.withKeybinding(commandId, options));
574 575
	}

B
Benjamin Pasero 已提交
576 577 578 579 580 581
	private setEditMenu(winLinuxEditMenu: Electron.Menu): void {
		let undo: Electron.MenuItem;
		let redo: Electron.MenuItem;
		let cut: Electron.MenuItem;
		let copy: Electron.MenuItem;
		let paste: Electron.MenuItem;
E
Erich Gamma 已提交
582

583
		if (isMacintosh) {
B
Benjamin Pasero 已提交
584 585
			undo = this.createDevToolsAwareMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo', devTools => devTools.undo());
			redo = this.createDevToolsAwareMenuItem(nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), 'redo', devTools => devTools.redo());
B
Benjamin Pasero 已提交
586 587
			cut = this.createRoleMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction', 'cut');
			copy = this.createRoleMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction', 'copy');
588
			paste = this.createRoleMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction', 'paste');
E
Erich Gamma 已提交
589
		} else {
B
Benjamin Pasero 已提交
590 591
			undo = this.createMenuItem(nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), 'undo');
			redo = this.createMenuItem(nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), 'redo');
B
Benjamin Pasero 已提交
592 593
			cut = this.createMenuItem(nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), 'editor.action.clipboardCutAction');
			copy = this.createMenuItem(nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), 'editor.action.clipboardCopyAction');
B
Benjamin Pasero 已提交
594
			paste = this.createMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction');
E
Erich Gamma 已提交
595 596
		}

B
Benjamin Pasero 已提交
597 598
		const find = this.createMenuItem(nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"), 'actions.find');
		const replace = this.createMenuItem(nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"), 'editor.action.startFindReplaceAction');
S
Sandeep Somavarapu 已提交
599
		const findInFiles = this.createMenuItem(nls.localize({ key: 'miFindInFiles', comment: ['&& denotes a mnemonic'] }, "Find &&in Files"), 'workbench.action.findInFiles');
B
Benjamin Pasero 已提交
600
		const replaceInFiles = this.createMenuItem(nls.localize({ key: 'miReplaceInFiles', comment: ['&& denotes a mnemonic'] }, "Replace &&in Files"), 'workbench.action.replaceInFiles');
E
Erich Gamma 已提交
601

602 603
		const emmetExpandAbbreviation = this.createMenuItem(nls.localize({ key: 'miEmmetExpandAbbreviation', comment: ['&& denotes a mnemonic'] }, "Emmet: E&&xpand Abbreviation"), 'editor.emmet.action.expandAbbreviation');
		const showEmmetCommands = this.createMenuItem(nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."), 'workbench.action.showEmmetCommands');
604 605
		const toggleLineComment = this.createMenuItem(nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment"), 'editor.action.commentLine');
		const toggleBlockComment = this.createMenuItem(nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment"), 'editor.action.blockComment');
606

E
Erich Gamma 已提交
607 608 609 610 611 612 613 614 615 616 617
		[
			undo,
			redo,
			__separator__(),
			cut,
			copy,
			paste,
			__separator__(),
			find,
			replace,
			__separator__(),
S
Sandeep Somavarapu 已提交
618
			findInFiles,
619 620
			replaceInFiles,
			__separator__(),
621 622
			toggleLineComment,
			toggleBlockComment,
623 624
			emmetExpandAbbreviation,
			showEmmetCommands
B
Benjamin Pasero 已提交
625
		].forEach(item => winLinuxEditMenu.append(item));
E
Erich Gamma 已提交
626 627
	}

C
Christof Marti 已提交
628
	private setSelectionMenu(winLinuxEditMenu: Electron.Menu): void {
629 630 631 632 633 634 635 636 637 638 639 640 641
		let multiCursorModifierLabel: string;
		if (this.currentMultiCursorModifierSetting === 'ctrlCmd') {
			// The default has been overwritten
			multiCursorModifierLabel = nls.localize('miMultiCursorAlt', "Use Alt+Click for Multi-Cursor");
		} else {
			multiCursorModifierLabel = (
				isMacintosh
					? nls.localize('miMultiCursorCmd', "Use Cmd+Click for Multi-Cursor")
					: nls.localize('miMultiCursorCtrl', "Use Ctrl+Click for Multi-Cursor")
			);
		}

		const multicursorModifier = this.createMenuItem(multiCursorModifierLabel, 'workbench.action.toggleMultiCursorModifier');
C
Christof Marti 已提交
642 643
		const insertCursorAbove = this.createMenuItem(nls.localize({ key: 'miInsertCursorAbove', comment: ['&& denotes a mnemonic'] }, "&&Add Cursor Above"), 'editor.action.insertCursorAbove');
		const insertCursorBelow = this.createMenuItem(nls.localize({ key: 'miInsertCursorBelow', comment: ['&& denotes a mnemonic'] }, "A&&dd Cursor Below"), 'editor.action.insertCursorBelow');
644
		const insertCursorAtEndOfEachLineSelected = this.createMenuItem(nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends"), 'editor.action.insertCursorAtEndOfEachLineSelected');
C
Christof Marti 已提交
645 646 647
		const addSelectionToNextFindMatch = this.createMenuItem(nls.localize({ key: 'miAddSelectionToNextFindMatch', comment: ['&& denotes a mnemonic'] }, "Add &&Next Occurrence"), 'editor.action.addSelectionToNextFindMatch');
		const addSelectionToPreviousFindMatch = this.createMenuItem(nls.localize({ key: 'miAddSelectionToPreviousFindMatch', comment: ['&& denotes a mnemonic'] }, "Add P&&revious Occurrence"), 'editor.action.addSelectionToPreviousFindMatch');
		const selectHighlights = this.createMenuItem(nls.localize({ key: 'miSelectHighlights', comment: ['&& denotes a mnemonic'] }, "Select All &&Occurrences"), 'editor.action.selectHighlights');
C
Christof Marti 已提交
648 649 650 651 652 653

		const copyLinesUp = this.createMenuItem(nls.localize({ key: 'miCopyLinesUp', comment: ['&& denotes a mnemonic'] }, "&&Copy Line Up"), 'editor.action.copyLinesUpAction');
		const copyLinesDown = this.createMenuItem(nls.localize({ key: 'miCopyLinesDown', comment: ['&& denotes a mnemonic'] }, "Co&&py Line Down"), 'editor.action.copyLinesDownAction');
		const moveLinesUp = this.createMenuItem(nls.localize({ key: 'miMoveLinesUp', comment: ['&& denotes a mnemonic'] }, "Mo&&ve Line Up"), 'editor.action.moveLinesUpAction');
		const moveLinesDown = this.createMenuItem(nls.localize({ key: 'miMoveLinesDown', comment: ['&& denotes a mnemonic'] }, "Move &&Line Down"), 'editor.action.moveLinesDownAction');

654
		let selectAll: Electron.MenuItem;
655
		if (isMacintosh) {
656 657 658 659
			selectAll = this.createDevToolsAwareMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll', (devTools) => devTools.selectAll());
		} else {
			selectAll = this.createMenuItem(nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), 'editor.action.selectAll');
		}
C
Christof Marti 已提交
660 661 662 663
		const smartSelectGrow = this.createMenuItem(nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection"), 'editor.action.smartSelect.grow');
		const smartSelectshrink = this.createMenuItem(nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection"), 'editor.action.smartSelect.shrink');

		[
664 665 666
			selectAll,
			smartSelectGrow,
			smartSelectshrink,
C
Christof Marti 已提交
667 668 669 670 671 672
			__separator__(),
			copyLinesUp,
			copyLinesDown,
			moveLinesUp,
			moveLinesDown,
			__separator__(),
673
			multicursorModifier,
674 675 676 677 678 679
			insertCursorAbove,
			insertCursorBelow,
			insertCursorAtEndOfEachLineSelected,
			addSelectionToNextFindMatch,
			addSelectionToPreviousFindMatch,
			selectHighlights,
C
Christof Marti 已提交
680 681 682
		].forEach(item => winLinuxEditMenu.append(item));
	}

B
Benjamin Pasero 已提交
683
	private setViewMenu(viewMenu: Electron.Menu): void {
B
Benjamin Pasero 已提交
684 685
		const explorer = this.createMenuItem(nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer"), 'workbench.view.explorer');
		const search = this.createMenuItem(nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search"), 'workbench.view.search');
J
Joao Moreno 已提交
686
		const scm = this.createMenuItem(nls.localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM"), 'workbench.view.scm');
B
Benjamin Pasero 已提交
687 688 689 690 691 692 693
		const debug = this.createMenuItem(nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug"), 'workbench.view.debug');
		const extensions = this.createMenuItem(nls.localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions"), 'workbench.view.extensions');
		const output = this.createMenuItem(nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output"), 'workbench.action.output.toggleOutput');
		const debugConsole = this.createMenuItem(nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console"), 'workbench.debug.action.toggleRepl');
		const integratedTerminal = this.createMenuItem(nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal"), 'workbench.action.terminal.toggleTerminal');
		const problems = this.createMenuItem(nls.localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems"), 'workbench.actions.view.problems');

694 695 696 697 698 699 700 701
		let additionalViewlets: Electron.MenuItem;
		if (this.extensionViewlets.length) {
			const additionalViewletsMenu = new Menu();

			this.extensionViewlets.forEach(viewlet => {
				additionalViewletsMenu.append(this.createMenuItem(viewlet.label, viewlet.id));
			});

702
			additionalViewlets = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAdditionalViews', comment: ['&& denotes a mnemonic'] }, "Additional &&Views")), submenu: additionalViewletsMenu, enabled: true });
703 704
		}

B
Benjamin Pasero 已提交
705 706
		const commands = this.createMenuItem(nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette..."), 'workbench.action.showCommands');

707
		const fullscreen = new MenuItem(this.withKeybinding('workbench.action.toggleFullScreen', { label: this.mnemonicLabel(nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen")), click: () => this.windowsService.getLastActiveWindow().toggleFullScreen(), enabled: this.windowsService.getWindowCount() > 0 }));
I
isidor 已提交
708
		const toggleZenMode = this.createMenuItem(nls.localize('miToggleZenMode', "Toggle Zen Mode"), 'workbench.action.toggleZenMode', this.windowsService.getWindowCount() > 0);
B
Benjamin Pasero 已提交
709 710
		const toggleMenuBar = this.createMenuItem(nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar"), 'workbench.action.toggleMenuBar');
		const splitEditor = this.createMenuItem(nls.localize({ key: 'miSplitEditor', comment: ['&& denotes a mnemonic'] }, "Split &&Editor"), 'workbench.action.splitEditor');
711
		const toggleEditorLayout = this.createMenuItem(nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Toggle Editor Group &&Layout"), 'workbench.action.toggleEditorGroupLayout');
B
Benjamin Pasero 已提交
712
		const toggleSidebar = this.createMenuItem(nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar"), 'workbench.action.toggleSidebarVisibility');
B
Benjamin Pasero 已提交
713 714 715 716 717 718 719 720 721 722

		let moveSideBarLabel: string;
		if (this.currentSidebarLocation !== 'right') {
			moveSideBarLabel = nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right");
		} else {
			moveSideBarLabel = nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left");
		}

		const moveSidebar = this.createMenuItem(moveSideBarLabel, 'workbench.action.toggleSidebarPosition');

B
Benjamin Pasero 已提交
723
		const togglePanel = this.createMenuItem(nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel"), 'workbench.action.togglePanel');
B
Benjamin Pasero 已提交
724 725 726 727 728 729 730 731

		let statusBarLabel: string;
		if (this.currentStatusbarVisible) {
			statusBarLabel = nls.localize({ key: 'miHideStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Hide Status Bar");
		} else {
			statusBarLabel = nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Show Status Bar");
		}
		const toggleStatusbar = this.createMenuItem(statusBarLabel, 'workbench.action.toggleStatusbarVisibility');
E
Erich Gamma 已提交
732

S
Sanders Lauture 已提交
733 734
		let activityBarLabel: string;
		if (this.currentActivityBarVisible) {
B
Benjamin Pasero 已提交
735
			activityBarLabel = nls.localize({ key: 'miHideActivityBar', comment: ['&& denotes a mnemonic'] }, "Hide &&Activity Bar");
S
Sanders Lauture 已提交
736
		} else {
B
Benjamin Pasero 已提交
737
			activityBarLabel = nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar");
S
Sanders Lauture 已提交
738 739 740
		}
		const toggleActivtyBar = this.createMenuItem(activityBarLabel, 'workbench.action.toggleActivityBarVisibility');

B
Benjamin Pasero 已提交
741
		const toggleWordWrap = this.createMenuItem(nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap"), 'editor.action.toggleWordWrap');
742
		const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace');
A
Alex Dima 已提交
743
		const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacter');
744

B
Benjamin Pasero 已提交
745 746 747
		const zoomIn = this.createMenuItem(nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In"), 'workbench.action.zoomIn');
		const zoomOut = this.createMenuItem(nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "Zoom O&&ut"), 'workbench.action.zoomOut');
		const resetZoom = this.createMenuItem(nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom"), 'workbench.action.zoomReset');
C
Chris Dias 已提交
748

B
Benjamin Pasero 已提交
749
		arrays.coalesce([
750 751
			commands,
			__separator__(),
752 753
			explorer,
			search,
J
Joao Moreno 已提交
754
			scm,
755
			debug,
J
Joao Moreno 已提交
756
			extensions,
757
			additionalViewlets,
758 759 760 761 762
			__separator__(),
			output,
			problems,
			debugConsole,
			integratedTerminal,
C
Chris Dias 已提交
763
			__separator__(),
E
Erich Gamma 已提交
764
			fullscreen,
I
isidor 已提交
765
			toggleZenMode,
766
			isWindows || isLinux ? toggleMenuBar : void 0,
E
Erich Gamma 已提交
767 768
			__separator__(),
			splitEditor,
769
			toggleEditorLayout,
E
Erich Gamma 已提交
770
			moveSidebar,
B
Benjamin Pasero 已提交
771 772 773
			toggleSidebar,
			togglePanel,
			toggleStatusbar,
S
Sanders Lauture 已提交
774
			toggleActivtyBar,
E
Erich Gamma 已提交
775
			__separator__(),
J
Joao Moreno 已提交
776
			toggleWordWrap,
777
			toggleRenderWhitespace,
778
			toggleRenderControlCharacters,
J
Joao Moreno 已提交
779
			__separator__(),
E
Erich Gamma 已提交
780
			zoomIn,
781 782
			zoomOut,
			resetZoom
B
Benjamin Pasero 已提交
783
		]).forEach(item => viewMenu.append(item));
E
Erich Gamma 已提交
784 785
	}

B
Benjamin Pasero 已提交
786
	private setGotoMenu(gotoMenu: Electron.Menu): void {
B
Benjamin Pasero 已提交
787 788
		const back = this.createMenuItem(nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"), 'workbench.action.navigateBack');
		const forward = this.createMenuItem(nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"), 'workbench.action.navigateForward');
B
Benjamin Pasero 已提交
789

B
Benjamin Pasero 已提交
790
		const switchEditorMenu = new Menu();
B
Benjamin Pasero 已提交
791

B
Benjamin Pasero 已提交
792 793 794 795
		const nextEditor = this.createMenuItem(nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor"), 'workbench.action.nextEditor');
		const previousEditor = this.createMenuItem(nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor"), 'workbench.action.previousEditor');
		const nextEditorInGroup = this.createMenuItem(nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group"), 'workbench.action.openNextRecentlyUsedEditorInGroup');
		const previousEditorInGroup = this.createMenuItem(nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group"), 'workbench.action.openPreviousRecentlyUsedEditorInGroup');
B
Benjamin Pasero 已提交
796 797 798 799 800

		[
			nextEditor,
			previousEditor,
			__separator__(),
801
			nextEditorInGroup,
B
Benjamin Pasero 已提交
802 803 804
			previousEditorInGroup
		].forEach(item => switchEditorMenu.append(item));

805
		const switchEditor = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor")), submenu: switchEditorMenu, enabled: true });
B
Benjamin Pasero 已提交
806

B
Benjamin Pasero 已提交
807
		const switchGroupMenu = new Menu();
B
Benjamin Pasero 已提交
808

809 810 811
		const focusFirstGroup = this.createMenuItem(nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "&&First Group"), 'workbench.action.focusFirstEditorGroup');
		const focusSecondGroup = this.createMenuItem(nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "&&Second Group"), 'workbench.action.focusSecondEditorGroup');
		const focusThirdGroup = this.createMenuItem(nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "&&Third Group"), 'workbench.action.focusThirdEditorGroup');
B
Benjamin Pasero 已提交
812 813
		const nextGroup = this.createMenuItem(nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group"), 'workbench.action.focusNextGroup');
		const previousGroup = this.createMenuItem(nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group"), 'workbench.action.focusPreviousGroup');
B
Benjamin Pasero 已提交
814 815 816 817 818 819 820 821 822 823

		[
			focusFirstGroup,
			focusSecondGroup,
			focusThirdGroup,
			__separator__(),
			nextGroup,
			previousGroup
		].forEach(item => switchGroupMenu.append(item));

824
		const switchGroup = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group")), submenu: switchGroupMenu, enabled: true });
B
Benjamin Pasero 已提交
825

B
Benjamin Pasero 已提交
826
		const gotoFile = this.createMenuItem(nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File..."), 'workbench.action.quickOpen');
827 828
		const gotoSymbolInFile = this.createMenuItem(nls.localize({ key: 'miGotoSymbolInFile', comment: ['&& denotes a mnemonic'] }, "Go to &&Symbol in File..."), 'workbench.action.gotoSymbol');
		const gotoSymbolInWorkspace = this.createMenuItem(nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace..."), 'workbench.action.showAllSymbols');
B
Benjamin Pasero 已提交
829
		const gotoDefinition = this.createMenuItem(nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition"), 'editor.action.goToDeclaration');
830 831
		const gotoTypeDefinition = this.createMenuItem(nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition"), 'editor.action.goToTypeDefinition');
		const goToImplementation = this.createMenuItem(nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation"), 'editor.action.goToImplementation');
B
Benjamin Pasero 已提交
832
		const gotoLine = this.createMenuItem(nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line..."), 'workbench.action.gotoLine');
E
Erich Gamma 已提交
833 834 835 836 837

		[
			back,
			forward,
			__separator__(),
B
Benjamin Pasero 已提交
838 839
			switchEditor,
			switchGroup,
E
Erich Gamma 已提交
840 841
			__separator__(),
			gotoFile,
842 843
			gotoSymbolInFile,
			gotoSymbolInWorkspace,
E
Erich Gamma 已提交
844
			gotoDefinition,
845 846
			gotoTypeDefinition,
			goToImplementation,
E
Erich Gamma 已提交
847
			gotoLine
B
Benjamin Pasero 已提交
848
		].forEach(item => gotoMenu.append(item));
E
Erich Gamma 已提交
849 850
	}

I
isidor 已提交
851 852 853 854 855 856
	private setDebugMenu(debugMenu: Electron.Menu): void {
		const start = this.createMenuItem(nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging"), 'workbench.action.debug.start');
		const startWithoutDebugging = this.createMenuItem(nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging"), 'workbench.action.debug.run');
		const stop = this.createMenuItem(nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), 'workbench.action.debug.stop');
		const restart = this.createMenuItem(nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), 'workbench.action.debug.restart');

I
isidor 已提交
857 858
		const openConfigurations = this.createMenuItem(nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations"), 'workbench.action.debug.configure');
		const addConfiguration = this.createMenuItem(nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "Add Configuration..."), 'debug.addConfiguration');
I
isidor 已提交
859 860 861 862 863 864 865 866 867 868 869

		const stepOver = this.createMenuItem(nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), 'workbench.action.debug.stepOver');
		const stepInto = this.createMenuItem(nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), 'workbench.action.debug.stepInto');
		const stepOut = this.createMenuItem(nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), 'workbench.action.debug.stepOut');
		const continueAction = this.createMenuItem(nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), 'workbench.action.debug.continue');

		const toggleBreakpoint = this.createMenuItem(nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint"), 'editor.debug.action.toggleBreakpoint');
		const breakpointsMenu = new Menu();
		breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint..."), 'editor.debug.action.conditionalBreakpoint'));
		breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miColumnBreakpoint', comment: ['&& denotes a mnemonic'] }, "C&&olumn Breakpoint"), 'editor.debug.action.toggleColumnBreakpoint'));
		breakpointsMenu.append(this.createMenuItem(nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint..."), 'workbench.debug.viewlet.action.addFunctionBreakpointAction'));
870
		const newBreakpoints = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint")), submenu: breakpointsMenu });
I
isidor 已提交
871
		const disableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints"), 'workbench.debug.viewlet.action.disableAllBreakpoints');
I
isidor 已提交
872
		const removeAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints"), 'workbench.debug.viewlet.action.removeAllBreakpoints');
I
isidor 已提交
873

I
isidor 已提交
874
		const installAdditionalDebuggers = this.createMenuItem(nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers..."), 'debug.installAdditionalDebuggers');
I
isidor 已提交
875 876 877 878 879 880
		[
			start,
			startWithoutDebugging,
			stop,
			restart,
			__separator__(),
I
isidor 已提交
881 882
			openConfigurations,
			addConfiguration,
I
isidor 已提交
883 884 885 886 887 888 889 890 891 892 893
			__separator__(),
			stepOver,
			stepInto,
			stepOut,
			continueAction,
			__separator__(),
			toggleBreakpoint,
			newBreakpoints,
			disableAllBreakpoints,
			removeAllBreakpoints,
			__separator__(),
I
isidor 已提交
894
			installAdditionalDebuggers
I
isidor 已提交
895 896 897 898
		].forEach(item => debugMenu.append(item));

	}

B
Benjamin Pasero 已提交
899
	private setMacWindowMenu(macWindowMenu: Electron.Menu): void {
B
Benjamin Pasero 已提交
900
		const minimize = new MenuItem({ label: nls.localize('mMinimize', "Minimize"), role: 'minimize', accelerator: 'Command+M', enabled: this.windowsService.getWindowCount() > 0 });
B
Benjamin Pasero 已提交
901
		const zoom = new MenuItem({ label: nls.localize('mZoom', "Zoom"), role: 'zoom', enabled: this.windowsService.getWindowCount() > 0 });
B
Benjamin Pasero 已提交
902
		const bringAllToFront = new MenuItem({ label: nls.localize('mBringToFront', "Bring All to Front"), role: 'front', enabled: this.windowsService.getWindowCount() > 0 });
B
Benjamin Pasero 已提交
903
		const switchWindow = this.createMenuItem(nls.localize({ key: 'miSwitchWindow', comment: ['&& denotes a mnemonic'] }, "Switch &&Window..."), 'workbench.action.switchWindow', this.windowsService.getWindowCount() > 0);
E
Erich Gamma 已提交
904 905 906

		[
			minimize,
B
Benjamin Pasero 已提交
907 908
			zoom,
			switchWindow,
E
Erich Gamma 已提交
909 910
			__separator__(),
			bringAllToFront
B
Benjamin Pasero 已提交
911
		].forEach(item => macWindowMenu.append(item));
E
Erich Gamma 已提交
912 913
	}

J
fix npe  
Joao Moreno 已提交
914
	private toggleDevTools(): void {
B
Benjamin Pasero 已提交
915
		const w = this.windowsService.getFocusedWindow();
J
fix npe  
Joao Moreno 已提交
916
		if (w && w.win) {
917 918 919 920 921 922
			const contents = w.win.webContents;
			if (w.hasHiddenTitleBarStyle() && !w.win.isFullScreen() && !contents.isDevToolsOpened()) {
				contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647
			} else {
				contents.toggleDevTools();
			}
J
fix npe  
Joao Moreno 已提交
923 924 925
		}
	}

B
Benjamin Pasero 已提交
926
	private setHelpMenu(helpMenu: Electron.Menu): void {
927
		const toggleDevToolsItem = new MenuItem(this.likeAction('workbench.action.toggleDevTools', {
928
			label: this.mnemonicLabel(nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")),
J
fix npe  
Joao Moreno 已提交
929
			click: () => this.toggleDevTools(),
B
Benjamin Pasero 已提交
930
			enabled: (this.windowsService.getWindowCount() > 0)
931
		}));
E
Erich Gamma 已提交
932

933
		const showAccessibilityOptions = new MenuItem(this.likeAction('accessibilityOptions', {
934
			label: this.mnemonicLabel(nls.localize({ key: 'miAccessibilityOptions', comment: ['&& denotes a mnemonic'] }, "Accessibility &&Options")),
935 936
			accelerator: null,
			click: () => {
937
				this.openAccessibilityOptions();
938
			}
939
		}, false));
940

B
Benjamin Pasero 已提交
941
		let reportIssuesItem: Electron.MenuItem = null;
B
Benjamin Pasero 已提交
942
		if (product.reportIssueUrl) {
B
Benjamin Pasero 已提交
943 944 945 946 947
			const label = nls.localize({ key: 'miReportIssues', comment: ['&& denotes a mnemonic'] }, "Report &&Issues");

			if (this.windowsService.getWindowCount() > 0) {
				reportIssuesItem = this.createMenuItem(label, 'workbench.action.reportIssues');
			} else {
948
				reportIssuesItem = new MenuItem({ label: this.mnemonicLabel(label), click: () => this.openUrl(product.reportIssueUrl, 'openReportIssues') });
B
Benjamin Pasero 已提交
949 950
			}
		}
J
Joao Moreno 已提交
951

952
		const keyboardShortcutsUrl = isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin;
E
Erich Gamma 已提交
953
		arrays.coalesce([
954
			new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage') }),
955
			new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "&&Interactive Playground")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showInteractivePlayground') }),
956 957
			product.documentationUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openDocumentationUrl') }) : null,
			product.releaseNotesUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'update.showCurrentReleaseNotes') }) : null,
958
			__separator__(),
959 960
			keyboardShortcutsUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.keybindingsReference') }) : null,
			product.introductoryVideosUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openIntroductoryVideosUrl') }) : null,
I
isidor 已提交
961
			(product.introductoryVideosUrl || keyboardShortcutsUrl) ? __separator__() : null,
962 963
			product.twitterUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter")), click: () => this.openUrl(product.twitterUrl, 'openTwitterUrl') }) : null,
			product.requestFeatureUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests")), click: () => this.openUrl(product.requestFeatureUrl, 'openUserVoiceUrl') }) : null,
B
Benjamin Pasero 已提交
964
			reportIssuesItem,
B
Benjamin Pasero 已提交
965 966
			(product.twitterUrl || product.requestFeatureUrl || product.reportIssueUrl) ? __separator__() : null,
			product.licenseUrl ? new MenuItem({
967
				label: this.mnemonicLabel(nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")), click: () => {
968
					if (language) {
B
Benjamin Pasero 已提交
969
						const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?';
970
						this.openUrl(`${product.licenseUrl}${queryArgChar}lang=${language}`, 'openLicenseUrl');
B
Benjamin Pasero 已提交
971
					} else {
B
Benjamin Pasero 已提交
972
						this.openUrl(product.licenseUrl, 'openLicenseUrl');
B
Benjamin Pasero 已提交
973
					}
974
				}
B
Benjamin Pasero 已提交
975
			}) : null,
B
Benjamin Pasero 已提交
976
			product.privacyStatementUrl ? new MenuItem({
977
				label: this.mnemonicLabel(nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement")), click: () => {
978
					if (language) {
B
Benjamin Pasero 已提交
979
						const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?';
980
						this.openUrl(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`, 'openPrivacyStatement');
981
					} else {
B
Benjamin Pasero 已提交
982
						this.openUrl(product.privacyStatementUrl, 'openPrivacyStatement');
983 984 985
					}
				}
			}) : null,
B
Benjamin Pasero 已提交
986
			(product.licenseUrl || product.privacyStatementUrl) ? __separator__() : null,
E
Erich Gamma 已提交
987
			toggleDevToolsItem,
988
			isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null
B
Benjamin Pasero 已提交
989
		]).forEach(item => helpMenu.append(item));
E
Erich Gamma 已提交
990

991
		if (!isMacintosh) {
E
Erich Gamma 已提交
992 993 994 995 996 997 998
			const updateMenuItems = this.getUpdateMenuItems();
			if (updateMenuItems.length) {
				helpMenu.append(__separator__());
				updateMenuItems.forEach(i => helpMenu.append(i));
			}

			helpMenu.append(__separator__());
999
			helpMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")), click: () => this.openAboutDialog() }));
E
Erich Gamma 已提交
1000 1001 1002
		}
	}

1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
	private openAccessibilityOptions(): void {
		let win = new BrowserWindow({
			alwaysOnTop: true,
			skipTaskbar: true,
			resizable: false,
			width: 450,
			height: 300,
			show: true,
			title: nls.localize('accessibilityOptionsWindowTitle', "Accessibility Options")
		});

		win.setMenuBarVisibility(false);

		win.loadURL('chrome://accessibility');
	}

B
Benjamin Pasero 已提交
1019
	private getUpdateMenuItems(): Electron.MenuItem[] {
B
Benjamin Pasero 已提交
1020
		switch (this.updateService.state) {
J
Joao Moreno 已提交
1021
			case UpdateState.Uninitialized:
E
Erich Gamma 已提交
1022 1023
				return [];

J
Joao Moreno 已提交
1024
			case UpdateState.UpdateDownloaded:
B
Benjamin Pasero 已提交
1025 1026
				return [new MenuItem({
					label: nls.localize('miRestartToUpdate', "Restart To Update..."), click: () => {
J
fix npe  
Joao Moreno 已提交
1027
						this.reportMenuActionTelemetry('RestartToUpdate');
J
Joao Moreno 已提交
1028
						this.updateService.quitAndInstall();
B
Benjamin Pasero 已提交
1029 1030
					}
				})];
E
Erich Gamma 已提交
1031

J
Joao Moreno 已提交
1032
			case UpdateState.CheckingForUpdate:
E
Erich Gamma 已提交
1033 1034
				return [new MenuItem({ label: nls.localize('miCheckingForUpdates', "Checking For Updates..."), enabled: false })];

J
Joao Moreno 已提交
1035
			case UpdateState.UpdateAvailable:
1036
				if (isLinux) {
J
Joao Moreno 已提交
1037
					return [new MenuItem({
J
Joao Moreno 已提交
1038
						label: nls.localize('miDownloadUpdate', "Download Available Update"), click: () => {
J
Joao Moreno 已提交
1039
							this.updateService.quitAndInstall();
J
Joao Moreno 已提交
1040 1041 1042 1043
						}
					})];
				}

1044
				const updateAvailableLabel = isWindows
E
Erich Gamma 已提交
1045 1046 1047 1048 1049 1050
					? nls.localize('miDownloadingUpdate', "Downloading Update...")
					: nls.localize('miInstallingUpdate', "Installing Update...");

				return [new MenuItem({ label: updateAvailableLabel, enabled: false })];

			default:
B
Benjamin Pasero 已提交
1051
				const result = [new MenuItem({
1052
					label: nls.localize('miCheckForUpdates', "Check for Updates..."), click: () => setTimeout(() => {
J
fix npe  
Joao Moreno 已提交
1053
						this.reportMenuActionTelemetry('CheckForUpdate');
J
Joao Moreno 已提交
1054
						this.updateService.checkForUpdates(true);
B
Benjamin Pasero 已提交
1055 1056
					}, 0)
				})];
E
Erich Gamma 已提交
1057 1058 1059 1060 1061

				return result;
		}
	}

1062
	private createMenuItem(label: string, commandId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem;
B
Benjamin Pasero 已提交
1063 1064
	private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem;
	private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem {
1065
		const label = this.mnemonicLabel(arg1);
1066
		const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem, win, event) => {
1067
			let commandId = arg2;
1068
			if (Array.isArray(arg2)) {
1069
				commandId = this.isOptionClick(event) ? arg2[1] : arg2[0]; // support alternative action if we got multiple action Ids and the option key was pressed while invoking
1070 1071
			}

1072
			this.windowsService.sendToFocused('vscode:runAction', commandId);
1073
		};
B
Benjamin Pasero 已提交
1074
		const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0;
B
Benjamin Pasero 已提交
1075
		const checked = typeof arg4 === 'boolean' ? arg4 : false;
E
Erich Gamma 已提交
1076

1077
		let commandId: string;
E
Erich Gamma 已提交
1078
		if (typeof arg2 === 'string') {
1079
			commandId = arg2;
E
Erich Gamma 已提交
1080 1081
		}

B
Benjamin Pasero 已提交
1082
		const options: Electron.MenuItemOptions = {
B
Benjamin Pasero 已提交
1083 1084 1085
			label,
			click,
			enabled
E
Erich Gamma 已提交
1086 1087
		};

B
Benjamin Pasero 已提交
1088 1089 1090 1091 1092
		if (checked) {
			options['type'] = 'checkbox';
			options['checked'] = checked;
		}

1093
		return new MenuItem(this.withKeybinding(commandId, options));
E
Erich Gamma 已提交
1094 1095
	}

1096 1097
	private createDevToolsAwareMenuItem(label: string, commandId: string, devToolsFocusedFn: (contents: Electron.WebContents) => void): Electron.MenuItem {
		return new MenuItem(this.withKeybinding(commandId, {
1098
			label: this.mnemonicLabel(label),
B
Benjamin Pasero 已提交
1099
			enabled: this.windowsService.getWindowCount() > 0,
1100
			click: () => {
B
Benjamin Pasero 已提交
1101
				const windowInFocus = this.windowsService.getFocusedWindow();
1102 1103 1104 1105
				if (!windowInFocus) {
					return;
				}

B
Benjamin Pasero 已提交
1106 1107
				if (windowInFocus.win.webContents.isDevToolsFocused()) {
					devToolsFocusedFn(windowInFocus.win.webContents.devToolsWebContents);
1108
				} else {
1109
					this.windowsService.sendToFocused('vscode:runAction', commandId);
1110 1111
				}
			}
1112
		}));
1113 1114
	}

1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
	private withKeybinding(commandId: string, options: Electron.MenuItemOptions): Electron.MenuItemOptions {
		const binding = this.keybindingsResolver.getKeybinding(commandId);

		// Apply binding if there is one
		if (binding && binding.label) {

			// if the binding is native, we can just apply it
			if (binding.isNative) {
				options.accelerator = binding.label;
			}

			// the keybinding is not native so we cannot show it as part of the accelerator of
			// the menu item. we fallback to a different strategy so that we always display it
			else {
B
Benjamin Pasero 已提交
1129
				const bindingIndex = options.label.indexOf('[');
1130
				if (bindingIndex >= 0) {
B
Benjamin Pasero 已提交
1131
					options.label = `${options.label.substr(0, bindingIndex)} [${binding.label}]`;
1132
				} else {
B
Benjamin Pasero 已提交
1133
					options.label = `${options.label} [${binding.label}]`;
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
				}
			}
		}

		// Unset bindings if there is none
		else {
			options.accelerator = void 0;
		}

		return options;
	}

	private likeAction(commandId: string, options: Electron.MenuItemOptions, setAccelerator = !options.accelerator): Electron.MenuItemOptions {
1147
		if (setAccelerator) {
1148
			options = this.withKeybinding(commandId, options);
1149
		}
1150

1151 1152
		const originalClick = options.click;
		options.click = (item, window, event) => {
1153
			this.reportMenuActionTelemetry(commandId);
1154 1155 1156 1157 1158
			if (originalClick) {
				originalClick(item, window, event);
			}
		};

1159
		return options;
E
Erich Gamma 已提交
1160 1161
	}

J
fix npe  
Joao Moreno 已提交
1162
	private openAboutDialog(): void {
B
Benjamin Pasero 已提交
1163
		const lastActiveWindow = this.windowsService.getFocusedWindow() || this.windowsService.getLastActiveWindow();
J
fix npe  
Joao Moreno 已提交
1164 1165

		dialog.showMessageBox(lastActiveWindow && lastActiveWindow.win, {
B
Benjamin Pasero 已提交
1166
			title: product.nameLong,
J
fix npe  
Joao Moreno 已提交
1167
			type: 'info',
B
Benjamin Pasero 已提交
1168
			message: product.nameLong,
J
fix npe  
Joao Moreno 已提交
1169 1170 1171
			detail: nls.localize('aboutDetail',
				"\nVersion {0}\nCommit {1}\nDate {2}\nShell {3}\nRenderer {4}\nNode {5}",
				app.getVersion(),
B
Benjamin Pasero 已提交
1172 1173
				product.commit || 'Unknown',
				product.date || 'Unknown',
J
fix npe  
Joao Moreno 已提交
1174 1175 1176 1177 1178 1179
				process.versions['electron'],
				process.versions['chrome'],
				process.versions['node']
			),
			buttons: [nls.localize('okButton', "OK")],
			noLink: true
B
Benjamin Pasero 已提交
1180
		}, result => null);
J
fix npe  
Joao Moreno 已提交
1181 1182 1183

		this.reportMenuActionTelemetry('showAboutDialog');
	}
E
Erich Gamma 已提交
1184

J
fix npe  
Joao Moreno 已提交
1185 1186 1187
	private openUrl(url: string, id: string): void {
		shell.openExternal(url);
		this.reportMenuActionTelemetry(id);
E
Erich Gamma 已提交
1188 1189
	}

J
fix npe  
Joao Moreno 已提交
1190
	private reportMenuActionTelemetry(id: string): void {
1191
		this.telemetryService.publicLog('workbenchActionExecuted', { id, from: telemetryFrom });
J
fix npe  
Joao Moreno 已提交
1192
	}
E
Erich Gamma 已提交
1193

1194 1195 1196 1197
	private mnemonicLabel(label: string): string {
		if (isMacintosh || !this.currentEnableMenuBarMnemonics) {
			return label.replace(/\(&&\w\)|&&/g, ''); // no mnemonic support on mac
		}
E
Erich Gamma 已提交
1198

1199
		return label.replace(/&&/g, '&');
E
Erich Gamma 已提交
1200 1201
	}

1202 1203 1204 1205
	private unmnemonicLabel(label: string): string {
		if (isMacintosh || !this.currentEnableMenuBarMnemonics) {
			return label; // no mnemonic support on mac
		}
1206

1207
		return label.replace(/&/g, '&&');
1208
	}
1209
}
1210

1211 1212
function __separator__(): Electron.MenuItem {
	return new MenuItem({ type: 'separator' });
1213
}