Add support for regex search to conhost and Terminal (#17316)

This is broken down into individual reviewable commits.

[Here
is](https://github.com/microsoft/terminal/assets/189190/3b2ffd50-1350-4f3c-86b0-75abbd846969)
a video of it in action!

Part of #3920

(cherry picked from commit ecb56314762dcb9b41e177d05d669b28bbbe1490)
Service-Card-Id: PVTI_lADOAF3p4s4AmhmszgTTM0g
Service-Version: 1.21
This commit is contained in:
Dustin L. Howett 2024-05-31 06:17:16 -05:00 committed by Dustin Howett
parent dc8a830fea
commit 87c87b51fc
24 changed files with 203 additions and 63 deletions

View File

@ -607,6 +607,7 @@ FILTERONPASTE
FINDCASE FINDCASE
FINDDLG FINDDLG
FINDDOWN FINDDOWN
FINDREGEX
FINDSTRINGEXACT FINDSTRINGEXACT
FINDUP FINDUP
FIter FIter

View File

@ -8,24 +8,26 @@
using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types;
bool Search::IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool caseInsensitive) const noexcept bool Search::IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags) const noexcept
{ {
return _renderData != &renderData || return _renderData != &renderData ||
_needle != needle || _needle != needle ||
_caseInsensitive != caseInsensitive || _flags != flags ||
_lastMutationId != renderData.GetTextBuffer().GetLastMutationId(); _lastMutationId != renderData.GetTextBuffer().GetLastMutationId();
} }
bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool caseInsensitive, bool reverse) bool Search::Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse)
{ {
const auto& textBuffer = renderData.GetTextBuffer(); const auto& textBuffer = renderData.GetTextBuffer();
_renderData = &renderData; _renderData = &renderData;
_needle = needle; _needle = needle;
_caseInsensitive = caseInsensitive; _flags = flags;
_lastMutationId = textBuffer.GetLastMutationId(); _lastMutationId = textBuffer.GetLastMutationId();
_results = textBuffer.SearchText(needle, caseInsensitive); auto result = textBuffer.SearchText(needle, _flags);
_ok = result.has_value();
_results = std::move(result).value_or(std::vector<til::point_span>{});
_index = reverse ? gsl::narrow_cast<ptrdiff_t>(_results.size()) - 1 : 0; _index = reverse ? gsl::narrow_cast<ptrdiff_t>(_results.size()) - 1 : 0;
_step = reverse ? -1 : 1; _step = reverse ? -1 : 1;
return true; return true;
@ -144,3 +146,8 @@ ptrdiff_t Search::CurrentMatch() const noexcept
{ {
return _index; return _index;
} }
bool Search::IsOk() const noexcept
{
return _ok;
}

View File

@ -20,13 +20,23 @@ Revision History:
#include "textBuffer.hpp" #include "textBuffer.hpp"
#include "../renderer/inc/IRenderData.hpp" #include "../renderer/inc/IRenderData.hpp"
enum class SearchFlag : unsigned int
{
None = 0,
CaseInsensitive = 1 << 0,
RegularExpression = 1 << 1,
};
DEFINE_ENUM_FLAG_OPERATORS(SearchFlag);
class Search final class Search final
{ {
public: public:
Search() = default; Search() = default;
bool IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool caseInsensitive) const noexcept; bool IsStale(const Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags) const noexcept;
bool Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, bool caseInsensitive, bool reverse); bool Reset(Microsoft::Console::Render::IRenderData& renderData, const std::wstring_view& needle, SearchFlag flags, bool reverse);
void MoveToCurrentSelection(); void MoveToCurrentSelection();
void MoveToPoint(til::point anchor) noexcept; void MoveToPoint(til::point anchor) noexcept;
@ -39,14 +49,16 @@ public:
const std::vector<til::point_span>& Results() const noexcept; const std::vector<til::point_span>& Results() const noexcept;
std::vector<til::point_span>&& ExtractResults() noexcept; std::vector<til::point_span>&& ExtractResults() noexcept;
ptrdiff_t CurrentMatch() const noexcept; ptrdiff_t CurrentMatch() const noexcept;
bool IsOk() const noexcept;
private: private:
// _renderData is a pointer so that Search() is constexpr default constructable. // _renderData is a pointer so that Search() is constexpr default constructable.
Microsoft::Console::Render::IRenderData* _renderData = nullptr; Microsoft::Console::Render::IRenderData* _renderData = nullptr;
std::wstring _needle; std::wstring _needle;
bool _caseInsensitive = false; SearchFlag _flags{};
uint64_t _lastMutationId = 0; uint64_t _lastMutationId = 0;
bool _ok{ false };
std::vector<til::point_span> _results; std::vector<til::point_span> _results;
ptrdiff_t _index = 0; ptrdiff_t _index = 0;
ptrdiff_t _step = 0; ptrdiff_t _step = 0;

View File

@ -12,6 +12,7 @@
#include "../../types/inc/GlyphWidth.hpp" #include "../../types/inc/GlyphWidth.hpp"
#include "../renderer/base/renderer.hpp" #include "../renderer/base/renderer.hpp"
#include "../types/inc/utils.hpp" #include "../types/inc/utils.hpp"
#include "search.h"
using namespace Microsoft::Console; using namespace Microsoft::Console;
using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types;
@ -3193,14 +3194,15 @@ void TextBuffer::CopyHyperlinkMaps(const TextBuffer& other)
// Searches through the entire (committed) text buffer for `needle` and returns the coordinates in absolute coordinates. // Searches through the entire (committed) text buffer for `needle` and returns the coordinates in absolute coordinates.
// The end coordinates of the returned ranges are considered inclusive. // The end coordinates of the returned ranges are considered inclusive.
std::vector<til::point_span> TextBuffer::SearchText(const std::wstring_view& needle, bool caseInsensitive) const std::optional<std::vector<til::point_span>> TextBuffer::SearchText(const std::wstring_view& needle, SearchFlag flags) const
{ {
return SearchText(needle, caseInsensitive, 0, til::CoordTypeMax); return SearchText(needle, flags, 0, til::CoordTypeMax);
} }
// Searches through the given rows [rowBeg,rowEnd) for `needle` and returns the coordinates in absolute coordinates. // Searches through the given rows [rowBeg,rowEnd) for `needle` and returns the coordinates in absolute coordinates.
// While the end coordinates of the returned ranges are considered inclusive, the [rowBeg,rowEnd) range is half-open. // While the end coordinates of the returned ranges are considered inclusive, the [rowBeg,rowEnd) range is half-open.
std::vector<til::point_span> TextBuffer::SearchText(const std::wstring_view& needle, bool caseInsensitive, til::CoordType rowBeg, til::CoordType rowEnd) const // Returns nullopt if the parameters were invalid (e.g. regex search was requested with an invalid regex)
std::optional<std::vector<til::point_span>> TextBuffer::SearchText(const std::wstring_view& needle, SearchFlag flags, til::CoordType rowBeg, til::CoordType rowEnd) const
{ {
rowEnd = std::min(rowEnd, _estimateOffsetOfLastCommittedRow() + 1); rowEnd = std::min(rowEnd, _estimateOffsetOfLastCommittedRow() + 1);
@ -3214,11 +3216,25 @@ std::vector<til::point_span> TextBuffer::SearchText(const std::wstring_view& nee
auto text = ICU::UTextFromTextBuffer(*this, rowBeg, rowEnd); auto text = ICU::UTextFromTextBuffer(*this, rowBeg, rowEnd);
uint32_t flags = UREGEX_LITERAL; uint32_t icuFlags{ 0 };
WI_SetFlagIf(flags, UREGEX_CASE_INSENSITIVE, caseInsensitive); WI_SetFlagIf(icuFlags, UREGEX_CASE_INSENSITIVE, WI_IsFlagSet(flags, SearchFlag::CaseInsensitive));
if (WI_IsFlagSet(flags, SearchFlag::RegularExpression))
{
WI_SetFlag(icuFlags, UREGEX_MULTILINE);
}
else
{
WI_SetFlag(icuFlags, UREGEX_LITERAL);
}
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
const auto re = ICU::CreateRegex(needle, flags, &status); const auto re = ICU::CreateRegex(needle, icuFlags, &status);
if (status > U_ZERO_ERROR)
{
return std::nullopt;
}
uregex_setUText(re.get(), &text, &status); uregex_setUText(re.get(), &text, &status);
if (uregex_find(re.get(), -1, &status)) if (uregex_find(re.get(), -1, &status))

View File

@ -58,6 +58,7 @@ filling in the last row, and updating the screen.
#include "../buffer/out/textBufferTextIterator.hpp" #include "../buffer/out/textBufferTextIterator.hpp"
struct URegularExpression; struct URegularExpression;
enum class SearchFlag : unsigned int;
namespace Microsoft::Console::Render namespace Microsoft::Console::Render
{ {
@ -293,8 +294,8 @@ public:
static void Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const Microsoft::Console::Types::Viewport* lastCharacterViewport = nullptr, PositionInformation* positionInfo = nullptr); static void Reflow(TextBuffer& oldBuffer, TextBuffer& newBuffer, const Microsoft::Console::Types::Viewport* lastCharacterViewport = nullptr, PositionInformation* positionInfo = nullptr);
std::vector<til::point_span> SearchText(const std::wstring_view& needle, bool caseInsensitive) const; std::optional<std::vector<til::point_span>> SearchText(const std::wstring_view& needle, SearchFlag flags) const;
std::vector<til::point_span> SearchText(const std::wstring_view& needle, bool caseInsensitive, til::CoordType rowBeg, til::CoordType rowEnd) const; std::optional<std::vector<til::point_span>> SearchText(const std::wstring_view& needle, SearchFlag flags, til::CoordType rowBeg, til::CoordType rowEnd) const;
// Mark handling // Mark handling
std::vector<ScrollMark> GetMarkRows() const; std::vector<ScrollMark> GetMarkRows() const;

View File

@ -6,6 +6,7 @@
#include "WexTestClass.h" #include "WexTestClass.h"
#include "../textBuffer.hpp" #include "../textBuffer.hpp"
#include "../../renderer/inc/DummyRenderer.hpp" #include "../../renderer/inc/DummyRenderer.hpp"
#include "../search.h"
template<> template<>
class WEX::TestExecution::VerifyOutputTraits<std::vector<til::point_span>> class WEX::TestExecution::VerifyOutputTraits<std::vector<til::point_span>>
@ -49,15 +50,15 @@ class UTextAdapterTests
}; };
auto expected = std::vector{ s(0, 2), s(8, 10) }; auto expected = std::vector{ s(0, 2), s(8, 10) };
auto actual = buffer.SearchText(L"abc", false); auto actual = buffer.SearchText(L"abc", SearchFlag::None);
VERIFY_ARE_EQUAL(expected, actual); VERIFY_ARE_EQUAL(expected, actual);
expected = std::vector{ s(5, 5) }; expected = std::vector{ s(5, 5) };
actual = buffer.SearchText(L"𝒷", false); actual = buffer.SearchText(L"𝒷", SearchFlag::None);
VERIFY_ARE_EQUAL(expected, actual); VERIFY_ARE_EQUAL(expected, actual);
expected = std::vector{ s(12, 15) }; expected = std::vector{ s(12, 15) };
actual = buffer.SearchText(L"ネコ", false); actual = buffer.SearchText(L"ネコ", SearchFlag::None);
VERIFY_ARE_EQUAL(expected, actual); VERIFY_ARE_EQUAL(expected, actual);
} }
}; };

