Repository: microsoft/terminal (language: cpp)

Issue:
Feature Request: Highlight all finding
# Description of the new feature/enhancement

A good and handy feature  will be the option to highlight all the find when using the ctrl+shift+f 
Something like the GitBash alt+f3 search option:

![2020-09-08_16-01-42](https://user-images.githubusercontent.com/6774998/92480374-7a52a080-f1ed-11ea-8850-f1988c2019c1.png)


This is really helpful when looking for something or doing some comparison



Discussion / hints:
I actually like how the terminal scrolls to the next selection when searching a term but this can be useful too. Can this be an optional sub-setting?
Linking this to #3920. Thanks!
Please to implement this - it's dreadfully useful 🙏

Fix patch:
diff --git a/src/buffer/out/search.cpp b/src/buffer/out/search.cpp
index 9707e8fdeaa..fd8942e0bac 100644
--- a/src/buffer/out/search.cpp
+++ b/src/buffer/out/search.cpp
@@ -111,6 +111,28 @@ const til::point_span* Search::GetCurrent() const noexcept
     return nullptr;
 }
 
+void Search::HighlightResults() const
+{
+    std::vector<til::inclusive_rect> toSelect;
+    const auto& textBuffer = _renderData->GetTextBuffer();
+
+    for (const auto& r : _results)
+    {
+        const auto rbStart = textBuffer.BufferToScreenPosition(r.start);
+        const auto rbEnd = textBuffer.BufferToScreenPosition(r.end);
+
+        til::inclusive_rect re;
+        re.top = rbStart.y;
+        re.bottom = rbEnd.y;
+        re.left = rbStart.x;
+        re.right = rbEnd.x;
+
+        toSelect.emplace_back(re);
+    }
+
+    _renderData->SelectSearchRegions(std::move(toSelect));
+}
+
 // Routine Description:
 // - Takes the found word and selects it in the screen buffer
 
@@ -127,6 +149,7 @@ bool Search::SelectCurrent() const
         return true;
     }
 
+    _renderData->ClearSelection();
     return false;
 }
 
diff --git a/src/buffer/out/search.h b/src/buffer/out/search.h
index c2d035e3e9c..a338f1272c4 100644
--- a/src/buffer/out/search.h
+++ b/src/buffer/out/search.h
@@ -33,6 +33,7 @@ class Search final
     void FindNext() noexcept;
 
     const til::point_span* GetCurrent() const noexcept;
+    void HighlightResults() const;
     bool SelectCurrent() const;
 
     const std::vector<til::point_span>& Results() const noexcept;
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index 734e1b98d2a..4efcabf711a 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -1652,6 +1652,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
         if (_searcher.ResetIfStale(*GetRenderData(), text, !goForward, !caseSensitive))
         {
+            _searcher.HighlightResults();
             _searcher.MoveToCurrentSelection();
             _cachedSearchResultRows = {};
         }
@@ -1668,7 +1669,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             // DO NOT call _updateSelectionUI() here.
             // We don't want to show the markers so manually tell it to clear it.
             _terminal->SetBlockSelection(false);
-            _renderer->TriggerSelection();
             _UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
 
             foundResults->TotalMatches(gsl::narrow<int32_t>(_searcher.Results().size()));
@@ -1676,6 +1676,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
             _terminal->AlwaysNotifyOnBufferRotation(true);
         }
+        _renderer->TriggerSelection();
 
         // Raise a FoundMatch event, which the control will use to notify
         // narrator if there was any results in the buffer
diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp
index 3e2ffe4c083..603e0e521ba 100644
--- a/src/cascadia/TerminalCore/Terminal.hpp
+++ b/src/cascadia/TerminalCore/Terminal.hpp
@@ -215,10 +215,12 @@ class Microsoft::Terminal::Core::Terminal final :
 
     std::pair<COLORREF, COLORREF> GetAttributeColors(const TextAttribute& attr) const noexcept override;
     std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override;
+    std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept override;
     const bool IsSelectionActive() const noexcept override;
     const bool IsBlockSelection() const noexcept override;
     void ClearSelection() override;
     void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override;
