From fcc6af52851f701514b107161fae6e0d1eea2bc9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 3 Aug 2023 12:00:29 -0700 Subject: [PATCH 1/4] fix #188702 --- extensions/javascript/syntaxes/Readme.md | 1 + .../accessibility/browser/accessibleView.ts | 110 +++++++++++++++++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/extensions/javascript/syntaxes/Readme.md b/extensions/javascript/syntaxes/Readme.md index bc29199fd73..c12505936c8 100644 --- a/extensions/javascript/syntaxes/Readme.md +++ b/extensions/javascript/syntaxes/Readme.md @@ -8,3 +8,4 @@ The script does the following changes: - fileTypes .tsx -> .js & .jsx - scopeName scope.tsx -> scope.js - update all rule names .tsx -> .js + diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 118c12d3f23..ff4ec3f4c3f 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -29,6 +29,9 @@ import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleVi import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; +import { marked } from 'vs/base/common/marked/marked'; const enum DIMENSIONS { @@ -52,6 +55,7 @@ export interface IAccessibleViewService { show(provider: IAccessibleContentProvider): void; next(): void; previous(): void; + goToSymbol(): void; /** * If the setting is enabled, provides the open accessible view hint as a localized string. * @param verbositySettingKey The setting key for the verbosity of the feature @@ -128,15 +132,21 @@ class AccessibleView extends Disposable { })); } - show(provider: IAccessibleContentProvider): void { + show(provider?: IAccessibleContentProvider, symbol?: IAccessibleViewSymbol): void { + if (!provider) { + provider = this._currentProvider; + } + if (!provider) { + return; + } const delegate: IContextViewDelegate = { getAnchor: () => { return { x: (window.innerWidth / 2) - ((Math.min(this._layoutService.dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.offset.quickPickTop }; }, render: (container) => { container.classList.add('accessible-view-container'); - return this._render(provider, container); + return this._render(provider!, container); }, onHide: () => { - if (provider.options.type === AccessibleViewType.Help) { + if (provider!.options.type === AccessibleViewType.Help) { this._accessiblityHelpIsShown.reset(); } else { this._accessibleViewIsShown.reset(); @@ -150,6 +160,9 @@ class AccessibleView extends Disposable { } else { this._accessibleViewIsShown.set(true); } + if (symbol) { + this.showSymbol(this._currentProvider!, symbol); + } this._currentProvider = provider; } @@ -167,6 +180,43 @@ class AccessibleView extends Disposable { this._currentProvider.next?.(); } + goToSymbol(): void { + this._instantiationService.createInstance(AccessibleViewSymbolQuickPick, this).show(this._currentProvider!); + } + + getSymbols(): IAccessibleViewSymbol[] { + const symbols: IAccessibleViewSymbol[] = []; + if (!this._currentProvider) { + return symbols; + } + const tokens = marked.lexer(this._currentProvider.provideContent()); + for (const token of tokens) { + let label: string | undefined = undefined; + switch (token.type) { + case 'heading': + case 'paragraph': + case 'code': + label = token.text; + break; + case 'list': + label = token.items?.map(i => i.text).join(', '); + } + if (label) { + symbols.push({ label, ariaLabel: localize('symbolLabel', "{0} {1}", token.type, label), iconClass: token.type }); + } + } + return symbols; + } + + showSymbol(provider: IAccessibleContentProvider, symbol: IAccessibleViewSymbol): void { + const index = provider.provideContent().split('\n').findIndex(line => line.includes(symbol.label.split('\n')[0])); + if (index >= 0) { + this.show(provider); + this._editorWidget.revealLine(index + 1); + this._editorWidget.setSelection({ startLineNumber: index + 1, startColumn: 1, endLineNumber: index + 1, endColumn: 1 }); + } + } + private _render(provider: IAccessibleContentProvider, container: HTMLElement): IDisposable { this._currentProvider = provider; const settingKey = `accessibility.verbosity.${provider.verbositySettingKey}`; @@ -286,6 +336,9 @@ export class AccessibleViewService extends Disposable implements IAccessibleView previous(): void { this._accessibleView?.previous(); } + goToSymbol(): void { + this._accessibleView?.goToSymbol(); + } getOpenAriaHint(verbositySettingKey: AccessibilityVerbositySettingId): string | null { if (!this._configurationService.getValue(verbositySettingKey)) { return null; @@ -321,6 +374,30 @@ class AccessibleViewNextAction extends Action2 { registerAction2(AccessibleViewNextAction); +class AccessibleViewGoToSymbolAction extends Action2 { + static id: 'editor.action.accessibleViewGoToSymbol'; + constructor() { + super({ + id: 'editor.action.accessibleViewGoToSymbol', + precondition: accessibleViewIsShown, + keybinding: { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyO, + weight: KeybindingWeight.WorkbenchContrib + 10 + }, + menu: [{ + id: MenuId.CommandPalette, + group: '', + order: 1 + }], + title: localize('editor.action.accessibleViewGoToSymbol', "Go To Symbol in Accessible View") + }); + } + run(accessor: ServicesAccessor, ...args: unknown[]): void { + accessor.get(IAccessibleViewService).goToSymbol(); + } +} +registerAction2(AccessibleViewGoToSymbolAction); + class AccessibleViewPreviousAction extends Action2 { static id: 'editor.action.accessibleViewPrevious'; constructor() { @@ -389,3 +466,30 @@ export const AccessibleViewAction = registerCommand(new MultiCommand({ }], })); + +class AccessibleViewSymbolQuickPick { + constructor(private _accessibleView: AccessibleView, @IQuickInputService private readonly _quickInputService: IQuickInputService) { + + } + show(provider: IAccessibleContentProvider): void { + const quickPick = this._quickInputService.createQuickPick(); + const picks = []; + const symbols = this._accessibleView.getSymbols(); + for (const symbol of symbols) { + picks.push({ + label: symbol.label, + ariaLabel: symbol.ariaLabel + }); + } + quickPick.canSelectMany = false; + quickPick.items = symbols; + quickPick.show(); + quickPick.onDidAccept(() => { + quickPick.hide(); + this._accessibleView.showSymbol(provider, quickPick.selectedItems[0]); + }); + } +} + +interface IAccessibleViewSymbol extends IPickerQuickAccessItem { +} From 1ba9ce403d2d5e9cec5c4e9c4aa5767c656f381c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 3 Aug 2023 12:02:54 -0700 Subject: [PATCH 2/4] clean up --- extensions/javascript/syntaxes/Readme.md | 1 - .../contrib/accessibility/browser/accessibleView.ts | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/extensions/javascript/syntaxes/Readme.md b/extensions/javascript/syntaxes/Readme.md index c12505936c8..bc29199fd73 100644 --- a/extensions/javascript/syntaxes/Readme.md +++ b/extensions/javascript/syntaxes/Readme.md @@ -8,4 +8,3 @@ The script does the following changes: - fileTypes .tsx -> .js & .jsx - scopeName scope.tsx -> scope.js - update all rule names .tsx -> .js - diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index ff4ec3f4c3f..123a64fc0d6 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -200,6 +200,7 @@ class AccessibleView extends Disposable { break; case 'list': label = token.items?.map(i => i.text).join(', '); + break; } if (label) { symbols.push({ label, ariaLabel: localize('symbolLabel', "{0} {1}", token.type, label), iconClass: token.type }); @@ -209,8 +210,8 @@ class AccessibleView extends Disposable { } showSymbol(provider: IAccessibleContentProvider, symbol: IAccessibleViewSymbol): void { - const index = provider.provideContent().split('\n').findIndex(line => line.includes(symbol.label.split('\n')[0])); - if (index >= 0) { + const index = provider.provideContent().split('\n').findIndex(line => line.includes(symbol.label.split('\n')[0])) ?? -1; + if (index) { this.show(provider); this._editorWidget.revealLine(index + 1); this._editorWidget.setSelection({ startLineNumber: index + 1, startColumn: 1, endLineNumber: index + 1, endColumn: 1 }); @@ -472,6 +473,9 @@ class AccessibleViewSymbolQuickPick { } show(provider: IAccessibleContentProvider): void { + if (provider.options.language !== 'markdown') { + return; + } const quickPick = this._quickInputService.createQuickPick(); const picks = []; const symbols = this._accessibleView.getSymbols(); From c029b48b27854879aced6bd4bc2bcb862db4d2b3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 3 Aug 2023 12:31:20 -0700 Subject: [PATCH 3/4] allow provider to provide symbols --- .../accessibility/browser/accessibleView.ts | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 123a64fc0d6..9cd0da71221 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -45,6 +45,10 @@ export interface IAccessibleContentProvider { onKeyDown?(e: IKeyboardEvent): void; previous?(): void; next?(): void; + /** + * When the language is markdown, this is provided by default. + */ + getSymbols?(): IAccessibleViewSymbol[]; options: IAccessibleViewOptions; } @@ -184,34 +188,41 @@ class AccessibleView extends Disposable { this._instantiationService.createInstance(AccessibleViewSymbolQuickPick, this).show(this._currentProvider!); } - getSymbols(): IAccessibleViewSymbol[] { - const symbols: IAccessibleViewSymbol[] = []; + getSymbols(): IAccessibleViewSymbol[] | undefined { if (!this._currentProvider) { - return symbols; + return; } - const tokens = marked.lexer(this._currentProvider.provideContent()); + const tokens = this._currentProvider.options.language && this._currentProvider.options.language !== 'markdown' ? this._currentProvider.getSymbols?.() : marked.lexer(this._currentProvider.provideContent()); + if (!tokens) { + return; + } + const symbols: IAccessibleViewSymbol[] = []; for (const token of tokens) { let label: string | undefined = undefined; - switch (token.type) { - case 'heading': - case 'paragraph': - case 'code': - label = token.text; - break; - case 'list': - label = token.items?.map(i => i.text).join(', '); - break; + if ('type' in token) { + switch (token.type) { + case 'heading': + case 'paragraph': + case 'code': + label = token.text; + break; + case 'list': + label = token.items?.map(i => i.text).join(', '); + break; + } + } else { + label = token.label; } if (label) { - symbols.push({ label, ariaLabel: localize('symbolLabel', "{0} {1}", token.type, label), iconClass: token.type }); + symbols.push({ info: label, label: localize('symbolLabel', "({0}) {1}", token.type, label), ariaLabel: localize('symbolLabelAria', "({0}) {1}", token.type, label) }); } } return symbols; } showSymbol(provider: IAccessibleContentProvider, symbol: IAccessibleViewSymbol): void { - const index = provider.provideContent().split('\n').findIndex(line => line.includes(symbol.label.split('\n')[0])) ?? -1; - if (index) { + const index = provider.provideContent().split('\n').findIndex(line => line.includes(symbol.info.split('\n')[0])) ?? -1; + if (index >= 0) { this.show(provider); this._editorWidget.revealLine(index + 1); this._editorWidget.setSelection({ startLineNumber: index + 1, startColumn: 1, endLineNumber: index + 1, endColumn: 1 }); @@ -473,12 +484,12 @@ class AccessibleViewSymbolQuickPick { } show(provider: IAccessibleContentProvider): void { - if (provider.options.language !== 'markdown') { - return; - } const quickPick = this._quickInputService.createQuickPick(); const picks = []; const symbols = this._accessibleView.getSymbols(); + if (!symbols) { + return; + } for (const symbol of symbols) { picks.push({ label: symbol.label, @@ -489,11 +500,11 @@ class AccessibleViewSymbolQuickPick { quickPick.items = symbols; quickPick.show(); quickPick.onDidAccept(() => { - quickPick.hide(); this._accessibleView.showSymbol(provider, quickPick.selectedItems[0]); }); } } interface IAccessibleViewSymbol extends IPickerQuickAccessItem { + info: string; } From a1f77aa87bfe760dbcaef480f9dd39131e8244ed Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 3 Aug 2023 12:32:44 -0700 Subject: [PATCH 4/4] clean up --- .../contrib/accessibility/browser/accessibleView.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 9cd0da71221..976563aaafa 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -33,7 +33,6 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { marked } from 'vs/base/common/marked/marked'; - const enum DIMENSIONS { MAX_WIDTH = 600 } @@ -164,8 +163,8 @@ class AccessibleView extends Disposable { } else { this._accessibleViewIsShown.set(true); } - if (symbol) { - this.showSymbol(this._currentProvider!, symbol); + if (symbol && this._currentProvider) { + this.showSymbol(this._currentProvider, symbol); } this._currentProvider = provider; }