Skip to content

Commit 28e32d1

Browse files
committed
Add commands for terminal navigation mode
Part of #76050
1 parent 55be0a9 commit 28e32d1

File tree

9 files changed

+207
-12
lines changed

9 files changed

+207
-12
lines changed

src/vs/editor/standalone/browser/standaloneServices.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,6 @@ export module StaticServices {
140140

141141
export const notificationService = define(INotificationService, () => new SimpleNotificationService());
142142

143-
export const accessibilityService = define(IAccessibilityService, () => new BrowserAccessibilityService());
144-
145143
export const markerService = define(IMarkerService, () => new MarkerService());
146144

147145
export const modeService = define(IModeService, (o) => new ModeServiceImpl());
@@ -194,6 +192,8 @@ export class DynamicStandaloneServices extends Disposable {
194192

195193
let contextKeyService = ensure(IContextKeyService, () => this._register(new ContextKeyService(configurationService)));
196194

195+
ensure(IAccessibilityService, () => new BrowserAccessibilityService(contextKeyService, configurationService));
196+
197197
ensure(IListService, () => new ListService(contextKeyService));
198198

199199
let commandService = ensure(ICommandService, () => new StandaloneCommandService(this._instantiationService));

src/vs/platform/accessibility/common/accessibility.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
77
import { Event } from 'vs/base/common/event';
8+
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
89

910
export const IAccessibilityService = createDecorator<IAccessibilityService>('accessibilityService');
1011

@@ -27,4 +28,6 @@ export const enum AccessibilitySupport {
2728
Disabled = 1,
2829

2930
Enabled = 2
30-
}
31+
}
32+
33+
export const CONTEXT_ACCESSIBILITY_MODE_ENABLED = new RawContextKey<boolean>('accessibilityModeEnabled', false);

src/vs/platform/accessibility/common/accessibilityService.ts

+25-1
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,34 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Emitter, Event } from 'vs/base/common/event';
7-
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
7+
import { IAccessibilityService, AccessibilitySupport, CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility';
88
import { Disposable } from 'vs/base/common/lifecycle';
9+
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
10+
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
911

1012
export class BrowserAccessibilityService extends Disposable implements IAccessibilityService {
1113

1214
_serviceBrand: any;
1315

1416
private _accessibilitySupport = AccessibilitySupport.Unknown;
17+
private _accessibilityModeEnabledContext: IContextKey<boolean>;
1518
private readonly _onDidChangeAccessibilitySupport = new Emitter<void>();
1619
readonly onDidChangeAccessibilitySupport: Event<void> = this._onDidChangeAccessibilitySupport.event;
1720

21+
constructor(
22+
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
23+
@IConfigurationService private readonly _configurationService: IConfigurationService,
24+
) {
25+
super();
26+
this._accessibilityModeEnabledContext = CONTEXT_ACCESSIBILITY_MODE_ENABLED.bindTo(this._contextKeyService);
27+
this._register(this._configurationService.onDidChangeConfiguration(e => {
28+
if (e.affectsConfiguration('editor.accessibilitySupport')) {
29+
this._updateContextKey();
30+
}
31+
}));
32+
this._updateContextKey();
33+
}
34+
1835
alwaysUnderlineAccessKeys(): Promise<boolean> {
1936
return Promise.resolve(false);
2037
}
@@ -26,9 +43,16 @@ export class BrowserAccessibilityService extends Disposable implements IAccessib
2643

2744
this._accessibilitySupport = accessibilitySupport;
2845
this._onDidChangeAccessibilitySupport.fire();
46+
this._updateContextKey();
2947
}
3048

3149
getAccessibilitySupport(): AccessibilitySupport {
3250
return this._accessibilitySupport;
3351
}
52+
53+
private _updateContextKey(): void {
54+
const detected = this.getAccessibilitySupport() === AccessibilitySupport.Enabled;
55+
const config = this._configurationService.getValue('editor.accessibilitySupport');
56+
this._accessibilityModeEnabledContext.set(config === 'on' || (config === 'auto' && detected));
57+
}
3458
}

