mark mode: begin selection at focused search result (#19550)

## Summary of the Pull Request
Searching in terminal highlights all search results. However, those
results are considered separate from a selection. In the past, the
highlighted result would be selected, resulting in it being the initial
position for mark mode. Now that it's separate, mark mode doesn't start
there.

To fix this, there's 2 changes here:
1. When we exit the search, we now select the focused search result.
This becomes the initial position for mark mode.
2. When we're in the middle of a search and mark mode becomes enabled,
the focused search result becomes the initial position for mark mode.

With this change, mark mode's initial position is determined in this
order:
1. the position of an active selection
2. the position of the focused search result (if one is available)
3. the top-left position of the viewport (if there is a scrollback) (see
#19549)
4. the current cursor position

## Validation Steps Performed
Entering mark mode in scenario X results in a starting position of Y:
 selected text during a search --> selected text
- NOTE: this seems to only occur if you start a search, then manually
click on the terminal to bring focus there, but keep the search results
active

 performed a search and results are available -->focused search result
 performed a search and no results are available
   - scrolled up --> top-left of viewport
   - no scrollback --> cursor position

 performed a search, got results, then closed search --> focused search
result

Closes #19358
This commit is contained in:
Carlos Zamora 2025-11-18 11:43:42 -08:00 committed by GitHub
parent 898b9e3cab
commit 19a85010fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 31 additions and 7 deletions

View File

@ -1787,6 +1787,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::ClearSearch()
{
const auto lock = _terminal->LockForWriting();
// GH #19358: select the focused search result before clearing search
if (const auto focusedSearchResult = _terminal->GetSearchHighlightFocused())
{
_terminal->SetSelectionAnchor(focusedSearchResult->start);
_terminal->SetSelectionEnd(focusedSearchResult->end);
_renderer->TriggerSelection();
}
_terminal->SetSearchHighlights({});
_terminal->SetSearchHighlightFocused(0);
_renderer->TriggerSearchHighlight(_searcher.Results());

View File

@ -362,10 +362,30 @@ void Terminal::ToggleMarkMode()
{
// Enter Mark Mode
// NOTE: directly set cursor state. We already should have locked before calling this function.
if (!IsSelectionActive())
if (IsSelectionActive())
{
// Selection already existed --> just target "end"
if (WI_AreAllFlagsClear(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End))
{
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
}
}
else if (const auto focusedSearchResult = GetSearchHighlightFocused())
{
// We have a focused search result --> treat it as selection
*_selection.write() = SelectionInfo{
.start = focusedSearchResult->start,
.end = focusedSearchResult->end,
.pivot = focusedSearchResult->start,
.blockSelection = false,
.active = true,
};
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
}
else
{
// If we're scrolled up, use the viewport origin as the selection start.
// Otherwise, use the cursor position.
// Otherwise, just use the cursor position.
const auto cursorPos = _scrollOffset != 0 ? _GetVisibleViewport().Origin() :
_activeBuffer().GetCursor().GetPosition();
*_selection.write() = SelectionInfo{
@ -377,11 +397,6 @@ void Terminal::ToggleMarkMode()
};
WI_SetAllFlags(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End);
}
else if (WI_AreAllFlagsClear(_selectionEndpoint, SelectionEndpoint::Start | SelectionEndpoint::End))
{
// Selection already existed
WI_SetFlag(_selectionEndpoint, SelectionEndpoint::End);
}
_ScrollToPoint(_selection->start);
_selectionMode = SelectionInteractionMode::Mark;
_selectionIsTargetingUrl = false;