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

Fuzzy Search #16586

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 15 additions & 0 deletions src/buffer/out/UTextAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,18 @@ til::point_span Microsoft::Console::ICU::BufferRangeFromMatch(UText* ut, URegula

return ret;
}

UText Microsoft::Console::ICU::UTextForWrappableRow(const TextBuffer& textBuffer, til::CoordType& row) noexcept
{
const auto startRow = row;
auto length = 0;
while (textBuffer.GetRowByOffset(row).WasWrapForced())
{
row++;
length += textBuffer.GetRowByOffset(row).size();
}
length += textBuffer.GetRowByOffset(row).size();
const auto ut = UTextFromTextBuffer(textBuffer, startRow, row + 1);

return ut;
}
1 change: 1 addition & 0 deletions src/buffer/out/UTextAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Microsoft::Console::ICU
using unique_uregex = wistd::unique_ptr<URegularExpression, wil::function_deleter<decltype(&uregex_close), &uregex_close>>;

UText UTextFromTextBuffer(const TextBuffer& textBuffer, til::CoordType rowBeg, til::CoordType rowEnd) noexcept;
UText UTextForWrappableRow(const TextBuffer& textBuffer, til::CoordType& row) noexcept;
unique_uregex CreateRegex(const std::wstring_view& pattern, uint32_t flags, UErrorCode* status) noexcept;
til::point_span BufferRangeFromMatch(UText* ut, URegularExpression* re);
}
20 changes: 20 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "TerminalPage.h"
#include "ScratchpadContent.h"
#include "FuzzySearchPane.h"
#include "../WinRTUtils/inc/WtExeUtils.h"
#include "../../types/inc/utils.hpp"
#include "Utils.h"
Expand Down Expand Up @@ -1467,6 +1468,25 @@ namespace winrt::TerminalApp::implementation
}
}

void TerminalPage::_HandleOpenFuzzySearch(const IInspectable& sender,
const ActionEventArgs& args)
{
if (Feature_FuzzySearch::IsEnabled())
{
const auto& fuzzySearchPane{ winrt::make_self<FuzzySearchPane>(_GetActiveControl()) };

// This is maybe a little wacky - add our key event handler to the pane
// we made. So that we can get actions for keys that the content didn't
// handle.
fuzzySearchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });

const auto resultPane = std::make_shared<Pane>(*fuzzySearchPane);
_SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane);
fuzzySearchPane->Focus();
args.Handled(true);
}
}

void TerminalPage::_HandleOpenAbout(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
Expand Down
171 changes: 171 additions & 0 deletions src/cascadia/TerminalApp/FuzzySearchPane.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "FuzzySearchPane.h"

using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Microsoft::Terminal::Settings::Model;

namespace winrt::TerminalApp::implementation
{
FuzzySearchPane::FuzzySearchPane(const winrt::Microsoft::Terminal::Control::TermControl& control)
{
_control = control;
_root = winrt::Windows::UI::Xaml::Controls::Grid{};
// Vertical and HorizontalAlignment are Stretch by default

auto res = Windows::UI::Xaml::Application::Current().Resources();
auto bg = res.Lookup(winrt::box_value(L"UnfocusedBorderBrush"));
_root.Background(bg.try_as<Media::Brush>());

const Controls::RowDefinition row1;
row1.Height(GridLengthHelper::FromValueAndType(1, GridUnitType::Star));
_root.RowDefinitions().Append(row1);

const Controls::RowDefinition row2;
row2.Height(GridLengthHelper::Auto());
_root.RowDefinitions().Append(row2);

_listBox = Controls::ListBox{};
_listBox.Margin({ 10, 10, 10, 10 });
_root.Children().Append(_listBox);

_searchBox = Controls::TextBox{};
_root.Children().Append(_searchBox);

_searchBox.TextChanged({ this, &FuzzySearchPane::OnTextChanged });
_searchBox.KeyDown({ this, &FuzzySearchPane::OnKeyUp });

Controls::Grid::SetRow(_listBox, 0);
Controls::Grid::SetRow(_searchBox, 1);
}

void FuzzySearchPane::OnKeyUp(Windows::Foundation::IInspectable const&, Input::KeyRoutedEventArgs const& e)
{
if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Down || e.OriginalKey() == winrt::Windows::System::VirtualKey::Up)
{
auto selectedIndex = _listBox.SelectedIndex();

if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Down)
{
selectedIndex++;
}
else if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Up)
{
selectedIndex--;
}

if (selectedIndex >= 0 && selectedIndex < static_cast<int32_t>(_listBox.Items().Size()))
{
_listBox.SelectedIndex(selectedIndex);
_listBox.ScrollIntoView(Controls::ListBox().SelectedItem());
}

e.Handled(true);
}
else if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Enter)
{
if (const auto selectedItem = _listBox.SelectedItem())
{
if (const auto listBoxItem = selectedItem.try_as<Controls::ListBoxItem>())
{
if (const auto fuzzyMatch = listBoxItem.DataContext().try_as<winrt::Microsoft::Terminal::Control::FuzzySearchTextLine>())
{
_control.SelectChar(fuzzyMatch.FirstPosition());
_control.Focus(FocusState::Programmatic);
e.Handled(true);
}
}
}
}
else if (e.OriginalKey() == winrt::Windows::System::VirtualKey::Escape)
{
Close();
e.Handled(true);
}
}

