Path: blob/main/src/vs/platform/actions/common/actions.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IAction, SubmenuAction } from '../../../base/common/actions.js';6import { Event, MicrotaskEmitter } from '../../../base/common/event.js';7import { DisposableStore, dispose, IDisposable, markAsSingleton, toDisposable } from '../../../base/common/lifecycle.js';8import { LinkedList } from '../../../base/common/linkedList.js';9import { ThemeIcon } from '../../../base/common/themables.js';10import { ICommandAction, ICommandActionTitle, Icon, ILocalizedString } from '../../action/common/action.js';11import { Categories } from '../../action/common/actionCommonCategories.js';12import { CommandsRegistry, ICommandService } from '../../commands/common/commands.js';13import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from '../../contextkey/common/contextkey.js';14import { createDecorator, ServicesAccessor } from '../../instantiation/common/instantiation.js';15import { IKeybindingRule, KeybindingsRegistry } from '../../keybinding/common/keybindingsRegistry.js';1617export interface IMenuItem {18command: ICommandAction;19alt?: ICommandAction;20/**21* Menu item is hidden if this expression returns false.22*/23when?: ContextKeyExpression;24group?: 'navigation' | string;25order?: number;26isHiddenByDefault?: boolean;27}2829export interface ISubmenuItem {30title: string | ICommandActionTitle;31submenu: MenuId;32icon?: Icon;33when?: ContextKeyExpression;34group?: 'navigation' | string;35order?: number;36isSelection?: boolean;37rememberDefaultAction?: boolean; // for dropdown menu: if true the last executed action is remembered as the default action38}3940export function isIMenuItem(item: any): item is IMenuItem {41return (item as IMenuItem).command !== undefined;42}4344export function isISubmenuItem(item: any): item is ISubmenuItem {45return (item as ISubmenuItem).submenu !== undefined;46}4748export class MenuId {4950private static readonly _instances = new Map<string, MenuId>();5152static readonly CommandPalette = new MenuId('CommandPalette');53static readonly DebugBreakpointsContext = new MenuId('DebugBreakpointsContext');54static readonly DebugCallStackContext = new MenuId('DebugCallStackContext');55static readonly DebugConsoleContext = new MenuId('DebugConsoleContext');56static readonly DebugVariablesContext = new MenuId('DebugVariablesContext');57static readonly NotebookVariablesContext = new MenuId('NotebookVariablesContext');58static readonly DebugHoverContext = new MenuId('DebugHoverContext');59static readonly DebugWatchContext = new MenuId('DebugWatchContext');60static readonly DebugToolBar = new MenuId('DebugToolBar');61static readonly DebugToolBarStop = new MenuId('DebugToolBarStop');62static readonly DebugDisassemblyContext = new MenuId('DebugDisassemblyContext');63static readonly DebugCallStackToolbar = new MenuId('DebugCallStackToolbar');64static readonly DebugCreateConfiguration = new MenuId('DebugCreateConfiguration');65static readonly EditorContext = new MenuId('EditorContext');66static readonly SimpleEditorContext = new MenuId('SimpleEditorContext');67static readonly EditorContent = new MenuId('EditorContent');68static readonly EditorLineNumberContext = new MenuId('EditorLineNumberContext');69static readonly EditorContextCopy = new MenuId('EditorContextCopy');70static readonly EditorContextPeek = new MenuId('EditorContextPeek');71static readonly EditorContextShare = new MenuId('EditorContextShare');72static readonly EditorTitle = new MenuId('EditorTitle');73static readonly CompactWindowEditorTitle = new MenuId('CompactWindowEditorTitle');74static readonly EditorTitleRun = new MenuId('EditorTitleRun');75static readonly EditorTitleContext = new MenuId('EditorTitleContext');76static readonly EditorTitleContextShare = new MenuId('EditorTitleContextShare');77static readonly EmptyEditorGroup = new MenuId('EmptyEditorGroup');78static readonly EmptyEditorGroupContext = new MenuId('EmptyEditorGroupContext');79static readonly EditorTabsBarContext = new MenuId('EditorTabsBarContext');80static readonly EditorTabsBarShowTabsSubmenu = new MenuId('EditorTabsBarShowTabsSubmenu');81static readonly EditorTabsBarShowTabsZenModeSubmenu = new MenuId('EditorTabsBarShowTabsZenModeSubmenu');82static readonly EditorActionsPositionSubmenu = new MenuId('EditorActionsPositionSubmenu');83static readonly EditorSplitMoveSubmenu = new MenuId('EditorSplitMoveSubmenu');84static readonly ExplorerContext = new MenuId('ExplorerContext');85static readonly ExplorerContextShare = new MenuId('ExplorerContextShare');86static readonly ExtensionContext = new MenuId('ExtensionContext');87static readonly ExtensionEditorContextMenu = new MenuId('ExtensionEditorContextMenu');88static readonly GlobalActivity = new MenuId('GlobalActivity');89static readonly CommandCenter = new MenuId('CommandCenter');90static readonly CommandCenterCenter = new MenuId('CommandCenterCenter');91static readonly LayoutControlMenuSubmenu = new MenuId('LayoutControlMenuSubmenu');92static readonly LayoutControlMenu = new MenuId('LayoutControlMenu');93static readonly MenubarMainMenu = new MenuId('MenubarMainMenu');94static readonly MenubarAppearanceMenu = new MenuId('MenubarAppearanceMenu');95static readonly MenubarDebugMenu = new MenuId('MenubarDebugMenu');96static readonly MenubarEditMenu = new MenuId('MenubarEditMenu');97static readonly MenubarCopy = new MenuId('MenubarCopy');98static readonly MenubarFileMenu = new MenuId('MenubarFileMenu');99static readonly MenubarGoMenu = new MenuId('MenubarGoMenu');100static readonly MenubarHelpMenu = new MenuId('MenubarHelpMenu');101static readonly MenubarLayoutMenu = new MenuId('MenubarLayoutMenu');102static readonly MenubarNewBreakpointMenu = new MenuId('MenubarNewBreakpointMenu');103static readonly PanelAlignmentMenu = new MenuId('PanelAlignmentMenu');104static readonly PanelPositionMenu = new MenuId('PanelPositionMenu');105static readonly ActivityBarPositionMenu = new MenuId('ActivityBarPositionMenu');106static readonly MenubarPreferencesMenu = new MenuId('MenubarPreferencesMenu');107static readonly MenubarRecentMenu = new MenuId('MenubarRecentMenu');108static readonly MenubarSelectionMenu = new MenuId('MenubarSelectionMenu');109static readonly MenubarShare = new MenuId('MenubarShare');110static readonly MenubarSwitchEditorMenu = new MenuId('MenubarSwitchEditorMenu');111static readonly MenubarSwitchGroupMenu = new MenuId('MenubarSwitchGroupMenu');112static readonly MenubarTerminalMenu = new MenuId('MenubarTerminalMenu');113static readonly MenubarTerminalSuggestStatusMenu = new MenuId('MenubarTerminalSuggestStatusMenu');114static readonly MenubarViewMenu = new MenuId('MenubarViewMenu');115static readonly MenubarHomeMenu = new MenuId('MenubarHomeMenu');116static readonly OpenEditorsContext = new MenuId('OpenEditorsContext');117static readonly OpenEditorsContextShare = new MenuId('OpenEditorsContextShare');118static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext');119static readonly SCMInputBox = new MenuId('SCMInputBox');120static readonly SCMChangeContext = new MenuId('SCMChangeContext');121static readonly SCMResourceContext = new MenuId('SCMResourceContext');122static readonly SCMResourceContextShare = new MenuId('SCMResourceContextShare');123static readonly SCMResourceFolderContext = new MenuId('SCMResourceFolderContext');124static readonly SCMResourceGroupContext = new MenuId('SCMResourceGroupContext');125static readonly SCMSourceControl = new MenuId('SCMSourceControl');126static readonly SCMSourceControlInline = new MenuId('SCMSourceControlInline');127static readonly SCMSourceControlTitle = new MenuId('SCMSourceControlTitle');128static readonly SCMHistoryTitle = new MenuId('SCMHistoryTitle');129static readonly SCMHistoryItemContext = new MenuId('SCMHistoryItemContext');130static readonly SCMHistoryItemChangeContext = new MenuId('SCMHistoryItemChangeContext');131static readonly SCMHistoryItemHover = new MenuId('SCMHistoryItemHover');132static readonly SCMHistoryItemRefContext = new MenuId('SCMHistoryItemRefContext');133static readonly SCMQuickDiffDecorations = new MenuId('SCMQuickDiffDecorations');134static readonly SCMTitle = new MenuId('SCMTitle');135static readonly SearchContext = new MenuId('SearchContext');136static readonly SearchActionMenu = new MenuId('SearchActionContext');137static readonly StatusBarWindowIndicatorMenu = new MenuId('StatusBarWindowIndicatorMenu');138static readonly StatusBarRemoteIndicatorMenu = new MenuId('StatusBarRemoteIndicatorMenu');139static readonly StickyScrollContext = new MenuId('StickyScrollContext');140static readonly TestItem = new MenuId('TestItem');141static readonly TestItemGutter = new MenuId('TestItemGutter');142static readonly TestProfilesContext = new MenuId('TestProfilesContext');143static readonly TestMessageContext = new MenuId('TestMessageContext');144static readonly TestMessageContent = new MenuId('TestMessageContent');145static readonly TestPeekElement = new MenuId('TestPeekElement');146static readonly TestPeekTitle = new MenuId('TestPeekTitle');147static readonly TestCallStack = new MenuId('TestCallStack');148static readonly TestCoverageFilterItem = new MenuId('TestCoverageFilterItem');149static readonly TouchBarContext = new MenuId('TouchBarContext');150static readonly TitleBar = new MenuId('TitleBar');151static readonly TitleBarContext = new MenuId('TitleBarContext');152static readonly TitleBarTitleContext = new MenuId('TitleBarTitleContext');153static readonly TunnelContext = new MenuId('TunnelContext');154static readonly TunnelPrivacy = new MenuId('TunnelPrivacy');155static readonly TunnelProtocol = new MenuId('TunnelProtocol');156static readonly TunnelPortInline = new MenuId('TunnelInline');157static readonly TunnelTitle = new MenuId('TunnelTitle');158static readonly TunnelLocalAddressInline = new MenuId('TunnelLocalAddressInline');159static readonly TunnelOriginInline = new MenuId('TunnelOriginInline');160static readonly ViewItemContext = new MenuId('ViewItemContext');161static readonly ViewContainerTitle = new MenuId('ViewContainerTitle');162static readonly ViewContainerTitleContext = new MenuId('ViewContainerTitleContext');163static readonly ViewTitle = new MenuId('ViewTitle');164static readonly ViewTitleContext = new MenuId('ViewTitleContext');165static readonly CommentEditorActions = new MenuId('CommentEditorActions');166static readonly CommentThreadTitle = new MenuId('CommentThreadTitle');167static readonly CommentThreadActions = new MenuId('CommentThreadActions');168static readonly CommentThreadAdditionalActions = new MenuId('CommentThreadAdditionalActions');169static readonly CommentThreadTitleContext = new MenuId('CommentThreadTitleContext');170static readonly CommentThreadCommentContext = new MenuId('CommentThreadCommentContext');171static readonly CommentTitle = new MenuId('CommentTitle');172static readonly CommentActions = new MenuId('CommentActions');173static readonly CommentsViewThreadActions = new MenuId('CommentsViewThreadActions');174static readonly InteractiveToolbar = new MenuId('InteractiveToolbar');175static readonly InteractiveCellTitle = new MenuId('InteractiveCellTitle');176static readonly InteractiveCellDelete = new MenuId('InteractiveCellDelete');177static readonly InteractiveCellExecute = new MenuId('InteractiveCellExecute');178static readonly InteractiveInputExecute = new MenuId('InteractiveInputExecute');179static readonly InteractiveInputConfig = new MenuId('InteractiveInputConfig');180static readonly ReplInputExecute = new MenuId('ReplInputExecute');181static readonly IssueReporter = new MenuId('IssueReporter');182static readonly NotebookToolbar = new MenuId('NotebookToolbar');183static readonly NotebookToolbarContext = new MenuId('NotebookToolbarContext');184static readonly NotebookStickyScrollContext = new MenuId('NotebookStickyScrollContext');185static readonly NotebookCellTitle = new MenuId('NotebookCellTitle');186static readonly NotebookCellDelete = new MenuId('NotebookCellDelete');187static readonly NotebookCellInsert = new MenuId('NotebookCellInsert');188static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');189static readonly NotebookCellListTop = new MenuId('NotebookCellTop');190static readonly NotebookCellExecute = new MenuId('NotebookCellExecute');191static readonly NotebookCellExecuteGoTo = new MenuId('NotebookCellExecuteGoTo');192static readonly NotebookCellExecutePrimary = new MenuId('NotebookCellExecutePrimary');193static readonly NotebookDiffCellInputTitle = new MenuId('NotebookDiffCellInputTitle');194static readonly NotebookDiffDocumentMetadata = new MenuId('NotebookDiffDocumentMetadata');195static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle');196static readonly NotebookDiffCellOutputsTitle = new MenuId('NotebookDiffCellOutputsTitle');197static readonly NotebookOutputToolbar = new MenuId('NotebookOutputToolbar');198static readonly NotebookOutlineFilter = new MenuId('NotebookOutlineFilter');199static readonly NotebookOutlineActionMenu = new MenuId('NotebookOutlineActionMenu');200static readonly NotebookEditorLayoutConfigure = new MenuId('NotebookEditorLayoutConfigure');201static readonly NotebookKernelSource = new MenuId('NotebookKernelSource');202static readonly BulkEditTitle = new MenuId('BulkEditTitle');203static readonly BulkEditContext = new MenuId('BulkEditContext');204static readonly TimelineItemContext = new MenuId('TimelineItemContext');205static readonly TimelineTitle = new MenuId('TimelineTitle');206static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');207static readonly TimelineFilterSubMenu = new MenuId('TimelineFilterSubMenu');208static readonly AccountsContext = new MenuId('AccountsContext');209static readonly SidebarTitle = new MenuId('SidebarTitle');210static readonly PanelTitle = new MenuId('PanelTitle');211static readonly AuxiliaryBarTitle = new MenuId('AuxiliaryBarTitle');212static readonly TerminalInstanceContext = new MenuId('TerminalInstanceContext');213static readonly TerminalEditorInstanceContext = new MenuId('TerminalEditorInstanceContext');214static readonly TerminalNewDropdownContext = new MenuId('TerminalNewDropdownContext');215static readonly TerminalTabContext = new MenuId('TerminalTabContext');216static readonly TerminalTabEmptyAreaContext = new MenuId('TerminalTabEmptyAreaContext');217static readonly TerminalStickyScrollContext = new MenuId('TerminalStickyScrollContext');218static readonly WebviewContext = new MenuId('WebviewContext');219static readonly InlineCompletionsActions = new MenuId('InlineCompletionsActions');220static readonly InlineEditsActions = new MenuId('InlineEditsActions');221static readonly NewFile = new MenuId('NewFile');222static readonly MergeInput1Toolbar = new MenuId('MergeToolbar1Toolbar');223static readonly MergeInput2Toolbar = new MenuId('MergeToolbar2Toolbar');224static readonly MergeBaseToolbar = new MenuId('MergeBaseToolbar');225static readonly MergeInputResultToolbar = new MenuId('MergeToolbarResultToolbar');226static readonly InlineSuggestionToolbar = new MenuId('InlineSuggestionToolbar');227static readonly InlineEditToolbar = new MenuId('InlineEditToolbar');228static readonly ChatContext = new MenuId('ChatContext');229static readonly ChatCodeBlock = new MenuId('ChatCodeblock');230static readonly ChatCompareBlock = new MenuId('ChatCompareBlock');231static readonly ChatMessageTitle = new MenuId('ChatMessageTitle');232static readonly ChatMessageFooter = new MenuId('ChatMessageFooter');233static readonly ChatExecute = new MenuId('ChatExecute');234static readonly ChatExecuteSecondary = new MenuId('ChatExecuteSecondary');235static readonly ChatInput = new MenuId('ChatInput');236static readonly ChatInputSide = new MenuId('ChatInputSide');237static readonly ChatModePicker = new MenuId('ChatModePicker');238static readonly ChatEditingWidgetToolbar = new MenuId('ChatEditingWidgetToolbar');239static readonly ChatEditingEditorContent = new MenuId('ChatEditingEditorContent');240static readonly ChatEditingEditorHunk = new MenuId('ChatEditingEditorHunk');241static readonly ChatEditingDeletedNotebookCell = new MenuId('ChatEditingDeletedNotebookCell');242static readonly ChatInputAttachmentToolbar = new MenuId('ChatInputAttachmentToolbar');243static readonly ChatEditingWidgetModifiedFilesToolbar = new MenuId('ChatEditingWidgetModifiedFilesToolbar');244static readonly ChatInputResourceAttachmentContext = new MenuId('ChatInputResourceAttachmentContext');245static readonly ChatInputSymbolAttachmentContext = new MenuId('ChatInputSymbolAttachmentContext');246static readonly ChatInlineResourceAnchorContext = new MenuId('ChatInlineResourceAnchorContext');247static readonly ChatInlineSymbolAnchorContext = new MenuId('ChatInlineSymbolAnchorContext');248static readonly ChatMessageCheckpoint: MenuId = new MenuId('ChatMessageCheckpoint');249static readonly ChatMessageRestoreCheckpoint: MenuId = new MenuId('ChatMessageRestoreCheckpoint');250static readonly ChatEditingCodeBlockContext = new MenuId('ChatEditingCodeBlockContext');251static readonly ChatTitleBarMenu = new MenuId('ChatTitleBarMenu');252static readonly ChatAttachmentsContext = new MenuId('ChatAttachmentsContext');253static readonly ChatToolOutputResourceToolbar = new MenuId('ChatToolOutputResourceToolbar');254static readonly ChatTextEditorMenu = new MenuId('ChatTextEditorMenu');255static readonly ChatToolOutputResourceContext = new MenuId('ChatToolOutputResourceContext');256static readonly ChatMultiDiffContext = new MenuId('ChatMultiDiffContext');257static readonly ChatSessionsMenu = new MenuId('ChatSessionsMenu');258static readonly ChatConfirmationMenu = new MenuId('ChatConfirmationMenu');259static readonly AccessibleView = new MenuId('AccessibleView');260static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar');261static readonly DiffEditorHunkToolbar = new MenuId('DiffEditorHunkToolbar');262static readonly DiffEditorSelectionToolbar = new MenuId('DiffEditorSelectionToolbar');263264265/**266* Create or reuse a `MenuId` with the given identifier267*/268static for(identifier: string): MenuId {269return MenuId._instances.get(identifier) ?? new MenuId(identifier);270}271272readonly id: string;273274/**275* Create a new `MenuId` with the unique identifier. Will throw if a menu276* with the identifier already exists, use `MenuId.for(ident)` or a unique277* identifier278*/279constructor(identifier: string) {280if (MenuId._instances.has(identifier)) {281throw new TypeError(`MenuId with identifier '${identifier}' already exists. Use MenuId.for(ident) or a unique identifier`);282}283MenuId._instances.set(identifier, this);284this.id = identifier;285}286}287288export interface IMenuActionOptions {289arg?: any;290shouldForwardArgs?: boolean;291renderShortTitle?: boolean;292}293294export interface IMenuChangeEvent {295readonly menu: IMenu;296readonly isStructuralChange: boolean;297readonly isToggleChange: boolean;298readonly isEnablementChange: boolean;299}300301export interface IMenu extends IDisposable {302readonly onDidChange: Event<IMenuChangeEvent>;303getActions(options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][];304}305306export interface IMenuData {307contexts: ReadonlySet<string>;308actions: [string, Array<MenuItemAction | SubmenuItemAction>][];309}310311export const IMenuService = createDecorator<IMenuService>('menuService');312313export interface IMenuCreateOptions {314emitEventsForSubmenuChanges?: boolean;315eventDebounceDelay?: number;316}317318export interface IMenuService {319320readonly _serviceBrand: undefined;321322/**323* Consider using getMenuActions if you don't need to listen to events.324*325* Create a new menu for the given menu identifier. A menu sends events when it's entries326* have changed (placement, enablement, checked-state). By default it does not send events for327* submenu entries. That is more expensive and must be explicitly enabled with the328* `emitEventsForSubmenuChanges` flag.329*/330createMenu(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuCreateOptions): IMenu;331332/**333* Creates a new menu, gets the actions, and then disposes of the menu.334*/335getMenuActions(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][];336337/**338* Gets the names of the contexts that this menu listens on.339*/340getMenuContexts(id: MenuId): ReadonlySet<string>;341342/**343* Reset **all** menu item hidden states.344*/345resetHiddenStates(): void;346347/**348* Reset the menu's hidden states.349*/350resetHiddenStates(menuIds: readonly MenuId[] | undefined): void;351}352353type ICommandsMap = Map<string, ICommandAction>;354355export interface IMenuRegistryChangeEvent {356has(id: MenuId): boolean;357}358359class MenuRegistryChangeEvent {360361private static _all = new Map<MenuId, MenuRegistryChangeEvent>();362363static for(id: MenuId): MenuRegistryChangeEvent {364let value = this._all.get(id);365if (!value) {366value = new MenuRegistryChangeEvent(id);367this._all.set(id, value);368}369return value;370}371372static merge(events: IMenuRegistryChangeEvent[]): IMenuRegistryChangeEvent {373const ids = new Set<MenuId>();374for (const item of events) {375if (item instanceof MenuRegistryChangeEvent) {376ids.add(item.id);377}378}379return ids;380}381382readonly has: (id: MenuId) => boolean;383384private constructor(private readonly id: MenuId) {385this.has = candidate => candidate === id;386}387}388389export interface IMenuRegistry {390readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent>;391addCommand(userCommand: ICommandAction): IDisposable;392getCommand(id: string): ICommandAction | undefined;393getCommands(): ICommandsMap;394395/**396* @deprecated Use `appendMenuItem` or most likely use `registerAction2` instead. There should be no strong397* reason to use this directly.398*/399appendMenuItems(items: Iterable<{ id: MenuId; item: IMenuItem | ISubmenuItem }>): IDisposable;400appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable;401getMenuItems(loc: MenuId): Array<IMenuItem | ISubmenuItem>;402}403404export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {405406private readonly _commands = new Map<string, ICommandAction>();407private readonly _menuItems = new Map<MenuId, LinkedList<IMenuItem | ISubmenuItem>>();408private readonly _onDidChangeMenu = new MicrotaskEmitter<IMenuRegistryChangeEvent>({409merge: MenuRegistryChangeEvent.merge410});411412readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent> = this._onDidChangeMenu.event;413414addCommand(command: ICommandAction): IDisposable {415this._commands.set(command.id, command);416this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette));417418return markAsSingleton(toDisposable(() => {419if (this._commands.delete(command.id)) {420this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette));421}422}));423}424425getCommand(id: string): ICommandAction | undefined {426return this._commands.get(id);427}428429getCommands(): ICommandsMap {430const map = new Map<string, ICommandAction>();431this._commands.forEach((value, key) => map.set(key, value));432return map;433}434435appendMenuItem(id: MenuId, item: IMenuItem | ISubmenuItem): IDisposable {436let list = this._menuItems.get(id);437if (!list) {438list = new LinkedList();439this._menuItems.set(id, list);440}441const rm = list.push(item);442this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id));443return markAsSingleton(toDisposable(() => {444rm();445this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id));446}));447}448449appendMenuItems(items: Iterable<{ id: MenuId; item: IMenuItem | ISubmenuItem }>): IDisposable {450const result = new DisposableStore();451for (const { id, item } of items) {452result.add(this.appendMenuItem(id, item));453}454return result;455}456457getMenuItems(id: MenuId): Array<IMenuItem | ISubmenuItem> {458let result: Array<IMenuItem | ISubmenuItem>;459if (this._menuItems.has(id)) {460result = [...this._menuItems.get(id)!];461} else {462result = [];463}464if (id === MenuId.CommandPalette) {465// CommandPalette is special because it shows466// all commands by default467this._appendImplicitItems(result);468}469return result;470}471472private _appendImplicitItems(result: Array<IMenuItem | ISubmenuItem>) {473const set = new Set<string>();474475for (const item of result) {476if (isIMenuItem(item)) {477set.add(item.command.id);478if (item.alt) {479set.add(item.alt.id);480}481}482}483this._commands.forEach((command, id) => {484if (!set.has(id)) {485result.push({ command });486}487});488}489};490491export class SubmenuItemAction extends SubmenuAction {492493constructor(494readonly item: ISubmenuItem,495readonly hideActions: IMenuItemHide | undefined,496actions: readonly IAction[],497) {498super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, actions, 'submenu');499}500}501502export interface IMenuItemHide {503readonly isHidden: boolean;504readonly hide: IAction;505readonly toggle: IAction;506}507508// implements IAction, does NOT extend Action, so that no one509// subscribes to events of Action or modified properties510export class MenuItemAction implements IAction {511512static label(action: ICommandAction, options?: IMenuActionOptions): string {513return options?.renderShortTitle && action.shortTitle514? (typeof action.shortTitle === 'string' ? action.shortTitle : action.shortTitle.value)515: (typeof action.title === 'string' ? action.title : action.title.value);516}517518readonly item: ICommandAction;519readonly alt: MenuItemAction | undefined;520521private readonly _options: IMenuActionOptions | undefined;522523readonly id: string;524readonly label: string;525readonly tooltip: string;526readonly class: string | undefined;527readonly enabled: boolean;528readonly checked?: boolean;529530constructor(531item: ICommandAction,532alt: ICommandAction | undefined,533options: IMenuActionOptions | undefined,534readonly hideActions: IMenuItemHide | undefined,535readonly menuKeybinding: IAction | undefined,536@IContextKeyService contextKeyService: IContextKeyService,537@ICommandService private _commandService: ICommandService538) {539this.id = item.id;540this.label = MenuItemAction.label(item, options);541this.tooltip = (typeof item.tooltip === 'string' ? item.tooltip : item.tooltip?.value) ?? '';542this.enabled = !item.precondition || contextKeyService.contextMatchesRules(item.precondition);543this.checked = undefined;544545let icon: ThemeIcon | undefined;546547if (item.toggled) {548const toggled = ((item.toggled as { condition: ContextKeyExpression }).condition ? item.toggled : { condition: item.toggled }) as {549condition: ContextKeyExpression; icon?: Icon; tooltip?: string | ILocalizedString; title?: string | ILocalizedString;550};551this.checked = contextKeyService.contextMatchesRules(toggled.condition);552if (this.checked && toggled.tooltip) {553this.tooltip = typeof toggled.tooltip === 'string' ? toggled.tooltip : toggled.tooltip.value;554}555556if (this.checked && ThemeIcon.isThemeIcon(toggled.icon)) {557icon = toggled.icon;558}559560if (this.checked && toggled.title) {561this.label = typeof toggled.title === 'string' ? toggled.title : toggled.title.value;562}563}564565if (!icon) {566icon = ThemeIcon.isThemeIcon(item.icon) ? item.icon : undefined;567}568569this.item = item;570this.alt = alt ? new MenuItemAction(alt, undefined, options, hideActions, undefined, contextKeyService, _commandService) : undefined;571this._options = options;572this.class = icon && ThemeIcon.asClassName(icon);573574}575576run(...args: any[]): Promise<void> {577let runArgs: any[] = [];578579if (this._options?.arg) {580runArgs = [...runArgs, this._options.arg];581}582583if (this._options?.shouldForwardArgs) {584runArgs = [...runArgs, ...args];585}586587return this._commandService.executeCommand(this.id, ...runArgs);588}589}590591//#region --- IAction2592593type OneOrN<T> = T | T[];594595interface IAction2CommonOptions extends ICommandAction {596/**597* One or many menu items.598*/599menu?: OneOrN<{ id: MenuId; precondition?: null } & Omit<IMenuItem, 'command'>>;600601/**602* One keybinding.603*/604keybinding?: OneOrN<Omit<IKeybindingRule, 'id'>>;605}606607interface IBaseAction2Options extends IAction2CommonOptions {608609/**610* This type is used when an action is not going to show up in the command palette.611* In that case, it's able to use a string for the `title` and `category` properties.612*/613f1?: false;614}615616export interface ICommandPaletteOptions extends IAction2CommonOptions {617618/**619* The title of the command that will be displayed in the command palette after the category.620* This overrides {@link ICommandAction.title} to ensure a string isn't used so that the title621* includes the localized value and the original value for users using language packs.622*/623title: ICommandActionTitle;624625/**626* The category of the command that will be displayed in the command palette before the title suffixed.627* with a colon This overrides {@link ICommandAction.title} to ensure a string isn't used so that628* the title includes the localized value and the original value for users using language packs.629*/630category?: keyof typeof Categories | ILocalizedString;631632/**633* Shorthand to add this command to the command palette. Note: this is not the only way to declare that634* a command should be in the command palette... however, enforcing ILocalizedString in the other scenarios635* is much more challenging and this gets us most of the way there.636*/637f1: true;638}639640export type IAction2Options = ICommandPaletteOptions | IBaseAction2Options;641642export interface IAction2F1RequiredOptions {643title: ICommandActionTitle;644category?: keyof typeof Categories | ILocalizedString;645}646647export abstract class Action2 {648constructor(readonly desc: Readonly<IAction2Options>) { }649abstract run(accessor: ServicesAccessor, ...args: any[]): void;650}651652export function registerAction2(ctor: { new(): Action2 }): IDisposable {653const disposables: IDisposable[] = []; // not using `DisposableStore` to reduce startup perf cost654const action = new ctor();655656const { f1, menu, keybinding, ...command } = action.desc;657658if (CommandsRegistry.getCommand(command.id)) {659throw new Error(`Cannot register two commands with the same id: ${command.id}`);660}661662// command663disposables.push(CommandsRegistry.registerCommand({664id: command.id,665handler: (accessor, ...args) => action.run(accessor, ...args),666metadata: command.metadata ?? { description: action.desc.title }667}));668669// menu670if (Array.isArray(menu)) {671for (const item of menu) {672disposables.push(MenuRegistry.appendMenuItem(item.id, { command: { ...command, precondition: item.precondition === null ? undefined : command.precondition }, ...item }));673}674675} else if (menu) {676disposables.push(MenuRegistry.appendMenuItem(menu.id, { command: { ...command, precondition: menu.precondition === null ? undefined : command.precondition }, ...menu }));677}678if (f1) {679disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when: command.precondition }));680disposables.push(MenuRegistry.addCommand(command));681}682683// keybinding684if (Array.isArray(keybinding)) {685for (const item of keybinding) {686disposables.push(KeybindingsRegistry.registerKeybindingRule({687...item,688id: command.id,689when: command.precondition ? ContextKeyExpr.and(command.precondition, item.when) : item.when690}));691}692} else if (keybinding) {693disposables.push(KeybindingsRegistry.registerKeybindingRule({694...keybinding,695id: command.id,696when: command.precondition ? ContextKeyExpr.and(command.precondition, keybinding.when) : keybinding.when697}));698}699700return {701dispose() {702dispose(disposables);703}704};705}706//#endregion707708709