add accessible view for comment thread widget (#228018)
parent
2c9654d473
commit
15183fce60
|
@ -7,7 +7,7 @@
|
|||
{
|
||||
"kind": 2,
|
||||
"language": "github-issues",
|
||||
"value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"August 2024\"\n\n$MINE=assignee:@me"
|
||||
"value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"September 2024\"\n\n$MINE=assignee:@me"
|
||||
},
|
||||
{
|
||||
"kind": 1,
|
||||
|
|
|
@ -28,6 +28,7 @@ export const enum AccessibleViewProviderId {
|
|||
Notification = 'notification',
|
||||
EmptyEditorHint = 'emptyEditorHint',
|
||||
Comments = 'comments',
|
||||
CommentThread = 'commentThread',
|
||||
Repl = 'repl',
|
||||
ReplHelp = 'replHelp',
|
||||
RunAndDebug = 'runAndDebug',
|
||||
|
|
|
@ -286,7 +286,13 @@ export class MainThreadCommentController implements ICommentController {
|
|||
private _features: CommentProviderFeatures
|
||||
) { }
|
||||
|
||||
get activeComment() {
|
||||
return this._activeComment;
|
||||
}
|
||||
|
||||
private _activeComment: { thread: languages.CommentThread; comment?: languages.Comment } | undefined;
|
||||
async setActiveCommentAndThread(commentInfo: { thread: languages.CommentThread; comment?: languages.Comment } | undefined) {
|
||||
this._activeComment = commentInfo;
|
||||
return this._proxy.$setActiveComment(this._handle, commentInfo ? { commentThreadHandle: commentInfo.thread.commentThreadHandle, uniqueIdInThread: commentInfo.comment?.uniqueIdInThread } : undefined);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ export interface ICommentController {
|
|||
options?: CommentOptions;
|
||||
contextValue?: string;
|
||||
owner: string;
|
||||
activeComment: { thread: CommentThread; comment?: Comment } | undefined;
|
||||
createCommentThreadTemplate(resource: UriComponents, range: IRange | undefined, editorId?: string): Promise<void>;
|
||||
updateCommentThreadTemplate(threadHandle: number, range: IRange): Promise<void>;
|
||||
deleteCommentThreadMain(commentThreadId: string): void;
|
||||
|
@ -91,6 +92,7 @@ export interface ICommentService {
|
|||
readonly onDidChangeCommentingEnabled: Event<boolean>;
|
||||
readonly isCommentingEnabled: boolean;
|
||||
readonly commentsModel: ICommentsModel;
|
||||
readonly lastActiveCommentcontroller: ICommentController | undefined;
|
||||
setDocumentComments(resource: URI, commentInfos: ICommentInfo[]): void;
|
||||
setWorkspaceComments(uniqueOwner: string, commentsByResource: CommentThread<IRange | ICellRange>[]): void;
|
||||
removeWorkspaceComments(uniqueOwner: string): void;
|
||||
|
@ -295,6 +297,10 @@ export class CommentService extends Disposable implements ICommentService {
|
|||
this._onDidChangeActiveEditingCommentThread.fire(commentThread);
|
||||
}
|
||||
|
||||
get lastActiveCommentcontroller() {
|
||||
return this._lastActiveCommentController;
|
||||
}
|
||||
|
||||
private _lastActiveCommentController: ICommentController | undefined;
|
||||
async setActiveCommentAndThread(uniqueOwner: string, commentInfo: { thread: CommentThread<IRange>; comment?: Comment } | undefined) {
|
||||
const commentController = this._commentControls.get(uniqueOwner);
|
||||
|
|
|
@ -28,7 +28,7 @@ import { MarshalledCommentThreadInternal } from '../../../common/comments.js';
|
|||
import { accessibleViewCurrentProviderId, accessibleViewIsShown } from '../../accessibility/browser/accessibilityConfiguration.js';
|
||||
import { AccessibleViewProviderId } from '../../../../platform/accessibility/browser/accessibleView.js';
|
||||
import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
|
||||
import { CommentsAccessibleView } from './commentsAccessibleView.js';
|
||||
import { CommentsAccessibleView, CommentThreadAccessibleView } from './commentsAccessibleView.js';
|
||||
import { CommentsAccessibilityHelp } from './commentsAccessibility.js';
|
||||
|
||||
registerAction2(class Collapse extends ViewAction<CommentsPanel> {
|
||||
|
@ -193,4 +193,5 @@ export class UnresolvedCommentsBadge extends Disposable implements IWorkbenchCon
|
|||
Registry.as<IWorkbenchContributionsRegistry>(Extensions.Workbench).registerWorkbenchContribution(UnresolvedCommentsBadge, LifecyclePhase.Eventually);
|
||||
|
||||
AccessibleViewRegistry.register(new CommentsAccessibleView());
|
||||
AccessibleViewRegistry.register(new CommentThreadAccessibleView());
|
||||
AccessibleViewRegistry.register(new CommentsAccessibilityHelp());
|
||||
|
|
|
@ -14,6 +14,15 @@ import { AccessibilityVerbositySettingId } from '../../accessibility/browser/acc
|
|||
import { COMMENTS_VIEW_ID, CommentsMenus } from './commentsTreeViewer.js';
|
||||
import { CommentsPanel, CONTEXT_KEY_COMMENT_FOCUSED } from './commentsView.js';
|
||||
import { IViewsService } from '../../../services/views/common/viewsService.js';
|
||||
import { ICommentService } from './commentService.js';
|
||||
import { CommentContextKeys } from '../common/commentContextKeys.js';
|
||||
import { moveToNextCommentInThread as findNextCommentInThread, revealCommentThread } from './commentsController.js';
|
||||
import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
|
||||
import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { CommentThread, Comment } from '../../../../editor/common/languages.js';
|
||||
import { IRange } from '../../../../editor/common/core/range.js';
|
||||
|
||||
export class CommentsAccessibleView extends Disposable implements IAccessibleViewImplentation {
|
||||
readonly priority = 90;
|
||||
|
@ -26,6 +35,7 @@ export class CommentsAccessibleView extends Disposable implements IAccessibleVie
|
|||
const menuService = accessor.get(IMenuService);
|
||||
const commentsView = viewsService.getActiveViewWithId<CommentsPanel>(COMMENTS_VIEW_ID);
|
||||
const focusedCommentNode = commentsView?.focusedCommentNode;
|
||||
|
||||
if (!commentsView || !focusedCommentNode) {
|
||||
return;
|
||||
}
|
||||
|
@ -39,6 +49,28 @@ export class CommentsAccessibleView extends Disposable implements IAccessibleVie
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export class CommentThreadAccessibleView extends Disposable implements IAccessibleViewImplentation {
|
||||
readonly priority = 85;
|
||||
readonly name = 'commentThread';
|
||||
readonly when = CommentContextKeys.commentFocused;
|
||||
readonly type = AccessibleViewType.View;
|
||||
getProvider(accessor: ServicesAccessor) {
|
||||
const commentService = accessor.get(ICommentService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const uriIdentityService = accessor.get(IUriIdentityService);
|
||||
const threads = commentService.commentsModel.hasCommentThreads();
|
||||
if (!threads) {
|
||||
return;
|
||||
}
|
||||
return new CommentsThreadWidgetAccessibleContentProvider(commentService, editorService, uriIdentityService);
|
||||
}
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CommentsAccessibleContentProvider extends Disposable implements IAccessibleViewContentProvider {
|
||||
constructor(
|
||||
private readonly _commentsView: CommentsPanel,
|
||||
|
@ -84,3 +116,68 @@ class CommentsAccessibleContentProvider extends Disposable implements IAccessibl
|
|||
return this.provideContent();
|
||||
}
|
||||
}
|
||||
|
||||
class CommentsThreadWidgetAccessibleContentProvider extends Disposable implements IAccessibleViewContentProvider {
|
||||
readonly id = AccessibleViewProviderId.CommentThread;
|
||||
readonly verbositySettingKey = AccessibilityVerbositySettingId.Comments;
|
||||
readonly options = { type: AccessibleViewType.View };
|
||||
private _activeCommentInfo: { thread: CommentThread<IRange>; comment?: Comment } | undefined;
|
||||
constructor(@ICommentService private readonly _commentService: ICommentService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
private get activeCommentInfo(): { thread: CommentThread<IRange>; comment?: Comment } | undefined {
|
||||
if (!this._activeCommentInfo && this._commentService.lastActiveCommentcontroller) {
|
||||
this._activeCommentInfo = this._commentService.lastActiveCommentcontroller.activeComment;
|
||||
}
|
||||
return this._activeCommentInfo;
|
||||
}
|
||||
|
||||
provideContent(): string {
|
||||
if (!this.activeCommentInfo) {
|
||||
throw new Error('No current comment thread');
|
||||
}
|
||||
const comment = this.activeCommentInfo.comment?.body;
|
||||
const commentLabel = typeof comment === 'string' ? comment : comment?.value ?? '';
|
||||
const resource = this.activeCommentInfo.thread.resource;
|
||||
const range = this.activeCommentInfo.thread.range;
|
||||
let contentLabel = '';
|
||||
if (resource && range) {
|
||||
const editor = this._editorService.findEditors(URI.parse(resource)) || [];
|
||||
const codeEditor = this._editorService.activeEditorPane?.getControl();
|
||||
if (editor?.length && isCodeEditor(codeEditor)) {
|
||||
const content = codeEditor.getModel()?.getValueInRange(range);
|
||||
if (content) {
|
||||
contentLabel = '\nCorresponding code: \n' + content;
|
||||
}
|
||||
}
|
||||
}
|
||||
return commentLabel + contentLabel;
|
||||
}
|
||||
onClose(): void {
|
||||
const lastComment = this._activeCommentInfo;
|
||||
this._activeCommentInfo = undefined;
|
||||
if (lastComment) {
|
||||
revealCommentThread(this._commentService, this._editorService, this._uriIdentityService, lastComment.thread, lastComment.comment);
|
||||
}
|
||||
}
|
||||
provideNextContent(): string | undefined {
|
||||
const newCommentInfo = findNextCommentInThread(this._activeCommentInfo, 'next');
|
||||
if (newCommentInfo) {
|
||||
this._activeCommentInfo = newCommentInfo;
|
||||
return this.provideContent();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
providePreviousContent(): string | undefined {
|
||||
const newCommentInfo = findNextCommentInThread(this._activeCommentInfo, 'previous');
|
||||
if (newCommentInfo) {
|
||||
this._activeCommentInfo = newCommentInfo;
|
||||
return this.provideContent();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -368,6 +368,34 @@ class CommentingRangeDecorator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the next or previous comment in the current thread.
|
||||
* @param type
|
||||
*/
|
||||
export function moveToNextCommentInThread(commentInfo: { thread: languages.CommentThread<IRange>; comment?: languages.Comment } | undefined, type: 'next' | 'previous') {
|
||||
if (!commentInfo?.comment || !commentInfo?.thread?.comments) {
|
||||
return;
|
||||
}
|
||||
const currentIndex = commentInfo.thread.comments?.indexOf(commentInfo.comment);
|
||||
if (currentIndex === undefined || currentIndex < 0) {
|
||||
return;
|
||||
}
|
||||
if (type === 'previous' && currentIndex === 0) {
|
||||
return;
|
||||
}
|
||||
if (type === 'next' && currentIndex === commentInfo.thread.comments.length - 1) {
|
||||
return;
|
||||
}
|
||||
const comment = commentInfo.thread.comments?.[type === 'previous' ? currentIndex - 1 : currentIndex + 1];
|
||||
if (!comment) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
...commentInfo,
|
||||
comment,
|
||||
};
|
||||
}
|
||||
|
||||
export function revealCommentThread(commentService: ICommentService, editorService: IEditorService, uriIdentityService: IUriIdentityService,
|
||||
commentThread: languages.CommentThread<IRange>, comment: languages.Comment | undefined, focusReply?: boolean, pinned?: boolean, preserveFocus?: boolean, sideBySide?: boolean): void {
|
||||
if (!commentThread.resource) {
|
||||
|
|
|
@ -49,6 +49,7 @@ class TestCommentThread implements CommentThread<IRange> {
|
|||
}
|
||||
|
||||
class TestCommentController implements ICommentController {
|
||||
activeComment: { thread: CommentThread; comment?: Comment } | undefined;
|
||||
id: string = 'test';
|
||||
label: string = 'Test Comments';
|
||||
owner: string = 'test';
|
||||
|
|
Loading…
Reference in New Issue