-
Notifications
You must be signed in to change notification settings - Fork 315
Add screen reader support #4854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
8b8a080
to
9ba544b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces prototype screen reader support by implementing a SafeRender
abstraction that uses ANSI control codes when screen reader mode is enabled. The changes modify rendering behavior throughout the application to provide better accessibility for screen reader users.
Key changes:
- Introduces
SafeRender
method that outputs ANSI escape sequences instead of full re-rendering when screen reader support is enabled - Adds screen reader detection for Windows (including Narrator) and macOS VoiceOver
- Replaces many
Render()
calls withSafeRender()
calls across editing operations, undo/redo, history, and completion functionality
Reviewed Changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.
Show a summary per file
File | Description |
---|---|
PSReadLine/Accessibility.cs | Enhanced screen reader detection for Windows and macOS platforms |
PSReadLine/Cmdlets.cs | Added ScreenReader option with automatic detection as default |
PSReadLine/Options.cs | Added logic to disable predictions when screen reader is enabled |
PSReadLine/Render.cs | Implemented SafeRender method using ANSI control codes |
PSReadLine/UndoRedo.cs | Replaced Render calls with SafeRender for undo/redo operations |
PSReadLine/PublicAPI.cs | Updated Insert and Replace methods to use SafeRender |
PSReadLine/BasicEditing.cs | Modified editing operations to use SafeRender with appropriate ANSI codes |
PSReadLine/History.cs | Updated history navigation and search to use SafeRender |
PSReadLine/KillYank.cs | Modified kill operations to use SafeRender |
PSReadLine/Completion.cs | Added TODO for menu completion screen reader testing |
PSReadLine/KeyBindings.cs | Added TODO for WhatIsKey screen reader evaluation |
PSReadLine/ReadLine.cs | Added TODOs for screen reader evaluation in various render calls |
PSReadLine/PlatformWindows.cs | Added IsMutexPresent method for detecting Windows Narrator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review in progress. Need to stop here today and want to share my comments so far.
0f93014
to
9090264
Compare
9090264
to
14fc559
Compare
f606bf4
to
02914fe
Compare
When this is merged, please rebase or merge, not squash 🙇 |
@andyleejordan Why not squash? The commit history is not that clean (has changes reverted) and also this repo always do squash merge (like in PowerShell repo). |
Because one single commit is going to be way too big, and the commits are clean, where reverts/changes logically follow new information. (Such as testing the DLL-loaded approach for screen readers.) |
ee18645
to
fb7d377
Compare
@andyleejordan can you please simply push new changes instead of force pushing all commits? It makes it hard for me to continue the view as I cannot view the diffs between a commit that I have reviewed and the subsequent new changes.
If the cost of keeping the commit history clean is to always force push all commits, then I'm afraid I disagree this is the way to go :) Since the PR already keeps all information, I think a squash merge should be fine. |
This character was always meant to be an ellipsis. I'm unsure exactly why Visual Studio interprets it so, but VS Code and GitHub do not. This commit replaces it with the actual Unicode character which ensures the tests continue to pass when these files are edited and saved in VS Code.
@daxian-dbw in order to facilitate getting this PR in with your requirement to squash PRs, this is now dependent on the separate but necessary PRs:
I finished up and tested your last requests above, and will rebase on master per this project's documentation after those dependent PRs are merged. Leaving this as a draft so it doesn't accidentally get merged before then. Thanks for your help! |
This supports checking for the built-in screen readers VoiceOver on macOS and Windows Narrator, as well as the popular open-source option, NVDA. The VoiceOver check spawns a quick `defaults` process since in .NET using the macOS events is difficult, but this is quick and easy. The Windows Narrator check inspects a system mutex. Notably though this screen reader handles re-rendering better than others. The check for NVDA et. al. inspects the system parameter information. While this approach is known to be buggy, the preferable and commonly used algorithm (as implemented by Electron) which checks for loaded libraries was tested and found to be unsupported for a non-windowed program like PowerShell. It's unknown if the SPI check will detect JAWS, Window-Eyes, or ZoomText, so a command-line option for the upcoming screen reader mode should also be provided. Linux is not yet supported.
These tests were using a hard-coded continuation prompt (and its length) which blocks tests for the upcoming screen reader mode. This is also simply a reasonable refactor, thanks Claude (for trying).
fb7d377
to
2439ec5
Compare
This adds the `EnableScreenReaderMode` command-line switch which defaults to true if an active screen reader is detected. In screen reader mode, the existing `ForceRender()` function is replaced by `RenderForScreenReader()` which uses a differential rendering approach to minimize extraneous output to the terminal, allowing the use of screen readers better than ever before. The differential rendering relies on calculating the common prefix of the `buffer` and `previousRender` strings. Nearly all necessary changes are consolidated in the new rendering function. Features known not to be supported: * Colors: as this necessitates redrawing to insert color sequences after the input is received and the AST parsed. * Inline predictions: as this by definition changes the suffix and thus requires endless redrawing. * List view predictions: since the render implementation never calls into the prediction engine, this is not available either. * Menu completion: well, it "works" since it's not disabled and does its own rendering, but no effort has been made to improve `DrawMenu()`, so it's not accessible (and I'm not sure it could be given our current constraints). Features known to be partially supported: * Text selection: mark and select commands work as intended, but provide no visual indication. * Multiple lines: as in newlines work fine, but there is no continuation prompt. * Visually wrapped lines: editing above a wrapped line redraws all subsequent lines and hence is noisy. * Status prompt based commands: what-is-key, digit-argument, and most notably, forward/backward incremental history search all render a "status prompt" on the line below the user's input buffer. This _is_ supported; however, it can be noisy since it necessarily has to render the whole buffer when the input buffer changes, including the status prompt (and search text). But what it's reading is almost always going to be relevant. Everything else should generally work, even Vi mode, and the tests pass. That said, this isn't perfect, and moreover the approach specifically doesn't attempt to enable things from the ground up. There may be features that are available but turn out not to be accessible (like `MenuComplete`) and I believe they should be left as-is. Specifically tested with NVDA on Windows and VoiceOver on macOS within VS Code's integrated terminal, with shell integration loaded, and Code's screen reader optimizations enabled.
Tests that depend on supported features such as menu completions, list view, inline predictions, continuation prompt, and colors are skipped. The "helper" module for running tests from the command-line and in AppVeyor is minimally updated to not skip the new fixture. A `BlankRestOfBuffer()` function implements the semantics of the `0J` control sequence in the mock console.
2439ec5
to
06e3b4b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
@andyleejordan Regarding PR reviews:
- It's most efficient to only push new changes and squash in the end, because for each iteration
- the author can just focus on addressing feedback.
- the reviewers can just focus on the new diffs.
- Commit history is preserved in the PR, along with all discussions.
- This is how we do PR reviews in this repo, in PowerShell repo, and most repos out there I believe.
However, since you've been insisting on it so much, I will merge this PR with a merge node as you expect.
@@ -210,7 +210,14 @@ function Start-TestRun | |||
|
|||
function RunXunitTestsInNewProcess ([string] $Layout, [string] $OperatingSystem) | |||
{ | |||
$filter = "FullyQualifiedName~Test.{0}_{1}" -f ($Layout -replace '-','_'), $OperatingSystem | |||
$filter = if ($Layout) { | |||
Write-Log "Testing $Layout on $OperatingSystem...`n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This cleaner. Thanks!
Thanks Dongbo! |
A new render implementation for use under screen readers that avoids redrawing the terminal buffer so as to avoid unnecessary and confusing output.
The differential rendering relies on calculating the common prefix of the
buffer
andpreviousRender
strings. Nearly all necessary changes are consolidated in the new rendering function.Features known not to be supported:
DrawMenu()
, so it's not accessible (and I'm not sure it could be given our current constraints).Features known to be partially supported:
Everything else should generally work, even Vi mode, and the tests pass. That said, this isn't perfect, and moreover the approach specifically doesn't attempt to enable things from the ground up. There may be features that are available but turn out not to be accessible (like
MenuComplete
) and I believe they should be left as-is.Specifically tested with NVDA on Windows and VoiceOver on macOS within VS Code's integrated terminal, with shell integration loaded, and Code's screen reader optimizations enabled.
Is a start to resolving #2504.