View File

@ -1654,10 +1654,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - resetOnly: If true, only Reset() will be called, if anything. FindNext() will never be called. // - resetOnly: If true, only Reset() will be called, if anything. FindNext() will never be called.
// Return Value: // Return Value:
// - <none> // - <none>
SearchResults ControlCore::Search(const std::wstring_view& text, const bool goForward, const bool caseSensitive, const bool resetOnly) SearchResults ControlCore::Search(const std::wstring_view& text, const bool goForward, const bool caseSensitive, const bool regularExpression, const bool resetOnly)
{ {
const auto lock = _terminal->LockForWriting(); const auto lock = _terminal->LockForWriting();
const auto searchInvalidated = _searcher.IsStale(*_terminal.get(), text, !caseSensitive); SearchFlag flags{};
WI_SetFlagIf(flags, SearchFlag::CaseInsensitive, !caseSensitive);
WI_SetFlagIf(flags, SearchFlag::RegularExpression, regularExpression);
const auto searchInvalidated = _searcher.IsStale(*_terminal.get(), text, flags);
if (searchInvalidated || !resetOnly) if (searchInvalidated || !resetOnly)
{ {
@ -1666,7 +1669,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (searchInvalidated) if (searchInvalidated)
{ {
oldResults = _searcher.ExtractResults(); oldResults = _searcher.ExtractResults();
_searcher.Reset(*_terminal.get(), text, !caseSensitive, !goForward); _searcher.Reset(*_terminal.get(), text, flags, !goForward);
if (SnapSearchResultToSelection()) if (SnapSearchResultToSelection())
{ {
@ -1700,6 +1703,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
.TotalMatches = totalMatches, .TotalMatches = totalMatches,
.CurrentMatch = currentMatch, .CurrentMatch = currentMatch,
.SearchInvalidated = searchInvalidated, .SearchInvalidated = searchInvalidated,
.SearchRegexInvalid = !_searcher.IsOk(),
}; };
} }

View File

@ -219,7 +219,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void SetSelectionAnchor(const til::point position); void SetSelectionAnchor(const til::point position);
void SetEndSelectionPoint(const til::point position); void SetEndSelectionPoint(const til::point position);
SearchResults Search(const std::wstring_view& text, bool goForward, bool caseSensitive, bool reset); SearchResults Search(const std::wstring_view& text, bool goForward, bool caseSensitive, bool regularExpression, bool reset);
const std::vector<til::point_span>& SearchResultRows() const noexcept; const std::vector<til::point_span>& SearchResultRows() const noexcept;
void ClearSearch(); void ClearSearch();
void SnapSearchResultToSelection(bool snap) noexcept; void SnapSearchResultToSelection(bool snap) noexcept;

View File

@ -54,6 +54,7 @@ namespace Microsoft.Terminal.Control
Int32 TotalMatches; Int32 TotalMatches;
Int32 CurrentMatch; Int32 CurrentMatch;
Boolean SearchInvalidated; Boolean SearchInvalidated;
Boolean SearchRegexInvalid;
}; };
[default_interface] runtimeclass SelectionColor [default_interface] runtimeclass SelectionColor
@ -134,7 +135,7 @@ namespace Microsoft.Terminal.Control
void ResumeRendering(); void ResumeRendering();
void BlinkAttributeTick(); void BlinkAttributeTick();
SearchResults Search(String text, Boolean goForward, Boolean caseSensitive, Boolean reset); SearchResults Search(String text, Boolean goForward, Boolean caseSensitive, Boolean regularExpression, Boolean reset);
void ClearSearch(); void ClearSearch();
Boolean SnapSearchResultToSelection; Boolean SnapSearchResultToSelection;

View File

@ -304,4 +304,16 @@ Please either install the missing font or choose another one.</value>
<value>Restored</value> <value>Restored</value>
<comment>"Restored" as in "This content was restored"</comment> <comment>"Restored" as in "This content was restored"</comment>
</data> </data>
</root> <data name="SearchBox_RegularExpression.ToolTipService.ToolTip" xml:space="preserve">
<value>Regular Expression</value>
<comment>The tooltip text for the button on the search box control governing the use of "regular expressions" ("regex").</comment>
</data>
<data name="SearchBox_RegularExpression.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Regular Expression Search</value>
<comment>The accessibility description text for the button on the search box control governing the use of "regular expressions" ("regex").</comment>
</data>
<data name="SearchRegexInvalid" xml:space="preserve">
<value>invalid</value>
<comment>This brief message is displayed when a regular expression is invalid.</comment>
</data>
</root>

View File

@ -30,6 +30,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_focusableElements.insert(TextBox()); _focusableElements.insert(TextBox());
_focusableElements.insert(CloseButton()); _focusableElements.insert(CloseButton());
_focusableElements.insert(CaseSensitivityButton()); _focusableElements.insert(CaseSensitivityButton());
_focusableElements.insert(RegexButton());
_focusableElements.insert(GoForwardButton()); _focusableElements.insert(GoForwardButton());
_focusableElements.insert(GoBackwardButton()); _focusableElements.insert(GoBackwardButton());
@ -235,6 +236,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return CaseSensitivityButton().IsChecked().GetBoolean(); return CaseSensitivityButton().IsChecked().GetBoolean();
} }
bool SearchBoxControl::RegularExpression()
{
return RegexButton().IsChecked().GetBoolean();
}
// Method Description: // Method Description:
// - Handler for pressing Enter on TextBox, trigger // - Handler for pressing Enter on TextBox, trigger
// text search // text search
@ -256,11 +262,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto state = CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift); const auto state = CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift);
if (WI_IsFlagSet(state, CoreVirtualKeyStates::Down)) if (WI_IsFlagSet(state, CoreVirtualKeyStates::Down))
{ {
Search.raise(Text(), !GoForward(), CaseSensitive()); Search.raise(Text(), !GoForward(), CaseSensitive(), RegularExpression());
} }
else else
{ {
Search.raise(Text(), GoForward(), CaseSensitive()); Search.raise(Text(), GoForward(), CaseSensitive(), RegularExpression());
} }
e.Handled(true); e.Handled(true);
} }
@ -351,7 +357,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
// kick off search // kick off search
Search.raise(Text(), GoForward(), CaseSensitive()); Search.raise(Text(), GoForward(), CaseSensitive(), RegularExpression());
} }
// Method Description: // Method Description:
@ -372,7 +378,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
// kick off search // kick off search
Search.raise(Text(), GoForward(), CaseSensitive()); Search.raise(Text(), GoForward(), CaseSensitive(), RegularExpression());
} }
// Method Description: // Method Description:
@ -410,7 +416,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none> // - <none>
void SearchBoxControl::TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/) void SearchBoxControl::TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/)
{ {
SearchChanged.raise(Text(), GoForward(), CaseSensitive()); SearchChanged.raise(Text(), GoForward(), CaseSensitive(), RegularExpression());
} }
// Method Description: // Method Description:
@ -422,7 +428,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none> // - <none>
void SearchBoxControl::CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/) void SearchBoxControl::CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/)
{ {
SearchChanged.raise(Text(), GoForward(), CaseSensitive()); SearchChanged.raise(Text(), GoForward(), CaseSensitive(), RegularExpression());
}
void SearchBoxControl::RegexButtonClicked(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/)
{
SearchChanged.raise(Text(), GoForward(), CaseSensitive(), RegularExpression());
} }
// Method Description: // Method Description:
@ -515,7 +526,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double SearchBoxControl::_GetStatusMaxWidth() double SearchBoxControl::_GetStatusMaxWidth()
{ {
const auto fontSize = StatusBox().FontSize(); const auto fontSize = StatusBox().FontSize();
const auto maxLength = std::max({ _TextWidth(_FormatStatus(-1, -1), fontSize), const auto maxLength = std::max({ _TextWidth(RS_(L"SearchRegexInvalid"), fontSize),
_TextWidth(_FormatStatus(-1, -1), fontSize),
_TextWidth(_FormatStatus(0, -1), fontSize), _TextWidth(_FormatStatus(0, -1), fontSize),
_TextWidth(_FormatStatus(MaximumTotalResultsToShowInStatus, MaximumTotalResultsToShowInStatus - 1), fontSize), _TextWidth(_FormatStatus(MaximumTotalResultsToShowInStatus, MaximumTotalResultsToShowInStatus - 1), fontSize),
_TextWidth(_FormatStatus(MaximumTotalResultsToShowInStatus + 1, MaximumTotalResultsToShowInStatus - 1), fontSize), _TextWidth(_FormatStatus(MaximumTotalResultsToShowInStatus + 1, MaximumTotalResultsToShowInStatus - 1), fontSize),
@ -532,9 +544,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - currentMatch - the index of the current match (0-based) // - currentMatch - the index of the current match (0-based)
// Return Value: // Return Value:
// - <none> // - <none>
void SearchBoxControl::SetStatus(int32_t totalMatches, int32_t currentMatch) void SearchBoxControl::SetStatus(int32_t totalMatches, int32_t currentMatch, bool searchRegexInvalid)
{ {
const auto status = _FormatStatus(totalMatches, currentMatch); hstring status;
if (searchRegexInvalid)
{
status = RS_(L"SearchRegexInvalid");
}
else
{
status = _FormatStatus(totalMatches, currentMatch);
}
StatusBox().Text(status); StatusBox().Text(status);
} }

View File

@ -39,10 +39,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::hstring Text(); winrt::hstring Text();
bool GoForward(); bool GoForward();
bool CaseSensitive(); bool CaseSensitive();
bool RegularExpression();
void SetFocusOnTextbox(); void SetFocusOnTextbox();
void PopulateTextbox(const winrt::hstring& text); void PopulateTextbox(const winrt::hstring& text);
bool ContainsFocus(); bool ContainsFocus();
void SetStatus(int32_t totalMatches, int32_t currentMatch); void SetStatus(int32_t totalMatches, int32_t currentMatch, bool searchRegexInvalid);
void ClearStatus(); void ClearStatus();
void GoBackwardClicked(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& /*e*/); void GoBackwardClicked(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::UI::Xaml::RoutedEventArgs& /*e*/);
@ -51,6 +52,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); void TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
void CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e); void CaseSensitivityButtonClicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
void RegexButtonClicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
void SearchBoxPointerPressedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); void SearchBoxPointerPressedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);
void SearchBoxPointerReleasedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e); void SearchBoxPointerReleasedHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e);