+    void SelectSearchRegions(std::vector<til::inclusive_rect> source) override;
     const til::point GetSelectionAnchor() const noexcept override;
     const til::point GetSelectionEnd() const noexcept override;
     const std::wstring_view GetConsoleTitle() const noexcept override;
@@ -377,6 +379,7 @@ class Microsoft::Terminal::Core::Terminal final :
         til::point pivot;
     };
     std::optional<SelectionAnchors> _selection;
+    std::vector<til::inclusive_rect> _searchSelections;
     bool _blockSelection = false;
     std::wstring _wordDelimiters;
     SelectionExpansion _multiClickSelectionMode = SelectionExpansion::Char;
@@ -464,6 +467,7 @@ class Microsoft::Terminal::Core::Terminal final :
 #pragma region TextSelection
     // These methods are defined in TerminalSelection.cpp
     std::vector<til::inclusive_rect> _GetSelectionRects() const noexcept;
+    std::vector<til::inclusive_rect> _GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept;
     std::vector<til::point_span> _GetSelectionSpans() const noexcept;
     std::pair<til::point, til::point> _PivotSelection(const til::point targetPos, bool& targetStart) const noexcept;
     std::pair<til::point, til::point> _ExpandSelectionAnchors(std::pair<til::point, til::point> anchors) const;
diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp
index 1978f5738bc..cfbc004b7d7 100644
--- a/src/cascadia/TerminalCore/TerminalSelection.cpp
+++ b/src/cascadia/TerminalCore/TerminalSelection.cpp
@@ -63,6 +63,40 @@ std::vector<til::inclusive_rect> Terminal::_GetSelectionRects() const noexcept
     return result;
 }
 
+// Method Description:
+// - Helper to determine the selected region of the buffer. Used for rendering.
+// Return Value:
+// - A vector of rectangles representing the regions to select, line by line. They are absolute coordinates relative to the buffer origin.
+std::vector<til::inclusive_rect> Terminal::_GetSearchSelectionRects(Microsoft::Console::Types::Viewport viewport) const noexcept
+{
+    std::vector<til::inclusive_rect> result;
+    try
+    {
+        auto lowerIt = std::lower_bound(_searchSelections.begin(), _searchSelections.end(), viewport.Top(), [](const til::inclusive_rect& rect, til::CoordType value) {
+            return rect.top < value;
+        });
+
+        auto upperIt = std::upper_bound(_searchSelections.begin(), _searchSelections.end(), viewport.BottomExclusive(), [](til::CoordType value, const til::inclusive_rect& rect) {
+            return value < rect.top;
+        });
+
+        for (auto selection = lowerIt; selection != upperIt; ++selection)
+        {
+            const auto start = til::point{ selection->left, selection->top };
+            const auto end = til::point{ selection->right, selection->top };
+            const auto adj = _activeBuffer().GetTextRects(start, end, _blockSelection, false);
+            for (auto a : adj)
+            {
+                result.emplace_back(a);
+            }
+        }
+
+        return result;
+    }
+    CATCH_LOG();
+    return result;
+}
+
 // Method Description:
 // - Identical to GetTextRects if it's a block selection, else returns a single span for the whole selection.
 // Return Value:
@@ -824,6 +858,7 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, til::point& pos) noex
 void Terminal::ClearSelection()
 {
     _assertLocked();
+    _searchSelections.clear();
     _selection = std::nullopt;
     _selectionMode = SelectionInteractionMode::None;
     _selectionIsTargetingUrl = false;
diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp
index 034a65ab784..8dd9b4dae9a 100644
--- a/src/cascadia/TerminalCore/terminalrenderdata.cpp
+++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp
@@ -150,6 +150,24 @@ catch (...)
     return {};
 }
 
