[PR #4018] Refactor UiaTextRange For Improved Navigation and Reliability #25603

Open
opened 2026-01-31 09:10:33 +00:00 by claunia · 0 comments
Owner

Original Pull Request: https://github.com/microsoft/terminal/pull/4018

State: closed
Merged: Yes


Summary of the Pull Request

This pull request is intended to achieve the following goals...

  1. reduce duplicate code
  2. remove static functions
  3. improve readability
  4. improve reliability
  5. improve code-coverage for testing
  6. establish functioning text buffer navigation in Narrator and NVDA

This also required a change to the wrapper class XamlUiaTextRange that has been causing issues with Narrator and NVDA.

See below for additional context.

References

#3976 - I believe this might have been a result of improperly handling degenerate ranges. Fixed here.
#3895 - reduced the duplicate code. No need to separate into different files
#2160 - same as #3976 above
#1993 - I think just about everything is no longer static

PR Checklist

Detailed Description of the Pull Request / Additional comments

UiaTextRange

  • converted endpoints into the COORD system in the TextBuffer coordinate space
  • start is inclusive, end is exclusive. A degenerate range is when start == end.
  • all functions are no longer static
  • MoveByUnit() functions now rely on MoveEndpointByUnit() functions
  • removed unnecessary typedefs like Endpoint, ScreenInfoRow, etc..
  • relied more heavily on existing functionality from TextBuffer and Viewport

XamlUiaTextRange

  • GetAttributeValue() must return a special HRESULT that signifies that the requested attribute is not supported. This was the cause of a number of inconsistencies between Narrator and NVDA.
  • FindText() should return nullptr if nothing was found. #4373 properly fixes this functionality now that Search is a shared module

TextBuffer

  • Word navigation functionality is entirely in TextBuffer for proper abstraction
  • a total of 6 functions are now dedicated to word navigation to get a good understanding of the differences between a "word" in Accessibility and a "word" in selection

As an example, consider a buffer with this text in it:
" word other "
In selection, a "word" is defined as the range between two delimiters, so the words in the example include [" ", "word", " ", "other", " "].
In accessibility , a "word" includes the delimiters after a range of readable characters, so the words in the example include ["word ", "other "].

Additionally, accessibility word navigation must be able to detect if it is on the first or last word. This resulted in a slight variant of word navigation functions that return a boolean instead of a COORD.

Ideally, these functions can be consolidated, but that is too risky for a PR of this size as it can have an effect on selection.

Viewport

  • the concept of EndExclusive is added. This is used by UiaTextRange's end anchor as it is exclusive. To signify that the last character in the buffer is included in this buffer, end must be one past the end of the buffer. This is EndExclusive
  • Since many functions check if the given COORD is in bounds, a flag must be set to allow EndExclusive as a valid COORD that is in bounds.

Testing

  • word navigation testing relies more heavily on TextBuffer tests
  • additional testing was created for non-movement focused functions of UiaTextRange
  • The results have been compared to Microsoft Word and some have been verified by UiAutomation/Narrator contacts as expected results.

Validation Steps Performed

Tests pass
Narrator works
NVDA works

**Original Pull Request:** https://github.com/microsoft/terminal/pull/4018 **State:** closed **Merged:** Yes --- ## Summary of the Pull Request This pull request is intended to achieve the following goals... 1) reduce duplicate code 2) remove static functions 3) improve readability 4) improve reliability 5) improve code-coverage for testing 6) establish functioning text buffer navigation in Narrator and NVDA This also required a change to the wrapper class `XamlUiaTextRange` that has been causing issues with Narrator and NVDA. See below for additional context. ## References #3976 - I believe this might have been a result of improperly handling degenerate ranges. Fixed here. #3895 - reduced the duplicate code. No need to separate into different files #2160 - same as #3976 above #1993 - I think just about everything is no longer static ## PR Checklist * [x] Closes #3895, Closes #1993, Closes #3976, Closes #2160 * [x] CLA signed * [x] Tests added/passed ## Detailed Description of the Pull Request / Additional comments ### UiaTextRange - converted endpoints into the COORD system in the TextBuffer coordinate space - `start` is inclusive, `end` is exclusive. A degenerate range is when start == end. - all functions are no longer static - `MoveByUnit()` functions now rely on `MoveEndpointByUnit()` functions - removed unnecessary typedefs like `Endpoint`, `ScreenInfoRow`, etc.. - relied more heavily on existing functionality from `TextBuffer` and `Viewport` ### XamlUiaTextRange - `GetAttributeValue()` must return a special HRESULT that signifies that the requested attribute is not supported. This was the cause of a number of inconsistencies between Narrator and NVDA. - `FindText()` should return `nullptr` if nothing was found. #4373 properly fixes this functionality now that Search is a shared module ### TextBuffer - Word navigation functionality is entirely in `TextBuffer` for proper abstraction - a total of 6 functions are now dedicated to word navigation to get a good understanding of the differences between a "word" in Accessibility and a "word" in selection As an example, consider a buffer with this text in it: " word other " In selection, a "word" is defined as the range between two delimiters, so the words in the example include [" ", "word", " ", "other", " "]. In accessibility , a "word" includes the delimiters after a range of readable characters, so the words in the example include ["word ", "other "]. Additionally, accessibility word navigation must be able to detect if it is on the first or last word. This resulted in a slight variant of word navigation functions that return a boolean instead of a COORD. Ideally, these functions can be consolidated, but that is too risky for a PR of this size as it can have an effect on selection. ### Viewport - the concept of `EndExclusive` is added. This is used by UiaTextRange's `end` anchor as it is exclusive. To signify that the last character in the buffer is included in this buffer, `end` must be one past the end of the buffer. This is `EndExclusive` - Since many functions check if the given `COORD` is in bounds, a flag must be set to allow `EndExclusive` as a valid `COORD` that is in bounds. ### Testing - word navigation testing relies more heavily on TextBuffer tests - additional testing was created for non-movement focused functions of UiaTextRange - The results have been compared to Microsoft Word and some have been verified by UiAutomation/Narrator contacts as expected results. ## Validation Steps Performed Tests pass Narrator works NVDA works
claunia added the pull-request label 2026-01-31 09:10:33 +00:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#25603