View File

@ -3,7 +3,7 @@
namespace Microsoft.Terminal.Control namespace Microsoft.Terminal.Control
{ {
delegate void SearchHandler(String query, Boolean goForward, Boolean isCaseSensitive); delegate void SearchHandler(String query, Boolean goForward, Boolean isCaseSensitive, Boolean regularExpression);
[default_interface] runtimeclass SearchBoxControl : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged [default_interface] runtimeclass SearchBoxControl : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged
{ {
@ -11,7 +11,6 @@ namespace Microsoft.Terminal.Control
void SetFocusOnTextbox(); void SetFocusOnTextbox();
void PopulateTextbox(String text); void PopulateTextbox(String text);
Boolean ContainsFocus(); Boolean ContainsFocus();
void SetStatus(Int32 totalMatches, Int32 currentMatch);
void ClearStatus(); void ClearStatus();
Windows.Foundation.Rect ContentClipRect{ get; }; Windows.Foundation.Rect ContentClipRect{ get; };
Double OpenAnimationStartPoint{ get; }; Double OpenAnimationStartPoint{ get; };

View File

@ -243,6 +243,17 @@
<PathIcon Data="M8.87305 10H7.60156L6.5625 7.25195H2.40625L1.42871 10H0.150391L3.91016 0.197266H5.09961L8.87305 10ZM6.18652 6.21973L4.64844 2.04297C4.59831 1.90625 4.54818 1.6875 4.49805 1.38672H4.4707C4.42513 1.66471 4.37272 1.88346 4.31348 2.04297L2.78906 6.21973H6.18652ZM15.1826 10H14.0615V8.90625H14.0342C13.5465 9.74479 12.8288 10.1641 11.8809 10.1641C11.1836 10.1641 10.6367 9.97949 10.2402 9.61035C9.84831 9.24121 9.65234 8.7513 9.65234 8.14062C9.65234 6.83268 10.4225 6.07161 11.9629 5.85742L14.0615 5.56348C14.0615 4.37402 13.5807 3.7793 12.6191 3.7793C11.776 3.7793 11.015 4.06641 10.3359 4.64062V3.49219C11.0241 3.05469 11.8171 2.83594 12.7148 2.83594C14.36 2.83594 15.1826 3.70638 15.1826 5.44727V10ZM14.0615 6.45898L12.373 6.69141C11.8535 6.76432 11.4616 6.89421 11.1973 7.08105C10.9329 7.26335 10.8008 7.58919 10.8008 8.05859C10.8008 8.40039 10.9215 8.68066 11.1631 8.89941C11.4092 9.11361 11.735 9.2207 12.1406 9.2207C12.6966 9.2207 13.1546 9.02702 13.5146 8.63965C13.8792 8.24772 14.0615 7.75326 14.0615 7.15625V6.45898Z" /> <PathIcon Data="M8.87305 10H7.60156L6.5625 7.25195H2.40625L1.42871 10H0.150391L3.91016 0.197266H5.09961L8.87305 10ZM6.18652 6.21973L4.64844 2.04297C4.59831 1.90625 4.54818 1.6875 4.49805 1.38672H4.4707C4.42513 1.66471 4.37272 1.88346 4.31348 2.04297L2.78906 6.21973H6.18652ZM15.1826 10H14.0615V8.90625H14.0342C13.5465 9.74479 12.8288 10.1641 11.8809 10.1641C11.1836 10.1641 10.6367 9.97949 10.2402 9.61035C9.84831 9.24121 9.65234 8.7513 9.65234 8.14062C9.65234 6.83268 10.4225 6.07161 11.9629 5.85742L14.0615 5.56348C14.0615 4.37402 13.5807 3.7793 12.6191 3.7793C11.776 3.7793 11.015 4.06641 10.3359 4.64062V3.49219C11.0241 3.05469 11.8171 2.83594 12.7148 2.83594C14.36 2.83594 15.1826 3.70638 15.1826 5.44727V10ZM14.0615 6.45898L12.373 6.69141C11.8535 6.76432 11.4616 6.89421 11.1973 7.08105C10.9329 7.26335 10.8008 7.58919 10.8008 8.05859C10.8008 8.40039 10.9215 8.68066 11.1631 8.89941C11.4092 9.11361 11.735 9.2207 12.1406 9.2207C12.6966 9.2207 13.1546 9.02702 13.5146 8.63965C13.8792 8.24772 14.0615 7.75326 14.0615 7.15625V6.45898Z" />
</ToggleButton> </ToggleButton>
<ToggleButton x:Name="RegexButton"
x:Uid="SearchBox_RegularExpression"
Width="32"
Height="32"
Margin="4,0"
Padding="0"
BackgroundSizing="OuterBorderEdge"
Click="RegexButtonClicked">
<PathIcon Data="M 11.7716,12.7647 V 14.2481 H 11.06066 V 12.8194 C 10.08996,12.8194 9.30156,12.62344 8.69546,12.23151 V 11.04201 C 8.95978,11.27443 9.31981,11.4704 9.77556,11.6299 10.23585,11.78485 10.66423,11.86232 11.06076,11.86232 V 8.36232 C 10.05816,7.87925 9.40876,7.43491 9.11256,7.02932 8.81634,6.61916 8.66822,6.13609 8.66822,5.58012 8.66822,4.91931 8.89381,4.34962 9.34498,3.87112 9.79615,3.38805 10.36808,3.09866 11.06078,3.00296 V 1.77246 H 11.77172 V 2.97556 C 12.66495,3.002904 13.28242,3.13051 13.62422,3.35837 V 4.52047 C 13.15938,4.15589 12.54182,3.95992 11.77172,3.93258 V 7.54198 C 12.71964,7.99315 13.36682,8.43065 13.71312,8.85448 14.06403,9.27375 14.23949,9.75227 14.23949,10.28998 14.23949,10.93712 14.02074,11.48168 13.58324,11.92378 13.1503,12.36128 12.54644,12.64155 11.77174,12.7646 Z M 11.06066,7.1592 V 3.9737 C 10.67785,4.051174 10.37479,4.22207 10.15148,4.4864 9.92817,4.74617 9.81652,5.0629 9.81652,5.4366 9.81652,5.82853 9.90767,6.14982 10.08996,6.40047 10.27225,6.65112 10.59582,6.90405 11.06066,7.15926 Z M 11.7716,8.7178 V 11.8076 C 12.65116,11.61619 13.0909,11.14224 13.0909,10.3857 13.0909,9.75679 12.65112,9.2008 11.7716,8.7177 Z M 8.0089,8.5538 H 6.9835 L 4.714,4.3224 H 4.672984 L 2.560684,8.5538 H 1.555784 L 4.440584,2.8526 H 4.884924 Z " />
</ToggleButton>
<Button x:Name="CloseButton" <Button x:Name="CloseButton"
x:Uid="SearchBox_Close" x:Uid="SearchBox_Close"
Width="32" Width="32"

View File

@ -568,7 +568,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
else else
{ {
_handleSearchResults(_core.Search(_searchBox->Text(), goForward, _searchBox->CaseSensitive(), false)); _handleSearchResults(_core.Search(_searchBox->Text(), goForward, _searchBox->CaseSensitive(), _searchBox->RegularExpression(), false));
} }
} }
@ -597,11 +597,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none> // - <none>
void TermControl::_Search(const winrt::hstring& text, void TermControl::_Search(const winrt::hstring& text,
const bool goForward, const bool goForward,
const bool caseSensitive) const bool caseSensitive,
const bool regularExpression)
{ {
if (_searchBox && _searchBox->IsOpen()) if (_searchBox && _searchBox->IsOpen())
{ {
_handleSearchResults(_core.Search(text, goForward, caseSensitive, false)); _handleSearchResults(_core.Search(text, goForward, caseSensitive, regularExpression, false));
} }
} }
@ -615,13 +616,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none> // - <none>
void TermControl::_SearchChanged(const winrt::hstring& text, void TermControl::_SearchChanged(const winrt::hstring& text,
const bool goForward, const bool goForward,
const bool caseSensitive) const bool caseSensitive,
const bool regularExpression)
{ {
if (_searchBox && _searchBox->IsOpen()) if (_searchBox && _searchBox->IsOpen())
{ {
// We only want to update the search results based on the new text. Set // We only want to update the search results based on the new text. Set
// `resetOnly` to true so we don't accidentally update the current match index. // `resetOnly` to true so we don't accidentally update the current match index.
const auto result = _core.Search(text, goForward, caseSensitive, true); const auto result = _core.Search(text, goForward, caseSensitive, regularExpression, true);
_handleSearchResults(result); _handleSearchResults(result);
} }
} }
@ -3747,7 +3749,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto goForward = _searchBox->GoForward(); const auto goForward = _searchBox->GoForward();
const auto caseSensitive = _searchBox->CaseSensitive(); const auto caseSensitive = _searchBox->CaseSensitive();
_handleSearchResults(_core.Search(text, goForward, caseSensitive, true)); const auto regularExpression = _searchBox->RegularExpression();
_handleSearchResults(_core.Search(text, goForward, caseSensitive, regularExpression, true));
} }
void TermControl::_handleSearchResults(SearchResults results) void TermControl::_handleSearchResults(SearchResults results)
@ -3764,7 +3767,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
} }
else else
{ {
_searchBox->SetStatus(results.TotalMatches, results.CurrentMatch); _searchBox->SetStatus(results.TotalMatches, results.CurrentMatch, results.SearchRegexInvalid);
} }
if (results.SearchInvalidated) if (results.SearchInvalidated)

