From cadbbf472ca50f9d7600eb01280d77c2763bed1e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Sep 2022 11:50:35 -0700 Subject: [PATCH 1/3] Cleaning up code action menu code - Move keybinding resolver to own file - Use list methods instead of re-implementing them - Reduce duplicated use state --- .../browser/codeActionKeybindingResolver.ts | 90 ++++++++ .../codeAction/browser/codeActionMenu.ts | 209 +++--------------- .../codeAction/browser/codeActionUi.ts | 4 +- .../codeAction/browser/media/action.css | 4 + .../codeActionKeybindingResolver.test.ts | 6 +- 5 files changed, 135 insertions(+), 178 deletions(-) create mode 100644 src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts b/src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts new file mode 100644 index 0000000000000..b65d75bc5b2d7 --- /dev/null +++ b/src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ResolvedKeybinding } from 'vs/base/common/keybindings'; +import { Lazy } from 'vs/base/common/lazy'; +import { CodeAction } from 'vs/editor/common/languages'; +import { codeActionCommandId, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; + +export interface ResolveCodeActionKeybinding { + readonly kind: CodeActionKind; + readonly preferred: boolean; + readonly resolvedKeybinding: ResolvedKeybinding; +} + +export class CodeActionKeybindingResolver { + private static readonly codeActionCommands: readonly string[] = [ + refactorCommandId, + codeActionCommandId, + sourceActionCommandId, + organizeImportsCommandId, + fixAllCommandId + ]; + + constructor( + private readonly keybindingService: IKeybindingService + ) { } + + public getResolver(): (action: CodeAction) => ResolvedKeybinding | undefined { + // Lazy since we may not actually ever read the value + const allCodeActionBindings = new Lazy(() => this.keybindingService.getKeybindings() + .filter(item => CodeActionKeybindingResolver.codeActionCommands.indexOf(item.command!) >= 0) + .filter(item => item.resolvedKeybinding) + .map((item): ResolveCodeActionKeybinding => { + // Special case these commands since they come built-in with VS Code and don't use 'commandArgs' + let commandArgs = item.commandArgs; + if (item.command === organizeImportsCommandId) { + commandArgs = { kind: CodeActionKind.SourceOrganizeImports.value }; + } else if (item.command === fixAllCommandId) { + commandArgs = { kind: CodeActionKind.SourceFixAll.value }; + } + + return { + resolvedKeybinding: item.resolvedKeybinding!, + ...CodeActionCommandArgs.fromUser(commandArgs, { + kind: CodeActionKind.None, + apply: CodeActionAutoApply.Never + }) + }; + })); + + return (action) => { + if (action.kind) { + const binding = this.bestKeybindingForCodeAction(action, allCodeActionBindings.getValue()); + return binding?.resolvedKeybinding; + } + return undefined; + }; + } + + private bestKeybindingForCodeAction( + action: CodeAction, + candidates: readonly ResolveCodeActionKeybinding[] + ): ResolveCodeActionKeybinding | undefined { + if (!action.kind) { + return undefined; + } + const kind = new CodeActionKind(action.kind); + + return candidates + .filter(candidate => candidate.kind.contains(kind)) + .filter(candidate => { + if (candidate.preferred) { + // If the candidate keybinding only applies to preferred actions, the this action must also be preferred + return action.isPreferred; + } + return true; + }) + .reduceRight((currentBest, candidate) => { + if (!currentBest) { + return candidate; + } + // Select the more specific binding + return currentBest.kind.contains(candidate.kind) ? candidate : currentBest; + }, undefined as ResolveCodeActionKeybinding | undefined); + } +} diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts index c01c0f51c430a..8a0c8b8eaff2b 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts @@ -12,17 +12,15 @@ import { IListEvent, IListMouseEvent, IListRenderer } from 'vs/base/browser/ui/l import { List } from 'vs/base/browser/ui/list/listWidget'; import { IAction } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; -import { ResolvedKeybinding } from 'vs/base/common/keybindings'; -import { Lazy } from 'vs/base/common/lazy'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { OS } from 'vs/base/common/platform'; import 'vs/css!./media/action'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { CodeAction, Command } from 'vs/editor/common/languages'; +import { Command } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { codeActionCommandId, CodeActionItem, CodeActionSet, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; -import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind, CodeActionTrigger, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; +import { CodeActionItem, CodeActionSet } from 'vs/editor/contrib/codeAction/browser/codeAction'; +import { CodeActionKind, CodeActionTrigger, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/browser/types'; import 'vs/editor/contrib/symbolIcons/browser/symbolIcons'; // The codicon symbol colors are defined here and must be loaded to get colors import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -31,6 +29,7 @@ import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/cont import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { CodeActionKeybindingResolver } from './codeActionKeybindingResolver'; export const Context = { Visible: new RawContextKey('codeActionMenuVisible', false, localize('codeActionMenuVisible', "Whether the code action list widget is visible")) @@ -43,16 +42,6 @@ interface CodeActionWidgetDelegate { onSelectCodeAction: (action: CodeActionItem, trigger: CodeActionTrigger) => Promise; } -interface ResolveCodeActionKeybinding { - readonly kind: CodeActionKind; - readonly preferred: boolean; - readonly resolvedKeybinding: ResolvedKeybinding; -} - -function stripNewlines(str: string): string { - return str.replace(/\r\n|\r|\n/g, ' '); -} - export interface CodeActionShowOptions { readonly includeDisabledActions: boolean; readonly fromLightbulb?: boolean; @@ -66,13 +55,11 @@ enum CodeActionListItemKind { interface CodeActionListItemCodeAction { readonly kind: CodeActionListItemKind.CodeAction; readonly action: CodeActionItem; - readonly index: number; } interface CodeActionListItemHeader { readonly kind: CodeActionListItemKind.Header; readonly headerTitle: string; - readonly index: number; } type ICodeActionMenuItem = CodeActionListItemCodeAction | CodeActionListItemHeader; @@ -84,6 +71,10 @@ interface ICodeActionMenuTemplateData { readonly keybinding: KeybindingLabel; } +function stripNewlines(str: string): string { + return str.replace(/\r\n|\r|\n/g, ' '); +} + class CodeActionItemRenderer implements IListRenderer { constructor( private readonly keybindingResolver: CodeActionKeybindingResolver, @@ -136,21 +127,11 @@ class CodeActionItemRenderer implements IListRenderer { - data.container.title = localize({ key: 'label', comment: ['placeholders are keybindings, e.g "F2 to Apply, Shift+F2 to Preview"'] }, "{0} to Apply, {1} to Preview", this.keybindingService.lookupKeybinding(acceptSelectedCodeActionCommand)?.getLabel(), this.keybindingService.lookupKeybinding(previewSelectedCodeActionCommand)?.getLabel()); - }; - updateLabel(); - } - - if (element.action.action.disabled) { data.container.classList.add('option-disabled'); - data.container.style.backgroundColor = 'transparent !important'; - data.icon.style.opacity = '0.4'; } else { + data.container.title = localize({ key: 'label', comment: ['placeholders are keybindings, e.g "F2 to Apply, Shift+F2 to Preview"'] }, "{0} to Apply, {1} to Preview", this.keybindingService.lookupKeybinding(acceptSelectedCodeActionCommand)?.getLabel(), this.keybindingService.lookupKeybinding(previewSelectedCodeActionCommand)?.getLabel()); data.container.classList.remove('option-disabled'); } } @@ -197,9 +178,6 @@ class CodeActionList extends Disposable { private readonly list: List; private readonly allMenuItems: ICodeActionMenuItem[]; - private readonly viewItems: readonly CodeActionListItemCodeAction[]; - private focusedEnabledItem?: number; - private currSelectedItem?: number; constructor( codeActions: readonly CodeActionItem[], @@ -244,14 +222,9 @@ class CodeActionList extends Disposable { this._register(this.list.onDidChangeSelection(e => this.onListSelection(e))); this.allMenuItems = this.toMenuItems(codeActions, showHeaders); - this.viewItems = this.allMenuItems.filter(item => item.kind === CodeActionListItemKind.CodeAction && !item.action.action.disabled) as CodeActionListItemCodeAction[]; this.list.splice(0, this.list.length, this.allMenuItems); - if (this.viewItems.length >= 1) { - this.focusedEnabledItem = 0; - this.currSelectedItem = this.viewItems[0].index; - this.list.setFocus([this.currSelectedItem]); - } + this.focusNext(); } public layout(minWidth: number): number { @@ -284,48 +257,26 @@ class CodeActionList extends Disposable { } public focusPrevious() { - if (typeof this.focusedEnabledItem === 'undefined') { - this.focusedEnabledItem = this.viewItems[0].index; - } else if (this.viewItems.length < 1) { - return; - } - - const startIndex = this.focusedEnabledItem; - let item: ICodeActionMenuItem; - - do { - this.focusedEnabledItem = this.focusedEnabledItem - 1; - if (this.focusedEnabledItem < 0) { - this.focusedEnabledItem = this.viewItems.length - 1; - } - item = this.viewItems[this.focusedEnabledItem]; - this.list.setFocus([item.index]); - this.currSelectedItem = item.index; - } while (this.focusedEnabledItem !== startIndex && item.action.action.disabled); + this.list.focusPrevious(1, true, undefined, element => element.kind === CodeActionListItemKind.CodeAction && !element.action.action.disabled); } public focusNext() { - if (typeof this.focusedEnabledItem === 'undefined') { - this.focusedEnabledItem = this.viewItems.length - 1; - } else if (this.viewItems.length < 1) { + this.list.focusNext(1, true, undefined, element => element.kind === CodeActionListItemKind.CodeAction && !element.action.action.disabled); + } + + public acceptSelected() { + const focused = this.list.getFocus(); + if (focused.length === 0) { return; } - const startIndex = this.focusedEnabledItem; - let item: ICodeActionMenuItem; - - do { - this.focusedEnabledItem = (this.focusedEnabledItem + 1) % this.viewItems.length; - item = this.viewItems[this.focusedEnabledItem]; - this.list.setFocus([item.index]); - this.currSelectedItem = item.index; - } while (this.focusedEnabledItem !== startIndex && item.action.action.disabled); - } - - public onEnterSet() { - if (typeof this.currSelectedItem === 'number') { - this.list.setSelection([this.currSelectedItem]); + const focusIndex = focused[0]; + const element = this.list.element(focusIndex); + if (element.kind !== CodeActionListItemKind.CodeAction || element.action.action.disabled) { + return; } + + this.list.setSelection([focusIndex]); } private onListSelection(e: IListEvent): void { @@ -337,31 +288,18 @@ class CodeActionList extends Disposable { } private onListHover(e: IListMouseEvent): void { - if (!e.element) { - this.currSelectedItem = undefined; - this.list.setFocus([]); - } else { - if (e.element.kind === CodeActionListItemKind.CodeAction && !e.element.action.action.disabled) { - this.list.setFocus([e.element.index]); - this.focusedEnabledItem = this.viewItems.indexOf(e.element); - this.currSelectedItem = e.element.index; - } else { - this.currSelectedItem = undefined; - this.list.setFocus([e.element.index]); - } - } + this.list.setFocus(typeof e.index === 'number' ? [e.index] : []); } private onListClick(e: IListMouseEvent): void { if (e.element && e.element.kind === CodeActionListItemKind.CodeAction && e.element.action.action.disabled) { - this.currSelectedItem = undefined; this.list.setFocus([]); } } private toMenuItems(inputCodeActions: readonly CodeActionItem[], showHeaders: boolean): ICodeActionMenuItem[] { if (!showHeaders) { - return inputCodeActions.map((action, index): ICodeActionMenuItem => ({ kind: CodeActionListItemKind.CodeAction, action, index })); + return inputCodeActions.map((action): ICodeActionMenuItem => ({ kind: CodeActionListItemKind.CodeAction, action })); } // Groups code actions by their kind @@ -401,9 +339,9 @@ class CodeActionList extends Disposable { const allMenuItems: ICodeActionMenuItem[] = []; for (const menuEntry of menuEntries) { if (menuEntry.actions.length) { - allMenuItems.push({ kind: CodeActionListItemKind.Header, headerTitle: menuEntry.title, index: allMenuItems.length }); + allMenuItems.push({ kind: CodeActionListItemKind.Header, headerTitle: menuEntry.title }); for (const action of menuEntry.actions) { - allMenuItems.push({ kind: CodeActionListItemKind.CodeAction, action, index: allMenuItems.length }); + allMenuItems.push({ kind: CodeActionListItemKind.CodeAction, action }); } } } @@ -431,7 +369,8 @@ export class CodeActionMenu extends Disposable implements IEditorContribution { readonly anchor: IAnchor; readonly codeActions: CodeActionSet; }; - private _ctxMenuWidgetVisible: IContextKey; + + private readonly _ctxMenuWidgetVisible: IContextKey; constructor( private readonly _editor: ICodeEditor, @@ -487,11 +426,11 @@ export class CodeActionMenu extends Disposable implements IEditorContribution { this.codeActionList.value?.focusNext(); } - public onEnterSet() { - this.codeActionList.value?.onEnterSet(); + public acceptSelected() { + this.codeActionList.value?.acceptSelected(); } - public hideCodeActionWidget() { + public hide() { this._ctxMenuWidgetVisible.reset(); this.codeActionList.clear(); this._contextViewService.hideContextView(); @@ -513,7 +452,7 @@ export class CodeActionMenu extends Disposable implements IEditorContribution { showingCodeActions, this.shouldShowHeaders(), action => { - this.hideCodeActionWidget(); + this.hide(); this._delegate.onSelectCodeAction(action, trigger); }, this._keybindingService); @@ -563,12 +502,10 @@ export class CodeActionMenu extends Disposable implements IEditorContribution { const width = this.codeActionList.value.layout(actionBarWidth); widget.style.width = `${width}px`; - renderDisposables.add(this._editor.onDidLayoutChange(() => this.hideCodeActionWidget())); + renderDisposables.add(this._editor.onDidLayoutChange(() => this.hide())); const focusTracker = renderDisposables.add(dom.trackFocus(element)); - renderDisposables.add(focusTracker.onDidBlur(() => { - this.hideCodeActionWidget(); - })); + renderDisposables.add(focusTracker.onDidBlur(() => this.hide())); this._ctxMenuWidgetVisible.set(true); @@ -581,7 +518,7 @@ export class CodeActionMenu extends Disposable implements IEditorContribution { private toggleShowDisabled(newShowDisabled: boolean): void { const previouslyShowingActions = this.currentShowingContext; - this.hideCodeActionWidget(); + this.hide(); showDisabled = newShowDisabled; @@ -676,77 +613,3 @@ export class CodeActionMenu extends Disposable implements IEditorContribution { })); } } - -export class CodeActionKeybindingResolver { - private static readonly codeActionCommands: readonly string[] = [ - refactorCommandId, - codeActionCommandId, - sourceActionCommandId, - organizeImportsCommandId, - fixAllCommandId - ]; - - constructor( - private readonly keybindingService: IKeybindingService, - ) { } - - public getResolver(): (action: CodeAction) => ResolvedKeybinding | undefined { - // Lazy since we may not actually ever read the value - const allCodeActionBindings = new Lazy(() => - this.keybindingService.getKeybindings() - .filter(item => CodeActionKeybindingResolver.codeActionCommands.indexOf(item.command!) >= 0) - .filter(item => item.resolvedKeybinding) - .map((item): ResolveCodeActionKeybinding => { - // Special case these commands since they come built-in with VS Code and don't use 'commandArgs' - let commandArgs = item.commandArgs; - if (item.command === organizeImportsCommandId) { - commandArgs = { kind: CodeActionKind.SourceOrganizeImports.value }; - } else if (item.command === fixAllCommandId) { - commandArgs = { kind: CodeActionKind.SourceFixAll.value }; - } - - return { - resolvedKeybinding: item.resolvedKeybinding!, - ...CodeActionCommandArgs.fromUser(commandArgs, { - kind: CodeActionKind.None, - apply: CodeActionAutoApply.Never - }) - }; - })); - - return (action) => { - if (action.kind) { - const binding = this.bestKeybindingForCodeAction(action, allCodeActionBindings.getValue()); - return binding?.resolvedKeybinding; - } - return undefined; - }; - } - - private bestKeybindingForCodeAction( - action: CodeAction, - candidates: readonly ResolveCodeActionKeybinding[], - ): ResolveCodeActionKeybinding | undefined { - if (!action.kind) { - return undefined; - } - const kind = new CodeActionKind(action.kind); - - return candidates - .filter(candidate => candidate.kind.contains(kind)) - .filter(candidate => { - if (candidate.preferred) { - // If the candidate keybinding only applies to preferred actions, the this action must also be preferred - return action.isPreferred; - } - return true; - }) - .reduceRight((currentBest, candidate) => { - if (!currentBest) { - return candidate; - } - // Select the more specific binding - return currentBest.kind.contains(candidate.kind) ? candidate : currentBest; - }, undefined as ResolveCodeActionKeybinding | undefined); - } -} diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts index 5d69261987007..d5af03fc55714 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionUi.ts @@ -66,11 +66,11 @@ export class CodeActionUi extends Disposable { } public hideCodeActionWidget() { - this._codeActionWidget.rawValue?.hideCodeActionWidget(); + this._codeActionWidget.rawValue?.hide(); } public onEnter() { - this._codeActionWidget.rawValue?.onEnterSet(); + this._codeActionWidget.rawValue?.acceptSelected(); } public onPreviewEnter() { diff --git a/src/vs/editor/contrib/codeAction/browser/media/action.css b/src/vs/editor/contrib/codeAction/browser/media/action.css index a2ed5cc247042..87efa095833bc 100644 --- a/src/vs/editor/contrib/codeAction/browser/media/action.css +++ b/src/vs/editor/contrib/codeAction/browser/media/action.css @@ -106,6 +106,10 @@ color: var(--vscode-disabledForeground); } +.codeActionWidget .monaco-list-row.code-action.option-disabled .codicon { + opacity: 0.4; +} + .codeActionWidget .monaco-list-row.code-action:not(.option-disabled) .codicon { color: inherit; } diff --git a/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts b/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts index 27138dce76f9f..6095de687f347 100644 --- a/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts +++ b/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { KeyCode } from 'vs/base/common/keyCodes'; import { ChordKeybinding, SimpleKeybinding } from 'vs/base/common/keybindings'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; import { organizeImportsCommandId, refactorCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; -import { CodeActionKeybindingResolver } from 'vs/editor/contrib/codeAction/browser/codeActionMenu'; +import { CodeActionKeybindingResolver } from "vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver"; import { CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; suite('CodeActionKeybindingResolver', () => { const refactorKeybinding = createCodeActionKeybinding( From 0551139a7c035a8b1d197ee9ebb3101eb626992e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Sep 2022 11:53:38 -0700 Subject: [PATCH 2/3] Fix quote style --- .../test/browser/codeActionKeybindingResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts b/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts index 6095de687f347..f25921dc01fd0 100644 --- a/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts +++ b/src/vs/editor/contrib/codeAction/test/browser/codeActionKeybindingResolver.test.ts @@ -8,7 +8,7 @@ import { ChordKeybinding, SimpleKeybinding } from 'vs/base/common/keybindings'; import { KeyCode } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; import { organizeImportsCommandId, refactorCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; -import { CodeActionKeybindingResolver } from "vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver"; +import { CodeActionKeybindingResolver } from 'vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/browser/types'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; From 423f2974e8b3d64d7dfd618931ad9c513f9d99df Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 12 Sep 2022 12:05:05 -0700 Subject: [PATCH 3/3] Restore mouse support in code action menus Fixes #160709 --- .../contrib/codeAction/browser/codeActionMenu.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts index 8a0c8b8eaff2b..42bbe69507f27 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts @@ -198,7 +198,6 @@ class CodeActionList extends Disposable { new HeaderRenderer(), ], { keyboardSupport: false, - mouseSupport: false, accessibilityProvider: { getAriaLabel: element => { if (element.kind === CodeActionListItemKind.CodeAction) { @@ -280,10 +279,15 @@ class CodeActionList extends Disposable { } private onListSelection(e: IListEvent): void { - for (const element of e.elements) { - if (element.kind === CodeActionListItemKind.CodeAction && !element.action.action.disabled) { - this.onDidSelect(element.action); - } + if (!e.elements.length) { + return; + } + + const element = e.elements[0]; + if (element.kind === CodeActionListItemKind.CodeAction && !element.action.action.disabled) { + this.onDidSelect(element.action); + } else { + this.list.setSelection([]); } }