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