View File

@ -397,8 +397,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const; double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive); void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive, const bool regularExpression);
void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive); void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive, const bool regularExpression);
void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args); void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
void _refreshSearch(); void _refreshSearch();
void _handleSearchResults(SearchResults results); void _handleSearchResults(SearchResults results);

View File

@ -1568,7 +1568,7 @@ void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Termi
if (!textView.empty()) if (!textView.empty())
{ {
const auto hits = textBuffer.SearchText(textView, true); const auto hits = textBuffer.SearchText(textView, SearchFlag::CaseInsensitive).value_or(std::vector<til::point_span>{});
for (const auto& s : hits) for (const auto& s : hits)
{ {
colorSelection(s.start, s.end, attr); colorSelection(s.start, s.end, attr);

View File

@ -83,6 +83,7 @@ BEGIN
EDITTEXT ID_CONSOLE_FINDSTR, 47, 7, 128, 12, WS_GROUP | WS_TABSTOP | ES_AUTOHSCROLL EDITTEXT ID_CONSOLE_FINDSTR, 47, 7, 128, 12, WS_GROUP | WS_TABSTOP | ES_AUTOHSCROLL
AUTOCHECKBOX "Match &case", ID_CONSOLE_FINDCASE, 4, 42, 64, 12 AUTOCHECKBOX "Match &case", ID_CONSOLE_FINDCASE, 4, 42, 64, 12
AUTOCHECKBOX "Regula&r expression", ID_CONSOLE_FINDREGEX, 4, 28, 96, 12
GROUPBOX "Direction", -1, 107, 26, 68, 28, WS_GROUP GROUPBOX "Direction", -1, 107, 26, 68, 28, WS_GROUP
AUTORADIOBUTTON "&Up", ID_CONSOLE_FINDUP, 111, 38, 25, 12, WS_GROUP AUTORADIOBUTTON "&Up", ID_CONSOLE_FINDUP, 111, 38, 25, 12, WS_GROUP

View File

@ -49,5 +49,6 @@ Author(s):
#define ID_CONSOLE_FINDCASE 602 #define ID_CONSOLE_FINDCASE 602
#define ID_CONSOLE_FINDUP 603 #define ID_CONSOLE_FINDUP 603
#define ID_CONSOLE_FINDDOWN 604 #define ID_CONSOLE_FINDDOWN 604
#define ID_CONSOLE_FINDREGEX 605
// clang-format on // clang-format on

View File

@ -701,7 +701,7 @@ bool Selection::_HandleColorSelection(const INPUT_KEY_INFO* const pInputKeyInfo)
ClearSelection(); ClearSelection();
const auto& textBuffer = gci.renderData.GetTextBuffer(); const auto& textBuffer = gci.renderData.GetTextBuffer();
const auto hits = textBuffer.SearchText(str, true); const auto hits = textBuffer.SearchText(str, SearchFlag::CaseInsensitive).value_or(std::vector<til::point_span>{});
for (const auto& s : hits) for (const auto& s : hits)
{ {
ColorSelection(s.start, s.end, selectionAttr); ColorSelection(s.start, s.end, selectionAttr);

View File

@ -98,7 +98,7 @@ class SearchTests
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"AB", false, false); s.Reset(gci.renderData, L"AB", SearchFlag::None, false);
DoFoundChecks(s, {}, 1, false); DoFoundChecks(s, {}, 1, false);
} }
@ -106,7 +106,7 @@ class SearchTests
{ {
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"\x304b", false, false); s.Reset(gci.renderData, L"\x304b", SearchFlag::None, false);
DoFoundChecks(s, { 2, 0 }, 1, false); DoFoundChecks(s, { 2, 0 }, 1, false);
} }
@ -115,7 +115,7 @@ class SearchTests
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"ab", true, false); s.Reset(gci.renderData, L"ab", SearchFlag::CaseInsensitive, false);
DoFoundChecks(s, {}, 1, false); DoFoundChecks(s, {}, 1, false);
} }
@ -123,7 +123,7 @@ class SearchTests
{ {
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"\x304b", true, false); s.Reset(gci.renderData, L"\x304b", SearchFlag::CaseInsensitive, false);
DoFoundChecks(s, { 2, 0 }, 1, false); DoFoundChecks(s, { 2, 0 }, 1, false);
} }
@ -131,7 +131,7 @@ class SearchTests
{ {
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"AB", false, true); s.Reset(gci.renderData, L"AB", SearchFlag::None, true);
DoFoundChecks(s, { 0, 3 }, -1, true); DoFoundChecks(s, { 0, 3 }, -1, true);
} }
@ -139,7 +139,7 @@ class SearchTests
{ {
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"\x304b", false, true); s.Reset(gci.renderData, L"\x304b", SearchFlag::None, true);
DoFoundChecks(s, { 2, 3 }, -1, true); DoFoundChecks(s, { 2, 3 }, -1, true);
} }
@ -147,7 +147,7 @@ class SearchTests
{ {
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"ab", true, true); s.Reset(gci.renderData, L"ab", SearchFlag::CaseInsensitive, true);
DoFoundChecks(s, { 0, 3 }, -1, true); DoFoundChecks(s, { 0, 3 }, -1, true);
} }
@ -155,7 +155,52 @@ class SearchTests
{ {
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s; Search s;
s.Reset(gci.renderData, L"\x304b", true, true); s.Reset(gci.renderData, L"\x304b", SearchFlag::CaseInsensitive, true);
DoFoundChecks(s, { 2, 3 }, -1, true); DoFoundChecks(s, { 2, 3 }, -1, true);
} }
TEST_METHOD(ForwardCaseSensitiveRegex)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s;
s.Reset(gci.renderData, L"[BA]{2}", SearchFlag::RegularExpression, false);
DoFoundChecks(s, {}, 1, false);
}
TEST_METHOD(ForwardCaseSensitiveRegexJapanese)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s;
// N.B. this is not a literal U+30xx, but a regex escape sequence \x{30xx}
s.Reset(gci.renderData, LR"-([\x{3041}-\x{304c}])-", SearchFlag::RegularExpression, false);
DoFoundChecks(s, { 2, 0 }, 1, false);
}
TEST_METHOD(ForwardCaseInsensitiveRegex)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s;
s.Reset(gci.renderData, L"ab", SearchFlag::CaseInsensitive | SearchFlag::RegularExpression, false);
DoFoundChecks(s, {}, 1, false);
}
TEST_METHOD(ForwardCaseInsensitiveRegexJapanese)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s;
// N.B. this is not a literal U+30xx, but a regex escape sequence \x{30xx}
s.Reset(gci.renderData, LR"-([\x{3041}-\x{304c}])-", SearchFlag::CaseInsensitive | SearchFlag::RegularExpression, false);
DoFoundChecks(s, { 2, 0 }, 1, false);
}
TEST_METHOD(ForwardCaseSensitiveRegexWithCaseInsensitiveFlag)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Search s;
s.Reset(gci.renderData, L"(?i)ab", SearchFlag::RegularExpression, false);
DoFoundChecks(s, {}, 1, false);
}
}; };

View File

@ -23,7 +23,7 @@ INT_PTR CALLBACK FindDialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM l
// This bool is used to track which option - up or down - was used to perform the last search. That way, the next time the // This bool is used to track which option - up or down - was used to perform the last search. That way, the next time the
// find dialog is opened, it will default to the last used option. // find dialog is opened, it will default to the last used option.
static auto reverse = true; static auto reverse = true;
static auto caseInsensitive = true; static SearchFlag flags{ SearchFlag::CaseInsensitive };
static std::wstring lastFindString; static std::wstring lastFindString;
static Search searcher; static Search searcher;
@ -32,7 +32,8 @@ INT_PTR CALLBACK FindDialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM l
case WM_INITDIALOG: case WM_INITDIALOG:
SetWindowLongPtrW(hWnd, DWLP_USER, lParam); SetWindowLongPtrW(hWnd, DWLP_USER, lParam);
CheckRadioButton(hWnd, ID_CONSOLE_FINDUP, ID_CONSOLE_FINDDOWN, (reverse ? ID_CONSOLE_FINDUP : ID_CONSOLE_FINDDOWN)); CheckRadioButton(hWnd, ID_CONSOLE_FINDUP, ID_CONSOLE_FINDDOWN, (reverse ? ID_CONSOLE_FINDUP : ID_CONSOLE_FINDDOWN));
CheckDlgButton(hWnd, ID_CONSOLE_FINDCASE, !caseInsensitive); CheckDlgButton(hWnd, ID_CONSOLE_FINDCASE, WI_IsFlagClear(flags, SearchFlag::CaseInsensitive));
CheckDlgButton(hWnd, ID_CONSOLE_FINDREGEX, WI_IsFlagSet(flags, SearchFlag::RegularExpression));
SetDlgItemTextW(hWnd, ID_CONSOLE_FINDSTR, lastFindString.c_str()); SetDlgItemTextW(hWnd, ID_CONSOLE_FINDSTR, lastFindString.c_str());
return TRUE; return TRUE;
case WM_COMMAND: case WM_COMMAND:
@ -46,15 +47,16 @@ INT_PTR CALLBACK FindDialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM l
length = GetDlgItemTextW(hWnd, ID_CONSOLE_FINDSTR, lastFindString.data(), gsl::narrow_cast<int>(length + 1)); length = GetDlgItemTextW(hWnd, ID_CONSOLE_FINDSTR, lastFindString.data(), gsl::narrow_cast<int>(length + 1));
lastFindString.resize(length); lastFindString.resize(length);
caseInsensitive = IsDlgButtonChecked(hWnd, ID_CONSOLE_FINDCASE) == 0; WI_UpdateFlag(flags, SearchFlag::CaseInsensitive, IsDlgButtonChecked(hWnd, ID_CONSOLE_FINDCASE) == 0);
WI_UpdateFlag(flags, SearchFlag::RegularExpression, IsDlgButtonChecked(hWnd, ID_CONSOLE_FINDREGEX) != 0);
reverse = IsDlgButtonChecked(hWnd, ID_CONSOLE_FINDDOWN) == 0; reverse = IsDlgButtonChecked(hWnd, ID_CONSOLE_FINDDOWN) == 0;
LockConsole(); LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
if (searcher.IsStale(gci.renderData, lastFindString, caseInsensitive)) if (searcher.IsStale(gci.renderData, lastFindString, flags))
{ {
searcher.Reset(gci.renderData, lastFindString, caseInsensitive, reverse); searcher.Reset(gci.renderData, lastFindString, flags, reverse);
searcher.MoveToCurrentSelection(); searcher.MoveToCurrentSelection();
} }
else else

