diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index 5c084d90156f8..112c5ff1caf27 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -25,12 +25,8 @@ export namespace AccessibilityHelpNLS { export const tabFocusModeOffMsg = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior {0}."); export const tabFocusModeOffMsgNoKb = nls.localize("tabFocusModeOffMsgNoKb", "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding."); export const showAccessibilityHelpAction = nls.localize("showAccessibilityHelpAction", "Show Accessibility Help"); - export const saveAudioCueDisabled = nls.localize("saveAudioCueDisabled", "`audioCues.save` is disabled, so an alert will occur when a file is saved."); - export const saveAudioCueAlways = nls.localize("saveAudioCueAlways", "`audioCues.save` is enabled, so will play whenever a file is saved."); - export const saveAudioCueUserGesture = nls.localize("saveAudioCueUserGesture", "`audioCues.save` is enabled, so will play when a file is saved via user gesture."); - export const formatAudioCueDisabled = nls.localize("formatAudioCueDisabled", "`audioCues.format` is disabled, so an alert will occur when a file is formatted."); - export const formatAudioCueAlways = nls.localize("formatAudioCueAlways", "`audioCues.format` is enabled, so will play whenever a file is formatted."); - export const formatAudioCueUserGesture = nls.localize("formatAudioCueUserGesture", "`audioCues.format` is enabled, so will play when a file is formatted via user gesture."); + export const listAudioCues = nls.localize("listAudioCuesCommand", "Run the command: List Audio Cues for an overview of all audio cues and their current status."); + export const listAlerts = nls.localize("listAlertsCommand", "Run the command: List Alerts for an overview of alerts and their current status."); } export namespace InspectTokensNLS { diff --git a/src/vs/editor/contrib/format/browser/format.ts b/src/vs/editor/contrib/format/browser/format.ts index 402d15f56430e..742518daae3b7 100644 --- a/src/vs/editor/contrib/format/browser/format.ts +++ b/src/vs/editor/contrib/format/browser/format.ts @@ -30,7 +30,7 @@ import { IProgress } from 'vs/platform/progress/common/progress'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { ILogService } from 'vs/platform/log/common/log'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; export function getRealAndSyntheticDocumentFormattersOrdered( documentFormattingEditProvider: LanguageFeatureRegistry, @@ -135,7 +135,7 @@ export async function formatDocumentRangesWithProvider( ): Promise { const workerService = accessor.get(IEditorWorkerService); const logService = accessor.get(ILogService); - const accessibleNotificationService = accessor.get(IAccessibleNotificationService); + const audioCueService = accessor.get(IAudioCueService); let model: ITextModel; let cts: CancellationTokenSource; @@ -279,7 +279,7 @@ export async function formatDocumentRangesWithProvider( return null; }); } - accessibleNotificationService.notify(AccessibleNotificationEvent.Format, userGesture); + audioCueService.playAudioCue(AudioCue.format, { userGesture }); return true; } @@ -312,7 +312,7 @@ export async function formatDocumentWithProvider( userGesture?: boolean ): Promise { const workerService = accessor.get(IEditorWorkerService); - const accessibleNotificationService = accessor.get(IAccessibleNotificationService); + const audioCueService = accessor.get(IAudioCueService); let model: ITextModel; let cts: CancellationTokenSource; @@ -373,7 +373,7 @@ export async function formatDocumentWithProvider( return null; }); } - accessibleNotificationService.notify(AccessibleNotificationEvent.Format, userGesture); + audioCueService.playAudioCue(AudioCue.format, { userGesture }); return true; } diff --git a/src/vs/editor/contrib/format/browser/formatActions.ts b/src/vs/editor/contrib/format/browser/formatActions.ts index 1282572b36161..a9c4984e4c1d5 100644 --- a/src/vs/editor/contrib/format/browser/formatActions.ts +++ b/src/vs/editor/contrib/format/browser/formatActions.ts @@ -21,7 +21,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode, getOnTypeFormattingEdits } from 'vs/editor/contrib/format/browser/format'; import { FormattingEdit } from 'vs/editor/contrib/format/browser/formattingEdit'; import * as nls from 'vs/nls'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -40,7 +40,7 @@ export class FormatOnType implements IEditorContribution { private readonly _editor: ICodeEditor, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @IEditorWorkerService private readonly _workerService: IEditorWorkerService, - @IAccessibleNotificationService private readonly _accessibleNotificationService: IAccessibleNotificationService + @IAudioCueService private readonly _audioCueService: IAudioCueService ) { this._disposables.add(_languageFeaturesService.onTypeFormattingEditProvider.onDidChange(this._update, this)); this._disposables.add(_editor.onDidChangeModel(() => this._update())); @@ -143,7 +143,7 @@ export class FormatOnType implements IEditorContribution { return; } if (isNonEmptyArray(edits)) { - this._accessibleNotificationService.notify(AccessibleNotificationEvent.Format, false); + this._audioCueService.playAudioCue(AudioCue.format, { userGesture: false }); FormattingEdit.execute(this._editor, edits, true); } }).finally(() => { diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 61e84831376a1..1e915d95a2585 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -71,7 +71,7 @@ import { StandaloneQuickInputService } from 'vs/editor/standalone/browser/quickI import { StandaloneThemeService } from 'vs/editor/standalone/browser/standaloneThemeService'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; import { AccessibilityService } from 'vs/platform/accessibility/browser/accessibilityService'; -import { AccessibleNotificationEvent, IAccessibilityService, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IMenuService } from 'vs/platform/actions/common/actions'; import { MenuService } from 'vs/platform/actions/common/menuService'; import { BrowserClipboardService } from 'vs/platform/clipboard/browser/clipboardService'; @@ -1059,7 +1059,11 @@ class StandaloneAudioService implements IAudioCueService { async playAudioCues(cues: AudioCue[]): Promise { } - isEnabled(cue: AudioCue): boolean { + isCueEnabled(cue: AudioCue): boolean { + return false; + } + + isAlertEnabled(cue: AudioCue): boolean { return false; } @@ -1074,14 +1078,6 @@ class StandaloneAudioService implements IAudioCueService { } } -class StandaloneAccessibleNotificationService implements IAccessibleNotificationService { - _serviceBrand: undefined; - - notify(event: AccessibleNotificationEvent, userGesture?: boolean | undefined): void { - // NOOP - } -} - export interface IEditorOverrideServices { [index: string]: any; } @@ -1120,7 +1116,6 @@ registerSingleton(IClipboardService, BrowserClipboardService, InstantiationType. registerSingleton(IContextMenuService, StandaloneContextMenuService, InstantiationType.Eager); registerSingleton(IMenuService, MenuService, InstantiationType.Eager); registerSingleton(IAudioCueService, StandaloneAudioService, InstantiationType.Eager); -registerSingleton(IAccessibleNotificationService, StandaloneAccessibleNotificationService, InstantiationType.Eager); /** * We don't want to eagerly instantiate services because embedders get a one time chance diff --git a/src/vs/platform/accessibility/common/accessibility.ts b/src/vs/platform/accessibility/common/accessibility.ts index 55b6f1ba0c1dc..b370b1e0267ed 100644 --- a/src/vs/platform/accessibility/common/accessibility.ts +++ b/src/vs/platform/accessibility/common/accessibility.ts @@ -47,19 +47,3 @@ export function isAccessibilityInformation(obj: any): obj is IAccessibilityInfor && (typeof obj.role === 'undefined' || typeof obj.role === 'string'); } -export const IAccessibleNotificationService = createDecorator('accessibleNotificationService'); -/** - * Manages whether an audio cue or an aria alert will be used - * in response to actions taken around the workbench. - * Targets screen reader and braille users. - */ -export interface IAccessibleNotificationService { - readonly _serviceBrand: undefined; - notify(event: AccessibleNotificationEvent, userGesture?: boolean): void; -} - -export const enum AccessibleNotificationEvent { - Clear = 'clear', - Save = 'save', - Format = 'format' -} diff --git a/src/vs/platform/audioCues/browser/audioCueService.ts b/src/vs/platform/audioCues/browser/audioCueService.ts index a97297094c116..62fd6b5042def 100644 --- a/src/vs/platform/audioCues/browser/audioCueService.ts +++ b/src/vs/platform/audioCues/browser/audioCueService.ts @@ -19,7 +19,8 @@ export interface IAudioCueService { readonly _serviceBrand: undefined; playAudioCue(cue: AudioCue, options?: IAudioCueOptions): Promise; playAudioCues(cues: (AudioCue | { cue: AudioCue; source: string })[]): Promise; - isEnabled(cue: AudioCue): boolean; + isCueEnabled(cue: AudioCue): boolean; + isAlertEnabled(cue: AudioCue): boolean; onEnabledChanged(cue: AudioCue): Event; playSound(cue: Sound, allowManyInParallel?: boolean): Promise; @@ -29,6 +30,12 @@ export interface IAudioCueService { export interface IAudioCueOptions { allowManyInParallel?: boolean; source?: string; + /** + * For actions like save or format, depending on the + * configured value, we will only + * play the sound if the user triggered the action. + */ + userGesture?: boolean; } export class AudioCueService extends Disposable implements IAudioCueService { @@ -49,7 +56,12 @@ export class AudioCueService extends Disposable implements IAudioCueService { } public async playAudioCue(cue: AudioCue, options: IAudioCueOptions = {}): Promise { - if (this.isEnabled(cue)) { + const alertMessage = cue.alertMessage; + if (this.isAlertEnabled(cue, options.userGesture) && alertMessage) { + this.accessibilityService.alert(alertMessage); + } + + if (this.isCueEnabled(cue, options.userGesture)) { this.sendAudioCueTelemetry(cue, options.source); await this.playSound(cue.sound.getSound(), options.allowManyInParallel); } @@ -59,12 +71,19 @@ export class AudioCueService extends Disposable implements IAudioCueService { for (const cue of cues) { this.sendAudioCueTelemetry('cue' in cue ? cue.cue : cue, 'source' in cue ? cue.source : undefined); } + const cueArray = cues.map(c => 'cue' in c ? c.cue : c); + const alerts = cueArray.filter(cue => this.isAlertEnabled(cue)).map(c => c.alertMessage); + if (alerts.length) { + this.accessibilityService.alert(alerts.join(', ')); + } // Some audio cues might reuse sounds. Don't play the same sound twice. - const sounds = new Set(cues.map(c => 'cue' in c ? c.cue : c).filter(cue => this.isEnabled(cue)).map(cue => cue.sound.getSound())); + const sounds = new Set(cueArray.filter(cue => this.isCueEnabled(cue)).map(cue => cue.sound.getSound())); await Promise.all(Array.from(sounds).map(sound => this.playSound(sound, true))); + } + private sendAudioCueTelemetry(cue: AudioCue, source: string | undefined): void { const isScreenReaderOptimized = this.accessibilityService.isScreenReaderOptimized(); const key = cue.name + (source ? `::${source}` : '') + (isScreenReaderOptimized ? '{screenReaderOptimized}' : ''); @@ -152,15 +171,15 @@ export class AudioCueService extends Disposable implements IAudioCueService { Event.filter(this.configurationService.onDidChangeConfiguration, (e) => e.affectsConfiguration('audioCues.enabled') ), - () => /** @description config: audioCues.enabled */ this.configurationService.getValue<'on' | 'off' | 'auto'>('audioCues.enabled') + () => /** @description config: audioCues.enabled */ this.configurationService.getValue<'on' | 'off' | 'auto' | 'userGesture' | 'always' | 'never'>('audioCues.enabled') ); - private readonly isEnabledCache = new Cache((cue: AudioCue) => { + private readonly isCueEnabledCache = new Cache((event: { readonly cue: AudioCue; readonly userGesture?: boolean }) => { const settingObservable = observableFromEvent( Event.filter(this.configurationService.onDidChangeConfiguration, (e) => - e.affectsConfiguration(cue.settingsKey) + e.affectsConfiguration(event.cue.settingsKey) ), - () => this.configurationService.getValue<'on' | 'off' | 'auto'>(cue.settingsKey) + () => this.configurationService.getValue<'on' | 'off' | 'auto' | 'userGesture' | 'always' | 'never'>(event.cue.settingsKey) ); return derived(reader => { /** @description audio cue enabled */ @@ -170,6 +189,8 @@ export class AudioCueService extends Disposable implements IAudioCueService { (setting === 'auto' && this.screenReaderAttached.read(reader)) ) { return true; + } else if (setting === 'always' || setting === 'userGesture' && event.userGesture) { + return true; } const obsoleteSetting = this.obsoleteAudioCuesEnabled.read(reader); @@ -182,17 +203,41 @@ export class AudioCueService extends Disposable implements IAudioCueService { return false; }); - }); + }, JSON.stringify); + + private readonly isAlertEnabledCache = new Cache((event: { readonly cue: AudioCue; readonly userGesture?: boolean }) => { + const settingObservable = observableFromEvent( + Event.filter(this.configurationService.onDidChangeConfiguration, (e) => + e.affectsConfiguration(event.cue.settingsKey) + ), + () => event.cue.alertSettingsKey ? this.configurationService.getValue(event.cue.alertSettingsKey) : false + ); + return derived(reader => { + /** @description audio cue enabled */ + const setting = settingObservable.read(reader); + if ( + !this.screenReaderAttached.read(reader) + ) { + return false; + } + return setting === true || setting === 'always' || setting === 'userGesture' && event.userGesture; + }); + }, JSON.stringify); - public isEnabled(cue: AudioCue): boolean { - return this.isEnabledCache.get(cue).get(); + public isAlertEnabled(cue: AudioCue, userGesture?: boolean): boolean { + return this.isAlertEnabledCache.get({ cue, userGesture }).get() ?? false; + } + + public isCueEnabled(cue: AudioCue, userGesture?: boolean): boolean { + return this.isCueEnabledCache.get({ cue, userGesture }).get() ?? false; } public onEnabledChanged(cue: AudioCue): Event { - return Event.fromObservableLight(this.isEnabledCache.get(cue)); + return Event.fromObservableLight(this.isCueEnabledCache.get({ cue })); } } + /** * Play the given audio url. * @volume value between 0 and 1 @@ -216,8 +261,8 @@ function playAudio(url: string, volume: number): Promise { } class Cache { - private readonly map = new Map(); - constructor(private readonly getValue: (value: TArg) => TValue) { + private readonly map = new Map(); + constructor(private readonly getValue: (value: TArg) => TValue, private readonly getKey: (value: TArg) => unknown) { } public get(arg: TArg): TValue { @@ -226,7 +271,8 @@ class Cache { } const value = this.getValue(arg); - this.map.set(arg, value); + const key = this.getKey(arg); + this.map.set(key, value); return value; } } @@ -279,6 +325,29 @@ export class SoundSource { } } +export const enum AccessibilityAlertSettingId { + Save = 'accessibility.alert.save', + Format = 'accessibility.alert.format', + Clear = 'accessibility.alert.clear', + Breakpoint = 'accessibility.alert.breakpoint', + Error = 'accessibility.alert.error', + Warning = 'accessibility.alert.warning', + FoldedArea = 'accessibility.alert.foldedArea', + TerminalQuickFix = 'accessibility.alert.terminalQuickFix', + TerminalBell = 'accessibility.alert.terminalBell', + TerminalCommandFailed = 'accessibility.alert.terminalCommandFailed', + TaskCompleted = 'accessibility.alert.taskCompleted', + TaskFailed = 'accessibility.alert.taskFailed', + ChatRequestSent = 'accessibility.alert.chatRequestSent', + NotebookCellCompleted = 'accessibility.alert.notebookCellCompleted', + NotebookCellFailed = 'accessibility.alert.notebookCellFailed', + OnDebugBreak = 'accessibility.alert.onDebugBreak', + NoInlayHints = 'accessibility.alert.noInlayHints', + LineHasBreakpoint = 'accessibility.alert.lineHasBreakpoint', + ChatResponsePending = 'accessibility.alert.chatResponsePending' +} + + export class AudioCue { private static _audioCues = new Set(); private static register(options: { @@ -291,9 +360,11 @@ export class AudioCue { randomOneOf: Sound[]; }; settingsKey: string; + alertSettingsKey?: AccessibilityAlertSettingId; + alertMessage?: string; }): AudioCue { const soundSource = new SoundSource('randomOneOf' in options.sound ? options.sound.randomOneOf : [options.sound]); - const audioCue = new AudioCue(soundSource, options.name, options.settingsKey); + const audioCue = new AudioCue(soundSource, options.name, options.settingsKey, options.alertSettingsKey, options.alertMessage); AudioCue._audioCues.add(audioCue); return audioCue; } @@ -306,21 +377,29 @@ export class AudioCue { name: localize('audioCues.lineHasError.name', 'Error on Line'), sound: Sound.error, settingsKey: 'audioCues.lineHasError', + alertSettingsKey: AccessibilityAlertSettingId.Error, + alertMessage: localize('audioCues.lineHasError.alertMessage', 'Error') }); public static readonly warning = AudioCue.register({ name: localize('audioCues.lineHasWarning.name', 'Warning on Line'), sound: Sound.warning, settingsKey: 'audioCues.lineHasWarning', + alertSettingsKey: AccessibilityAlertSettingId.Warning, + alertMessage: localize('audioCues.lineHasWarning.alertMessage', 'Warning') }); public static readonly foldedArea = AudioCue.register({ name: localize('audioCues.lineHasFoldedArea.name', 'Folded Area on Line'), sound: Sound.foldedArea, settingsKey: 'audioCues.lineHasFoldedArea', + alertSettingsKey: AccessibilityAlertSettingId.FoldedArea, + alertMessage: localize('audioCues.lineHasFoldedArea.alertMessage', 'Folded') }); public static readonly break = AudioCue.register({ name: localize('audioCues.lineHasBreakpoint.name', 'Breakpoint on Line'), sound: Sound.break, settingsKey: 'audioCues.lineHasBreakpoint', + alertSettingsKey: AccessibilityAlertSettingId.Breakpoint, + alertMessage: localize('audioCues.lineHasBreakpoint.alertMessage', 'Breakpoint') }); public static readonly inlineSuggestion = AudioCue.register({ name: localize('audioCues.lineHasInlineSuggestion.name', 'Inline Suggestion on Line'), @@ -332,78 +411,98 @@ export class AudioCue { name: localize('audioCues.terminalQuickFix.name', 'Terminal Quick Fix'), sound: Sound.quickFixes, settingsKey: 'audioCues.terminalQuickFix', + alertSettingsKey: AccessibilityAlertSettingId.TerminalQuickFix, + alertMessage: localize('audioCues.terminalQuickFix.alertMessage', 'Quick Fix') }); public static readonly onDebugBreak = AudioCue.register({ name: localize('audioCues.onDebugBreak.name', 'Debugger Stopped on Breakpoint'), sound: Sound.break, settingsKey: 'audioCues.onDebugBreak', + alertSettingsKey: AccessibilityAlertSettingId.OnDebugBreak, + alertMessage: localize('audioCues.onDebugBreak.alertMessage', 'Breakpoint') }); public static readonly noInlayHints = AudioCue.register({ name: localize('audioCues.noInlayHints', 'No Inlay Hints on Line'), sound: Sound.error, - settingsKey: 'audioCues.noInlayHints' + settingsKey: 'audioCues.noInlayHints', + alertSettingsKey: AccessibilityAlertSettingId.NoInlayHints, + alertMessage: localize('audioCues.noInlayHints.alertMessage', 'No Inlay Hints') }); public static readonly taskCompleted = AudioCue.register({ name: localize('audioCues.taskCompleted', 'Task Completed'), sound: Sound.taskCompleted, - settingsKey: 'audioCues.taskCompleted' + settingsKey: 'audioCues.taskCompleted', + alertSettingsKey: AccessibilityAlertSettingId.TaskCompleted, + alertMessage: localize('audioCues.taskCompleted.alertMessage', 'Task Completed') }); public static readonly taskFailed = AudioCue.register({ name: localize('audioCues.taskFailed', 'Task Failed'), sound: Sound.taskFailed, - settingsKey: 'audioCues.taskFailed' + settingsKey: 'audioCues.taskFailed', + alertSettingsKey: AccessibilityAlertSettingId.TaskFailed, + alertMessage: localize('audioCues.taskFailed.alertMessage', 'Task Failed') }); public static readonly terminalCommandFailed = AudioCue.register({ name: localize('audioCues.terminalCommandFailed', 'Terminal Command Failed'), sound: Sound.error, - settingsKey: 'audioCues.terminalCommandFailed' + settingsKey: 'audioCues.terminalCommandFailed', + alertSettingsKey: AccessibilityAlertSettingId.TerminalCommandFailed, + alertMessage: localize('audioCues.terminalCommandFailed.alertMessage', 'Command Failed') }); public static readonly terminalBell = AudioCue.register({ name: localize('audioCues.terminalBell', 'Terminal Bell'), sound: Sound.terminalBell, - settingsKey: 'audioCues.terminalBell' + settingsKey: 'audioCues.terminalBell', + alertSettingsKey: AccessibilityAlertSettingId.TerminalBell, + alertMessage: localize('audioCues.terminalBell.alertMessage', 'Terminal Bell') }); public static readonly notebookCellCompleted = AudioCue.register({ name: localize('audioCues.notebookCellCompleted', 'Notebook Cell Completed'), sound: Sound.taskCompleted, - settingsKey: 'audioCues.notebookCellCompleted' + settingsKey: 'audioCues.notebookCellCompleted', + alertSettingsKey: AccessibilityAlertSettingId.NotebookCellCompleted, + alertMessage: localize('audioCues.notebookCellCompleted.alertMessage', 'Notebook Cell Completed') }); public static readonly notebookCellFailed = AudioCue.register({ name: localize('audioCues.notebookCellFailed', 'Notebook Cell Failed'), sound: Sound.taskFailed, - settingsKey: 'audioCues.notebookCellFailed' + settingsKey: 'audioCues.notebookCellFailed', + alertSettingsKey: AccessibilityAlertSettingId.NotebookCellFailed, + alertMessage: localize('audioCues.notebookCellFailed.alertMessage', 'Notebook Cell Failed') }); public static readonly diffLineInserted = AudioCue.register({ name: localize('audioCues.diffLineInserted', 'Diff Line Inserted'), sound: Sound.diffLineInserted, - settingsKey: 'audioCues.diffLineInserted' + settingsKey: 'audioCues.diffLineInserted', }); public static readonly diffLineDeleted = AudioCue.register({ name: localize('audioCues.diffLineDeleted', 'Diff Line Deleted'), sound: Sound.diffLineDeleted, - settingsKey: 'audioCues.diffLineDeleted' + settingsKey: 'audioCues.diffLineDeleted', }); public static readonly diffLineModified = AudioCue.register({ name: localize('audioCues.diffLineModified', 'Diff Line Modified'), sound: Sound.diffLineModified, - settingsKey: 'audioCues.diffLineModified' + settingsKey: 'audioCues.diffLineModified', }); public static readonly chatRequestSent = AudioCue.register({ name: localize('audioCues.chatRequestSent', 'Chat Request Sent'), sound: Sound.chatRequestSent, - settingsKey: 'audioCues.chatRequestSent' + settingsKey: 'audioCues.chatRequestSent', + alertSettingsKey: AccessibilityAlertSettingId.ChatRequestSent, + alertMessage: localize('audioCues.chatRequestSent.alertMessage', 'Chat Request Sent') }); public static readonly chatResponseReceived = AudioCue.register({ @@ -416,36 +515,46 @@ export class AudioCue { Sound.chatResponseReceived3, Sound.chatResponseReceived4 ] - } + }, }); public static readonly chatResponsePending = AudioCue.register({ name: localize('audioCues.chatResponsePending', 'Chat Response Pending'), sound: Sound.chatResponsePending, - settingsKey: 'audioCues.chatResponsePending' + settingsKey: 'audioCues.chatResponsePending', + alertSettingsKey: AccessibilityAlertSettingId.ChatResponsePending, + alertMessage: localize('audioCues.chatResponsePending.alertMessage', 'Chat Response Pending') }); public static readonly clear = AudioCue.register({ name: localize('audioCues.clear', 'Clear'), sound: Sound.clear, - settingsKey: 'audioCues.clear' + settingsKey: 'audioCues.clear', + alertSettingsKey: AccessibilityAlertSettingId.Clear, + alertMessage: localize('audioCues.clear.alertMessage', 'Clear') }); public static readonly save = AudioCue.register({ name: localize('audioCues.save', 'Save'), sound: Sound.save, - settingsKey: 'audioCues.save' + settingsKey: 'audioCues.save', + alertSettingsKey: AccessibilityAlertSettingId.Save, + alertMessage: localize('audioCues.save.alertMessage', 'Save') }); public static readonly format = AudioCue.register({ name: localize('audioCues.format', 'Format'), sound: Sound.format, - settingsKey: 'audioCues.format' + settingsKey: 'audioCues.format', + alertSettingsKey: AccessibilityAlertSettingId.Format, + alertMessage: localize('audioCues.format.alertMessage', 'Format') }); private constructor( public readonly sound: SoundSource, public readonly name: string, public readonly settingsKey: string, + public readonly alertSettingsKey?: string, + public readonly alertMessage?: string ) { } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 568e4fb5ac334..8a88a9e5f4a81 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -25,10 +25,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { assertAllDefined, assertIsDefined } from 'vs/base/common/types'; import { NotificationsCenterVisibleContext } from 'vs/workbench/common/contextkeys'; import { INotificationService, NotificationsFilter } from 'vs/platform/notification/common/notification'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; import { mainWindow } from 'vs/base/browser/window'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; export class NotificationsCenter extends Themable implements INotificationsCenterController { @@ -59,7 +59,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IKeybindingService private readonly keybindingService: IKeybindingService, @INotificationService private readonly notificationService: INotificationService, - @IAccessibleNotificationService private readonly accessibleNotificationService: IAccessibleNotificationService, + @IAudioCueService private readonly audioCueService: IAudioCueService, @IContextMenuService private readonly contextMenuService: IContextMenuService ) { super(themeService); @@ -383,7 +383,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente if (!notification.hasProgress) { notification.close(); } - this.accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); + this.audioCueService.playAudioCue(AudioCue.clear); } } } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 0816033b762fb..5e8135b4fcce5 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -19,9 +19,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ActionRunner, IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { hash } from 'vs/base/common/hash'; import { firstOrDefault } from 'vs/base/common/arrays'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; // Center export const SHOW_NOTIFICATIONS_CENTER = 'notifications.showList'; @@ -142,11 +142,11 @@ export function registerNotificationCommands(center: INotificationsCenterControl primary: KeyMod.CtrlCmd | KeyCode.Backspace }, handler: (accessor, args?) => { - const accessibleNotificationService = accessor.get(IAccessibleNotificationService); + const audioCueService = accessor.get(IAudioCueService); const notification = getNotificationFromContext(accessor.get(IListService), args); if (notification && !notification.hasProgress) { notification.close(); - accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); + audioCueService.playAudioCue(AudioCue.clear); } } }); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index c08a26dccc9cf..5df70bb0761a7 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -13,13 +13,11 @@ import { UnfocusedViewDimmingContribution } from 'vs/workbench/contrib/accessibi import { 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'; -import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; -import { AccessibleNotificationService } from 'vs/workbench/contrib/accessibility/browser/accessibleNotificationService'; import { EditorAccessibilityHelpContribution } from 'vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp'; +import { SaveAudioCueContribution } from 'vs/workbench/contrib/accessibility/browser/saveAudioCue'; registerAccessibilityConfiguration(); registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed); -registerSingleton(IAccessibleNotificationService, AccessibleNotificationService, InstantiationType.Delayed); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(EditorAccessibilityHelpContribution, LifecyclePhase.Eventually); @@ -31,3 +29,4 @@ workbenchContributionsRegistry.registerWorkbenchContribution(HoverAccessibleView workbenchContributionsRegistry.registerWorkbenchContribution(NotificationAccessibleViewContribution, LifecyclePhase.Eventually); workbenchContributionsRegistry.registerWorkbenchContribution(InlineCompletionsAccessibleViewContribution, LifecyclePhase.Eventually); workbenchContributionsRegistry.registerWorkbenchContribution(AccessibilityStatus, LifecyclePhase.Ready); +workbenchContributionsRegistry.registerWorkbenchContribution(SaveAudioCueContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index e75de941aa5ea..16b03b216cd7d 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -8,6 +8,7 @@ import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegis import { Registry } from 'vs/platform/registry/common/platform'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { AccessibilityAlertSettingId } from 'vs/platform/audioCues/browser/audioCueService'; export const accessibilityHelpIsShown = new RawContextKey('accessibilityHelpIsShown', false, true); export const accessibleViewIsShown = new RawContextKey('accessibleViewIsShown', false, true); @@ -54,11 +55,6 @@ export const enum AccessibilityVerbositySettingId { Comments = 'accessibility.verbosity.comments' } -export const enum AccessibilityAlertSettingId { - Save = 'accessibility.alert.save', - Format = 'accessibility.alert.format' -} - export const enum AccessibleViewProviderId { Terminal = 'terminal', TerminalHelp = 'terminal-help', @@ -131,8 +127,7 @@ const configuration: IConfigurationNode = { ...baseProperty }, [AccessibilityAlertSettingId.Save]: { - 'markdownDescription': localize('alert.save', "When in screen reader mode, alerts when a file is saved. Note that this will be ignored when {0} is enabled.", '`#audioCues.save#`'), - 'type': 'string', + 'markdownDescription': localize('alert.save', "Alerts when a file is saved. Also see {0}.", '`#audioCues.save#`'), 'enum': ['userGesture', 'always', 'never'], 'default': 'always', 'enumDescriptions': [ @@ -142,8 +137,14 @@ const configuration: IConfigurationNode = { ], tags: ['accessibility'] }, + [AccessibilityAlertSettingId.Clear]: { + 'markdownDescription': localize('alert.clear', "Alerts when a feature is cleared (for example, the terminal, Debug Console, or Output channel). Also see {0}.", '`#audioCues.clear#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, [AccessibilityAlertSettingId.Format]: { - 'markdownDescription': localize('alert.format', "When in screen reader mode, alerts when a file or notebook cell is formatted. Note that this will be ignored when {0} is enabled.", '`#audioCues.format#`'), + 'markdownDescription': localize('alert.format', "Alerts when a file or notebook cell is formatted. Also see {0}.", '`#audioCues.format#`'), 'type': 'string', 'enum': ['userGesture', 'always', 'never'], 'default': 'always', @@ -154,6 +155,102 @@ const configuration: IConfigurationNode = { ], tags: ['accessibility'] }, + [AccessibilityAlertSettingId.Breakpoint]: { + 'markdownDescription': localize('alert.breakpoint', "Alerts when the active line has a breakpoint. Also see {0}.", '`#audioCues.breakpoint#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.Error]: { + 'markdownDescription': localize('alert.error', "Alerts when the active line has an error. Also see {0}.", '`#audioCues.error#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.Warning]: { + 'markdownDescription': localize('alert.warning', "Alerts when the active line has a warning. Also see {0}.", '`#audioCues.warning#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.FoldedArea]: { + 'markdownDescription': localize('alert.foldedArea', "Alerts when the active line has a folded area that can be unfolded. Also see {0}.", '`#audioCues.foldedArea#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.TerminalQuickFix]: { + 'markdownDescription': localize('alert.terminalQuickFix', "Alerts when there is an available terminal quick fix. Also see {0}.", '`#audioCues.terminalQuickFix#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.TerminalBell]: { + 'markdownDescription': localize('alert.terminalBell', "Alerts when the terminal bell is activated. Also see {0}.", '`#audioCues.terminalBell#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.TerminalCommandFailed]: { + 'markdownDescription': localize('alert.terminalCommandFailed', "Alerts when a terminal command fails (non-zero exit code). Also see {0}.", '`#audioCues.terminalCommandFailed#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.TaskFailed]: { + 'markdownDescription': localize('alert.taskFailed', "Alerts when a task fails (non-zero exit code). Also see {0}.", '`#audioCues.taskFailed#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.TaskCompleted]: { + 'markdownDescription': localize('alert.taskCompleted', "Alerts when a task completes successfully (zero exit code). Also see {0}.", '`#audioCues.taskCompleted#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.ChatRequestSent]: { + 'markdownDescription': localize('alert.chatRequestSent', "Alerts when a chat request is sent. Also see {0}.", '`#audioCues.chatRequestSent#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.ChatResponsePending]: { + 'markdownDescription': localize('alert.chatResponsePending', "Alerts when a chat response is pending. Also see {0}.", '`#audioCues.chatResponsePending#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.NoInlayHints]: { + 'markdownDescription': localize('alert.noInlayHints', "Alerts when there are no inlay hints. Also see {0}.", '`#audioCues.noInlayHints#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.LineHasBreakpoint]: { + 'markdownDescription': localize('alert.lineHasBreakpoint', "Alerts when on a line with a breakpoint. Also see {0}.", '`#audioCues.lineHasBreakpoint#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.NotebookCellCompleted]: { + 'markdownDescription': localize('alert.notebookCellCompleted', "Alerts when a notebook cell completes successfully. Also see {0}.", '`#audioCues.notebookCellCompleted#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.NotebookCellFailed]: { + 'markdownDescription': localize('alert.notebookCellFailed', "Alerts when a notebook cell fails. Also see {0}.", '`#audioCues.notebookCellFailed#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, + [AccessibilityAlertSettingId.OnDebugBreak]: { + 'markdownDescription': localize('alert.onDebugBreak', "Alerts when the debugger breaks. Also see {0}.", '`#audioCues.onDebugBreak#`'), + 'type': 'boolean', + 'default': true, + tags: ['accessibility'] + }, [AccessibilityVoiceSettingId.SpeechTimeout]: { '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."), 'type': 'number', diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 73833a9fad3e3..815ac45fe1d0a 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -32,7 +32,7 @@ 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 { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; export function descriptionForCommand(commandId: string, msg: string, noKbMsg: string, keybindingService: IKeybindingService): string { const kb = keybindingService.lookupKeybinding(commandId); @@ -104,7 +104,7 @@ export class NotificationAccessibleViewContribution extends Disposable { const accessibleViewService = accessor.get(IAccessibleViewService); const listService = accessor.get(IListService); const commandService = accessor.get(ICommandService); - const accessibleNotificationService = accessor.get(IAccessibleNotificationService); + const audioCueService = accessor.get(IAudioCueService); function renderAccessibleView(): boolean { const notification = getNotificationFromContext(listService); @@ -165,7 +165,7 @@ export class NotificationAccessibleViewContribution extends Disposable { }, verbositySettingKey: AccessibilityVerbositySettingId.Notification, options: { type: AccessibleViewType.View }, - actions: getActionsFromNotification(notification, accessibleNotificationService) + actions: getActionsFromNotification(notification, audioCueService) }); return true; } @@ -174,7 +174,7 @@ export class NotificationAccessibleViewContribution extends Disposable { } } -function getActionsFromNotification(notification: INotificationViewItem, accessibleNotificationService: IAccessibleNotificationService): IAction[] | undefined { +function getActionsFromNotification(notification: INotificationViewItem, audioCueService: IAudioCueService): IAction[] | undefined { let actions = undefined; if (notification.actions) { actions = []; @@ -203,7 +203,7 @@ function getActionsFromNotification(notification: INotificationViewItem, accessi actions.push({ id: 'clearNotification', label: localize('clearNotification', "Clear Notification"), tooltip: localize('clearNotification', "Clear Notification"), run: () => { notification.close(); - accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); + audioCueService.playAudioCue(AudioCue.clear); }, enabled: true, class: ThemeIcon.asClassName(Codicon.clearAll) }); } @@ -272,3 +272,4 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { })); } } + diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts deleted file mode 100644 index 5afd59cd2e330..0000000000000 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleNotificationService.ts +++ /dev/null @@ -1,82 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Disposable } from 'vs/base/common/lifecycle'; -import { localize } from 'vs/nls'; -import { AccessibleNotificationEvent, IAccessibilityService, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; -import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILogService } from 'vs/platform/log/common/log'; -import { SaveReason } from 'vs/workbench/common/editor'; -import { AccessibilityAlertSettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; - -export class AccessibleNotificationService extends Disposable implements IAccessibleNotificationService { - declare readonly _serviceBrand: undefined; - private _events: Map = new Map(); - constructor( - @IAudioCueService private readonly _audioCueService: IAudioCueService, - @IConfigurationService private readonly _configurationService: IConfigurationService, - @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, - @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, - @ILogService private readonly _logService: ILogService) { - super(); - this._events.set(AccessibleNotificationEvent.Clear, { audioCue: AudioCue.clear, alertMessage: localize('cleared', "Cleared") }); - this._events.set(AccessibleNotificationEvent.Save, { audioCue: AudioCue.save, alertMessage: localize('saved', "Saved"), alertSetting: AccessibilityAlertSettingId.Save }); - this._events.set(AccessibleNotificationEvent.Format, { audioCue: AudioCue.format, alertMessage: localize('formatted', "Formatted"), alertSetting: AccessibilityAlertSettingId.Format }); - - this._register(this._workingCopyService.onDidSave((e) => this._notify(AccessibleNotificationEvent.Save, e.reason === SaveReason.EXPLICIT))); - } - - notify(event: AccessibleNotificationEvent, userGesture?: boolean): void { - if (event === AccessibleNotificationEvent.Format) { - return this._notify(event, userGesture); - } - const { audioCue, alertMessage } = this._events.get(event)!; - const audioCueValue = this._configurationService.getValue(audioCue.settingsKey); - if (audioCueValue === 'on' || audioCueValue === 'auto' && this._accessibilityService.isScreenReaderOptimized()) { - this._logService.debug('AccessibleNotificationService playing sound: ', audioCue.name); - this._audioCueService.playAudioCue(audioCue); - } else { - this._logService.debug('AccessibleNotificationService alerting: ', alertMessage); - this._accessibilityService.alert(alertMessage); - } - } - - private _notify(event: AccessibleNotificationEvent, userGesture?: boolean): void { - const { audioCue, alertMessage, alertSetting } = this._events.get(event)!; - if (!alertSetting) { - return; - } - const audioCueSetting: NotificationSetting = this._configurationService.getValue(audioCue.settingsKey); - if (this._shouldNotify(audioCueSetting, userGesture)) { - this._logService.debug('AccessibleNotificationService playing sound: ', audioCue.name); - // Play sound bypasses the usual audio cue checks IE screen reader optimized, auto, etc. - this._audioCueService.playSound(audioCue.sound.getSound(), true); - return; - } - if (audioCueSetting !== 'never') { - // Never do both sound and alert - return; - } - const alertSettingValue: NotificationSetting = this._configurationService.getValue(alertSetting); - if (this._shouldNotify(alertSettingValue, userGesture)) { - this._logService.debug('AccessibleNotificationService alerting: ', alertMessage); - this._accessibilityService.alert(alertMessage); - } - } - - private _shouldNotify(settingValue: NotificationSetting, userGesture?: boolean): boolean { - return settingValue === 'always' || settingValue === 'userGesture' && userGesture === true; - } -} -type NotificationSetting = 'never' | 'always' | 'userGesture'; - -export class TestAccessibleNotificationService extends Disposable implements IAccessibleNotificationService { - - declare readonly _serviceBrand: undefined; - - notify(event: AccessibleNotificationEvent, userGesture?: boolean): void { } -} diff --git a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts index 1bd79ceb9a39a..d4bde61062fca 100644 --- a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts @@ -10,9 +10,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode'; -import { AudioCue } from 'vs/platform/audioCues/browser/audioCueService'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -54,8 +52,7 @@ class EditorAccessibilityHelpProvider implements IAccessibleContentProvider { constructor( private readonly _editor: ICodeEditor, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { } @@ -76,30 +73,9 @@ class EditorAccessibilityHelpProvider implements IAccessibleContentProvider { content.push(AccessibilityHelpNLS.editableEditor); } } - const saveAudioCue = this._configurationService.getValue(AudioCue.save.settingsKey); - switch (saveAudioCue) { - case 'never': - content.push(AccessibilityHelpNLS.saveAudioCueDisabled); - break; - case 'always': - content.push(AccessibilityHelpNLS.saveAudioCueAlways); - break; - case 'userGesture': - content.push(AccessibilityHelpNLS.saveAudioCueUserGesture); - break; - } - const formatAudioCue = this._configurationService.getValue(AudioCue.format.settingsKey); - switch (formatAudioCue) { - case 'never': - content.push(AccessibilityHelpNLS.formatAudioCueDisabled); - break; - case 'always': - content.push(AccessibilityHelpNLS.formatAudioCueAlways); - break; - case 'userGesture': - content.push(AccessibilityHelpNLS.formatAudioCueUserGesture); - break; - } + + content.push(AccessibilityHelpNLS.listAudioCues); + content.push(AccessibilityHelpNLS.listAlerts); const commentCommandInfo = getCommentCommandInfo(this._keybindingService, this._contextKeyService, this._editor); if (commentCommandInfo) { diff --git a/src/vs/workbench/contrib/accessibility/browser/saveAudioCue.ts b/src/vs/workbench/contrib/accessibility/browser/saveAudioCue.ts new file mode 100644 index 0000000000000..45a0c582aa907 --- /dev/null +++ b/src/vs/workbench/contrib/accessibility/browser/saveAudioCue.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { SaveReason } from 'vs/workbench/common/editor'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; + +export class SaveAudioCueContribution extends Disposable implements IWorkbenchContribution { + constructor( + @IAudioCueService private readonly _audioCueService: IAudioCueService, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, + ) { + super(); + this._register(this._workingCopyService.onDidSave((e) => { + this._audioCueService.playAudioCue(AudioCue.save, { userGesture: e.reason === SaveReason.EXPLICIT }); + })); + } +} diff --git a/src/vs/workbench/contrib/audioCues/browser/audioCueDebuggerContribution.ts b/src/vs/workbench/contrib/audioCues/browser/audioCueDebuggerContribution.ts index e5051feb3a45b..6150308ccfa19 100644 --- a/src/vs/workbench/contrib/audioCues/browser/audioCueDebuggerContribution.ts +++ b/src/vs/workbench/contrib/audioCues/browser/audioCueDebuggerContribution.ts @@ -21,7 +21,7 @@ export class AudioCueLineDebuggerContribution const isEnabled = observableFromEvent( audioCueService.onEnabledChanged(AudioCue.onDebugBreak), - () => audioCueService.isEnabled(AudioCue.onDebugBreak) + () => audioCueService.isCueEnabled(AudioCue.onDebugBreak) ); this._register(autorunWithStore((reader, store) => { /** @description subscribe to debug sessions */ diff --git a/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts b/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts index c9421fd053d01..06a9a8645ed33 100644 --- a/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts +++ b/src/vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution.ts @@ -34,7 +34,7 @@ export class AudioCueLineFeatureContribution private readonly isEnabledCache = new CachedFunction>((cue) => observableFromEvent( this.audioCueService.onEnabledChanged(cue), - () => this.audioCueService.isEnabled(cue) + () => this.audioCueService.isCueEnabled(cue) )); constructor( diff --git a/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts b/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts index efc297855f2c9..dcb9c5067017a 100644 --- a/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts +++ b/src/vs/workbench/contrib/audioCues/browser/audioCues.contribution.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ShowAudioCueHelp } from 'vs/workbench/contrib/audioCues/browser/commands'; +import { ShowAccessibilityAlertHelp, ShowAudioCueHelp } from 'vs/workbench/contrib/audioCues/browser/commands'; import { localize } from 'vs/nls'; import { registerAction2 } from 'vs/platform/actions/common/actions'; import { Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; @@ -165,3 +165,4 @@ Registry.as(ConfigurationExtensions.Configuration).regis }); registerAction2(ShowAudioCueHelp); +registerAction2(ShowAccessibilityAlertHelp); diff --git a/src/vs/workbench/contrib/audioCues/browser/commands.ts b/src/vs/workbench/contrib/audioCues/browser/commands.ts index f5d295f7c4cda..bda17b0a3e947 100644 --- a/src/vs/workbench/contrib/audioCues/browser/commands.ts +++ b/src/vs/workbench/contrib/audioCues/browser/commands.ts @@ -35,8 +35,8 @@ export class ShowAudioCueHelp extends Action2 { const items: (IQuickPickItem & { audioCue: AudioCue })[] = AudioCue.allAudioCues.map((cue, idx) => ({ label: accessibilityService.isScreenReaderOptimized() ? - `${cue.name}${audioCueService.isEnabled(cue) ? '' : ' (' + localize('disabled', "Disabled") + ')'}` - : `${audioCueService.isEnabled(cue) ? '$(check)' : ' '} ${cue.name}`, + `${cue.name}${audioCueService.isCueEnabled(cue) ? '' : ' (' + localize('disabled', "Disabled") + ')'}` + : `${audioCueService.isCueEnabled(cue) ? '$(check)' : ' '} ${cue.name}`, audioCue: cue, buttons: [{ iconClass: ThemeIcon.asClassName(Codicon.settingsGear), @@ -61,3 +61,49 @@ export class ShowAudioCueHelp extends Action2 { await quickPick; } } + +export class ShowAccessibilityAlertHelp extends Action2 { + static readonly ID = 'accessibility.alert.help'; + + constructor() { + super({ + id: ShowAccessibilityAlertHelp.ID, + title: { + value: localize('accessibility.alert.help', "Help: List Alerts"), + original: 'Help: List Alerts' + }, + f1: true, + }); + } + + override async run(accessor: ServicesAccessor): Promise { + const audioCueService = accessor.get(IAudioCueService); + const quickPickService = accessor.get(IQuickInputService); + const preferencesService = accessor.get(IPreferencesService); + const accessibilityService = accessor.get(IAccessibilityService); + + const items: (IQuickPickItem & { audioCue: AudioCue })[] = AudioCue.allAudioCues.filter(c => !!c.alertMessage).map((cue, idx) => ({ + label: accessibilityService.isScreenReaderOptimized() ? + `${cue.name}${audioCueService.isAlertEnabled(cue) ? '' : ' (' + localize('disabled', "Disabled") + ')'}` + : `${audioCueService.isAlertEnabled(cue) ? '$(check)' : ' '} ${cue.name}`, + audioCue: cue, + buttons: [{ + iconClass: ThemeIcon.asClassName(Codicon.settingsGear), + tooltip: localize('alerts.help.settings', 'Enable/Disable Audio Cue'), + }], + })); + + const quickPick = quickPickService.pick( + items, + { + activeItem: items[0], + onDidTriggerItemButton: (context) => { + preferencesService.openSettings({ query: context.item.audioCue.alertSettingsKey }); + }, + placeHolder: localize('alerts.help.placeholder', 'Inspect and configure the status of an alert'), + } + ); + + await quickPick; + } +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index dd15cd78e37a8..9e3a418ab6c9f 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -7,8 +7,8 @@ import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize2 } from 'vs/nls'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; import { Action2, IAction2Options, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; @@ -117,5 +117,5 @@ export function getClearAction(viewId: string, providerId: string) { } function announceChatCleared(accessor: ServicesAccessor): void { - accessor.get(IAccessibleNotificationService).notify(AccessibleNotificationEvent.Clear); + accessor.get(IAudioCueService).playAudioCue(AudioCue.clear); } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 2aa639163de5c..4f35acf8ab168 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -69,7 +69,7 @@ import { Variable } from 'vs/workbench/contrib/debug/common/debugModel'; import { ReplEvaluationResult, ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; const $ = dom.$; @@ -988,9 +988,9 @@ registerAction2(class extends ViewAction { } runInView(_accessor: ServicesAccessor, view: Repl): void { - const accessibleNotificationService = _accessor.get(IAccessibleNotificationService); + const audioCueService = _accessor.get(IAudioCueService); view.clearRepl(); - accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); + audioCueService.playAudioCue(AudioCue.clear); } }); diff --git a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts index 05f9e919889d2..27594f7cd7c51 100644 --- a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts @@ -23,8 +23,7 @@ import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { TestContextService, TestMarkerService } from 'vs/workbench/test/common/workbenchTestServices'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; -import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; -import { TestAccessibleNotificationService } from 'vs/workbench/contrib/accessibility/browser/accessibleNotificationService'; +import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; suite('EditorAutoSave', () => { @@ -44,7 +43,7 @@ suite('EditorAutoSave', () => { const configurationService = new TestConfigurationService(); configurationService.setUserConfiguration('files', autoSaveConfig); instantiationService.stub(IConfigurationService, configurationService); - instantiationService.stub(IAccessibleNotificationService, disposables.add(new TestAccessibleNotificationService())); + instantiationService.stub(IAudioCueService, { playAudioCue: async () => { }, isEnabled(cue: unknown) { return false; } } as any); instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService, diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 4aecf41e33f43..eaad7257dae71 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -28,7 +28,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; // Register Service registerSingleton(IOutputService, OutputService, InstantiationType.Delayed); @@ -223,11 +223,11 @@ class OutputContribution extends Disposable implements IWorkbenchContribution { } async run(accessor: ServicesAccessor): Promise { const outputService = accessor.get(IOutputService); - const accessibleNotificationService = accessor.get(IAccessibleNotificationService); + const audioCueService = accessor.get(IAudioCueService); const activeChannel = outputService.getActiveChannel(); if (activeChannel) { activeChannel.clear(); - accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); + audioCueService.playAudioCue(AudioCue.clear); } } })); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index f37b2f4e2021d..423e7aa80aff5 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -30,7 +30,7 @@ import { IEditor } from 'vs/editor/common/editorCommon'; import { CommonFindController } from 'vs/editor/contrib/find/browser/findController'; import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/browser/multicursor'; import * as nls from 'vs/nls'; -import { AccessibleNotificationEvent, IAccessibilityService, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { MenuId } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -80,6 +80,7 @@ import { TextSearchCompleteMessage } from 'vs/workbench/services/search/common/s import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { ILogService } from 'vs/platform/log/common/log'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; const $ = dom.$; @@ -189,7 +190,7 @@ export class SearchView extends ViewPane { @ITelemetryService telemetryService: ITelemetryService, @INotebookService private readonly notebookService: INotebookService, @ILogService private readonly logService: ILogService, - @IAccessibleNotificationService private readonly accessibleNotificationService: IAccessibleNotificationService + @IAudioCueService private readonly audioCueService: IAudioCueService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService); @@ -1246,7 +1247,7 @@ export class SearchView extends ViewPane { this.viewModel.cancelSearch(); this.tree.ariaLabel = nls.localize('emptySearch', "Empty Search"); - this.accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); + this.audioCueService.playAudioCue(AudioCue.clear); this.reLayout(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 4aefa9ab3ff5f..bec7a9a3502ed 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -759,8 +759,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { icon: Codicon.bell, tooltip: nls.localize('bellStatus', "Bell") }, this._configHelper.config.bellDuration); - this._audioCueService.playSound(AudioCue.terminalBell.sound.getSound()); } + this._audioCueService.playAudioCue(AudioCue.terminalBell); })); }, 1000, this._store); this._register(xterm.raw.onSelectionChange(async () => this._onSelectionChange())); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 97bf4a1ff7825..529b33ddef54c 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -42,8 +42,8 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { debounce } from 'vs/base/common/decorators'; import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; -import { AccessibleNotificationEvent, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; const enum RenderConstants { /** @@ -205,7 +205,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach @ITelemetryService private readonly _telemetryService: ITelemetryService, @IClipboardService private readonly _clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService, - @IAccessibleNotificationService private readonly _accessibleNotificationService: IAccessibleNotificationService, + @IAudioCueService private readonly _audioCueService: IAudioCueService, @ILayoutService layoutService: ILayoutService ) { super(); @@ -584,7 +584,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach // the prompt being written this._capabilities.get(TerminalCapability.CommandDetection)?.handlePromptStart(); this._capabilities.get(TerminalCapability.CommandDetection)?.handleCommandStart(); - this._accessibleNotificationService.notify(AccessibleNotificationEvent.Clear); + this._audioCueService.playAudioCue(AudioCue.clear); } hasSelection(): boolean { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index 75812ea4a318e..833db09d807b7 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { importAMDNodeModule } from 'vs/amdX'; import { isWindows } from 'vs/base/common/platform'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -22,7 +21,6 @@ import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilitie import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { TestAccessibleNotificationService } from 'vs/workbench/contrib/accessibility/browser/accessibleNotificationService'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; @@ -32,6 +30,7 @@ import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecy import { TestLayoutService, TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestLoggerService } from 'vs/workbench/test/common/workbenchTestServices'; import type { Terminal } from '@xterm/xterm'; +import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; const defaultTerminalConfig: Partial = { fontFamily: 'monospace', @@ -68,7 +67,8 @@ suite('Buffer Content Tracker', () => { instantiationService.stub(IContextMenuService, store.add(instantiationService.createInstance(ContextMenuService))); instantiationService.stub(ILifecycleService, store.add(new TestLifecycleService())); instantiationService.stub(IContextKeyService, store.add(new MockContextKeyService())); - instantiationService.stub(IAccessibleNotificationService, store.add(new TestAccessibleNotificationService())); + instantiationService.stub(IAudioCueService, { playAudioCue: async () => { }, isEnabled(cue: unknown) { return false; } } as any); + instantiationService.stub(ILayoutService, new TestLayoutService()); configHelper = store.add(instantiationService.createInstance(TerminalConfigHelper)); capabilities = store.add(new TerminalCapabilityStore()); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index b7f8f32f918de..e398096245825 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -73,7 +73,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkingCopyService, WorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IWorkingCopy, IWorkingCopyBackupMeta, IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { IFilesConfigurationService, FilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; -import { IAccessibilityService, IAccessibleNotificationService } from 'vs/platform/accessibility/common/accessibility'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { BrowserTextFileService } from 'vs/workbench/services/textfile/browser/browserTextFileService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -166,10 +166,10 @@ import { IHoverOptions, IHoverService } from 'vs/platform/hover/browser/hover'; import { IRemoteExtensionsScannerService } from 'vs/platform/remote/common/remoteExtensionsScanner'; import { IRemoteSocketFactoryService, RemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; import { EditorParts } from 'vs/workbench/browser/parts/editor/editorParts'; -import { TestAccessibleNotificationService } from 'vs/workbench/contrib/accessibility/browser/accessibleNotificationService'; import { mainWindow } from 'vs/base/browser/window'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IHoverWidget } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -279,8 +279,7 @@ export function workbenchInstantiationService( instantiationService.stub(IDialogService, new TestDialogService()); const accessibilityService = new TestAccessibilityService(); instantiationService.stub(IAccessibilityService, accessibilityService); - const accessibleNotificationService = disposables.add(new TestAccessibleNotificationService()); - instantiationService.stub(IAccessibleNotificationService, accessibleNotificationService); + instantiationService.stub(IAudioCueService, { playAudioCue: async () => { }, isEnabled(cue: unknown) { return false; } } as any); instantiationService.stub(IFileDialogService, instantiationService.createInstance(TestFileDialogService)); instantiationService.stub(ILanguageService, disposables.add(instantiationService.createInstance(LanguageService))); instantiationService.stub(ILanguageFeaturesService, new LanguageFeaturesService());