Skip to content

Make selection an exclusive range#18106

Merged
DHowett merged 38 commits intomainfrom
dev/cazamor/sln/exclusive-end
Jan 28, 2025
Merged

Make selection an exclusive range#18106
DHowett merged 38 commits intomainfrom
dev/cazamor/sln/exclusive-end

Conversation

@carlos-zamora
Copy link
Copy Markdown
Member

@carlos-zamora carlos-zamora commented Oct 23, 2024

Summary of the Pull Request

Selection is generally stored as an inclusive start and end. This PR makes the end exclusive which now allows degenerate selections, namely in mark mode. This also modifies mouse selection to round to the nearest cell boundary (see #5099) and improves word boundaries to be a bit more modern and make sense for degenerate selections (similar to #15787).

References and Relevant Issues

Closes #5099
Closes #13447
Closes #17892

Detailed Description of the Pull Request / Additional comments

  • Buffer, Viewport, and Point
    • Introduced a few new functions here to find word boundaries, delimiter class runs, and glyph boundaries.
      • 📝These new functions should be able to replace a few other functions (i.e. GetWordStart --> GetWordStart2). That migration is going to be a part of Refactor Word Expansion in TextBuffer #4423 to reduce the risk of breaking UIA.
    • Viewport: added a few functions to handle navigating the exclusive bounds (namely allowing RightExclusive as a position for buffer coordinates). This is important for selection to be able to highlight the entire line.
      • 📝BottomInclusiveRightExclusive() will replace EndExclusive in the UIA code
    • Point: iterate_rows_exclusive is similar to iterate_rows, except it has handling for RightExclusive
  • Renderer
    • Use iterate_rows_exclusive for proper handling (this actually fixed a lot of our issues)
    • Remove some workarounds in _drawHighlighted (this is a boundary where we got inclusive coords and made them exclusive, but now we don't need that!)
  • Terminal
    • fix selection marker rendering
    • _ConvertToBufferCell(): add a param to allow for RightExclusive or clamp it to RightInclusive (original behavior). Both are useful!
    • Use new GetWordStart2 and GetWordEnd2 to improve word boundaries and make them feel right now that the selection an exclusive range.
    • Convert a few IsInBounds --> IsInExclusiveBounds for safety and correctness
    • Add TriggerSelection to SelectNewRegion
      • 📝 We normally called TriggerSelection in a different layer, but it turns out, UIA's Select function wouldn't actually update the renderer. Whoops! This fixes that.
  • TermControl
  • UIA
    • TermControlUIAProvider::GetSelectionRange no need to convert from inclusive range to exclusive range anymore!
    • TextBuffer::GetPlainText now works on an exclusive range, so no need to convert the range anymore!

Validation Steps Performed

This fundamental change impacts a lot of scenarios:

  • ✅Rendering selections
  • ✅Selection markers
  • ✅Copy text
  • ✅Session restore
  • ✅Mark mode navigation (i.e. character, word, line, buffer)
  • ✅Mouse selection (i.e. click+drag, shift+click, multi-click, alt+click)
  • ✅Hyperlinks (interaction and rendering)
  • ✅Accessibility (i.e. get selection, movement, text extraction, selecting text)
  • Prev/Next Command/Output (untested)
  • ✅Unit tests
    We should definitely bug bash this to get good coverage.

Follow-ups

  • Refactor Word Expansion in TextBuffer #4423
    • Now that selection and UIA are both exclusive ranges, it should be a lot easier to deduplicate code between selection and UIA. We should be able to remove EndExclusive as well when we do that. This'll also be an opportunity to modernize that code and use more til classes.

Comment thread src/buffer/out/textBuffer.cpp Fixed
Comment thread src/buffer/out/textBuffer.cpp Fixed
Comment thread src/buffer/out/textBuffer.cpp Fixed
Comment thread src/buffer/out/textBuffer.cpp Fixed
@microsoft-github-policy-service microsoft-github-policy-service Bot added Issue-Bug It either shouldn't be doing this or needs an investigation. Issue-Task It's a feature request, but it doesn't really need a major design. Area-Accessibility Issues related to accessibility Area-TerminalControl Issues pertaining to the terminal control (input, selection, keybindings, mouse interaction, etc.) Priority-3 A description (P3) Product-Terminal The new Windows Terminal. labels Oct 23, 2024
@github-actions

This comment has been minimized.

Comment thread src/types/UiaTextRangeBase.cpp Outdated
@github-actions

This comment has been minimized.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. label Oct 24, 2024
// it was the last row but the highlight ends outside of our x range.)
// We will resume from here in the next call.
if (!isFinalRow || hiEnd.x /*inclusive*/ >= x2 /*exclusive*/)
if (!isFinalRow || hiEnd.x > x2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW I think we may have made a mistake here due to my poor understanding of this code. Probably needs to be mulled over once more...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, be careful - this is used in TWO places. search highlights and selection rendering. they're both inclusive today. this PR only says it changes one of them.

@carlos-zamora
Copy link
Copy Markdown
Member Author

All the tests are good now. The only one that failed was:

Release.x64.ConPtyTests::DiesOnClose#metadataSet0 Failed

which seems unrelated, so I'm running it again. /azp run

Copy link
Copy Markdown
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

18/30 i have a lot of questions after reading a few tests so i'm checkpointing here

Comment thread src/host/ut_host/TextBufferTests.cpp Outdated
Comment thread src/host/selection.cpp Outdated
Comment thread src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp Outdated
Comment thread src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp Outdated
Comment thread src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp
Comment thread src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp Outdated
Comment thread src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp Outdated
Comment thread src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp Outdated
Comment thread src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp Outdated
@@ -709,7 +709,7 @@ namespace TerminalCoreUnitTests
ValidateLinearSelection(term, { 5, 10 }, { 10, 10 });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why these coordinates did not become exclusive

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Looked into it more. Since we're moving behind the pivot, end is actually inclusive! (which, admittedly, is pretty confusing)

Leaving this thread unresolved in case we want to make any changes because of this

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crazy, but i think i understand

@microsoft-github-policy-service microsoft-github-policy-service Bot added Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something and removed Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something labels Dec 11, 2024
@carlos-zamora
Copy link
Copy Markdown
Member Author

Bug bash 1/21

  • When starting the selection in the right half of a cell and dragging left it'll not select the cell you started in.
  • ...if, on the other hand, you immediately start dragging to the right it'll select both cells.
  • ...one option would be to represent selections in float cell coordinates and use floor(top), ceil(bottom), floor(left+0.5), and ceil(right-0.5) to turn it into integer coordinates, or something.

@carlos-zamora
Copy link
Copy Markdown
Member Author

  • When starting the selection in the right half of a cell and dragging left it'll not select the cell you started in.
  • ...if, on the other hand, you immediately start dragging to the right it'll select both cells.

selection demo

@DHowett (I think you filed the bug?) I wasn't able to get the issue to repro (unless I'm misunderstanding it?). Is there a desired behavior you want instead?

Copy link
Copy Markdown
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30/30, couple open comments

Comment thread src/cascadia/UnitTests_Control/ControlInteractivityTests.cpp Outdated
@@ -709,7 +709,7 @@ namespace TerminalCoreUnitTests
ValidateLinearSelection(term, { 5, 10 }, { 10, 10 });
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

crazy, but i think i understand

Comment thread src/types/viewport.cpp
{
const auto l = static_cast<ptrdiff_t>(_sr.left);
const auto t = static_cast<ptrdiff_t>(_sr.top);
const auto w = static_cast<ptrdiff_t>(std::max(0, _sr.right - _sr.left + 2));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you ever make that change and validate?

Comment thread src/buffer/out/textBuffer.cpp

// Try to move forward, but if we hit the buffer boundary, we fail to move.
const bool success = bufferSize.IncrementInExclusiveBounds(pos);
if (success &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we ever have glyphs more than two cells wide, this can easily become while() (i think?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving this as-is for now. We can change it to while() and fully test it out when that's the case

@microsoft-github-policy-service microsoft-github-policy-service Bot added Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something and removed Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something labels Jan 24, 2025
Copy link
Copy Markdown
Member

@lhecker lhecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd not delay it too much longer and just fix it up in post as needed.

@DHowett DHowett merged commit 64d4fba into main Jan 28, 2025
@DHowett DHowett deleted the dev/cazamor/sln/exclusive-end branch January 28, 2025 22:54
carlos-zamora added a commit that referenced this pull request Jan 31, 2025
## Summary of the Pull Request
Fixes a bug where VT mouse mode would round to the nearest cell when
clicking the mouse button.
The fix is to round to the nearest cell only when we're selecting text.
The other scenarios affected are:
- clicking on a hyperlink
- vt mouse mode
- where the context menu is anchored

Really the most notable ones were the first two. So now, we use the
position of the cell we clicked on. We only round for selection.

## References and Relevant Issues
Follow-up to #18106

## Detailed Description of the Pull Request / Additional comments

## Validation Steps Performed
Opened Midnight Commander in Ubuntu and clicked between the two panes.
- Before: threshold was too early to switch between panes
- After: threshold is clearly separated between the outline of the two
panes

---------

Co-authored-by: Dustin L. Howett <duhowett@microsoft.com>
carlos-zamora added a commit that referenced this pull request Aug 20, 2025
…9259)

## Summary of the Pull Request
Fixes a bug where copying and coloring selected text would be off by
one. This was introduced in #18106 when selection was updated to be
stored as an exclusive range. `Selection::_RegenerateSelectionSpans()`
was updated then, but copying text and coloring selection didn't rely on
selection spans.

Copying text relies on `GetSelectionAnchors()`. This function has now
been updated to increment the bottom-right point of the selection. This
way, `GetTextSpans()` operates on the expected _exclusive_ range.

Coloring selection relies on `TextBuffer::SearchText()`,
`TextBuffer::GetTextRects` and `GetSelectionSpans()`. Both
`Selection::ColorSelection()` were updated to use `rect` over
`inclusive_rect` to emphasize that they are exclusive ranges. Converting
between the two improves clarity and fixes the bug.

## References and Relevant Issues
Introduced in #18106 

## Validation Steps Performed
Copying text works in the following scenarios:
✅ single line, left-to-right and right-to-left
✅ multi-line, diagonal directions
✅ block selection

Coloring text works in the following scenarios:
✅ctrl+# --> color instance
✅ctrl+shift+# --> color all instances

Closes #19053
DHowett pushed a commit that referenced this pull request Mar 17, 2026
Selection in VT mouse mode has been feeling a little weird. I've made a
few changes in this space to improve the overall experience when in vt
mouse mode:
- don't round selection positions: this means that you now highlight the
cell you're on if you're going right, and the adjacent cell if you're
going left
- fix drag-left excluding the current cell
- #9608: shift+click now clears any existing selection instead of
extending it. This feels more intuitive since Shift already works as the
override modifier

Somewhat related to #18106 

## Validation
✅ alt selections feel consistent
✅ selecting in VTMM feels accurate (selects the cell we started on)
✅ creating new selections (aka clearing old selection) in VTMM feels
intuitive (forwards and backwards)

Closes #9608
DHowett pushed a commit that referenced this pull request Apr 3, 2026
Selection in VT mouse mode has been feeling a little weird. I've made a
few changes in this space to improve the overall experience when in vt
mouse mode:
- don't round selection positions: this means that you now highlight the
cell you're on if you're going right, and the adjacent cell if you're
going left
- fix drag-left excluding the current cell
- #9608: shift+click now clears any existing selection instead of
extending it. This feels more intuitive since Shift already works as the
override modifier

Somewhat related to #18106

## Validation
✅ alt selections feel consistent
✅ selecting in VTMM feels accurate (selects the cell we started on)
✅ creating new selections (aka clearing old selection) in VTMM feels
intuitive (forwards and backwards)

Closes #9608

(cherry picked from commit 2e33056)
Service-Card-Id: PVTI_lADOAF3p4s4BQX0-zgna3V8
Service-Version: 1.25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Accessibility Issues related to accessibility Area-TerminalControl Issues pertaining to the terminal control (input, selection, keybindings, mouse interaction, etc.) Issue-Bug It either shouldn't be doing this or needs an investigation. Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Issue-Task It's a feature request, but it doesn't really need a major design. Priority-3 A description (P3) Product-Terminal The new Windows Terminal. zBugBash-Consider

Projects

None yet

5 participants