Files
vscode/src/vs/code/electron-main/menus.ts

1155 lines
59 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* 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 * as nls from 'vs/nls';
import { isMacintosh, isLinux, isWindows, language } from 'vs/base/common/platform';
import * as arrays from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem, BrowserWindow } from 'electron';
import { OpenContext } from 'vs/platform/windows/common/windows';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFilesConfiguration, AutoSaveConfiguration } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update';
import product from 'vs/platform/node/product';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { tildify } from 'vs/base/common/labels';
import { KeybindingsResolver } from "vs/code/electron-main/keyboard";
import { IWindowsMainService } from "vs/platform/windows/electron-main/windows";
import { IHistoryMainService } from "vs/platform/history/electron-main/historyMainService";
interface IExtensionViewlet {
id: string;
label: string;
}
interface IConfiguration extends IFilesConfiguration {
window: {
enableMenuBarMnemonics: boolean;
};
workbench: {
sideBar: {
location: 'left' | 'right';
},
statusBar: {
visible: boolean;
},
activityBar: {
visible: boolean;
}
};
editor: {
multiCursorModifier: 'ctrlCmd' | 'alt'
};
}
const telemetryFrom = 'menu';
export class CodeMenu {
private static MAX_MENU_RECENT_ENTRIES = 10;
private currentAutoSaveSetting: string;
private currentMultiCursorModifierSetting: string;
private currentSidebarLocation: 'left' | 'right';
private currentStatusbarVisible: boolean;
private currentActivityBarVisible: boolean;
private currentEnableMenuBarMnemonics: boolean;
private isQuitting: boolean;
private appMenuInstalled: boolean;
private menuUpdater: RunOnceScheduler;
private keybindingsResolver: KeybindingsResolver;
private extensionViewlets: IExtensionViewlet[];
constructor(
@IUpdateService private updateService: IUpdateService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService private configurationService: IConfigurationService,
@IWindowsMainService private windowsService: IWindowsMainService,
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService,
@IHistoryMainService private historyService: IHistoryMainService
) {
this.extensionViewlets = [];
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver);
this.onConfigurationUpdated(this.configurationService.getConfiguration<IConfiguration>());
this.install();
this.registerListeners();
}
private registerListeners(): void {
// Keep flag when app quits
app.on('will-quit', () => {
this.isQuitting = true;
});
// Listen to some events from window service
this.windowsService.onPathsOpen(paths => this.updateMenu());
this.historyService.onRecentPathsChange(paths => this.updateMenu());
this.windowsService.onWindowClose(_ => this.onClose(this.windowsService.getWindowCount()));
// 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();
}
});
// Update when auto save config changes
this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationUpdated(this.configurationService.getConfiguration<IConfiguration>(), true /* update menu if changed */));
// Listen to update service
this.updateService.onStateChange(() => this.updateMenu());
// Listen to keybindings change
this.keybindingsResolver.onKeybindingsChanged(() => this.updateMenu());
}
private onConfigurationUpdated(config: IConfiguration, handleMenu?: boolean): void {
let updateMenu = false;
const newAutoSaveSetting = config && config.files && config.files.autoSave;
if (newAutoSaveSetting !== this.currentAutoSaveSetting) {
this.currentAutoSaveSetting = newAutoSaveSetting;
updateMenu = true;
}
const newMultiCursorModifierSetting = config && config.editor && config.editor.multiCursorModifier;
if (newMultiCursorModifierSetting !== this.currentMultiCursorModifierSetting) {
this.currentMultiCursorModifierSetting = newMultiCursorModifierSetting;
updateMenu = true;
}
const newSidebarLocation = config && config.workbench && config.workbench.sideBar && config.workbench.sideBar.location || 'left';
if (newSidebarLocation !== this.currentSidebarLocation) {
this.currentSidebarLocation = newSidebarLocation;
updateMenu = true;
}
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;
}
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;
}
let newEnableMenuBarMnemonics = config && config.window && config.window.enableMenuBarMnemonics;
if (typeof newEnableMenuBarMnemonics !== 'boolean') {
newEnableMenuBarMnemonics = true;
}
if (newEnableMenuBarMnemonics !== this.currentEnableMenuBarMnemonics) {
this.currentEnableMenuBarMnemonics = newEnableMenuBarMnemonics;
updateMenu = true;
}
if (handleMenu && updateMenu) {
this.updateMenu();
}
}
private updateMenu(): void {
this.menuUpdater.schedule(); // buffer multiple attempts to update the menu
}
private doUpdateMenu(): void {
// 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.
// See also https://github.com/electron/electron/issues/846
//
// 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 */);
}
}
private onClose(remainingWindowCount: number): void {
if (remainingWindowCount === 0 && isMacintosh) {
this.updateMenu();
}
}
private install(): void {
// Menus
const menubar = new Menu();
// Mac: Application
let macApplicationMenuItem: Electron.MenuItem;
if (isMacintosh) {
const applicationMenu = new Menu();
macApplicationMenuItem = new MenuItem({ label: product.nameShort, submenu: applicationMenu });
this.setMacApplicationMenu(applicationMenu);
}
// File
const fileMenu = new Menu();
const fileMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mFile', comment: ['&& denotes a mnemonic'] }, "&&File")), submenu: fileMenu });
this.setFileMenu(fileMenu);
// Edit
const editMenu = new Menu();
const editMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mEdit', comment: ['&& denotes a mnemonic'] }, "&&Edit")), submenu: editMenu });
this.setEditMenu(editMenu);
// Selection
const selectionMenu = new Menu();
const selectionMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu });
this.setSelectionMenu(selectionMenu);
// View
const viewMenu = new Menu();
const viewMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mView', comment: ['&& denotes a mnemonic'] }, "&&View")), submenu: viewMenu });
this.setViewMenu(viewMenu);
// Goto
const gotoMenu = new Menu();
const gotoMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mGoto', comment: ['&& denotes a mnemonic'] }, "&&Go")), submenu: gotoMenu });
this.setGotoMenu(gotoMenu);
// Debug
const debugMenu = new Menu();
const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu });
this.setDebugMenu(debugMenu);
// Mac: Window
let macWindowMenuItem: Electron.MenuItem;
if (isMacintosh) {
const windowMenu = new Menu();
macWindowMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize('mWindow', "Window")), submenu: windowMenu, role: 'window' });
this.setMacWindowMenu(windowMenu);
}
// Help
const helpMenu = new Menu();
const helpMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help")), submenu: helpMenu, role: 'help' });
this.setHelpMenu(helpMenu);
// Tasks
const taskMenu = new Menu();
const taskMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTask', comment: ['&& denotes a mnemonic'] }, "&&Tasks")), submenu: taskMenu });
this.setTaskMenu(taskMenu);
// Menu Structure
if (macApplicationMenuItem) {
menubar.append(macApplicationMenuItem);
}
menubar.append(fileMenuItem);
menubar.append(editMenuItem);
menubar.append(selectionMenuItem);
menubar.append(viewMenuItem);
menubar.append(gotoMenuItem);
menubar.append(debugMenuItem);
menubar.append(taskMenuItem);
if (macWindowMenuItem) {
menubar.append(macWindowMenuItem);
}
menubar.append(helpMenuItem);
Menu.setApplicationMenu(menubar);
// Dock Menu
if (isMacintosh && !this.appMenuInstalled) {
this.appMenuInstalled = true;
const dockMenu = new Menu();
dockMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window")), click: () => this.windowsService.openNewWindow(OpenContext.DOCK) }));
app.dock.setMenu(dockMenu);
}
}
private setMacApplicationMenu(macApplicationMenu: Electron.Menu): void {
const about = new MenuItem({ label: nls.localize('mAbout', "About {0}", product.nameLong), role: 'about' });
const checkForUpdates = this.getUpdateMenuItems();
const preferences = this.getPreferencesMenu();
const servicesMenu = new Menu();
const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu });
const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' });
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' });
const quit = new MenuItem(this.likeAction('workbench.action.quit', { label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => this.windowsService.quit() }));
const actions = [about];
actions.push(...checkForUpdates);
actions.push(...[
__separator__(),
preferences,
__separator__(),
services,
__separator__(),
hide,
hideOthers,
showAll,
__separator__(),
quit
]);
actions.forEach(i => macApplicationMenu.append(i));
}
private setFileMenu(fileMenu: Electron.Menu): void {
const hasNoWindows = (this.windowsService.getWindowCount() === 0);
let newFile: Electron.MenuItem;
if (hasNoWindows) {
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) }));
} else {
newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile');
}
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.pickFileFolderAndOpen(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.pickFolderAndOpen(this.isOptionClick(event), undefined, { from: telemetryFrom }) }));
let openFile: Electron.MenuItem;
if (hasNoWindows) {
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.pickFileAndOpen(this.isOptionClick(event), undefined, undefined, { from: telemetryFrom }) }));
} else {
openFile = this.createMenuItem(nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File..."), ['workbench.action.files.openFile', 'workbench.action.files.openFileInNewWindow']);
}
const openRecentMenu = new Menu();
this.setOpenRecentMenu(openRecentMenu);
const openRecent = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent")), submenu: openRecentMenu, enabled: openRecentMenu.items.length > 0 });
const isMultiRootEnabled = (product.quality !== 'stable'); // TODO@Ben multi root
const addFolder = this.createMenuItem(nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Add Folder to Workspace..."), 'workbench.action.addRootFolder', this.windowsService.getWindowCount() > 0);
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);
const autoSaveEnabled = [AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE].some(s => this.currentAutoSaveSetting === s);
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));
const preferences = this.getPreferencesMenu();
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) }));
const revertFile = this.createMenuItem(nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), 'workbench.action.files.revert', this.windowsService.getWindowCount() > 0);
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 }));
const closeFolder = this.createMenuItem(nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), 'workbench.action.closeFolder');
const closeEditor = this.createMenuItem(nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor"), 'workbench.action.closeActiveEditor');
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() }));
arrays.coalesce([
newFile,
newWindow,
__separator__(),
isMacintosh ? open : null,
!isMacintosh ? openFile : null,
!isMacintosh ? openFolder : null,
openRecent,
isMultiRootEnabled ? __separator__() : null,
isMultiRootEnabled ? addFolder : null,
__separator__(),
saveFile,
saveFileAs,
saveAllFiles,
__separator__(),
autoSave,
__separator__(),
!isMacintosh ? preferences : null,
!isMacintosh ? __separator__() : null,
revertFile,
closeEditor,
closeFolder,
closeWindow,
!isMacintosh ? __separator__() : null,
!isMacintosh ? exit : null
]).forEach(item => fileMenu.append(item));
}
private getPreferencesMenu(): Electron.MenuItem {
const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openGlobalSettings');
const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings');
const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions');
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();
preferencesMenu.append(settings);
preferencesMenu.append(__separator__());
preferencesMenu.append(kebindingSettings);
preferencesMenu.append(keymapExtensions);
preferencesMenu.append(__separator__());
preferencesMenu.append(snippetsSettings);
preferencesMenu.append(__separator__());
preferencesMenu.append(colorThemeSelection);
preferencesMenu.append(iconThemeSelection);
return new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu });
}
private setOpenRecentMenu(openRecentMenu: Electron.Menu): void {
openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor'));
const { folders, files } = this.historyService.getRecentPathsList();
// Folders
if (folders.length > 0) {
openRecentMenu.append(__separator__());
for (let i = 0; i < CodeMenu.MAX_MENU_RECENT_ENTRIES && i < folders.length; i++) {
openRecentMenu.append(this.createOpenRecentMenuItem(folders[i], 'openRecentFolder'));
}
}
// Files
if (files.length > 0) {
openRecentMenu.append(__separator__());
for (let i = 0; i < CodeMenu.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) {
openRecentMenu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile'));
}
}
if (folders.length || files.length) {
openRecentMenu.append(__separator__());
openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent'));
openRecentMenu.append(__separator__());
openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recent Files"), 'workbench.action.clearRecentFiles'));
}
}
private createOpenRecentMenuItem(path: string, commandId: string): Electron.MenuItem {
return new MenuItem(this.likeAction(commandId, {
label: this.unmnemonicLabel(tildify(path, this.environmentService.userHome)), click: (menuItem, win, event) => {
const openInNewWindow = this.isOptionClick(event);
const success = this.windowsService.open({ context: OpenContext.MENU, cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow }).length > 0;
if (!success) {
this.historyService.removeFromRecentPathsList(path);
}
}
}, false));
}
private isOptionClick(event: Electron.Event & Electron.Modifiers): boolean {
return event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)));
}
private createRoleMenuItem(label: string, commandId: string, role: Electron.MenuItemRole): Electron.MenuItem {
const options: Electron.MenuItemOptions = {
label: this.mnemonicLabel(label),
role,
enabled: true
};
return new MenuItem(this.withKeybinding(commandId, options));
}
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;
if (isMacintosh) {
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());
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');
paste = this.createRoleMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction', 'paste');
} else {
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');
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');
paste = this.createMenuItem(nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), 'editor.action.clipboardPasteAction');
}
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');
const findInFiles = this.createMenuItem(nls.localize({ key: 'miFindInFiles', comment: ['&& denotes a mnemonic'] }, "Find &&in Files"), 'workbench.action.findInFiles');
const replaceInFiles = this.createMenuItem(nls.localize({ key: 'miReplaceInFiles', comment: ['&& denotes a mnemonic'] }, "Replace &&in Files"), 'workbench.action.replaceInFiles');
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');
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');
[
undo,
redo,
__separator__(),
cut,
copy,
paste,
__separator__(),
find,
replace,
__separator__(),
findInFiles,
replaceInFiles,
__separator__(),
toggleLineComment,
toggleBlockComment,
emmetExpandAbbreviation,
showEmmetCommands
].forEach(item => winLinuxEditMenu.append(item));
}
private setSelectionMenu(winLinuxEditMenu: Electron.Menu): void {
let multiCursorModifierLabel: string;
if (this.currentMultiCursorModifierSetting === 'ctrlCmd') {
// The default has been overwritten
multiCursorModifierLabel = nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor");
} else {
multiCursorModifierLabel = (
isMacintosh
? nls.localize('miMultiCursorCmd', "Switch to Cmd+Click for Multi-Cursor")
: nls.localize('miMultiCursorCtrl', "Switch to Ctrl+Click for Multi-Cursor")
);
}
const multicursorModifier = this.createMenuItem(multiCursorModifierLabel, 'workbench.action.toggleMultiCursorModifier');
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');
const insertCursorAtEndOfEachLineSelected = this.createMenuItem(nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends"), 'editor.action.insertCursorAtEndOfEachLineSelected');
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');
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');
let selectAll: Electron.MenuItem;
if (isMacintosh) {
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');
}
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');
[
selectAll,
smartSelectGrow,
smartSelectshrink,
__separator__(),
copyLinesUp,
copyLinesDown,
moveLinesUp,
moveLinesDown,
__separator__(),
multicursorModifier,
insertCursorAbove,
insertCursorBelow,
insertCursorAtEndOfEachLineSelected,
addSelectionToNextFindMatch,
addSelectionToPreviousFindMatch,
selectHighlights,
].forEach(item => winLinuxEditMenu.append(item));
}
private setViewMenu(viewMenu: Electron.Menu): void {
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');
const scm = this.createMenuItem(nls.localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM"), 'workbench.view.scm');
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');
let additionalViewlets: Electron.MenuItem;
if (this.extensionViewlets.length) {
const additionalViewletsMenu = new Menu();
this.extensionViewlets.forEach(viewlet => {
additionalViewletsMenu.append(this.createMenuItem(viewlet.label, viewlet.id));
});
additionalViewlets = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAdditionalViews', comment: ['&& denotes a mnemonic'] }, "Additional &&Views")), submenu: additionalViewletsMenu, enabled: true });
}
const commands = this.createMenuItem(nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette..."), 'workbench.action.showCommands');
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 }));
const toggleZenMode = this.createMenuItem(nls.localize('miToggleZenMode', "Toggle Zen Mode"), 'workbench.action.toggleZenMode', this.windowsService.getWindowCount() > 0);
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');
const toggleEditorLayout = this.createMenuItem(nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Toggle Editor Group &&Layout"), 'workbench.action.toggleEditorGroupLayout');
const toggleSidebar = this.createMenuItem(nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar"), 'workbench.action.toggleSidebarVisibility');
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');
const togglePanel = this.createMenuItem(nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel"), 'workbench.action.togglePanel');
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');
let activityBarLabel: string;
if (this.currentActivityBarVisible) {
activityBarLabel = nls.localize({ key: 'miHideActivityBar', comment: ['&& denotes a mnemonic'] }, "Hide &&Activity Bar");
} else {
activityBarLabel = nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar");
}
const toggleActivtyBar = this.createMenuItem(activityBarLabel, 'workbench.action.toggleActivityBarVisibility');
const toggleWordWrap = this.createMenuItem(nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap"), 'editor.action.toggleWordWrap');
const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace');
const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacter');
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');
arrays.coalesce([
commands,
__separator__(),
explorer,
search,
scm,
debug,
extensions,
additionalViewlets,
__separator__(),
output,
problems,
debugConsole,
integratedTerminal,
__separator__(),
fullscreen,
toggleZenMode,
isWindows || isLinux ? toggleMenuBar : void 0,
__separator__(),
splitEditor,
toggleEditorLayout,
moveSidebar,
toggleSidebar,
togglePanel,
toggleStatusbar,
toggleActivtyBar,
__separator__(),
toggleWordWrap,
toggleRenderWhitespace,
toggleRenderControlCharacters,
__separator__(),
zoomIn,
zoomOut,
resetZoom
]).forEach(item => viewMenu.append(item));
}
private setGotoMenu(gotoMenu: Electron.Menu): void {
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');
const switchEditorMenu = new Menu();
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');
[
nextEditor,
previousEditor,
__separator__(),
nextEditorInGroup,
previousEditorInGroup
].forEach(item => switchEditorMenu.append(item));
const switchEditor = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor")), submenu: switchEditorMenu, enabled: true });
const switchGroupMenu = new Menu();
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');
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');
[
focusFirstGroup,
focusSecondGroup,
focusThirdGroup,
__separator__(),
nextGroup,
previousGroup
].forEach(item => switchGroupMenu.append(item));
const switchGroup = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group")), submenu: switchGroupMenu, enabled: true });
const gotoFile = this.createMenuItem(nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File..."), 'workbench.action.quickOpen');
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');
const gotoDefinition = this.createMenuItem(nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition"), 'editor.action.goToDeclaration');
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');
const gotoLine = this.createMenuItem(nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line..."), 'workbench.action.gotoLine');
[
back,
forward,
__separator__(),
switchEditor,
switchGroup,
__separator__(),
gotoFile,
gotoSymbolInFile,
gotoSymbolInWorkspace,
gotoDefinition,
gotoTypeDefinition,
goToImplementation,
gotoLine
].forEach(item => gotoMenu.append(item));
}
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');
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');
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'));
const newBreakpoints = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint")), submenu: breakpointsMenu });
const enableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints"), 'workbench.debug.viewlet.action.enableAllBreakpoints');
const disableAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints"), 'workbench.debug.viewlet.action.disableAllBreakpoints');
const removeAllBreakpoints = this.createMenuItem(nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints"), 'workbench.debug.viewlet.action.removeAllBreakpoints');
const installAdditionalDebuggers = this.createMenuItem(nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers..."), 'debug.installAdditionalDebuggers');
[
start,
startWithoutDebugging,
stop,
restart,
__separator__(),
openConfigurations,
addConfiguration,
__separator__(),
stepOver,
stepInto,
stepOut,
continueAction,
__separator__(),
toggleBreakpoint,
newBreakpoints,
enableAllBreakpoints,
disableAllBreakpoints,
removeAllBreakpoints,
__separator__(),
installAdditionalDebuggers
].forEach(item => debugMenu.append(item));
}
private setMacWindowMenu(macWindowMenu: Electron.Menu): void {
const minimize = new MenuItem({ label: nls.localize('mMinimize', "Minimize"), role: 'minimize', accelerator: 'Command+M', enabled: this.windowsService.getWindowCount() > 0 });
const zoom = new MenuItem({ label: nls.localize('mZoom', "Zoom"), role: 'zoom', enabled: this.windowsService.getWindowCount() > 0 });
const bringAllToFront = new MenuItem({ label: nls.localize('mBringToFront', "Bring All to Front"), role: 'front', enabled: this.windowsService.getWindowCount() > 0 });
const switchWindow = this.createMenuItem(nls.localize({ key: 'miSwitchWindow', comment: ['&& denotes a mnemonic'] }, "Switch &&Window..."), 'workbench.action.switchWindow', this.windowsService.getWindowCount() > 0);
[
minimize,
zoom,
switchWindow,
__separator__(),
bringAllToFront
].forEach(item => macWindowMenu.append(item));
}
private toggleDevTools(): void {
const w = this.windowsService.getFocusedWindow();
if (w && w.win) {
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();
}
}
}
private setHelpMenu(helpMenu: Electron.Menu): void {
const toggleDevToolsItem = new MenuItem(this.likeAction('workbench.action.toggleDevTools', {
label: this.mnemonicLabel(nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools")),
click: () => this.toggleDevTools(),
enabled: (this.windowsService.getWindowCount() > 0)
}));
const showAccessibilityOptions = new MenuItem(this.likeAction('accessibilityOptions', {
label: this.mnemonicLabel(nls.localize({ key: 'miAccessibilityOptions', comment: ['&& denotes a mnemonic'] }, "Accessibility &&Options")),
accelerator: null,
click: () => {
this.openAccessibilityOptions();
}
}, false));
let reportIssuesItem: Electron.MenuItem = null;
if (product.reportIssueUrl) {
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 {
reportIssuesItem = new MenuItem({ label: this.mnemonicLabel(label), click: () => this.openUrl(product.reportIssueUrl, 'openReportIssues') });
}
}
const keyboardShortcutsUrl = isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin;
arrays.coalesce([
new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showWelcomePage') }),
new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "&&Interactive Playground")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.showInteractivePlayground') }),
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,
__separator__(),
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,
product.tipsAndTricksUrl ? new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "&&Tips and Tricks")), click: () => this.windowsService.sendToFocused('vscode:runAction', 'workbench.action.openTipsAndTricksUrl') }) : null,
(product.introductoryVideosUrl || keyboardShortcutsUrl) ? __separator__() : null,
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,
reportIssuesItem,
(product.twitterUrl || product.requestFeatureUrl || product.reportIssueUrl) ? __separator__() : null,
product.licenseUrl ? new MenuItem({
label: this.mnemonicLabel(nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License")), click: () => {
if (language) {
const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?';
this.openUrl(`${product.licenseUrl}${queryArgChar}lang=${language}`, 'openLicenseUrl');
} else {
this.openUrl(product.licenseUrl, 'openLicenseUrl');
}
}
}) : null,
product.privacyStatementUrl ? new MenuItem({
label: this.mnemonicLabel(nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement")), click: () => {
if (language) {
const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?';
this.openUrl(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`, 'openPrivacyStatement');
} else {
this.openUrl(product.privacyStatementUrl, 'openPrivacyStatement');
}
}
}) : null,
(product.licenseUrl || product.privacyStatementUrl) ? __separator__() : null,
toggleDevToolsItem,
isWindows && product.quality !== 'stable' ? showAccessibilityOptions : null
]).forEach(item => helpMenu.append(item));
if (!isMacintosh) {
const updateMenuItems = this.getUpdateMenuItems();
if (updateMenuItems.length) {
helpMenu.append(__separator__());
updateMenuItems.forEach(i => helpMenu.append(i));
}
helpMenu.append(__separator__());
helpMenu.append(new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About")), click: () => this.openAboutDialog() }));
}
}
private setTaskMenu(taskMenu: Electron.Menu): void {
const runTask = this.createMenuItem(nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task..."), 'workbench.action.tasks.runTask');
const buildTask = this.createMenuItem(nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task..."), 'workbench.action.tasks.build');
const showTasks = this.createMenuItem(nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks..."), 'workbench.action.tasks.showTasks');
const restartTask = this.createMenuItem(nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task..."), 'workbench.action.tasks.restartTask');
const terminateTask = this.createMenuItem(nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task..."), 'workbench.action.tasks.terminate');
// const testTask = this.createMenuItem(nls.localize({ key: 'miTestTask', comment: ['&& denotes a mnemonic'] }, "Run Test T&&ask..."), 'workbench.action.tasks.test');
// const showTaskLog = this.createMenuItem(nls.localize({ key: 'miShowTaskLog', comment: ['&& denotes a mnemonic'] }, "&&Show Task Log"), 'workbench.action.tasks.showLog');
const configureTask = this.createMenuItem(nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks"), 'workbench.action.tasks.configureTaskRunner');
const configureBuildTask = this.createMenuItem(nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task"), 'workbench.action.tasks.configureDefaultBuildTask');
// const configureTestTask = this.createMenuItem(nls.localize({ key: 'miConfigureTestTask', comment: ['&& denotes a mnemonic'] }, "Configure Defau&&lt Test Task"), 'workbench.action.tasks.configureDefaultTestTask');
[
//__separator__(),
runTask,
buildTask,
// testTask,
__separator__(),
terminateTask,
restartTask,
showTasks,
__separator__(),
//showTaskLog,
configureTask,
configureBuildTask
// configureTestTask
].forEach(item => taskMenu.append(item));
}
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');
}
private getUpdateMenuItems(): Electron.MenuItem[] {
switch (this.updateService.state) {
case UpdateState.Uninitialized:
return [];
case UpdateState.UpdateDownloaded:
return [new MenuItem({
label: nls.localize('miRestartToUpdate', "Restart to Update..."), click: () => {
this.reportMenuActionTelemetry('RestartToUpdate');
this.updateService.quitAndInstall();
}
})];
case UpdateState.CheckingForUpdate:
return [new MenuItem({ label: nls.localize('miCheckingForUpdates', "Checking For Updates..."), enabled: false })];
case UpdateState.UpdateAvailable:
if (isLinux) {
return [new MenuItem({
label: nls.localize('miDownloadUpdate', "Download Available Update"), click: () => {
this.updateService.quitAndInstall();
}
})];
}
const updateAvailableLabel = isWindows
? nls.localize('miDownloadingUpdate', "Downloading Update...")
: nls.localize('miInstallingUpdate', "Installing Update...");
return [new MenuItem({ label: updateAvailableLabel, enabled: false })];
default:
const result = [new MenuItem({
label: nls.localize('miCheckForUpdates', "Check for Updates..."), click: () => setTimeout(() => {
this.reportMenuActionTelemetry('CheckForUpdate');
this.updateService.checkForUpdates(true);
}, 0)
})];
return result;
}
}
private createMenuItem(label: string, commandId: string | string[], enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(label: string, click: () => void, enabled?: boolean, checked?: boolean): Electron.MenuItem;
private createMenuItem(arg1: string, arg2: any, arg3?: boolean, arg4?: boolean): Electron.MenuItem {
const label = this.mnemonicLabel(arg1);
const click: () => void = (typeof arg2 === 'function') ? arg2 : (menuItem, win, event) => {
let commandId = arg2;
if (Array.isArray(arg2)) {
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
}
this.windowsService.sendToFocused('vscode:runAction', commandId);
};
const enabled = typeof arg3 === 'boolean' ? arg3 : this.windowsService.getWindowCount() > 0;
const checked = typeof arg4 === 'boolean' ? arg4 : false;
let commandId: string;
if (typeof arg2 === 'string') {
commandId = arg2;
}
const options: Electron.MenuItemOptions = {
label,
click,
enabled
};
if (checked) {
options['type'] = 'checkbox';
options['checked'] = checked;
}
return new MenuItem(this.withKeybinding(commandId, options));
}
private createDevToolsAwareMenuItem(label: string, commandId: string, devToolsFocusedFn: (contents: Electron.WebContents) => void): Electron.MenuItem {
return new MenuItem(this.withKeybinding(commandId, {
label: this.mnemonicLabel(label),
enabled: this.windowsService.getWindowCount() > 0,
click: () => {
const windowInFocus = this.windowsService.getFocusedWindow();
if (!windowInFocus) {
return;
}
if (windowInFocus.win.webContents.isDevToolsFocused()) {
devToolsFocusedFn(windowInFocus.win.webContents.devToolsWebContents);
} else {
this.windowsService.sendToFocused('vscode:runAction', commandId);
}
}
}));
}
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 {
const bindingIndex = options.label.indexOf('[');
if (bindingIndex >= 0) {
options.label = `${options.label.substr(0, bindingIndex)} [${binding.label}]`;
} else {
options.label = `${options.label} [${binding.label}]`;
}
}
}
// 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 {
if (setAccelerator) {
options = this.withKeybinding(commandId, options);
}
const originalClick = options.click;
options.click = (item, window, event) => {
this.reportMenuActionTelemetry(commandId);
if (originalClick) {
originalClick(item, window, event);
}
};
return options;
}
private openAboutDialog(): void {
const lastActiveWindow = this.windowsService.getFocusedWindow() || this.windowsService.getLastActiveWindow();
dialog.showMessageBox(lastActiveWindow && lastActiveWindow.win, {
title: product.nameLong,
type: 'info',
message: product.nameLong,
detail: nls.localize('aboutDetail',
"\nVersion {0}\nCommit {1}\nDate {2}\nShell {3}\nRenderer {4}\nNode {5}\nArchitecture {6}",
app.getVersion(),
product.commit || 'Unknown',
product.date || 'Unknown',
process.versions['electron'],
process.versions['chrome'],
process.versions['node'],
process.arch
),
buttons: [nls.localize('okButton', "OK")],
noLink: true
}, result => null);
this.reportMenuActionTelemetry('showAboutDialog');
}
private openUrl(url: string, id: string): void {
shell.openExternal(url);
this.reportMenuActionTelemetry(id);
}
private reportMenuActionTelemetry(id: string): void {
this.telemetryService.publicLog('workbenchActionExecuted', { id, from: telemetryFrom });
}
private mnemonicLabel(label: string): string {
if (isMacintosh || !this.currentEnableMenuBarMnemonics) {
return label.replace(/\(&&\w\)|&&/g, ''); // no mnemonic support on mac
}
return label.replace(/&&/g, '&');
}
private unmnemonicLabel(label: string): string {
if (isMacintosh || !this.currentEnableMenuBarMnemonics) {
return label; // no mnemonic support on mac
}
return label.replace(/&/g, '&&');
}
}
function __separator__(): Electron.MenuItem {
return new MenuItem({ type: 'separator' });
}