Skip to content

Commit

Permalink
more a11y polish on REPL (#233792)
Browse files Browse the repository at this point in the history
read minimal error if possible
  • Loading branch information
amunger authored Nov 13, 2024
1 parent 9355625 commit a53bfec
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,12 @@ const configuration: IConfigurationNode = {
'type': 'boolean',
'description': localize('accessibility.replEditor.readLastExecutedOutput', "Controls whether the output from an execution in the native REPL will be announced."),
'default': true,
},
'accessibility.replEditor.autoFocusReplExecution': {
type: 'string',
enum: ['none', 'input', 'lastExecution'],
default: 'lastExecution',
description: localize('replEditor.autoFocusAppendedCell', "Control whether focus should automatically be sent to the REPL when code is executed."),
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -752,13 +752,8 @@ registerAction2(class extends Action2 {
category: interactiveWindowCategory,
menu: {
id: MenuId.CommandPalette,
when: InteractiveWindowOpen,
when: InteractiveWindowOpen
},
keybinding: {
when: ContextKeyExpr.and(IS_COMPOSITE_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED),
weight: KeybindingWeight.WorkbenchContrib + 5,
primary: KeyMod.CtrlCmd | KeyCode.DownArrow
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class NotebookAccessibilityProvider extends Disposable implements IListAc
if (this.shouldReadCellOutputs(lastUpdate.state)) {
const cell = viewModel.getCellByHandle(lastUpdate.cellHandle);
if (cell && cell.outputsViewModels.length) {
const text = getAllOutputsText(viewModel.notebookDocument, cell);
const text = getAllOutputsText(viewModel.notebookDocument, cell, true);
alert(text);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface Error {
stack?: string;
}

export function getAllOutputsText(notebook: NotebookTextModel, viewCell: ICellViewModel): string {
export function getAllOutputsText(notebook: NotebookTextModel, viewCell: ICellViewModel, shortErrors: boolean = false): string {
const outputText: string[] = [];
for (let i = 0; i < viewCell.outputsViewModels.length; i++) {
const outputViewModel = viewCell.outputsViewModels[i];
Expand All @@ -40,7 +40,7 @@ export function getAllOutputsText(notebook: NotebookTextModel, viewCell: ICellVi
i += count - 1;
}
} else {
text = getOutputText(mimeType, buffer);
text = getOutputText(mimeType, buffer, shortErrors);
}

outputText.push(text);
Expand Down Expand Up @@ -80,7 +80,7 @@ export function getOutputStreamText(output: ICellOutputViewModel): { text: strin

const decoder = new TextDecoder();

export function getOutputText(mimeType: string, buffer: IOutputItemDto) {
export function getOutputText(mimeType: string, buffer: IOutputItemDto, shortError: boolean = false): string {
let text = `${mimeType}`; // default in case we can't get the text value for some reason.

const charLimit = 100000;
Expand All @@ -92,10 +92,10 @@ export function getOutputText(mimeType: string, buffer: IOutputItemDto) {
text = text.replace(/\\u001b\[[0-9;]*m/gi, '');
try {
const error = JSON.parse(text) as Error;
if (error.stack) {
text = error.stack;
} else {
if (!error.stack || shortError) {
text = `${error.name}: ${error.message}`;
} else {
text = error.stack;
}
} catch {
// just use raw text
Expand Down
58 changes: 23 additions & 35 deletions src/vs/workbench/contrib/replNotebook/browser/repl.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { alert } from '../../../../base/browser/ui/aria/aria.js';
import { Event } from '../../../../base/common/event.js';
import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
Expand All @@ -18,7 +17,6 @@ import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/c
import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js';
import { localize2 } from '../../../../nls.js';
import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../platform/accessibility/common/accessibility.js';
import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
Expand Down Expand Up @@ -46,9 +44,8 @@ import { INotebookEditorOptions } from '../../notebook/browser/notebookBrowser.j
import { NotebookEditorWidget } from '../../notebook/browser/notebookEditorWidget.js';
import * as icons from '../../notebook/browser/notebookIcons.js';
import { INotebookEditorService } from '../../notebook/browser/services/notebookEditorService.js';
import { getAllOutputsText } from '../../notebook/browser/viewModel/cellOutputTextHelper.js';
import { CellEditType, CellKind, NotebookSetting, NotebookWorkingCopyTypeIdentifier, REPL_EDITOR_ID } from '../../notebook/common/notebookCommon.js';
import { IS_COMPOSITE_NOTEBOOK, MOST_RECENT_REPL_EDITOR, NOTEBOOK_CELL_LIST_FOCUSED } from '../../notebook/common/notebookContextKeys.js';
import { IS_COMPOSITE_NOTEBOOK, MOST_RECENT_REPL_EDITOR, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_EDITOR_FOCUSED } from '../../notebook/common/notebookContextKeys.js';
import { NotebookEditorInputOptions } from '../../notebook/common/notebookEditorInput.js';
import { INotebookEditorModelResolverService } from '../../notebook/common/notebookEditorModelResolverService.js';
import { INotebookService } from '../../notebook/common/notebookService.js';
Expand Down Expand Up @@ -238,6 +235,11 @@ registerAction2(class extends Action2 {
id: MenuId.CommandPalette,
when: MOST_RECENT_REPL_EDITOR,
},
keybinding: [{
primary: KeyChord(KeyMod.Alt | KeyCode.End, KeyMod.Alt | KeyCode.End),
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT,
when: ContextKeyExpr.or(IS_COMPOSITE_NOTEBOOK, NOTEBOOK_CELL_LIST_FOCUSED.negate())
}],
precondition: MOST_RECENT_REPL_EDITOR
});
}
Expand Down Expand Up @@ -284,33 +286,34 @@ registerAction2(class extends Action2 {
registerAction2(class extends Action2 {
constructor() {
super({
id: 'repl.readLastExecutionOutput',
title: localize2('repl.readMostRecentExecution', 'Read Most Recent Execution Output'),
id: 'repl.input.focus',
title: localize2('repl.input.focus', 'Focus Input Editor'),
category: 'REPL',
keybinding: [{
primary: KeyChord(KeyMod.Alt | KeyCode.End, KeyMod.Alt | KeyCode.End),
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT
}],
menu: {
id: MenuId.CommandPalette,
when: MOST_RECENT_REPL_EDITOR,
},
precondition: ContextKeyExpr.and(
ContextKeyExpr.or(IS_COMPOSITE_NOTEBOOK, NOTEBOOK_CELL_LIST_FOCUSED.negate()),
MOST_RECENT_REPL_EDITOR,
CONTEXT_ACCESSIBILITY_MODE_ENABLED)
keybinding: [{
when: ContextKeyExpr.and(IS_COMPOSITE_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED),
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT,
primary: KeyMod.CtrlCmd | KeyCode.DownArrow
}, {
when: ContextKeyExpr.and(MOST_RECENT_REPL_EDITOR),
weight: KeybindingWeight.WorkbenchContrib + 5,
primary: KeyChord(KeyMod.Alt | KeyCode.Home, KeyMod.Alt | KeyCode.Home),
}]
});
}

async run(accessor: ServicesAccessor, context?: UriComponents): Promise<void> {
async run(accessor: ServicesAccessor): Promise<void> {
const editorService = accessor.get(IEditorService);
const editorControl = editorService.activeEditorPane?.getControl();
const contextKeyService = accessor.get(IContextKeyService);

let notebookEditor: NotebookEditorWidget | undefined;
if (editorControl && isReplEditorControl(editorControl)) {
notebookEditor = editorControl.notebookEditor;
} else {
if (editorControl && isReplEditorControl(editorControl) && editorControl.notebookEditor) {
editorService.activeEditorPane?.focus();
}
else {
const uriString = MOST_RECENT_REPL_EDITOR.getValue(contextKeyService);
const uri = uriString ? URI.parse(uriString) : undefined;

Expand All @@ -320,22 +323,7 @@ registerAction2(class extends Action2 {
const replEditor = editorService.findEditors(uri)[0];

if (replEditor) {
const editor = await editorService.openEditor({ resource: uri, options: { preserveFocus: true } }, replEditor.groupId);
const editorControl = editor?.getControl();

if (editorControl && isReplEditorControl(editorControl)) {
notebookEditor = editorControl.notebookEditor;
}
}
}

const viewModel = notebookEditor?.getViewModel();
const notebook = notebookEditor?.textModel;
if (viewModel && notebook) {
const cell = viewModel.getMostRecentlyExecutedCell();
if (cell) {
const text = getAllOutputsText(notebook, cell);
alert(text);
await editorService.openEditor({ resource: uri, options: { preserveFocus: false } }, replEditor.groupId);
}
}
}
Expand Down
25 changes: 23 additions & 2 deletions src/vs/workbench/contrib/replNotebook/browser/replEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import { ReplInputHintContentWidget } from '../../interactive/browser/replInputH
import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
import { localize } from '../../../../nls.js';
import { NotebookViewModel } from '../../notebook/browser/viewModel/notebookViewModelImpl.js';
import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js';

const INTERACTIVE_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'InteractiveEditorViewState';

Expand Down Expand Up @@ -129,7 +131,8 @@ export class ReplEditor extends EditorPane implements IEditorPaneWithScrolling {
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
@INotebookExecutionStateService notebookExecutionStateService: INotebookExecutionStateService,
@IExtensionService extensionService: IExtensionService
@IExtensionService extensionService: IExtensionService,
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService
) {
super(
REPL_EDITOR_ID,
Expand Down Expand Up @@ -540,13 +543,31 @@ export class ReplEditor extends EditorPane implements IEditorPaneWithScrolling {
if (addedCells.length) {
const viewModel = notebookWidget.viewModel;
if (viewModel) {
this._notebookWidgetService.updateReplContextKey(viewModel.notebookDocument.uri.toString());
this.handleAppend(notebookWidget, viewModel);
break;
}
}
}
}

private handleAppend(notebookWidget: NotebookEditorWidget, viewModel: NotebookViewModel) {
this._notebookWidgetService.updateReplContextKey(viewModel.notebookDocument.uri.toString());
const navigateToCell = this._configurationService.getValue('accessibility.replEditor.autoFocusReplExecution');
if (this._accessibilityService.isScreenReaderOptimized()) {
if (navigateToCell === 'lastExecution') {
setTimeout(() => {
const lastCellIndex = viewModel.length - 1;
if (lastCellIndex >= 0) {
const cell = viewModel.viewCells[lastCellIndex];
notebookWidget.focusNotebookCell(cell, 'container');
}
}, 0);
} else if (navigateToCell === 'input') {
this._codeEditorWidget.focus();
}
}
}

override setOptions(options: INotebookEditorOptions | undefined): void {
this._notebookWidget.value?.setOptions(options);
super.setOptions(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,12 @@ function getAccessibilityHelpText(): string {
return [
localize('replEditor.overview', 'You are in a REPL Editor which contains in input box to evaluate expressions and a list of previously executed expressions and their output.'),
localize('replEditor.execute', 'The Execute command{0} will evaluate the expression in the input box.', '<keybinding:repl.execute>'),
localize('replEditor.readLastExecution', 'The Read Last Execution Output command{0} will read the output of the last executed item.', '<keybinding:repl.readLastExecutionOutput>'),
localize('replEditor.configReadExecution', 'The setting `accessibility.replEditor.readLastExecutionOutput` controls if output will be automatically read when execution completes.'),
localize('replEditor.focusHistory', 'The Focus History command{0} will move focus to the list of previously executed items.', '<keybinding:interactive.history.focus>'),
localize('replEditor.autoFocusRepl', 'The setting `accessibility.replEditor.autoFocusReplExecution` controls if focus will automatically move to the REPL after executing code.'),
localize('replEditor.focusLastItemAdded', 'The Focus Last executed command{0} will move focus to the last executed item in the REPL history.', '<keybinding:repl.focusLastItemExecuted>'),
localize('replEditor.accessibilityView', 'Run the Open Accessbility View command{0} while navigating the history for an accessible view of the item\'s output.', '<keybinding:editor.action.accessibleView>'),
localize('replEditor.focusLastItemAdded', 'The Focus Last executed command{0} will move focus to the last executed item without needing to first focus on the editor.', '<keybinding:repl.focusLastItemExecuted>'),
localize('replEditor.focusReplInput', 'The Focus Input Editor command{0} will move focus to the REPL input box.', '<keybinding:interactive.input.focus>'),
localize('replEditor.cellNavigation', 'The up and down arrows will also move focus between previously executed items.'),
localize('replEditor.cellNavigation', 'The up and down arrows will also move focus between previously executed items while focused on the REPL history.'),
localize('replEditor.focusReplInput', 'The Focus Input Editor command{0} will move focus to the REPL input box.', '<keybinding:repl.input.focus>'),
localize('replEditor.focusInOutput', 'The Focus Output command{0} will set focus on the output when focused on a previously executed item.', '<keybinding:notebook.cell.focusInOutput>'),
].join('\n');
}
Expand Down

0 comments on commit a53bfec

Please sign in to comment.