+std::vector<Microsoft::Console::Types::Viewport> Terminal::GetSearchSelectionRects() noexcept
+try
+{
+    std::vector<Viewport> result;
+
+    for (const auto& lineRect : _GetSearchSelectionRects(_GetVisibleViewport()))
+    {
+        result.emplace_back(Viewport::FromInclusive(lineRect));
+    }
+
+    return result;
+}
+catch (...)
+{
+    LOG_CAUGHT_EXCEPTION();
+    return {};
+}
+
 void Terminal::SelectNewRegion(const til::point coordStart, const til::point coordEnd)
 {
 #pragma warning(push)
@@ -188,6 +206,23 @@ void Terminal::SelectNewRegion(const til::point coordStart, const til::point coo
     SetSelectionEnd(realCoordEnd, SelectionExpansion::Char);
 }
 
+void Terminal::SelectSearchRegions(std::vector<til::inclusive_rect> rects)
+{
+    _searchSelections.clear();
+    for (auto& rect : rects)
+    {
+        rect.top -= _VisibleStartIndex();
+        rect.bottom -= _VisibleStartIndex();
+
+        const auto realStart = _ConvertToBufferCell(til::point{ rect.left, rect.top });
+        const auto realEnd = _ConvertToBufferCell(til::point{ rect.right, rect.bottom });
+
+        auto rr = til::inclusive_rect{ realStart.x, realStart.y, realEnd.x, realEnd.y };
+
+        _searchSelections.emplace_back(rr);
+    }
+}
+
 const std::wstring_view Terminal::GetConsoleTitle() const noexcept
 {
     _assertLocked();
diff --git a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp
index d5baebadf26..21022f30333 100644
--- a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp
+++ b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp
@@ -59,6 +59,7 @@ namespace
         HRESULT PaintBufferLine(std::span<const Cluster> /*clusters*/, til::point /*coord*/, bool /*fTrimLeft*/, bool /*lineWrapped*/) noexcept { return S_OK; }
         HRESULT PaintBufferGridLines(GridLineSet /*lines*/, COLORREF /*gridlineColor*/, COLORREF /*underlineColor*/, size_t /*cchLine*/, til::point /*coordTarget*/) noexcept { return S_OK; }
         HRESULT PaintSelection(const til::rect& /*rect*/) noexcept { return S_OK; }
+        HRESULT PaintSelections(const std::vector<til::rect>& /*rects*/) noexcept { return S_OK; }
         HRESULT PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; }
         HRESULT UpdateDrawingBrushes(const TextAttribute& /*textAttributes*/, const RenderSettings& /*renderSettings*/, gsl::not_null<IRenderData*> /*pData*/, bool /*usingSoftFont*/, bool /*isSettingDefaultBrushes*/) noexcept { return S_OK; }
         HRESULT UpdateFont(const FontInfoDesired& /*FontInfoDesired*/, _Out_ FontInfo& /*FontInfo*/) noexcept { return S_OK; }
diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp
index 5c89edae9a9..d9b094626ba 100644
--- a/src/host/renderData.cpp
+++ b/src/host/renderData.cpp
@@ -79,6 +79,16 @@ std::vector<Viewport> RenderData::GetSelectionRects() noexcept
     return result;
 }
 
+// Method Description:
+// - Retrieves one rectangle per line describing the area of the viewport
+//   that should be highlighted in some way to represent a user-interactive selection
+// Return Value:
+// - Vector of Viewports describing the area selected
+std::vector<Viewport> RenderData::GetSearchSelectionRects() noexcept
+{
+    return {};
+}
+
 // Method Description:
 // - Lock the console for reading the contents of the buffer. Ensures that the
 //      contents of the console won't be changed in the middle of a paint
@@ -371,6 +381,10 @@ void RenderData::SelectNewRegion(const til::point coordStart, const til::point c
     Selection::Instance().SelectNewRegion(coordStart, coordEnd);
 }
 
+void RenderData::SelectSearchRegions(std::vector<til::inclusive_rect> source)
+{
+}
+
 // Routine Description:
 // - Gets the current selection anchor position
 // Arguments:
diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp
index 5e649353cd7..52056d7a6f5 100644
--- a/src/host/renderData.hpp
+++ b/src/host/renderData.hpp
@@ -26,6 +26,7 @@ class RenderData final :
     const FontInfo& GetFontInfo() const noexcept override;
 
     std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override;
+    std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept override;
 
     void LockConsole() noexcept override;
     void UnlockConsole() noexcept override;
@@ -54,6 +55,7 @@ class RenderData final :
     const bool IsBlockSelection() const noexcept override;
     void ClearSelection() override;
     void SelectNewRegion(const til::point coordStart, const til::point coordEnd) override;
