diff --git a/build/rules/Microsoft.WindowsPackageManager.ComInterop.Additional.targets b/build/rules/Microsoft.WindowsPackageManager.ComInterop.Additional.targets
new file mode 100644
index 0000000000..be2a691bb2
--- /dev/null
+++ b/build/rules/Microsoft.WindowsPackageManager.ComInterop.Additional.targets
@@ -0,0 +1,22 @@
+
+
+
+
+ x86
+ $(Platform)
+
+
+
+ true
+
+
+
+
+
+ CustomOutputGroupForPackaging
+ $(ProjectName)
+ Microsoft.Management.Deployment.winmd
+
+
+
+
\ No newline at end of file
diff --git a/dep/nuget/packages.config b/dep/nuget/packages.config
index a02190c6bc..a641da7c51 100644
--- a/dep/nuget/packages.config
+++ b/dep/nuget/packages.config
@@ -10,6 +10,7 @@
+
diff --git a/src/cascadia/TerminalApp/SuggestionsControl.cpp b/src/cascadia/TerminalApp/SuggestionsControl.cpp
index 80206ea5dd..442b80243c 100644
--- a/src/cascadia/TerminalApp/SuggestionsControl.cpp
+++ b/src/cascadia/TerminalApp/SuggestionsControl.cpp
@@ -724,6 +724,20 @@ namespace winrt::TerminalApp::implementation
// ever allow non-sendInput actions.
DispatchCommandRequested.raise(*this, actionPaletteItem.Command());
+ if (const auto& sendInputCmd = actionPaletteItem.Command().ActionAndArgs().Args().try_as())
+ {
+ if (til::starts_with(sendInputCmd.Input(), L"winget"))
+ {
+ TraceLoggingWrite(
+ g_hTerminalAppProvider,
+ "QuickFixSuggestionUsed",
+ TraceLoggingDescription("Event emitted when a winget suggestion from is used"),
+ TraceLoggingValue("SuggestionsUI", "Source"),
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
+ }
+ }
+
TraceLoggingWrite(
g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider
"SuggestionsControlDispatchedAction",
diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
index 8549c32cd4..99ccf110ff 100644
--- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
+++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
@@ -25,6 +25,7 @@
true
true
+ true
@@ -177,6 +178,7 @@
SuggestionsControl.xaml
+
@@ -361,7 +363,7 @@
-
+
TaskPaneContent.xaml
Code
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 002886fd3b..aae32e336c 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -26,6 +26,7 @@
#include "LaunchPositionRequest.g.cpp"
using namespace winrt;
+using namespace winrt::Microsoft::Management::Deployment;
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Settings::Model;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
@@ -3001,18 +3002,82 @@ namespace winrt::TerminalApp::implementation
ShowWindowChanged.raise(*this, args);
}
- winrt::fire_and_forget TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
+ Windows::Foundation::IAsyncOperation> TerminalPage::_FindPackageAsync(hstring query)
{
- assert(!Dispatcher().HasThreadAccess());
+ const PackageManager packageManager = WindowsPackageManagerFactory::CreatePackageManager();
+ PackageCatalogReference catalogRef{
+ packageManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog)
+ };
+ catalogRef.PackageCatalogBackgroundUpdateInterval(std::chrono::hours(24));
+ ConnectResult connectResult{ nullptr };
+ for (int retries = 0;;)
+ {
+ connectResult = catalogRef.Connect();
+ if (connectResult.Status() == ConnectResultStatus::Ok)
+ {
+ break;
+ }
+
+ if (++retries == 3)
+ {
+ co_return nullptr;
+ }
+ }
+
+ PackageCatalog catalog = connectResult.PackageCatalog();
+ // clang-format off
+ static constexpr std::array searches{ {
+ { .Field = PackageMatchField::Command, .MatchOption = PackageFieldMatchOption::StartsWithCaseInsensitive },
+ { .Field = PackageMatchField::Name, .MatchOption = PackageFieldMatchOption::ContainsCaseInsensitive },
+ { .Field = PackageMatchField::Moniker, .MatchOption = PackageFieldMatchOption::ContainsCaseInsensitive } } };
+ // clang-format on
+
+ PackageMatchFilter filter = WindowsPackageManagerFactory::CreatePackageMatchFilter();
+ filter.Value(query);
+
+ FindPackagesOptions options = WindowsPackageManagerFactory::CreateFindPackagesOptions();
+ options.Filters().Append(filter);
+ options.ResultLimit(20);
+
+ IVectorView pkgList;
+ for (const auto& search : searches)
+ {
+ filter.Field(search.Field);
+ filter.Option(search.MatchOption);
+
+ const auto result = co_await catalog.FindPackagesAsync(options);
+ pkgList = result.Matches();
+ if (pkgList.Size() > 0)
+ {
+ break;
+ }
+ }
+ co_return pkgList;
+ }
+
+ Windows::Foundation::IAsyncAction TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args)
+ {
if (!Feature_QuickFix::IsEnabled())
{
co_return;
}
+ co_await winrt::resume_background();
+
+ // no packages were found, nothing to suggest
+ const auto pkgList = co_await _FindPackageAsync(args.MissingCommand());
+ if (!pkgList || pkgList.Size() == 0)
+ {
+ co_return;
+ }
std::vector suggestions;
- suggestions.reserve(1);
- suggestions.emplace_back(fmt::format(FMT_COMPILE(L"winget install {}"), args.MissingCommand()));
+ suggestions.reserve(pkgList.Size());
+ for (const auto& pkg : pkgList)
+ {
+ // --id and --source ensure we don't collide with another package catalog
+ suggestions.emplace_back(fmt::format(FMT_COMPILE(L"winget install --id {} -s winget"), pkg.CatalogPackage().Id()));
+ }
co_await wil::resume_foreground(Dispatcher());
@@ -5006,6 +5071,14 @@ namespace winrt::TerminalApp::implementation
{
ctrl.ClearQuickFix();
}
+
+ TraceLoggingWrite(
+ g_hTerminalAppProvider,
+ "QuickFixSuggestionUsed",
+ TraceLoggingDescription("Event emitted when a winget suggestion from is used"),
+ TraceLoggingValue("QuickFixMenu", "Source"),
+ TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
+ TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}
};
};
@@ -5027,6 +5100,7 @@ namespace winrt::TerminalApp::implementation
item.Text(qf);
item.Click(makeCallback(qf));
+ ToolTipService::SetToolTip(item, box_value(qf));
menu.Items().Append(item);
}
}
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index 65bfa9bad7..449ffa1fff 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -13,6 +13,8 @@
#include "LaunchPositionRequest.g.h"
#include "Toast.h"
+#include "WindowsPackageManagerFactory.h"
+
#define DECLARE_ACTION_HANDLER(action) void _Handle##action(const IInspectable& sender, const Microsoft::Terminal::Settings::Model::ActionEventArgs& args);
namespace TerminalAppLocalTests
@@ -87,6 +89,12 @@ namespace winrt::TerminalApp::implementation
til::property Position;
};
+ struct WinGetSearchParams
+ {
+ winrt::Microsoft::Management::Deployment::PackageMatchField Field;
+ winrt::Microsoft::Management::Deployment::PackageFieldMatchOption MatchOption;
+ };
+
struct TerminalPage : TerminalPageT
{
public:
@@ -530,7 +538,8 @@ namespace winrt::TerminalApp::implementation
void _OpenSuggestions(const Microsoft::Terminal::Control::TermControl& sender, Windows::Foundation::Collections::IVector commandsCollection, winrt::TerminalApp::SuggestionsMode mode, winrt::hstring filterText);
void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
- winrt::fire_and_forget _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
+ Windows::Foundation::IAsyncAction _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args);
+ Windows::Foundation::IAsyncOperation> _FindPackageAsync(hstring query);
winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args);
diff --git a/src/cascadia/TerminalApp/WindowsPackageManagerFactory.h b/src/cascadia/TerminalApp/WindowsPackageManagerFactory.h
new file mode 100644
index 0000000000..1032eb2076
--- /dev/null
+++ b/src/cascadia/TerminalApp/WindowsPackageManagerFactory.h
@@ -0,0 +1,61 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+//
+// Module Name:
+// - WindowsPackageManagerFactory.h
+//
+// Abstract:
+// - This factory is designed to create production-level instances of WinGet objects.
+// Elevated sessions require manual activation of WinGet objects and are not currently supported,
+// while non-elevated sessions can use the standard WinRT activation system.
+// Author:
+// - Carlos Zamora (carlos-zamora) 23-Jul-2024
+
+#pragma once
+
+#include
+
+namespace winrt::TerminalApp::implementation
+{
+ struct WindowsPackageManagerFactory
+ {
+ public:
+ static winrt::Microsoft::Management::Deployment::PackageManager CreatePackageManager()
+ {
+ return winrt::create_instance(PackageManagerGuid, CLSCTX_ALL);
+ }
+
+ static winrt::Microsoft::Management::Deployment::FindPackagesOptions CreateFindPackagesOptions()
+ {
+ return winrt::create_instance(FindPackageOptionsGuid, CLSCTX_ALL);
+ }
+
+ static winrt::Microsoft::Management::Deployment::CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions()
+ {
+ return winrt::create_instance(CreateCompositePackageCatalogOptionsGuid, CLSCTX_ALL);
+ }
+
+ static winrt::Microsoft::Management::Deployment::InstallOptions CreateInstallOptions()
+ {
+ return winrt::create_instance(InstallOptionsGuid, CLSCTX_ALL);
+ }
+
+ static winrt::Microsoft::Management::Deployment::UninstallOptions CreateUninstallOptions()
+ {
+ return winrt::create_instance(UninstallOptionsGuid, CLSCTX_ALL);
+ }
+
+ static winrt::Microsoft::Management::Deployment::PackageMatchFilter CreatePackageMatchFilter()
+ {
+ return winrt::create_instance(PackageMatchFilterGuid, CLSCTX_ALL);
+ }
+
+ private:
+ static constexpr winrt::guid PackageManagerGuid{ 0xC53A4F16, 0x787E, 0x42A4, { 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 } };
+ static constexpr winrt::guid FindPackageOptionsGuid{ 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } };
+ static constexpr winrt::guid CreateCompositePackageCatalogOptionsGuid{ 0x526534B8, 0x7E46, 0x47C8, { 0x84, 0x16, 0xB1, 0x68, 0x5C, 0x32, 0x7D, 0x37 } };
+ static constexpr winrt::guid InstallOptionsGuid{ 0x1095F097, 0xEB96, 0x453B, { 0xB4, 0xE6, 0x16, 0x13, 0x63, 0x7F, 0x3B, 0x14 } };
+ static constexpr winrt::guid UninstallOptionsGuid{ 0xE1D9A11E, 0x9F85, 0x4D87, { 0x9C, 0x17, 0x2B, 0x93, 0x14, 0x3A, 0xDB, 0x8D } };
+ static constexpr winrt::guid PackageMatchFilterGuid{ 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } };
+ };
+}
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index d8d975f667..ac306b69c6 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -128,7 +128,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); };
_terminal->CompletionsChangedCallback(pfnCompletionsChanged);
- auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward(PH1)); };
+ auto pfnSearchMissingCommand = [this](auto&& PH1, auto&& PH2) { _terminalSearchMissingCommand(std::forward(PH1), std::forward(PH2)); };
_terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand);
auto pfnClearQuickFix = [this] { ClearQuickFix(); };
@@ -1629,9 +1629,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_midiAudio.PlayNote(reinterpret_cast(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast(duration));
}
- void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand)
+ void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand, const til::CoordType& bufferRow)
{
- SearchMissingCommand.raise(*this, make(hstring{ missingCommand }));
+ SearchMissingCommand.raise(*this, make(hstring{ missingCommand }, bufferRow));
}
void ControlCore::ClearQuickFix()
diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h
index 938d048243..32fa4c0253 100644
--- a/src/cascadia/TerminalControl/ControlCore.h
+++ b/src/cascadia/TerminalControl/ControlCore.h
@@ -389,7 +389,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _terminalPlayMidiNote(const int noteNumber,
const int velocity,
const std::chrono::microseconds duration);
- void _terminalSearchMissingCommand(std::wstring_view missingCommand);
+ void _terminalSearchMissingCommand(std::wstring_view missingCommand, const til::CoordType& bufferRow);
winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength);
diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h
index 6770da9f62..0e98cefd67 100644
--- a/src/cascadia/TerminalControl/EventArgs.h
+++ b/src/cascadia/TerminalControl/EventArgs.h
@@ -216,10 +216,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT
{
public:
- SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) :
- MissingCommand(missingCommand) {}
+ SearchMissingCommandEventArgs(const winrt::hstring& missingCommand, const til::CoordType& bufferRow) :
+ MissingCommand(missingCommand),
+ BufferRow(bufferRow) {}
til::property MissingCommand;
+ til::property BufferRow;
};
}
diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl
index 6cad9ccff7..c3255bc667 100644
--- a/src/cascadia/TerminalControl/EventArgs.idl
+++ b/src/cascadia/TerminalControl/EventArgs.idl
@@ -130,5 +130,6 @@ namespace Microsoft.Terminal.Control
runtimeclass SearchMissingCommandEventArgs
{
String MissingCommand { get; };
+ Int32 BufferRow { get; };
}
}
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 8c2a37e7d2..aa38206d10 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -4059,18 +4059,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto rd = get_self(_core)->GetRenderData();
rd->LockConsole();
const auto viewportBufferPosition = rd->GetViewport();
- const auto cursorBufferPosition = rd->GetCursorPosition();
rd->UnlockConsole();
- if (cursorBufferPosition.y < viewportBufferPosition.Top() || cursorBufferPosition.y > viewportBufferPosition.BottomInclusive())
+ if (_quickFixBufferPos < viewportBufferPosition.Top() || _quickFixBufferPos > viewportBufferPosition.BottomInclusive())
{
quickFixBtn.Visibility(Visibility::Collapsed);
return;
}
// draw the button in the gutter
- const auto& cursorPosInDips = CursorPositionInDips();
+ const auto& quickFixBtnPosInDips = _toPosInDips({ 0, _quickFixBufferPos });
Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left);
- Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y - termPadding.Top);
+ Controls::Canvas::SetTop(quickFixBtn, quickFixBtnPosInDips.y - termPadding.Top);
quickFixBtn.Visibility(Visibility::Visible);
if (auto automationPeer{ FrameworkElementAutomationPeer::FromElement(*this) })
@@ -4085,6 +4084,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void TermControl::_bubbleSearchMissingCommand(const IInspectable& /*sender*/, const Control::SearchMissingCommandEventArgs& args)
{
+ _quickFixBufferPos = args.BufferRow();
SearchMissingCommand.raise(*this, args);
}
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index ab5b0a1ae7..c4fbdb40d9 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -278,6 +278,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _initializedTerminal{ false };
bool _quickFixButtonCollapsible{ false };
bool _quickFixesAvailable{ false };
+ til::CoordType _quickFixBufferPos{};
std::shared_ptr _playWarningBell;
diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp
index b7e343998d..8a82d4d91a 100644
--- a/src/cascadia/TerminalCore/Terminal.cpp
+++ b/src/cascadia/TerminalCore/Terminal.cpp
@@ -1233,7 +1233,7 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi
_pfnCompletionsChanged.swap(pfn);
}
-void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::function pfn) noexcept
+void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::function pfn) noexcept
{
_pfnSearchMissingCommand.swap(pfn);
}
diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp
index cf8dea0a16..34d92213d8 100644
--- a/src/cascadia/TerminalCore/Terminal.hpp
+++ b/src/cascadia/TerminalCore/Terminal.hpp
@@ -230,7 +230,7 @@ public:
void SetShowWindowCallback(std::function pfn) noexcept;
void SetPlayMidiNoteCallback(std::function pfn) noexcept;
void CompletionsChangedCallback(std::function pfn) noexcept;
- void SetSearchMissingCommandCallback(std::function pfn) noexcept;
+ void SetSearchMissingCommandCallback(std::function pfn) noexcept;
void SetClearQuickFixCallback(std::function pfn) noexcept;
void SetSearchHighlights(const std::vector& highlights) noexcept;
void SetSearchHighlightFocused(const size_t focusedIdx, til::CoordType searchScrollOffset);
@@ -342,7 +342,7 @@ private:
std::function _pfnShowWindowChanged;
std::function _pfnPlayMidiNote;
std::function _pfnCompletionsChanged;
- std::function _pfnSearchMissingCommand;
+ std::function _pfnSearchMissingCommand;
std::function _pfnClearQuickFix;
RenderSettings _renderSettings;
diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp
index 26daf24e5d..9114d7866e 100644
--- a/src/cascadia/TerminalCore/TerminalApi.cpp
+++ b/src/cascadia/TerminalCore/TerminalApi.cpp
@@ -338,7 +338,8 @@ void Terminal::SearchMissingCommand(const std::wstring_view command)
{
if (_pfnSearchMissingCommand)
{
- _pfnSearchMissingCommand(command);
+ const auto bufferRow = GetCursorPosition().y;
+ _pfnSearchMissingCommand(command, bufferRow);
}
}
diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj
index 68a52a67fd..21fea0cf5f 100644
--- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj
+++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj
@@ -20,6 +20,7 @@
true
true
true
+ true
diff --git a/src/common.nugetversions.props b/src/common.nugetversions.props
index 7989c7ce0e..6e53f2cde2 100644
--- a/src/common.nugetversions.props
+++ b/src/common.nugetversions.props
@@ -39,4 +39,9 @@
+
+
+ $(MSBuildThisFileDirectory)..\packages\Microsoft.WindowsPackageManager.ComInterop.1.8.1911\
+
+
diff --git a/src/common.nugetversions.targets b/src/common.nugetversions.targets
index 375bfd2c39..837c575fd8 100644
--- a/src/common.nugetversions.targets
+++ b/src/common.nugetversions.targets
@@ -64,6 +64,8 @@
+
+