mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-14 12:11:43 +01:00
Merge pull request #188310 from microsoft/aamunger/notebookOutputView
put cell output content into accessibility view
This commit is contained in:
+1
-1
@@ -47,7 +47,7 @@ export class ContributedStatusBarItemController extends Disposable implements IN
|
||||
added: ICellViewModel[];
|
||||
removed: { handle: number }[];
|
||||
}): void {
|
||||
const vm = this._notebookEditor._getViewModel();
|
||||
const vm = this._notebookEditor.getViewModel();
|
||||
if (!vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ export class NotebookStatusBarController extends Disposable {
|
||||
}
|
||||
|
||||
private _updateVisibleCells(e: ICellVisibilityChangeEvent): void {
|
||||
const vm = this._notebookEditor._getViewModel();
|
||||
const vm = this._notebookEditor.getViewModel();
|
||||
if (!vm) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ export class FindModel extends Disposable {
|
||||
|
||||
// we only update cell state if users are using the hybrid mode (both input and preview are enabled)
|
||||
const updateEditingState = () => {
|
||||
const viewModel = this._notebookEditor._getViewModel() as NotebookViewModel | undefined;
|
||||
const viewModel = this._notebookEditor.getViewModel() as NotebookViewModel | undefined;
|
||||
if (!viewModel) {
|
||||
return;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ export class FindModel extends Disposable {
|
||||
|
||||
if (e.isReplaceRevealed && !this._state.isReplaceRevealed) {
|
||||
// replace is hidden, we need to switch all markdown cells to preview mode
|
||||
const viewModel = this._notebookEditor._getViewModel() as NotebookViewModel | undefined;
|
||||
const viewModel = this._notebookEditor.getViewModel() as NotebookViewModel | undefined;
|
||||
if (!viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEdi
|
||||
const replacePattern = this.replacePattern;
|
||||
const replaceString = replacePattern.buildReplaceString(match.matches, this._state.preserveCase);
|
||||
|
||||
const viewModel = this._notebookEditor._getViewModel();
|
||||
const viewModel = this._notebookEditor.getViewModel();
|
||||
viewModel.replaceOne(cell, match.range, replaceString).then(() => {
|
||||
this._progressBar.stop();
|
||||
});
|
||||
@@ -215,7 +215,7 @@ class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEdi
|
||||
});
|
||||
});
|
||||
|
||||
const viewModel = this._notebookEditor._getViewModel();
|
||||
const viewModel = this._notebookEditor.getViewModel();
|
||||
viewModel.replaceAll(this._findModel.findMatches, replaceStrings).then(() => {
|
||||
this._progressBar.stop();
|
||||
});
|
||||
|
||||
@@ -431,7 +431,7 @@ registerAction2(class extends NotebookCellAction {
|
||||
|
||||
function getPageSize(context: INotebookCellActionContext) {
|
||||
const editor = context.notebookEditor;
|
||||
const layoutInfo = editor._getViewModel().layoutInfo;
|
||||
const layoutInfo = editor.getViewModel().layoutInfo;
|
||||
const lineHeight = layoutInfo?.fontInfo.lineHeight || 17;
|
||||
return Math.max(1, Math.floor((layoutInfo?.height || 0) / lineHeight) - 2);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ export class TroubleshootController extends Disposable implements INotebookEdito
|
||||
});
|
||||
}));
|
||||
|
||||
const vm = this._notebookEditor._getViewModel();
|
||||
const vm = this._notebookEditor.getViewModel();
|
||||
let items: INotebookDeltaCellStatusBarItems[] = [];
|
||||
|
||||
if (this._enabled) {
|
||||
|
||||
@@ -21,7 +21,7 @@ class NotebookUndoRedoContribution extends Disposable {
|
||||
const PRIORITY = 105;
|
||||
this._register(UndoCommand.addImplementation(PRIORITY, 'notebook-undo-redo', () => {
|
||||
const editor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
const viewModel = editor?._getViewModel() as NotebookViewModel | undefined;
|
||||
const viewModel = editor?.getViewModel() as NotebookViewModel | undefined;
|
||||
if (editor && editor.hasModel() && viewModel) {
|
||||
return viewModel.undo().then(cellResources => {
|
||||
if (cellResources?.length) {
|
||||
@@ -42,7 +42,7 @@ class NotebookUndoRedoContribution extends Disposable {
|
||||
|
||||
this._register(RedoCommand.addImplementation(PRIORITY, 'notebook-undo-redo', () => {
|
||||
const editor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
const viewModel = editor?._getViewModel() as NotebookViewModel | undefined;
|
||||
const viewModel = editor?.getViewModel() as NotebookViewModel | undefined;
|
||||
|
||||
if (editor && editor.hasModel() && viewModel) {
|
||||
return viewModel.redo().then(cellResources => {
|
||||
|
||||
@@ -498,7 +498,7 @@ export async function joinNotebookCells(editor: IActiveNotebookEditor, range: IC
|
||||
export async function joinCellsWithSurrounds(bulkEditService: IBulkEditService, context: INotebookCellActionContext, direction: 'above' | 'below'): Promise<void> {
|
||||
const editor = context.notebookEditor;
|
||||
const textModel = editor.textModel;
|
||||
const viewModel = editor._getViewModel() as NotebookViewModel;
|
||||
const viewModel = editor.getViewModel() as NotebookViewModel;
|
||||
let ret: {
|
||||
edits: ResourceEdit[];
|
||||
cell: ICellViewModel;
|
||||
@@ -656,7 +656,7 @@ export function insertCell(
|
||||
initialText: string = '',
|
||||
ui: boolean = false
|
||||
) {
|
||||
const viewModel = editor._getViewModel() as NotebookViewModel;
|
||||
const viewModel = editor.getViewModel() as NotebookViewModel;
|
||||
const activeKernel = editor.activeKernel;
|
||||
if (viewModel.options.isReadOnly) {
|
||||
return null;
|
||||
|
||||
@@ -49,7 +49,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont
|
||||
|
||||
this._foldingModel = new FoldingModel();
|
||||
this._localStore.add(this._foldingModel);
|
||||
this._foldingModel.attachViewModel(this._notebookEditor._getViewModel());
|
||||
this._foldingModel.attachViewModel(this._notebookEditor.getViewModel());
|
||||
|
||||
this._localStore.add(this._foldingModel.onDidFoldingRegionChanged(() => {
|
||||
this._updateEditorFoldingRanges();
|
||||
@@ -103,7 +103,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont
|
||||
return;
|
||||
}
|
||||
|
||||
const vm = this._notebookEditor._getViewModel() as NotebookViewModel;
|
||||
const vm = this._notebookEditor.getViewModel() as NotebookViewModel;
|
||||
|
||||
vm.updateFoldingRanges(this._foldingModel.regions);
|
||||
const hiddenRanges = vm.getHiddenRanges();
|
||||
@@ -119,7 +119,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont
|
||||
return;
|
||||
}
|
||||
|
||||
const viewModel = this._notebookEditor._getViewModel() as NotebookViewModel;
|
||||
const viewModel = this._notebookEditor.getViewModel() as NotebookViewModel;
|
||||
const target = e.event.target as HTMLElement;
|
||||
|
||||
if (target.classList.contains('codicon-notebook-collapsed') || target.classList.contains('codicon-notebook-expanded')) {
|
||||
@@ -243,7 +243,7 @@ registerAction2(class extends Action2 {
|
||||
controller.setFoldingStateDown(index, CellFoldingState.Collapsed, levels);
|
||||
}
|
||||
|
||||
const viewIndex = editor._getViewModel().getNearestVisibleCellIndexUpwards(index);
|
||||
const viewIndex = editor.getViewModel().getNearestVisibleCellIndexUpwards(index);
|
||||
editor.focusElement(editor.cellAt(viewIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,9 +112,10 @@ import { NotebookKernelHistoryService } from 'vs/workbench/contrib/notebook/brow
|
||||
import { INotebookLoggingService } from 'vs/workbench/contrib/notebook/common/notebookLoggingService';
|
||||
import { NotebookLoggingService } from 'vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
|
||||
import { NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
|
||||
import { runAccessibilityHelpAction } from 'vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp';
|
||||
import { AccessibilityHelpAction, AccessibleViewAction } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
|
||||
import { NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys';
|
||||
import { runAccessibilityHelpAction, showAccessibleOutput } from 'vs/workbench/contrib/notebook/browser/notebookAccessibility';
|
||||
import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
|
||||
|
||||
/*--------------------------------------------------------------------------------------------- */
|
||||
|
||||
@@ -689,6 +690,19 @@ class NotebookAccessibilityHelpContribution extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
class NotebookAccessibleViewContribution extends Disposable {
|
||||
static ID: 'chatAccessibleViewContribution';
|
||||
constructor() {
|
||||
super();
|
||||
this._register(AccessibleViewAction.addImplementation(100, 'notebook', accessor => {
|
||||
const accessibleViewService = accessor.get(IAccessibleViewService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
return showAccessibleOutput(accessibleViewService, editorService);
|
||||
}, NOTEBOOK_OUTPUT_FOCUSED));
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookContribution, LifecyclePhase.Starting);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(CellContentProvider, LifecyclePhase.Starting);
|
||||
@@ -698,6 +712,7 @@ workbenchContributionsRegistry.registerWorkbenchContribution(NotebookEditorManag
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookLanguageSelectorScoreRefine, LifecyclePhase.Ready);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(SimpleNotebookWorkingCopyEditorHandler, LifecyclePhase.Ready);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookAccessibilityHelpContribution, LifecyclePhase.Eventually);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(NotebookAccessibleViewContribution, LifecyclePhase.Eventually);
|
||||
|
||||
registerSingleton(INotebookService, NotebookService, InstantiationType.Delayed);
|
||||
registerSingleton(INotebookEditorWorkerService, NotebookEditorWorkerServiceImpl, InstantiationType.Delayed);
|
||||
|
||||
+67
@@ -10,6 +10,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView';
|
||||
import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityContribution';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
|
||||
export function getAccessibilityHelpText(accessor: ServicesAccessor): string {
|
||||
const keybindingService = accessor.get(IKeybindingService);
|
||||
@@ -55,3 +57,68 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi
|
||||
options: { type: AccessibleViewType.HelpMenu, ariaLabel: 'Notebook accessibility help' }
|
||||
});
|
||||
}
|
||||
|
||||
export function showAccessibleOutput(accessibleViewService: IAccessibleViewService, editorService: IEditorService) {
|
||||
const activePane = editorService.activeEditorPane;
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(activePane);
|
||||
const notebookViewModel = notebookEditor?.getViewModel();
|
||||
const selections = notebookViewModel?.getSelections();
|
||||
const notebookDocument = notebookViewModel?.notebookDocument;
|
||||
|
||||
if (!selections || !notebookDocument || !notebookEditor?.textModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const viewCell = notebookViewModel.viewCells[selections[0].start];
|
||||
let outputContent = '';
|
||||
const decoder = new TextDecoder();
|
||||
for (let i = 0; i < viewCell.outputsViewModels.length; i++) {
|
||||
const outputViewModel = viewCell.outputsViewModels[i];
|
||||
const outputTextModel = viewCell.model.outputs[i];
|
||||
const [mimeTypes, pick] = outputViewModel.resolveMimeTypes(notebookEditor.textModel, undefined);
|
||||
const mimeType = mimeTypes[pick].mimeType;
|
||||
let buffer = outputTextModel.outputs.find(output => output.mime === mimeType);
|
||||
|
||||
if (!buffer || mimeType.startsWith('image')) {
|
||||
buffer = outputTextModel.outputs.find(output => !output.mime.startsWith('image'));
|
||||
}
|
||||
|
||||
let text = `${mimeType}`; // default in case we can't get the text value for some reason.
|
||||
if (buffer) {
|
||||
const charLimit = 100_000;
|
||||
text = decoder.decode(buffer.data.slice(0, charLimit).buffer);
|
||||
|
||||
if (buffer.data.byteLength > charLimit) {
|
||||
text = text + '...(truncated)';
|
||||
}
|
||||
|
||||
if (mimeType.endsWith('error')) {
|
||||
text = text.replace(/\\u001b\[[0-9;]*m/gi, '').replaceAll('\\n', '\n');
|
||||
}
|
||||
}
|
||||
|
||||
const index = viewCell.outputsViewModels.length > 1
|
||||
? `Cell output ${i + 1} of ${viewCell.outputsViewModels.length}\n`
|
||||
: '';
|
||||
outputContent = outputContent.concat(`${index}${text}\n`);
|
||||
}
|
||||
|
||||
if (!outputContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
accessibleViewService.show({
|
||||
verbositySettingKey: AccessibilityVerbositySettingId.Notebook,
|
||||
provideContent(): string { return outputContent; },
|
||||
onClose() {
|
||||
notebookEditor?.setFocus(selections[0]);
|
||||
activePane?.focus();
|
||||
},
|
||||
options: {
|
||||
ariaLabel: localize('NotebookCellOutputAccessibleView', "Notebook Cell Output Accessible View"),
|
||||
language: 'plaintext',
|
||||
type: AccessibleViewType.View
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -476,7 +476,7 @@ export interface INotebookEditor {
|
||||
setFocus(focus: ICellRange): void;
|
||||
getId(): string;
|
||||
|
||||
_getViewModel(): INotebookViewModel | undefined;
|
||||
getViewModel(): INotebookViewModel | undefined;
|
||||
hasModel(): this is IActiveNotebookEditor;
|
||||
dispose(): void;
|
||||
getDomNode(): HTMLElement;
|
||||
@@ -684,7 +684,7 @@ export interface INotebookEditor {
|
||||
}
|
||||
|
||||
export interface IActiveNotebookEditor extends INotebookEditor {
|
||||
_getViewModel(): INotebookViewModel;
|
||||
getViewModel(): INotebookViewModel;
|
||||
textModel: NotebookTextModel;
|
||||
getFocus(): ICellRange;
|
||||
cellAt(index: number): ICellViewModel;
|
||||
@@ -730,7 +730,7 @@ export interface INotebookEditorDelegate extends INotebookEditor {
|
||||
}
|
||||
|
||||
export interface IActiveNotebookEditorDelegate extends INotebookEditorDelegate {
|
||||
_getViewModel(): INotebookViewModel;
|
||||
getViewModel(): INotebookViewModel;
|
||||
textModel: NotebookTextModel;
|
||||
getFocus(): ICellRange;
|
||||
cellAt(index: number): ICellViewModel;
|
||||
|
||||
@@ -442,7 +442,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
||||
return this._uuid;
|
||||
}
|
||||
|
||||
_getViewModel(): NotebookViewModel | undefined {
|
||||
getViewModel(): NotebookViewModel | undefined {
|
||||
return this.viewModel;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ export class FoldedCellHint extends CellContentPart {
|
||||
if (element.isInputCollapsed || element.getEditState() === CellEditState.Editing) {
|
||||
DOM.hide(this._container);
|
||||
} else if (element.foldingState === CellFoldingState.Collapsed) {
|
||||
const idx = this._notebookEditor._getViewModel().getCellIndex(element);
|
||||
const length = this._notebookEditor._getViewModel().getFoldedLength(idx);
|
||||
const idx = this._notebookEditor.getViewModel().getCellIndex(element);
|
||||
const length = this._notebookEditor.getViewModel().getFoldedLength(idx);
|
||||
DOM.reset(this._container, this.getHiddenCellsLabel(length), this.getHiddenCellHintButton(element));
|
||||
DOM.show(this._container);
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export class NotebookOverviewRuler extends Themable {
|
||||
}
|
||||
|
||||
private _render(ctx: CanvasRenderingContext2D, width: number, height: number, scrollHeight: number, ratio: number) {
|
||||
const viewModel = this.notebookEditor._getViewModel();
|
||||
const viewModel = this.notebookEditor.getViewModel();
|
||||
const fontInfo = this.notebookEditor.getLayoutInfo().fontInfo;
|
||||
const laneWidth = width / this._lanes;
|
||||
|
||||
|
||||
@@ -221,7 +221,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic
|
||||
override notebookOptions = notebookOptions;
|
||||
override onDidChangeModel: Event<NotebookTextModel | undefined> = new Emitter<NotebookTextModel | undefined>().event;
|
||||
override onDidChangeCellState: Event<NotebookCellStateChangedEvent> = new Emitter<NotebookCellStateChangedEvent>().event;
|
||||
override _getViewModel(): NotebookViewModel {
|
||||
override getViewModel(): NotebookViewModel {
|
||||
return viewModel;
|
||||
}
|
||||
override textModel = viewModel.notebookDocument;
|
||||
|
||||
Reference in New Issue
Block a user