From 19a85010fe23d486dbc7b832c9c1e54069a1b233 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 18 Nov 2025 11:43:42 -0800 Subject: [PATCH] mark mode: begin selection at focused search result (#19550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 --- src/cascadia/TerminalControl/ControlCore.cpp | 9 ++++++ .../TerminalCore/TerminalSelection.cpp | 29 ++++++++++++++----- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index cffc4e91a8..644bb2c0bc 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -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()); diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index f316cc782a..6942fb9ea5 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -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;