diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index de7ddcd7c0c73..efc4a052f460a 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -12,12 +12,14 @@ import { IAccessibleViewService, AccessibleViewService } from 'vs/workbench/cont import { UnfocusedViewDimmingContribution } from 'vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution'; import { EditorAccessibilityHelpContribution, HoverAccessibleViewContribution, InlineCompletionsAccessibleViewContribution, NotificationAccessibleViewContribution } from 'vs/workbench/contrib/accessibility/browser/accessibilityContributions'; import { AccessibilityStatus } from 'vs/workbench/contrib/accessibility/browser/accessibilityStatus'; +import { CommentsAccessibilityHelpContribution } from 'vs/workbench/contrib/comments/browser/comments.contribution'; registerAccessibilityConfiguration(); registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(EditorAccessibilityHelpContribution, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(CommentsAccessibilityHelpContribution, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(UnfocusedViewDimmingContribution, LifecyclePhase.Restored); const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 6a6df6719ef96..2b9fecde5ce7d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -43,7 +43,8 @@ export const enum AccessibilityVerbositySettingId { Editor = 'accessibility.verbosity.editor', Hover = 'accessibility.verbosity.hover', Notification = 'accessibility.verbosity.notification', - EmptyEditorHint = 'accessibility.verbosity.emptyEditorHint' + EmptyEditorHint = 'accessibility.verbosity.emptyEditorHint', + Comments = 'accessibility.verbosity.comments' } export const enum AccessibleViewProviderId { @@ -57,7 +58,8 @@ export const enum AccessibleViewProviderId { Editor = 'editor', Hover = 'hover', Notification = 'notification', - EmptyEditorHint = 'emptyEditorHint' + EmptyEditorHint = 'emptyEditorHint', + Comments = 'comments' } const baseProperty: object = { @@ -110,6 +112,10 @@ const configuration: IConfigurationNode = { [AccessibilityVerbositySettingId.EmptyEditorHint]: { description: localize('verbosity.emptyEditorHint', 'Provide information about relevant actions in an empty text editor.'), ...baseProperty + }, + [AccessibilityVerbositySettingId.Comments]: { + description: localize('verbosity.comments', 'Provide information about actions that can be taken in the comment widget or in a file which contains comments.'), + ...baseProperty } } }; diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 11bf6795196d9..0c245548ad0a8 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -37,7 +37,10 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; import { InlineCompletionContextKeys } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionContextKeys'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; +import { CommentAccessibilityHelpNLS } from 'vs/workbench/contrib/comments/browser/comments.contribution'; +import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds'; export class EditorAccessibilityHelpContribution extends Disposable { static ID: 'editorAccessibilityHelpContribution'; @@ -66,7 +69,8 @@ class AccessibilityHelpProvider implements IAccessibleContentProvider { verbositySettingKey = AccessibilityVerbositySettingId.Editor; constructor( private readonly _editor: ICodeEditor, - @IKeybindingService private readonly _keybindingService: IKeybindingService + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { } @@ -96,6 +100,15 @@ class AccessibilityHelpProvider implements IAccessibleContentProvider { } } + const editorContext = this._contextKeyService.getContext(this._editor.getDomNode()!); + if (editorContext.getValue(CommentContextKeys.activeEditorHasCommentingRange.key)) { + content.push(this._descriptionForCommand(CommentCommandId.Add, CommentAccessibilityHelpNLS.addComment, CommentAccessibilityHelpNLS.addCommentNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.NextThread, CommentAccessibilityHelpNLS.nextCommentThreadKb, CommentAccessibilityHelpNLS.nextCommentThreadNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.PreviousThread, CommentAccessibilityHelpNLS.previousCommentThreadKb, CommentAccessibilityHelpNLS.previousCommentThreadNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.NextRange, CommentAccessibilityHelpNLS.nextRange, CommentAccessibilityHelpNLS.nextRangeNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.PreviousRange, CommentAccessibilityHelpNLS.previousRange, CommentAccessibilityHelpNLS.previousRangeNoKb)); + } + if (options.get(EditorOption.stickyScroll).enabled) { content.push(this._descriptionForCommand('editor.action.focusStickyScroll', AccessibilityHelpNLS.stickScrollKb, AccessibilityHelpNLS.stickScrollNoKb)); } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 0b4723b0c507d..4b12128a3d113 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -31,6 +31,10 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { COMMENTS_SECTION, ICommentsConfiguration } from 'vs/workbench/contrib/comments/common/commentsConfiguration'; +import { localize } from 'vs/nls'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; @@ -45,6 +49,7 @@ export class CommentThreadWidget extends private _threadIsEmpty: IContextKey; private _styleElement: HTMLStyleElement; private _commentThreadContextValue: IContextKey; + private _focusedContextKey: IContextKey; private _onDidResize = new Emitter(); onDidResize = this._onDidResize.event; @@ -53,7 +58,6 @@ export class CommentThreadWidget extends get commentThread() { return this._commentThread; } - constructor( readonly container: HTMLElement, private _owner: string, @@ -71,12 +75,14 @@ export class CommentThreadWidget extends }, @ICommentService private commentService: ICommentService, @IContextMenuService contextMenuService: IContextMenuService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IKeybindingService private _keybindingService: IKeybindingService ) { super(); this._threadIsEmpty = CommentContextKeys.commentThreadIsEmpty.bindTo(this._contextKeyService); this._threadIsEmpty.set(!_commentThread.comments || !_commentThread.comments.length); + this._focusedContextKey = CommentContextKeys.commentFocused.bindTo(this._contextKeyService); this._commentMenus = this.commentService.getCommentMenus(this._owner); @@ -111,7 +117,13 @@ export class CommentThreadWidget extends } } })); - + this._register(tracker.onDidFocus(() => this._focusedContextKey.set(true))); + this._register(tracker.onDidBlur(() => this._focusedContextKey.reset())); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(AccessibilityVerbositySettingId.Comments)) { + this._setAriaLabel(); + } + })); this._body = this._scopedInstantiationService.createInstance( CommentThreadBody, this._owner, @@ -124,7 +136,7 @@ export class CommentThreadWidget extends this ) as unknown as CommentThreadBody; this._register(this._body); - + this._setAriaLabel(); this._styleElement = dom.createStyleSheet(this.container); @@ -141,6 +153,21 @@ export class CommentThreadWidget extends this.currentThreadListeners(); } + private _setAriaLabel(): void { + let ariaLabel = localize('commentLabel', "Comment"); + let keybinding: string | undefined; + const verbose = this.configurationService.getValue(AccessibilityVerbositySettingId.Comments); + if (verbose) { + keybinding = this._keybindingService.lookupKeybinding(AccessibilityCommandId.OpenAccessibilityHelp, this._contextKeyService)?.getLabel() ?? undefined; + } + if (keybinding) { + ariaLabel = localize('commentLabelWithKeybinding', "{0}, use ({1}) for accessibility help", ariaLabel, keybinding); + } else { + ariaLabel = localize('commentLabelWithKeybindingNoKeybinding', "{0}, run the command Open Accessibility Help which is currently not triggerable via keybinding.", ariaLabel); + } + this._body.container.ariaLabel = ariaLabel; + } + private updateCurrentThread(hasMouse: boolean, hasFocus: boolean) { if (hasMouse || hasFocus) { this.commentService.setCurrentCommentThread(this.commentThread); diff --git a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts index 63400eb834cb7..a7e9ee0999504 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts @@ -9,6 +9,17 @@ import { Registry } from 'vs/platform/registry/common/platform'; import 'vs/workbench/contrib/comments/browser/commentsEditorContribution'; import { ICommentService, CommentService } from 'vs/workbench/contrib/comments/browser/commentService'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { ctxCommentEditorFocused } from 'vs/workbench/contrib/comments/browser/simpleCommentEditor'; +import * as strings from 'vs/base/common/strings'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; +import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds'; Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ id: 'comments', @@ -54,3 +65,63 @@ Registry.as(ConfigurationExtensions.Configuration).regis }); registerSingleton(ICommentService, CommentService, InstantiationType.Delayed); + + +export namespace CommentAccessibilityHelpNLS { + export const escape = nls.localize('escape', "Dismiss the comment widget via Escape."); + export const nextRange = nls.localize('next', "Navigate to the next commenting range via ({0})."); + export const nextRangeNoKb = nls.localize('nextNoKb', "Run the command: Go to Next Commenting Range, which is currently not triggerable via keybinding."); + export const previousRange = nls.localize('previous', "Navigate to the previous comment range via ({0})."); + export const previousRangeNoKb = nls.localize('previousNoKb', "Run the command: Go to Previous Commenting Range, which is currently not triggerable via keybinding."); + export const nextCommentThreadKb = nls.localize('nextCommentThreadKb', "Navigate to the next comment thread via ({0})."); + export const nextCommentThreadNoKb = nls.localize('nextCommentThreadNoKb', "Run the command: Go to Next Comment Thread, which is currently not triggerable via keybinding."); + export const previousCommentThreadKb = nls.localize('previousCommentThreadKb', "Navigate to the previous comment thread via ({0})."); + export const previousCommentThreadNoKb = nls.localize('previousCommentThreadNoKb', "Run the command: Go to Previous Comment Thread, which is currently not triggerable via keybinding."); + export const addComment = nls.localize('addComment', "Add a comment via ({0})."); + export const addCommentNoKb = nls.localize('addCommentNoKb', "Add a comment via the command: Add Comment on Current Selection, which is currently not triggerable via keybinding."); + export const submitComment = nls.localize('submitComment', "Submit the comment via ({0})."); + export const submitCommentNoKb = nls.localize('submitCommentNoKb', "Submit the comment by navigating with tab to the button, as it's currently not triggerable via keybinding."); +} + +export class CommentsAccessibilityHelpContribution extends Disposable { + static ID: 'commentsAccessibilityHelpContribution'; + constructor() { + super(); + this._register(AccessibilityHelpAction.addImplementation(110, 'comments', accessor => { + const instantiationService = accessor.get(IInstantiationService); + const accessibleViewService = accessor.get(IAccessibleViewService); + accessibleViewService.show(instantiationService.createInstance(CommentsAccessibilityHelpProvider)); + return true; + }, ContextKeyExpr.or(ctxCommentEditorFocused, CommentContextKeys.commentFocused))); + } +} +export class CommentsAccessibilityHelpProvider implements IAccessibleContentProvider { + verbositySettingKey: AccessibilityVerbositySettingId = AccessibilityVerbositySettingId.Comments; + options: IAccessibleViewOptions = { type: AccessibleViewType.Help }; + private _element: HTMLElement | undefined; + constructor( + @IKeybindingService private readonly _keybindingService: IKeybindingService + ) { + + } + private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { + const kb = this._keybindingService.lookupKeybinding(commandId); + if (kb) { + return strings.format(msg, kb.getAriaLabel()); + } + return strings.format(noKbMsg, commandId); + } + provideContent(): string { + this._element = document.activeElement as HTMLElement; + const content: string[] = []; + content.push(CommentAccessibilityHelpNLS.escape); + content.push(this._descriptionForCommand(CommentCommandId.Add, CommentAccessibilityHelpNLS.addComment, CommentAccessibilityHelpNLS.addCommentNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.NextRange, CommentAccessibilityHelpNLS.nextRange, CommentAccessibilityHelpNLS.nextRangeNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.PreviousRange, CommentAccessibilityHelpNLS.previousRange, CommentAccessibilityHelpNLS.previousRangeNoKb)); + content.push(this._descriptionForCommand(CommentCommandId.Submit, CommentAccessibilityHelpNLS.submitComment, CommentAccessibilityHelpNLS.submitCommentNoKb)); + return content.join('\n\n'); + } + onClose(): void { + this._element?.focus(); + } +} diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 23eea52044075..5e42b720516db 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -40,6 +40,9 @@ import { ICursorSelectionChangedEvent } from 'vs/editor/common/cursorEvents'; import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; import { status } from 'vs/base/browser/ui/aria/aria'; import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export const ID = 'editor.contrib.review'; @@ -380,7 +383,8 @@ export class CommentController implements IEditorContribution { @IViewsService private readonly viewsService: IViewsService, @IConfigurationService private readonly configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @IEditorService private readonly editorService: IEditorService + @IEditorService private readonly editorService: IEditorService, + @IKeybindingService private readonly keybindingService: IKeybindingService ) { this._commentInfos = []; this._commentWidgets = []; @@ -848,7 +852,17 @@ export class CommentController implements IEditorContribution { if (this._commentInfos.some(commentInfo => commentInfo.commentingRanges.ranges.length > 0 || commentInfo.commentingRanges.fileComments)) { this._hasRespondedToEditorChange = true; this._activeEditorHasCommentingRange.set(true); - status(nls.localize('hasCommentRanges', "Editor has commenting ranges.")); + const verbose = this.configurationService.getValue(AccessibilityVerbositySettingId.Comments); + if (verbose) { + const keybinding = this.keybindingService.lookupKeybinding(AccessibilityCommandId.OpenAccessibilityHelp)?.getAriaLabel(); + if (keybinding) { + status(nls.localize('hasCommentRangesKb', "Editor has commenting ranges, run the command Open Accessibility Help ({0}), for more information.", keybinding)); + } else { + status(nls.localize('hasCommentRangesNoKb', "Editor has commenting ranges, run the command Open Accessibility Help, which is currently not triggerable via keybinding, for more information.")); + } + } else { + status(nls.localize('hasCommentRanges', "Editor has commenting ranges.")); + } } else { this._activeEditorHasCommentingRange.set(false); } diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 6384fe1e2cd0f..be0a96f0d634f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -5,8 +5,8 @@ import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import 'vs/css!./media/review'; -import { IActiveCodeEditor, ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IActiveCodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import * as nls from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -21,101 +21,91 @@ import { CommentController, ID } from 'vs/workbench/contrib/comments/browser/com import { IRange, Range } from 'vs/editor/common/core/range'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; +import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { accessibilityHelpIsShown, accessibleViewCurrentProviderId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { CommentCommandId } from 'vs/workbench/contrib/comments/common/commentCommandIds'; -export class NextCommentThreadAction extends EditorAction { - constructor() { - super({ - id: 'editor.action.nextCommentThreadAction', - label: nls.localize('nextCommentThreadAction', "Go to Next Comment Thread"), - alias: 'Go to Next Comment Thread', - precondition: undefined, - kbOpts: { - kbExpr: EditorContextKeys.focus, - primary: KeyMod.Alt | KeyCode.F9, - weight: KeybindingWeight.EditorContrib - } - }); - } - - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - const controller = CommentController.get(editor); - controller?.nextCommentThread(); - } -} +registerEditorContribution(ID, CommentController, EditorContributionInstantiation.AfterFirstRender); -export class PreviousCommentThreadAction extends EditorAction { - constructor() { - super({ - id: 'editor.action.previousCommentThreadAction', - label: nls.localize('previousCommentThreadAction', "Go to Previous Comment Thread"), - alias: 'Go to Previous Comment Thread', - precondition: undefined, - kbOpts: { - kbExpr: EditorContextKeys.focus, - primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F9, - weight: KeybindingWeight.EditorContrib - } - }); - } +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: CommentCommandId.NextThread, + handler: async (accessor, args?: { range: IRange; fileComment: boolean }) => { + const activeEditor = getActiveEditor(accessor); + if (!activeEditor) { + return Promise.resolve(); + } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - const controller = CommentController.get(editor); - controller?.previousCommentThread(); - } -} + const controller = CommentController.get(activeEditor); + if (!controller) { + return Promise.resolve(); + } + controller.nextCommentThread(); + }, + weight: KeybindingWeight.EditorContrib, + when: EditorContextKeys.focus, + primary: KeyMod.Alt | KeyCode.F9, +}); -registerEditorContribution(ID, CommentController, EditorContributionInstantiation.AfterFirstRender); -registerEditorAction(NextCommentThreadAction); -registerEditorAction(PreviousCommentThreadAction); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: CommentCommandId.PreviousThread, + handler: async (accessor, args?: { range: IRange; fileComment: boolean }) => { + const activeEditor = getActiveEditor(accessor); + if (!activeEditor) { + return Promise.resolve(); + } -export class NextCommentingRangeAction extends EditorAction { - constructor() { - super({ - id: 'editor.action.goToNextCommentingRange', - label: nls.localize('goToNextCommentingRange', "Go to Next Commenting Range"), - alias: 'Go to Next Commenting Range', - precondition: CommentContextKeys.WorkspaceHasCommenting, - kbOpts: { - kbExpr: EditorContextKeys.focus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.DownArrow), - weight: KeybindingWeight.EditorContrib - } - }); - } + const controller = CommentController.get(activeEditor); + if (!controller) { + return Promise.resolve(); + } + controller.previousCommentThread(); + }, + weight: KeybindingWeight.EditorContrib, + when: EditorContextKeys.focus, + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F9 +}); - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - const controller = CommentController.get(editor); - controller?.nextCommentingRange(); - } -} +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: CommentCommandId.NextRange, + handler: async (accessor, args?: { range: IRange; fileComment: boolean }) => { + const activeEditor = getActiveEditor(accessor); + if (!activeEditor) { + return Promise.resolve(); + } -export class PreviousCommentingRangeAction extends EditorAction { - constructor() { - super({ - id: 'editor.action.goToPreviousCommentingRange', - label: nls.localize('goToPreviousCommentingRange', "Go to Previous Commenting Range"), - alias: 'Go to Next Commenting Range', - precondition: CommentContextKeys.WorkspaceHasCommenting, - kbOpts: { - kbExpr: EditorContextKeys.focus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.UpArrow), - weight: KeybindingWeight.EditorContrib - } - }); - } + const controller = CommentController.get(activeEditor); + if (!controller) { + return Promise.resolve(); + } + controller.nextCommentingRange(); + }, + when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, ContextKeyExpr.or(EditorContextKeys.focus, CommentContextKeys.commentFocused, ContextKeyExpr.and(accessibilityHelpIsShown, accessibleViewCurrentProviderId.isEqualTo(AccessibleViewProviderId.Comments)))), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.DownArrow), + weight: KeybindingWeight.EditorContrib +}); - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { - const controller = CommentController.get(editor); - controller?.previousCommentingRange(); - } -} +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: CommentCommandId.PreviousRange, + handler: async (accessor, args?: { range: IRange; fileComment: boolean }) => { + const activeEditor = getActiveEditor(accessor); + if (!activeEditor) { + return Promise.resolve(); + } -registerEditorAction(NextCommentingRangeAction); -registerEditorAction(PreviousCommentingRangeAction); + const controller = CommentController.get(activeEditor); + if (!controller) { + return Promise.resolve(); + } + controller.previousCommentingRange(); + }, + when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, ContextKeyExpr.or(EditorContextKeys.focus, CommentContextKeys.commentFocused, ContextKeyExpr.and(accessibilityHelpIsShown, accessibleViewCurrentProviderId.isEqualTo(AccessibleViewProviderId.Comments)))), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.UpArrow), + weight: KeybindingWeight.EditorContrib +}); -const TOGGLE_COMMENTING_COMMAND = 'workbench.action.toggleCommenting'; CommandsRegistry.registerCommand({ - id: TOGGLE_COMMENTING_COMMAND, + id: CommentCommandId.ToggleCommenting, handler: (accessor) => { const commentService = accessor.get(ICommentService); const enable = commentService.isCommentingEnabled; @@ -125,16 +115,15 @@ CommandsRegistry.registerCommand({ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: TOGGLE_COMMENTING_COMMAND, + id: CommentCommandId.ToggleCommenting, title: nls.localize('comments.toggleCommenting', "Toggle Editor Commenting"), category: 'Comments', }, when: CommentContextKeys.WorkspaceHasCommenting }); -const ADD_COMMENT_COMMAND = 'workbench.action.addComment'; KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: ADD_COMMENT_COMMAND, + id: CommentCommandId.Add, handler: async (accessor, args?: { range: IRange; fileComment: boolean }) => { const activeEditor = getActiveEditor(accessor); if (!activeEditor) { @@ -161,16 +150,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: ADD_COMMENT_COMMAND, + id: CommentCommandId.Add, title: nls.localize('comments.addCommand', "Add Comment on Current Selection"), category: 'Comments' }, when: CommentContextKeys.activeCursorHasCommentingRange }); -const COLLAPSE_ALL_COMMENT_COMMAND = 'workbench.action.collapseAllComments'; CommandsRegistry.registerCommand({ - id: COLLAPSE_ALL_COMMENT_COMMAND, + id: CommentCommandId.CollapseAll, handler: (accessor) => { return getActiveController(accessor)?.collapseAll(); } @@ -178,16 +166,15 @@ CommandsRegistry.registerCommand({ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: COLLAPSE_ALL_COMMENT_COMMAND, + id: CommentCommandId.CollapseAll, title: nls.localize('comments.collapseAll', "Collapse All Comments"), category: 'Comments' }, when: CommentContextKeys.WorkspaceHasCommenting }); -const EXPAND_ALL_COMMENT_COMMAND = 'workbench.action.expandAllComments'; CommandsRegistry.registerCommand({ - id: EXPAND_ALL_COMMENT_COMMAND, + id: CommentCommandId.ExpandAll, handler: (accessor) => { return getActiveController(accessor)?.expandAll(); } @@ -195,16 +182,15 @@ CommandsRegistry.registerCommand({ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: EXPAND_ALL_COMMENT_COMMAND, + id: CommentCommandId.ExpandAll, title: nls.localize('comments.expandAll', "Expand All Comments"), category: 'Comments' }, when: CommentContextKeys.WorkspaceHasCommenting }); -const EXPAND_UNRESOLVED_COMMENT_COMMAND = 'workbench.action.expandUnresolvedComments'; CommandsRegistry.registerCommand({ - id: EXPAND_UNRESOLVED_COMMENT_COMMAND, + id: CommentCommandId.ExpandUnresolved, handler: (accessor) => { return getActiveController(accessor)?.expandUnresolved(); } @@ -212,7 +198,7 @@ CommandsRegistry.registerCommand({ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: EXPAND_UNRESOLVED_COMMENT_COMMAND, + id: CommentCommandId.ExpandUnresolved, title: nls.localize('comments.expandUnresolved', "Expand Unresolved Comments"), category: 'Comments' }, @@ -220,7 +206,7 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.submitComment', + id: CommentCommandId.Submit, weight: KeybindingWeight.EditorContrib, primary: KeyMod.CtrlCmd | KeyCode.Enter, when: ctxCommentEditorFocused, @@ -233,7 +219,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.hideComment', + id: CommentCommandId.Hide, weight: KeybindingWeight.EditorContrib, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], diff --git a/src/vs/workbench/contrib/comments/common/commentCommandIds.ts b/src/vs/workbench/contrib/comments/common/commentCommandIds.ts new file mode 100644 index 0000000000000..8f0c14875de25 --- /dev/null +++ b/src/vs/workbench/contrib/comments/common/commentCommandIds.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const enum CommentCommandId { + Add = 'workbench.action.addComment', + NextThread = 'editor.action.nextCommentThreadAction', + PreviousThread = 'editor.action.previousCommentThreadAction', + NextRange = 'editor.action.nextCommentingRange', + PreviousRange = 'editor.action.previousCommentingRange', + ToggleCommenting = 'workbench.action.toggleCommenting', + Submit = 'editor.action.submitComment', + Hide = 'workbench.action.hideComment', + CollapseAll = 'workbench.action.collapseAllComments', + ExpandAll = 'workbench.action.expandAllComments', + ExpandUnresolved = 'workbench.action.expandUnresolvedComments' +} diff --git a/src/vs/workbench/contrib/comments/common/commentContextKeys.ts b/src/vs/workbench/contrib/comments/common/commentContextKeys.ts index f792163c11868..e48f7a59d7315 100644 --- a/src/vs/workbench/contrib/comments/common/commentContextKeys.ts +++ b/src/vs/workbench/contrib/comments/common/commentContextKeys.ts @@ -53,4 +53,9 @@ export namespace CommentContextKeys { * The comment controller id associated with a comment thread. */ export const commentControllerContext = new RawContextKey('commentController', undefined, { type: 'string', description: nls.localize('commentController', "The comment controller id associated with a comment thread") }); + + /** + * The comment widget is focused. + */ + export const commentFocused = new RawContextKey('commentFocused', false, { type: 'boolean', description: nls.localize('commentFocused', "Set when the comment is focused") }); }