@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
1,133 lines (1,044 loc) • 123 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2017 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/* eslint-disable max-len, @typescript-eslint/indent */
import debounce = require('lodash.debounce');
import { injectable, inject, optional } from 'inversify';
import { MAIN_MENU_BAR, MANAGE_MENU, MenuContribution, MenuModelRegistry, ACCOUNTS_MENU } from '../common/menu';
import { KeybindingContribution, KeybindingRegistry } from './keybinding';
import { FrontendApplication } from './frontend-application';
import { FrontendApplicationContribution, OnWillStopAction } from './frontend-application-contribution';
import { CommandContribution, CommandRegistry, Command } from '../common/command';
import { UriAwareCommandHandler } from '../common/uri-command-handler';
import { SelectionService } from '../common/selection-service';
import { MessageService } from '../common/message-service';
import { OpenerService, open } from '../browser/opener-service';
import { ApplicationShell } from './shell/application-shell';
import { SHELL_TABBAR_CONTEXT_CLOSE, SHELL_TABBAR_CONTEXT_COPY, SHELL_TABBAR_CONTEXT_PIN, SHELL_TABBAR_CONTEXT_SPLIT } from './shell/tab-bars';
import { AboutDialog } from './about-dialog';
import * as browser from './browser';
import URI from '../common/uri';
import { ContextKey, ContextKeyService } from './context-key-service';
import { OS, isOSX, isWindows, EOL } from '../common/os';
import { ResourceContextKey } from './resource-context-key';
import { UriSelection } from '../common/selection';
import { StorageService } from './storage-service';
import { Navigatable, NavigatableWidget } from './navigatable';
import { QuickViewService } from './quick-input/quick-view-service';
import { environment } from '@theia/application-package/lib/environment';
import { IconTheme, IconThemeService } from './icon-theme-service';
import { ColorContribution } from './color-application-contribution';
import { ColorRegistry } from './color-registry';
import { Color } from '../common/color';
import { CoreConfiguration, CorePreferences } from './core-preferences';
import { ThemeService } from './theming';
import { PreferenceService, PreferenceChangeEvent, PreferenceScope } from './preferences';
import { ClipboardService } from './clipboard-service';
import { EncodingRegistry } from './encoding-registry';
import { UTF8 } from '../common/encodings';
import { EnvVariablesServer } from '../common/env-variables';
import { AuthenticationService } from './authentication-service';
import { FormatType, Saveable, SaveOptions } from './saveable';
import { QuickInputService, QuickPickItem, QuickPickItemOrSeparator, QuickPickSeparator } from './quick-input';
import { AsyncLocalizationProvider } from '../common/i18n/localization';
import { nls } from '../common/nls';
import { CurrentWidgetCommandAdapter } from './shell/current-widget-command-adapter';
import { ConfirmDialog, confirmExitWithOrWithoutSaving, Dialog } from './dialogs';
import { WindowService } from './window/window-service';
import { FrontendApplicationConfigProvider } from './frontend-application-config-provider';
import { DecorationStyle } from './decoration-style';
import { isPinned, Title, togglePinned, Widget } from './widgets';
import { SaveResourceService } from './save-resource-service';
import { UserWorkingDirectoryProvider } from './user-working-directory-provider';
import { UNTITLED_SCHEME, UntitledResourceResolver } from '../common';
import { LanguageQuickPickService } from './i18n/language-quick-pick-service';
export namespace CommonMenus {
export const FILE = [...MAIN_MENU_BAR, '1_file'];
export const FILE_NEW_TEXT = [...FILE, '1_new_text'];
export const FILE_NEW = [...FILE, '1_new'];
export const FILE_OPEN = [...FILE, '2_open'];
export const FILE_SAVE = [...FILE, '3_save'];
export const FILE_AUTOSAVE = [...FILE, '4_autosave'];
export const FILE_SETTINGS = [...FILE, '5_settings'];
export const FILE_SETTINGS_SUBMENU = [...FILE_SETTINGS, '1_settings_submenu'];
export const FILE_SETTINGS_SUBMENU_OPEN = [...FILE_SETTINGS_SUBMENU, '1_settings_submenu_open'];
export const FILE_SETTINGS_SUBMENU_THEME = [...FILE_SETTINGS_SUBMENU, '2_settings_submenu_theme'];
export const FILE_CLOSE = [...FILE, '6_close'];
export const FILE_NEW_CONTRIBUTIONS = 'file/newFile';
export const EDIT = [...MAIN_MENU_BAR, '2_edit'];
export const EDIT_UNDO = [...EDIT, '1_undo'];
export const EDIT_CLIPBOARD = [...EDIT, '2_clipboard'];
export const EDIT_FIND = [...EDIT, '3_find'];
export const VIEW = [...MAIN_MENU_BAR, '4_view'];
export const VIEW_PRIMARY = [...VIEW, '0_primary'];
export const VIEW_APPEARANCE = [...VIEW, '1_appearance'];
export const VIEW_APPEARANCE_SUBMENU = [...VIEW_APPEARANCE, '1_appearance_submenu'];
export const VIEW_APPEARANCE_SUBMENU_SCREEN = [...VIEW_APPEARANCE_SUBMENU, '2_appearance_submenu_screen'];
export const VIEW_APPEARANCE_SUBMENU_BAR = [...VIEW_APPEARANCE_SUBMENU, '3_appearance_submenu_bar'];
export const VIEW_EDITOR_SUBMENU = [...VIEW_APPEARANCE, '2_editor_submenu'];
export const VIEW_EDITOR_SUBMENU_SPLIT = [...VIEW_EDITOR_SUBMENU, '1_editor_submenu_split'];
export const VIEW_EDITOR_SUBMENU_ORTHO = [...VIEW_EDITOR_SUBMENU, '2_editor_submenu_ortho'];
export const VIEW_VIEWS = [...VIEW, '2_views'];
export const VIEW_LAYOUT = [...VIEW, '3_layout'];
export const VIEW_TOGGLE = [...VIEW, '4_toggle'];
export const MANAGE_GENERAL = [...MANAGE_MENU, '1_manage_general'];
export const MANAGE_SETTINGS = [...MANAGE_MENU, '2_manage_settings'];
export const MANAGE_SETTINGS_THEMES = [...MANAGE_SETTINGS, '1_manage_settings_themes'];
// last menu item
export const HELP = [...MAIN_MENU_BAR, '9_help'];
}
export namespace CommonCommands {
export const FILE_CATEGORY = 'File';
export const VIEW_CATEGORY = 'View';
export const CREATE_CATEGORY = 'Create';
export const PREFERENCES_CATEGORY = 'Preferences';
export const MANAGE_CATEGORY = 'Manage';
export const FILE_CATEGORY_KEY = nls.getDefaultKey(FILE_CATEGORY);
export const VIEW_CATEGORY_KEY = nls.getDefaultKey(VIEW_CATEGORY);
export const PREFERENCES_CATEGORY_KEY = nls.getDefaultKey(PREFERENCES_CATEGORY);
export const OPEN: Command = {
id: 'core.open',
};
export const CUT = Command.toDefaultLocalizedCommand({
id: 'core.cut',
label: 'Cut'
});
export const COPY = Command.toDefaultLocalizedCommand({
id: 'core.copy',
label: 'Copy'
});
export const PASTE = Command.toDefaultLocalizedCommand({
id: 'core.paste',
label: 'Paste'
});
export const COPY_PATH = Command.toDefaultLocalizedCommand({
id: 'core.copy.path',
label: 'Copy Path'
});
export const UNDO = Command.toDefaultLocalizedCommand({
id: 'core.undo',
label: 'Undo'
});
export const REDO = Command.toDefaultLocalizedCommand({
id: 'core.redo',
label: 'Redo'
});
export const SELECT_ALL = Command.toDefaultLocalizedCommand({
id: 'core.selectAll',
label: 'Select All'
});
export const FIND = Command.toDefaultLocalizedCommand({
id: 'core.find',
label: 'Find'
});
export const REPLACE = Command.toDefaultLocalizedCommand({
id: 'core.replace',
label: 'Replace'
});
export const NEXT_TAB = Command.toDefaultLocalizedCommand({
id: 'core.nextTab',
category: VIEW_CATEGORY,
label: 'Show Next Tab'
});
export const PREVIOUS_TAB = Command.toDefaultLocalizedCommand({
id: 'core.previousTab',
category: VIEW_CATEGORY,
label: 'Show Previous Tab'
});
export const NEXT_TAB_IN_GROUP = Command.toLocalizedCommand({
id: 'core.nextTabInGroup',
category: VIEW_CATEGORY,
label: 'Switch to Next Tab in Group'
}, 'theia/core/common/showNextTabInGroup', VIEW_CATEGORY_KEY);
export const PREVIOUS_TAB_IN_GROUP = Command.toLocalizedCommand({
id: 'core.previousTabInGroup',
category: VIEW_CATEGORY,
label: 'Switch to Previous Tab in Group'
}, 'theia/core/common/showPreviousTabInGroup', VIEW_CATEGORY_KEY);
export const NEXT_TAB_GROUP = Command.toLocalizedCommand({
id: 'core.nextTabGroup',
category: VIEW_CATEGORY,
label: 'Switch to Next Tab Group'
}, 'theia/core/common/showNextTabGroup', VIEW_CATEGORY_KEY);
export const PREVIOUS_TAB_GROUP = Command.toLocalizedCommand({
id: 'core.previousTabBar',
category: VIEW_CATEGORY,
label: 'Switch to Previous Tab Group'
}, 'theia/core/common/showPreviousTabGroup', VIEW_CATEGORY_KEY);
export const CLOSE_TAB = Command.toLocalizedCommand({
id: 'core.close.tab',
category: VIEW_CATEGORY,
label: 'Close Tab'
}, 'theia/core/common/closeTab', VIEW_CATEGORY_KEY);
export const CLOSE_OTHER_TABS = Command.toLocalizedCommand({
id: 'core.close.other.tabs',
category: VIEW_CATEGORY,
label: 'Close Other Tabs'
}, 'theia/core/common/closeOthers', VIEW_CATEGORY_KEY);
export const CLOSE_SAVED_TABS = Command.toDefaultLocalizedCommand({
id: 'workbench.action.closeUnmodifiedEditors',
category: VIEW_CATEGORY,
label: 'Close Saved Editors in Group',
});
export const CLOSE_RIGHT_TABS = Command.toLocalizedCommand({
id: 'core.close.right.tabs',
category: VIEW_CATEGORY,
label: 'Close Tabs to the Right'
}, 'theia/core/common/closeRight', VIEW_CATEGORY_KEY);
export const CLOSE_ALL_TABS = Command.toLocalizedCommand({
id: 'core.close.all.tabs',
category: VIEW_CATEGORY,
label: 'Close All Tabs'
}, 'theia/core/common/closeAll', VIEW_CATEGORY_KEY);
export const CLOSE_MAIN_TAB = Command.toLocalizedCommand({
id: 'core.close.main.tab',
category: VIEW_CATEGORY,
label: 'Close Tab in Main Area'
}, 'theia/core/common/closeTabMain', VIEW_CATEGORY_KEY);
export const CLOSE_OTHER_MAIN_TABS = Command.toLocalizedCommand({
id: 'core.close.other.main.tabs',
category: VIEW_CATEGORY,
label: 'Close Other Tabs in Main Area'
}, 'theia/core/common/closeOtherTabMain', VIEW_CATEGORY_KEY);
export const CLOSE_ALL_MAIN_TABS = Command.toLocalizedCommand({
id: 'core.close.all.main.tabs',
category: VIEW_CATEGORY,
label: 'Close All Tabs in Main Area'
}, 'theia/core/common/closeAllTabMain', VIEW_CATEGORY_KEY);
export const COLLAPSE_PANEL = Command.toLocalizedCommand({
id: 'core.collapse.tab',
category: VIEW_CATEGORY,
label: 'Collapse Side Panel'
}, 'theia/core/common/collapseTab', VIEW_CATEGORY_KEY);
export const COLLAPSE_ALL_PANELS = Command.toLocalizedCommand({
id: 'core.collapse.all.tabs',
category: VIEW_CATEGORY,
label: 'Collapse All Side Panels'
}, 'theia/core/common/collapseAllTabs', VIEW_CATEGORY_KEY);
export const TOGGLE_BOTTOM_PANEL = Command.toLocalizedCommand({
id: 'core.toggle.bottom.panel',
category: VIEW_CATEGORY,
label: 'Toggle Bottom Panel'
}, 'theia/core/common/collapseBottomPanel', VIEW_CATEGORY_KEY);
export const TOGGLE_STATUS_BAR = Command.toDefaultLocalizedCommand({
id: 'workbench.action.toggleStatusbarVisibility',
category: VIEW_CATEGORY,
label: 'Toggle Status Bar Visibility'
});
export const PIN_TAB = Command.toDefaultLocalizedCommand({
id: 'workbench.action.pinEditor',
category: VIEW_CATEGORY,
label: 'Pin Editor'
});
export const UNPIN_TAB = Command.toDefaultLocalizedCommand({
id: 'workbench.action.unpinEditor',
category: VIEW_CATEGORY,
label: 'Unpin Editor'
});
export const TOGGLE_MAXIMIZED = Command.toLocalizedCommand({
id: 'core.toggleMaximized',
category: VIEW_CATEGORY,
label: 'Toggle Maximized'
}, 'theia/core/common/toggleMaximized', VIEW_CATEGORY_KEY);
export const OPEN_VIEW = Command.toDefaultLocalizedCommand({
id: 'core.openView',
category: VIEW_CATEGORY,
label: 'Open View...'
});
export const SHOW_MENU_BAR = Command.toDefaultLocalizedCommand({
id: 'window.menuBarVisibility',
category: VIEW_CATEGORY,
label: 'Toggle Menu Bar'
});
export const NEW_UNTITLED_TEXT_FILE = Command.toDefaultLocalizedCommand({
id: 'workbench.action.files.newUntitledTextFile',
category: FILE_CATEGORY,
label: 'New Untitled Text File'
});
export const NEW_UNTITLED_FILE = Command.toDefaultLocalizedCommand({
id: 'workbench.action.files.newUntitledFile',
category: CREATE_CATEGORY,
label: 'New File...'
});
export const SAVE = Command.toDefaultLocalizedCommand({
id: 'core.save',
category: FILE_CATEGORY,
label: 'Save',
});
export const SAVE_AS = Command.toDefaultLocalizedCommand({
id: 'file.saveAs',
category: FILE_CATEGORY,
label: 'Save As...',
});
export const SAVE_WITHOUT_FORMATTING = Command.toDefaultLocalizedCommand({
id: 'core.saveWithoutFormatting',
category: FILE_CATEGORY,
label: 'Save without Formatting',
});
export const SAVE_ALL = Command.toDefaultLocalizedCommand({
id: 'core.saveAll',
category: FILE_CATEGORY,
label: 'Save All',
});
export const AUTO_SAVE = Command.toDefaultLocalizedCommand({
id: 'textEditor.commands.autosave',
category: FILE_CATEGORY,
label: 'Auto Save',
});
export const ABOUT_COMMAND = Command.toDefaultLocalizedCommand({
id: 'core.about',
label: 'About'
});
export const OPEN_PREFERENCES = Command.toDefaultLocalizedCommand({
id: 'preferences:open',
category: PREFERENCES_CATEGORY,
label: 'Open Settings (UI)',
});
export const SELECT_COLOR_THEME = Command.toDefaultLocalizedCommand({
id: 'workbench.action.selectTheme',
label: 'Color Theme',
category: PREFERENCES_CATEGORY
});
export const SELECT_ICON_THEME = Command.toDefaultLocalizedCommand({
id: 'workbench.action.selectIconTheme',
label: 'File Icon Theme',
category: PREFERENCES_CATEGORY
});
export const CONFIGURE_DISPLAY_LANGUAGE = Command.toDefaultLocalizedCommand({
id: 'workbench.action.configureLanguage',
label: 'Configure Display Language'
});
export const TOGGLE_BREADCRUMBS = Command.toDefaultLocalizedCommand({
id: 'breadcrumbs.toggle',
label: 'Toggle Breadcrumbs',
category: VIEW_CATEGORY
});
}
export const supportCut = browser.isNative || document.queryCommandSupported('cut');
export const supportCopy = browser.isNative || document.queryCommandSupported('copy');
// Chrome incorrectly returns true for document.queryCommandSupported('paste')
// when the paste feature is available but the calling script has insufficient
// privileges to actually perform the action
export const supportPaste = browser.isNative || (!browser.isChrome && document.queryCommandSupported('paste'));
export const RECENT_COMMANDS_STORAGE_KEY = 'commands';
@injectable()
export class CommonFrontendContribution implements FrontendApplicationContribution, MenuContribution, CommandContribution, KeybindingContribution, ColorContribution {
protected commonDecorationsStyleSheet: CSSStyleSheet = DecorationStyle.createStyleSheet('coreCommonDecorationsStyle');
constructor(
@inject(ApplicationShell) protected readonly shell: ApplicationShell,
@inject(SelectionService) protected readonly selectionService: SelectionService,
@inject(MessageService) protected readonly messageService: MessageService,
@inject(OpenerService) protected readonly openerService: OpenerService,
@inject(AboutDialog) protected readonly aboutDialog: AboutDialog,
@inject(AsyncLocalizationProvider) protected readonly localizationProvider: AsyncLocalizationProvider,
@inject(SaveResourceService) protected readonly saveResourceService: SaveResourceService,
) { }
@inject(ContextKeyService)
protected readonly contextKeyService: ContextKeyService;
@inject(ResourceContextKey)
protected readonly resourceContextKey: ResourceContextKey;
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;
@inject(MenuModelRegistry)
protected readonly menuRegistry: MenuModelRegistry;
@inject(StorageService)
protected readonly storageService: StorageService;
@inject(QuickInputService) @optional()
protected readonly quickInputService: QuickInputService;
@inject(IconThemeService)
protected readonly iconThemes: IconThemeService;
@inject(ThemeService)
protected readonly themeService: ThemeService;
@inject(CorePreferences)
protected readonly preferences: CorePreferences;
@inject(PreferenceService)
protected readonly preferenceService: PreferenceService;
@inject(ClipboardService)
protected readonly clipboardService: ClipboardService;
@inject(EncodingRegistry)
protected readonly encodingRegistry: EncodingRegistry;
@inject(EnvVariablesServer)
protected readonly environments: EnvVariablesServer;
@inject(AuthenticationService)
protected readonly authenticationService: AuthenticationService;
@inject(WindowService)
protected readonly windowService: WindowService;
@inject(UserWorkingDirectoryProvider)
protected readonly workingDirProvider: UserWorkingDirectoryProvider;
@inject(LanguageQuickPickService)
protected readonly languageQuickPickService: LanguageQuickPickService;
@inject(UntitledResourceResolver)
protected readonly untitledResourceResolver: UntitledResourceResolver;
protected pinnedKey: ContextKey<boolean>;
async configure(app: FrontendApplication): Promise<void> {
// FIXME: This request blocks valuable startup time (~200ms).
const configDirUri = await this.environments.getConfigDirUri();
// Global settings
this.encodingRegistry.registerOverride({
encoding: UTF8,
parent: new URI(configDirUri)
});
this.contextKeyService.createKey<boolean>('isLinux', OS.type() === OS.Type.Linux);
this.contextKeyService.createKey<boolean>('isMac', OS.type() === OS.Type.OSX);
this.contextKeyService.createKey<boolean>('isWindows', OS.type() === OS.Type.Windows);
this.contextKeyService.createKey<boolean>('isWeb', !this.isElectron());
this.pinnedKey = this.contextKeyService.createKey<boolean>('activeEditorIsPinned', false);
this.updatePinnedKey();
this.shell.onDidChangeActiveWidget(() => this.updatePinnedKey());
this.initResourceContextKeys();
this.registerCtrlWHandling();
this.updateStyles();
this.preferences.ready.then(() => this.setSashProperties());
this.preferences.onPreferenceChanged(e => this.handlePreferenceChange(e, app));
app.shell.leftPanelHandler.addBottomMenu({
id: 'settings-menu',
iconClass: 'codicon codicon-settings-gear',
title: nls.localizeByDefault(CommonCommands.MANAGE_CATEGORY),
menuPath: MANAGE_MENU,
order: 1,
});
const accountsMenu = {
id: 'accounts-menu',
iconClass: 'codicon codicon-person',
title: nls.localizeByDefault('Accounts'),
menuPath: ACCOUNTS_MENU,
order: 0,
};
this.authenticationService.onDidRegisterAuthenticationProvider(() => {
app.shell.leftPanelHandler.addBottomMenu(accountsMenu);
});
this.authenticationService.onDidUnregisterAuthenticationProvider(() => {
if (this.authenticationService.getProviderIds().length === 0) {
app.shell.leftPanelHandler.removeBottomMenu(accountsMenu.id);
}
});
}
protected updateStyles(): void {
document.body.classList.remove('theia-editor-highlightModifiedTabs');
if (this.preferences['workbench.editor.highlightModifiedTabs']) {
document.body.classList.add('theia-editor-highlightModifiedTabs');
}
}
protected updatePinnedKey(): void {
const activeTab = this.shell.findTabBar();
const pinningTarget = activeTab && this.shell.findTitle(activeTab);
const value = pinningTarget && isPinned(pinningTarget);
this.pinnedKey.set(value);
}
protected handlePreferenceChange(e: PreferenceChangeEvent<CoreConfiguration>, app: FrontendApplication): void {
switch (e.preferenceName) {
case 'workbench.editor.highlightModifiedTabs': {
this.updateStyles();
break;
}
case 'window.menuBarVisibility': {
const { newValue } = e;
const mainMenuId = 'main-menu';
if (newValue === 'compact') {
this.shell.leftPanelHandler.addTopMenu({
id: mainMenuId,
iconClass: 'codicon codicon-menu',
title: nls.localizeByDefault('Application Menu'),
menuPath: MAIN_MENU_BAR,
order: 0,
});
} else {
app.shell.leftPanelHandler.removeTopMenu(mainMenuId);
}
break;
}
case 'workbench.sash.hoverDelay':
case 'workbench.sash.size': {
this.setSashProperties();
break;
}
}
}
protected setSashProperties(): void {
const sashRule = `:root {
--theia-sash-hoverDelay: ${this.preferences['workbench.sash.hoverDelay']}ms;
--theia-sash-width: ${this.preferences['workbench.sash.size']}px;
}`;
DecorationStyle.deleteStyleRule(':root', this.commonDecorationsStyleSheet);
this.commonDecorationsStyleSheet.insertRule(sashRule);
}
onStart(): void {
this.storageService.getData<{ recent: Command[] }>(RECENT_COMMANDS_STORAGE_KEY, { recent: [] })
.then(tasks => this.commandRegistry.recent = tasks.recent);
}
onStop(): void {
const recent = this.commandRegistry.recent;
this.storageService.setData<{ recent: Command[] }>(RECENT_COMMANDS_STORAGE_KEY, { recent });
window.localStorage.setItem(IconThemeService.STORAGE_KEY, this.iconThemes.current);
window.localStorage.setItem(ThemeService.STORAGE_KEY, this.themeService.getCurrentTheme().id);
}
protected initResourceContextKeys(): void {
const updateContextKeys = () => {
const selection = this.selectionService.selection;
const resourceUri = Navigatable.is(selection) && selection.getResourceUri() || UriSelection.getUri(selection);
this.resourceContextKey.set(resourceUri);
};
updateContextKeys();
this.selectionService.onSelectionChanged(updateContextKeys);
}
registerMenus(registry: MenuModelRegistry): void {
registry.registerSubmenu(CommonMenus.FILE, nls.localizeByDefault('File'));
registry.registerSubmenu(CommonMenus.EDIT, nls.localizeByDefault('Edit'));
registry.registerSubmenu(CommonMenus.VIEW, nls.localizeByDefault('View'));
registry.registerSubmenu(CommonMenus.HELP, nls.localizeByDefault('Help'));
// For plugins contributing create new file commands/menu-actions
registry.registerIndependentSubmenu(CommonMenus.FILE_NEW_CONTRIBUTIONS, nls.localizeByDefault('New File...'));
registry.registerMenuAction(CommonMenus.FILE_SAVE, {
commandId: CommonCommands.SAVE.id
});
registry.registerMenuAction(CommonMenus.FILE_SAVE, {
commandId: CommonCommands.SAVE_ALL.id
});
registry.registerMenuAction(CommonMenus.FILE_AUTOSAVE, {
commandId: CommonCommands.AUTO_SAVE.id
});
registry.registerSubmenu(CommonMenus.FILE_SETTINGS_SUBMENU, nls.localizeByDefault(CommonCommands.PREFERENCES_CATEGORY));
registry.registerMenuAction(CommonMenus.EDIT_UNDO, {
commandId: CommonCommands.UNDO.id,
order: '0'
});
registry.registerMenuAction(CommonMenus.EDIT_UNDO, {
commandId: CommonCommands.REDO.id,
order: '1'
});
registry.registerMenuAction(CommonMenus.EDIT_FIND, {
commandId: CommonCommands.FIND.id,
order: '0'
});
registry.registerMenuAction(CommonMenus.EDIT_FIND, {
commandId: CommonCommands.REPLACE.id,
order: '1'
});
registry.registerMenuAction(CommonMenus.EDIT_CLIPBOARD, {
commandId: CommonCommands.CUT.id,
order: '0'
});
registry.registerMenuAction(CommonMenus.EDIT_CLIPBOARD, {
commandId: CommonCommands.COPY.id,
order: '1'
});
registry.registerMenuAction(CommonMenus.EDIT_CLIPBOARD, {
commandId: CommonCommands.PASTE.id,
order: '2'
});
registry.registerMenuAction(CommonMenus.EDIT_CLIPBOARD, {
commandId: CommonCommands.COPY_PATH.id,
order: '3'
});
registry.registerMenuAction(CommonMenus.VIEW_APPEARANCE_SUBMENU_BAR, {
commandId: CommonCommands.TOGGLE_BOTTOM_PANEL.id,
order: '1'
});
registry.registerMenuAction(CommonMenus.VIEW_APPEARANCE_SUBMENU_BAR, {
commandId: CommonCommands.TOGGLE_STATUS_BAR.id,
order: '2',
label: nls.localizeByDefault('Toggle Status Bar Visibility')
});
registry.registerMenuAction(CommonMenus.VIEW_APPEARANCE_SUBMENU_BAR, {
commandId: CommonCommands.COLLAPSE_ALL_PANELS.id,
order: '3'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
commandId: CommonCommands.CLOSE_TAB.id,
label: nls.localizeByDefault('Close'),
order: '0'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
commandId: CommonCommands.CLOSE_OTHER_TABS.id,
label: nls.localizeByDefault('Close Others'),
order: '1'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
commandId: CommonCommands.CLOSE_RIGHT_TABS.id,
label: nls.localizeByDefault('Close to the Right'),
order: '2'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
commandId: CommonCommands.CLOSE_SAVED_TABS.id,
label: nls.localizeByDefault('Close Saved'),
order: '3',
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_CLOSE, {
commandId: CommonCommands.CLOSE_ALL_TABS.id,
label: nls.localizeByDefault('Close All'),
order: '4'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
commandId: CommonCommands.COLLAPSE_PANEL.id,
label: CommonCommands.COLLAPSE_PANEL.label,
order: '5'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_SPLIT, {
commandId: CommonCommands.TOGGLE_MAXIMIZED.id,
label: CommonCommands.TOGGLE_MAXIMIZED.label,
order: '6'
});
registry.registerMenuAction(CommonMenus.VIEW_APPEARANCE_SUBMENU_SCREEN, {
commandId: CommonCommands.TOGGLE_MAXIMIZED.id,
label: CommonCommands.TOGGLE_MAXIMIZED.label,
order: '6'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_COPY, {
commandId: CommonCommands.COPY_PATH.id,
label: CommonCommands.COPY_PATH.label,
order: '1',
});
registry.registerMenuAction(CommonMenus.VIEW_APPEARANCE_SUBMENU_BAR, {
commandId: CommonCommands.SHOW_MENU_BAR.id,
label: nls.localizeByDefault('Toggle Menu Bar'),
order: '0'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_PIN, {
commandId: CommonCommands.PIN_TAB.id,
label: nls.localizeByDefault('Pin'),
order: '7'
});
registry.registerMenuAction(SHELL_TABBAR_CONTEXT_PIN, {
commandId: CommonCommands.UNPIN_TAB.id,
label: nls.localizeByDefault('Unpin'),
order: '8'
});
registry.registerMenuAction(CommonMenus.HELP, {
commandId: CommonCommands.ABOUT_COMMAND.id,
label: CommonCommands.ABOUT_COMMAND.label,
order: '9'
});
registry.registerMenuAction(CommonMenus.VIEW_PRIMARY, {
commandId: CommonCommands.OPEN_VIEW.id
});
registry.registerMenuAction(CommonMenus.FILE_SETTINGS_SUBMENU_THEME, {
commandId: CommonCommands.SELECT_COLOR_THEME.id
});
registry.registerMenuAction(CommonMenus.FILE_SETTINGS_SUBMENU_THEME, {
commandId: CommonCommands.SELECT_ICON_THEME.id
});
registry.registerSubmenu(CommonMenus.MANAGE_SETTINGS_THEMES, nls.localizeByDefault('Themes'), { order: 'a50' });
registry.registerMenuAction(CommonMenus.MANAGE_SETTINGS_THEMES, {
commandId: CommonCommands.SELECT_COLOR_THEME.id,
order: '0'
});
registry.registerMenuAction(CommonMenus.MANAGE_SETTINGS_THEMES, {
commandId: CommonCommands.SELECT_ICON_THEME.id,
order: '1'
});
registry.registerSubmenu(CommonMenus.VIEW_APPEARANCE_SUBMENU, nls.localizeByDefault('Appearance'));
registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, {
commandId: CommonCommands.NEW_UNTITLED_TEXT_FILE.id,
label: nls.localizeByDefault('New Text File'),
order: 'a'
});
registry.registerMenuAction(CommonMenus.FILE_NEW_TEXT, {
commandId: CommonCommands.NEW_UNTITLED_FILE.id,
label: nls.localizeByDefault('New File...'),
order: 'a1'
});
}
registerCommands(commandRegistry: CommandRegistry): void {
commandRegistry.registerCommand(CommonCommands.OPEN, UriAwareCommandHandler.MultiSelect(this.selectionService, {
execute: uris => uris.map(uri => open(this.openerService, uri)),
}));
commandRegistry.registerCommand(CommonCommands.CUT, {
execute: () => {
if (supportCut) {
document.execCommand('cut');
} else {
this.messageService.warn(nls.localize('theia/core/cutWarn', "Please use the browser's cut command or shortcut."));
}
}
});
commandRegistry.registerCommand(CommonCommands.COPY, {
execute: () => {
if (supportCopy) {
document.execCommand('copy');
} else {
this.messageService.warn(nls.localize('theia/core/copyWarn', "Please use the browser's copy command or shortcut."));
}
}
});
commandRegistry.registerCommand(CommonCommands.PASTE, {
execute: () => {
if (supportPaste) {
document.execCommand('paste');
} else {
this.messageService.warn(nls.localize('theia/core/pasteWarn', "Please use the browser's paste command or shortcut."));
}
}
});
commandRegistry.registerCommand(CommonCommands.COPY_PATH, UriAwareCommandHandler.MultiSelect(this.selectionService, {
isVisible: uris => Array.isArray(uris) && uris.some(uri => uri instanceof URI),
isEnabled: uris => Array.isArray(uris) && uris.some(uri => uri instanceof URI),
execute: async uris => {
if (uris.length) {
const lineDelimiter = EOL;
const text = uris.map(resource => resource.path.fsPath()).join(lineDelimiter);
await this.clipboardService.writeText(text);
} else {
await this.messageService.info(nls.localize('theia/core/copyInfo', 'Open a file first to copy its path'));
}
}
}));
commandRegistry.registerCommand(CommonCommands.UNDO, {
execute: () => document.execCommand('undo')
});
commandRegistry.registerCommand(CommonCommands.REDO, {
execute: () => document.execCommand('redo')
});
commandRegistry.registerCommand(CommonCommands.SELECT_ALL, {
execute: () => document.execCommand('selectAll')
});
commandRegistry.registerCommand(CommonCommands.FIND, {
execute: () => { /* no-op */ }
});
commandRegistry.registerCommand(CommonCommands.REPLACE, {
execute: () => { /* no-op */ }
});
commandRegistry.registerCommand(CommonCommands.NEXT_TAB, {
isEnabled: () => this.shell.currentTabBar !== undefined,
execute: () => this.shell.activateNextTab()
});
commandRegistry.registerCommand(CommonCommands.PREVIOUS_TAB, {
isEnabled: () => this.shell.currentTabBar !== undefined,
execute: () => this.shell.activatePreviousTab()
});
commandRegistry.registerCommand(CommonCommands.NEXT_TAB_IN_GROUP, {
isEnabled: () => this.shell.nextTabIndexInTabBar() !== -1,
execute: () => this.shell.activateNextTabInTabBar()
});
commandRegistry.registerCommand(CommonCommands.PREVIOUS_TAB_IN_GROUP, {
isEnabled: () => this.shell.previousTabIndexInTabBar() !== -1,
execute: () => this.shell.activatePreviousTabInTabBar()
});
commandRegistry.registerCommand(CommonCommands.NEXT_TAB_GROUP, {
isEnabled: () => this.shell.nextTabBar() !== undefined,
execute: () => this.shell.activateNextTabBar()
});
commandRegistry.registerCommand(CommonCommands.PREVIOUS_TAB_GROUP, {
isEnabled: () => this.shell.previousTabBar() !== undefined,
execute: () => this.shell.activatePreviousTabBar()
});
commandRegistry.registerCommand(CommonCommands.CLOSE_TAB, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: title => Boolean(title?.closable),
execute: (title, tabBar) => tabBar && this.shell.closeTabs(tabBar, candidate => candidate === title),
}));
commandRegistry.registerCommand(CommonCommands.CLOSE_OTHER_TABS, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: (title, tabbar) => Boolean(tabbar?.titles.some(candidate => candidate !== title && candidate.closable)),
execute: (title, tabbar) => tabbar && this.shell.closeTabs(tabbar, candidate => candidate !== title && candidate.closable),
}));
commandRegistry.registerCommand(CommonCommands.CLOSE_SAVED_TABS, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: (_title, tabbar) => Boolean(tabbar?.titles.some(candidate => candidate.closable && !Saveable.isDirty(candidate.owner))),
execute: (_title, tabbar) => tabbar && this.shell.closeTabs(tabbar, candidate => candidate.closable && !Saveable.isDirty(candidate.owner)),
}));
commandRegistry.registerCommand(CommonCommands.CLOSE_RIGHT_TABS, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: (title, tabbar) => {
let targetSeen = false;
return Boolean(tabbar?.titles.some(candidate => {
if (targetSeen && candidate.closable) { return true; };
if (candidate === title) { targetSeen = true; };
}));
},
isVisible: (_title, tabbar) => {
const area = (tabbar && this.shell.getAreaFor(tabbar)) ?? this.shell.currentTabArea;
return area !== undefined && area !== 'left' && area !== 'right';
},
execute: (title, tabbar) => {
if (tabbar) {
let targetSeen = false;
this.shell.closeTabs(tabbar, candidate => {
if (targetSeen && candidate.closable) { return true; };
if (candidate === title) { targetSeen = true; };
return false;
});
}
}
}));
commandRegistry.registerCommand(CommonCommands.CLOSE_ALL_TABS, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: (_title, tabbar) => Boolean(tabbar?.titles.some(title => title.closable)),
execute: (_title, tabbar) => tabbar && this.shell.closeTabs(tabbar, candidate => candidate.closable),
}));
commandRegistry.registerCommand(CommonCommands.CLOSE_MAIN_TAB, {
isEnabled: () => {
const currentWidget = this.shell.getCurrentWidget('main');
return currentWidget !== undefined && currentWidget.title.closable;
},
execute: () => this.shell.getCurrentWidget('main')!.close()
});
commandRegistry.registerCommand(CommonCommands.CLOSE_OTHER_MAIN_TABS, {
isEnabled: () => {
const currentWidget = this.shell.getCurrentWidget('main');
return currentWidget !== undefined &&
this.shell.mainAreaTabBars.some(tb => tb.titles.some(title => title.owner !== currentWidget && title.closable));
},
execute: () => {
const currentWidget = this.shell.getCurrentWidget('main');
this.shell.closeTabs('main', title => title.owner !== currentWidget && title.closable);
}
});
commandRegistry.registerCommand(CommonCommands.CLOSE_ALL_MAIN_TABS, {
isEnabled: () => this.shell.mainAreaTabBars.some(tb => tb.titles.some(title => title.closable)),
execute: () => this.shell.closeTabs('main', title => title.closable)
});
commandRegistry.registerCommand(CommonCommands.COLLAPSE_PANEL, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: (_title, tabbar) => Boolean(tabbar && ApplicationShell.isSideArea(this.shell.getAreaFor(tabbar))),
isVisible: (_title, tabbar) => Boolean(tabbar && ApplicationShell.isSideArea(this.shell.getAreaFor(tabbar))),
execute: (_title, tabbar) => tabbar && this.shell.collapsePanel(this.shell.getAreaFor(tabbar)!)
}));
commandRegistry.registerCommand(CommonCommands.COLLAPSE_ALL_PANELS, {
execute: () => {
this.shell.collapsePanel('left');
this.shell.collapsePanel('right');
this.shell.collapsePanel('bottom');
}
});
commandRegistry.registerCommand(CommonCommands.TOGGLE_BOTTOM_PANEL, {
isEnabled: () => this.shell.getWidgets('bottom').length > 0,
execute: () => {
if (this.shell.isExpanded('bottom')) {
this.shell.collapsePanel('bottom');
} else {
this.shell.expandPanel('bottom');
}
}
});
commandRegistry.registerCommand(CommonCommands.TOGGLE_STATUS_BAR, {
execute: () => this.preferenceService.updateValue('workbench.statusBar.visible', !this.preferences['workbench.statusBar.visible'])
});
commandRegistry.registerCommand(CommonCommands.TOGGLE_MAXIMIZED, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: title => Boolean(title?.owner && this.shell.canToggleMaximized(title?.owner)),
isVisible: title => Boolean(title?.owner && this.shell.canToggleMaximized(title?.owner)),
execute: title => title?.owner && this.shell.toggleMaximized(title?.owner),
}));
commandRegistry.registerCommand(CommonCommands.SHOW_MENU_BAR, {
isEnabled: () => !isOSX,
isVisible: () => !isOSX,
execute: () => {
const menuBarVisibility = 'window.menuBarVisibility';
const visibility = this.preferences[menuBarVisibility];
if (visibility !== 'compact') {
this.preferenceService.updateValue(menuBarVisibility, 'compact');
} else {
this.preferenceService.updateValue(menuBarVisibility, 'classic');
}
}
});
commandRegistry.registerCommand(CommonCommands.SAVE, {
execute: () => this.save({ formatType: FormatType.ON })
});
commandRegistry.registerCommand(CommonCommands.SAVE_AS, {
isEnabled: () => this.saveResourceService.canSaveAs(this.shell.currentWidget),
execute: () => {
const { currentWidget } = this.shell;
// No clue what could have happened between `isEnabled` and `execute`
// when fetching currentWidget, so better to double-check:
if (this.saveResourceService.canSaveAs(currentWidget)) {
this.saveResourceService.saveAs(currentWidget);
} else {
this.messageService.error(nls.localize('theia/workspace/failSaveAs', 'Cannot run "{0}" for the current widget.', CommonCommands.SAVE_AS.label!));
}
},
});
commandRegistry.registerCommand(CommonCommands.SAVE_WITHOUT_FORMATTING, {
execute: () => this.save({ formatType: FormatType.OFF })
});
commandRegistry.registerCommand(CommonCommands.SAVE_ALL, {
execute: () => this.shell.saveAll({ formatType: FormatType.DIRTY })
});
commandRegistry.registerCommand(CommonCommands.ABOUT_COMMAND, {
execute: () => this.openAbout()
});
commandRegistry.registerCommand(CommonCommands.OPEN_VIEW, {
execute: () => this.quickInputService?.open(QuickViewService.PREFIX)
});
commandRegistry.registerCommand(CommonCommands.SELECT_COLOR_THEME, {
execute: () => this.selectColorTheme()
});
commandRegistry.registerCommand(CommonCommands.SELECT_ICON_THEME, {
execute: () => this.selectIconTheme()
});
commandRegistry.registerCommand(CommonCommands.PIN_TAB, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: title => Boolean(title && !isPinned(title)),
execute: title => this.togglePinned(title),
}));
commandRegistry.registerCommand(CommonCommands.UNPIN_TAB, new CurrentWidgetCommandAdapter(this.shell, {
isEnabled: title => Boolean(title && isPinned(title)),
execute: title => this.togglePinned(title),
}));
commandRegistry.registerCommand(CommonCommands.CONFIGURE_DISPLAY_LANGUAGE, {
execute: () => this.configureDisplayLanguage()
});
commandRegistry.registerCommand(CommonCommands.TOGGLE_BREADCRUMBS, {
execute: () => this.toggleBreadcrumbs(),
isToggled: () => this.isBreadcrumbsEnabled(),
});
commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_TEXT_FILE, {
execute: async () => {
const untitledUri = this.untitledResourceResolver.createUntitledURI('', await this.workingDirProvider.getUserWorkingDir());
this.untitledResourceResolver.resolve(untitledUri);
return open(this.openerService, untitledUri);
}
});
commandRegistry.registerCommand(CommonCommands.NEW_UNTITLED_FILE, {
execute: async () => this.showNewFilePicker()
});
for (const [index, ordinal] of this.getOrdinalNumbers().entries()) {
commandRegistry.registerCommand({ id: `workbench.action.focus${ordinal}EditorGroup`, label: index === 0 ? nls.localizeByDefault('Focus First Editor Group') : '', category: nls.localize(CommonCommands.VIEW_CATEGORY_KEY, CommonCommands.VIEW_CATEGORY) }, {
isEnabled: () => this.shell.mainAreaTabBars.length > index,
execute: () => {
const widget = this.shell.mainAreaTabBars[index]?.currentTitle?.owner;
if (widget) {
this.shell.activateWidget(widget.id);
}
}
});
}
}
protected getOrdinalNumbers(): readonly string[] {
return ['First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth', 'Seventh', 'Eighth', 'Ninth'];
}
protected isElectron(): boolean {
return environment.electron.is();
}
protected togglePinned(title?: Title<Widget>): void {
if (title) {
togglePinned(title);
this.updatePinnedKey();
}
}
registerKeybindings(registry: KeybindingRegistry): void {
if (supportCut) {
registry.registerKeybinding({
command: CommonCommands.CUT.id,
keybinding: 'ctrlcmd+x'
});
}
if (supportCopy) {
registry.registerKeybinding({
command: CommonCommands.COPY.id,
keybinding: 'ctrlcmd+c'
});
}
if (supportPaste) {
registry.registerKeybinding({
command: CommonCommands.PASTE.id,
keybinding: 'ctrlcmd+v'
});
}
registry.registerKeybinding({
command: CommonCommands.COPY_PATH.id,
keybinding: isWindows ? 'shift+alt+c' : 'ctrlcmd+alt+c',
when: '!editorFocus'
});
registry.registerKeybindings(
// Edition
{
command: CommonCommands.UNDO.id,
keybinding: 'ctrlcmd+z'
},
{
command: CommonCommands.REDO.id,
keybinding: 'ctrlcmd+shift+z'
},
{
command: CommonCommands.SELECT_ALL.id,
keybinding: 'ctrlcmd+a'
},
{
command: CommonCommands.FIND.id,
keybinding: 'ctrlcmd+f'
},
{
command: CommonCommands.REPLACE.id,
keybinding: 'ctrlcmd+alt+f'
},
// Tabs
{
command: CommonCommands.NEXT_TAB.id,
keybinding: 'ctrlcmd+tab'
},
{
command: CommonCommands.NEXT_TAB.id,
keybinding: 'ctrlcmd+alt+d'
},
{
command: CommonCommands.PREVIOUS_TAB.id,
keybinding: 'ctrlcmd+shift+tab'
},
{
command: CommonCommands.PREVIOUS_TAB.id,
keybinding: 'ctrlcmd+alt+a'
},
{
command: CommonCommands.CLOSE_MAIN_TAB.id,
keybinding: this.isElectron() ? (isWindows ? 'ctrl+f4' : 'ctrlcmd+w') : 'alt+w'
},
{
command: CommonCommands.CLOSE_OTHER_MAIN_TABS.id,
keybinding: 'ctrlcmd+alt+t'
},
{
command: CommonCommands.CLOSE_ALL_MAIN_TABS.id,
keybinding: this.isElectron() ? 'ctrlCmd+k ctrlCmd+w' : 'alt+shift+w'
},
// Panels
{
command: CommonCommands.COLLAPSE_PANEL.id,
keybinding: 'alt+c'
},
{
command: CommonCommands.TOGGLE_BOTTOM_PANEL.id,
keybinding: 'ctrlcmd+j',
},
{
command: CommonCommands.COLLAPSE_ALL_PANELS.id,
keybinding: 'alt+shift+c',
},
{
command: CommonCommands.TOGGLE_MAXIMIZED.id,
keybinding: 'alt+m',
},
// Saving
{
command: CommonCommands.SAVE.id,
keybinding: 'ctrlcmd+s'
},
{
command: CommonCommands.SAVE_WITHOUT_FORMATTING.id,
keybinding: 'ctrlcmd+k s'
},
{
command: CommonCommands.SAVE_ALL.id,
keybinding: 'ctrlcmd+alt+s'
},
// Theming
{
command: CommonCommands.SELECT_COLOR_THEME.id,
keybinding: 'ctrlcmd+k ctrlcmd+t'
},
{
command: CommonCommands.PIN_TAB.id,
keybinding: 'ctrlcmd+k shift+enter',
when: '!activeEditorIsPinned'
},
{
command: CommonCommands.UNPIN_TAB.id,
keybinding: 'ctrlcmd+k shift+enter',
when: 'activeEditorIsPinned'
},
{
comm