@@ -9,8 +9,8 @@ import * as dom from 'vs/base/browser/dom';
9
9
import { basename } from 'vs/base/common/resources' ;
10
10
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
11
11
import { IThemeService } from 'vs/platform/theme/common/themeService' ;
12
- import { CommentNode , ResourceWithCommentThreads , ICommentThreadChangedEvent } from 'vs/workbench/contrib/comments/common/commentModel' ;
13
- import { IWorkspaceCommentThreadsEvent , ICommentService } from 'vs/workbench/contrib/comments/browser/commentService' ;
12
+ import { CommentNode , ICommentThreadChangedEvent , ResourceWithCommentThreads } from 'vs/workbench/contrib/comments/common/commentModel' ;
13
+ import { ICommentService , IWorkspaceCommentThreadsEvent } from 'vs/workbench/contrib/comments/browser/commentService' ;
14
14
import { IEditorService } from 'vs/workbench/services/editor/common/editorService' ;
15
15
import { ResourceLabels } from 'vs/workbench/browser/labels' ;
16
16
import { CommentsList , COMMENTS_VIEW_TITLE , Filter } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer' ;
@@ -35,6 +35,8 @@ import { revealCommentThread } from 'vs/workbench/contrib/comments/browser/comme
35
35
import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands' ;
36
36
import { CommentsModel , ICommentsModel } from 'vs/workbench/contrib/comments/browser/commentsModel' ;
37
37
import { IHoverService } from 'vs/platform/hover/browser/hover' ;
38
+ import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration' ;
39
+ import { AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions' ;
38
40
39
41
export const CONTEXT_KEY_HAS_COMMENTS = new RawContextKey < boolean > ( 'commentsView.hasComments' , false ) ;
40
42
export const CONTEXT_KEY_SOME_COMMENTS_EXPANDED = new RawContextKey < boolean > ( 'commentsView.someCommentsExpanded' , false ) ;
@@ -68,6 +70,58 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView {
68
70
69
71
readonly onDidChangeVisibility = this . onDidChangeBodyVisibility ;
70
72
73
+ get focusedCommentNode ( ) : CommentNode | undefined {
74
+ const focused = this . tree ?. getFocus ( ) ;
75
+ if ( focused ?. length === 1 && focused [ 0 ] instanceof CommentNode ) {
76
+ return focused [ 0 ] ;
77
+ }
78
+ return undefined ;
79
+ }
80
+
81
+ get focusedCommentInfo ( ) : string | undefined {
82
+ const focused = this . focusedCommentNode ;
83
+ if ( ! focused ) {
84
+ return ;
85
+ }
86
+ return this . getScreenReaderInfoForNode ( focused , 'accessibleViewContent' ) ;
87
+ }
88
+
89
+ focusNextNode ( ) : void {
90
+ if ( ! this . tree ) {
91
+ return ;
92
+ }
93
+ const focused = this . tree . getFocus ( ) ?. [ 0 ] ;
94
+ if ( ! focused ) {
95
+ return ;
96
+ }
97
+ let next = this . tree . navigate ( focused ) . next ( ) ;
98
+ while ( next && ! ( next instanceof CommentNode ) ) {
99
+ next = this . tree . navigate ( next ) . next ( ) ;
100
+ }
101
+ if ( ! next ) {
102
+ return ;
103
+ }
104
+ this . tree . setFocus ( [ next ] ) ;
105
+ }
106
+
107
+ focusPreviousNode ( ) : void {
108
+ if ( ! this . tree ) {
109
+ return ;
110
+ }
111
+ const focused = this . tree . getFocus ( ) ?. [ 0 ] ;
112
+ if ( ! focused ) {
113
+ return ;
114
+ }
115
+ let previous = this . tree . navigate ( focused ) . previous ( ) ;
116
+ while ( previous && ! ( previous instanceof CommentNode ) ) {
117
+ previous = this . tree . navigate ( previous ) . previous ( ) ;
118
+ }
119
+ if ( ! previous ) {
120
+ return ;
121
+ }
122
+ this . tree . setFocus ( [ previous ] ) ;
123
+ }
124
+
71
125
constructor (
72
126
options : IViewPaneOptions ,
73
127
@IInstantiationService instantiationService : IInstantiationService ,
@@ -263,46 +317,63 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView {
263
317
this . messageBoxContainer . classList . toggle ( 'hidden' , this . commentService . commentsModel . hasCommentThreads ( ) ) ;
264
318
}
265
319
266
- private getAriaForNode ( element : CommentNode ) {
320
+ private getScreenReaderInfoForNode ( element : CommentNode , type : 'ariaLabel' | 'accessibleViewContent' ) {
321
+ let accessibleViewHint = '' ;
322
+ if ( type === 'ariaLabel' && this . configurationService . getValue ( AccessibilityVerbositySettingId . Comments ) ) {
323
+ const kbLabel = this . keybindingService . lookupKeybinding ( AccessibleViewAction . id ) ?. getAriaLabel ( ) ;
324
+ accessibleViewHint = kbLabel ? nls . localize ( 'acessibleViewHint' , "Inspect this in the accessible view ({0}).\n" , kbLabel ) : nls . localize ( 'acessibleViewHintNoKbOpen' , "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.\n" ) ;
325
+ }
326
+ const replies = this . getRepliesAsString ( element , type ) || '' ;
267
327
if ( element . range ) {
268
328
if ( element . threadRelevance === CommentThreadApplicability . Outdated ) {
269
- return nls . localize ( 'resourceWithCommentLabelOutdated' ,
270
- "Outdated from ${0} at line {1} column {2} in {3}, source : {4}" ,
329
+ return accessibleViewHint + nls . localize ( 'resourceWithCommentLabelOutdated' ,
330
+ "Outdated from ${0} at line {1} column {2} in {3}, comment : {4}" ,
271
331
element . comment . userName ,
272
332
element . range . startLineNumber ,
273
333
element . range . startColumn ,
274
334
basename ( element . resource ) ,
275
335
( typeof element . comment . body === 'string' ) ? element . comment . body : element . comment . body . value
276
- ) ;
336
+ ) + replies ;
277
337
} else {
278
- return nls . localize ( 'resourceWithCommentLabel' ,
279
- "${0} at line {1} column {2} in {3}, source : {4}" ,
338
+ return accessibleViewHint + nls . localize ( 'resourceWithCommentLabel' ,
339
+ "${0} at line {1} column {2} in {3}, comment : {4}" ,
280
340
element . comment . userName ,
281
341
element . range . startLineNumber ,
282
342
element . range . startColumn ,
283
343
basename ( element . resource ) ,
284
- ( typeof element . comment . body === 'string' ) ? element . comment . body : element . comment . body . value
285
- ) ;
344
+ ( typeof element . comment . body === 'string' ) ? element . comment . body : element . comment . body . value ,
345
+ ) + replies ;
286
346
}
287
347
} else {
288
348
if ( element . threadRelevance === CommentThreadApplicability . Outdated ) {
289
- return nls . localize ( 'resourceWithCommentLabelFileOutdated' ,
290
- "Outdated from {0} in {1}, source : {2}" ,
349
+ return accessibleViewHint + nls . localize ( 'resourceWithCommentLabelFileOutdated' ,
350
+ "Outdated from {0} in {1}, comment : {2}" ,
291
351
element . comment . userName ,
292
352
basename ( element . resource ) ,
293
353
( typeof element . comment . body === 'string' ) ? element . comment . body : element . comment . body . value
294
- ) ;
354
+ ) + replies ;
295
355
} else {
296
- return nls . localize ( 'resourceWithCommentLabelFile' ,
297
- "{0} in {1}, source : {2}" ,
356
+ return accessibleViewHint + nls . localize ( 'resourceWithCommentLabelFile' ,
357
+ "{0} in {1}, comment : {2}" ,
298
358
element . comment . userName ,
299
359
basename ( element . resource ) ,
300
360
( typeof element . comment . body === 'string' ) ? element . comment . body : element . comment . body . value
301
- ) ;
361
+ ) + replies ;
302
362
}
303
363
}
304
364
}
305
365
366
+ private getRepliesAsString ( node : CommentNode , type : 'ariaLabel' | 'accessibleViewContent' ) : string {
367
+ if ( ! node . replies . length || type === 'ariaLabel' ) {
368
+ return '' ;
369
+ }
370
+ return nls . localize ( 'replies' , " {0} replies:\n{1}" , node . replies . length , node . replies . map ( reply => nls . localize ( 'resourceWithRepliesLabel' ,
371
+ "${0} {1}" ,
372
+ reply . comment . userName ,
373
+ ( typeof reply . comment . body === 'string' ) ? reply . comment . body : reply . comment . body . value )
374
+ ) . join ( '\n' ) ) ;
375
+ }
376
+
306
377
private createTree ( ) : void {
307
378
this . treeLabels = this . _register ( this . instantiationService . createInstance ( ResourceLabels , this ) ) ;
308
379
this . tree = this . _register ( this . instantiationService . createInstance ( CommentsList , this . treeLabels , this . treeContainer , {
@@ -323,7 +394,7 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView {
323
394
return nls . localize ( 'resourceWithCommentThreadsLabel' , "Comments in {0}, full path {1}" , basename ( element . resource ) , element . resource . fsPath ) ;
324
395
}
325
396
if ( element instanceof CommentNode ) {
326
- return this . getAriaForNode ( element ) ;
397
+ return this . getScreenReaderInfoForNode ( element , 'ariaLabel' ) ;
327
398
}
328
399
return '' ;
329
400
} ,
0 commit comments