Skip to content

Commit cfaa09d

Browse files
committed
moar progress! (useTransitions -> crash!)
1 parent a2e57b4 commit cfaa09d

File tree

15 files changed

+425
-169
lines changed

15 files changed

+425
-169
lines changed

src/cascadia/TerminalApp/TerminalPage.cpp

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,8 @@ namespace winrt::TerminalApp::implementation
16701670

16711671
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
16721672

1673+
term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler });
1674+
16731675
// Don't even register for the event if the feature is compiled off.
16741676
if constexpr (Feature_ShellCompletions::IsEnabled())
16751677
{
@@ -1688,6 +1690,12 @@ namespace winrt::TerminalApp::implementation
16881690
page->_PopulateContextMenu(weakTerm.get(), sender.try_as<MUX::Controls::CommandBarFlyout>(), true);
16891691
}
16901692
});
1693+
term.QuickFixMenu().Opening([weak = get_weak(), weakTerm](auto&& sender, auto&& /*args*/) {
1694+
if (const auto& page{ weak.get() })
1695+
{
1696+
page->_PopulateQuickFixMenu(weakTerm.get(), sender.try_as<Controls::MenuFlyout>());
1697+
}
1698+
});
16911699
}
16921700

16931701
// Method Description:
@@ -2889,6 +2897,180 @@ namespace winrt::TerminalApp::implementation
28892897
ShowWindowChanged.raise(*this, args);
28902898
}
28912899

