Skip to content
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

Make selection an exclusive range #18106

Merged
merged 38 commits into from
Jan 28, 2025
Merged

Make selection an exclusive range #18106

merged 38 commits into from
Jan 28, 2025

Conversation

carlos-zamora
Copy link
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.

@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

This comment has been minimized.

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
_fillColorBitmap(row, x1, end, fgColor, bgColor);

// Return early if we couldn't paint the whole region (either this was not the last row, or
// 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
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
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.

@DHowett
Copy link
Member

DHowett commented Dec 2, 2024

FYI - the tests failed on all 3 platforms, which indicates a real produce code failure!

Comment on lines +310 to +312
// To remedy this, decrement the end's position by 1.
// However, only do this when expanding right (it's correct
// as is when expanding left).
Copy link
Member

Choose a reason for hiding this comment

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

I'm not 100% sure if I understand this code, but to me it feels like the calling code should contain the fix for this and not this function. Is this perhaps just a case of floor VS. ceil in the calling code?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I'm not happy about this part either. Let me explain what's going on better. The code flow is:

  • ControlInteractivity::SetEndSelectionPoint() (link) - called on mouse drag
    • ControlInteractivity::_getTerminalPosition() (link): round to the nearest cell, that way, we have to drag past a cell to "select" it
  • ControlCore::SetEndSelectionPoint() (link): really just clamps the position to the viewport bounds
  • Terminal::SetSelectionEnd() (link) --> Terminal::_SetSelectionEnd() (link):
    • This is the important part of the code, really. It's where we update our selection state for shift+click, pivoting, and multi-clicks (+ dragging).
    • The issue really only arises when we're multi-clicking+dragging (think "double-click" then drag, as it should expand by word as we drag). So the issue has to do with the expansion part. Anything before that in this function wouldn't make sense to alter, imo.
  • Terminal::_ExpandSelectionAnchors() (link): this is where we actually expand our selection endpoints if we're multi-clicking.

Thankfully, _ExpandSelectionAnchors() is only used in this spot. I'm not sure where else the change would make sense to go though. Open to suggestions.

@carlos-zamora
Copy link
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
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

@@ -709,7 +709,7 @@ namespace TerminalCoreUnitTests
ValidateLinearSelection(term, { 5, 10 }, { 10, 10 });
Copy link
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
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
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
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
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
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

@@ -709,7 +709,7 @@ namespace TerminalCoreUnitTests
ValidateLinearSelection(term, { 5, 10 }, { 10, 10 });
Copy link
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

{
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
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?


// 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
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
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
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
20 checks passed
@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>
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
4 participants