Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/actions/common/actions.ts
5260 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IAction, SubmenuAction } from '../../../base/common/actions.js';
7
import { Event, MicrotaskEmitter } from '../../../base/common/event.js';
8
import { DisposableStore, dispose, IDisposable, markAsSingleton, toDisposable } from '../../../base/common/lifecycle.js';
9
import { LinkedList } from '../../../base/common/linkedList.js';
10
import { ThemeIcon } from '../../../base/common/themables.js';
11
import { ICommandAction, ICommandActionTitle, Icon, ILocalizedString } from '../../action/common/action.js';
12
import { Categories } from '../../action/common/actionCommonCategories.js';
13
import { CommandsRegistry, ICommandService } from '../../commands/common/commands.js';
14
import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from '../../contextkey/common/contextkey.js';
15
import { createDecorator, ServicesAccessor } from '../../instantiation/common/instantiation.js';
16
import { IKeybindingRule, KeybindingsRegistry } from '../../keybinding/common/keybindingsRegistry.js';
17
18
export interface IMenuItem {
19
command: ICommandAction;
20
alt?: ICommandAction;
21
/**
22
* Menu item is hidden if this expression returns false.
23
*/
24
when?: ContextKeyExpression;
25
group?: 'navigation' | string;
26
order?: number;
27
isHiddenByDefault?: boolean;
28
}
29
30
export interface ISubmenuItem {
31
title: string | ICommandActionTitle;
32
submenu: MenuId;
33
icon?: Icon;
34
when?: ContextKeyExpression;
35
group?: 'navigation' | string;
36
order?: number;
37
isSelection?: boolean;
38
/**
39
* A split button shows the first action
40
* as primary action and the rest of the
41
* actions in a dropdown.
42
*
43
* Use `togglePrimaryAction` to promote
44
* the action that was last used to be
45
* the primary action and remember that
46
* choice.
47
*/
48
isSplitButton?: boolean | {
49
/**
50
* Will update the primary action based
51
* on the action that was last run.
52
*/
53
togglePrimaryAction: true;
54
};
55
}
56
57
export function isIMenuItem(item: unknown): item is IMenuItem {
58
return (item as IMenuItem).command !== undefined;
59
}
60
61
export function isISubmenuItem(item: unknown): item is ISubmenuItem {
62
return (item as ISubmenuItem).submenu !== undefined;
63
}
64
65
export class MenuId {
66
67
private static readonly _instances = new Map<string, MenuId>();
68
69
static readonly CommandPalette = new MenuId('CommandPalette');
70
static readonly DebugBreakpointsContext = new MenuId('DebugBreakpointsContext');
71
static readonly DebugCallStackContext = new MenuId('DebugCallStackContext');
72
static readonly DebugConsoleContext = new MenuId('DebugConsoleContext');
73
static readonly DebugVariablesContext = new MenuId('DebugVariablesContext');
74
static readonly NotebookVariablesContext = new MenuId('NotebookVariablesContext');
75
static readonly DebugHoverContext = new MenuId('DebugHoverContext');
76
static readonly DebugWatchContext = new MenuId('DebugWatchContext');
77
static readonly DebugToolBar = new MenuId('DebugToolBar');
78
static readonly DebugToolBarStop = new MenuId('DebugToolBarStop');
79
static readonly DebugDisassemblyContext = new MenuId('DebugDisassemblyContext');
80
static readonly DebugCallStackToolbar = new MenuId('DebugCallStackToolbar');
81
static readonly DebugCreateConfiguration = new MenuId('DebugCreateConfiguration');
82
static readonly DebugScopesContext = new MenuId('DebugScopesContext');
83
static readonly EditorContext = new MenuId('EditorContext');
84
static readonly SimpleEditorContext = new MenuId('SimpleEditorContext');
85
static readonly EditorContent = new MenuId('EditorContent');
86
static readonly EditorLineNumberContext = new MenuId('EditorLineNumberContext');
87
static readonly EditorContextCopy = new MenuId('EditorContextCopy');
88
static readonly EditorContextPeek = new MenuId('EditorContextPeek');
89
static readonly EditorContextShare = new MenuId('EditorContextShare');
90
static readonly EditorTitle = new MenuId('EditorTitle');
91
static readonly ModalEditorTitle = new MenuId('ModalEditorTitle');
92
static readonly CompactWindowEditorTitle = new MenuId('CompactWindowEditorTitle');
93
static readonly EditorTitleRun = new MenuId('EditorTitleRun');
94
static readonly EditorTitleContext = new MenuId('EditorTitleContext');
95
static readonly EditorTitleContextShare = new MenuId('EditorTitleContextShare');
96
static readonly EmptyEditorGroup = new MenuId('EmptyEditorGroup');
97
static readonly EmptyEditorGroupContext = new MenuId('EmptyEditorGroupContext');
98
static readonly EditorTabsBarContext = new MenuId('EditorTabsBarContext');
99
static readonly EditorTabsBarShowTabsSubmenu = new MenuId('EditorTabsBarShowTabsSubmenu');
100
static readonly EditorTabsBarShowTabsZenModeSubmenu = new MenuId('EditorTabsBarShowTabsZenModeSubmenu');
101
static readonly EditorActionsPositionSubmenu = new MenuId('EditorActionsPositionSubmenu');
102
static readonly EditorSplitMoveSubmenu = new MenuId('EditorSplitMoveSubmenu');
103
static readonly ExplorerContext = new MenuId('ExplorerContext');
104
static readonly ExplorerContextShare = new MenuId('ExplorerContextShare');
105
static readonly ExtensionContext = new MenuId('ExtensionContext');
106
static readonly ExtensionEditorContextMenu = new MenuId('ExtensionEditorContextMenu');
107
static readonly GlobalActivity = new MenuId('GlobalActivity');
108
static readonly CommandCenter = new MenuId('CommandCenter');
109
static readonly CommandCenterCenter = new MenuId('CommandCenterCenter');
110
static readonly LayoutControlMenuSubmenu = new MenuId('LayoutControlMenuSubmenu');
111
static readonly LayoutControlMenu = new MenuId('LayoutControlMenu');
112
static readonly MenubarMainMenu = new MenuId('MenubarMainMenu');
113
static readonly MenubarAppearanceMenu = new MenuId('MenubarAppearanceMenu');
114
static readonly MenubarDebugMenu = new MenuId('MenubarDebugMenu');
115
static readonly MenubarEditMenu = new MenuId('MenubarEditMenu');
116
static readonly MenubarCopy = new MenuId('MenubarCopy');
117
static readonly MenubarFileMenu = new MenuId('MenubarFileMenu');
118
static readonly MenubarGoMenu = new MenuId('MenubarGoMenu');
119
static readonly MenubarHelpMenu = new MenuId('MenubarHelpMenu');
120
static readonly MenubarLayoutMenu = new MenuId('MenubarLayoutMenu');
121
static readonly MenubarNewBreakpointMenu = new MenuId('MenubarNewBreakpointMenu');
122
static readonly PanelAlignmentMenu = new MenuId('PanelAlignmentMenu');
123
static readonly PanelPositionMenu = new MenuId('PanelPositionMenu');
124
static readonly ActivityBarPositionMenu = new MenuId('ActivityBarPositionMenu');
125
static readonly MenubarPreferencesMenu = new MenuId('MenubarPreferencesMenu');
126
static readonly MenubarRecentMenu = new MenuId('MenubarRecentMenu');
127
static readonly MenubarSelectionMenu = new MenuId('MenubarSelectionMenu');
128
static readonly MenubarShare = new MenuId('MenubarShare');
129
static readonly MenubarSwitchEditorMenu = new MenuId('MenubarSwitchEditorMenu');
130
static readonly MenubarSwitchGroupMenu = new MenuId('MenubarSwitchGroupMenu');
131
static readonly MenubarTerminalMenu = new MenuId('MenubarTerminalMenu');
132
static readonly MenubarTerminalSuggestStatusMenu = new MenuId('MenubarTerminalSuggestStatusMenu');
133
static readonly MenubarViewMenu = new MenuId('MenubarViewMenu');
134
static readonly MenubarHomeMenu = new MenuId('MenubarHomeMenu');
135
static readonly OpenEditorsContext = new MenuId('OpenEditorsContext');
136
static readonly OpenEditorsContextShare = new MenuId('OpenEditorsContextShare');
137
static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext');
138
static readonly SCMInputBox = new MenuId('SCMInputBox');
139
static readonly SCMChangeContext = new MenuId('SCMChangeContext');
140
static readonly SCMResourceContext = new MenuId('SCMResourceContext');
141
static readonly SCMResourceContextShare = new MenuId('SCMResourceContextShare');
142
static readonly SCMResourceFolderContext = new MenuId('SCMResourceFolderContext');
143
static readonly SCMResourceGroupContext = new MenuId('SCMResourceGroupContext');
144
static readonly SCMSourceControl = new MenuId('SCMSourceControl');
145
static readonly SCMSourceControlInline = new MenuId('SCMSourceControlInline');
146
static readonly SCMSourceControlTitle = new MenuId('SCMSourceControlTitle');
147
static readonly SCMHistoryTitle = new MenuId('SCMHistoryTitle');
148
static readonly SCMHistoryItemContext = new MenuId('SCMHistoryItemContext');
149
static readonly SCMHistoryItemChangeContext = new MenuId('SCMHistoryItemChangeContext');
150
static readonly SCMHistoryItemRefContext = new MenuId('SCMHistoryItemRefContext');
151
static readonly SCMArtifactGroupContext = new MenuId('SCMArtifactGroupContext');
152
static readonly SCMArtifactContext = new MenuId('SCMArtifactContext');
153
static readonly SCMQuickDiffDecorations = new MenuId('SCMQuickDiffDecorations');
154
static readonly SCMTitle = new MenuId('SCMTitle');
155
static readonly SearchContext = new MenuId('SearchContext');
156
static readonly SearchActionMenu = new MenuId('SearchActionContext');
157
static readonly StatusBarWindowIndicatorMenu = new MenuId('StatusBarWindowIndicatorMenu');
158
static readonly StatusBarRemoteIndicatorMenu = new MenuId('StatusBarRemoteIndicatorMenu');
159
static readonly StickyScrollContext = new MenuId('StickyScrollContext');
160
static readonly TestItem = new MenuId('TestItem');
161
static readonly TestItemGutter = new MenuId('TestItemGutter');
162
static readonly TestProfilesContext = new MenuId('TestProfilesContext');
163
static readonly TestMessageContext = new MenuId('TestMessageContext');
164
static readonly TestMessageContent = new MenuId('TestMessageContent');
165
static readonly TestPeekElement = new MenuId('TestPeekElement');
166
static readonly TestPeekTitle = new MenuId('TestPeekTitle');
167
static readonly TestCallStack = new MenuId('TestCallStack');
168
static readonly TestCoverageFilterItem = new MenuId('TestCoverageFilterItem');
169
static readonly TouchBarContext = new MenuId('TouchBarContext');
170
static readonly TitleBar = new MenuId('TitleBar');
171
static readonly TitleBarContext = new MenuId('TitleBarContext');
172
static readonly TitleBarTitleContext = new MenuId('TitleBarTitleContext');
173
static readonly TunnelContext = new MenuId('TunnelContext');
174
static readonly TunnelPrivacy = new MenuId('TunnelPrivacy');
175
static readonly TunnelProtocol = new MenuId('TunnelProtocol');
176
static readonly TunnelPortInline = new MenuId('TunnelInline');
177
static readonly TunnelTitle = new MenuId('TunnelTitle');
178
static readonly TunnelLocalAddressInline = new MenuId('TunnelLocalAddressInline');
179
static readonly TunnelOriginInline = new MenuId('TunnelOriginInline');
180
static readonly ViewItemContext = new MenuId('ViewItemContext');
181
static readonly ViewContainerTitle = new MenuId('ViewContainerTitle');
182
static readonly ViewContainerTitleContext = new MenuId('ViewContainerTitleContext');
183
static readonly ViewTitle = new MenuId('ViewTitle');
184
static readonly ViewTitleContext = new MenuId('ViewTitleContext');
185
static readonly CommentEditorActions = new MenuId('CommentEditorActions');
186
static readonly CommentThreadTitle = new MenuId('CommentThreadTitle');
187
static readonly CommentThreadActions = new MenuId('CommentThreadActions');
188
static readonly CommentThreadAdditionalActions = new MenuId('CommentThreadAdditionalActions');
189
static readonly CommentThreadTitleContext = new MenuId('CommentThreadTitleContext');
190
static readonly CommentThreadCommentContext = new MenuId('CommentThreadCommentContext');
191
static readonly CommentTitle = new MenuId('CommentTitle');
192
static readonly CommentActions = new MenuId('CommentActions');
193
static readonly CommentsViewThreadActions = new MenuId('CommentsViewThreadActions');
194
static readonly InteractiveToolbar = new MenuId('InteractiveToolbar');
195
static readonly InteractiveCellTitle = new MenuId('InteractiveCellTitle');
196
static readonly InteractiveCellDelete = new MenuId('InteractiveCellDelete');
197
static readonly InteractiveCellExecute = new MenuId('InteractiveCellExecute');
198
static readonly InteractiveInputExecute = new MenuId('InteractiveInputExecute');
199
static readonly InteractiveInputConfig = new MenuId('InteractiveInputConfig');
200
static readonly ReplInputExecute = new MenuId('ReplInputExecute');
201
static readonly IssueReporter = new MenuId('IssueReporter');
202
static readonly NotebookToolbar = new MenuId('NotebookToolbar');
203
static readonly NotebookToolbarContext = new MenuId('NotebookToolbarContext');
204
static readonly NotebookStickyScrollContext = new MenuId('NotebookStickyScrollContext');
205
static readonly NotebookCellTitle = new MenuId('NotebookCellTitle');
206
static readonly NotebookCellDelete = new MenuId('NotebookCellDelete');
207
static readonly NotebookCellInsert = new MenuId('NotebookCellInsert');
208
static readonly NotebookCellBetween = new MenuId('NotebookCellBetween');
209
static readonly NotebookCellListTop = new MenuId('NotebookCellTop');
210
static readonly NotebookCellExecute = new MenuId('NotebookCellExecute');
211
static readonly NotebookCellExecuteGoTo = new MenuId('NotebookCellExecuteGoTo');
212
static readonly NotebookCellExecutePrimary = new MenuId('NotebookCellExecutePrimary');
213
static readonly NotebookDiffCellInputTitle = new MenuId('NotebookDiffCellInputTitle');
214
static readonly NotebookDiffDocumentMetadata = new MenuId('NotebookDiffDocumentMetadata');
215
static readonly NotebookDiffCellMetadataTitle = new MenuId('NotebookDiffCellMetadataTitle');
216
static readonly NotebookDiffCellOutputsTitle = new MenuId('NotebookDiffCellOutputsTitle');
217
static readonly NotebookOutputToolbar = new MenuId('NotebookOutputToolbar');
218
static readonly NotebookOutlineFilter = new MenuId('NotebookOutlineFilter');
219
static readonly NotebookOutlineActionMenu = new MenuId('NotebookOutlineActionMenu');
220
static readonly NotebookEditorLayoutConfigure = new MenuId('NotebookEditorLayoutConfigure');
221
static readonly NotebookKernelSource = new MenuId('NotebookKernelSource');
222
static readonly BulkEditTitle = new MenuId('BulkEditTitle');
223
static readonly BulkEditContext = new MenuId('BulkEditContext');
224
static readonly TimelineItemContext = new MenuId('TimelineItemContext');
225
static readonly TimelineTitle = new MenuId('TimelineTitle');
226
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
227
static readonly TimelineFilterSubMenu = new MenuId('TimelineFilterSubMenu');
228
static readonly AccountsContext = new MenuId('AccountsContext');
229
static readonly SidebarTitle = new MenuId('SidebarTitle');
230
static readonly PanelTitle = new MenuId('PanelTitle');
231
static readonly AuxiliaryBarTitle = new MenuId('AuxiliaryBarTitle');
232
static readonly TerminalInstanceContext = new MenuId('TerminalInstanceContext');
233
static readonly TerminalEditorInstanceContext = new MenuId('TerminalEditorInstanceContext');
234
static readonly TerminalNewDropdownContext = new MenuId('TerminalNewDropdownContext');
235
static readonly TerminalTabContext = new MenuId('TerminalTabContext');
236
static readonly TerminalTabEmptyAreaContext = new MenuId('TerminalTabEmptyAreaContext');
237
static readonly TerminalStickyScrollContext = new MenuId('TerminalStickyScrollContext');
238
static readonly WebviewContext = new MenuId('WebviewContext');
239
static readonly InlineCompletionsActions = new MenuId('InlineCompletionsActions');
240
static readonly InlineEditsActions = new MenuId('InlineEditsActions');
241
static readonly NewFile = new MenuId('NewFile');
242
static readonly MergeInput1Toolbar = new MenuId('MergeToolbar1Toolbar');
243
static readonly MergeInput2Toolbar = new MenuId('MergeToolbar2Toolbar');
244
static readonly MergeBaseToolbar = new MenuId('MergeBaseToolbar');
245
static readonly MergeInputResultToolbar = new MenuId('MergeToolbarResultToolbar');
246
static readonly InlineSuggestionToolbar = new MenuId('InlineSuggestionToolbar');
247
static readonly InlineEditToolbar = new MenuId('InlineEditToolbar');
248
static readonly ChatContext = new MenuId('ChatContext');
249
static readonly ChatCodeBlock = new MenuId('ChatCodeblock');
250
static readonly ChatCompareBlock = new MenuId('ChatCompareBlock');
251
static readonly ChatMessageTitle = new MenuId('ChatMessageTitle');
252
static readonly ChatWelcomeContext = new MenuId('ChatWelcomeContext');
253
static readonly ChatMessageFooter = new MenuId('ChatMessageFooter');
254
static readonly ChatExecute = new MenuId('ChatExecute');
255
static readonly ChatExecuteQueue = new MenuId('ChatExecuteQueue');
256
static readonly ChatInput = new MenuId('ChatInput');
257
static readonly ChatInputSide = new MenuId('ChatInputSide');
258
static readonly ChatModePicker = new MenuId('ChatModePicker');
259
static readonly ChatEditingWidgetToolbar = new MenuId('ChatEditingWidgetToolbar');
260
static readonly ChatEditingSessionChangesToolbar = new MenuId('ChatEditingSessionChangesToolbar');
261
static readonly ChatEditingEditorContent = new MenuId('ChatEditingEditorContent');
262
static readonly ChatEditingEditorHunk = new MenuId('ChatEditingEditorHunk');
263
static readonly ChatEditingDeletedNotebookCell = new MenuId('ChatEditingDeletedNotebookCell');
264
static readonly ChatInputAttachmentToolbar = new MenuId('ChatInputAttachmentToolbar');
265
static readonly ChatEditingWidgetModifiedFilesToolbar = new MenuId('ChatEditingWidgetModifiedFilesToolbar');
266
static readonly ChatInputResourceAttachmentContext = new MenuId('ChatInputResourceAttachmentContext');
267
static readonly ChatInputSymbolAttachmentContext = new MenuId('ChatInputSymbolAttachmentContext');
268
static readonly ChatInlineResourceAnchorContext = new MenuId('ChatInlineResourceAnchorContext');
269
static readonly ChatInlineSymbolAnchorContext = new MenuId('ChatInlineSymbolAnchorContext');
270
static readonly ChatMessageCheckpoint: MenuId = new MenuId('ChatMessageCheckpoint');
271
static readonly ChatMessageRestoreCheckpoint: MenuId = new MenuId('ChatMessageRestoreCheckpoint');
272
static readonly ChatNewMenu = new MenuId('ChatNewMenu');
273
static readonly ChatEditingCodeBlockContext = new MenuId('ChatEditingCodeBlockContext');
274
static readonly ChatTitleBarMenu = new MenuId('ChatTitleBarMenu');
275
static readonly ChatAttachmentsContext = new MenuId('ChatAttachmentsContext');
276
static readonly ChatTipContext = new MenuId('ChatTipContext');
277
static readonly ChatToolOutputResourceToolbar = new MenuId('ChatToolOutputResourceToolbar');
278
static readonly ChatTextEditorMenu = new MenuId('ChatTextEditorMenu');
279
static readonly ChatToolOutputResourceContext = new MenuId('ChatToolOutputResourceContext');
280
static readonly ChatMultiDiffContext = new MenuId('ChatMultiDiffContext');
281
static readonly ChatConfirmationMenu = new MenuId('ChatConfirmationMenu');
282
static readonly ChatEditorInlineGutter = new MenuId('ChatEditorInlineGutter');
283
static readonly ChatEditorInlineExecute = new MenuId('ChatEditorInputExecute');
284
static readonly ChatEditorInlineInputSide = new MenuId('ChatEditorInputSide');
285
static readonly InlineChatEditorAffordance = new MenuId('InlineChatEditorAffordance');
286
static readonly AccessibleView = new MenuId('AccessibleView');
287
static readonly MultiDiffEditorContent = new MenuId('MultiDiffEditorContent');
288
static readonly MultiDiffEditorFileToolbar = new MenuId('MultiDiffEditorFileToolbar');
289
static readonly DiffEditorHunkToolbar = new MenuId('DiffEditorHunkToolbar');
290
static readonly DiffEditorSelectionToolbar = new MenuId('DiffEditorSelectionToolbar');
291
static readonly BrowserNavigationToolbar = new MenuId('BrowserNavigationToolbar');
292
static readonly BrowserActionsToolbar = new MenuId('BrowserActionsToolbar');
293
static readonly AgentSessionsViewerFilterSubMenu = new MenuId('AgentSessionsViewerFilterSubMenu');
294
static readonly AgentSessionsContext = new MenuId('AgentSessionsContext');
295
static readonly AgentSessionSectionContext = new MenuId('AgentSessionSectionContext');
296
static readonly AgentSessionsCreateSubMenu = new MenuId('AgentSessionsCreateSubMenu');
297
static readonly AgentSessionsToolbar = new MenuId('AgentSessionsToolbar');
298
static readonly AgentSessionItemToolbar = new MenuId('AgentSessionItemToolbar');
299
static readonly AgentSessionSectionToolbar = new MenuId('AgentSessionSectionToolbar');
300
static readonly AgentsTitleBarControlMenu = new MenuId('AgentsTitleBarControlMenu');
301
static readonly ChatViewSessionTitleNavigationToolbar = new MenuId('ChatViewSessionTitleNavigationToolbar');
302
static readonly ChatViewSessionTitleToolbar = new MenuId('ChatViewSessionTitleToolbar');
303
static readonly ChatContextUsageActions = new MenuId('ChatContextUsageActions');
304
305
/**
306
* Create or reuse a `MenuId` with the given identifier
307
*/
308
static for(identifier: string): MenuId {
309
return MenuId._instances.get(identifier) ?? new MenuId(identifier);
310
}
311
312
readonly id: string;
313
314
/**
315
* Create a new `MenuId` with the unique identifier. Will throw if a menu
316
* with the identifier already exists, use `MenuId.for(ident)` or a unique
317
* identifier
318
*/
319
constructor(identifier: string) {
320
if (MenuId._instances.has(identifier)) {
321
throw new TypeError(`MenuId with identifier '${identifier}' already exists. Use MenuId.for(ident) or a unique identifier`);
322
}
323
MenuId._instances.set(identifier, this);
324
this.id = identifier;
325
}
326
}
327
328
export interface IMenuActionOptions {
329
arg?: unknown;
330
args?: unknown[];
331
shouldForwardArgs?: boolean;
332
renderShortTitle?: boolean;
333
}
334
335
export interface IMenuChangeEvent {
336
readonly menu: IMenu;
337
readonly isStructuralChange: boolean;
338
readonly isToggleChange: boolean;
339
readonly isEnablementChange: boolean;
340
}
341
342
export interface IMenu extends IDisposable {
343
readonly onDidChange: Event<IMenuChangeEvent>;
344
getActions(options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][];
345
}
346
347
export interface IMenuData {
348
contexts: ReadonlySet<string>;
349
actions: [string, Array<MenuItemAction | SubmenuItemAction>][];
350
}
351
352
export const IMenuService = createDecorator<IMenuService>('menuService');
353
354
export interface IMenuCreateOptions {
355
emitEventsForSubmenuChanges?: boolean;
356
eventDebounceDelay?: number;
357
}
358
359
export interface IMenuService {
360
361
readonly _serviceBrand: undefined;
362
363
/**
364
* Consider using getMenuActions if you don't need to listen to events.
365
*
366
* Create a new menu for the given menu identifier. A menu sends events when it's entries
367
* have changed (placement, enablement, checked-state). By default it does not send events for
368
* submenu entries. That is more expensive and must be explicitly enabled with the
369
* `emitEventsForSubmenuChanges` flag.
370
*/
371
createMenu(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuCreateOptions): IMenu;
372
373
/**
374
* Creates a new menu, gets the actions, and then disposes of the menu.
375
*/
376
getMenuActions(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][];
377
378
/**
379
* Gets the names of the contexts that this menu listens on.
380
*/
381
getMenuContexts(id: MenuId): ReadonlySet<string>;
382
383
/**
384
* Reset **all** menu item hidden states.
385
*/
386
resetHiddenStates(): void;
387
388
/**
389
* Reset the menu's hidden states.
390
*/
391
resetHiddenStates(menuIds: readonly MenuId[] | undefined): void;
392
}
393
394
type ICommandsMap = Map<string, ICommandAction>;
395
396
export interface IMenuRegistryChangeEvent {
397
has(id: MenuId): boolean;
398
}
399
400
class MenuRegistryChangeEvent {
401
402
private static _all = new Map<MenuId, MenuRegistryChangeEvent>();
403
404
static for(id: MenuId): MenuRegistryChangeEvent {
405
let value = this._all.get(id);
406
if (!value) {
407
value = new MenuRegistryChangeEvent(id);
408
this._all.set(id, value);
409
}
410
return value;
411
}
412
413
static merge(events: IMenuRegistryChangeEvent[]): IMenuRegistryChangeEvent {
414
const ids = new Set<MenuId>();
415
for (const item of events) {
416
if (item instanceof MenuRegistryChangeEvent) {
417
ids.add(item.id);
418
}
419
}
420
return ids;
421
}
422
423
readonly has: (id: MenuId) => boolean;
424
425
private constructor(private readonly id: MenuId) {
426
this.has = candidate => candidate === id;
427
}
428
}
429
430
export interface IMenuRegistry {
431
readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent>;
432
addCommand(userCommand: ICommandAction): IDisposable;
433
getCommand(id: string): ICommandAction | undefined;
434
getCommands(): ICommandsMap;
435
436
/**
437
* @deprecated Use `appendMenuItem` or most likely use `registerAction2` instead. There should be no strong
438
* reason to use this directly.
439
*/
440
appendMenuItems(items: Iterable<{ id: MenuId; item: IMenuItem | ISubmenuItem }>): IDisposable;
441
appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable;
442
getMenuItems(loc: MenuId): Array<IMenuItem | ISubmenuItem>;
443
}
444
445
export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
446
447
private readonly _commands = new Map<string, ICommandAction>();
448
private readonly _menuItems = new Map<MenuId, LinkedList<IMenuItem | ISubmenuItem>>();
449
private readonly _onDidChangeMenu = new MicrotaskEmitter<IMenuRegistryChangeEvent>({
450
merge: MenuRegistryChangeEvent.merge
451
});
452
453
readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent> = this._onDidChangeMenu.event;
454
455
addCommand(command: ICommandAction): IDisposable {
456
this._commands.set(command.id, command);
457
this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette));
458
459
return markAsSingleton(toDisposable(() => {
460
if (this._commands.delete(command.id)) {
461
this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(MenuId.CommandPalette));
462
}
463
}));
464
}
465
466
getCommand(id: string): ICommandAction | undefined {
467
return this._commands.get(id);
468
}
469
470
getCommands(): ICommandsMap {
471
const map = new Map<string, ICommandAction>();
472
this._commands.forEach((value, key) => map.set(key, value));
473
return map;
474
}
475
476
appendMenuItem(id: MenuId, item: IMenuItem | ISubmenuItem): IDisposable {
477
let list = this._menuItems.get(id);
478
if (!list) {
479
list = new LinkedList();
480
this._menuItems.set(id, list);
481
}
482
const rm = list.push(item);
483
this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id));
484
return markAsSingleton(toDisposable(() => {
485
rm();
486
this._onDidChangeMenu.fire(MenuRegistryChangeEvent.for(id));
487
}));
488
}
489
490
appendMenuItems(items: Iterable<{ id: MenuId; item: IMenuItem | ISubmenuItem }>): IDisposable {
491
const result = new DisposableStore();
492
for (const { id, item } of items) {
493
result.add(this.appendMenuItem(id, item));
494
}
495
return result;
496
}
497
498
getMenuItems(id: MenuId): Array<IMenuItem | ISubmenuItem> {
499
let result: Array<IMenuItem | ISubmenuItem>;
500
if (this._menuItems.has(id)) {
501
result = [...this._menuItems.get(id)!];
502
} else {
503
result = [];
504
}
505
if (id === MenuId.CommandPalette) {
506
// CommandPalette is special because it shows
507
// all commands by default
508
this._appendImplicitItems(result);
509
}
510
return result;
511
}
512
513
private _appendImplicitItems(result: Array<IMenuItem | ISubmenuItem>) {
514
const set = new Set<string>();
515
516
for (const item of result) {
517
if (isIMenuItem(item)) {
518
set.add(item.command.id);
519
if (item.alt) {
520
set.add(item.alt.id);
521
}
522
}
523
}
524
this._commands.forEach((command, id) => {
525
if (!set.has(id)) {
526
result.push({ command });
527
}
528
});
529
}
530
};
531
532
export class SubmenuItemAction extends SubmenuAction {
533
534
constructor(
535
readonly item: ISubmenuItem,
536
readonly hideActions: IMenuItemHide | undefined,
537
actions: readonly IAction[],
538
) {
539
super(`submenuitem.${item.submenu.id}`, typeof item.title === 'string' ? item.title : item.title.value, actions, 'submenu');
540
}
541
}
542
543
export interface IMenuItemHide {
544
readonly isHidden: boolean;
545
readonly hide: IAction;
546
readonly toggle: IAction;
547
}
548
549
// implements IAction, does NOT extend Action, so that no one
550
// subscribes to events of Action or modified properties
551
export class MenuItemAction implements IAction {
552
553
static label(action: ICommandAction, options?: IMenuActionOptions): string {
554
return options?.renderShortTitle && action.shortTitle
555
? (typeof action.shortTitle === 'string' ? action.shortTitle : action.shortTitle.value)
556
: (typeof action.title === 'string' ? action.title : action.title.value);
557
}
558
559
readonly item: ICommandAction;
560
readonly alt: MenuItemAction | undefined;
561
562
private readonly _options: IMenuActionOptions | undefined;
563
564
readonly id: string;
565
readonly label: string;
566
readonly tooltip: string;
567
readonly class: string | undefined;
568
readonly enabled: boolean;
569
readonly checked?: boolean;
570
571
constructor(
572
item: ICommandAction,
573
alt: ICommandAction | undefined,
574
options: IMenuActionOptions | undefined,
575
readonly hideActions: IMenuItemHide | undefined,
576
readonly menuKeybinding: IAction | undefined,
577
@IContextKeyService contextKeyService: IContextKeyService,
578
@ICommandService private _commandService: ICommandService
579
) {
580
this.id = item.id;
581
this.label = MenuItemAction.label(item, options);
582
this.tooltip = (typeof item.tooltip === 'string' ? item.tooltip : item.tooltip?.value) ?? '';
583
this.enabled = !item.precondition || contextKeyService.contextMatchesRules(item.precondition);
584
this.checked = undefined;
585
586
let icon: ThemeIcon | undefined;
587
588
if (item.toggled) {
589
const toggled = ((item.toggled as { condition: ContextKeyExpression }).condition ? item.toggled : { condition: item.toggled }) as {
590
condition: ContextKeyExpression; icon?: Icon; tooltip?: string | ILocalizedString; title?: string | ILocalizedString;
591
};
592
this.checked = contextKeyService.contextMatchesRules(toggled.condition);
593
if (this.checked && toggled.tooltip) {
594
this.tooltip = typeof toggled.tooltip === 'string' ? toggled.tooltip : toggled.tooltip.value;
595
}
596
597
if (this.checked && ThemeIcon.isThemeIcon(toggled.icon)) {
598
icon = toggled.icon;
599
}
600
601
if (this.checked && toggled.title) {
602
this.label = typeof toggled.title === 'string' ? toggled.title : toggled.title.value;
603
}
604
}
605
606
if (!icon) {
607
icon = ThemeIcon.isThemeIcon(item.icon) ? item.icon : undefined;
608
}
609
610
this.item = item;
611
this.alt = alt ? new MenuItemAction(alt, undefined, options, hideActions, undefined, contextKeyService, _commandService) : undefined;
612
this._options = options;
613
this.class = icon && ThemeIcon.asClassName(icon);
614
615
}
616
617
run(...args: unknown[]): Promise<void> {
618
let runArgs: unknown[] = [];
619
620
if (this._options?.args) {
621
runArgs = [...runArgs, ...this._options.args];
622
} else if (this._options?.arg) {
623
runArgs = [...runArgs, this._options.arg];
624
}
625
626
if (this._options?.shouldForwardArgs) {
627
runArgs = [...runArgs, ...args];
628
}
629
630
return this._commandService.executeCommand(this.id, ...runArgs);
631
}
632
}
633
634
//#region --- IAction2
635
636
type OneOrN<T> = T | T[];
637
638
interface IAction2CommonOptions extends ICommandAction {
639
/**
640
* One or many menu items.
641
*/
642
menu?: OneOrN<{ id: MenuId; precondition?: null } & Omit<IMenuItem, 'command'>>;
643
644
/**
645
* One keybinding.
646
*/
647
keybinding?: OneOrN<Omit<IKeybindingRule, 'id'>>;
648
}
649
650
interface IBaseAction2Options extends IAction2CommonOptions {
651
652
/**
653
* This type is used when an action is not going to show up in the command palette.
654
* In that case, it's able to use a string for the `title` and `category` properties.
655
*/
656
f1?: false;
657
}
658
659
export interface ICommandPaletteOptions extends IAction2CommonOptions {
660
661
/**
662
* The title of the command that will be displayed in the command palette after the category.
663
* This overrides {@link ICommandAction.title} to ensure a string isn't used so that the title
664
* includes the localized value and the original value for users using language packs.
665
*/
666
title: ICommandActionTitle;
667
668
/**
669
* The category of the command that will be displayed in the command palette before the title suffixed.
670
* with a colon This overrides {@link ICommandAction.title} to ensure a string isn't used so that
671
* the title includes the localized value and the original value for users using language packs.
672
*/
673
category?: keyof typeof Categories | ILocalizedString;
674
675
/**
676
* Shorthand to add this command to the command palette. Note: this is not the only way to declare that
677
* a command should be in the command palette... however, enforcing ILocalizedString in the other scenarios
678
* is much more challenging and this gets us most of the way there.
679
*/
680
f1: true;
681
}
682
683
export type IAction2Options = ICommandPaletteOptions | IBaseAction2Options;
684
685
export interface IAction2F1RequiredOptions {
686
title: ICommandActionTitle;
687
category?: keyof typeof Categories | ILocalizedString;
688
}
689
690
export abstract class Action2 {
691
constructor(readonly desc: Readonly<IAction2Options>) { }
692
abstract run(accessor: ServicesAccessor, ...args: unknown[]): void;
693
}
694
695
export function registerAction2(ctor: { new(): Action2 }): IDisposable {
696
const disposables: IDisposable[] = []; // not using `DisposableStore` to reduce startup perf cost
697
const action = new ctor();
698
699
const { f1, menu, keybinding, ...command } = action.desc;
700
701
if (CommandsRegistry.getCommand(command.id)) {
702
throw new Error(`Cannot register two commands with the same id: ${command.id}`);
703
}
704
705
// command
706
disposables.push(CommandsRegistry.registerCommand({
707
id: command.id,
708
handler: (accessor, ...args) => action.run(accessor, ...args),
709
metadata: command.metadata ?? { description: action.desc.title }
710
}));
711
712
// menu
713
if (Array.isArray(menu)) {
714
for (const item of menu) {
715
disposables.push(MenuRegistry.appendMenuItem(item.id, { command: { ...command, precondition: item.precondition === null ? undefined : command.precondition }, ...item }));
716
}
717
718
} else if (menu) {
719
disposables.push(MenuRegistry.appendMenuItem(menu.id, { command: { ...command, precondition: menu.precondition === null ? undefined : command.precondition }, ...menu }));
720
}
721
if (f1) {
722
disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when: command.precondition }));
723
disposables.push(MenuRegistry.addCommand(command));
724
}
725
726
// keybinding
727
if (Array.isArray(keybinding)) {
728
for (const item of keybinding) {
729
disposables.push(KeybindingsRegistry.registerKeybindingRule({
730
...item,
731
id: command.id,
732
when: command.precondition ? ContextKeyExpr.and(command.precondition, item.when) : item.when
733
}));
734
}
735
} else if (keybinding) {
736
disposables.push(KeybindingsRegistry.registerKeybindingRule({
737
...keybinding,
738
id: command.id,
739
when: command.precondition ? ContextKeyExpr.and(command.precondition, keybinding.when) : keybinding.when
740
}));
741
}
742
743
return {
744
dispose() {
745
dispose(disposables);
746
}
747
};
748
}
749
//#endregion
750
751