2900+
winrt::fire_and_forget TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
2901+
{
2902+
assert(!Dispatcher().HasThreadAccess());
2903+
2904+
#if 0
2905+
static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597
2906+
static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A
2907+
static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000
2908+
2909+
static constexpr unsigned int maxSuggestions = 5;
2910+
bool tooManySuggestions = false;
2911+
2912+
// TODO CARLOS: this is where we fail! "Class not registered" error
2913+
PackageManager pkgManager = winrt::create_instance<PackageManager>(CLSID_PackageManager, CLSCTX_ALL);
2914+
auto catalogRef = pkgManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog);
2915+
auto connectResult = catalogRef.Connect();
2916+
int retryCount = 0;
2917+
while (connectResult.Status() != ConnectResultStatus::Ok && retryCount < 3)
2918+
{
2919+
connectResult = catalogRef.Connect();
2920+
++retryCount;
2921+
}
2922+
if (connectResult.Status() != ConnectResultStatus::Ok)
2923+
{
2924+
return;
2925+
}
2926+
auto catalog = connectResult.PackageCatalog();
2927+
2928+
// Perform the query (search by command)
2929+
auto packageMatchFilter = winrt::create_instance<PackageMatchFilter>(CLSID_PackageMatchFilter, CLSCTX_ALL);
2930+
auto findPackagesOptions = winrt::create_instance<FindPackagesOptions>(CLSID_FindPackagesOptions, CLSCTX_ALL);
2931+
2932+
// Helper lambda to apply a filter to the query
2933+
auto applyPackageMatchFilter = [&packageMatchFilter, &findPackagesOptions](PackageMatchField field, PackageFieldMatchOption matchOption, hstring query) {
2934+
// Configure filter
2935+
packageMatchFilter.Field(field);
2936+
packageMatchFilter.Option(matchOption);
2937+
packageMatchFilter.Value(query);
2938+
2939+
// Apply filter
2940+
findPackagesOptions.ResultLimit(maxSuggestions + 1u);
2941+
findPackagesOptions.Filters().Clear();
2942+
findPackagesOptions.Filters().Append(packageMatchFilter);
2943+
};
2944+
2945+
// Helper lambda to retrieve the best matching package(s) from the query's result
2946+
auto tryGetBestMatchingPackage = [&tooManySuggestions](IVectorView<MatchResult> matches) {
2947+
std::vector<CatalogPackage> results;
2948+
results.reserve(std::min(matches.Size(), maxSuggestions));
2949+
if (matches.Size() == 1)
2950+
{
2951+
// One match --> return the package
2952+
results.emplace_back(matches.GetAt(0).CatalogPackage());
2953+
}
2954+
else if (matches.Size() > 1)
2955+
{
2956+
// Multiple matches --> display top 5 matches (prioritize best matches first)
2957+
std::queue<CatalogPackage> bestExactMatches, secondaryMatches, tertiaryMatches;
2958+
for (auto match : matches)
2959+
{
2960+
switch (match.MatchCriteria().Option())
2961+
{
2962+
case PackageFieldMatchOption::EqualsCaseInsensitive:
2963+
case PackageFieldMatchOption::Equals:
2964+
bestExactMatches.push(match.CatalogPackage());
2965+
break;
2966+
case PackageFieldMatchOption::StartsWithCaseInsensitive:
2967+
secondaryMatches.push(match.CatalogPackage());
2968+
break;
2969+
case PackageFieldMatchOption::ContainsCaseInsensitive:
2970+
tertiaryMatches.push(match.CatalogPackage());
2971+
break;
2972+
}
2973+
}
2974+
2975+
// Now return the top maxSuggestions
2976+
while (results.size() < maxSuggestions)
2977+
{
2978+
if (bestExactMatches.size() > 0)
2979+
{
2980+
results.emplace_back(bestExactMatches.front());
2981+
bestExactMatches.pop();
2982+
}
2983+
else if (secondaryMatches.size() > 0)
2984+
{
2985+
results.emplace_back(secondaryMatches.front());
2986+
secondaryMatches.pop();
2987+
}
2988+
else if (tertiaryMatches.size() > 0)
2989+
{
2990+
results.emplace_back(tertiaryMatches.front());
2991+
tertiaryMatches.pop();
2992+
}
2993+
else
2994+
{
2995+
break;
2996+
}
2997+
}
2998+
}
2999+
tooManySuggestions = matches.Size() > maxSuggestions;
3000+
return results;
3001+
};
3002+
3003+
// Search by command
3004+
auto missingCmd = args.MissingCommand();
3005+
std::wstring searchOption = L"command";
3006+
applyPackageMatchFilter(PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, missingCmd);
3007+
auto findPackagesResult = catalog.FindPackages(findPackagesOptions);
3008+
auto matches = findPackagesResult.Matches();
3009+
auto pkgList = tryGetBestMatchingPackage(matches);
3010+
if (pkgList.empty())
3011+
{
3012+
// No matches found --> search by name
3013+
applyPackageMatchFilter(PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd);
3014+
3015+
findPackagesResult = catalog.FindPackages(findPackagesOptions);
3016+
matches = findPackagesResult.Matches();
3017+
pkgList = tryGetBestMatchingPackage(matches);
3018+
searchOption = L"name";
3019+
3020+
if (pkgList.empty())
3021+
{
3022+
// No matches found --> search by moniker
3023+
applyPackageMatchFilter(PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd);
3024+
3025+
// Perform the query (search by name)
3026+
findPackagesResult = catalog.FindPackages(findPackagesOptions);
3027+
matches = findPackagesResult.Matches();
3028+
pkgList = tryGetBestMatchingPackage(matches);
3029+
searchOption = L"moniker";
3030+
}
3031+
}
3032+
3033+
// Display packages in UI
3034+
if (!pkgList.empty())
3035+
{
3036+
std::vector<std::wstring> suggestions;
3037+
suggestions.reserve(pkgList.size());
3038+
for (auto pkg : pkgList)
3039+
{
3040+
suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg.Id()));
3041+
}
3042+
3043+
std::wstring footer = tooManySuggestions ?
3044+
fmt::format(L"winget search --{} {}", searchOption, missingCmd) :
3045+
L"";
3046+
3047+
// TODO CARLOS: no more info bar; replace!
3048+
//ShowCommandNotFoundInfoBar(suggestions, footer);
3049+
}
3050+
#elif defined(DEBUG) || defined(_DEBUG) || defined(DBG)
3051+
//const bool tooManySuggestions = false;
3052+
//const std::wstring searchOption = L"command";
3053+
//const std::wstring missingCmd = args.MissingCommand().data();
3054+
std::vector<std::wstring> pkgList = { L"pkg1", L"pkg2", L"pkg3" };
3055+
std::vector<hstring> suggestions;
3056+
suggestions.reserve(pkgList.size());
3057+
for (auto pkg : pkgList)
3058+
{
3059+
suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg));
3060+
}
3061+
3062+
co_await wil::resume_foreground(Dispatcher());
3063+
3064+
auto term = _GetActiveControl();
3065+
if (!term)
3066+
{
3067+
co_return;
3068+
}
3069+
term.UpdateWinGetSuggestions(single_threaded_vector<hstring>(std::move(suggestions)));
3070+
term.ShowQuickFixMenu();
3071+
#endif
3072+
}
3073+
28923074
// Method Description:
28933075
// - Paste text from the Windows Clipboard to the focused terminal
28943076
void TerminalPage::_PasteText()
@@ -4806,6 +4988,58 @@ namespace winrt::TerminalApp::implementation
48064988
makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } });
48074989
}
48084990

