Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Disposable } from '../../../../base/common/lifecycle.js';
7
import { MarshalledId } from '../../../../base/common/marshallingIds.js';
8
import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js';
9
import { AccessibleViewProviderId, AccessibleViewType, IAccessibleViewContentProvider } from '../../../../platform/accessibility/browser/accessibleView.js';
10
import { IAccessibleViewImplementation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
11
import { IMenuService } from '../../../../platform/actions/common/actions.js';
12
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
13
import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js';
14
import { COMMENTS_VIEW_ID, CommentsMenus } from './commentsTreeViewer.js';
15
import { CommentsPanel, CONTEXT_KEY_COMMENT_FOCUSED } from './commentsView.js';
16
import { IViewsService } from '../../../services/views/common/viewsService.js';
17
import { ICommentService } from './commentService.js';
18
import { CommentContextKeys } from '../common/commentContextKeys.js';
19
import { moveToNextCommentInThread as findNextCommentInThread, revealCommentThread } from './commentsController.js';
20
import { IEditorService } from '../../../services/editor/common/editorService.js';
21
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
22
import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js';
23
import { URI } from '../../../../base/common/uri.js';
24
import { CommentThread, Comment } from '../../../../editor/common/languages.js';
25
import { IRange } from '../../../../editor/common/core/range.js';
26
import { IAction } from '../../../../base/common/actions.js';
27
28
export class CommentsAccessibleView extends Disposable implements IAccessibleViewImplementation {
29
readonly priority = 90;
30
readonly name = 'comment';
31
readonly when = CONTEXT_KEY_COMMENT_FOCUSED;
32
readonly type = AccessibleViewType.View;
33
getProvider(accessor: ServicesAccessor) {
34
const contextKeyService = accessor.get(IContextKeyService);
35
const viewsService = accessor.get(IViewsService);
36
const menuService = accessor.get(IMenuService);
37
const commentsView = viewsService.getActiveViewWithId<CommentsPanel>(COMMENTS_VIEW_ID);
38
const focusedCommentNode = commentsView?.focusedCommentNode;
39
40
if (!commentsView || !focusedCommentNode) {
41
return;
42
}
43
const menus = this._register(new CommentsMenus(menuService));
44
menus.setContextKeyService(contextKeyService);
45
46
return new CommentsAccessibleContentProvider(commentsView, focusedCommentNode, menus);
47
}
48
constructor() {
49
super();
50
}
51
}
52
53
54
export class CommentThreadAccessibleView extends Disposable implements IAccessibleViewImplementation {
55
readonly priority = 85;
56
readonly name = 'commentThread';
57
readonly when = CommentContextKeys.commentFocused;
58
readonly type = AccessibleViewType.View;
59
getProvider(accessor: ServicesAccessor) {
60
const commentService = accessor.get(ICommentService);
61
const editorService = accessor.get(IEditorService);
62
const uriIdentityService = accessor.get(IUriIdentityService);
63
const threads = commentService.commentsModel.hasCommentThreads();
64
if (!threads) {
65
return;
66
}
67
return new CommentsThreadWidgetAccessibleContentProvider(commentService, editorService, uriIdentityService);
68
}
69
constructor() {
70
super();
71
}
72
}
73
74
75
class CommentsAccessibleContentProvider extends Disposable implements IAccessibleViewContentProvider {
76
public readonly actions: IAction[];
77
constructor(
78
private readonly _commentsView: CommentsPanel,
79
private readonly _focusedCommentNode: any,
80
private readonly _menus: CommentsMenus,
81
) {
82
super();
83
84
this.actions = [...this._menus.getResourceContextActions(this._focusedCommentNode)].filter(i => i.enabled).map(action => {
85
return {
86
...action,
87
run: () => {
88
this._commentsView.focus();
89
action.run({
90
thread: this._focusedCommentNode.thread,
91
$mid: MarshalledId.CommentThread,
92
commentControlHandle: this._focusedCommentNode.controllerHandle,
93
commentThreadHandle: this._focusedCommentNode.threadHandle,
94
});
95
}
96
};
97
});
98
}
99
readonly id = AccessibleViewProviderId.Comments;
100
readonly verbositySettingKey = AccessibilityVerbositySettingId.Comments;
101
readonly options = { type: AccessibleViewType.View };
102
103
provideContent(): string {
104
const commentNode = this._commentsView.focusedCommentNode;
105
const content = this._commentsView.focusedCommentInfo?.toString();
106
if (!commentNode || !content) {
107
throw new Error('Comment tree is focused but no comment is selected');
108
}
109
return content;
110
}
111
onClose(): void {
112
this._commentsView.focus();
113
}
114
provideNextContent(): string | undefined {
115
this._commentsView.focusNextNode();
116
return this.provideContent();
117
}
118
providePreviousContent(): string | undefined {
119
this._commentsView.focusPreviousNode();
120
return this.provideContent();
121
}
122
}
123
124
class CommentsThreadWidgetAccessibleContentProvider extends Disposable implements IAccessibleViewContentProvider {
125
readonly id = AccessibleViewProviderId.CommentThread;
126
readonly verbositySettingKey = AccessibilityVerbositySettingId.Comments;
127
readonly options = { type: AccessibleViewType.View };
128
private _activeCommentInfo: { thread: CommentThread<IRange>; comment?: Comment } | undefined;
129
constructor(@ICommentService private readonly _commentService: ICommentService,
130
@IEditorService private readonly _editorService: IEditorService,
131
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
132
) {
133
super();
134
}
135
136
private get activeCommentInfo(): { thread: CommentThread<IRange>; comment?: Comment } | undefined {
137
if (!this._activeCommentInfo && this._commentService.lastActiveCommentcontroller) {
138
this._activeCommentInfo = this._commentService.lastActiveCommentcontroller.activeComment;
139
}
140
return this._activeCommentInfo;
141
}
142
143
provideContent(): string {
144
if (!this.activeCommentInfo) {
145
throw new Error('No current comment thread');
146
}
147
const comment = this.activeCommentInfo.comment?.body;
148
const commentLabel = typeof comment === 'string' ? comment : comment?.value ?? '';
149
const resource = this.activeCommentInfo.thread.resource;
150
const range = this.activeCommentInfo.thread.range;
151
let contentLabel = '';
152
if (resource && range) {
153
const editor = this._editorService.findEditors(URI.parse(resource)) || [];
154
const codeEditor = this._editorService.activeEditorPane?.getControl();
155
if (editor?.length && isCodeEditor(codeEditor)) {
156
const content = codeEditor.getModel()?.getValueInRange(range);
157
if (content) {
158
contentLabel = '\nCorresponding code: \n' + content;
159
}
160
}
161
}
162
return commentLabel + contentLabel;
163
}
164
onClose(): void {
165
const lastComment = this._activeCommentInfo;
166
this._activeCommentInfo = undefined;
167
if (lastComment) {
168
revealCommentThread(this._commentService, this._editorService, this._uriIdentityService, lastComment.thread, lastComment.comment);
169
}
170
}
171
provideNextContent(): string | undefined {
172
const newCommentInfo = findNextCommentInThread(this._activeCommentInfo, 'next');
173
if (newCommentInfo) {
174
this._activeCommentInfo = newCommentInfo;
175
return this.provideContent();
176
}
177
return undefined;
178
}
179
providePreviousContent(): string | undefined {
180
const newCommentInfo = findNextCommentInThread(this._activeCommentInfo, 'previous');
181
if (newCommentInfo) {
182
this._activeCommentInfo = newCommentInfo;
183
return this.provideContent();
184
}
185
return undefined;
186
}
187
}
188
189