src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import * as panel from 'vs/workbench/browser/panel';
2020
import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen';
2121
import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
2222
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
23-
import { AllowWorkspaceShellTerminalCommand, ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, DisallowWorkspaceShellTerminalCommand, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions';
23+
import { AllowWorkspaceShellTerminalCommand, ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, DisallowWorkspaceShellTerminalCommand, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, FocusPreviousA11yLineTerminalAction, FocusNextA11yLineTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions';
2424
import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel';
2525
import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen';
26-
import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, ITerminalService, TERMINAL_ACTION_CATEGORY } from 'vs/workbench/contrib/terminal/common/terminal';
26+
import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, ITerminalService, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal';
2727
import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
2828
import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands';
2929
import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu';
@@ -33,6 +33,7 @@ import { DEFAULT_COMMANDS_TO_SKIP_SHELL } from 'vs/workbench/contrib/terminal/br
3333
import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService';
3434
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
3535
import { registerShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalShellConfig';
36+
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility';
3637

3738
registerSingleton(ITerminalService, TerminalService, true);
3839

@@ -459,6 +460,21 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextComm
459460
primary: 0,
460461
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow }
461462
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category);
463+
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveTerminalAction, FocusActiveTerminalAction.ID, FocusActiveTerminalAction.LABEL, {
464+
primary: KeyCode.Escape
465+
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Exit Navigation Mode', category);
466+
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousA11yLineTerminalAction, FocusPreviousA11yLineTerminalAction.ID, FocusPreviousA11yLineTerminalAction.LABEL, {
467+
primary: KeyMod.CtrlCmd | KeyCode.UpArrow
468+
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Previous Line (Navigation Mode)', category);
469+
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousA11yLineTerminalAction, FocusPreviousA11yLineTerminalAction.ID, FocusPreviousA11yLineTerminalAction.LABEL, {
470+
primary: KeyMod.CtrlCmd | KeyCode.UpArrow
471+
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Previous Line (Navigation Mode)', category);
472+
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextA11yLineTerminalAction, FocusNextA11yLineTerminalAction.ID, FocusNextA11yLineTerminalAction.LABEL, {
473+
primary: KeyMod.CtrlCmd | KeyCode.DownArrow
474+
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Next Line (Navigation Mode)', category);
475+
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextA11yLineTerminalAction, FocusNextA11yLineTerminalAction.ID, FocusNextA11yLineTerminalAction.LABEL, {
476+
primary: KeyMod.CtrlCmd | KeyCode.DownArrow
477+
}, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, CONTEXT_ACCESSIBILITY_MODE_ENABLED)), 'Terminal: Focus Next Line (Navigation Mode)', category);
462478
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category);
463479
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category);
464480
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category);

src/vs/workbench/contrib/terminal/browser/terminalActions.ts

+42
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,48 @@ export class ScrollToTopTerminalAction extends Action {
886886
}
887887
}
888888

889+
export class FocusPreviousA11yLineTerminalAction extends Action {
890+
891+
public static readonly ID = TERMINAL_COMMAND_ID.FOCUS_PREVIOUS_A11Y_LINE;
892+
public static readonly LABEL = nls.localize('workbench.action.terminal.focusPreviousA11yLine', "Focus Previous Line (Accessibility Mode)");
893+
894+
constructor(
895+
id: string, label: string,
896+
@ITerminalService private readonly terminalService: ITerminalService
897+
) {
898+
super(id, label);
899+
}
900+
901+
public run(event?: any): Promise<any> {
902+
const terminalInstance = this.terminalService.getActiveInstance();
903+
if (terminalInstance) {
904+
terminalInstance.focusPreviousA11yLine();
905+
}
906+
return Promise.resolve(undefined);
907+
}
908+
}
909+
910+
export class FocusNextA11yLineTerminalAction extends Action {
911+
912+
public static readonly ID = TERMINAL_COMMAND_ID.FOCUS_NEXT_A11Y_LINE;
913+
public static readonly LABEL = nls.localize('workbench.action.terminal.focusNextA11yLine', "Focus Next Line (Accessibility Mode)");
914+
915+
constructor(
916+
id: string, label: string,
917+
@ITerminalService private readonly terminalService: ITerminalService
918+
) {
919+
super(id, label);
920+
}
921+
922+
public run(event?: any): Promise<any> {
923+
const terminalInstance = this.terminalService.getActiveInstance();
924+
if (terminalInstance) {
925+
terminalInstance.focusNextA11yLine();
926+
}
927+
return Promise.resolve(undefined);
928+
}
929+
}
930+
889931
export class ClearTerminalAction extends Action {
890932

891933
public static readonly ID = TERMINAL_COMMAND_ID.CLEAR;

src/vs/workbench/contrib/terminal/browser/terminalInstance.ts

+83-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderB
2525
import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
2626
import { PANEL_BACKGROUND } from 'vs/workbench/common/theme';
2727
import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/terminalWidgetManager';
28-
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE } from 'vs/workbench/contrib/terminal/common/terminal';
28+
import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID, IWindowsShellHelper, SHELL_PATH_INVALID_EXIT_CODE, SHELL_PATH_DIRECTORY_EXIT_CODE, SHELL_CWD_INVALID_EXIT_CODE, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS } from 'vs/workbench/contrib/terminal/common/terminal';
2929
import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry';
3030
import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminalCommands';
3131
import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper';
@@ -57,8 +57,10 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [
5757
TERMINAL_COMMAND_ID.TOGGLE_FIND_REGEX_TERMINAL_FOCUS,
5858
TERMINAL_COMMAND_ID.TOGGLE_FIND_WHOLE_WORD_TERMINAL_FOCUS,
5959
TERMINAL_COMMAND_ID.TOGGLE_FIND_CASE_SENSITIVE_TERMINAL_FOCUS,
60+
TERMINAL_COMMAND_ID.FOCUS_NEXT_A11Y_LINE,
6061
TERMINAL_COMMAND_ID.FOCUS_NEXT_PANE,
6162
TERMINAL_COMMAND_ID.FOCUS_NEXT,
63+
TERMINAL_COMMAND_ID.FOCUS_PREVIOUS_A11Y_LINE,
6264
TERMINAL_COMMAND_ID.FOCUS_PREVIOUS_PANE,
6365
TERMINAL_COMMAND_ID.FOCUS_PREVIOUS,
6466
TERMINAL_COMMAND_ID.FOCUS,
@@ -172,6 +174,7 @@ export class TerminalInstance implements ITerminalInstance {
172174
private _xtermSearch: SearchAddon | undefined;
173175
private _xtermElement: HTMLDivElement;
174176
private _terminalHasTextContextKey: IContextKey<boolean>;
177+
private _terminalA11yTreeFocusContextKey: IContextKey<boolean>;
175178
private _cols: number;
176179
private _rows: number;
177180
private _dimensionsOverride: ITerminalDimensions | undefined;
@@ -270,6 +273,7 @@ export class TerminalInstance implements ITerminalInstance {
270273
});
271274

272275
this._terminalHasTextContextKey = KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED.bindTo(this._contextKeyService);
276+
this._terminalA11yTreeFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS.bindTo(this._contextKeyService);
273277
this.disableLayout = false;
274278

275279
this._logService.trace(`terminalInstance#ctor (id: ${this.id})`, this._shellLaunchConfig);
@@ -907,6 +911,84 @@ export class TerminalInstance implements ITerminalInstance {
907911
}
908912
}
909913

