Path: blob/main/src/vs/workbench/contrib/debug/browser/replAccessibleView.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { AccessibleViewProviderId, AccessibleViewType, IAccessibleViewContentProvider, IAccessibleViewService } from '../../../../platform/accessibility/browser/accessibleView.js';6import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js';7import { IReplElement } from '../common/debug.js';8import { IAccessibleViewImplementation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js';9import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';10import { getReplView, Repl } from './repl.js';11import { IViewsService } from '../../../services/views/common/viewsService.js';12import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';13import { Emitter, Event } from '../../../../base/common/event.js';14import { Disposable } from '../../../../base/common/lifecycle.js';15import { Position } from '../../../../editor/common/core/position.js';1617export class ReplAccessibleView implements IAccessibleViewImplementation {18priority = 70;19name = 'debugConsole';20when = ContextKeyExpr.equals('focusedView', 'workbench.panel.repl.view');21type: AccessibleViewType = AccessibleViewType.View;22getProvider(accessor: ServicesAccessor) {23const viewsService = accessor.get(IViewsService);24const accessibleViewService = accessor.get(IAccessibleViewService);25const replView = getReplView(viewsService);26if (!replView) {27return undefined;28}2930const focusedElement = replView.getFocusedElement();31return new ReplOutputAccessibleViewProvider(replView, focusedElement, accessibleViewService);32}33}3435class ReplOutputAccessibleViewProvider extends Disposable implements IAccessibleViewContentProvider {36public readonly id = AccessibleViewProviderId.Repl;37private _content: string | undefined;38private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());39public readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;40private readonly _onDidResolveChildren: Emitter<void> = this._register(new Emitter<void>());41public readonly onDidResolveChildren: Event<void> = this._onDidResolveChildren.event;4243public readonly verbositySettingKey = AccessibilityVerbositySettingId.Debug;44public readonly options = {45type: AccessibleViewType.View46};4748private _elementPositionMap: Map<string, Position> = new Map<string, Position>();49private _treeHadFocus = false;5051constructor(52private readonly _replView: Repl,53private readonly _focusedElement: IReplElement | undefined,54@IAccessibleViewService private readonly _accessibleViewService: IAccessibleViewService) {55super();56this._treeHadFocus = !!_focusedElement;57}58public provideContent(): string {59const debugSession = this._replView.getDebugSession();60if (!debugSession) {61return 'No debug session available.';62}63const elements = debugSession.getReplElements();64if (!elements.length) {65return 'No output in the debug console.';66}67if (!this._content) {68this._updateContent(elements);69}70// Content is loaded asynchronously, so we need to check if it's available or fallback to the elements that are already available.71return this._content ?? elements.map(e => e.toString(true)).join('\n');72}7374public onClose(): void {75this._content = undefined;76this._elementPositionMap.clear();77if (this._treeHadFocus) {78return this._replView.focusTree();79}80this._replView.getReplInput().focus();81}8283public onOpen(): void {84// Children are resolved async, so we need to update the content when they are resolved.85this._register(this.onDidResolveChildren(() => {86this._onDidChangeContent.fire();87queueMicrotask(() => {88if (this._focusedElement) {89const position = this._elementPositionMap.get(this._focusedElement.getId());90if (position) {91this._accessibleViewService.setPosition(position, true);92}93}94});95}));96}9798private async _updateContent(elements: IReplElement[]) {99const dataSource = this._replView.getReplDataSource();100if (!dataSource) {101return;102}103let line = 1;104const content: string[] = [];105for (const e of elements) {106content.push(e.toString().replace(/\n/g, ''));107this._elementPositionMap.set(e.getId(), new Position(line, 1));108line++;109if (dataSource.hasChildren(e)) {110const childContent: string[] = [];111const children = await dataSource.getChildren(e);112for (const child of children) {113const id = child.getId();114if (!this._elementPositionMap.has(id)) {115// don't overwrite parent position116this._elementPositionMap.set(id, new Position(line, 1));117}118childContent.push(' ' + child.toString());119line++;120}121content.push(childContent.join('\n'));122}123}124125this._content = content.join('\n');126this._onDidResolveChildren.fire();127}128}129130131