4991+
void TerminalPage::_PopulateQuickFixMenu(const TermControl& control,
4992+
const Controls::MenuFlyout& menu)
4993+
{
4994+
if (!control || !menu)
4995+
{
4996+
return;
4997+
}
4998+
4999+
// Helper lambda for dispatching an ActionAndArgs onto the
5000+
// ShortcutActionDispatch. Used below to wire up each menu entry to the
5001+
// respective action.
5002+
5003+
auto weak = get_weak();
5004+
auto makeCallback = [weak](const ActionAndArgs& actionAndArgs) {
5005+
return [weak, actionAndArgs](auto&&, auto&&) {
5006+
if (auto page{ weak.get() })
5007+
{
5008+
page->_actionDispatch->DoAction(actionAndArgs);
5009+
}
5010+
};
5011+
};
5012+
5013+
auto makeItem = [&menu, &makeCallback](const winrt::hstring& label,
5014+
const winrt::hstring& icon,
5015+
const winrt::hstring& suggestion) {
5016+
MenuFlyoutItem item{};
5017+
5018+
if (!icon.empty())
5019+
{
5020+
auto iconElement = UI::IconPathConverter::IconWUX(icon);
5021+
Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw);
5022+
item.Icon(iconElement);
5023+
}
5024+
5025+
item.Text(label);
5026+
item.Click(makeCallback(ActionAndArgs{ ShortcutAction::SendInput, SendInputArgs{ hstring{ L"\u0003" } + suggestion } }));
5027+
menu.Items().Append(item);
5028+
};
5029+
5030+
// Wire up each item to the action that should be performed. By actually
5031+
// connecting these to actions, we ensure the implementation is
5032+
// consistent. This also leaves room for customizing this menu with
5033+
// actions in the future.
5034+
5035+
menu.Items().Clear();
5036+
const auto quickFixes = control.CommandHistory().QuickFixes();
5037+
for (const auto& qf : quickFixes)
5038+
{
5039+
makeItem(qf, L"\ue74c", qf);
5040+
}
5041+
}
5042+
48095043
// Handler for our WindowProperties's PropertyChanged event. We'll use this
48105044
// to pop the "Identify Window" toast when the user renames our window.
48115045
winrt::fire_and_forget TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/,

src/cascadia/TerminalApp/TerminalPage.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ namespace winrt::TerminalApp::implementation
510510
void _OpenSuggestions(const Microsoft::Terminal::Control::TermControl& sender, Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> commandsCollection, winrt::TerminalApp::SuggestionsMode mode, winrt::hstring filterText);
511511

512512
void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
513+
winrt::fire_and_forget _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
513514

514515
winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
515516

@@ -527,6 +528,7 @@ namespace winrt::TerminalApp::implementation
527528
void _sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional<til::point> dragPoint);
528529

529530
void _PopulateContextMenu(const Microsoft::Terminal::Control::TermControl& control, const Microsoft::UI::Xaml::Controls::CommandBarFlyout& sender, const bool withSelection);
531+
void _PopulateQuickFixMenu(const Microsoft::Terminal::Control::TermControl& control, const Windows::UI::Xaml::Controls::MenuFlyout& sender);
530532
winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex);
531533

532534
winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender);

0 commit comments

Comments
 (0)