+    void SelectSearchRegions(std::vector<til::inclusive_rect> source) override;
     const til::point GetSelectionAnchor() const noexcept override;
     const til::point GetSelectionEnd() const noexcept override;
     const bool IsUiaDataInitialized() const noexcept override { return true; }
diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp
index 902f711771d..7f4796df60d 100644
--- a/src/host/ut_host/VtIoTests.cpp
+++ b/src/host/ut_host/VtIoTests.cpp
@@ -282,6 +282,11 @@ class MockRenderData : public IRenderData
         return std::vector<Microsoft::Console::Types::Viewport>{};
     }
 
+    std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept override
+    {
+        return std::vector<Microsoft::Console::Types::Viewport>{};
+    }
+
     void LockConsole() noexcept override
     {
     }
@@ -363,6 +368,10 @@ class MockRenderData : public IRenderData
     {
     }
 
+    void SelectSearchRegions(std::vector<til::inclusive_rect> /*source*/) override
+    {
+    }
+
     const til::point GetSelectionAnchor() const noexcept
     {
         return {};
diff --git a/src/interactivity/onecore/BgfxEngine.cpp b/src/interactivity/onecore/BgfxEngine.cpp
index d508603d5f9..a730ae8219e 100644
--- a/src/interactivity/onecore/BgfxEngine.cpp
+++ b/src/interactivity/onecore/BgfxEngine.cpp
@@ -161,6 +161,11 @@ CATCH_RETURN()
     return S_OK;
 }
 
