diff --git a/src/vs/platform/accessibility/browser/accessibleView.ts b/src/vs/platform/accessibility/browser/accessibleView.ts index 34d2b431a08d3..7674bf33f245e 100644 --- a/src/vs/platform/accessibility/browser/accessibleView.ts +++ b/src/vs/platform/accessibility/browser/accessibleView.ts @@ -8,6 +8,7 @@ import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { Event } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; +import { IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; export const IAccessibleViewService = createDecorator('accessibleViewService'); @@ -60,6 +61,11 @@ export interface IAccessibleViewOptions { * If this provider might want to request to be shown again, provide an ID. */ id?: AccessibleViewProviderId; + + /** + * Keybinding items to configure + */ + configureKeybindingItems?: IQuickPickItem[]; } @@ -113,6 +119,7 @@ export interface IAccessibleViewService { */ getOpenAriaHint(verbositySettingKey: string): string | null; getCodeBlockContext(): ICodeBlockActionContext | undefined; + configureKeybindings(): void; } diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index bab9d8b97e5ca..73ca041e3db48 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -173,7 +173,7 @@ const viewDescriptor: IJSONSchema = { }, accessibilityHelpContent: { type: 'string', - markdownDescription: localize('vscode.extension.contributes.view.accessibilityHelpContent', "When the accessibility help dialog is invoked in this view, this content will be presented to the user as a markdown string. Keybindings will be resolved when provided in the format of . If there is no keybinding, that will be indicated with a link to configure one.") + markdownDescription: localize('vscode.extension.contributes.view.accessibilityHelpContent', "When the accessibility help dialog is invoked in this view, this content will be presented to the user as a markdown string. Keybindings will be resolved when provided in the format of . If there is no keybinding, that will be indicated and this command will be included in a quickpick for easy configuration.") } } }; diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index dbba0da04200d..25f23161a5a99 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -38,7 +38,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { AccessibilityVerbositySettingId, AccessibilityWorkbenchSettingId, accessibilityHelpIsShown, accessibleViewContainsCodeBlocks, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewInCodeBlock, accessibleViewIsShown, accessibleViewOnLastLine, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; @@ -96,7 +96,8 @@ export class AccessibleView extends Disposable { @IMenuService private readonly _menuService: IMenuService, @ICommandService private readonly _commandService: ICommandService, @IChatCodeBlockContextProviderService private readonly _codeBlockContextProviderService: IChatCodeBlockContextProviderService, - @IStorageService private readonly _storageService: IStorageService + @IStorageService private readonly _storageService: IStorageService, + @IQuickInputService private readonly _quickInputService: IQuickInputService ) { super(); @@ -361,6 +362,27 @@ export class AccessibleView extends Disposable { return symbols.length ? symbols : undefined; } + configureKeybindings(): void { + const items = this._currentProvider?.options?.configureKeybindingItems; + if (!items) { + return; + } + const quickPick: IQuickPick = this._quickInputService.createQuickPick(); + this._register(quickPick); + quickPick.items = items; + quickPick.title = localize('keybindings', 'Configure keybindings'); + quickPick.placeholder = localize('selectKeybinding', 'Select a command ID to configure a keybinding for it'); + quickPick.show(); + quickPick.onDidAccept(async () => { + const item = quickPick.selectedItems[0]; + if (item) { + await this._commandService.executeCommand('workbench.action.openGlobalKeybindings', item.id); + } + quickPick.dispose(); + }); + quickPick.onDidHide(() => quickPick.dispose()); + } + private _convertTokensToSymbols(tokens: marked.TokensList, symbols: IAccessibleViewSymbol[]): void { let firstListItem: string | undefined; for (const token of tokens) { @@ -750,6 +772,9 @@ export class AccessibleViewService extends Disposable implements IAccessibleView } this._accessibleView.show(provider, undefined, undefined, position); } + configureKeybindings(): void { + this._accessibleView?.configureKeybindings(); + } showLastProvider(id: AccessibleViewProviderId): void { this._accessibleView?.showLastProvider(id); } diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index 0284884cab233..8d9bd29623d1b 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -228,6 +228,24 @@ class AccessibleViewDisableHintAction extends Action2 { } registerAction2(AccessibleViewDisableHintAction); +class AccessibilityHelpConfigureKeybindingsAction extends Action2 { + constructor() { + super({ + id: AccessibilityCommandId.AccessibilityHelpConfigureKeybindings, + precondition: ContextKeyExpr.and(accessibilityHelpIsShown), + keybinding: { + primary: KeyMod.Alt | KeyCode.KeyK, + weight: KeybindingWeight.WorkbenchContrib + }, + title: localize('editor.action.accessibilityHelpConfigureKeybindings', "Accessibility Help Configure Keybindings") + }); + } + async run(accessor: ServicesAccessor): Promise { + await accessor.get(IAccessibleViewService).configureKeybindings(); + } +} +registerAction2(AccessibilityHelpConfigureKeybindingsAction); + class AccessibleViewAcceptInlineCompletionAction extends Action2 { constructor() { super({ @@ -267,3 +285,4 @@ class AccessibleViewAcceptInlineCompletionAction extends Action2 { } } registerAction2(AccessibleViewAcceptInlineCompletionAction); + diff --git a/src/vs/workbench/contrib/accessibility/browser/extensionAccesibilityHelp.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/extensionAccesibilityHelp.contribution.ts index 6161c11f1ccd8..2e5056bc56737 100644 --- a/src/vs/workbench/contrib/accessibility/browser/extensionAccesibilityHelp.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/extensionAccesibilityHelp.contribution.ts @@ -5,14 +5,15 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; import { DisposableMap, IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; import { AccessibleViewType, ExtensionContentProvider } from 'vs/platform/accessibility/browser/accessibleView'; import { AccessibleViewRegistry } from 'vs/platform/accessibility/browser/accessibleViewRegistry'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { Registry } from 'vs/platform/registry/common/platform'; import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; import { IViewsRegistry, Extensions, IViewDescriptor } from 'vs/workbench/common/views'; +import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; export class ExtensionAccessibilityHelpDialogContribution extends Disposable { @@ -54,9 +55,9 @@ function registerAccessibilityHelpAction(keybindingService: IKeybindingService, const viewsService = accessor.get(IViewsService); return new ExtensionContentProvider( viewDescriptor.id, - { type: AccessibleViewType.Help }, - () => helpContent.value, - () => viewsService.openView(viewDescriptor.id, true) + { type: AccessibleViewType.Help, configureKeybindingItems: helpContent.configureKeybindingItems }, + () => helpContent.value.value, + () => viewsService.openView(viewDescriptor.id, true), ); } })); @@ -68,27 +69,34 @@ function registerAccessibilityHelpAction(keybindingService: IKeybindingService, return disposableStore; } -function resolveExtensionHelpContent(keybindingService: IKeybindingService, content?: MarkdownString): MarkdownString | undefined { +function resolveExtensionHelpContent(keybindingService: IKeybindingService, content?: MarkdownString): { value: MarkdownString; configureKeybindingItems: IPickerQuickAccessItem[] | undefined } | undefined { if (!content) { return; } + const configureKeybindingItems: IPickerQuickAccessItem[] = []; let resolvedContent = typeof content === 'string' ? content : content.value; const matches = resolvedContent.matchAll(/\.*)\>/gm); for (const match of [...matches]) { const commandId = match?.groups?.commandId; + let kbLabel; if (match?.length && commandId) { const keybinding = keybindingService.lookupKeybinding(commandId)?.getAriaLabel(); - let kbLabel = keybinding; - if (!kbLabel) { - const args = URI.parse(`command:workbench.action.openGlobalKeybindings?${encodeURIComponent(JSON.stringify(commandId))}`); - kbLabel = ` [Configure a keybinding](${args})`; + if (!keybinding) { + const configureKb = keybindingService.lookupKeybinding(AccessibilityCommandId.AccessibilityHelpConfigureKeybindings)?.getAriaLabel(); + const keybindingToConfigureQuickPick = configureKb ? '(' + configureKb + ')' : 'by assigning a keybinding to the command accessibility.openQuickPick.'; + kbLabel = `, configure a keybinding ` + keybindingToConfigureQuickPick; + configureKeybindingItems.push({ + label: commandId, + id: commandId + }); } else { kbLabel = ' (' + keybinding + ')'; } resolvedContent = resolvedContent.replace(match[0], kbLabel); } } - const result = new MarkdownString(resolvedContent); - result.isTrusted = true; - return result; + const value = new MarkdownString(resolvedContent); + value.isTrusted = true; + return { value, configureKeybindingItems: configureKeybindingItems.length ? configureKeybindingItems : undefined }; } + diff --git a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts index 53ad46e784655..4c5e852c355ad 100644 --- a/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts +++ b/src/vs/workbench/contrib/accessibility/common/accessibilityCommands.ts @@ -12,5 +12,6 @@ export const enum AccessibilityCommandId { ShowPrevious = 'editor.action.accessibleViewPrevious', AccessibleViewAcceptInlineCompletion = 'editor.action.accessibleViewAcceptInlineCompletion', NextCodeBlock = 'editor.action.accessibleViewNextCodeBlock', - PreviousCodeBlock = 'editor.action.accessibleViewPreviousCodeBlock' + PreviousCodeBlock = 'editor.action.accessibleViewPreviousCodeBlock', + AccessibilityHelpConfigureKeybindings = 'editor.action.accessibilityHelpConfigureKeybindings', }