add audio cues for reviewing a diff editor (#166413)

This commit is contained in:
Megan Rogge
2022-11-16 15:10:45 -08:00
committed by GitHub
parent 5588a85039
commit 91c7eaf973
25 changed files with 70 additions and 31 deletions
@@ -1878,6 +1878,11 @@ function createDecoration(startLineNumber: number, startColumn: number, endLineN
};
}
const enum DiffEditorLineClasses {
Insert = 'line-insert',
Delete = 'line-delete'
}
const DECORATIONS = {
arrowRevertChange: ModelDecorationOptions.register({
@@ -1907,13 +1912,13 @@ const DECORATIONS = {
lineInsert: ModelDecorationOptions.register({
description: 'diff-editor-line-insert',
className: 'line-insert',
className: DiffEditorLineClasses.Insert,
marginClassName: 'gutter-insert',
isWholeLine: true
}),
lineInsertWithSign: ModelDecorationOptions.register({
description: 'diff-editor-line-insert-with-sign',
className: 'line-insert',
className: DiffEditorLineClasses.Insert,
linesDecorationsClassName: 'insert-sign ' + ThemeIcon.asClassName(diffInsertIcon),
marginClassName: 'gutter-insert',
isWholeLine: true
@@ -1921,13 +1926,13 @@ const DECORATIONS = {
lineDelete: ModelDecorationOptions.register({
description: 'diff-editor-line-delete',
className: 'line-delete',
className: DiffEditorLineClasses.Delete,
marginClassName: 'gutter-delete',
isWholeLine: true
}),
lineDeleteWithSign: ModelDecorationOptions.register({
description: 'diff-editor-line-delete-with-sign',
className: 'line-delete',
className: DiffEditorLineClasses.Delete,
linesDecorationsClassName: 'delete-sign ' + ThemeIcon.asClassName(diffRemoveIcon),
marginClassName: 'gutter-delete',
isWholeLine: true
+22 -9
View File
@@ -33,6 +33,7 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { ILanguageIdCodec } from 'vs/editor/common/languages';
import { ILanguageService } from 'vs/editor/common/languages/language';
import { ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
const DIFF_LINES_PADDING = 3;
@@ -66,6 +67,11 @@ class DiffEntry {
}
}
const enum DiffEditorLineClasses {
Insert = 'line-insert',
Delete = 'line-delete'
}
class Diff {
readonly entries: DiffEntry[];
@@ -95,7 +101,8 @@ export class DiffReview extends Disposable {
constructor(
diffEditor: DiffEditorWidget,
@ILanguageService private readonly _languageService: ILanguageService
@ILanguageService private readonly _languageService: ILanguageService,
@IAudioCueService private readonly _audioCueService: IAudioCueService
) {
super();
this._diffEditor = diffEditor;
@@ -149,7 +156,7 @@ export class DiffReview extends Disposable {
|| e.equals(KeyMod.Alt | KeyCode.DownArrow)
) {
e.preventDefault();
this._goToRow(this._getNextRow());
this._goToRow(this._getNextRow(), 'next');
}
if (
@@ -158,7 +165,7 @@ export class DiffReview extends Disposable {
|| e.equals(KeyMod.Alt | KeyCode.UpArrow)
) {
e.preventDefault();
this._goToRow(this._getPrevRow());
this._goToRow(this._getPrevRow(), 'previous');
}
if (
@@ -215,7 +222,7 @@ export class DiffReview extends Disposable {
this._isVisible = true;
this._diffEditor.doLayout();
this._render();
this._goToRow(this._getNextRow());
this._goToRow(this._getPrevRow(), 'previous');
}
public next(): void {
@@ -250,7 +257,7 @@ export class DiffReview extends Disposable {
this._isVisible = true;
this._diffEditor.doLayout();
this._render();
this._goToRow(this._getNextRow());
this._goToRow(this._getNextRow(), 'next');
}
private accept(): void {
@@ -312,12 +319,18 @@ export class DiffReview extends Disposable {
return null;
}
private _goToRow(row: HTMLElement): void {
const prev = this._getCurrentFocusedRow();
private _goToRow(row: HTMLElement, type?: 'next' | 'previous'): void {
const current = this._getCurrentFocusedRow();
row.tabIndex = 0;
row.focus();
if (prev && prev !== row) {
prev.tabIndex = -1;
if (current && current !== row) {
current.tabIndex = -1;
}
const element = !type ? current : type === 'next' ? current?.nextElementSibling : current?.previousElementSibling;
if (element?.classList.contains(DiffEditorLineClasses.Insert)) {
this._audioCueService.playAudioCue(AudioCue.diffLineInserted, true);
} else if (element?.classList.contains(DiffEditorLineClasses.Delete)) {
this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, true);
}
this.scrollbar.scanDomNode();
}
@@ -495,7 +495,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon
@IConfigurationService configurationService: IConfigurationService,
@IContextMenuService contextMenuService: IContextMenuService,
@IEditorProgressService editorProgressService: IEditorProgressService,
@IClipboardService clipboardService: IClipboardService,
@IClipboardService clipboardService: IClipboardService
) {
const options = { ..._options };
updateConfigurationService(configurationService, options, true);
@@ -17,7 +17,7 @@ export const IAudioCueService = createDecorator<IAudioCueService>('audioCue');
export interface IAudioCueService {
readonly _serviceBrand: undefined;
playAudioCue(cue: AudioCue): Promise<void>;
playAudioCue(cue: AudioCue, allowManyInParallel?: boolean): Promise<void>;
playAudioCues(cues: AudioCue[]): Promise<void>;
isEnabled(cue: AudioCue): IObservable<boolean>;
@@ -39,9 +39,9 @@ export class AudioCueService extends Disposable implements IAudioCueService {
super();
}
public async playAudioCue(cue: AudioCue): Promise<void> {
public async playAudioCue(cue: AudioCue, allowManyInParallel = false): Promise<void> {
if (this.isEnabled(cue).get()) {
await this.playSound(cue.sound);
await this.playSound(cue.sound, allowManyInParallel);
}
}
@@ -70,7 +70,7 @@ export class AudioCueService extends Disposable implements IAudioCueService {
this.playingSounds.add(sound);
const url = FileAccess.asBrowserUri(
`vs/workbench/contrib/audioCues/browser/media/${sound.fileName}`
`vs/platform/audioCues/common/media/${sound.fileName}`
).toString();
const audio = new Audio(url);
audio.volume = this.getVolumeInPercent() / 100;
@@ -164,6 +164,8 @@ export class Sound {
public static readonly taskCompleted = Sound.register({ fileName: 'taskCompleted.mp3' });
public static readonly taskFailed = Sound.register({ fileName: 'taskFailed.mp3' });
public static readonly terminalBell = Sound.register({ fileName: 'terminalBell.mp3' });
public static readonly diffLineInserted = Sound.register({ fileName: 'diffLineInserted.mp3' });
public static readonly diffLineDeleted = Sound.register({ fileName: 'diffLineDeleted.mp3' });
private constructor(public readonly fileName: string) { }
}
@@ -247,6 +249,18 @@ export class AudioCue {
settingsKey: 'audioCues.terminalBell'
});
public static readonly diffLineInserted = AudioCue.register({
name: localize('audioCues.diffLineInserted', 'Diff Line Inserted'),
sound: Sound.diffLineInserted,
settingsKey: 'audioCues.diffLineInserted'
});
public static readonly diffLineDeleted = AudioCue.register({
name: localize('audioCues.diffLineDeleted', 'Diff Line Deleted'),
sound: Sound.diffLineDeleted,
settingsKey: 'audioCues.diffLineDeleted'
});
private constructor(
public readonly sound: Sound,
public readonly name: string,
@@ -129,7 +129,6 @@ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).regi
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DynamicEditorConfigurations, LifecyclePhase.Ready);
registerEditorContribution(FloatingClickMenu.ID, FloatingClickMenu);
//#endregion
//#region Quick Access
@@ -5,8 +5,8 @@
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { autorunWithStore } from 'vs/base/common/observable';
import { IAudioCueService, AudioCue } from 'vs/platform/audioCues/browser/audioCueService';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { IDebugService, IDebugSession } from 'vs/workbench/contrib/debug/common/debug';
export class AudioCueLineDebuggerContribution
@@ -14,9 +14,9 @@ import { FoldingController } from 'vs/editor/contrib/folding/browser/folding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel } from 'vs/editor/common/model';
import { GhostTextController } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextController';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { CursorChangeReason } from 'vs/editor/common/cursorEvents';
import { autorun, autorunDelta, constObservable, debouncedObservable, derived, IObservable, observableFromEvent, observableFromPromise, wasEventTriggeredRecently } from 'vs/base/common/observable';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
export class AudioCueLineFeatureContribution
extends Disposable
@@ -3,17 +3,17 @@
* 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 { localize } from 'vs/nls';
import { registerAction2 } from 'vs/platform/actions/common/actions';
import { Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IAudioCueService, AudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
import { AudioCueLineDebuggerContribution } from 'vs/workbench/contrib/audioCues/browser/audioCueDebuggerContribution';
import { AudioCueLineFeatureContribution } from 'vs/workbench/contrib/audioCues/browser/audioCueLineFeatureContribution';
import { AudioCueService, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { ShowAudioCueHelp } from 'vs/workbench/contrib/audioCues/browser/commands';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
registerSingleton(IAudioCueService, AudioCueService, InstantiationType.Delayed);
@@ -87,6 +87,14 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
'description': localize('audioCues.terminalQuickFix', "Plays a sound when terminal Quick Fixes are available."),
...audioCueFeatureBase,
},
'audioCues.diffLineInserted': {
'description': localize('audioCues.diffLineInserted', "Plays a sound when the focus moves to an inserted line in diff review mode"),
...audioCueFeatureBase,
},
'audioCues.diffLineDeleted': {
'description': localize('audioCues.diffLineDeleted', "Plays a sound when the focus moves to a deleted line in diff review mode"),
...audioCueFeatureBase,
},
}
});
@@ -7,9 +7,9 @@ import { Codicon } from 'vs/base/common/codicons';
import { localize } from 'vs/nls';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { Action2 } from 'vs/platform/actions/common/actions';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
export class ShowAudioCueHelp extends Action2 {
@@ -15,11 +15,11 @@ import { InlayHintItem, asCommandLink } from 'vs/editor/contrib/inlayHints/brows
import { InlayHintsController } from 'vs/editor/contrib/inlayHints/browser/inlayHintsController';
import { localize } from 'vs/nls';
import { registerAction2 } from 'vs/platform/actions/common/actions';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Link } from 'vs/platform/opener/browser/link';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
export class InlayHintsAccessibility implements IEditorContribution {
@@ -15,7 +15,7 @@ import { ITerminalStatus } from 'vs/workbench/contrib/terminal/browser/terminalS
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { spinningLoading } from 'vs/platform/theme/common/iconRegistry';
import { IMarker } from 'vs/platform/terminal/common/capabilities/capabilities';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
interface ITerminalData {
terminal: ITerminalInstance;
@@ -46,7 +46,7 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
interface IWorkspaceFolderConfigurationResult {
workspaceFolder: IWorkspaceFolder;
@@ -5,9 +5,9 @@
import { ok } from 'assert';
import { Emitter, Event } from 'vs/base/common/event';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { ACTIVE_TASK_STATUS, FAILED_TASK_STATUS, SUCCEEDED_TASK_STATUS, TaskTerminalStatus } from 'vs/workbench/contrib/tasks/browser/taskTerminalStatus';
import { AbstractProblemCollector } from 'vs/workbench/contrib/tasks/common/problemCollectors';
import { CommonTask, ITaskEvent, TaskEventKind, TaskRunType } from 'vs/workbench/contrib/tasks/common/tasks';
@@ -55,7 +55,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { IViewDescriptorService, IViewsService, ViewContainerLocation } from 'vs/workbench/common/views';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { TaskSettingId } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDetectedLinks, TerminalLinkManager } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkManager';
import { TerminalLinkQuickpick } from 'vs/workbench/contrib/terminal/browser/links/terminalLinkQuickpick';
@@ -89,6 +88,7 @@ import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import type { IMarker, ITerminalAddon, Terminal as XTermTerminal } from 'xterm';
import { IAudioCueService, AudioCue } from 'vs/platform/audioCues/browser/audioCueService';
const enum Constants {
/**
@@ -11,7 +11,6 @@ import { asArray } from 'vs/base/common/arrays';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { AudioCue, IAudioCueService } from 'vs/workbench/contrib/audioCues/browser/audioCueService';
import { ITerminalQuickFixOpenerAction, ITerminalQuickFixOptions, TerminalQuickFixAction, TerminalQuickFixMatchResult } from 'vs/workbench/contrib/terminal/browser/terminal';
import { DecorationSelector, updateLayout } from 'vs/workbench/contrib/terminal/browser/xterm/decorationStyles';
import { IDecoration, Terminal } from 'xterm';
@@ -24,6 +23,7 @@ import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/comm
import { IExtensionTerminalQuickFix } from 'vs/platform/terminal/common/terminal';
import { URI } from 'vs/base/common/uri';
import { gitCreatePr, gitPushSetUpstream, gitSimilar } from 'vs/workbench/contrib/terminal/browser/terminalQuickFixBuiltinActions';
import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IActionWidgetService } from 'vs/platform/actionWidget/browser/actionWidget';
import { ActionSet } from 'vs/platform/actionWidget/common/actionWidget';