Path: blob/main/src/vs/platform/actions/common/actions.ts
5260 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;37/**38* A split button shows the first action39* as primary action and the rest of the40* actions in a dropdown.41*42* Use `togglePrimaryAction` to promote43* the action that was last used to be44* the primary action and remember that45* choice.46*/47isSplitButton?: boolean | {48/**49* Will update the primary action based50* on the action that was last run.51*/52togglePrimaryAction: true;53};54}5556export function isIMenuItem(item: unknown): item is IMenuItem {57return (item as IMenuItem).command !== undefined;58}5960export function isISubmenuItem(item: unknown): item is ISubmenuItem {61return (item as ISubmenuItem).submenu !== undefined;62}6364export class MenuId {6566private static readonly _instances = new Map<string, MenuId>();6768static readonly CommandPalette = new MenuId('CommandPalette');69static readonly DebugBreakpointsContext = new MenuId('DebugBreakpointsContext');70static readonly DebugCallStackContext = new MenuId('DebugCallStackContext');71static readonly DebugConsoleContext = new MenuId('DebugConsoleContext');72static readonly DebugVariablesContext = new MenuId('DebugVariablesContext');73static readonly NotebookVariablesContext = new MenuId('NotebookVariablesContext');74static readonly DebugHoverContext = new MenuId('DebugHoverContext');75static readonly DebugWatchContext = new MenuId('DebugWatchContext');76static readonly DebugToolBar = new MenuId('DebugToolBar');77static readonly DebugToolBarStop = new MenuId('DebugToolBarStop');78static readonly DebugDisassemblyContext = new MenuId('DebugDisassemblyContext');79static readonly DebugCallStackToolbar = new MenuId('DebugCallStackToolbar');80static readonly DebugCreateConfiguration = new MenuId('DebugCreateConfiguration');81static readonly DebugScopesContext = new MenuId('DebugScopesContext');82static readonly EditorContext = new MenuId('EditorContext');83static readonly SimpleEditorContext = new MenuId('SimpleEditorContext');84static readonly EditorContent = new MenuId('EditorContent');85static readonly EditorLineNumberContext = new MenuId('EditorLineNumberContext');86static readonly EditorContextCopy = new MenuId('EditorContextCopy');87static readonly EditorContextPeek = new MenuId('EditorContextPeek');88static readonly EditorContextShare = new MenuId('EditorContextShare');89static readonly EditorTitle = new MenuId('EditorTitle');90static readonly ModalEditorTitle = new MenuId('ModalEditorTitle');91static readonly CompactWindowEditorTitle = new MenuId('CompactWindowEditorTitle');92static readonly EditorTitleRun = new MenuId('EditorTitleRun');93static readonly EditorTitleContext = new MenuId('EditorTitleContext');94static readonly EditorTitleContextShare = new MenuId('EditorTitleContextShare');95static readonly EmptyEditorGroup = new MenuId('EmptyEditorGroup');96static readonly EmptyEditorGroupContext = new MenuId('EmptyEditorGroupContext');97static readonly EditorTabsBarContext = new MenuId('EditorTabsBarContext');98static readonly EditorTabsBarShowTabsSubmenu = new MenuId('EditorTabsBarShowTabsSubmenu');99static readonly EditorTabsBarShowTabsZenModeSubmenu = new MenuId('EditorTabsBarShowTabsZenModeSubmenu');100static readonly EditorActionsPositionSubmenu = new MenuId('EditorActionsPositionSubmenu');101static readonly EditorSplitMoveSubmenu = new MenuId('EditorSplitMoveSubmenu');102static readonly ExplorerContext = new MenuId('ExplorerContext');103static readonly ExplorerContextShare = new MenuId('ExplorerContextShare');104static readonly ExtensionContext = new MenuId('ExtensionContext');105static readonly ExtensionEditorContextMenu = new MenuId('ExtensionEditorContextMenu');106static readonly GlobalActivity = new MenuId('GlobalActivity');107static readonly CommandCenter = new MenuId('CommandCenter');108static readonly CommandCenterCenter = new MenuId('CommandCenterCenter');109static readonly LayoutControlMenuSubmenu = new MenuId('LayoutControlMenuSubmenu');110static readonly LayoutControlMenu = new MenuId('LayoutControlMenu');111static readonly MenubarMainMenu = new MenuId('MenubarMainMenu');112static readonly MenubarAppearanceMenu = new MenuId('MenubarAppearanceMenu');113static readonly MenubarDebugMenu = new MenuId('MenubarDebugMenu');114static readonly MenubarEditMenu = new MenuId('MenubarEditMenu');115static readonly MenubarCopy = new MenuId('MenubarCopy');116static readonly MenubarFileMenu = new MenuId('MenubarFileMenu');117static readonly MenubarGoMenu = new MenuId('MenubarGoMenu');118static readonly MenubarHelpMenu = new MenuId('MenubarHelpMenu');119static readonly MenubarLayoutMenu = new MenuId('MenubarLayoutMenu');120static readonly MenubarNewBreakpointMenu = new MenuId('MenubarNewBreakpointMenu');121static readonly PanelAlignmentMenu = new MenuId('PanelAlignmentMenu');122static readonly PanelPositionMenu = new MenuId('PanelPositionMenu');123static readonly ActivityBarPositionMenu = new MenuId('ActivityBarPositionMenu');124static readonly MenubarPreferencesMenu = new MenuId('MenubarPreferencesMenu');125static readonly MenubarRecentMenu = new MenuId('MenubarRecentMenu');126static readonly MenubarSelectionMenu = new MenuId('MenubarSelectionMenu');127static readonly MenubarShare = new MenuId('MenubarShare');128static readonly MenubarSwitchEditorMenu = new MenuId('MenubarSwitchEditorMenu');129static readonly MenubarSwitchGroupMenu = new MenuId('MenubarSwitchGroupMenu');130static readonly MenubarTerminalMenu = new MenuId('MenubarTerminalMenu');131static readonly MenubarTerminalSuggestStatusMenu = new MenuId('MenubarTerminalSuggestStatusMenu');132static readonly MenubarViewMenu = new MenuId('MenubarViewMenu');133static readonly MenubarHomeMenu = new MenuId('MenubarHomeMenu');134static readonly OpenEditorsContext = new MenuId('OpenEditorsContext');135static readonly OpenEditorsContextShare = new MenuId('OpenEditorsContextShare');136static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext');137static readonly SCMInputBox = new MenuId('SCMInputBox');138static readonly SCMChangeContext = new MenuId('SCMChangeContext');139static readonly SCMResourceContext = new MenuId('SCMResourceContext');140static readonly SCMResourceContextShare = new MenuId('SCMResourceContextShare');141static readonly SCMResourceFolderContext = new MenuId('SCMResourceFolderContext');142static readonly SCMResourceGroupContext = new MenuId('SCMResourceGroupContext');143static readonly SCMSourceControl = new MenuId('SCMSourceControl');144static readonly SCMSourceControlInline = new MenuId('SCMSourceControlInline');145static readonly SCMSourceControlTitle = new MenuId('SCMSourceControlTitle');146static readonly SCMHistoryTitle = new MenuId('SCMHistoryTitle');147static readonly SCMHistoryItemContext = new MenuId('SCMHistoryItemContext');148static readonly SCMHistoryItemChangeContext = new MenuId('SCMHistoryItemChangeContext');149static readonly SCMHistoryItemRefContext = new MenuId('SCMHistoryItemRefContext');150static readonly SCMArtifactGroupContext = new MenuId('SCMArtifactGroupContext');151static readonly SCMArtifactContext = new MenuId('SCMArtifactContext');152static readonly SCMQuickDiffDecorations = new MenuId('SCMQuickDiffDecorations');153static readonly SCMTitle = new MenuId('SCMTitle');154static readonly SearchContext = new MenuId('SearchContext');155static readonly SearchActionMenu = new MenuId('SearchActionContext');156static readonly StatusBarWindowIndicatorMenu = new MenuId('StatusBarWindowIndicatorMenu');157static readonly StatusBarRemoteIndicatorMenu = new MenuId('StatusBarRemoteIndicatorMenu');158static readonly StickyScrollContext = new MenuId('StickyScrollContext');159static readonly TestItem = new MenuId('TestItem');160static readonly TestItemGutter = new MenuId('TestItemGutter');161static readonly TestProfilesContext = new MenuId('TestProfilesContext');162static readonly TestMessageContext = new MenuId('TestMessageContext');163static readonly TestMessageContent = new MenuId('TestMessageContent');164static readonly TestPeekElement = new MenuId('TestPeekElement');165static readonly TestPeekTitle = new MenuId('TestPeekTitle');166static readonly TestCallStack = new MenuId('TestCallStack');167static readonly TestCoverageFilterItem = new MenuId('TestCoverageFilterItem');168static readonly TouchBarContext = new MenuId('TouchBarContext');169static readonly TitleBar = new MenuId('TitleBar');170static readonly TitleBarContext = new MenuId('TitleBarContext');171static readonly TitleBarTitleContext = new MenuId('TitleBarTitleContext');172static readonly TunnelContext = new MenuId('TunnelContext');173static readonly TunnelPrivacy = new MenuId('TunnelPrivacy');174static readonly TunnelProtocol = new MenuId('TunnelProtocol');175static readonly TunnelPortInline = new MenuId('TunnelInline');176static readonly TunnelTitle = new MenuId('TunnelTitle');177static readonly TunnelLocalAddressInline = new MenuId('TunnelLocalAddressInline');178static readonly TunnelOriginInline = new MenuId('TunnelOriginInline');179static readonly ViewItemContext = new MenuId('ViewItemContext');180static readonly ViewContainerTitle = new MenuId('ViewContainerTitle');181static readonly ViewContainerTitleContext = new MenuId('ViewContainerTitleContext');182static readonly ViewTitle = new MenuId('ViewTitle');183static readonly ViewTitleContext = new MenuId('ViewTitleContext');184static readonly CommentEditorActions = new MenuId('CommentEditorActions');185static readonly CommentThreadTitle = new MenuId('CommentThreadTitle');186static readonly CommentThreadActions = new MenuId('CommentThreadActions');187static readonly CommentThreadAdditionalActions = new MenuId('CommentThreadAdditionalActions');188static readonly CommentThreadTitleContext = new MenuId('CommentThreadTitleContext');189static readonly CommentThreadCommentContext = new MenuId('CommentThreadCommentContext');190static readonly CommentTitle = new MenuId('CommentTitle');191static readonly CommentActions = new MenuId('CommentActions');192static readonly CommentsViewThreadActions = new MenuId('CommentsViewThreadActions');193static readonly InteractiveToolbar = new MenuId('InteractiveToolbar');194static readonly InteractiveCellTitle = new MenuId('InteractiveCellTitle');195static readonly InteractiveCellDelete = new MenuId('InteractiveCellDelete');196static readonly InteractiveCellExecute = new MenuId('InteractiveCellExecute');197static readonly InteractiveInputExecute = new MenuId('InteractiveInputExecute');198static readonly InteractiveInputConfig = new MenuId('InteractiveInputConfig');199static readonly ReplInputExecute = new MenuId('ReplInputExecute');200static readonly IssueReporter = new MenuId('IssueReporter');201static readonly NotebookToolbar = new MenuId('NotebookToolbar');202static readonly NotebookToolbarContext = new MenuId('NotebookToolbarContext');203static readonly NotebookStickyScrollContext = new MenuId('NotebookStickyScrollContext');204static readonly NotebookCellTitle = new MenuId('NotebookCellTitle');205static readonly NotebookCellDelete = new MenuId('NotebookCellDelete');206static readonly NotebookCellInsert = new MenuId('NotebookCellInsert');207static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');208static readonly NotebookCellListTop = new MenuId('NotebookCellTop');209static readonly NotebookCellExecute = new MenuId('NotebookCellExecute');210static readonly NotebookCellExecuteGoTo = new MenuId('NotebookCellExecuteGoTo');211static readonly NotebookCellExecutePrimary = new MenuId('NotebookCellExecutePrimary');212static readonly NotebookDiffCellInputTitle = new MenuId('NotebookDiffCellInputTitle');213static readonly NotebookDiffDocumentMetadata = new MenuId('NotebookDiffDocumentMetadata');214static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle');215static readonly NotebookDiffCellOutputsTitle = new MenuId('NotebookDiffCellOutputsTitle');216static readonly NotebookOutputToolbar = new MenuId('NotebookOutputToolbar');217static readonly NotebookOutlineFilter = new MenuId('NotebookOutlineFilter');218static readonly NotebookOutlineActionMenu = new MenuId('NotebookOutlineActionMenu');219static readonly NotebookEditorLayoutConfigure = new MenuId('NotebookEditorLayoutConfigure');220static readonly NotebookKernelSource = new MenuId('NotebookKernelSource');221static readonly BulkEditTitle = new MenuId('BulkEditTitle');222static readonly BulkEditContext = new MenuId('BulkEditContext');223static readonly TimelineItemContext = new MenuId('TimelineItemContext');224static readonly TimelineTitle = new MenuId('TimelineTitle');225static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');226static readonly TimelineFilterSubMenu = new MenuId('TimelineFilterSubMenu');227static readonly AccountsContext = new MenuId('AccountsContext');228static readonly SidebarTitle = new MenuId('SidebarTitle');229static readonly PanelTitle = new MenuId('PanelTitle');230static readonly AuxiliaryBarTitle = new MenuId('AuxiliaryBarTitle');231static readonly TerminalInstanceContext = new MenuId('TerminalInstanceContext');232static readonly TerminalEditorInstanceContext = new MenuId('TerminalEditorInstanceContext');233static readonly TerminalNewDropdownContext = new MenuId('TerminalNewDropdownContext');234static readonly TerminalTabContext = new MenuId('TerminalTabContext');235static readonly TerminalTabEmptyAreaContext = new MenuId('TerminalTabEmptyAreaContext');236static readonly TerminalStickyScrollContext = new MenuId('TerminalStickyScrollContext');237static readonly WebviewContext = new MenuId('WebviewContext');238static readonly InlineCompletionsActions = new MenuId('InlineCompletionsActions');239static readonly InlineEditsActions = new MenuId('InlineEditsActions');240static readonly NewFile = new MenuId('NewFile');241static readonly MergeInput1Toolbar = new MenuId('MergeToolbar1Toolbar');242static readonly MergeInput2Toolbar = new MenuId('MergeToolbar2Toolbar');243static readonly MergeBaseToolbar = new MenuId('MergeBaseToolbar');244static readonly MergeInputResultToolbar = new MenuId('MergeToolbarResultToolbar');245static readonly InlineSuggestionToolbar = new MenuId('InlineSuggestionToolbar');246static readonly InlineEditToolbar = new MenuId('InlineEditToolbar');247static readonly ChatContext = new MenuId('ChatContext');248static readonly ChatCodeBlock = new MenuId('ChatCodeblock');249static readonly ChatCompareBlock = new MenuId('ChatCompareBlock');250static readonly ChatMessageTitle = new MenuId('ChatMessageTitle');251static readonly ChatWelcomeContext = new MenuId('ChatWelcomeContext');252static readonly ChatMessageFooter = new MenuId('ChatMessageFooter');253static readonly ChatExecute = new MenuId('ChatExecute');254static readonly ChatExecuteQueue = new MenuId('ChatExecuteQueue');255static readonly ChatInput = new MenuId('ChatInput');256static readonly ChatInputSide = new MenuId('ChatInputSide');257static readonly ChatModePicker = new MenuId('ChatModePicker');258static readonly ChatEditingWidgetToolbar = new MenuId('ChatEditingWidgetToolbar');259static readonly ChatEditingSessionChangesToolbar = new MenuId('ChatEditingSessionChangesToolbar');260static readonly ChatEditingEditorContent = new MenuId('ChatEditingEditorContent');261static readonly ChatEditingEditorHunk = new MenuId('ChatEditingEditorHunk');262static readonly ChatEditingDeletedNotebookCell = new MenuId('ChatEditingDeletedNotebookCell');263static readonly ChatInputAttachmentToolbar = new MenuId('ChatInputAttachmentToolbar');264static readonly ChatEditingWidgetModifiedFilesToolbar = new MenuId('ChatEditingWidgetModifiedFilesToolbar');265static readonly ChatInputResourceAttachmentContext = new MenuId('ChatInputResourceAttachmentContext');266static readonly ChatInputSymbolAttachmentContext = new MenuId('ChatInputSymbolAttachmentContext');267static readonly ChatInlineResourceAnchorContext = new MenuId('ChatInlineResourceAnchorContext');268static readonly ChatInlineSymbolAnchorContext = new MenuId('ChatInlineSymbolAnchorContext');269static readonly ChatMessageCheckpoint: MenuId = new MenuId('ChatMessageCheckpoint');270static readonly ChatMessageRestoreCheckpoint: MenuId = new MenuId('ChatMessageRestoreCheckpoint');271static readonly ChatNewMenu = new MenuId('ChatNewMenu');272static readonly ChatEditingCodeBlockContext = new MenuId('ChatEditingCodeBlockContext');273static readonly ChatTitleBarMenu = new MenuId('ChatTitleBarMenu');274static readonly ChatAttachmentsContext = new MenuId('ChatAttachmentsContext');275static readonly ChatTipContext = new MenuId('ChatTipContext');276static readonly ChatToolOutputResourceToolbar = new MenuId('ChatToolOutputResourceToolbar');277static readonly ChatTextEditorMenu = new MenuId('ChatTextEditorMenu');278static readonly ChatToolOutputResourceContext = new MenuId('ChatToolOutputResourceContext');279static readonly ChatMultiDiffContext = new MenuId('ChatMultiDiffContext');280static readonly ChatConfirmationMenu = new MenuId('ChatConfirmationMenu');281static readonly ChatEditorInlineGutter = new MenuId('ChatEditorInlineGutter');282static readonly ChatEditorInlineExecute = new MenuId('ChatEditorInputExecute');283static readonly ChatEditorInlineInputSide = new MenuId('ChatEditorInputSide');284static readonly InlineChatEditorAffordance = new MenuId('InlineChatEditorAffordance');285static readonly AccessibleView = new MenuId('AccessibleView');286static readonly MultiDiffEditorContent = new MenuId('MultiDiffEditorContent');287static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar');288static readonly DiffEditorHunkToolbar = new MenuId('DiffEditorHunkToolbar');289static readonly DiffEditorSelectionToolbar = new MenuId('DiffEditorSelectionToolbar');290static readonly BrowserNavigationToolbar = new MenuId('BrowserNavigationToolbar');291static readonly BrowserActionsToolbar = new MenuId('BrowserActionsToolbar');292static readonly AgentSessionsViewerFilterSubMenu = new MenuId('AgentSessionsViewerFilterSubMenu');293static readonly AgentSessionsContext = new MenuId('AgentSessionsContext');294static readonly AgentSessionSectionContext = new MenuId('AgentSessionSectionContext');295static readonly AgentSessionsCreateSubMenu = new MenuId('AgentSessionsCreateSubMenu');296static readonly AgentSessionsToolbar = new MenuId('AgentSessionsToolbar');297static readonly AgentSessionItemToolbar = new MenuId('AgentSessionItemToolbar');298static readonly AgentSessionSectionToolbar = new MenuId('AgentSessionSectionToolbar');299static readonly AgentsTitleBarControlMenu = new MenuId('AgentsTitleBarControlMenu');300static readonly ChatViewSessionTitleNavigationToolbar = new MenuId('ChatViewSessionTitleNavigationToolbar');301static readonly ChatViewSessionTitleToolbar = new MenuId('ChatViewSessionTitleToolbar');302static readonly ChatContextUsageActions = new MenuId('ChatContextUsageActions');303304/**305* Create or reuse a `MenuId` with the given identifier306*/307static for(identifier: string): MenuId {308return MenuId._instances.get(identifier) ?? new MenuId(identifier);309}310311readonly id: string;312313/**314* Create a new `MenuId` with the unique identifier. Will throw if a menu315* with the identifier already exists, use `MenuId.for(ident)` or a unique316* identifier317*/318constructor(identifier: string) {319if (MenuId._instances.has(identifier)) {320throw new TypeError(`MenuId with identifier '${identifier}' already exists. Use MenuId.for(ident) or a unique identifier`);321}322MenuId._instances.set(identifier, this);323this.id = identifier;324}325}326327export interface IMenuActionOptions {328arg?: unknown;329args?: unknown[];330shouldForwardArgs?: boolean;331renderShortTitle?: boolean;332}333334export interface IMenuChangeEvent {335readonly menu: IMenu;336readonly isStructuralChange: boolean;337readonly isToggleChange: boolean;338readonly isEnablementChange: boolean;339}340341export interface IMenu extends IDisposable {342readonly onDidChange: Event<IMenuChangeEvent>;343getActions(options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][];344}345346export interface IMenuData {347contexts: ReadonlySet<string>;348actions: [string, Array<MenuItemAction | SubmenuItemAction>][];349}350351export const IMenuService = createDecorator<IMenuService>('menuService');352353export interface IMenuCreateOptions {354emitEventsForSubmenuChanges?: boolean;355eventDebounceDelay?: number;356}357358export interface IMenuService {359360readonly _serviceBrand: undefined;361362/**363* Consider using getMenuActions if you don't need to listen to events.364*365* Create a new menu for the given menu identifier. A menu sends events when it's entries366* have changed (placement, enablement, checked-state). By default it does not send events for367* submenu entries. That is more expensive and must be explicitly enabled with the368* `emitEventsForSubmenuChanges` flag.369*/370createMenu(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuCreateOptions): IMenu;371372/**373* Creates a new menu, gets the actions, and then disposes of the menu.374*/375getMenuActions(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][];376377/**378* Gets the names of the contexts that this menu listens on.379*/380getMenuContexts(id: MenuId): ReadonlySet<string>;381382/**383* Reset **all** menu item hidden states.384*/385resetHiddenStates(): void;386387/**388* Reset the menu's hidden states.389*/390resetHiddenStates(menuIds: readonly MenuId[] | undefined): void;391}392393type ICommandsMap = Map<string, ICommandAction>;394395export interface IMenuRegistryChangeEvent {396has(id: MenuId): boolean;397}398399class MenuRegistryChangeEvent {400401private static _all = new Map<MenuId, MenuRegistryChangeEvent>();402403static for(id: MenuId): MenuRegistryChangeEvent {404let value = this._all.get(id);405if (!value) {406value = new MenuRegistryChangeEvent(id);407this._all.set(id, value);408}409return value;410}411412static merge(events: IMenuRegistryChangeEvent[]): IMenuRegistryChangeEvent {413const ids = new Set<MenuId>();414for (const item of events) {415if (item instanceof MenuRegistryChangeEvent) {416ids.add(item.id);417}418}419return ids;420}421422readonly has: (id: MenuId) => boolean;423424private constructor(private readonly id: MenuId) {425this.has = candidate => candidate === id;426}427}428429export interface IMenuRegistry {430readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent>;431addCommand(userCommand: ICommandAction): IDisposable;432getCommand(id: string): ICommandAction | undefined;433getCommands(): ICommandsMap;434435/**436* @deprecated Use `appendMenuItem` or most likely use `registerAction2` instead. There should be no strong437* reason to use this directly.438*/439appendMenuItems(items: Iterable<{ id: MenuId; item: IMenuItem | ISubmenuItem }>): IDisposable;440appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable;441getMenuItems(loc: MenuId): Array<IMenuItem | ISubmenuItem>;442}443444export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {445446private readonly _commands = new Map<string, ICommandAction>();447private readonly _menuItems = new Map<MenuId, LinkedList<IMenuItem | ISubmenuItem>>();448private readonly _onDidChangeMenu = new MicrotaskEmitter<IMenuRegistryChangeEvent>({449merge: MenuRegistryChangeEvent.merge450});451452readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent> = this._onDidChangeMenu.event;453454addCommand(command: ICommandAction): IDisposable {455this._commands.set(command.id, command);456this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette));457458return markAsSingleton(toDisposable(() => {459if (this._commands.delete(command.id)) {460this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette));461}462}));463}464465getCommand(id: string): ICommandAction | undefined {466return this._commands.get(id);467}468469getCommands(): ICommandsMap {470const map = new Map<string, ICommandAction>();471this._commands.forEach((value, key) => map.set(key, value));472return map;473}474475appendMenuItem(id: MenuId, item: IMenuItem | ISubmenuItem): IDisposable {476let list = this._menuItems.get(id);477if (!list) {478list = new LinkedList();479this._menuItems.set(id, list);480}481const rm = list.push(item);482this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id));483return markAsSingleton(toDisposable(() => {484rm();485this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id));486}));487}488489appendMenuItems(items: Iterable<{ id: MenuId; item: IMenuItem | ISubmenuItem }>): IDisposable {490const result = new DisposableStore();491for (const { id, item } of items) {492result.add(this.appendMenuItem(id, item));493}494return result;495}496497getMenuItems(id: MenuId): Array<IMenuItem | ISubmenuItem> {498let result: Array<IMenuItem | ISubmenuItem>;499if (this._menuItems.has(id)) {500result = [...this._menuItems.get(id)!];501} else {502result = [];503}504if (id === MenuId.CommandPalette) {505// CommandPalette is special because it shows506// all commands by default507this._appendImplicitItems(result);508}509return result;510}511512private _appendImplicitItems(result: Array<IMenuItem | ISubmenuItem>) {513const set = new Set<string>();514515for (const item of result) {516if (isIMenuItem(item)) {517set.add(item.command.id);518if (item.alt) {519set.add(item.alt.id);520}521}522}523this._commands.forEach((command, id) => {524if (!set.has(id)) {525result.push({ command });526}527});528}529};530531export class SubmenuItemAction extends SubmenuAction {532533constructor(534readonly item: ISubmenuItem,535readonly hideActions: IMenuItemHide | undefined,536actions: readonly IAction[],537) {538super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, actions, 'submenu');539}540}541542export interface IMenuItemHide {543readonly isHidden: boolean;544readonly hide: IAction;545readonly toggle: IAction;546}547548// implements IAction, does NOT extend Action, so that no one549// subscribes to events of Action or modified properties550export class MenuItemAction implements IAction {551552static label(action: ICommandAction, options?: IMenuActionOptions): string {553return options?.renderShortTitle && action.shortTitle554? (typeof action.shortTitle === 'string' ? action.shortTitle : action.shortTitle.value)555: (typeof action.title === 'string' ? action.title : action.title.value);556}557558readonly item: ICommandAction;559readonly alt: MenuItemAction | undefined;560561private readonly _options: IMenuActionOptions | undefined;562563readonly id: string;564readonly label: string;565readonly tooltip: string;566readonly class: string | undefined;567readonly enabled: boolean;568readonly checked?: boolean;569570constructor(571item: ICommandAction,572alt: ICommandAction | undefined,573options: IMenuActionOptions | undefined,574readonly hideActions: IMenuItemHide | undefined,575readonly menuKeybinding: IAction | undefined,576@IContextKeyService contextKeyService: IContextKeyService,577@ICommandService private _commandService: ICommandService578) {579this.id = item.id;580this.label = MenuItemAction.label(item, options);581this.tooltip = (typeof item.tooltip === 'string' ? item.tooltip : item.tooltip?.value) ?? '';582this.enabled = !item.precondition || contextKeyService.contextMatchesRules(item.precondition);583this.checked = undefined;584585let icon: ThemeIcon | undefined;586587if (item.toggled) {588const toggled = ((item.toggled as { condition: ContextKeyExpression }).condition ? item.toggled : { condition: item.toggled }) as {589condition: ContextKeyExpression; icon?: Icon; tooltip?: string | ILocalizedString; title?: string | ILocalizedString;590};591this.checked = contextKeyService.contextMatchesRules(toggled.condition);592if (this.checked && toggled.tooltip) {593this.tooltip = typeof toggled.tooltip === 'string' ? toggled.tooltip : toggled.tooltip.value;594}595596if (this.checked && ThemeIcon.isThemeIcon(toggled.icon)) {597icon = toggled.icon;598}599600if (this.checked && toggled.title) {601this.label = typeof toggled.title === 'string' ? toggled.title : toggled.title.value;602}603}604605if (!icon) {606icon = ThemeIcon.isThemeIcon(item.icon) ? item.icon : undefined;607}608609this.item = item;610this.alt = alt ? new MenuItemAction(alt, undefined, options, hideActions, undefined, contextKeyService, _commandService) : undefined;611this._options = options;612this.class = icon && ThemeIcon.asClassName(icon);613614}615616run(...args: unknown[]): Promise<void> {617let runArgs: unknown[] = [];618619if (this._options?.args) {620runArgs = [...runArgs, ...this._options.args];621} else if (this._options?.arg) {622runArgs = [...runArgs, this._options.arg];623}624625if (this._options?.shouldForwardArgs) {626runArgs = [...runArgs, ...args];627}628629return this._commandService.executeCommand(this.id, ...runArgs);630}631}632633//#region --- IAction2634635type OneOrN<T> = T | T[];636637interface IAction2CommonOptions extends ICommandAction {638/**639* One or many menu items.640*/641menu?: OneOrN<{ id: MenuId; precondition?: null } & Omit<IMenuItem, 'command'>>;642643/**644* One keybinding.645*/646keybinding?: OneOrN<Omit<IKeybindingRule, 'id'>>;647}648649interface IBaseAction2Options extends IAction2CommonOptions {650651/**652* This type is used when an action is not going to show up in the command palette.653* In that case, it's able to use a string for the `title` and `category` properties.654*/655f1?: false;656}657658export interface ICommandPaletteOptions extends IAction2CommonOptions {659660/**661* The title of the command that will be displayed in the command palette after the category.662* This overrides {@link ICommandAction.title} to ensure a string isn't used so that the title663* includes the localized value and the original value for users using language packs.664*/665title: ICommandActionTitle;666667/**668* The category of the command that will be displayed in the command palette before the title suffixed.669* with a colon This overrides {@link ICommandAction.title} to ensure a string isn't used so that670* the title includes the localized value and the original value for users using language packs.671*/672category?: keyof typeof Categories | ILocalizedString;673674/**675* Shorthand to add this command to the command palette. Note: this is not the only way to declare that676* a command should be in the command palette... however, enforcing ILocalizedString in the other scenarios677* is much more challenging and this gets us most of the way there.678*/679f1: true;680}681682export type IAction2Options = ICommandPaletteOptions | IBaseAction2Options;683684export interface IAction2F1RequiredOptions {685title: ICommandActionTitle;686category?: keyof typeof Categories | ILocalizedString;687}688689export abstract class Action2 {690constructor(readonly desc: Readonly<IAction2Options>) { }691abstract run(accessor: ServicesAccessor, ...args: unknown[]): void;692}693694export function registerAction2(ctor: { new(): Action2 }): IDisposable {695const disposables: IDisposable[] = []; // not using `DisposableStore` to reduce startup perf cost696const action = new ctor();697698const { f1, menu, keybinding, ...command } = action.desc;699700if (CommandsRegistry.getCommand(command.id)) {701throw new Error(`Cannot register two commands with the same id: ${command.id}`);702}703704// command705disposables.push(CommandsRegistry.registerCommand({706id: command.id,707handler: (accessor, ...args) => action.run(accessor, ...args),708metadata: command.metadata ?? { description: action.desc.title }709}));710711// menu712if (Array.isArray(menu)) {713for (const item of menu) {714disposables.push(MenuRegistry.appendMenuItem(item.id, { command: { ...command, precondition: item.precondition === null ? undefined : command.precondition }, ...item }));715}716717} else if (menu) {718disposables.push(MenuRegistry.appendMenuItem(menu.id, { command: { ...command, precondition: menu.precondition === null ? undefined : command.precondition }, ...menu }));719}720if (f1) {721disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when: command.precondition }));722disposables.push(MenuRegistry.addCommand(command));723}724725// keybinding726if (Array.isArray(keybinding)) {727for (const item of keybinding) {728disposables.push(KeybindingsRegistry.registerKeybindingRule({729...item,730id: command.id,731when: command.precondition ? ContextKeyExpr.and(command.precondition, item.when) : item.when732}));733}734} else if (keybinding) {735disposables.push(KeybindingsRegistry.registerKeybindingRule({736...keybinding,737id: command.id,738when: command.precondition ? ContextKeyExpr.and(command.precondition, keybinding.when) : keybinding.when739}));740}741742return {743dispose() {744dispose(disposables);745}746};747}748//#endregion749750751