void FuzzySearchPane::OnTextChanged(Windows::Foundation::IInspectable const&, Controls::TextChangedEventArgs const&) const
{
_listBox.Items().Clear();
const auto needle = _searchBox.Text();
const auto fuzzySearchResult = _control.FuzzySearch(needle);
if (fuzzySearchResult.NumberOfResults() > 0)
{
for (auto fuzzyMatch : fuzzySearchResult.Results())
{
auto control = Controls::TextBlock{};
const auto inlinesCollection = control.Inlines();
inlinesCollection.Clear();

for (const auto& match : fuzzyMatch.Segments())
{
const auto matchText = match.TextSegment();
const auto fontWeight = match.IsHighlighted() ? Windows::UI::Text::FontWeights::Bold() : Windows::UI::Text::FontWeights::Normal();

Media::SolidColorBrush foregroundBrush;

if (match.IsHighlighted())
{
foregroundBrush.Color(Windows::UI::Colors::OrangeRed());
}
else
{
foregroundBrush.Color(Windows::UI::Colors::White());
}

Documents::Run run;
run.Text(matchText);
run.FontWeight(fontWeight);
run.Foreground(foregroundBrush);
inlinesCollection.Append(run);
}
auto lbi = Controls::ListBoxItem();
lbi.DataContext(fuzzyMatch);
lbi.Content(control);
_listBox.Items().Append(lbi);
}
_listBox.SelectedIndex(0);
}
}

void FuzzySearchPane::UpdateSettings(const CascadiaSettings& /*settings*/)
{
// Nothing to do.
}

winrt::Windows::UI::Xaml::FrameworkElement FuzzySearchPane::GetRoot()
{
return _root;
}
winrt::Windows::Foundation::Size FuzzySearchPane::MinimumSize()
{
return { 1, 1 };
}
void FuzzySearchPane::Focus(winrt::Windows::UI::Xaml::FocusState reason)
{
_searchBox.Focus(reason);
}
void FuzzySearchPane::Close()
{
CloseRequested.raise(*this, nullptr);
}

INewContentArgs FuzzySearchPane::GetNewTerminalArgs(const BuildStartupKind /* kind */) const
{
return BaseContentArgs(L"scratchpad");
}

winrt::hstring FuzzySearchPane::Icon() const
{
static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
return winrt::hstring{ glyph };
}

winrt::Windows::UI::Xaml::Media::Brush FuzzySearchPane::BackgroundBrush()
{
return _root.Background();
}
}
50 changes: 50 additions & 0 deletions src/cascadia/TerminalApp/FuzzySearchPane.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once
#include "winrt/TerminalApp.h"

namespace winrt::TerminalApp::implementation
{
class FuzzySearchPane : public winrt::implements<FuzzySearchPane, IPaneContent>
{
public:
FuzzySearchPane(const winrt::Microsoft::Terminal::Control::TermControl& control);
void TextBoxTextChanged(winrt::Windows::Foundation::IInspectable const& /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const& /*e*/);
void OnTextChanged(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Controls::TextChangedEventArgs const&) const;
void OnKeyUp(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::Input::KeyRoutedEventArgs const&);

winrt::Windows::UI::Xaml::FrameworkElement GetRoot();

void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);

winrt::Windows::Foundation::Size MinimumSize();

void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
void Close();
winrt::Microsoft::Terminal::Settings::Model::INewContentArgs GetNewTerminalArgs(BuildStartupKind kind) const;

winrt::hstring Title() { return L"FuzzySearch"; }
uint64_t TaskbarState() { return 0; }
uint64_t TaskbarProgress() { return 0; }
bool ReadOnly() { return false; }
winrt::hstring Icon() const;
Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();

til::typed_event<> ConnectionStateChanged;
til::typed_event<IPaneContent> CloseRequested;
til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
til::typed_event<IPaneContent> TitleChanged;
til::typed_event<IPaneContent> TabColorChanged;
til::typed_event<IPaneContent> TaskbarProgressChanged;
til::typed_event<IPaneContent> ReadOnlyChanged;
til::typed_event<IPaneContent> FocusRequested;

private:
winrt::Windows::UI::Xaml::Controls::Grid _root{ nullptr };
winrt::Windows::UI::Xaml::Controls::ListBox _listBox{ nullptr };
winrt::Windows::UI::Xaml::Controls::TextBox _searchBox{ nullptr };
winrt::Microsoft::Terminal::Control::TermControl _control{ nullptr };
};
}
11 changes: 7 additions & 4 deletions src/cascadia/TerminalApp/TerminalAppLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@
<ClInclude Include="ScratchpadContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="FuzzySearchPane.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
<ClInclude Include="SettingsPaneContent.h">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClInclude>
Expand Down Expand Up @@ -274,6 +277,9 @@
<ClCompile Include="ScratchpadContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="FuzzySearchPane.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
<ClCompile Include="SettingsPaneContent.cpp">
<DependentUpon>TerminalPaneContent.idl</DependentUpon>
</ClCompile>
Expand Down Expand Up @@ -400,7 +406,6 @@
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\UIHelpers\UIHelpers.vcxproj">
<Project>{6515F03F-E56D-4DB4-B23D-AC4FB80DB36F}</Project>
</ProjectReference>

</ItemGroup>
<PropertyGroup>
<!-- This is a hack to get the ARM64 CI build working. See
Expand Down Expand Up @@ -466,10 +471,8 @@
</ItemDefinitionGroup>
<!-- ========================= Globals ======================== -->
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />

<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />

<!--
By default, the PRI file will contain resource paths beginning with the
project name. Since we enabled XBF embedding, this *also* includes App.xbf.
Expand All @@ -490,4 +493,4 @@
</ItemGroup>
</Target>
<Import Project="$(SolutionDir)build\rules\CollectWildcardResources.targets" />
</Project>
</Project>
Loading
Loading