+[[nodiscard]] HRESULT BgfxEngine::PaintSelections(const std::vector<til::rect>& /*rects*/) noexcept
+{
+    return S_OK;
+}
+
 [[nodiscard]] HRESULT BgfxEngine::PaintCursor(const CursorOptions& options) noexcept
 try
 {
diff --git a/src/interactivity/onecore/BgfxEngine.hpp b/src/interactivity/onecore/BgfxEngine.hpp
index e9759ce0306..c787baba392 100644
--- a/src/interactivity/onecore/BgfxEngine.hpp
+++ b/src/interactivity/onecore/BgfxEngine.hpp
@@ -53,6 +53,7 @@ namespace Microsoft::Console::Render
                                               const bool lineWrapped) noexcept override;
         [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
         [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
+        [[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
 
         [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
 
diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp
index 7438b93543e..93821394ce1 100644
--- a/src/renderer/atlas/AtlasEngine.cpp
+++ b/src/renderer/atlas/AtlasEngine.cpp
@@ -414,6 +414,40 @@ try
 }
 CATCH_RETURN()
 
+[[nodiscard]] HRESULT AtlasEngine::PaintSelections(const std::vector<til::rect>& rects) noexcept
+try
+{
+    if (rects.empty())
+    {
+        return S_OK;
+    }
+
+    for (const auto& rect : rects)
+    {
+        const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.top, 0, _p.s->viewportCellCount.y));
+        const auto from = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.left, 0, _p.s->viewportCellCount.x - 1));
+        const auto to = gsl::narrow_cast<u16>(clamp<til::CoordType>(rect.right, from, _p.s->viewportCellCount.x));
+
+        if (rect.bottom <= 0 || rect.top >= _p.s->viewportCellCount.y)
+        {
+            continue;
+        }
+
+        const auto bg = &_p.backgroundBitmap[_p.colorBitmapRowStride * y];
+        const auto fg = &_p.foregroundBitmap[_p.colorBitmapRowStride * y];
+        std::fill(bg + from, bg + to, 0xff3296ff);
+        std::fill(fg + from, fg + to, 0xff000000);
+    }
+
+    for (int i = 0; i < 2; ++i)
+    {
+        _p.colorBitmapGenerations[i].bump();
+    }
+
+    return S_OK;
+}
+CATCH_RETURN()
+
 [[nodiscard]] HRESULT AtlasEngine::PaintCursor(const CursorOptions& options) noexcept
 try
 {
diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h
index 4ec09a9e931..8d6d76795c0 100644
--- a/src/renderer/atlas/AtlasEngine.h
+++ b/src/renderer/atlas/AtlasEngine.h
@@ -45,6 +45,7 @@ namespace Microsoft::Console::Render::Atlas
         [[nodiscard]] HRESULT PaintBufferLine(std::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
         [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
         [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
+        [[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
         [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
         [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null<IRenderData*> pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept override;
         [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp
index 0b1d6b004cb..c7c5c491815 100644
--- a/src/renderer/base/renderer.cpp
+++ b/src/renderer/base/renderer.cpp
@@ -362,6 +362,7 @@ void Renderer::TriggerSelection()
     {
         // Get selection rectangles
         auto rects = _GetSelectionRects();
+        auto searchSelections = _GetSearchSelectionRects();
 
         // Make a viewport representing the coordinates that are currently presentable.
         const til::rect viewport{ _pData->GetViewport().Dimensions() };
@@ -374,11 +375,14 @@ void Renderer::TriggerSelection()
 
         FOREACH_ENGINE(pEngine)
         {
+            LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSearchSelection));
             LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection));
+            LOG_IF_FAILED(pEngine->InvalidateSelection(searchSelections));
             LOG_IF_FAILED(pEngine->InvalidateSelection(rects));
         }
 
         _previousSelection = std::move(rects);
+        _previousSearchSelection = std::move(searchSelections);
 
         NotifyPaintFrame();
     }
@@ -1206,9 +1210,20 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
 
         // Get selection rectangles
         const auto rectangles = _GetSelectionRects();
-        for (const auto& rect : rectangles)
+        const auto searchRectangles = _GetSearchSelectionRects();
+
+        std::vector<til::rect> dirtySearchRectangles;
+        for (auto& dirtyRect : dirtyAreas)
         {
-            for (auto& dirtyRect : dirtyAreas)
+            for (const auto& sr : searchRectangles)
+            {
+                if (const auto rectCopy = sr & dirtyRect)
+                {
+                    dirtySearchRectangles.emplace_back(rectCopy);
+                }
+            }
+
+            for (const auto& rect : rectangles)
             {
                 if (const auto rectCopy = rect & dirtyRect)
                 {
@@ -1216,6 +1231,11 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
                 }
             }
         }
+
+        if (!dirtySearchRectangles.empty())
+        {
+            LOG_IF_FAILED(pEngine->PaintSelections(std::move(dirtySearchRectangles)));
+        }
     }
     CATCH_LOG();
 }
@@ -1281,6 +1301,28 @@ std::vector<til::rect> Renderer::_GetSelectionRects() const
     return result;
 }
 
+std::vector<til::rect> Renderer::_GetSearchSelectionRects() const
+{
+    const auto& buffer = _pData->GetTextBuffer();
+    auto rects = _pData->GetSearchSelectionRects();
+    // Adjust rectangles to viewport
+    auto view = _pData->GetViewport();
+
+    std::vector<til::rect> result;
+    result.reserve(rects.size());
+
+    for (auto rect : rects)
+    {
+        // Convert buffer offsets to the equivalent range of screen cells
+        // expected by callers, taking line rendition into account.
+        const auto lineRendition = buffer.GetLineRendition(rect.Top());
+        rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition));
+        result.emplace_back(view.ConvertToOrigin(rect).ToExclusive());
+    }
+
+    return result;
+}
+
 // Method Description:
 // - Offsets all of the selection rectangles we might be holding onto
 //   as the previously selected area. If the whole viewport scrolls,
diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp
index ae3ed2cfda9..1cd61799a8f 100644
--- a/src/renderer/base/renderer.hpp
+++ b/src/renderer/base/renderer.hpp
@@ -110,6 +110,7 @@ namespace Microsoft::Console::Render
         [[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes);
         [[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine);
         std::vector<til::rect> _GetSelectionRects() const;
+        std::vector<til::rect> _GetSearchSelectionRects() const;
         void _ScrollPreviousSelection(const til::point delta);
         [[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine);
         bool _isInHoveredInterval(til::point coordTarget) const noexcept;
@@ -127,6 +128,7 @@ namespace Microsoft::Console::Render
         Microsoft::Console::Types::Viewport _viewport;
         std::vector<Cluster> _clusterBuffer;
         std::vector<til::rect> _previousSelection;
+        std::vector<til::rect> _previousSearchSelection;
         std::function<void()> _pfnBackgroundColorChanged;
         std::function<void()> _pfnFrameColorChanged;
         std::function<void()> _pfnRendererEnteredErrorState;
diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp
index 064d219f72a..e03cfb20081 100644
--- a/src/renderer/dx/DxRenderer.cpp
+++ b/src/renderer/dx/DxRenderer.cpp
@@ -1844,6 +1844,14 @@ try
 }
 CATCH_RETURN()
 
+[[nodiscard]] HRESULT DxEngine::PaintSelections(const std::vector<til::rect>& rects) noexcept
+try
+{
+    UNREFERENCED_PARAMETER(rects);
+    return S_OK;
+}
+CATCH_RETURN()
+
 // Routine Description:
 // - Does nothing. Our cursor is drawn in CustomTextRenderer::DrawGlyphRun,
 //   either above or below the text.
diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp
index 877b3f1adfa..990c77e18e5 100644
--- a/src/renderer/dx/DxRenderer.hpp
+++ b/src/renderer/dx/DxRenderer.hpp
@@ -108,6 +108,7 @@ namespace Microsoft::Console::Render
 
         [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
         [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
+        [[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rect) noexcept override;
 
         [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
 
diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp
index 2cc5b4dc14c..1d855d2746e 100644
--- a/src/renderer/gdi/gdirenderer.hpp
+++ b/src/renderer/gdi/gdirenderer.hpp
@@ -57,6 +57,7 @@ namespace Microsoft::Console::Render
                                                    const size_t cchLine,
                                                    const til::point coordTarget) noexcept override;
         [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
+        [[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
 
         [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
 
diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp
index 222576390e2..5951c57ef29 100644
--- a/src/renderer/gdi/paint.cpp
+++ b/src/renderer/gdi/paint.cpp
@@ -815,6 +815,13 @@ bool GdiEngine::FontHasWesternScript(HDC hdc)
     return S_OK;
 }
 
+[[nodiscard]] HRESULT GdiEngine::PaintSelections(const std::vector<til::rect>& rects) noexcept
+{
+    UNREFERENCED_PARAMETER(rects);
+
+    return S_OK;
+}
+
 #ifdef DBG
 
 void GdiEngine::_CreateDebugWindow()
diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp
index 69e0dc2a0e6..cfc035a7f9b 100644
--- a/src/renderer/inc/IRenderData.hpp
+++ b/src/renderer/inc/IRenderData.hpp
@@ -47,6 +47,7 @@ namespace Microsoft::Console::Render
         virtual const TextBuffer& GetTextBuffer() const noexcept = 0;
         virtual const FontInfo& GetFontInfo() const noexcept = 0;
         virtual std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept = 0;
+        virtual std::vector<Microsoft::Console::Types::Viewport> GetSearchSelectionRects() noexcept = 0;
         virtual void LockConsole() noexcept = 0;
         virtual void UnlockConsole() noexcept = 0;
 
@@ -71,6 +72,7 @@ namespace Microsoft::Console::Render
         virtual const bool IsBlockSelection() const = 0;
         virtual void ClearSelection() = 0;
         virtual void SelectNewRegion(const til::point coordStart, const til::point coordEnd) = 0;
+        virtual void SelectSearchRegions(std::vector<til::inclusive_rect> source) = 0;
         virtual const til::point GetSelectionAnchor() const noexcept = 0;
         virtual const til::point GetSelectionEnd() const noexcept = 0;
         virtual const bool IsUiaDataInitialized() const noexcept = 0;
diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp
index afcaa5aff59..016f0f10baa 100644
--- a/src/renderer/inc/IRenderEngine.hpp
+++ b/src/renderer/inc/IRenderEngine.hpp
@@ -78,6 +78,7 @@ namespace Microsoft::Console::Render
         [[nodiscard]] virtual HRESULT PaintBufferLine(std::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept = 0;
         [[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF gridlineColor, COLORREF underlineColor, size_t cchLine, til::point coordTarget) noexcept = 0;
         [[nodiscard]] virtual HRESULT PaintSelection(const til::rect& rect) noexcept = 0;
+        [[nodiscard]] virtual HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept = 0;
         [[nodiscard]] virtual HRESULT PaintCursor(const CursorOptions& options) noexcept = 0;
         [[nodiscard]] virtual HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, gsl::not_null<IRenderData*> pData, bool usingSoftFont, bool isSettingDefaultBrushes) noexcept = 0;
         [[nodiscard]] virtual HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept = 0;
diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp
index d1e72fbd0eb..a245491dead 100644
--- a/src/renderer/uia/UiaRenderer.cpp
+++ b/src/renderer/uia/UiaRenderer.cpp
@@ -386,6 +386,11 @@ void UiaEngine::WaitUntilCanRender() noexcept
     return S_FALSE;
 }
 
+[[nodiscard]] HRESULT UiaEngine::PaintSelections(const std::vector<til::rect>& /*rect*/) noexcept
+{
+    return S_FALSE;
+}
+
 // Routine Description:
 // - Draws the cursor on the screen
 //  For UIA, this doesn't mean anything. So do nothing.
diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp
index 4625ca155cb..bd1734c5d64 100644
--- a/src/renderer/uia/UiaRenderer.hpp
+++ b/src/renderer/uia/UiaRenderer.hpp
@@ -51,6 +51,7 @@ namespace Microsoft::Console::Render
         [[nodiscard]] HRESULT PaintBufferLine(const std::span<const Cluster> clusters, const til::point coord, const bool fTrimLeft, const bool lineWrapped) noexcept override;
         [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
         [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
+        [[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
         [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
         [[nodiscard]] HRESULT UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, const gsl::not_null<IRenderData*> pData, const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept override;
         [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
diff --git a/src/renderer/vt/paint.cpp b/src/renderer/vt/paint.cpp
index 99f8853a98f..d8e3e66686b 100644
--- a/src/renderer/vt/paint.cpp
+++ b/src/renderer/vt/paint.cpp
@@ -233,6 +233,11 @@ using namespace Microsoft::Console::Types;
     return S_OK;
 }
 
+[[nodiscard]] HRESULT VtEngine::PaintSelections(const std::vector<til::rect>& /*rect*/) noexcept
+{
+    return S_OK;
+}
+
 // Routine Description:
 // - Write a VT sequence to change the current colors of text. Writes true RGB
 //      color sequences.
diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp
index 676672ee814..7a974850afc 100644
--- a/src/renderer/vt/vtrenderer.hpp
+++ b/src/renderer/vt/vtrenderer.hpp
@@ -64,6 +64,7 @@ namespace Microsoft::Console::Render
         [[nodiscard]] HRESULT PaintBufferLine(std::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
         [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
         [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
+        [[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
         [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
         [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
         [[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override;
diff --git a/src/renderer/wddmcon/WddmConRenderer.cpp b/src/renderer/wddmcon/WddmConRenderer.cpp
index fda8ae6c21b..db729467cae 100644
--- a/src/renderer/wddmcon/WddmConRenderer.cpp
+++ b/src/renderer/wddmcon/WddmConRenderer.cpp
@@ -298,6 +298,11 @@ CATCH_RETURN()
     return S_OK;
 }
 
+[[nodiscard]] HRESULT WddmConEngine::PaintSelections(const std::vector<til::rect>& /*rects*/) noexcept
+{
+    return S_OK;
+}
+
 [[nodiscard]] HRESULT WddmConEngine::PaintCursor(const CursorOptions& /*options*/) noexcept
 {
     return S_OK;
diff --git a/src/renderer/wddmcon/WddmConRenderer.hpp b/src/renderer/wddmcon/WddmConRenderer.hpp
index 954dc226946..930fbe6dea6 100644
--- a/src/renderer/wddmcon/WddmConRenderer.hpp
+++ b/src/renderer/wddmcon/WddmConRenderer.hpp
@@ -46,6 +46,7 @@ namespace Microsoft::Console::Render
                                               const bool lineWrapped) noexcept override;
         [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF gridlineColor, const COLORREF underlineColor, const size_t cchLine, const til::point coordTarget) noexcept override;
         [[nodiscard]] HRESULT PaintSelection(const til::rect& rect) noexcept override;
+        [[nodiscard]] HRESULT PaintSelections(const std::vector<til::rect>& rects) noexcept override;
 
         [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
 


Test patch: