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

Allow regex search #9228

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 85 additions & 14 deletions src/buffer/out/search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ using namespace Microsoft::Console::Types;
Search::Search(IUiaData& uiaData,
const std::wstring& str,
const Direction direction,
const Sensitivity sensitivity) :
const Sensitivity sensitivity,
bool regex) :
_direction(direction),
_sensitivity(sensitivity),
_needle(s_CreateNeedleFromString(str)),
_uiaData(uiaData),
_regex(regex),
_inputString(str),
_coordAnchor(s_GetInitialAnchor(uiaData, direction))
{
_coordNext = _coordAnchor;
Expand All @@ -50,11 +53,14 @@ Search::Search(IUiaData& uiaData,
const std::wstring& str,
const Direction direction,
const Sensitivity sensitivity,
const COORD anchor) :
const COORD anchor,
bool regex) :
_direction(direction),
_sensitivity(sensitivity),
_needle(s_CreateNeedleFromString(str)),
_coordAnchor(anchor),
_regex(regex),
_inputString(str),
_uiaData(uiaData)
{
_coordNext = _coordAnchor;
Expand All @@ -75,20 +81,31 @@ bool Search::FindNext()
return false;
}

do
if (_regex)
{
if (_FindNeedleInHaystackAt(_coordNext, _coordSelStart, _coordSelEnd))
{
_UpdateNextPosition();
_reachedEnd = _coordNext == _coordAnchor;
return true;
}
else
auto start = _direction == Direction::Forward ? _coordNext : COORD{ 0 };
auto end = _direction == Direction::Forward ? _uiaData.GetTextBufferEndPosition() : _coordNext;
auto wrapStart = _direction == Direction::Forward ? COORD{ 0 } : _coordNext;
auto wrapEnd = _direction == Direction::Forward ? _coordNext : _uiaData.GetTextBufferEndPosition();
return _RegexHelper(start, end) || _RegexHelper(wrapStart, wrapEnd);
}
else
{
do
{
_UpdateNextPosition();
}

} while (_coordNext != _coordAnchor);
if (_FindNeedleInHaystackAt(_coordNext, _coordSelStart, _coordSelEnd))
{
_UpdateNextPosition();
_reachedEnd = _coordNext == _coordAnchor;
return true;
}
else
{
_UpdateNextPosition();
}

} while (_coordNext != _coordAnchor);
}

return false;
}
Expand Down Expand Up @@ -344,3 +361,57 @@ std::vector<std::vector<wchar_t>> Search::s_CreateNeedleFromString(const std::ws
}
return cells;
}

bool Search::_RegexHelper(COORD start, COORD end)
{
std::wstring concatAll;
auto begin = start;
std::wsmatch match;

// To deal with text that spans multiple lines, we will first concatenate
// all the text into one string and find the regex in that string
while (begin != end)
{
concatAll += *_uiaData.GetTextBuffer().GetTextDataAt(begin);
_IncrementCoord(begin);
}

// Create the regex object and iterator
std::wregex regexObj{ _inputString };
auto matches_begin = std::wsregex_iterator(concatAll.begin(), concatAll.end(), regexObj);

Choose a reason for hiding this comment

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

If the user accidentally types a pathological regex and the search takes too long, is there any way to abort it? It doesn't look like std::regex_iterator supports any kind of cancellation token.

Choose a reason for hiding this comment

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

I wonder if cancellation could be implemented by throwing an exception from operators of a custom iterator type. Would there be a risk that such an exception can skip some cleanup in the regexp algorithm, or that the algorithm can copy part of the input to an internal buffer and then spend excessive time examining that buffer, without using the original iterators?

auto matches_end = std::wsregex_iterator();

// If we found a match, get its position and length to update the selection coordinates
if (matches_begin != matches_end)
{
std::wsregex_iterator desired;
if (_direction == Direction::Forward)
{
desired = matches_begin;
}
else
{
// Regex iterators are not bidirectional, so we cannot create a reverse iterator out of one
// So, if we want to find the previous match, we need to manually progress the iterator
while (matches_begin != matches_end)
{
desired = matches_begin;
++matches_begin;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There really should be a better way to do this

}
const auto pos = desired->position();
for (auto i = 0; i < pos; ++i)
{
_IncrementCoord(start);
}
PankajBhojwani marked this conversation as resolved.
Show resolved Hide resolved
_coordSelStart = start;
const auto len = desired->length();
for (auto i = 0; i < (len - 1); ++i)
{
_IncrementCoord(start);
}
_coordSelEnd = start;
return true;
}
return false;
}
10 changes: 8 additions & 2 deletions src/buffer/out/search.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ class Search final
Search(Microsoft::Console::Types::IUiaData& uiaData,
const std::wstring& str,
const Direction dir,
const Sensitivity sensitivity);
const Sensitivity sensitivity,
bool regex = false);

Search(Microsoft::Console::Types::IUiaData& uiaData,
const std::wstring& str,
const Direction dir,
const Sensitivity sensitivity,
const COORD anchor);
const COORD anchor,
bool regex = false);

bool FindNext();
void Select() const;
Expand All @@ -70,6 +72,8 @@ class Search final

static std::vector<std::vector<wchar_t>> s_CreateNeedleFromString(const std::wstring& wstr);

bool _RegexHelper(COORD start, COORD end);

bool _reachedEnd = false;
COORD _coordNext = { 0 };
COORD _coordSelStart = { 0 };
Expand All @@ -79,6 +83,8 @@ class Search final
const std::vector<std::vector<wchar_t>> _needle;
const Direction _direction;
const Sensitivity _sensitivity;
const bool _regex;
const std::wstring _inputString;
Microsoft::Console::Types::IUiaData& _uiaData;

#ifdef UNIT_TESTING
Expand Down
8 changes: 8 additions & 0 deletions src/cascadia/TerminalControl/Resources/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@
<value>Match Case</value>
<comment>The tooltip text for the case sensitivity button on the search box control.</comment>
</data>
<data name="SearchBox_Regex.ToolTipService.ToolTip" xml:space="preserve">
<value>Treat the search text as a regular expression</value>
<comment>The tooltip text for the regex button on the search box control.</comment>
</data>
<data name="SearchBox_Close.ToolTipService.ToolTip" xml:space="preserve">
<value>Close</value>
<comment>The tooltip text for the close button on the search box control.</comment>
Expand Down Expand Up @@ -149,6 +153,10 @@
<value>Case Sensitivity</value>
<comment>The name of the case sensitivity button on the search box control for accessibility.</comment>
</data>
<data name="SearchBox_Regex.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Regex</value>
<comment>The name of the regex button on the search box control for accessibility.</comment>
</data>
<data name="SearchBox_SearchForwards.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Search Forward</value>
<comment>The name of the search forward button for accessibility.</comment>
Expand Down
19 changes: 15 additions & 4 deletions src/cascadia/TerminalControl/SearchBoxControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
_focusableElements.insert(TextBox());
_focusableElements.insert(CloseButton());
_focusableElements.insert(CaseSensitivityButton());
_focusableElements.insert(RegexButton());
_focusableElements.insert(GoForwardButton());
_focusableElements.insert(GoBackwardButton());
}
Expand Down Expand Up @@ -50,6 +51,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
return CaseSensitivityButton().IsChecked().GetBoolean();
}

// Method Description:
// - Check if the current search is a regex one
// Return Value:
// - bool: whether the current search is case sensitive (case button is checked )
// or not
PankajBhojwani marked this conversation as resolved.
Show resolved Hide resolved
bool SearchBoxControl::_IsRegex()
{
return RegexButton().IsChecked().GetBoolean();
}

// Method Description:
// - Handler for pressing Enter on TextBox, trigger
// text search
Expand All @@ -65,11 +76,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
auto const state = CoreWindow::GetForCurrentThread().GetKeyState(winrt::Windows::System::VirtualKey::Shift);
if (WI_IsFlagSet(state, CoreVirtualKeyStates::Down))
{
_SearchHandlers(TextBox().Text(), !_GoForward(), _CaseSensitive());
_SearchHandlers(TextBox().Text(), !_GoForward(), _CaseSensitive(), _IsRegex());
}
else
{
_SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive());
_SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive(), _IsRegex());
}
e.Handled(true);
}
Expand Down Expand Up @@ -160,7 +171,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}

// kick off search
_SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive());
_SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive(), _IsRegex());
}

// Method Description:
Expand All @@ -181,7 +192,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}

// kick off search
_SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive());
_SearchHandlers(TextBox().Text(), _GoForward(), _CaseSensitive(), _IsRegex());
}

// Method Description:
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalControl/SearchBoxControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation

bool _GoForward();
bool _CaseSensitive();
bool _IsRegex();
void _KeyDownHandler(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e);
void _CharacterHandler(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::Input::CharacterReceivedRoutedEventArgs const& e);
};
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalControl/SearchBoxControl.idl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Microsoft.Terminal.TerminalControl
{
delegate void SearchHandler(String query, Boolean goForward, Boolean isCaseSensitive);
delegate void SearchHandler(String query, Boolean goForward, Boolean isCaseSensitive, Boolean isRegex);

[default_interface] runtimeclass SearchBoxControl : Windows.UI.Xaml.Controls.UserControl
{
Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/TerminalControl/SearchBoxControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@
<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 x:Name="RegexButton"
x:Uid="SearchBox_Regex"
Style="{StaticResource ToggleButtonStyle}">
<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>

<Button x:Name="CloseButton"
x:Uid="SearchBox_Close"
Padding="0"
Expand Down
7 changes: 4 additions & 3 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
else
{
_Search(_searchBox->TextBox().Text(), goForward, false);
_Search(_searchBox->TextBox().Text(), goForward, false, false);
}
}

Expand All @@ -248,7 +248,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
// - <none>
void TermControl::_Search(const winrt::hstring& text,
const bool goForward,
const bool caseSensitive)
const bool caseSensitive,
const bool regex)
{
if (text.size() == 0 || _closing)
{
Expand All @@ -263,7 +264,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
Search::Sensitivity::CaseSensitive :
Search::Sensitivity::CaseInsensitive;

Search search(*GetUiaData(), text.c_str(), direction, sensitivity);
Search search(*GetUiaData(), text.c_str(), direction, sensitivity, regex);
auto lock = _terminal->LockForWriting();
if (search.FindNext())
{
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
const unsigned int _NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime);
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 regex);
void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, Windows::UI::Xaml::RoutedEventArgs const& args);

// TSFInputControl Handlers
Expand Down