View File

@ -46,5 +46,6 @@ Author(s):
#define ID_CONSOLE_FINDCASE 602 #define ID_CONSOLE_FINDCASE 602
#define ID_CONSOLE_FINDUP 603 #define ID_CONSOLE_FINDUP 603
#define ID_CONSOLE_FINDDOWN 604 #define ID_CONSOLE_FINDDOWN 604
#define ID_CONSOLE_FINDREGEX 605
// clang-format on // clang-format on

View File

@ -625,9 +625,9 @@ try
// -> We need to turn [_beg,_end) into (_beg,_end). // -> We need to turn [_beg,_end) into (_beg,_end).
exclusiveBegin.x--; exclusiveBegin.x--;
if (_searcher.IsStale(*_pData, queryText, ignoreCase)) if (_searcher.IsStale(*_pData, queryText, ignoreCase ? SearchFlag::CaseInsensitive : SearchFlag::None))
{ {
_searcher.Reset(*_pData, queryText, ignoreCase, searchBackward); _searcher.Reset(*_pData, queryText, ignoreCase ? SearchFlag::CaseInsensitive : SearchFlag::None, searchBackward);
} }
_searcher.MovePastPoint(searchBackward ? _end : exclusiveBegin); _searcher.MovePastPoint(searchBackward ? _end : exclusiveBegin);