Skip to content

Commit ab4852b

Browse files
committed
work on #195282
1 parent fffda8e commit ab4852b

File tree

4 files changed

+80
-15
lines changed

4 files changed

+80
-15
lines changed

src/vs/platform/accessibility/common/accessibility.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@ export const IAccessibleNotificationService = createDecorator<IAccessibleNotific
5656
export interface IAccessibleNotificationService {
5757
readonly _serviceBrand: undefined;
5858
notify(event: AccessibleNotificationEvent, userGesture?: boolean): void;
59+
notifyLineChanges(event: AccessibleNotificationEvent[]): void;
5960
}
6061

6162
export const enum AccessibleNotificationEvent {
6263
Clear = 'clear',
6364
Save = 'save',
64-
Format = 'format'
65+
Format = 'format',
66+
Breakpoint = 'breakpoint',
67+
Error = 'error',
68+
Warning = 'warning',
69+
Folded = 'folded'
6570
}

src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ export const enum AccessibilityVerbositySettingId {
5656

5757
export const enum AccessibilityAlertSettingId {
5858
Save = 'accessibility.alert.save',
59-
Format = 'accessibility.alert.format'
59+
Format = 'accessibility.alert.format',
60+
Breakpoint = 'accessibility.alert.breakpoint',
61+
Error = 'accessibility.alert.error',
62+
Warning = 'accessibility.alert.warning',
63+
FoldedArea = 'accessibility.alert.foldedArea'
6064
}
6165

6266
export const enum AccessibleViewProviderId {
@@ -154,6 +158,30 @@ const configuration: IConfigurationNode = {
154158
],
155159
tags: ['accessibility']
156160
},
161+
[AccessibilityAlertSettingId.Breakpoint]: {
162+
'markdownDescription': localize('alert.breakpoint', "When in screen reader mode, alerts when the active line has a breakpoint. Also see {0}.", '`#audioCues.breakpoint#`'),
163+
'type': 'boolean',
164+
'default': true,
165+
tags: ['accessibility']
166+
},
167+
[AccessibilityAlertSettingId.Error]: {
168+
'markdownDescription': localize('alert.error', "When in screen reader mode, alerts when the active line has an error. Also see {0}.", '`#audioCues.error#`'),
169+
'type': 'boolean',
170+
'default': true,
171+
tags: ['accessibility']
172+
},
173+
[AccessibilityAlertSettingId.Warning]: {
174+
'markdownDescription': localize('alert.warning', "When in screen reader mode, alerts when the active line has a warning. Also see {0}.", '`#audioCues.warning#`'),
175+
'type': 'boolean',
176+
'default': true,
177+
tags: ['accessibility']
178+
},
179+
[AccessibilityAlertSettingId.FoldedArea]: {
180+
'markdownDescription': localize('alert.foldedArea', "When in screen reader mode, alerts when the active line has a folded area that can be unfolded. Also see {0}.", '`#audioCues.foldedArea#`'),
181+
'type': 'boolean',
182+
'default': true,
183+
tags: ['accessibility']
184+
},
157185
[AccessibilityVoiceSettingId.SpeechTimeout]: {
158186
'markdownDescription': localize('voice.speechTimeout', "The duration in milliseconds that voice speech recognition remains active after you stop speaking. For example in a chat session, the transcribed text is submitted automatically after the timeout is met. Set to `0` to disable this feature."),
159187
'type': 'number',

src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts

+35-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/wo
1515

1616
export class AccessibleNotificationService extends Disposable implements IAccessibleNotificationService {
1717
declare readonly _serviceBrand: undefined;
18-
private _events: Map<AccessibleNotificationEvent, { audioCue: AudioCue; alertMessage: string; alertSetting?: string }> = new Map();
18+
private _events: Map<AccessibleNotificationEvent, {
19+
audioCue: AudioCue;
20+
alertMessage: string;
21+
alertSetting?: string;
22+
}> = new Map();
1923
constructor(
2024
@IAudioCueService private readonly _audioCueService: IAudioCueService,
2125
@IConfigurationService private readonly _configurationService: IConfigurationService,
@@ -26,17 +30,24 @@ export class AccessibleNotificationService extends Disposable implements IAccess
2630
this._events.set(AccessibleNotificationEvent.Clear, { audioCue: AudioCue.clear, alertMessage: localize('cleared', "Cleared") });
2731
this._events.set(AccessibleNotificationEvent.Save, { audioCue: AudioCue.save, alertMessage: localize('saved', "Saved"), alertSetting: AccessibilityAlertSettingId.Save });
2832
this._events.set(AccessibleNotificationEvent.Format, { audioCue: AudioCue.format, alertMessage: localize('formatted', "Formatted"), alertSetting: AccessibilityAlertSettingId.Format });
33+
this._events.set(AccessibleNotificationEvent.Breakpoint, { audioCue: AudioCue.break, alertMessage: localize('breakpoint', "Breakpoint"), alertSetting: AccessibilityAlertSettingId.Breakpoint });
34+
this._events.set(AccessibleNotificationEvent.Error, { audioCue: AudioCue.error, alertMessage: localize('error', "Error"), alertSetting: AccessibilityAlertSettingId.Error });
35+
this._events.set(AccessibleNotificationEvent.Warning, { audioCue: AudioCue.warning, alertMessage: localize('warning', "Warning"), alertSetting: AccessibilityAlertSettingId.Warning });
36+
this._events.set(AccessibleNotificationEvent.Folded, { audioCue: AudioCue.foldedArea, alertMessage: localize('foldedArea', "Folded Area"), alertSetting: AccessibilityAlertSettingId.FoldedArea });
2937

30-
this._register(this._workingCopyService.onDidSave((e) => this._notify(AccessibleNotificationEvent.Save, e.reason === SaveReason.EXPLICIT)));
38+
this._register(this._workingCopyService.onDidSave((e) => this._notifyBasedOnUserGesture(AccessibleNotificationEvent.Save, e.reason === SaveReason.EXPLICIT)));
3139
}
3240

41+
/**
42+
* Notify on clear, save, format. Alerts and audio cues are mutually exclusive.
43+
*/
3344
notify(event: AccessibleNotificationEvent, userGesture?: boolean): void {
3445
if (event === AccessibleNotificationEvent.Format) {
35-
return this._notify(event, userGesture);
46+
return this._notifyBasedOnUserGesture(AccessibleNotificationEvent.Format, userGesture);
3647
}
3748
const { audioCue, alertMessage } = this._events.get(event)!;
38-
const audioCueValue = this._configurationService.getValue(audioCue.settingsKey);
39-
if (audioCueValue === 'on' || audioCueValue === 'auto' && this._accessibilityService.isScreenReaderOptimized()) {
49+
const audioCueSetting = this._configurationService.getValue(audioCue.settingsKey);
50+
if (audioCueSetting === 'on' || audioCueSetting === 'auto' && this._accessibilityService.isScreenReaderOptimized()) {
4051
this._logService.debug('AccessibleNotificationService playing sound: ', audioCue.name);
4152
this._audioCueService.playAudioCue(audioCue);
4253
} else {
@@ -45,7 +56,24 @@ export class AccessibleNotificationService extends Disposable implements IAccess
4556
}
4657
}
4758

48-
private _notify(event: AccessibleNotificationEvent, userGesture?: boolean): void {
59+
/**
60+
* Line feature contributions can use this to notify the user of changes to the line.
61+
*/
62+
notifyLineChanges(events: AccessibleNotificationEvent[]): void {
63+
const audioCues = events.map(e => this._events.get(e)!.audioCue);
64+
if (audioCues.length) {
65+
this._logService.debug('AccessibleNotificationService playing sounds if enabled: ', events.map(e => this._events.get(e)!.audioCue.name).join(', '));
66+
this._audioCueService.playAudioCues(audioCues);
67+
}
68+
69+
const alerts = events.filter(e => this._configurationService.getValue(this._events.get(e)!.alertSetting!) === true).map(e => this._events.get(e)?.alertMessage);
70+
if (alerts.length) {
71+
this._logService.debug('AccessibleNotificationService alerting: ', alerts.join(', '));
72+
this._accessibilityService.alert(alerts.join(', '));
73+
}
74+
}
75+
76+
private _notifyBasedOnUserGesture(event: AccessibleNotificationEvent, userGesture?: boolean): void {
4977
const { audioCue, alertMessage, alertSetting } = this._events.get(event)!;
5078
if (!alertSetting) {
5179
return;
@@ -79,4 +107,5 @@ export class TestAccessibleNotificationService extends Disposable implements IAc
79107
declare readonly _serviceBrand: undefined;
80108

81109
notify(event: AccessibleNotificationEvent, userGesture?: boolean): void { }
110+
notifyLineChanges(event: AccessibleNotificationEvent[]): void { }
82111
}

src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Position } from 'vs/editor/common/core/position';
1212
import { CursorChangeReason } from 'vs/editor/common/cursorEvents';
1313
import { ITextModel } from 'vs/editor/common/model';
1414
import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
15+
import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility';
1516
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
1617
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1718
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -26,8 +27,8 @@ export class AudioCueLineFeatureContribution
2627
private readonly store = this._register(new DisposableStore());
2728

2829
private readonly features: LineFeature[] = [
29-
this.instantiationService.createInstance(MarkerLineFeature, AudioCue.error, MarkerSeverity.Error),
30-
this.instantiationService.createInstance(MarkerLineFeature, AudioCue.warning, MarkerSeverity.Warning),
30+
this.instantiationService.createInstance(MarkerLineFeature, AccessibleNotificationEvent.Error, AudioCue.error, MarkerSeverity.Error),
31+
this.instantiationService.createInstance(MarkerLineFeature, AccessibleNotificationEvent.Warning, AudioCue.warning, MarkerSeverity.Warning),
3132
this.instantiationService.createInstance(FoldedAreaLineFeature),
3233
this.instantiationService.createInstance(BreakpointLineFeature),
3334
];
@@ -41,7 +42,8 @@ export class AudioCueLineFeatureContribution
4142
@IEditorService private readonly editorService: IEditorService,
4243
@IInstantiationService private readonly instantiationService: IInstantiationService,
4344
@IAudioCueService private readonly audioCueService: IAudioCueService,
44-
@IConfigurationService private readonly _configurationService: IConfigurationService
45+
@IConfigurationService private readonly _configurationService: IConfigurationService,
46+
@IAccessibleNotificationService private readonly _accessibleNotificationService: IAccessibleNotificationService
4547
) {
4648
super();
4749

@@ -155,15 +157,15 @@ export class AudioCueLineFeatureContribution
155157
newValue?.featureStates.get(feature) &&
156158
(!lastValue?.featureStates?.get(feature) || newValue.lineNumber !== lastValue.lineNumber)
157159
);
158-
159-
this.audioCueService.playAudioCues(newFeatures.map(f => f.audioCue));
160+
this._accessibleNotificationService.notifyLineChanges(newFeatures.map(feature => feature.event));
160161
})
161162
);
162163
}
163164
}
164165

