Skip to content

Commit 4600d99

Browse files
authored
Improve Configure Keybindings Support (microsoft#210251)
* Improve Configure Keybindings Support * fix test
1 parent 2d58a39 commit 4600d99

File tree

10 files changed

+98
-76
lines changed

10 files changed

+98
-76
lines changed

src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,9 +319,10 @@ export class CustomizedMenuWorkbenchToolBar extends WorkbenchToolBar {
319319
@IContextKeyService private readonly contextKeyService: IContextKeyService,
320320
@IContextMenuService contextMenuService: IContextMenuService,
321321
@IKeybindingService keybindingService: IKeybindingService,
322+
@ICommandService commandService: ICommandService,
322323
@ITelemetryService telemetryService: ITelemetryService,
323324
) {
324-
super(container, { resetMenu: menuId, ...options2 }, menuService, contextKeyService, contextMenuService, keybindingService, telemetryService);
325+
super(container, { resetMenu: menuId, ...options2 }, menuService, contextKeyService, contextMenuService, keybindingService, commandService, telemetryService);
325326

326327
this._store.add(this.menu.onDidChange(() => this.updateToolbar()));
327328
this.updateToolbar();

src/vs/editor/contrib/inlineEdit/browser/inlineEditHintsWidget.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { InlineEditWidget } from 'vs/editor/contrib/inlineEdit/browser/inlineEdi
1919
import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
2020
import { IMenuWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
2121
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
22+
import { ICommandService } from 'vs/platform/commands/common/commands';
2223
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
2324
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
2425
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -202,9 +203,10 @@ export class CustomizedMenuWorkbenchToolBar extends WorkbenchToolBar {
202203
@IContextKeyService private readonly contextKeyService: IContextKeyService,
203204
@IContextMenuService contextMenuService: IContextMenuService,
204205
@IKeybindingService keybindingService: IKeybindingService,
206+
@ICommandService commandService: ICommandService,
205207
@ITelemetryService telemetryService: ITelemetryService,
206208
) {
207-
super(container, { resetMenu: menuId, ...options2 }, menuService, contextKeyService, contextMenuService, keybindingService, telemetryService);
209+
super(container, { resetMenu: menuId, ...options2 }, menuService, contextKeyService, contextMenuService, keybindingService, commandService, telemetryService);
208210

209211
this._store.add(this.menu.onDidChange(() => this.updateToolbar()));
210212
this._store.add(this.editor.onDidChangeCursorPosition(() => this.updateToolbar()));

src/vs/platform/actions/browser/toolbar.ts

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { addDisposableListener, getWindow } from 'vs/base/browser/dom';
77
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
8-
import { IToolBarOptions, ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
8+
import { IToolBarOptions, ToggleMenuAction, ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
99
import { IAction, Separator, SubmenuAction, toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions';
1010
import { coalesceInPlace } from 'vs/base/common/arrays';
1111
import { intersection } from 'vs/base/common/collections';
@@ -16,6 +16,8 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
1616
import { localize } from 'vs/nls';
1717
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
1818
import { IMenuActionOptions, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions';
19+
import { createConfigureKeybindingAction } from 'vs/platform/actions/common/menuService';
20+
import { ICommandService } from 'vs/platform/commands/common/commands';
1921
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
2022
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
2123
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -90,12 +92,13 @@ export class WorkbenchToolBar extends ToolBar {
9092
@IMenuService private readonly _menuService: IMenuService,
9193
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
9294
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
93-
@IKeybindingService keybindingService: IKeybindingService,
95+
@IKeybindingService private readonly _keybindingService: IKeybindingService,
96+
@ICommandService private readonly _commandService: ICommandService,
9497
@ITelemetryService telemetryService: ITelemetryService,
9598
) {
9699
super(container, _contextMenuService, {
97100
// defaults
98-
getKeyBinding: (action) => keybindingService.lookupKeybinding(action.id) ?? undefined,
101+
getKeyBinding: (action) => _keybindingService.lookupKeybinding(action.id) ?? undefined,
99102
// options (override defaults)
100103
..._options,
101104
// mandatory (overide options)
@@ -181,8 +184,8 @@ export class WorkbenchToolBar extends ToolBar {
181184
coalesceInPlace(extraSecondary);
182185
super.setActions(primary, Separator.join(extraSecondary, secondary));
183186

184-
// add context menu for toggle actions
185-
if (toggleActions.length > 0) {
187+
// add context menu for toggle and configure keybinding actions
188+
if (toggleActions.length > 0 || primary.length > 0) {
186189
this._sessionDisposables.add(addDisposableListener(this.getElement(), 'contextmenu', e => {
187190
const event = new StandardMouseEvent(getWindow(this.getElement()), e);
188191

@@ -193,47 +196,53 @@ export class WorkbenchToolBar extends ToolBar {
193196
event.preventDefault();
194197
event.stopPropagation();
195198

196-
let noHide = false;
197-
198-
// last item cannot be hidden when using ignore strategy
199-
if (toggleActionsCheckedCount === 1 && this._options?.hiddenItemStrategy === HiddenItemStrategy.Ignore) {
200-
noHide = true;
201-
for (let i = 0; i < toggleActions.length; i++) {
202-
if (toggleActions[i].checked) {
203-
toggleActions[i] = toAction({
204-
id: action.id,
205-
label: action.label,
206-
checked: true,
207-
enabled: false,
208-
run() { }
209-
});
210-
break; // there is only one
211-
}
212-
}
213-
}
214-
215199
const primaryActions = [];
216200

201+
// -- Configure Keybinding Action --
217202
if (action instanceof MenuItemAction && action.menuKeybinding) {
218203
primaryActions.push(action.menuKeybinding);
204+
} else if (!(action instanceof SubmenuItemAction || action instanceof ToggleMenuAction)) {
205+
primaryActions.push(createConfigureKeybindingAction(action.id, undefined, this._commandService, this._keybindingService));
219206
}
220207

221-
// add "hide foo" actions
222-
if (!noHide && (action instanceof MenuItemAction || action instanceof SubmenuItemAction)) {
223-
if (!action.hideActions) {
224-
// no context menu for MenuItemAction instances that support no hiding
225-
// those are fake actions and need to be cleaned up
226-
return;
208+
// -- Hide Actions --
209+
if (toggleActions.length > 0) {
210+
let noHide = false;
211+
212+
// last item cannot be hidden when using ignore strategy
213+
if (toggleActionsCheckedCount === 1 && this._options?.hiddenItemStrategy === HiddenItemStrategy.Ignore) {
214+
noHide = true;
215+
for (let i = 0; i < toggleActions.length; i++) {
216+
if (toggleActions[i].checked) {
217+
toggleActions[i] = toAction({
218+
id: action.id,
219+
label: action.label,
220+
checked: true,
221+
enabled: false,
222+
run() { }
223+
});
224+
break; // there is only one
225+
}
226+
}
227+
}
228+
229+
// add "hide foo" actions
230+
if (!noHide && (action instanceof MenuItemAction || action instanceof SubmenuItemAction)) {
231+
if (!action.hideActions) {
232+
// no context menu for MenuItemAction instances that support no hiding
233+
// those are fake actions and need to be cleaned up
234+
return;
235+
}
236+
primaryActions.push(action.hideActions.hide);
237+
238+
} else {
239+
primaryActions.push(toAction({
240+
id: 'label',
241+
label: localize('hide', "Hide"),
242+
enabled: false,
243+
run() { }
244+
}));
227245
}
228-
primaryActions.push(action.hideActions.hide);
229-
230-
} else {
231-
primaryActions.push(toAction({
232-
id: 'label',
233-
label: localize('hide', "Hide"),
234-
enabled: false,
235-
run() { }
236-
}));
237246
}
238247

239248
const actions = Separator.join(primaryActions, toggleActions);
@@ -251,6 +260,10 @@ export class WorkbenchToolBar extends ToolBar {
251260
}));
252261
}
253262

263+
if (actions.length === 0) {
264+
return;
265+
}
266+
254267
this._contextMenuService.showContextMenu({
255268
getAnchor: () => event,
256269
getActions: () => actions,
@@ -318,9 +331,10 @@ export class MenuWorkbenchToolBar extends WorkbenchToolBar {
318331
@IContextKeyService contextKeyService: IContextKeyService,
319332
@IContextMenuService contextMenuService: IContextMenuService,
320333
@IKeybindingService keybindingService: IKeybindingService,
334+
@ICommandService commandService: ICommandService,
321335
@ITelemetryService telemetryService: ITelemetryService,
322336
) {
323-
super(container, { resetMenu: menuId, ...options }, menuService, contextKeyService, contextMenuService, keybindingService, telemetryService);
337+
super(container, { resetMenu: menuId, ...options }, menuService, contextKeyService, contextMenuService, keybindingService, commandService, telemetryService);
324338

325339
// update logic
326340
const menu = this._store.add(menuService.createMenu(menuId, contextKeyService, { emitEventsForSubmenuChanges: true }));

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

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ class MenuInfo {
248248
const menuHide = createMenuHide(this._id, isMenuItem ? item.command : item, this._hiddenStates);
249249
if (isMenuItem) {
250250
// MenuItemAction
251-
const menuKeybinding = createMenuKeybindingAction(this._id, item.command, item.when, this._commandService, this._keybindingService);
251+
const menuKeybinding = createConfigureKeybindingAction(item.command.id, item.when, this._commandService, this._keybindingService);
252252
activeActions.push(new MenuItemAction(item.command, item.alt, options, menuHide, menuKeybinding, this._contextKeyService, this._commandService));
253253
} else {
254254
// SubmenuItemAction
@@ -442,19 +442,16 @@ function createMenuHide(menu: MenuId, command: ICommandAction | ISubmenuItem, st
442442
};
443443
}
444444

445-
function createMenuKeybindingAction(menu: MenuId, command: ICommandAction | ISubmenuItem, when: ContextKeyExpression | undefined = undefined, commandService: ICommandService, keybindingService: IKeybindingService): IAction | undefined {
446-
if (isISubmenuItem(command)) {
447-
return undefined;
448-
}
449-
450-
const configureKeybindingAction = toAction({
451-
id: `configureKeybinding/${menu.id}/${command.id}`,
452-
label: keybindingService.lookupKeybinding(command.id) ? localize('change keybinding', "Change Keybinding") : localize('configure keybinding', "Configure Keybinding"),
445+
export function createConfigureKeybindingAction(commandId: string, when: ContextKeyExpression | undefined = undefined, commandService: ICommandService, keybindingService: IKeybindingService): IAction {
446+
const hasKeybinding = !!keybindingService.lookupKeybinding(commandId);
447+
return toAction({
448+
id: `configureKeybinding/${commandId}`,
449+
label: hasKeybinding ? localize('change keybinding', "Change Keybinding") : localize('configure keybinding', "Configure Keybinding"),
453450
run() {
454-
const whenValue = when ? when.serialize() : undefined;
455-
commandService.executeCommand('workbench.action.openGlobalKeybindings', `@command:${command.id}` + (whenValue ? ` +when:${whenValue}` : ''));
451+
// Only set the when clause when there is no keybinding
452+
// It is possible that the action and the keybinding have different when clauses
453+
const whenValue = !hasKeybinding && when ? when.serialize() : undefined;
454+
commandService.executeCommand('workbench.action.openGlobalKeybindings', `@command:${commandId}` + (whenValue ? ` +when:${whenValue}` : ''));
456455
}
457456
});
458-
459-
return configureKeybindingAction;
460457
}

src/vs/platform/quickinput/browser/commandsQuickAccess.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
5656
constructor(
5757
options: ICommandsQuickAccessOptions,
5858
@IInstantiationService private readonly instantiationService: IInstantiationService,
59-
@IKeybindingService private readonly keybindingService: IKeybindingService,
59+
@IKeybindingService protected readonly keybindingService: IKeybindingService,
6060
@ICommandService private readonly commandService: ICommandService,
6161
@ITelemetryService private readonly telemetryService: ITelemetryService,
6262
@IDialogService private readonly dialogService: IDialogService

src/vs/workbench/browser/parts/paneCompositePart.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import { localize } from 'vs/nls';
2929
import { CompositeDragAndDropObserver, toggleDropEffect } from 'vs/workbench/browser/dnd';
3030
import { EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
3131
import { IPartOptions } from 'vs/workbench/browser/part';
32-
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
3332
import { CompositeMenuActions } from 'vs/workbench/browser/actions';
3433
import { IMenuService, MenuId } from 'vs/platform/actions/common/actions';
3534
import { ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -40,6 +39,7 @@ import { Composite } from 'vs/workbench/browser/composite';
4039
import { ViewsSubMenu } from 'vs/workbench/browser/parts/views/viewPaneContainer';
4140
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
4241
import { IHoverService } from 'vs/platform/hover/browser/hover';
42+
import { HiddenItemStrategy, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar';
4343

4444
export enum CompositeBarPosition {
4545
TOP,
@@ -122,7 +122,7 @@ export abstract class AbstractPaneCompositePart extends CompositePart<PaneCompos
122122
private compositeBarPosition: CompositeBarPosition | undefined = undefined;
123123
private emptyPaneMessageElement: HTMLElement | undefined;
124124

125-
private globalToolBar: ToolBar | undefined;
125+
private globalToolBar: WorkbenchToolBar | undefined;
126126
private readonly globalActions: CompositeMenuActions;
127127

128128
private blockOpening = false;
@@ -315,13 +315,14 @@ export abstract class AbstractPaneCompositePart extends CompositePart<PaneCompos
315315
const globalTitleActionsContainer = titleArea.appendChild($('.global-actions'));
316316

317317
// Global Actions Toolbar
318-
this.globalToolBar = this._register(new ToolBar(globalTitleActionsContainer, this.contextMenuService, {
318+
this.globalToolBar = this._register(this.instantiationService.createInstance(WorkbenchToolBar, globalTitleActionsContainer, {
319319
actionViewItemProvider: (action, options) => this.actionViewItemProvider(action, options),
320320
orientation: ActionsOrientation.HORIZONTAL,
321321
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
322322
anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment(),
323323
toggleMenuTitle: localize('moreActions', "More Actions..."),
324-
hoverDelegate: this.toolbarHoverDelegate
324+
hoverDelegate: this.toolbarHoverDelegate,
325+
hiddenItemStrategy: HiddenItemStrategy.NoHide
325326
}));
326327

327328
this.updateGlobalToolbarActions();

src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { fixedDiffEditorOptions, fixedEditorOptions, fixedEditorPadding } from '
4242
import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
4343
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
4444
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
45+
import { ICommandService } from 'vs/platform/commands/common/commands';
4546

4647
export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions {
4748
return {
@@ -82,6 +83,7 @@ class PropertyHeader extends Disposable {
8283
},
8384
@IContextMenuService private readonly contextMenuService: IContextMenuService,
8485
@IKeybindingService private readonly keybindingService: IKeybindingService,
86+
@ICommandService private readonly commandService: ICommandService,
8587
@INotificationService private readonly notificationService: INotificationService,
8688
@IMenuService private readonly menuService: IMenuService,
8789
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@@ -125,7 +127,7 @@ class PropertyHeader extends Disposable {
125127

126128
return undefined;
127129
}
128-
}, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.telemetryService);
130+
}, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.commandService, this.telemetryService);
129131
this._register(this._toolbar);
130132
this._toolbar.context = {
131133
cell: this.cell

src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,20 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce
126126
return [
127127
...this.getCodeEditorCommandPicks(),
128128
...this.getGlobalCommandPicks()
129-
].map(picks => ({
130-
...picks,
131-
buttons: [{
132-
iconClass: ThemeIcon.asClassName(Codicon.gear),
133-
tooltip: localize('configure keybinding', "Configure Keybinding"),
134-
}],
135-
trigger: (): TriggerAction => {
136-
this.preferencesService.openGlobalKeybindingSettings(false, { query: createKeybindingCommandQuery(picks.commandId, picks.commandWhen) });
137-
return TriggerAction.CLOSE_PICKER;
138-
},
139-
}));
129+
].map(picks => {
130+
const hasKeybinding = !!this.keybindingService.lookupKeybindings(picks.commandId);
131+
return {
132+
...picks,
133+
buttons: [{
134+
iconClass: ThemeIcon.asClassName(Codicon.gear),
135+
tooltip: hasKeybinding ? localize('change keybinding', "Change Keybinding") : localize('configure keybinding', "Configure Keybinding"),
136+
}],
137+
trigger: (): TriggerAction => {
138+
this.preferencesService.openGlobalKeybindingSettings(false, { query: createKeybindingCommandQuery(picks.commandId, picks.commandWhen) });
139+
return TriggerAction.CLOSE_PICKER;
140+
},
141+
};
142+
});
140143
}
141144

142145
protected hasAdditionalCommandPicks(filter: string, token: CancellationToken): boolean {

src/vs/workbench/contrib/scm/browser/scmRepositoryRenderer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class RepositoryRenderer implements ICompressibleTreeRenderer<ISCMReposit
8080
const name = append(label, $('span.name'));
8181
const description = append(label, $('span.description'));
8282
const actions = append(provider, $('.actions'));
83-
const toolBar = new WorkbenchToolBar(actions, { actionViewItemProvider: this.actionViewItemProvider, resetMenu: this.toolbarMenuId }, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.telemetryService);
83+
const toolBar = new WorkbenchToolBar(actions, { actionViewItemProvider: this.actionViewItemProvider, resetMenu: this.toolbarMenuId }, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.commandService, this.telemetryService);
8484
const countContainer = append(provider, $('.count'));
8585
const count = new CountBadge(countContainer, {}, defaultCountBadgeStyles);
8686
const visibilityDisposable = toolBar.onDidChangeDropdownVisibility(e => provider.classList.toggle('active', e));

0 commit comments

Comments
 (0)