914+
public focusPreviousA11yLine(): void {
915+
// Focus previous row if a row is already focused
916+
if (document.activeElement && document.activeElement.parentElement && document.activeElement.parentElement.classList.contains('xterm-accessibility-tree')) {
917+
const element = <HTMLElement | null>document.activeElement.previousElementSibling;
918+
if (element) {
919+
element.focus();
920+
const disposable = dom.addDisposableListener(element, 'blur', () => {
921+
this._terminalA11yTreeFocusContextKey.set(false);
922+
disposable.dispose();
923+
});
924+
this._terminalA11yTreeFocusContextKey.set(true);
925+
}
926+
return;
927+
}
928+
929+
// Ensure a11y tree exists
930+
const treeContainer = this._xterm.element.querySelector('.xterm-accessibility-tree');
931+
if (!treeContainer) {
932+
return;
933+
}
934+
935+
// Target is row before the cursor
936+
const targetRow = Math.max(this._xterm.buffer.cursorY - 1, 0);
937+
938+
// Check bounds
939+
if (treeContainer.childElementCount < targetRow) {
940+
return;
941+
}
942+
943+
// Focus
944+
const element = <HTMLElement>treeContainer.childNodes.item(targetRow);
945+
element.focus();
946+
const disposable = dom.addDisposableListener(element, 'blur', () => {
947+
this._terminalA11yTreeFocusContextKey.set(false);
948+
disposable.dispose();
949+
});
950+
this._terminalA11yTreeFocusContextKey.set(true);
951+
}
952+
953+
public focusNextA11yLine(): void {
954+
// Focus previous row if a row is already focused
955+
if (document.activeElement && document.activeElement.parentElement && document.activeElement.parentElement.classList.contains('xterm-accessibility-tree')) {
956+
const element = <HTMLElement | null>document.activeElement.nextElementSibling;
957+
if (element) {
958+
element.focus();
959+
const disposable = dom.addDisposableListener(element, 'blur', () => {
960+
this._terminalA11yTreeFocusContextKey.set(false);
961+
disposable.dispose();
962+
});
963+
this._terminalA11yTreeFocusContextKey.set(true);
964+
}
965+
return;
966+
}
967+
968+
// Ensure a11y tree exists
969+
const treeContainer = this._xterm.element.querySelector('.xterm-accessibility-tree');
970+
if (!treeContainer) {
971+
return;
972+
}
973+
974+
// Target is cursor row
975+
const targetRow = this._xterm.buffer.cursorY;
976+
977+
// Check bounds
978+
if (treeContainer.childElementCount < targetRow) {
979+
return;
980+
}
981+
982+
// Focus row before cursor
983+
const element = <HTMLElement>treeContainer.childNodes.item(targetRow);
984+
element.focus();
985+
const disposable = dom.addDisposableListener(element, 'blur', () => {
986+
this._terminalA11yTreeFocusContextKey.set(false);
987+
disposable.dispose();
988+
});
989+
this._terminalA11yTreeFocusContextKey.set(true);
990+
}
991+
910992
public scrollDownLine(): void {
911993
this._xterm.scrollLines(1);
912994
}
@@ -938,7 +1020,6 @@ export class TerminalInstance implements ITerminalInstance {
9381020
private _refreshSelectionContextKey() {
9391021
const activePanel = this._panelService.getActivePanel();
9401022
const isActive = !!activePanel && activePanel.getId() === TERMINAL_PANEL_ID;
941-
9421023
this._terminalHasTextContextKey.set(isActive && this.hasSelection());
9431024
}
9441025

0 commit comments

Comments
 (0)