165166
interface LineFeature {
166167
audioCue: AudioCue;
168+
event: AccessibleNotificationEvent;
167169
debounceWhileTyping?: boolean;
168170
getObservableState(
169171
editor: ICodeEditor,
@@ -179,6 +181,7 @@ class MarkerLineFeature implements LineFeature {
179181
public readonly debounceWhileTyping = true;
180182
private _previousLine: number = 0;
181183
constructor(
184+
public readonly event: AccessibleNotificationEvent,
182185
public readonly audioCue: AudioCue,
183186
private readonly severity: MarkerSeverity,
184187
@IMarkerService private readonly markerService: IMarkerService,
@@ -210,7 +213,7 @@ class MarkerLineFeature implements LineFeature {
210213

211214
class FoldedAreaLineFeature implements LineFeature {
212215
public readonly audioCue = AudioCue.foldedArea;
213-
216+
public readonly event = AccessibleNotificationEvent.Folded;
214217
getObservableState(editor: ICodeEditor, model: ITextModel): IObservable<LineFeatureState> {
215218
const foldingController = FoldingController.get(editor);
216219
if (!foldingController) {
@@ -237,7 +240,7 @@ class FoldedAreaLineFeature implements LineFeature {
237240

238241
class BreakpointLineFeature implements LineFeature {
239242
public readonly audioCue = AudioCue.break;
240-
243+
public readonly event = AccessibleNotificationEvent.Breakpoint;
241244
constructor(@IDebugService private readonly debugService: IDebugService) { }
242245

243246
getObservableState(editor: ICodeEditor, model: ITextModel): IObservable<LineFeatureState> {

0 commit comments

Comments
 (0)