src/vs/platform/actions/common/menuService.ts

501 LOC · 345 covered · 156 uncovered · 68 ranges · 10 concepts · 6 introducers · 6 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/base/common/actions.ts · 294 LOCcommon/actions.tssrc/vs/platform/actions/common/actions.ts · 774 LOCcommon/actions.tssrc/vs/platform/commands/test/common/nullCommandService.ts · 16 LOCcommon/nullCommandServic…src/vs/platform/keybinding/test/common/mockKeybindingService.ts · 178 LOCcommon/mockKeybindingSer…menuService.test|title=MenuService in group sorting, by title and order|occurrence=1 · 0 introduced LOCmenuService.test|title=M…menuService.ts ×2 · 7 introduced LOCmenuService.ts ×2menuService.ts ×1 · 1 introduced LOCmenuService.ts ×1menuService.ts ×2 · 19 introduced LOCmenuService.ts ×2menuService.ts ×2 · 4 introduced LOCmenuService.ts ×2menuService.ts ×29 · 223 introduced LOCmenuService.ts ×29actions.ts ×4 · 27 introduced LOCactions.ts ×4actions.ts ×7 · 32 introduced LOCactions.ts ×7actions.ts ×2 · 4 introduced LOCactions.ts ×2menuService.ts ×32 · 156 introduced LOCmenuService.ts ×32menuService.test|title=MenuService Extension contributed submenus missing with errors in output #155030|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/actions/test/common/menuService.test|title=MenuService Extension contributed submenus missing with errors in output #155030|occurrence=1menuService.test|title=M…menuService.test|title=MenuService group sorting|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/actions/test/common/menuService.test|title=MenuService group sorting|occurrence=1menuService.test|title=M…menuService.test|title=MenuService in group sorting, by title and order|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/actions/test/common/menuService.test|title=MenuService in group sorting, by title and order|occurrence=1menuService.test|title=M…menuService.test|title=MenuService in group sorting, by title|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/actions/test/common/menuService.test|title=MenuService in group sorting, by title|occurrence=1menuService.test|title=M…menuService.test|title=MenuService in group sorting, special: navigation|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/actions/test/common/menuService.test|title=MenuService in group sorting, special: navigation|occurrence=1menuService.test|title=M…menuService.test|title=MenuService special MenuId palette|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/actions/test/common/menuService.test|title=MenuService special MenuId palette|occurrence=1menuService.test|title=M…Focused file · src/vs/platform/actions/common/menuService.ts · 501 LOCcommon/menuService.ts

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- menuService.ts ×32
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 { RunOnceScheduler } from '../../../base/common/async.js';
7 > import { DebounceEmitter, Emitter, Event } from '../../../base/common/event.js';
8 > import { DisposableStore, Disposable, IDisposable } from '../../../base/common/lifecycle.js';
9 > import { IMenu, IMenuActionOptions, IMenuChangeEvent, IMenuCreateOptions, IMenuItem, IMenuItemHide, IMenuService, isIMenuItem, isISubmenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction } from './actions.js';
10 > import { ICommandAction, ILocalizedString } from '../../action/common/action.js';
11 > import { ICommandService } from '../../commands/common/commands.js';
12 > import { ContextKeyExpression, IContextKeyService } from '../../contextkey/common/contextkey.js';
13 > import { IAction, Separator, toAction } from '../../../base/common/actions.js';
14 > import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js';
15 > import { removeFastWithoutKeepingOrder } from '../../../base/common/arrays.js';
16 > import { localize } from '../../../nls.js';
17 > import { IKeybindingService } from '../../keybinding/common/keybinding.js';
18 >
19 > export class MenuService extends Disposable implements IMenuService {
20 >
21 > declare readonly _serviceBrand: undefined;
22 >
23 > private readonly _hiddenStates: PersistedMenuHideState;
24 >
25 > constructor(
26 > @ICommandService private readonly _commandService: ICommandService,
27 > @IKeybindingService private readonly _keybindingService: IKeybindingService,
28 > @IStorageService storageService: IStorageService,
29 > ) {
30 > super();
31 > this._hiddenStates = this._register(new PersistedMenuHideState(storageService));
32 > }
33 >
34 > createMenu(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuCreateOptions): IMenu {
35 > return new MenuImpl(id, this._hiddenStates, { emitEventsForSubmenuChanges: false, eventDebounceDelay: 50, ...options }, this._commandService, this._keybindingService, contextKeyService); menuService.ts ×29
36 > }
38 > getMenuActions(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][] {
39 const menu = new MenuImpl(id, this._hiddenStates, { emitEventsForSubmenuChanges: false, eventDebounceDelay: 50, ...options }, this._commandService, this._keybindingService, contextKeyService);
40 const actions = menu.getActions(options);
41 menu.dispose();
42 return actions;
43 }
45 > getMenuContexts(id: MenuId): ReadonlySet<string> {
46 const menuInfo = new MenuInfoSnapshot(id, false);
47 return new Set<string>([...menuInfo.structureContextKeys, ...menuInfo.preconditionContextKeys, ...menuInfo.toggledContextKeys]);
48 }
50 > resetHiddenStates(ids?: MenuId[]): void {
51 this._hiddenStates.reset(ids);
52 }
54 >
55 > class PersistedMenuHideState implements IDisposable {
56 >
57 > private static readonly _key = 'menu.hiddenCommands';
58 >
59 > private readonly _disposables = new DisposableStore();
60 > private readonly _onDidChange = new Emitter<void>();
61 > readonly onDidChange: Event<void> = this._onDidChange.event;
62 >
63 > private _ignoreChangeEvent: boolean = false;
64 > private _data: Record<string, string[] | undefined>;
65 >
66 > private _hiddenByDefaultCache = new Map<string, boolean>();
67 >
68 > constructor(@IStorageService private readonly _storageService: IStorageService) {
69 > try {
70 > const raw = _storageService.get(PersistedMenuHideState._key, StorageScope.PROFILE, '{}');
71 > this._data = JSON.parse(raw);
72 > } catch (err) {
73 this._data = Object.create(null);
74 }
76 > this._disposables.add(_storageService.onDidChangeValue(StorageScope.PROFILE, PersistedMenuHideState._key, this._disposables)(() => {
77 if (!this._ignoreChangeEvent) {
78 try {
79 const raw = _storageService.get(PersistedMenuHideState._key, StorageScope.PROFILE, '{}');
80 this._data = JSON.parse(raw);
81 } catch (err) {
82 console.log('FAILED to read storage after UPDATE', err);
83 }
84 }
85 this._onDidChange.fire();
87 > }
88 >
89 > dispose() {
90 this._onDidChange.dispose();
91 this._disposables.dispose();
92 }
94 > private _isHiddenByDefault(menu: MenuId, commandId: string) {
95 > return this._hiddenByDefaultCache.get(`${menu.id}/${commandId}`) ?? false; menuService.ts ×29
96 > }
98 > setDefaultState(menu: MenuId, commandId: string, hidden: boolean): void {
99 > this._hiddenByDefaultCache.set(`${menu.id}/${commandId}`, hidden); menuService.ts ×29
100 > }
102 > isHidden(menu: MenuId, commandId: string): boolean {
103 > const hiddenByDefault = this._isHiddenByDefault(menu, commandId); menuService.ts ×29
104 > const state = this._data[menu.id]?.includes(commandId) ?? false;
105 > return hiddenByDefault ? !state : state;
106 > }
108 > updateHidden(menu: MenuId, commandId: string, hidden: boolean): void {
109 const hiddenByDefault = this._isHiddenByDefault(menu, commandId);
110 if (hiddenByDefault) {
111 hidden = !hidden;
112 }
113 const entries = this._data[menu.id];
114 if (!hidden) {
115 // remove and cleanup
116 if (entries) {
117 const idx = entries.indexOf(commandId);
118 if (idx >= 0) {
119 removeFastWithoutKeepingOrder(entries, idx);
120 }
121 if (entries.length === 0) {
122 delete this._data[menu.id];
123 }
124 }
125 } else {
126 // add unless already added
127 if (!entries) {
128 this._data[menu.id] = [commandId];
129 } else {
130 const idx = entries.indexOf(commandId);
131 if (idx < 0) {
132 entries.push(commandId);
133 }
134 }
135 }
136 this._persist();
137 }
139 > reset(menus?: MenuId[]): void {
140 if (menus === undefined) {
141 // reset all
142 this._data = Object.create(null);
143 this._persist();
144 } else {
145 // reset only for a specific menu
146 for (const { id } of menus) {
147 if (this._data[id]) {
148 delete this._data[id];
149 }
150 }
151 this._persist();
152 }
153 }
155 > private _persist(): void {
156 try {
157 this._ignoreChangeEvent = true;
158 const raw = JSON.stringify(this._data);
159 this._storageService.store(PersistedMenuHideState._key, raw, StorageScope.PROFILE, StorageTarget.USER);
160 } finally {
161 this._ignoreChangeEvent = false;
162 }
163 }
165 >
166 > type MenuItemGroup = [string, Array<IMenuItem | ISubmenuItem>];
167 >
168 > class MenuInfoSnapshot {
169 > protected _menuGroups: MenuItemGroup[] = [];
170 > private _allMenuIds: Set<MenuId> = new Set();
171 > private _structureContextKeys: Set<string> = new Set();
172 > private _preconditionContextKeys: Set<string> = new Set();
173 > private _toggledContextKeys: Set<string> = new Set();
174 >
175 > constructor(
176 > protected readonly _id: MenuId, menuService.ts ×29
177 > protected readonly _collectContextKeysForSubmenus: boolean,
178 > ) {
179 > this.refresh();
180 > }
182 > get allMenuIds(): ReadonlySet<MenuId> {
183 return this._allMenuIds;
184 }
186 > get structureContextKeys(): ReadonlySet<string> {
187 return this._structureContextKeys;
188 }
190 > get preconditionContextKeys(): ReadonlySet<string> {
191 return this._preconditionContextKeys;
192 }
194 > get toggledContextKeys(): ReadonlySet<string> {
195 return this._toggledContextKeys;
196 }
198 > refresh(): void {
200 > // reset
201 > this._menuGroups.length = 0;
202 > this._allMenuIds.clear();
203 > this._structureContextKeys.clear();
204 > this._preconditionContextKeys.clear();
205 > this._toggledContextKeys.clear();
206 >
207 > const menuItems = this._sort(MenuRegistry.getMenuItems(this._id));
208 > let group: MenuItemGroup | undefined;
209 >
210 > for (const item of menuItems) {
211 > // group by groupId
212 > const groupName = item.group || '';
213 > if (!group || group[0] !== groupName) {
214 > group = [groupName, []];
215 > this._menuGroups.push(group);
216 > }
217 > group[1].push(item);
218 >
219 > // keep keys and submenu ids for eventing
220 > this._collectContextKeysAndSubmenuIds(item);
221 > }
222 > this._allMenuIds.add(this._id);
223 > }
225 > protected _sort(menuItems: (IMenuItem | ISubmenuItem)[]) {
226 // no sorting needed in snapshot
227 return menuItems;
228 }
230 > private _collectContextKeysAndSubmenuIds(item: IMenuItem | ISubmenuItem): void {
232 > MenuInfoSnapshot._fillInKbExprKeys(item.when, this._structureContextKeys);
233 >
234 > if (isIMenuItem(item)) {
235 > // keep precondition keys for event if applicable
236 > if (item.command.precondition) {
237 MenuInfoSnapshot._fillInKbExprKeys(item.command.precondition, this._preconditionContextKeys);
238 }
239 > // keep toggled keys for event if applicable menuService.ts ×29
240 > if (item.command.toggled) {
241 const toggledExpression: ContextKeyExpression = (item.command.toggled as { condition: ContextKeyExpression }).condition || item.command.toggled;
242 MenuInfoSnapshot._fillInKbExprKeys(toggledExpression, this._toggledContextKeys);
243 }
245 > } else if (this._collectContextKeysForSubmenus) {
246 // recursively collect context keys from submenus so that this
247 // menu fires events when context key changes affect submenus
248 MenuRegistry.getMenuItems(item.submenu).forEach(this._collectContextKeysAndSubmenuIds, this);
249
250 this._allMenuIds.add(item.submenu);
251 }
254 > private static _fillInKbExprKeys(exp: ContextKeyExpression | undefined, set: Set<string>): void {
255 > if (exp) { menuService.ts ×29
256 for (const key of exp.keys()) {
257 set.add(key);
258 }
259 }
262 > }
263 >
264 > class MenuInfo extends MenuInfoSnapshot {
265 >
266 > constructor(
267 > _id: MenuId, menuService.ts ×29
268 > private readonly _hiddenStates: PersistedMenuHideState,
269 > _collectContextKeysForSubmenus: boolean,
270 > @ICommandService private readonly _commandService: ICommandService,
271 > @IKeybindingService private readonly _keybindingService: IKeybindingService,
272 > @IContextKeyService private readonly _contextKeyService: IContextKeyService
273 > ) {
274 > super(_id, _collectContextKeysForSubmenus);
275 > this.refresh();
276 > }
278 > createActionGroups(options: IMenuActionOptions | undefined): [string, Array<MenuItemAction | SubmenuItemAction>][] {
279 > const result: [string, Array<MenuItemAction | SubmenuItemAction>][] = []; menuService.ts ×29
280 >
281 > for (const group of this._menuGroups) {
282 > const [id, items] = group;
283 >
284 > let activeActions: Array<MenuItemAction | SubmenuItemAction> | undefined;
285 > for (const item of items) {
286 > if (this._contextKeyService.contextMatchesRules(item.when)) {
287 > const isMenuItem = isIMenuItem(item);
288 > if (isMenuItem) {
289 > this._hiddenStates.setDefaultState(this._id, item.command.id, !!item.isHiddenByDefault);
290 > }
291 >
292 > const menuHide = createMenuHide(this._id, isMenuItem ? item.command : item, this._hiddenStates);
293 > if (isMenuItem) {
294 > // MenuItemAction
295 > const menuKeybinding = createConfigureKeybindingAction(this._commandService, this._keybindingService, item.command.id, item.when);
296 > (activeActions ??= []).push(new MenuItemAction(item.command, item.alt, options, menuHide, menuKeybinding, this._contextKeyService, this._commandService));
297 > } else {
298 // SubmenuItemAction
299 const groups = new MenuInfo(item.submenu, this._hiddenStates, this._collectContextKeysForSubmenus, this._commandService, this._keybindingService, this._contextKeyService).createActionGroups(options);
300 const submenuActions = Separator.join(...groups.map(g => g[1]));
301 if (submenuActions.length > 0) {
302 (activeActions ??= []).push(new SubmenuItemAction(item, menuHide, submenuActions));
303 }
304 }
306 > }
307 > if (activeActions && activeActions.length > 0) {
308 > result.push([id, activeActions]);
309 > }
310 > }
311 > return result;
312 > }
314 > protected override _sort(menuItems: (IMenuItem | ISubmenuItem)[]): (IMenuItem | ISubmenuItem)[] {
315 > return menuItems.sort(MenuInfo._compareMenuItems); menuService.ts ×29
316 > }
318 > private static _compareMenuItems(a: IMenuItem | ISubmenuItem, b: IMenuItem | ISubmenuItem): number {
320 > const aGroup = a.group;
321 > const bGroup = b.group;
322 >
323 > if (aGroup !== bGroup) {
325 > // Falsy groups come last
326 > if (!aGroup) {
327 > return 1;
328 > } else if (!bGroup) {
329 > return -1;
330 > }
331 >
332 > // 'navigation' group comes first
333 > if (aGroup === 'navigation') {
334 > return -1;
335 > } else if (bGroup === 'navigation') {
336 return 1;
337 }
339 > // lexical sort for groups
340 > const value = aGroup.localeCompare(bGroup);
341 > if (value !== 0) {
342 > return value;
343 > }
344 > }
346 > // sort on priority - default is 0
347 > const aPrio = a.order || 0;
348 > const bPrio = b.order || 0; menuService.ts ×29
349 > if (aPrio < bPrio) {
350 > return -1; menuService.ts ×1
351 > } else if (aPrio > bPrio) { menuService.ts ×2
352 return 1;
353 }
355 > // sort on titles
356 > return MenuInfo._compareTitles(
357 > isIMenuItem(a) ? a.command.title : a.title, menuService.ts ×29
358 > isIMenuItem(b) ? b.command.title : b.title
359 > );
360 > }
362 > private static _compareTitles(a: string | ILocalizedString, b: string | ILocalizedString) {
363 > const aStr = typeof a === 'string' ? a : a.original; menuService.ts ×2
364 > const bStr = typeof b === 'string' ? b : b.original;
365 > return aStr.localeCompare(bStr);
366 > }
368 >
369 > class MenuImpl implements IMenu {
370 >
371 > private readonly _menuInfo: MenuInfo;
372 > private readonly _disposables = new DisposableStore();
373 >
374 > private readonly _onDidChange: Emitter<IMenuChangeEvent>;
375 > readonly onDidChange: Event<IMenuChangeEvent>;
376 >
377 > constructor(
378 > id: MenuId, menuService.ts ×29
379 > hiddenStates: PersistedMenuHideState,
380 > options: Required<IMenuCreateOptions>,
381 > @ICommandService commandService: ICommandService,
382 > @IKeybindingService keybindingService: IKeybindingService,
383 > @IContextKeyService contextKeyService: IContextKeyService
384 > ) {
385 > this._menuInfo = new MenuInfo(id, hiddenStates, options.emitEventsForSubmenuChanges, commandService, keybindingService, contextKeyService);
386 >
387 > // Rebuild this menu whenever the menu registry reports an event for this MenuId.
388 > // This usually happen while code and extensions are loaded and affects the over
389 > // structure of the menu
390 > const rebuildMenuSoon = new RunOnceScheduler(() => {
391 this._menuInfo.refresh();
392 this._onDidChange.fire({ menu: this, isStructuralChange: true, isEnablementChange: true, isToggleChange: true });
393 > }, options.eventDebounceDelay); menuService.ts ×29
394 > this._disposables.add(rebuildMenuSoon);
395 > this._disposables.add(MenuRegistry.onDidChangeMenu(e => {
396 for (const id of this._menuInfo.allMenuIds) {
397 if (e.has(id)) {
398 rebuildMenuSoon.schedule();
399 break;
400 }
401 }
402 > })); menuService.ts ×29
403 >
404 > // When context keys or storage state changes we need to check if the menu also has changed. However,
405 > // we only do that when someone listens on this menu because (1) these events are
406 > // firing often and (2) menu are often leaked
407 > const lazyListener = this._disposables.add(new DisposableStore());
408 >
409 > const merge = (events: IMenuChangeEvent[]): IMenuChangeEvent => {
410
411 let isStructuralChange = false;
412 let isEnablementChange = false;
413 let isToggleChange = false;
414
415 for (const item of events) {
416 isStructuralChange = isStructuralChange || item.isStructuralChange;
417 isEnablementChange = isEnablementChange || item.isEnablementChange;
418 isToggleChange = isToggleChange || item.isToggleChange;
419 if (isStructuralChange && isEnablementChange && isToggleChange) {
420 // everything is TRUE, no need to continue iterating
421 break;
422 }
423 }
424
425 return { menu: this, isStructuralChange, isEnablementChange, isToggleChange };
426 };
428 > const startLazyListener = () => {
429
430 lazyListener.add(contextKeyService.onDidChangeContext(e => {
431 const isStructuralChange = e.affectsSome(this._menuInfo.structureContextKeys);
432 const isEnablementChange = e.affectsSome(this._menuInfo.preconditionContextKeys);
433 const isToggleChange = e.affectsSome(this._menuInfo.toggledContextKeys);
434 if (isStructuralChange || isEnablementChange || isToggleChange) {
435 this._onDidChange.fire({ menu: this, isStructuralChange, isEnablementChange, isToggleChange });
436 }
437 }));
438 lazyListener.add(hiddenStates.onDidChange(e => {
439 this._onDidChange.fire({ menu: this, isStructuralChange: true, isEnablementChange: false, isToggleChange: false });
440 }));
441 };
443 > this._onDidChange = new DebounceEmitter({
444 > // start/stop context key listener
445 > onWillAddFirstListener: startLazyListener,
446 > onDidRemoveLastListener: lazyListener.clear.bind(lazyListener),
447 > delay: options.eventDebounceDelay,
448 > merge
449 > });
450 > this.onDidChange = this._onDidChange.event;
451 > }
453 > getActions(options?: IMenuActionOptions | undefined): [string, (MenuItemAction | SubmenuItemAction)[]][] {
454 > return this._menuInfo.createActionGroups(options); menuService.ts ×29
455 > }
457 > dispose(): void {
458 > this._disposables.dispose(); menuService.ts ×29
459 > this._onDidChange.dispose();
460 > }
462 >
463 > function createMenuHide(menu: MenuId, command: ICommandAction | ISubmenuItem, states: PersistedMenuHideState): IMenuItemHide { menuService.ts ×29
464 >
465 > const id = isISubmenuItem(command) ? command.submenu.id : command.id;
466 > const title = typeof command.title === 'string' ? command.title : command.title.value;
467 >
468 > const hide = toAction({
469 > id: `hide/${menu.id}/${id}`,
470 > label: localize('hide.label', 'Hide \'{0}\'', title),
471 > run() { states.updateHidden(menu, id, true); }
472 > });
473 >
474 > const toggle = toAction({
475 > id: `toggle/${menu.id}/${id}`,
476 > label: title,
477 > get checked() { return !states.isHidden(menu, id); },
478 > run() { states.updateHidden(menu, id, !!this.checked); }
479 > });
480 >
481 > return {
482 > hide,
483 > toggle,
484 > get isHidden() { return !toggle.checked; },
485 > };
486 > }
488 > export function createConfigureKeybindingAction(commandService: ICommandService, keybindingService: IKeybindingService, commandId: string, when: ContextKeyExpression | undefined = undefined, enabled = true): IAction {
489 > return toAction({ menuService.ts ×29
490 > id: `configureKeybinding/${commandId}`,
491 > label: localize('configure keybinding', "Configure Keybinding"),
492 > enabled,
493 > run() {
494 // Only set the when clause when there is no keybinding
495 // It is possible that the action and the keybinding have different when clauses
496 const hasKeybinding = !!keybindingService.lookupKeybinding(commandId); // This may only be called inside the `run()` method as it can be expensive on startup. #210529
497 const whenValue = !hasKeybinding && when ? when.serialize() : undefined;
498 commandService.executeCommand('workbench.action.openGlobalKeybindings', `@command:${commandId}` + (whenValue ? ` +when:${whenValue}` : ''));
499 }
501 > }